xref: /openbmc/sdbusplus/src/async/fdio.cpp (revision 7c0fb15f6bca1fb107d4c55006a5eed9bd82b325)
1 #include <sdbusplus/async/fdio.hpp>
2 
3 namespace sdbusplus::async
4 {
5 fdio::fdio(context& ctx, int fd, std::chrono::microseconds timeout) :
6     context_ref(ctx),
7     timeout(std::chrono::duration_cast<event_t::time_resolution>(timeout))
8 {
9     static auto eventHandler =
10         [](sd_event_source*, int, uint32_t, void* data) noexcept {
11             static_cast<fdio*>(data)->handleEvent();
12             return 0;
13         };
14 
15     try
16     {
17         source = event_loop().add_io(fd, EPOLLIN, eventHandler, this);
18     }
19     catch (...)
20     {
21         throw std::runtime_error("Failed to add fd to event loop");
22     }
23 }
24 
25 void fdio::handleEvent() noexcept
26 {
27     std::unique_lock l{lock};
28     if (complete == nullptr)
29     {
30         return;
31     }
32     auto c = std::exchange(complete, nullptr);
33     l.unlock();
34     c->complete();
35 }
36 
37 void fdio::handleTimeout() noexcept
38 {
39     std::unique_lock l{lock};
40     if (complete == nullptr)
41     {
42         return;
43     }
44     auto c = std::exchange(complete, nullptr);
45     l.unlock();
46     c->error(std::make_exception_ptr(fdio_timeout_exception()));
47 }
48 
49 namespace fdio_ns
50 {
51 
52 fdio_completion::~fdio_completion()
53 {
54     std::unique_lock l{fdioInstance.lock};
55 
56     if (fdioInstance.complete == this)
57     {
58         std::exchange(fdioInstance.complete, nullptr);
59     }
60 }
61 
62 void fdio_completion::arm() noexcept
63 {
64     // Set ourselves as the awaiting Receiver
65     std::unique_lock l{fdioInstance.lock};
66 
67     if (std::exchange(fdioInstance.complete, this) != nullptr)
68     {
69         // We do not support two awaiters; throw exception. Since we are in
70         // a noexcept context this will std::terminate anyhow, which is
71         // approximately the same as 'assert' but with better information.
72         try
73         {
74             throw std::logic_error(
75                 "fdio_completion started with another await already pending!");
76         }
77         catch (...)
78         {
79             std::terminate();
80         }
81     }
82 
83     l.unlock();
84 
85     // Schedule the timeout
86     if (fdioInstance.timeout != event_t::time_resolution::zero())
87     {
88         static auto eventHandler =
89             [](sd_event_source*, uint64_t, void* data) noexcept {
90                 static_cast<fdio*>(data)->handleTimeout();
91                 return 0;
92             };
93 
94         try
95         {
96             source = fdioInstance.event_loop().add_oneshot_timer(
97                 eventHandler, &fdioInstance, fdioInstance.timeout);
98         }
99         catch (...)
100         {
101             error(std::current_exception());
102         }
103     }
104 }
105 
106 } // namespace fdio_ns
107 
108 } // namespace sdbusplus::async
109