uvcc
libuv C++ bindings
loop.hpp
1 
2 #ifndef UVCC_LOOP__HPP
3 #define UVCC_LOOP__HPP
4 
5 #include "uvcc/debug.hpp"
6 #include "uvcc/utility.hpp"
7 
8 #include <cstddef> // offsetof
9 #include <uv.h>
10 
11 #include <functional> // function bind placeholders::
12 #include <type_traits> // is_standard_layout enable_if is_convertible
13 #include <utility> // swap() forward()
14 #include <exception> // uncaught_exception()
15 #include <stdexcept> // runtime_error logic_error
16 
17 
18 namespace uv
19 {
20 
21 
22 class handle;
23 
24 
25 /*! \defgroup doxy_group__loop Event loop
26  \brief The I/O event loop.
27  \sa libuv API documentation: [the I/O loop](http://docs.libuv.org/en/v1.x/design.html#the-i-o-loop). */
28 
29 /*! \ingroup doxy_group__loop
30  \brief The I/O event loop class.
31  \details All event loops (including the default one) are the instances of this class.
32  \sa libuv API documentation: [`uv_loop_t`](http://docs.libuv.org/en/v1.x/loop.html#uv-loop-t-event-loop). */
33 class loop
34 {
35  //! \cond
36  friend class handle;
37  friend class fs;
38  friend class getaddrinfo;
39  friend class getnameinfo;
40  template< typename > friend class work;
41  //! \endcond
42 
43 public: /*types*/
44  using uv_t = ::uv_loop_t;
45  using on_destroy_t = std::function< void(void *_data) >;
46  /*!< \brief The function type of the callback called when the loop instance is about to be destroyed. */
47  using on_exit_t = std::function< void(loop _loop) >;
48  /*!< \brief The function type of the callback called after the loop exit. */
49  template< typename... _Args_ >
50  using on_walk_t = std::function< void(handle _handle, _Args_&&... _args) >;
51  /*!< \brief The function type of the callback called by the `walk()` function.
52  \sa libuv API documentation: [`uv_walk_cb`](http://docs.libuv.org/en/v1.x/loop.html#c.uv_walk_cb),
53  [`uv_walk()`](http://docs.libuv.org/en/v1.x/loop.html#c.uv_walk). */
54 
55 private: /*types*/
56  class instance
57  {
58  public: /*data*/
59  mutable int uv_error = 0;
60  ref_count refs;
61  type_storage< on_destroy_t > destroy_cb_storage;
62  type_storage< on_exit_t > exit_cb_storage;
63  uv_t uv_loop_struct = { 0,};
64 
65  private: /*constructors*/
66  instance()
67  {
68  uv_error = ::uv_loop_init(&uv_loop_struct);
69  uvcc_debug_function_return("instance [0x%08tX] for loop [0x%08tX] (uv_error=%i)", (ptrdiff_t)this, (ptrdiff_t)&uv_loop_struct, uv_error);
70  }
71 
72  public: /*constructors*/
73  ~instance() noexcept(false)
74  {
75  uvcc_debug_function_enter("instance [0x%08tX] for loop [0x%08tX] (is_alive=%i)", (ptrdiff_t)this, (ptrdiff_t)&uv_loop_struct, ::uv_loop_alive(&uv_loop_struct));
76  uvcc_debug_do_if(true, {
77  uvcc_debug_log_if(true, "walk on loop [0x%08tX] destroying...", (ptrdiff_t)&uv_loop_struct);
78  debug::print_loop_handles(&uv_loop_struct);
79  });
80 
81  uv_error = ::uv_loop_close(&uv_loop_struct);
82  auto loop_closed = (uv_error == 0);
83  uvcc_debug_condition(loop_closed, "loop [0x%08tX] (is_alive=%i)", (ptrdiff_t)&uv_loop_struct, ::uv_loop_alive(&uv_loop_struct));
84 
85  if (!loop_closed) // this may be because of:
86  {
87  // 1) there are handles associated with the loop that are in deed not closed by the time the loop instance destroying
88  unsigned num_open_handles = 0;
89  ::uv_walk(
90  &uv_loop_struct,
91  [](::uv_handle_t *_h, void *_n){ if (!::uv_is_closing(_h)) ++*static_cast< unsigned* >(_n); },
92  &num_open_handles
93  );
94  uvcc_debug_condition(num_open_handles == 0, "loop [0x%08tX] (num_open_handles=%u)", (ptrdiff_t)&uv_loop_struct, num_open_handles);
95  if (num_open_handles)
96  {
97  // it is not a proper circumstances for the libuv loop to be closed
98  // but the uvcc loop instance is destroying because of some weird things;
99  // due to loop booking all the uvcc-managed handles are closed before the loop which they are associated with;
100  // so, if this is a consequence of some exception, try to not make the bad situation even worse
101  if (std::uncaught_exception())
102  {
103  uvcc_debug_log_if(true, "loop [0x%08tX] is being destroyed during stack unwinding", (ptrdiff_t)&uv_loop_struct);
104  return;
105  }
106  else
107  throw std::logic_error(__PRETTY_FUNCTION__);
108  }
109  // 2) there are registered callbacks from closed handles (that should be nullptrs by the way),
110  // or some libuv internal requests; so, simply try to dispose of them
111  do {
112  uvcc_debug_log_if(true, "try at loop [0x%08tX] premortal one shot nonblocking run", (ptrdiff_t)&uv_loop_struct);
113  uv_error = ::uv_run(&uv_loop_struct, UV_RUN_NOWAIT);
114  } while (uv_error);
115 
116  uv_error = ::uv_loop_close(&uv_loop_struct);
117  loop_closed = (uv_error == 0);
118  uvcc_debug_condition(loop_closed, "loop [0x%08tX] (is_alive=%i)", (ptrdiff_t)&uv_loop_struct, ::uv_loop_alive(&uv_loop_struct));
119  if (!loop_closed) throw std::runtime_error(__PRETTY_FUNCTION__);
120  }
121  }
122 
123  instance(const instance&) = delete;
124  instance& operator =(const instance&) = delete;
125 
126  instance(instance&&) = delete;
127  instance& operator =(instance&&) = delete;
128 
129  private: /*functions*/
130  void destroy()
131  {
132  auto &destroy_cb = destroy_cb_storage.value();
133  if (destroy_cb) destroy_cb(uv_loop_struct.data);
134  delete this;
135  }
136 
137  public: /*interface*/
138  static uv_t* create() { return &(new instance())->uv_loop_struct; }
139 
140  constexpr static instance* from(uv_t *_uv_loop) noexcept
141  {
142  static_assert(std::is_standard_layout< instance >::value, "not a standard layout type");
143  return reinterpret_cast< instance* >(reinterpret_cast< char* >(_uv_loop) - offsetof(instance, uv_loop_struct));
144  }
145 
146  void ref()
147  {
148  uvcc_debug_function_enter("loop [0x%08tX]", (ptrdiff_t)&uv_loop_struct);
149  refs.inc();
150  }
151  void unref()
152  {
153  uvcc_debug_function_enter("loop [0x%08tX]", (ptrdiff_t)&uv_loop_struct);
154  auto nrefs = refs.dec();
155  uvcc_debug_condition(nrefs == 0, "loop [0x%08tX] (nrefs=%li)", (ptrdiff_t)&uv_loop_struct, nrefs);
156  if (nrefs == 0) destroy();
157  }
158  };
159  //! \cond
160  friend typename loop::instance* debug::instance<>(loop&) noexcept;
161  //! \endcond
162 
163 private: /*data*/
164  uv_t *uv_loop;
165 
166 private: /*constructors*/
167  explicit loop(uv_t *_uv_loop)
168  {
169  if (_uv_loop) instance::from(_uv_loop)->ref();
170  uv_loop = _uv_loop;
171  }
172 
173 public: /*constructors*/
174  ~loop() { if (uv_loop) instance::from(uv_loop)->unref(); }
175 
176  /*! \brief Create a new event loop. */
177  loop() : uv_loop(instance::create()) {}
178 
179  loop(const loop &_that) : loop(_that.uv_loop) {}
180  loop& operator =(const loop &_that)
181  {
182  if (this != &_that)
183  {
184  if (_that.uv_loop) instance::from(_that.uv_loop)->ref();
185  auto t = uv_loop;
186  uv_loop = _that.uv_loop;
187  if (t) instance::from(t)->unref();
188  }
189  return *this;
190  }
191 
192  loop(loop &&_that) noexcept : uv_loop(_that.uv_loop) { _that.uv_loop = nullptr; }
193  loop& operator =(loop &&_that) noexcept
194  {
195  if (this != &_that)
196  {
197  auto t = uv_loop;
198  uv_loop = _that.uv_loop;
199  _that.uv_loop = nullptr;
200  if (t) instance::from(t)->unref();
201  }
202  return *this;
203  }
204 
205 private: /*functions*/
206  template< typename = void > static void walk_cb(::uv_handle_t*, void*);
207 
208  int uv_status(int _value) const noexcept
209  {
210  instance::from(uv_loop)->uv_error = _value;
211  return _value;
212  }
213 
214 public: /*interface*/
215  /*! \brief Returns the initialized loop that can be used as a global default loop throughout the program. */
216  /*! \internal \note This function does not need to use the libuv function
217  [`uv_default_loop()`](http://docs.libuv.org/en/v1.x/loop.html#c.uv_default_loop)
218  to create, initialize, and get the default loop instance as far as that one is just an ordinary loop
219  instance stored in the global static variable. \endinternal */
220  static loop& Default() noexcept
221  {
222  static loop default_loop;
223  return default_loop;
224  }
225 
226  void swap(loop &_that) noexcept { std::swap(uv_loop, _that.uv_loop); }
227  /*! \brief The current number of existing references to the same loop as this variable refers to. */
228  long nrefs() const noexcept { return instance::from(uv_loop)->refs.get_value(); }
229  /*! \brief The status value returned by the last executed libuv API function. */
230  int uv_status() const noexcept { return instance::from(uv_loop)->uv_error; }
231 
232  on_destroy_t& on_destroy() const noexcept { return instance::from(uv_loop)->destroy_cb_storage.value(); }
233 
234  on_exit_t& on_exit() const noexcept { return instance::from(uv_loop)->exit_cb_storage.value(); }
235 
236  /*! \details The pointer to the user-defined arbitrary data.
237  \sa libuv API documentation: [`uv_loop_t.data`](http://docs.libuv.org/en/v1.x/loop.html#c.uv_loop_t.data). */
238  void* const& data() const noexcept { return uv_loop->data; }
239  void* & data() noexcept { return uv_loop->data; }
240 
241  /*! \brief Set additional loop options.
242  \sa libuv API documentation: [`uv_loop_configure()`](http://docs.libuv.org/en/v1.x/loop.html#c.uv_loop_configure). */
243  template< typename... _Args_ >
244  int configure(::uv_loop_option _opt, _Args_&&... _args)
245  {
246  return uv_status(::uv_loop_configure(uv_loop, _opt, std::forward< _Args_ >(_args)...));
247  }
248 
249  /*! \brief Go into a loop and process events and their callbacks with the current thread.
250  \details The function acts and returns depending on circumstances which processing is defined by the `_mode` argument.
251  \sa libuv API documentation: [`uv_run()`](http://docs.libuv.org/en/v1.x/loop.html#c.uv_run),
252  [`uv_run_mode`](http://docs.libuv.org/en/v1.x/loop.html#c.uv_run_mode).
253  \note If you start a loop with this function within a callback executed by another loop the first one will
254  be "blocked" until the second started loop ends and the function returns. */
255  int run(::uv_run_mode _mode)
256  {
257  auto uv_ret = uv_status(::uv_run(uv_loop, _mode));
258 
259  uvcc_debug_do_if(true, {
260  uvcc_debug_log_if(true, "walk on loop [0x%08tX] (is_alive=%i) exiting (uv_error=%i)...", (ptrdiff_t)uv_loop, ::uv_loop_alive(uv_loop), uv_ret);
261  debug::print_loop_handles(uv_loop);
262  });
263 
264  auto &exit_cb = instance::from(uv_loop)->exit_cb_storage.value();
265  if (exit_cb) exit_cb(loop(uv_loop));
266 
267  return uv_ret;
268  }
269 
270  /*! \brief Stop the event loop.
271  \sa libuv API documentation: [`uv_stop()`](http://docs.libuv.org/en/v1.x/loop.html#c.uv_stop). */
272  void stop() { ::uv_stop(uv_loop); }
273 
274  /*! \brief Returns non-zero if there are active handles or request in the loop. */
275  int is_alive() const noexcept { return uv_status(::uv_loop_alive(uv_loop)); }
276 
277  /*! \details Get backend file descriptor.
278  \sa libuv API documentation: [`uv_backend_fd()`](http://docs.libuv.org/en/v1.x/loop.html#c.uv_backend_fd). */
279  int backend_fd() const noexcept { return ::uv_backend_fd(uv_loop); }
280  /*! \brief Get the poll timeout. The return value is in _milliseconds_, or \b -1 for no timeout. */
281  int backend_timeout() const noexcept { return ::uv_backend_timeout(uv_loop); }
282  /*! \details Return the current timestamp in _milliseconds_.
283  \sa libuv API documentation: [`uv_now()`](http://docs.libuv.org/en/v1.x/loop.html#c.uv_now). */
284  uint64_t now() const noexcept { return ::uv_now(uv_loop); }
285 
286  /*! \details Update the event loop’s concept of “now”.
287  \sa libuv API documentation: [`uv_update_time()`](http://docs.libuv.org/en/v1.x/loop.html#c.uv_update_time). */
288  void update_time() noexcept { ::uv_update_time(uv_loop); }
289 
290  /*! \details Walk the list of active handles referenced by the loop: for each handle `_walk_cb`
291  will be executed with the given `_args`.
292  \note All arguments are copied (or moved) to the callback function object.
293  For passing arguments by reference when some callback parameters are used as output ones,
294  wrap corresponding arguments with `std::ref()` or pass them through raw pointer parameters. */
295  template< class _Func_, typename... _Args_,
296  typename = std::enable_if_t< std::is_convertible< _Func_, on_walk_t< _Args_&&... > >::value >
297  >
298  void walk(_Func_&& _walk_cb, _Args_&&... _args)
299  {
300  std::function< void(handle) > cb{ std::bind(std::forward< _Func_ >(_walk_cb), std::placeholders::_1, std::forward< _Args_ >(_args)...) };
301  if (cb) ::uv_walk(uv_loop, walk_cb, &cb);
302  }
303 
304 public: /*conversion operators*/
305  explicit operator const uv_t*() const noexcept { return uv_loop; }
306  explicit operator uv_t*() noexcept { return uv_loop; }
307 
308  explicit operator bool() const noexcept { return (uv_status() >= 0); } /*!< \brief Equivalent to `(uv_status() >= 0)`. */
309 };
310 
311 
312 }
313 
314 
315 #include "uvcc/handle-base.hpp"
316 
317 
318 namespace uv
319 {
320 template< typename >
321 void loop::walk_cb(::uv_handle_t *_uv_handle, void *_arg)
322 {
323  static_cast< std::function< void(handle) >* >(_arg)->operator()(handle(_uv_handle));
324 }
325 }
326 
327 
328 namespace std
329 {
330 
331 //! \ingroup doxy_group__loop
332 template<> inline void swap(uv::loop &_this, uv::loop &_that) noexcept { _this.swap(_that); }
333 
334 }
335 
336 
337 #endif
Namespace for all uvcc definitions.
Definition: buffer.hpp:17
void stop()
Stop the event loop.
Definition: loop.hpp:272
int backend_timeout() const noexcept
Get the poll timeout. The return value is in milliseconds, or -1 for no timeout.
Definition: loop.hpp:281
A wrapper providing the feature of being a standard layout type for the given type _T_...
Definition: utility.hpp:475
long nrefs() const noexcept
The current number of existing references to the same loop as this variable refers to...
Definition: loop.hpp:228
void update_time() noexcept
Definition: loop.hpp:288
int uv_status() const noexcept
The status value returned by the last executed libuv API function.
Definition: loop.hpp:230
int is_alive() const noexcept
Returns non-zero if there are active handles or request in the loop.
Definition: loop.hpp:275
int configure(::uv_loop_option _opt, _Args_ &&... _args)
Set additional loop options.
Definition: loop.hpp:244
loop()
Create a new event loop.
Definition: loop.hpp:177
The base class for the libuv handles.
Definition: handle-base.hpp:33
static loop & Default() noexcept
Returns the initialized loop that can be used as a global default loop throughout the program...
Definition: loop.hpp:220
uint64_t now() const noexcept
Definition: loop.hpp:284
int run(::uv_run_mode _mode)
Go into a loop and process events and their callbacks with the current thread.
Definition: loop.hpp:255
operator bool() const noexcept
Equivalent to (uv_status() >= 0).
Definition: loop.hpp:308
The I/O event loop class.
Definition: loop.hpp:33
A reference counter with atomic increment/decrement.
Definition: utility.hpp:229
void walk(_Func_ &&_walk_cb, _Args_ &&... _args)
Definition: loop.hpp:298
int backend_fd() const noexcept
Definition: loop.hpp:279
void *const & data() const noexcept
Definition: loop.hpp:238