Fcitx
event_libuv.cpp
1 /*
2  * SPDX-FileCopyrightText: 2017-2017 Henry Hu
3  * henry.hu.sh@gmail.com
4  *
5  * SPDX-License-Identifier: LGPL-2.1-or-later
6  *
7  */
8 
9 #include "event_libuv.h"
10 #include <cstdint>
11 #include <cstdlib>
12 #include <ctime>
13 #include <exception>
14 #include <functional>
15 #include <memory>
16 #include <utility>
17 #include <vector>
18 #include <uv.h>
19 #include "event_p.h"
20 #include "eventloopinterface.h"
21 #include "log.h"
22 #include "trackableobject.h"
23 
24 #define FCITX_LIBUV_DEBUG() FCITX_LOGC(::fcitx::libuv_logcategory, Debug)
25 
26 namespace fcitx {
27 
28 std::unique_ptr<EventLoopInterface> createDefaultEventLoop() {
29  return std::make_unique<EventLoopLibUV>();
30 }
31 
32 const char *defaultEventLoopImplementation() { return "libuv"; }
33 
34 FCITX_DEFINE_LOG_CATEGORY(libuv_logcategory, "libuv");
35 
36 static int IOEventFlagsToLibUVFlags(IOEventFlags flags) {
37  int result = 0;
38  if (flags & IOEventFlag::In) {
39  result |= UV_READABLE;
40  }
41  if (flags & IOEventFlag::Out) {
42  result |= UV_WRITABLE;
43  }
44  if (flags & IOEventFlag::Hup) {
45  result |= UV_DISCONNECT;
46  }
47  return result;
48 }
49 
50 static IOEventFlags LibUVFlagsToIOEventFlags(int flags) {
51  return ((flags & UV_READABLE) ? IOEventFlag::In : IOEventFlags()) |
52  ((flags & UV_WRITABLE) ? IOEventFlag::Out : IOEventFlags()) |
53  ((flags & UV_DISCONNECT) ? IOEventFlag::Hup : IOEventFlags());
54 }
55 
56 void IOEventCallback(uv_poll_t *handle, int status, int events);
57 void TimeEventCallback(uv_timer_t *handle);
58 void PostEventCallback(uv_prepare_t *handle);
59 
60 void AsyncEventCallback(uv_async_t *handle) {
61  auto *source = static_cast<LibUVSourceAsync *>(
62  static_cast<LibUVSourceBase *>(handle->data));
63 
64  if (!source->isEnabled()) {
65  return;
66  }
67 
68  try {
69  auto sourceRef = source->watch();
70  if (source->isOneShot()) {
71  source->setEnabled(false);
72  }
73  auto callback = source->callback_;
74  auto ret = (*callback)(source);
75  if (sourceRef.isValid()) {
76  if (!ret) {
77  source->setEnabled(false);
78  }
79  }
80  } catch (const std::exception &e) {
81  // some abnormal things threw{
82  FCITX_FATAL() << e.what();
83  }
84 }
85 
86 UVLoop::~UVLoop() {
87  // Close and detach all handle.
88  uv_walk(
89  &loop_,
90  [](uv_handle_t *handle, void *) {
91  if (handle && !uv_is_closing(handle)) {
92  if (handle->data) {
93  static_cast<LibUVSourceBase *>(handle->data)->cleanup();
94  }
95  }
96  },
97  nullptr);
98  int r = uv_loop_close(&loop_);
99  FCITX_DEBUG() << "UVLoop close: " << r;
100  if (r == 0) {
101  return;
102  }
103  do {
104  r = uv_run(&loop_, UV_RUN_ONCE);
105  } while (r != 0);
106  // Now we're safe.
107  r = uv_loop_close(&loop_);
108  FCITX_DEBUG() << "UVLoop close r2: " << r;
109 }
110 
111 bool LibUVSourceTime::setup(uv_loop_t *loop, uv_timer_t *timer) {
112  if (int err = uv_timer_init(loop, timer); err < 0) {
113  FCITX_LIBUV_DEBUG() << "Failed to init timer with error: " << err;
114  return false;
115  }
116  auto curr = now(clock_);
117  uint64_t timeout = time_ > curr ? (time_ - curr) : 0;
118  // libuv is milliseconds, ceil towards 1ms.
119  timeout = timeout / 1000 + (timeout % 1000 != 0);
120  if (int err = uv_timer_start(timer, &TimeEventCallback, timeout, 0);
121  err < 0) {
122  FCITX_LIBUV_DEBUG() << "Failed to start timer with error: " << err;
123  return false;
124  }
125  return true;
126 }
127 
128 bool LibUVSourcePost::setup(uv_loop_t *loop, uv_prepare_t *prepare) {
129  if (int err = uv_prepare_init(loop, prepare); err < 0) {
130  FCITX_LIBUV_DEBUG() << "Failed to init prepare with error: " << err;
131  return false;
132  }
133  if (int err = uv_prepare_start(prepare, &PostEventCallback); err < 0) {
134  FCITX_LIBUV_DEBUG() << "Failed to start prepare with error: " << err;
135  return false;
136  }
137  return true;
138 }
139 
140 bool LibUVSourceAsync::setup(uv_loop_t *loop, uv_async_t *async) {
141  if (int err = uv_async_init(loop, async, &AsyncEventCallback); err < 0) {
142  FCITX_LIBUV_DEBUG() << "Failed to init async with error: " << err;
143  return false;
144  }
145  return true;
146 }
147 
149  uv_async_send(reinterpret_cast<uv_async_t *>(handle_));
150 }
151 
152 bool LibUVSourceIO::setup(uv_loop_t *loop, uv_poll_t *poll) {
153  if (int err = uv_poll_init(loop, poll, fd_); err < 0) {
154  FCITX_LIBUV_DEBUG()
155  << "Failed to init poll for fd: " << fd_ << " with error: " << err;
156  return false;
157  }
158  const auto flags = IOEventFlagsToLibUVFlags(flags_);
159  if (int err = uv_poll_start(poll, flags, &IOEventCallback); err < 0) {
160  FCITX_LIBUV_DEBUG() << "Failed to start poll with error: " << err;
161  return false;
162  }
163  return true;
164 }
165 
166 EventLoopLibUV::EventLoopLibUV() : loop_(std::make_shared<UVLoop>()) {}
167 
168 const char *EventLoopLibUV::implementation() const { return "libuv"; }
169 
171  return static_cast<uv_loop_t *>(*loop_);
172 }
173 
175  int r = uv_run(*loop_, UV_RUN_DEFAULT);
176  for (auto iter = exitEvents_.begin(); iter != exitEvents_.end();) {
177  if (auto *event = iter->get()) {
178  if (event->isEnabled()) {
179  try {
180  if (event->isOneShot()) {
181  event->setEnabled(false);
182  }
183  event->callback_(event);
184  } catch (const std::exception &e) {
185  // some abnormal things threw
186  FCITX_FATAL() << e.what();
187  }
188  }
189  }
190  if (!iter->isValid()) {
191  iter = exitEvents_.erase(iter);
192  } else {
193  ++iter;
194  }
195  }
196  return r >= 0;
197 }
198 
199 void EventLoopLibUV::exit() { uv_stop(*loop_); }
200 
201 void IOEventCallback(uv_poll_t *handle, int status, int events) {
202  auto *source = static_cast<LibUVSourceIO *>(
203  static_cast<LibUVSourceBase *>(handle->data));
204  auto sourceRef = source->watch();
205  try {
206  if (source->isOneShot()) {
207  source->setEnabled(false);
208  }
209  auto flags = LibUVFlagsToIOEventFlags(events);
210  if (status < 0) {
211  flags |= IOEventFlag::Err;
212  }
213  auto callback = source->callback_;
214  bool ret = (*callback)(source, source->fd(), flags);
215  if (sourceRef.isValid()) {
216  if (!ret) {
217  source->setEnabled(false);
218  }
219  }
220  } catch (const std::exception &e) {
221  // some abnormal things threw
222  FCITX_FATAL() << e.what();
223  }
224 }
225 
226 std::unique_ptr<EventSourceIO>
227 EventLoopLibUV::addIOEvent(int fd, IOEventFlags flags, IOCallback callback) {
228  auto source =
229  std::make_unique<LibUVSourceIO>(std::move(callback), loop_, fd, flags);
230  return source;
231 }
232 
233 void TimeEventCallback(uv_timer_t *handle) {
234  auto *source = static_cast<LibUVSourceTime *>(
235  static_cast<LibUVSourceBase *>(handle->data));
236 
237  try {
238  auto sourceRef = source->watch();
239  if (source->isOneShot()) {
240  source->setEnabled(false);
241  }
242  auto callback = source->callback_;
243  bool ret = (*callback)(source, source->time());
244  if (sourceRef.isValid()) {
245  if (!ret) {
246  source->setEnabled(false);
247  }
248  if (source->isEnabled()) {
249  source->resetEvent();
250  }
251  }
252  } catch (const std::exception &e) {
253  // some abnormal things threw
254  FCITX_FATAL() << e.what();
255  }
256 }
257 
258 std::unique_ptr<EventSourceTime>
259 EventLoopLibUV::addTimeEvent(clockid_t clock, uint64_t usec, uint64_t accuracy,
260  TimeCallback callback) {
261  auto source = std::make_unique<LibUVSourceTime>(std::move(callback), loop_,
262  usec, clock, accuracy);
263  return source;
264 }
265 
266 std::unique_ptr<EventSource>
267 EventLoopLibUV::addExitEvent(EventCallback callback) {
268  auto source = std::make_unique<LibUVSourceExit>(std::move(callback));
269  exitEvents_.push_back(source->watch());
270  return source;
271 }
272 
273 std::unique_ptr<EventSource>
274 EventLoopLibUV::addDeferEvent(EventCallback callback) {
275  return addTimeEvent(
276  CLOCK_MONOTONIC, 0, 0,
277  [callback = std::move(callback)](EventSourceTime *source, uint64_t) {
278  return callback(source);
279  });
280 }
281 
282 void PostEventCallback(uv_prepare_t *handle) {
283  auto *source = static_cast<LibUVSourcePost *>(
284  static_cast<LibUVSourceBase *>(handle->data));
285 
286  try {
287  auto sourceRef = source->watch();
288  if (source->isOneShot()) {
289  source->setEnabled(false);
290  }
291  auto callback = source->callback_;
292  auto ret = (*callback)(source);
293  if (sourceRef.isValid()) {
294  if (!ret) {
295  source->setEnabled(false);
296  }
297  }
298  } catch (const std::exception &e) {
299  // some abnormal things threw{
300  FCITX_FATAL() << e.what();
301  }
302 }
303 
304 std::unique_ptr<EventSource>
305 EventLoopLibUV::addPostEvent(EventCallback callback) {
306  auto source = std::make_unique<LibUVSourcePost>(std::move(callback), loop_);
307  return source;
308 }
309 
310 std::unique_ptr<EventSourceAsync>
311 EventLoopLibUV::addAsyncEvent(EventCallback callback) {
312  auto source =
313  std::make_unique<LibUVSourceAsync>(std::move(callback), loop_);
314  return source;
315 }
316 
317 } // namespace fcitx
Utitliy classes for statically tracking the life of a object.
Definition: action.cpp:17
void * nativeHandle() override
Return the internal native handle to the event loop.
void send() override
Trigger the event from other thread.
void exit() override
Quit event loop.
bool exec() override
Execute event loop.
Log utilities.
const char * implementation() const override
Return a static implementation name of event loop.