TLA Line data Source code
1 : //
2 : // Copyright (c) 2026 Michael Vandeberg
3 : //
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)
6 : //
7 : // Official repository: https://github.com/cppalliance/corosio
8 : //
9 :
10 : #ifndef BOOST_COROSIO_NATIVE_DETAIL_REACTOR_REACTOR_DESCRIPTOR_SERVICE_HPP
11 : #define BOOST_COROSIO_NATIVE_DETAIL_REACTOR_REACTOR_DESCRIPTOR_SERVICE_HPP
12 :
13 : #include <boost/corosio/detail/platform.hpp>
14 :
15 : #if BOOST_COROSIO_POSIX
16 :
17 : #include <boost/corosio/detail/descriptor_service.hpp>
18 : #include <boost/corosio/detail/scheduler_op.hpp>
19 : #include <boost/corosio/native/detail/validate_fd.hpp>
20 : #include <boost/corosio/native/detail/reactor/reactor_descriptor.hpp>
21 : #include <boost/corosio/native/detail/reactor/reactor_service_state.hpp>
22 : #include <boost/capy/ex/execution_context.hpp>
23 :
24 : #include <memory>
25 : #include <mutex>
26 : #include <system_error>
27 :
28 : /* Reactor-backed descriptor_service.
29 :
30 : assign_descriptor is the validate-before-mutate core the public
31 : assign() contract rests on, modelled on do_assign_fd in
32 : reactor_service_finals.hpp.
33 :
34 : PARALLEL COPY: construct, destroy, close and shutdown here mirror the
35 : same four members of reactor_socket_service.hpp, which this cannot
36 : reuse because it calls close_socket() by name. A fix to the service
37 : lifecycle -- the construct/destroy bookkeeping under state_->mutex_,
38 : or shutdown's deliberate retention of impl_ptrs_ so impls outlive the
39 : scheduler's drain -- belongs in both files.
40 : */
41 :
42 : namespace boost::corosio::detail {
43 :
44 : /** CRTP base for reactor-backed descriptor services.
45 :
46 : @tparam Derived The named final service type (CRTP self).
47 : @tparam Traits Backend traits (epoll_traits, kqueue_traits, ...).
48 : @tparam DescFinal The named final descriptor impl type.
49 : */
50 : template<class Derived, class Traits, class DescFinal>
51 : class reactor_descriptor_service : public descriptor_service
52 : {
53 : using scheduler_type = typename Traits::scheduler_type;
54 : using state_type = reactor_service_state<scheduler_type, DescFinal>;
55 :
56 : friend Derived;
57 :
58 : protected:
59 : // NOLINTNEXTLINE(bugprone-crtp-constructor-accessibility)
60 HIT 105 : explicit reactor_descriptor_service(capy::execution_context& ctx)
61 105 : : state_(
62 : std::make_unique<state_type>(
63 105 : ctx.template use_service<scheduler_type>()))
64 : {
65 105 : }
66 :
67 : public:
68 : /// True when any parked op must wake the reactor.
69 : static constexpr bool needs_park_notification =
70 : Traits::needs_park_notification;
71 :
72 105 : ~reactor_descriptor_service() override = default;
73 :
74 : std::error_code assign_descriptor(
75 : posix_stream_descriptor::implementation& impl,
76 : native_handle_type fd) override;
77 :
78 105 : void shutdown() override
79 : {
80 105 : std::lock_guard lock(state_->mutex_);
81 :
82 119 : while (auto* impl = state_->impl_list_.pop_front())
83 14 : impl->close_descriptor();
84 :
85 : // Don't clear impl_ptrs_ here: the scheduler shuts down after us
86 : // and drains completed_ops_, so every impl must outlive that.
87 105 : }
88 :
89 121 : io_object::implementation* construct() override
90 : {
91 121 : auto impl = std::make_shared<DescFinal>(static_cast<Derived&>(*this));
92 121 : auto* raw = impl.get();
93 :
94 : {
95 121 : std::lock_guard lock(state_->mutex_);
96 121 : state_->impl_ptrs_.emplace(raw, std::move(impl));
97 121 : state_->impl_list_.push_back(raw);
98 121 : }
99 :
100 121 : return raw;
101 121 : }
102 :
103 107 : void destroy(io_object::implementation* impl) override
104 : {
105 107 : auto* typed = static_cast<DescFinal*>(impl);
106 107 : typed->close_descriptor();
107 107 : std::lock_guard lock(state_->mutex_);
108 107 : state_->impl_list_.remove(typed);
109 107 : state_->impl_ptrs_.erase(typed);
110 107 : }
111 :
112 195 : void close(io_object::handle& h) override
113 : {
114 195 : static_cast<DescFinal*>(h.get())->close_descriptor();
115 195 : }
116 :
117 296 : scheduler_type& scheduler() const noexcept
118 : {
119 296 : return state_->sched_;
120 : }
121 :
122 71 : void post(scheduler_op* op)
123 : {
124 71 : state_->sched_.post(op);
125 71 : }
126 :
127 55 : void work_started() noexcept
128 : {
129 55 : state_->sched_.work_started();
130 55 : }
131 :
132 30 : void work_finished() noexcept
133 : {
134 30 : state_->sched_.work_finished();
135 30 : }
136 :
137 : protected:
138 : std::unique_ptr<state_type> state_;
139 :
140 : private:
141 : reactor_descriptor_service(reactor_descriptor_service const&) = delete;
142 : reactor_descriptor_service&
143 : operator=(reactor_descriptor_service const&) = delete;
144 : };
145 :
146 : template<class Derived, class Traits, class DescFinal>
147 : std::error_code
148 111 : reactor_descriptor_service<Derived, Traits, DescFinal>::assign_descriptor(
149 : posix_stream_descriptor::implementation& impl_base, native_handle_type fd)
150 : {
151 111 : auto* impl = static_cast<DescFinal*>(&impl_base);
152 :
153 : // The public assign() guarantees the object is closed.
154 111 : if (auto ec = validate_descriptor_fd(fd))
155 4 : return ec;
156 :
157 : if constexpr (requires { Traits::max_descriptor; })
158 : {
159 53 : if (fd >= Traits::max_descriptor)
160 1 : return make_err(EMFILE);
161 : }
162 :
163 106 : return impl->init_and_register(fd);
164 : }
165 :
166 : } // namespace boost::corosio::detail
167 :
168 : #endif // BOOST_COROSIO_POSIX
169 :
170 : #endif // BOOST_COROSIO_NATIVE_DETAIL_REACTOR_REACTOR_DESCRIPTOR_SERVICE_HPP
|