1 #pragma once
2 
3 #include <sdbusplus/async/context.hpp>
4 #include <sdbusplus/async/execution.hpp>
5 #include <sdbusplus/event.hpp>
6 
7 namespace sdbusplus::async
8 {
9 
10 namespace fdio_ns
11 {
12 struct fdio_completion;
13 }
14 
15 class fdio : private context_ref, details::context_friend
16 {
17   public:
18     fdio() = delete;
19     fdio(const fdio&) = delete;
20     fdio& operator=(const fdio&) = delete;
21     fdio(fdio&&) = delete;
22     fdio& operator=(fdio&&) = delete;
23     ~fdio() = default;
24 
25     /** Construct a new fdio object from a context and a file descriptor. */
26     fdio(context& ctx, int fd);
27 
28     /** Get the next fd event.
29      *  Note: the implementation only supports a single awaiting task.  Two
30      *  tasks should not share this object and both call `next`.
31      */
32     auto next() noexcept;
33 
34     friend fdio_ns::fdio_completion;
35 
36   private:
37     int fd;
38     event_source_t source;
39     std::mutex lock{};
40     fdio_ns::fdio_completion* complete{nullptr};
41 
event_loop()42     event_t& event_loop()
43     {
44         return get_event_loop(ctx);
45     }
46 
47     void handleEvent() noexcept;
48 };
49 
50 namespace fdio_ns
51 {
52 
53 struct fdio_completion
54 {
55     fdio_completion() = delete;
56     fdio_completion(const fdio_completion&) = delete;
57     fdio_completion& operator=(const fdio_completion&) = delete;
58     fdio_completion(fdio_completion&&) = delete;
59 
fdio_completionsdbusplus::async::fdio_ns::fdio_completion60     explicit fdio_completion(fdio& fdioInstance) noexcept :
61         fdioInstance(fdioInstance) {};
62     ~fdio_completion();
63 
64     friend fdio;
65 
tag_invoke(execution::start_t,fdio_completion & self)66     friend void tag_invoke(execution::start_t, fdio_completion& self) noexcept
67     {
68         self.arm();
69     }
70 
71   private:
72     virtual void complete() noexcept = 0;
73     virtual void stop() noexcept = 0;
74     void arm() noexcept;
75 
76     fdio& fdioInstance;
77 };
78 
79 // Implementation (templated based on Receiver) of fdio_completion.
80 template <execution::receiver Receiver>
81 struct fdio_operation : fdio_completion
82 {
fdio_operationsdbusplus::async::fdio_ns::fdio_operation83     fdio_operation(fdio& fdioInstance, Receiver r) :
84         fdio_completion(fdioInstance), receiver(std::move(r))
85     {}
86 
87   private:
completesdbusplus::async::fdio_ns::fdio_operation88     void complete() noexcept override final
89     {
90         execution::set_value(std::move(receiver));
91     }
92 
stopsdbusplus::async::fdio_ns::fdio_operation93     void stop() noexcept override final
94     {
95         execution::set_stopped(std::move(receiver));
96     }
97 
98     Receiver receiver;
99 };
100 
101 // fdio Sender implementation.
102 struct fdio_sender
103 {
104     using is_sender = void;
105 
106     fdio_sender() = delete;
fdio_sendersdbusplus::async::fdio_ns::fdio_sender107     explicit fdio_sender(fdio& fdioInstance) noexcept :
108         fdioInstance(fdioInstance) {};
109 
110     friend auto tag_invoke(
111         execution::get_completion_signatures_t, const fdio_sender&,
112         auto) -> execution::completion_signatures<execution::set_value_t(),
113                                                   execution::set_stopped_t()>;
114 
115     template <execution::receiver R>
tag_invoke(execution::connect_t,fdio_sender && self,R r)116     friend auto tag_invoke(execution::connect_t, fdio_sender&& self,
117                            R r) -> fdio_operation<R>
118     {
119         return {self.fdioInstance, std::move(r)};
120     }
121 
122   private:
123     fdio& fdioInstance;
124 };
125 
126 } // namespace fdio_ns
127 
next()128 inline auto fdio::next() noexcept
129 {
130     return fdio_ns::fdio_sender{*this};
131 }
132 
133 } // namespace sdbusplus::async
134