1  
//
1  
//
2  
// Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com)
2  
// Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com)
3  
//
3  
//
4  
// Distributed under the Boost Software License, Version 1.0. (See accompanying
4  
// Distributed under the Boost Software License, Version 1.0. (See accompanying
5  
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
5  
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6  
//
6  
//
7  
// Official repository: https://github.com/cppalliance/capy
7  
// Official repository: https://github.com/cppalliance/capy
8  
//
8  
//
9  

9  

10  
#ifndef BOOST_CAPY_IO_ENV_HPP
10  
#ifndef BOOST_CAPY_IO_ENV_HPP
11  
#define BOOST_CAPY_IO_ENV_HPP
11  
#define BOOST_CAPY_IO_ENV_HPP
12  

12  

13  
#include <boost/capy/detail/config.hpp>
13  
#include <boost/capy/detail/config.hpp>
14  
#include <boost/capy/ex/executor_ref.hpp>
14  
#include <boost/capy/ex/executor_ref.hpp>
15 -
#include <coroutine>
 
16  

15  

17  
#include <memory_resource>
16  
#include <memory_resource>
18  
#include <stop_token>
17  
#include <stop_token>
19  

18  

20  
namespace boost {
19  
namespace boost {
21  
namespace capy {
20  
namespace capy {
22 -
/** Callable that posts a coroutine handle to an executor.
 
23 -

 
24 -
    Use this as the callback type for `std::stop_callback` instead
 
25 -
    of a raw `std::coroutine_handle<>`. Raw handles resume the
 
26 -
    coroutine inline on whatever thread calls `request_stop()`,
 
27 -
    which bypasses the executor and corrupts the thread-local
 
28 -
    frame allocator.
 
29 -

 
30 -
    Prefer @ref io_env::post_resume and the @ref stop_resume_callback
 
31 -
    alias to construct these—see examples there.
 
32 -

 
33 -
    @see io_env::post_resume, stop_resume_callback
 
34 -
*/
 
35 -
struct resume_via_post
 
36 -
{
 
37 -
    executor_ref ex;
 
38 -
    std::coroutine_handle<> h;
 
39 -

 
40 -
    // post() must not throw; stop_callback requires a
 
41 -
    // non-throwing invocable.
 
42 -
    void operator()() const noexcept
 
43 -
    {
 
44 -
        ex.post(h);
 
45 -
    }
 
46 -
};
 
47 -

 
48  

21  

49  
/** Execution environment for IoAwaitables.
22  
/** Execution environment for IoAwaitables.
50  

23  

51  
    This struct bundles the execution context passed through
24  
    This struct bundles the execution context passed through
52  
    coroutine chains via the IoAwaitable protocol. It contains
25  
    coroutine chains via the IoAwaitable protocol. It contains
53  
    the executor for resumption, a stop token for cancellation,
26  
    the executor for resumption, a stop token for cancellation,
54  
    and an optional frame allocator for coroutine frame allocation.
27  
    and an optional frame allocator for coroutine frame allocation.
55  

28  

56  
    @par Lifetime
29  
    @par Lifetime
57  

30  

58  
    Launch functions (@ref run_async, @ref run) own the `io_env` and
31  
    Launch functions (@ref run_async, @ref run) own the `io_env` and
59  
    guarantee it outlives all tasks and awaitables in the launched
32  
    guarantee it outlives all tasks and awaitables in the launched
60  
    chain. Awaitables receive `io_env const*` in `await_suspend`
33  
    chain. Awaitables receive `io_env const*` in `await_suspend`
61  
    and should store it directly, never copy the pointed-to object.
34  
    and should store it directly, never copy the pointed-to object.
62 -
    @par Stop Callback Contract
 
63 -

 
64 -
    Awaitables that register a `std::stop_callback` **must not**
 
65 -
    resume the coroutine handle directly. The callback fires
 
66 -
    synchronously on the thread that calls `request_stop()`, which
 
67 -
    may not be an executor-managed thread. Resuming inline poisons
 
68 -
    that thread's TLS frame allocator with the pool's allocator,
 
69 -
    causing use-after-free on the next coroutine allocation.
 
70 -

 
71 -
    Use @ref io_env::post_resume and @ref stop_resume_callback:
 
72 -
    @code
 
73 -
    std::optional<stop_resume_callback> stop_cb_;
 
74 -
    // In await_suspend:
 
75 -
    stop_cb_.emplace(env->stop_token, env->post_resume(h));
 
76 -
    @endcode
 
77 -

 
78  

35  

79  
    @par Thread Safety
36  
    @par Thread Safety
80  
    The referenced executor and allocator must remain valid
37  
    The referenced executor and allocator must remain valid
81  
    for the lifetime of any coroutine using this environment.
38  
    for the lifetime of any coroutine using this environment.
82  

39  

83 -
    @see IoAwaitable, IoRunnable, resume_via_post
40 +
    @see IoAwaitable, IoRunnable
84  
*/
41  
*/
85  
struct io_env
42  
struct io_env
86  
{
43  
{
87  
    /** The executor for coroutine resumption. */
44  
    /** The executor for coroutine resumption. */
88  
    executor_ref executor;
45  
    executor_ref executor;
89  

46  

90  
    /** The stop token for cancellation propagation. */
47  
    /** The stop token for cancellation propagation. */
91  
    std::stop_token stop_token;
48  
    std::stop_token stop_token;
92  

49  

93  
    /** The frame allocator for coroutine frame allocation.
50  
    /** The frame allocator for coroutine frame allocation.
94  

51  

95  
        When null, the default allocator is used.
52  
        When null, the default allocator is used.
96  
    */
53  
    */
97 -

 
98 -
    /** Create a resume_via_post callable for this environment.
 
99 -

 
100 -
        Convenience method for registering @ref stop_resume_callback
 
101 -
        instances. Equivalent to `resume_via_post{executor, h}`.
 
102 -

 
103 -
        @par Example
 
104 -
        @code
 
105 -
        stop_cb_.emplace(env->stop_token, env->post_resume(h));
 
106 -
        @endcode
 
107 -

 
108 -
        @param h The coroutine handle to post on cancellation.
 
109 -

 
110 -
        @return A @ref resume_via_post callable that holds a
 
111 -
        non-owning @ref executor_ref and the coroutine handle.
 
112 -
        The callable must not outlive the executor it references.
 
113 -

 
114 -
        @see resume_via_post, stop_resume_callback
 
115 -
    */
 
116 -
    resume_via_post
 
117 -
    post_resume(std::coroutine_handle<> h) const noexcept
 
118 -
    {
 
119 -
        return resume_via_post{executor, h};
 
120 -
    }
 
121  
    std::pmr::memory_resource* frame_allocator = nullptr;
54  
    std::pmr::memory_resource* frame_allocator = nullptr;
122 -

 
123 -
/** Type alias for a stop callback that posts through the executor.
 
124 -

 
125 -
    Use this to declare the stop callback member in your awaitable:
 
126 -
    @code
 
127 -
    std::optional<stop_resume_callback> stop_cb_;
 
128 -
    @endcode
 
129 -

 
130 -
    @see resume_via_post, io_env::post_resume
 
131 -
*/
 
132 -
using stop_resume_callback = std::stop_callback<resume_via_post>;
 
133  
};
55  
};
134  

56  

135  
} // capy
57  
} // capy
136  
} // boost
58  
} // boost
137  

59  

138  
#endif
60  
#endif