uvcc
libuv C++ bindings
handle-fs.hpp
1 
2 #ifndef UVCC_HANDLE_FS__HPP
3 #define UVCC_HANDLE_FS__HPP
4 
5 #include "uvcc/utility.hpp"
6 #include "uvcc/handle-base.hpp"
7 #include "uvcc/handle-io.hpp"
8 #include "uvcc/loop.hpp"
9 
10 #include <uv.h>
11 
12 #ifdef _WIN32
13 #include <io.h> // _telli64()
14 #else
15 #include <unistd.h> // lseek64()
16 #endif
17 
18 #include <functional> // function
19 #include <string> // string
20 #include <utility> // move()
21 
22 
23 namespace uv
24 {
25 
26 
27 /*! \ingroup doxy_group__handle
28  \brief The open file handle. */
29 class file : public io
30 {
31  //! \cond
32  friend class handle::uv_interface;
33  friend class handle::instance< file >;
34  friend class fs;
35  //! \endcond
36 
37 public: /*types*/
38  using uv_t = ::uv_fs_t;
39  using on_open_t = std::function< void(file) >;
40  /*!< \brief The function type of the callback called after the asynchronous file open/create operation has been completed. */
41 
42 protected: /*types*/
43  //! \cond internals
44  //! \addtogroup doxy_group__internals
45  //! \{
46 
47  struct properties : io::properties
48  {
49  on_open_t open_cb;
50  struct
51  {
52  ::uv_buf_t uv_buf_struct = { 0,};
53  ::uv_fs_t uv_req_struct = { 0,};
54  } rd;
55  std::size_t write_queue_size = 0;
56  int is_closing = 0;
57  };
58 
59  struct uv_interface : handle::uv_fs_interface, io::uv_interface
60  {
61  int is_closing(void *_uv_fs) const noexcept override
62  { return instance::from(_uv_fs)->properties().is_closing; }
63 
64  std::size_t write_queue_size(void *_uv_handle) const noexcept override
65  { return instance::from(_uv_handle)->properties().write_queue_size; }
66 
67  int read_start(void *_uv_handle, int64_t _offset) const noexcept override
68  {
69  auto instance_ptr = instance::from(_uv_handle);
70  auto &properties = instance_ptr->properties();
71 
72  properties.rd.uv_req_struct.data = instance_ptr;
73 
74  if (_offset < 0)
75  {
76 #ifdef _WIN32
77  /*! \sa Windows: [`_tell()`, `_telli64()`](https://msdn.microsoft.com/en-us/library/c3kc5e7a.aspx). */
78  _offset = _telli64(instance_ptr->uv_handle_struct.result);
79 #else
80  _offset = lseek64(instance_ptr->uv_handle_struct.result, 0, SEEK_CUR);
81 #endif
82  }
83  properties.rdoffset = _offset;
84 
85  return file_read(instance_ptr);
86  }
87 
88  int read_stop(void *_uv_handle) const noexcept override { return 0; }
89  };
90 
91  //! \}
92  //! \endcond
93 
94 private: /*types*/
95  using instance = handle::instance< file >;
96 
97 protected: /*constructors*/
98  //! \cond
99  explicit file(uv::loop::uv_t *_loop, ::uv_file _fd, const char *_path)
100  {
101  uv_handle = instance::create();
102  static_cast< uv_t* >(uv_handle)->loop = _loop;
103  static_cast< uv_t* >(uv_handle)->result = _fd;
104  static_cast< uv_t* >(uv_handle)->path = _path;
105  if (_fd < 0) instance::from(uv_handle)->properties().is_closing = 1;
106  instance::from(uv_handle)->book_loop();
107  }
108 
109  explicit file(uv_t *_uv_handle) : io(static_cast< io::uv_t* >(_uv_handle)) {}
110  //! \endcond
111 
112 public: /*constructors*/
113  ~file() = default;
114 
115  file(const file&) = default;
116  file& operator =(const file&) = default;
117 
118  file(file&&) noexcept = default;
119  file& operator =(file&&) noexcept = default;
120 
121  /*! \name File handle constructors - open and possibly create a file:
122  \sa libuv API documentation: [`uv_fs_open()`](http://docs.libuv.org/en/v1.x/fs.html#c.uv_fs_open).
123  \sa Linux: [`open()`](http://man7.org/linux/man-pages/man2/open.2.html).\n
124  Windows: [`_open()`](https://msdn.microsoft.com/en-us/library/z0kc8e3z.aspx).
125 
126  The file descriptor will be closed automatically when the file handle reference count has became zero. */
127  //! \{
128  /*! \brief Open and possibly create a file _synchronously_. */
129  file(uv::loop &_loop, const char *_path, int _flags, int _mode)
130  {
131  uv_handle = instance::create();
132 
133  auto uv_ret = uv_status(::uv_fs_open(
134  static_cast< uv::loop::uv_t* >(_loop), static_cast< uv_t* >(uv_handle),
135  _path, _flags, _mode,
136  nullptr
137  ));
138  if (uv_ret >= 0) instance::from(uv_handle)->book_loop();
139  }
140  /*! \brief Open and possibly create a file _asynchronously_.
141  \note If the `_open_cb` callback is empty the operation is completed _synchronously_. */
142  file(uv::loop &_loop, const char *_path, int _flags, int _mode, const on_open_t &_open_cb)
143  {
144  if (!_open_cb)
145  {
146  new (this) file(_loop, _path, _flags, _mode);
147  return;
148  }
149 
150  uv_handle = instance::create();
151 
152  auto instance_ptr = instance::from(uv_handle);
153  instance_ptr->ref();
154 
155  instance_ptr->properties().open_cb = _open_cb;
156 
157  uv_status(0);
158  auto uv_ret = ::uv_fs_open(
159  static_cast< uv::loop::uv_t* >(_loop), static_cast< uv_t* >(uv_handle),
160  _path, _flags, _mode,
161  open_cb
162  );
163  if (uv_ret >= 0)
164  instance_ptr->book_loop();
165  else
166  {
167  uv_status(uv_ret);
168  instance_ptr->unref();
169  }
170  }
171  /*! \brief Create a file object from an existing file descriptor. */
172  file(uv::loop &_loop, ::uv_file _fd) : file(static_cast< uv::loop::uv_t* >(_loop), _fd, nullptr) {}
173  //! \}
174 
175 private: /*functions*/
176  template< typename = void > static void open_cb(::uv_fs_t*);
177  template< typename = void > static void read_cb(::uv_fs_t*);
178 
179  static int file_read(instance *_instance_ptr)
180  {
181  auto &properties = _instance_ptr->properties();
182 
183  io_alloc_cb(&_instance_ptr->uv_handle_struct, 65536, &properties.rd.uv_buf_struct);
184 
185  return ::uv_fs_read(
186  _instance_ptr->uv_handle_struct.loop, &properties.rd.uv_req_struct,
187  _instance_ptr->uv_handle_struct.result,
188  &properties.rd.uv_buf_struct, 1,
189  properties.rdoffset,
190  read_cb
191  );
192  }
193 
194 public: /*interface*/
195  /*! \brief The amount of bytes waiting to be written to the file. */
196  std::size_t write_queue_size() const noexcept { return instance::from(uv_handle)->properties().write_queue_size; }
197 
198  /*! \brief Get the cross platform representation of the file handle.
199  \details On Windows this function returns _a C run-time file descriptor_ which differs from the
200  _operating-system file handle_ that is returned by `handle::fileno()` function.
201  On Unix-like systems both functions return the same value.
202  \sa Windows: [`_open_osfhandle()`](https://msdn.microsoft.com/en-us/library/bdts1c9x.aspx). */
203  ::uv_file fd() const noexcept { return static_cast< uv_t* >(uv_handle)->result; }
204 
205  /*! \brief The file path.
206  \sa libuv API documentation: [`uv_fs_t.path`](http://docs.libuv.org/en/v1.x/fs.html#c.uv_fs_t.path). */
207  const char* path() const noexcept { return static_cast< uv_t* >(uv_handle)->path; }
208 
209 public: /*conversion operators*/
210  explicit operator const uv_t*() const noexcept { return static_cast< const uv_t* >(uv_handle); }
211  explicit operator uv_t*() noexcept { return static_cast< uv_t* >(uv_handle); }
212 };
213 
214 template< typename >
215 void file::open_cb(::uv_fs_t *_uv_handle)
216 {
217  auto instance_ptr = instance::from(_uv_handle);
218  instance_ptr->uv_error = _uv_handle->result;
219 
220  ref_guard< instance > unref_req(*instance_ptr, adopt_ref);
221 
222  auto &open_cb = instance_ptr->properties().open_cb;
223  if (open_cb) open_cb(file(_uv_handle));
224 }
225 
226 template< typename >
227 void file::read_cb(::uv_fs_t *_uv_req)
228 {
229  auto instance_ptr = static_cast< instance* >(_uv_req->data);
230  auto &properties = instance_ptr->properties();
231 
232  ssize_t nread = _uv_req->result == 0 ? UV_EOF : _uv_req->result;
233  if (nread < 0) // on error or EOF release the unused buffer and replace it with a null-initialized structure
234  {
235  buffer::instance::from(buffer::instance::uv_buf::from(properties.rd.uv_buf_struct.base))->unref();
236  properties.rd.uv_buf_struct = ::uv_buf_init(nullptr, 0);
237  }
238 
239  io_read_cb(&instance_ptr->uv_handle_struct, nread , &properties.rd.uv_buf_struct, nullptr);
240 
241  ::uv_fs_req_cleanup(_uv_req);
242 
243  switch (properties.rdcmd_state)
244  {
245  case rdcmd::UNKNOWN:
246  case rdcmd::STOP:
247  case rdcmd::PAUSE:
248  break;
249  case rdcmd::START:
250  case rdcmd::RESUME:
251  {
252  instance_ptr->uv_error = 0;
253  auto uv_ret = file_read(instance_ptr);
254  if (uv_ret < 0) instance_ptr->uv_error = uv_ret;
255  }
256  break;
257  }
258 }
259 
260 
261 
262 /*! \ingroup doxy_group__handle
263  \brief FS Event handle.
264  \sa libuv API documentation: [`uv_fs_event_t` — FS Event handle](http://docs.libuv.org/en/v1.x/fs_event.html#uv-fs-event-t-fs-event-handle). */
265 class fs_event : public handle
266 {
267  //! \cond
268  friend class handle::uv_interface;
269  friend class handle::instance< fs_event >;
270  //! \endcond
271 
272 public: /*types*/
273  using uv_t = ::uv_fs_event_t;
274  using on_fs_event_t = std::function< void(fs_event _handle, const char *_filename, int _events) >;
275  /*!< \brief The function type of the FS event callback.
276  \sa libuv API documentation: [`uv_fs_event_cb`](http://docs.libuv.org/en/v1.x/fs_event.html#c.uv_fs_event_cb),
277  [`uv_fs_event`](http://docs.libuv.org/en/v1.x/fs_event.html#c.uv_fs_event). */
278 
279 protected: /*types*/
280  //! \cond internals
281  //! \addtogroup doxy_group__internals
282  //! \{
283 
284  enum class opcmd { UNKNOWN, STOP, START };
285 
286  struct properties : handle::properties
287  {
288  opcmd opcmd_state = opcmd::UNKNOWN;
289  int event_flags = 0;
290  std::string path;
291  on_fs_event_t fs_event_cb;
292  };
293 
294  struct uv_interface : handle::uv_handle_interface {};
295 
296  //! \}
297  //! \endcond
298 
299 private: /*types*/
300  using instance = handle::instance< fs_event >;
301 
302 private: /*functions*/
303  template < typename = void > static void fs_event_cb(::uv_fs_event_t*, const char*, int, int);
304 
305 protected: /*constructors*/
306  //! \cond
307  explicit fs_event(uv_t *_uv_handle) : handle(reinterpret_cast< handle::uv_t* >(_uv_handle)) {}
308  //! \endcond
309 
310 public: /*constructors*/
311  ~fs_event() = default;
312 
313  fs_event(const fs_event&) = default;
314  fs_event& operator =(const fs_event&) = default;
315 
316  fs_event(fs_event&&) noexcept = default;
317  fs_event& operator =(fs_event&&) noexcept = default;
318 
319  /*! \brief Create an `fs_event`. */
320  fs_event(uv::loop &_loop, int _event_flags)
321  {
322  uv_handle = instance::create();
323 
324  auto uv_ret = ::uv_fs_event_init(static_cast< uv::loop::uv_t* >(_loop), static_cast< uv_t* >(uv_handle));
325  instance::from(uv_handle)->properties().event_flags = _event_flags;
326 
327  if (uv_status(uv_ret) < 0) return;
328 
329  instance::from(uv_handle)->book_loop();
330  }
331 
332 public: /*interface*/
333  /*! \brief Set the FS path being monitored by this handle. */
334  std::string& path() const noexcept { return instance::from(uv_handle)->properties().path; }
335 
336  /*! \brief Set the FS event callback. */
337  on_fs_event_t& on_fs_event() const noexcept { return instance::from(uv_handle)->properties().fs_event_cb; }
338 
339  /*! \brief Start the handle.
340  \details Repeated call to this function results in the automatic call to `stop()` first.
341  \note On successful start this function adds an extra reference to the handle instance,
342  which is released when the counterpart function `stop()` is called.
343  \sa libuv API documentation: [`uv_fs_event_start()`](http://docs.libuv.org/en/v1.x/fs_event.html#c.uv_fs_event_start). */
344  int start() const
345  {
346  auto instance_ptr = instance::from(uv_handle);
347  auto &properties = instance_ptr->properties();
348 
349  auto opcmd_state0 = properties.opcmd_state;
350 
351  properties.opcmd_state = opcmd::START;
352  instance_ptr->ref(); // REF:START -- make sure it will exist for the future fs_event_cb() calls until stop()
353 
354  switch (opcmd_state0)
355  {
356  case opcmd::UNKNOWN:
357  case opcmd::STOP:
358  break;
359  case opcmd::START:
360  uv_status(::uv_fs_event_stop(static_cast< uv_t* >(uv_handle)));
361  instance_ptr->unref(); // UNREF:RESTART -- adjust extra reference number
362  break;
363  }
364 
365  uv_status(0);
366  auto uv_ret = ::uv_fs_event_start(static_cast< uv_t* >(uv_handle), fs_event_cb, properties.path.c_str(), properties.event_flags);
367  if (uv_ret < 0)
368  {
369  uv_status(uv_ret);
370  properties.opcmd_state = opcmd::UNKNOWN;
371  instance_ptr->unref(); // UNREF:START_FAILURE -- release the extra reference on failure
372  }
373 
374  return uv_ret;
375  }
376 
377  /*! \brief Start the handle with the given callback.
378  \details This is equivalent for
379  ```
380  fs_event.on_fs_event() = std::bind(
381  std::forward< _Cb_ >(_cb), std::placeholders::_1, std::placeholders::_2, std::forward< _Args_ >(_args)...
382  );
383  fs_event.start();
384  ```
385  \sa `fs_event::start()` */
386  template< class _Cb_, typename... _Args_, typename = std::enable_if_t< std::is_convertible<
387  decltype(std::bind(std::declval< _Cb_ >(), std::placeholders::_1, std::placeholders::_2, static_cast< _Args_&& >(std::declval< _Args_ >())...)),
389  >::value > >
390  int start(_Cb_ &&_cb, _Args_&&... _args) const
391  {
392  instance::from(uv_handle)->properties().fs_event_cb = std::bind(
393  std::forward< _Cb_ >(_cb), std::placeholders::_1, std::placeholders::_2, std::forward< _Args_ >(_args)...
394  );
395  return start();
396  }
397 
398  /*! \brief Start the handle with the given callback, watching for the specified FS path.
399  \details This is equivalent for
400  ```
401  fs_event.path() = _path;
402  fs_event.on_fs_event() = std::bind(
403  std::forward< _Cb_ >(_cb), std::placeholders::_1, std::placeholders::_2, std::forward< _Args_ >(_args)...
404  );
405  fs_event.start();
406  ```
407  \sa `fs_event::start()` */
408  template< class _Cb_, typename... _Args_, typename = std::enable_if_t< std::is_convertible<
409  decltype(std::bind(std::declval< _Cb_ >(), std::placeholders::_1, std::placeholders::_2, static_cast< _Args_&& >(std::declval< _Args_ >())...)),
411  >::value > >
412  int start(std::string _path, _Cb_ &&_cb, _Args_&&... _args) const
413  {
414  instance::from(uv_handle)->properties().path = std::move(_path);
415  instance::from(uv_handle)->properties().fs_event_cb = std::bind(
416  std::forward< _Cb_ >(_cb), std::placeholders::_1, std::placeholders::_2, std::forward< _Args_ >(_args)...
417  );
418  return start();
419  }
420 
421  /*! \brief Stop the handle, the callback will no longer be called. */
422  int stop() const noexcept
423  {
424  auto instance_ptr = instance::from(uv_handle);
425  auto &opcmd_state = instance_ptr->properties().opcmd_state;
426 
427  auto opcmd_state0 = opcmd_state;
428  opcmd_state = opcmd::STOP;
429 
430  auto uv_ret = uv_status(::uv_fs_event_stop(static_cast< uv_t* >(uv_handle)));
431 
432  switch (opcmd_state0)
433  {
434  case opcmd::UNKNOWN:
435  case opcmd::STOP:
436  break;
437  case opcmd::START:
438  instance_ptr->unref(); // UNREF:STOP -- release the reference from start()
439  break;
440  }
441 
442  return uv_ret;
443  }
444 
445 public: /*conversion operators*/
446  explicit operator const uv_t*() const noexcept { return static_cast< const uv_t* >(uv_handle); }
447  explicit operator uv_t*() noexcept { return static_cast< uv_t* >(uv_handle); }
448 };
449 
450 template< typename >
451 void fs_event::fs_event_cb(::uv_fs_event_t *_uv_handle, const char *_filename, int _events, int _status)
452 {
453  auto instance_ptr = instance::from(_uv_handle);
454  instance_ptr->uv_error = _status;
455  auto &fs_event_cb = instance_ptr->properties().fs_event_cb;
456  if (fs_event_cb) fs_event_cb(fs_event(_uv_handle), _filename, _events);
457 }
458 
459 
460 }
461 
462 
463 #endif
Namespace for all uvcc definitions.
Definition: buffer.hpp:17
int start(std::string _path, _Cb_ &&_cb, _Args_ &&... _args) const
Start the handle with the given callback, watching for the specified FS path.
Definition: handle-fs.hpp:412
The open file handle.
Definition: handle-fs.hpp:29
fs_event(uv::loop &_loop, int _event_flags)
Create an fs_event.
Definition: handle-fs.hpp:320
int stop() const noexcept
Stop the handle, the callback will no longer be called.
Definition: handle-fs.hpp:422
std::string & path() const noexcept
Set the FS path being monitored by this handle.
Definition: handle-fs.hpp:334
int start() const
Start the handle.
Definition: handle-fs.hpp:344
file(uv::loop &_loop, const char *_path, int _flags, int _mode)
Open and possibly create a file synchronously.
Definition: handle-fs.hpp:129
The base class for the libuv handles.
Definition: handle-base.hpp:33
The I/O event loop class.
Definition: loop.hpp:33
The base class for handles representing I/O endpoints: a file, TCP/UDP socket, pipe, TTY.
Definition: handle-io.hpp:25
A scoped reference counting guard.
Definition: utility.hpp:280
int start(_Cb_ &&_cb, _Args_ &&... _args) const
Start the handle with the given callback.
Definition: handle-fs.hpp:390
file(uv::loop &_loop, const char *_path, int _flags, int _mode, const on_open_t &_open_cb)
Open and possibly create a file asynchronously.
Definition: handle-fs.hpp:142
const char * path() const noexcept
The file path.
Definition: handle-fs.hpp:207
::uv_file fd() const noexcept
Get the cross platform representation of the file handle.
Definition: handle-fs.hpp:203
file(uv::loop &_loop, ::uv_file _fd)
Create a file object from an existing file descriptor.
Definition: handle-fs.hpp:172
on_fs_event_t & on_fs_event() const noexcept
Set the FS event callback.
Definition: handle-fs.hpp:337
std::size_t write_queue_size() const noexcept
The amount of bytes waiting to be written to the file.
Definition: handle-fs.hpp:196
FS Event handle.
Definition: handle-fs.hpp:265