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