uvcc
libuv C++ bindings
request-misc.hpp
1 
2 #ifndef UVCC_REQUEST_MISC__HPP
3 #define UVCC_REQUEST_MISC__HPP
4 
5 #include "uvcc/utility.hpp"
6 #include "uvcc/request-base.hpp"
7 
8 #include <uv.h>
9 
10 #include <functional> // function bind placeholders::
11 #include <future> // shared_future packaged_task
12 #include <type_traits> // enable_if is_convertible
13 
14 
15 namespace uv
16 {
17 
18 
19 /*! \ingroup doxy_group__request
20  \brief Work scheduling request type.
21  \sa libuv API documentation: [Thread pool work scheduling](http://docs.libuv.org/en/v1.x/threadpool.html#thread-pool-work-scheduling). */
22 template< typename _Result_ = void >
23 class work : public request
24 {
25  //! \cond
26  friend class request::instance< work >;
27  //! \endcond
28 
29 public: /*types*/
30  using uv_t = ::uv_work_t;
31  using on_request_t = std::function< void(work _request) >;
32  /*!< \brief The function type of the callback called after the work on the threadpool has been completed.
33  \details This callback is called _on the loop thread_.
34  \sa libuv API documentation: [`uv_after_work_cb`](http://docs.libuv.org/en/v1.x/threadpool.html#c.uv_after_work_cb). */
35  template< typename... _Args_ >
36  using on_work_t = std::function< _Result_(_Args_&&... _args) >;
37  /*!< \brief The function type of the task which is scheduled to be run on the thread pool.
38  \details This function is called _on the one of the threads from the thread pool_.
39  \sa libuv API documentation: [`uv_work_cb`](http://docs.libuv.org/en/v1.x/threadpool.html#c.uv_work_cb). */
40 
41 protected: /*types*/
42  //! \cond internals
43  //! \addtogroup doxy_group__internals
44  //! \{
45  struct properties : request::properties
46  {
47  std::packaged_task< _Result_() > task;
48  std::shared_future< _Result_ > result;
49  };
50  //! \}
51  //! \endcond
52 
53 private: /*types*/
54  using instance = request::instance< work >;
55 
56 protected: /*constructors*/
57  //! \cond
58  explicit work(uv_t *_uv_req) : request(reinterpret_cast< request::uv_t* >(_uv_req)) {}
59  //! \endcond
60 
61 public: /*constructors*/
62  ~work() = default;
63  work()
64  {
65  uv_req = instance::create();
66  static_cast< uv_t* >(uv_req)->type = UV_WORK;
67  }
68 
69  work(const work&) = default;
70  work& operator =(const work&) = default;
71 
72  work(work&&) noexcept = default;
73  work& operator =(work&&) noexcept = default;
74 
75 private: /*functions*/
76  template< typename = void > static void work_cb(::uv_work_t*);
77  template< typename = void > static void after_work_cb(::uv_work_t*, int);
78 
79 public: /*interface*/
80  on_request_t& on_request() const noexcept { return instance::from(uv_req)->request_cb_storage.value(); }
81 
82  /*! \brief The libuv loop that started this `work` request and where completion will be reported.
83  \details It is guaranteed that it will be a valid instance at least within the request callback. */
84  uv::loop loop() const noexcept { return uv::loop(static_cast< uv_t* >(uv_req)->loop); }
85 
86  /*! \brief Get the result of the work. */
87  std::shared_future< _Result_ >& result() const { return instance::from(uv_req)->properties().result; }
88 
89  /*! \brief Run the request. Queue the `_Task_` to the thread pool.
90  \details The given `_Task_` function is called with specified `_args` applied and is executed
91  on the one of the threads from the thread pool. Once it is completed, `on_request` callback will be called
92  on the `_loop` thread.
93  \note All arguments are copied (or moved) to the `_task` function object. For passing arguments by reference
94  (when parameters are used as output ones), wrap them with `std::ref()` or use raw pointers.
95  \sa libuv API documentation: [`uv_queue_work()`](http://docs.libuv.org/en/v1.x/threadpool.html#c.uv_queue_work). */
96  template< class _Task_, typename... _Args_,
97  typename = std::enable_if_t< std::is_convertible< _Task_, on_work_t< _Args_&&... > >::value >
98  >
99  int run(uv::loop &_loop, _Task_&& _task, _Args_&&... _args)
100  {
101  auto instance_ptr = instance::from(uv_req);
102 
103  instance_ptr->ref();
104 
105  auto &properties = instance_ptr->properties();
106  {
107  using task_t = decltype(properties.task);
108  properties.task = task_t{ std::bind(std::forward< _Task_ >(_task), std::forward< _Args_ >(_args)...) };
109  properties.result = properties.task.get_future().share();
110  }
111 
112  uv_status(0);
113  auto uv_ret = ::uv_queue_work(
114  static_cast< uv::loop::uv_t* >(_loop), static_cast< uv_t* >(uv_req),
115  work_cb<>, after_work_cb<>
116  );
117  if (uv_ret < 0)
118  {
119  uv_status(uv_ret);
120  instance_ptr->unref();
121  }
122 
123  return uv_ret;
124  }
125 
126 public: /*conversion operators*/
127  explicit operator const uv_t*() const noexcept { return static_cast< const uv_t* >(uv_req); }
128  explicit operator uv_t*() noexcept { return static_cast< uv_t* >(uv_req); }
129 };
130 
131 template< typename _Result_ >
132 template< typename >
133 void work< _Result_ >::work_cb(::uv_work_t *_uv_req)
134 {
135  auto &task = instance::from(_uv_req)->properties().task;
136  if (task.valid()) task();
137 }
138 
139 template< typename _Result_ >
140 template< typename >
141 void work< _Result_ >::after_work_cb(::uv_work_t *_uv_req, int _status)
142 {
143  auto instance_ptr = instance::from(_uv_req);
144  instance_ptr->uv_error = _status;
145 
146  ref_guard< instance > unref_req(*instance_ptr, adopt_ref);
147 
148  auto &after_work_cb = instance_ptr->request_cb_storage.value();
149  if (after_work_cb) after_work_cb(work(_uv_req));
150 }
151 
152 
153 }
154 
155 
156 #endif
Namespace for all uvcc definitions.
Definition: buffer.hpp:17
Work scheduling request type.
constexpr const adopt_ref_t adopt_ref
The tag to be used to prevent ref_guard constructor from increasing reference count of the protected ...
Definition: utility.hpp:274
The base class for the libuv requests.
int run(uv::loop &_loop, _Task_ &&_task, _Args_ &&... _args)
Run the request. Queue the _Task_ to the thread pool.
A scoped reference counting guard.
Definition: utility.hpp:280
std::shared_future< _Result_ > & result() const
Get the result of the work.
uv::loop loop() const noexcept
The libuv loop that started this work request and where completion will be reported.