Compare commits

..

278 Commits
cpp11 ... main

Author SHA1 Message Date
Michele Caini
cb15b5aa1b linter: disable avoid-c-arrays (not ready for that yet) 2024-12-05 17:00:08 +01:00
xndcn
93476594ee
doc: fix typo (uwv -> uvw) (#321) 2024-12-03 15:05:12 +01:00
Michele Caini
04cb432e72 linter: disable bugprone-easily-swappable-parameters (too big of an effort today) 2024-10-24 12:46:51 +02:00
Michele Caini
242838e3f3 *: cleanup 2024-10-24 09:28:13 +02:00
Michele Caini
0a993494e2 udp: avoid C-style arrays 2024-10-24 08:31:14 +02:00
Michele Caini
a3f2a6e3ac fs: avoid unnecessary array 2024-10-23 18:01:29 +02:00
Michele Caini
58b8b3d610 util: avoid C-style arrays 2024-10-23 10:38:32 +02:00
Michele Caini
998a86975b stream: avoid C-style arrays 2024-10-23 10:35:37 +02:00
Michele Caini
6bb7a6f291 work: cleanup 2024-10-23 10:33:42 +02:00
Michele Caini
32f06f4fe8 thread: cleanup 2024-10-23 10:33:36 +02:00
Michele Caini
f26fe9dea8 fs: avoid C-style arrays 2024-10-23 10:27:34 +02:00
Michele Caini
d5ea72a18a uv_type: cleanup 2024-10-23 10:22:03 +02:00
Michele Caini
8b2aa01b3d stream: [[nodiscard]] 2024-10-23 09:31:27 +02:00
Michele Caini
35c840ab61 lib: [[nodiscard]] 2024-10-23 09:30:21 +02:00
Michele Caini
08350199e3 request: [[nodiscard]] 2024-10-23 09:30:03 +02:00
Michele Caini
e774913db9 *: perf improvements 2024-10-23 09:28:51 +02:00
Michele Caini
949e46642a util: use std::array 2024-10-23 09:17:06 +02:00
Michele Caini
3a8c06fe5b test: minor changes 2024-10-23 08:56:22 +02:00
Michele Caini
e4bb43bce7 util: thanks msvc for accepting invalid code 2024-10-23 08:56:13 +02:00
Michele Caini
3c57dd9a98 test: minor changes 2024-10-23 08:51:15 +02:00
Michele Caini
3754bfe088 process:: suppress warning due to [[nodiscard]] 2024-10-23 08:51:09 +02:00
Michele Caini
cef9d850d8 util: [[nodiscard]] 2024-10-23 08:50:54 +02:00
Michele Caini
04a7c36e7b fs: explicit override 2024-10-23 08:42:35 +02:00
Michele Caini
9adfaf25ca util: move ctor params as possible 2024-10-23 08:39:19 +02:00
Michele Caini
8c10ef9de7 loop: explicit override 2024-10-23 08:37:03 +02:00
Michele Caini
78e410dc1a resource: [[nodiscard]] 2024-10-23 08:36:11 +02:00
Michele Caini
258ddaed5f uv_type: [[nodiscard]] 2024-10-23 08:35:37 +02:00
Michele Caini
70572c4424 handle: [[nodiscard]] 2024-10-23 08:33:58 +02:00
Michele Caini
5be31bad4c emitter: [[nodiscard]] 2024-10-23 08:33:52 +02:00
Michele Caini
d4f217f9f1 test: avoid deprecated headers 2024-10-23 08:30:45 +02:00
Michele Caini
6e0fe7b8c6 type_info: char to unsigned char conversion before using the value 2024-10-23 08:29:51 +02:00
Michele Caini
2b4a0f158b poll: instruct the linter on libuv callbacks 2024-10-23 08:29:27 +02:00
Michele Caini
34d2b00441 test: instruct the linter 2024-10-22 19:38:03 +02:00
Michele Caini
eb06aed649 fs_event: instruct the linter 2024-10-22 19:37:53 +02:00
Michele Caini
7cb57e0c46 fs: use make_unique if possible 2024-10-22 19:32:03 +02:00
Michele Caini
b771134121 clang-tidy: enable more checks 2024-10-22 19:24:19 +02:00
Michele Caini
dd63b4e225 type_info: internal changes 2024-10-22 19:19:17 +02:00
Michele Caini
b53cd6fd8b util: refine try_read 2024-10-22 09:25:15 +02:00
Michele Caini
a4657c479d util: passwd cannot be noexcept 2024-10-22 09:25:07 +02:00
Michele Caini
39c142fa56 enum: avoid using reserved identifiers 2024-10-22 08:00:46 +02:00
Michele Caini
a578b44788 build: updated workflow 2024-10-21 11:03:14 +02:00
Michele Caini
21372c17e8 build: split h/ipp/cpp and update clang-tidy config 2024-10-21 10:57:36 +02:00
Michele Caini
f70a0dbc2b cmake: a couple of fixes to the build system 2024-10-18 17:31:54 +02:00
Michele Caini
fb2f8d9eff cmake: use UVW_ to avoid polluting parent projects 2024-10-18 15:09:24 +02:00
Michele Caini
4d54eb946f iwyu: basic config to start with 2024-10-18 12:28:03 +02:00
Michele Caini
98dd44b5eb linter: let's start with something simple 2024-10-18 12:27:49 +02:00
Michele Caini
7261037df8 build: add missing \ 2024-10-17 18:36:45 +02:00
Michele Caini
0784a26081 build: rename file 2024-10-17 18:34:05 +02:00
Michele Caini
a89a4155ea build: fix typos 2024-10-17 18:22:51 +02:00
Michele Caini
25feca605d build: tools workflow (first draft, to be tested) 2024-10-08 16:40:59 +02:00
Michele Caini
4a1f1c6daa thread: set/get priority 2024-10-03 17:29:44 +02:00
Michele Caini
1678cdbddd utility: resident_set_memory 2024-10-03 16:08:01 +02:00
Michele Caini
a38f5452d8 build: prepare to work with libuv 1.49 2024-10-03 09:53:46 +02:00
Ruurd Adema
44541bd1bd
event: make error_event copy assignable (#316) 2024-09-05 09:36:57 +02:00
brenfwd
a4436b746e
build(meson): bump libuv to 1.48.0 (#314) 2024-05-15 17:10:40 +02:00
Michele Caini
204e31d481 now working on v3.5.0 2024-05-13 12:28:35 +02:00
Michele Caini
d32ddd2970 doc: related projects - close #313 2024-05-13 12:25:09 +02:00
Michele Caini
c64c538b6c doc: update min doxy version 2024-05-13 12:21:08 +02:00
Michele Caini
63af166c32 process: WINDOWS_FILE_PATH_EXACT_NAME 2024-05-13 12:14:38 +02:00
Michele Caini
4c9520165e build: prepare to work with libuv 1.48 2024-05-13 12:13:45 +02:00
brenfwd
ba10b27646
build: add initial meson support (#306) 2024-02-08 14:03:38 +01:00
Michele Caini
a497837cc9 now working on v3.4.0 2024-02-06 10:04:57 +01:00
Michele Caini
f3414a0603 doc: updated copyright 2024-02-06 09:48:06 +01:00
Michele Caini
ed5b527559 build: prepare to work with libuv 1.47 2024-02-06 09:47:43 +01:00
Alois Klink
b32fd63c83
ci: fail Ubuntu CI if there are compiler warnings (#304) 2023-09-06 14:41:47 +02:00
Alois Klink
683883b08a
test: fix compiler warnings in test directory (#302) 2023-09-01 09:22:50 +02:00
Alois Klink
8830320c60
uv_type: make ~uv_type() destructor protected (#303) 2023-08-30 16:18:30 +02:00
Michele Caini
77b91ff98e test: I removed a bit too much stuff with the last commit :) 2023-08-30 10:50:05 +02:00
Michele Caini
dea293ffaa uv_type: drop virtual init function - close #302 2023-08-30 09:33:39 +02:00
Alois Klink
b004e1a808
util: fix -Wsign-conversion in ip_addr() (#298) 2023-08-28 11:33:11 +02:00
Alois Klink
3eaf8efc37
pipe: hide pipe_handle::connect -Wsign-conversion (#299) 2023-08-28 11:32:32 +02:00
Alois Klink
113772521d
type_info: avoid gcc-7 -Wsign-conversion warning (#301) 2023-08-27 15:32:58 +02:00
Alois Klink
0c866baaf7
tty: remove unnecessary semi-colon (#300) 2023-08-25 17:44:16 +02:00
Alois Klink
87e5440e00
uv_type: supress -Werror=unused compiler errors (#296) 2023-08-25 17:43:02 +02:00
Alois Klink
9c88a9b352
loop: rename variable to fix -Wshadow warning (#297) 2023-08-25 17:42:01 +02:00
Reimu NotMoe
36b586f7d3
build: fix minor typo in CMakeLists.txt (#295) 2023-08-21 13:41:45 +02:00
Michele Caini
d88bfcae87 now working on v3.3.0 2023-07-03 14:51:23 +02:00
Michele Caini
7fda7c5072 pipe: use UV_PIPE_NO_TRUNCATE as needed 2023-07-03 14:40:46 +02:00
Michele Caini
14cb806b1c build: prepare to work with libuv 1.46 2023-07-03 14:29:50 +02:00
Michele Caini
042da09665 stream/udp: add support to custom allocation functions - close #289 2023-07-03 14:16:41 +02:00
Michele Caini
18081ecb66 emitter: use more meaningful names for template arguments 2023-06-09 12:11:29 +02:00
Michele Caini
2da8140e2d updated .gitignore 2023-05-22 11:15:51 +02:00
Michele Caini
def75703a4 now working on v3.2.0 2023-05-22 11:01:28 +02:00
Michele Caini
426988b000 loop: metrics 2023-05-19 14:57:15 +02:00
Michele Caini
f86c810aea util: avoid testing constrained_memory 2023-05-19 14:56:29 +02:00
Michele Caini
06f084d4ba thread: getcpu 2023-05-19 14:44:33 +02:00
Michele Caini
8743dfc155 util: available_memory + missing declarations for gettime :) 2023-05-19 14:25:21 +02:00
Michele Caini
bd90d77bc7 util: formatting 2023-05-19 14:24:08 +02:00
Michele Caini
5e4694abba util: uvw_clock_id and gettime 2023-05-19 14:23:56 +02:00
Michele Caini
f0ac08cb42 build: prepare to work with libuv 1.45 2023-05-19 14:23:08 +02:00
Michele Caini
67acfdb53b doc: fixed typo 2023-05-15 14:35:30 +02:00
Jon Daniel
cf0f8c6fd4
process: fix missing const in equality relation for C++23 (#278) 2023-05-12 08:29:43 +02:00
Benoît
ac42b79b30
process: fix ambiguous overloaded from c++20 with explicit cast (#280) 2023-05-11 14:29:57 +02:00
Michele Caini
d84a031f32 loop: add missing UVW_INLINE (close #282) 2023-05-11 14:26:20 +02:00
Reimu NotMoe
e059473135
poll: fix compile errors caused by ambiguous naming of poll_event in poll_handle (#281) 2023-05-08 08:08:30 +02:00
Michele Caini
950aa6486e fix doxygen link 2023-05-05 08:54:37 +02:00
Michele Caini
a3c179d089 now working on v3.1.0 2023-05-05 08:53:55 +02:00
Michele Caini
220c4fe13b updated doxy file 2023-05-05 08:37:44 +02:00
Michele Caini
ec1f973bd4 updated libuv version 2023-05-05 08:37:30 +02:00
Petr Menšík
6d771874a8
test: do not fail unit test because cpu speed 0 (#277) 2023-03-11 19:12:47 +01:00
Michele Caini
70697f4ae9
uvw v3 (#263) 2023-03-10 10:52:03 +01:00
Stefano Fiorentino
3636701fc2
avoid cmake complaining about using VERSION keyword (#275)
[cmake] CMake Error at CMakeLists.txt:190 (set_target_properties):
	[cmake]   INTERFACE_LIBRARY targets may only have whitelisted properties.  The
	[cmake]   property "VERSION" is not allowed.

Signed-off-by: Fiorentino, Stefano <stefano.fiorentino@adesso.ch>
Co-authored-by: Fiorentino, Stefano <stefano.fiorentino@adesso.ch>
2023-02-27 18:47:35 +01:00
Nicolas Jakob
b2ed37f5f5
doc: add vcpkg badge and vcpkg.link (#274) 2023-02-24 10:42:20 +01:00
Petr Menšík
1e4f964c47
Include cstdint where uintX_t is used (#273) 2023-02-22 18:14:31 +01:00
Michele Caini
68803053f4 doc: updated copyright 2023-01-20 08:51:29 +01:00
Stefano Fiorentino
36b9d189a1
shared object versioning (mirroring the behaviour of autotools) (#269)
* shared object versioning (mirroring the behaviour of autotools)

Closes #268

Signed-off-by: Fiorentino, Stefano <stefano.fiorentino@adesso.ch>
2023-01-16 18:15:02 +01:00
Michele Caini
897e83f66a build: updated workflows 2023-01-16 09:53:23 +01:00
Michele Caini
efcd2a3858 doc: drop download link, no longer relevant 2023-01-16 09:46:57 +01:00
Michele Caini
324ca2ac32 doc: drop links to docsforge (close #272) 2023-01-16 09:46:04 +01:00
Stefano Fiorentino
64fb9fc8e5
set workflows with only default compiler packages (#271) - close #270 2023-01-12 17:40:53 +01:00
Moody
684d912ca9
export uvwConfig to build a uvw submodule statically (#264) 2022-04-07 14:36:54 +02:00
Michele Caini
3db9e8f75a update to libuv 1.44.1 2022-03-10 11:30:41 +01:00
Michele Caini
6243d3624a added Utilities::availableParallelism (libuv 1.44) 2022-03-08 13:15:38 +01:00
Michele Caini
5664b04075 added a clang-format file, updated the whole codebase 2022-03-08 12:59:19 +01:00
Michele Caini
25b8fad800 test: remove broken check 2022-03-08 12:15:05 +01:00
Michele Caini
1d3d6dd2dc updated workflow 2022-03-08 11:54:00 +01:00
Michele Caini
2918b4e9ce updated to libuv v1.44 2022-03-08 11:37:50 +01:00
Michele Caini
9f8047d6d4 osPriorirty -> OS::priority as it ought to be 2022-03-08 11:35:56 +01:00
Petr Menšík
7326baf110
Support dynamic linkage of tests (#253) 2022-02-08 16:53:05 +01:00
Fiorentino Ing. Stefano
472245a8bd install either static or shared lib only
Close #246

Signed-off-by: Fiorentino Ing. Stefano <stefano.fiore84@gmail.com>
2022-02-01 12:23:15 +01:00
Fiorentino Ing. Stefano
c8c2ccadd6 remove the configurations for pkg-config
Signed-off-by: Fiorentino Ing. Stefano <stefano.fiore84@gmail.com>
2022-02-01 12:23:15 +01:00
Fiorentino Ing. Stefano
62511d77bc Adding subdirectories name to the installation PATHs.
Also removing the libuv general targets from ALL

Signed-off-by: Fiorentino Ing. Stefano <stefano.fiore84@gmail.com>
2022-02-01 12:23:15 +01:00
Michele Caini
2f6be7b7ab doc: release it under MIT (no need to use a different license) - close #260 2022-01-19 10:09:59 +01:00
Michele Caini
cca65db1f4 doc: updated copyright 2022-01-18 15:56:49 +01:00
Michele Caini
fa83b0fb5e type_info: remove non-UTF-8 characters (close #259) 2022-01-17 14:21:08 +01:00
Michele Caini
86fb93d0ef update to libuv v1.43 (it just works fortunately) 2022-01-14 09:17:02 +01:00
Michele Caini
432c55de9b build system: update gtest upstream branch name 2021-11-16 09:29:41 +01:00
Moody
b388750a8d
added CMake package configuration files (#246) 2021-11-11 13:52:18 +01:00
Michele Caini
5876a2b11d Add support for uv_try_write2 2021-07-28 11:29:10 +02:00
Michele Caini
a0f7be6825 Prepare for cutting v2.10 2021-07-28 11:04:58 +02:00
Fiorentino Ing. Stefano
1e6013d230 libuv version update to v1.42.0
Signed-off-by: Fiorentino Ing. Stefano <stefano.fiore84@gmail.com>
2021-07-28 10:59:18 +02:00
Moody
97c02c4f57
Fixed a typo in conanfile.py (#247) 2021-07-05 08:34:18 +02:00
Michele Caini
3d60ab1a6d build system: updated GH workflow 2021-06-30 14:58:44 +02:00
Michele Caini
a32596ed02 added CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS to the workflow 2021-05-10 11:36:48 +02:00
Michele Caini
67a5eab44f updated type_info.hpp 2021-05-10 11:11:56 +02:00
Michele Caini
775a7262f8 updated build system (test also older compilers) 2021-05-10 11:10:53 +02:00
Michele Caini
3ed391ab13 updated build system 2021-05-10 00:07:57 +02:00
Michele Caini
9f6bcee5be updated workflow 2021-05-09 23:50:23 +02:00
Michele Caini
d2efc89796 cleanup 2021-05-09 23:50:13 +02:00
Michele Caini
437ab75068 make internal::counter static inline 2021-05-09 23:47:11 +02:00
Michele Caini
aea6671b14 refine type hash indexing model 2021-05-09 23:42:16 +02:00
Stefano Fiorentino
010d8de944
Removing the monostate pattern from uvw. (#242) 2021-05-09 22:49:33 +02:00
Michele Caini
b0015be668 always initialize local variables (close #241) 2021-04-01 09:47:16 +02:00
Michele Caini
bf61f55dd3 loop::walk ignores unmanaged handles (close #239) 2021-03-22 15:18:39 +01:00
Michele Caini
83973cbc04 removed a bunch of unnecessary copies (close #238) 2021-03-22 12:20:21 +01:00
Michele Caini
58b299ee60 Updated to libuv 1.41 (close #237).
uv_socketpair and uv_pipe aren't explicitly eported via uwv.
2021-02-14 16:15:20 +01:00
Michele Caini
d799a7fc85 poll handle: rename variable to avoid clashing (close #236) 2021-02-11 23:26:30 +01:00
Fiorentino Ing. Stefano
2ea56b1dd1 Give the option to build uvw as lib without fetching the libuv dependency
creating a fake lib to re-activate the lib test
	closes #231

Signed-off-by: Fiorentino Ing. Stefano <stefano.fiore84@gmail.com>
2020-11-17 13:46:31 +00:00
Michele Caini
6ed211328e make AddrFuncType in IpTraits an inline static variable rather than a constexpr one (close #226) 2020-10-15 09:39:35 +02:00
Michele Caini
e0e0be194e build system: updated cmake file for docs target 2020-10-15 09:34:54 +02:00
Stefano Fiorentino
0156258c9b
Merge pull request #224 from stefanofiorentino/experimental
refactor the `test' includes
2020-10-08 10:46:11 +02:00
Fiorentino Ing. Stefano
184488be25 refactor the `test' includes
Signed-off-by: Fiorentino Ing. Stefano <stefano.fiore84@gmail.com>
2020-10-08 08:10:11 +00:00
Stefano Fiorentino
60bd3aea38
Merge pull request #222 from stefanofiorentino/experimental
Avoid instantiations as header-only config is used.
2020-10-05 14:16:11 +02:00
Fiorentino Ing. Stefano
41257c60f2 Avoid instantiations as header-only config is used.
Closes #221

Signed-off-by: Fiorentino Ing. Stefano <stefano.fiore84@gmail.com>
2020-10-05 08:26:43 +00:00
Michele Caini
26973f335c updated build system 2020-10-04 22:13:10 +02:00
Michele Caini
6dd4a420f8 updated to libuv 1.40 (close #220) 2020-10-03 23:02:29 +02:00
Michele Caini
77af4a3fc4 updated workflow 2020-09-10 15:42:41 +02:00
Michele Caini
6e5890921a build system: solved an issue with codecov 2020-09-07 08:46:41 +02:00
Michele Caini
13cbd81881 ready to cut a new release (close #219) 2020-09-07 08:44:41 +02:00
Michele Caini
0c1a21e75c doc: added a note about Emitter::clear (close #217) 2020-08-10 00:06:45 +02:00
Michele Caini
0e86ec5847 doc: updated documentation for Loop::walk (close #214) 2020-08-09 23:58:53 +02:00
Michele Caini
91ab230d81 a more vs-friendly gitignore file 2020-07-31 17:51:21 +02:00
Michele Caini
112828c677 test: make work.cpp run correcty also on Windows 2020-07-31 17:06:52 +02:00
Michele Caini
6b1651be7e fixed typo 2020-07-31 16:34:38 +02:00
Michele Caini
a4a27cacff test: make pipe tests run correctly also on Windows 2020-07-31 16:09:29 +02:00
Michele Caini
d2cc600e97 test: make thread tests run fine also on Windows 2020-07-31 13:01:13 +02:00
Michele Caini
9ed3eaebd1 test: make tty test run without errors also on Windows 2020-07-31 12:44:08 +02:00
Michele Caini
a77051e73a test: make util test compile also on Windows 2020-07-31 12:27:43 +02:00
Michele Caini
934851e3f5 test: make loop test compile also on Windows 2020-07-31 12:12:18 +02:00
gallexme
c3e189c612
thread.cpp: suppress narrowing conversions warnings (#216) 2020-07-31 11:20:34 +02:00
Michele Caini
0b329720bf get rid of base handle, loop::walk returns actual handles (see #212, close #214) 2020-07-28 22:34:09 +02:00
Stefano Fiorentino
a10844d4bf
basehandle::type() in uv_walk which is in a timer callback causes double free (#213)
Close #212
2020-07-19 12:08:36 +02:00
Michele Caini
52785475b9 udp: get around an issue with ICC (close #209) 2020-07-02 15:47:57 +02:00
Eli Lindsey
1a794772b2
add openbsd support (#204)
* add openbsd support

Renames fileno() so it doesn't clash with OpenBSD's macro expansion.
Makes minor changes to build steps so the test suite compiles and runs
on OpenBSD.

See https://github.com/skypjack/uvw/issues/201 for full context

* drop no-error override

* disable sendfile tests on openbsd

* forgot to commit the new file
2020-06-29 16:37:31 +02:00
Michele Caini
dc67c97ab2 doc: small review + added links to docsforge (close #210) 2020-06-29 16:29:45 +02:00
Michele Caini
f9056e732c resolve an issue with ICC (close #208) 2020-06-29 15:45:55 +02:00
Fiorentino Ing. Stefano
60614d32b8 remove all reference to shared library
too much effort to support cross-platform DSO

Signed-off-by: Fiorentino Ing. Stefano <stefano.fiore84@gmail.com>
2020-06-29 11:10:47 +02:00
Michele Caini
00d5c11e65 doc: make it compile again and suppress some warnings 2020-05-31 16:46:47 +02:00
Michele Caini
37aa1c8260 udp: data event returns non-const data (close #200) 2020-05-27 12:31:53 +02:00
Michele Caini
3a29890651 ready to cut a new release (close #199) 2020-05-25 23:41:44 +02:00
Michele Caini
fb270887b4 updated to libuv v1.38 2020-05-25 23:38:52 +02:00
Michele Caini
e5870c79e1 doc: updated cmake minimum required version (see #194) 2020-05-21 22:35:06 +02:00
Michele Caini
fd52f76e4d doc: removed references to patreon account 2020-05-21 08:46:11 +02:00
Michele Caini
3a02460be1 build system: updated required version for cmake (close #194) 2020-05-17 22:12:33 +02:00
Eli Lindsey
d35f557161
add elindsey to contributors list (#193) 2020-04-27 16:01:43 +02:00
Michele Caini
99e6ba7124 ready to cut a new release (close #191) 2020-04-24 00:59:15 +02:00
Michele Caini
ff97bbbc9d now works with libuv v1.37 (see #191) 2020-04-24 00:57:09 +02:00
Fiorentino Ing. Stefano
3d9e45e733 Switch pragma once to legacy include guards
Signed-off-by: Fiorentino Ing. Stefano <stefano.fiore84@gmail.com>
2020-04-20 18:24:11 +02:00
Fiorentino Ing. Stefano
991f5cdbb8 Extern template instantiations to get rid of clang warnings.
Signed-off-by: Fiorentino Ing. Stefano <stefano.fiore84@gmail.com>
2020-04-19 21:27:43 +02:00
Fiorentino Ing. Stefano
938b8af9b0 Skip including header in .cpp in case UVW_AS_LIB is defined
Signed-off-by: Fiorentino Ing. Stefano <stefano.fiore84@gmail.com>
2020-04-19 18:47:32 +02:00
Michele Caini
c0b9dc919b updated to libuv v1.36 (close #191) 2020-04-18 16:45:21 +02:00
Stefano Fiorentino
4a6289ecf7 improve udp instantiations
Signed-off-by: Stefano Fiorentino <stefano.fiore84@gmail.com>
2020-04-06 17:37:05 +02:00
Stefano Fiorentino
ff0d29309a improve dns instantiations
Signed-off-by: Stefano Fiorentino <stefano.fiore84@gmail.com>
2020-04-06 16:54:07 +02:00
Stefano Fiorentino
b2647468e2 avoid to install .cpp with BUILD_UVW_LIBS
Signed-off-by: Stefano Fiorentino <stefano.fiore84@gmail.com>
2020-04-06 16:15:36 +02:00
Stefano Fiorentino
8872af2d86 improve tcp instantiations
Signed-off-by: Stefano Fiorentino <stefano.fiore84@gmail.com>
2020-04-06 16:03:45 +02:00
Stefano Fiorentino
d611ca264f Instantiate Loop::run<Mode>() in the .cpp
This is possible because a limited values are available as Mode

Signed-off-by: Stefano Fiorentino <stefano.fiore84@gmail.com>
2020-04-06 15:02:26 +02:00
Stefano Fiorentino
0ee7ef41fe adding option to activate UB sanitizer
Signed-off-by: Stefano Fiorentino <stefano.fiore84@gmail.com>
2020-04-03 22:42:45 +02:00
Stefano Fiorentino
03b98bc234 update project description to add shared/static library
Signed-off-by: Stefano Fiorentino <stefano.fiore84@gmail.com>
2020-04-03 22:42:45 +02:00
Eli Lindsey
fc197b5117 fix undefined behavior in resource.hpp
Today, uvw triggers undefined behavior in resource.hpp:
  runtime error: downcast of address which does not point to an object of type 'uvw::FsEventHandle'
  note: object is of type 'uvw::BaseHandle'

It looks like we're saving the current handle in the void* data member
so it can be retrieved on the next callback run. This downcast to a
derived class from its parents ctor as it's being constructed isn't
valid.

Since we're storing this as a void* anyways and all the callsites using
it need to do their own cast on retrieval, we can instead persist the
this pointer. For downcasting to work in all cases the Resource class
tree needs to be leftmost in Handler's multiple inheritance.

Tested by verifying that my unit tests no longer show ubsan errors and
uvw's test suite still passes.
2020-04-02 14:37:45 -04:00
Michele Caini
8233a6e6b2 updated README file (thanks to @escherstair for pointing out some errors in the documentation) 2020-04-01 15:12:30 +02:00
Michele Caini
fda5b57f4f doc: updated README (close #187) 2020-03-31 22:54:44 +02:00
Michele Caini
1f2b6ed23d increase code coverage 2020-03-28 00:03:54 +01:00
Michele Caini
4d92e354be increase code coverage 2020-03-27 23:39:08 +01:00
Michele Caini
fc50c60987 removed dead branch (BUILD_TESTING is used already to enter the test directory) 2020-03-27 23:32:48 +01:00
Stefano Fiorentino
d9ab73a281 `make install' requires gtest and tests to be built #186
Signed-off-by: Stefano Fiorentino <stefano.fiore84@gmail.com>
2020-03-27 21:50:28 +01:00
Stefano Fiorentino
336e5dfb1d Improving test coverage [adding Mutex::RecursiveLockUnlock]
Signed-off-by: Stefano Fiorentino <stefano.fiore84@gmail.com>
2020-03-27 12:31:33 +01:00
Stefano Fiorentino
bfc4a2c95b Improving test coverage [adding Mutex::LockUnlock]
Signed-off-by: Stefano Fiorentino <stefano.fiore84@gmail.com>
2020-03-27 12:02:05 +01:00
Stefano Fiorentino
53da326aea FsReq.ReadDir test now reads all dirent's entries
Signed-off-by: Stefano Fiorentino <stefano.fiore84@gmail.com>
2020-03-25 23:14:48 +01:00
Stefano Fiorentino
6fd64f93a0 Improving test coverage [adding FsReq::readdirSync]
Signed-off-by: Stefano Fiorentino <stefano.fiore84@gmail.com>
2020-03-25 19:34:05 +01:00
Stefano Fiorentino
216f5f4da1 Improving test coverage [adding FsReq::readdir] 2020-03-25 16:04:23 +01:00
Stefano Fiorentino
fe73fa048a explicit instantiation of compile-time flag-based functionalities #183 2020-03-25 15:56:35 +01:00
Stefano Fiorentino
1f7d4acc40 Improving test coverage [adding Thread] (#181) 2020-03-25 15:55:16 +01:00
Michele Caini
008361d83c updated README.md (close #185) 2020-03-25 15:51:26 +01:00
Michele Caini
0fe8d7959b updated README.md to reflect the new dual-mode 2020-03-22 22:54:22 +01:00
Michele Caini
8d1a431f68 allow custom deleters with write requests (close #182) 2020-03-22 22:29:15 +01:00
Michele Caini
4afb5ebb20 updated list of collaborators 2020-03-21 15:54:23 +01:00
Stefano Fiorentino
0e36f685e8 Export cmake targets (close #179) 2020-03-21 15:47:50 +01:00
Stefano Fiorentino
ebc8199d42 Add support for pkg-config (close #177) 2020-03-18 23:05:06 +01:00
Michele Caini
3a32097dc8 ready to cut a new release 2020-03-16 23:25:09 +01:00
Michele Caini
855d851f47 updated to libuv v1.35 (close #175) 2020-03-16 23:23:05 +01:00
Michele Caini
55d7782af5 allow static build - see #171, thanks to @stefanofiorentino 2020-03-16 23:01:53 +01:00
Sergiu Giurgiu
76f247847f
Changed FsReq::stasfs method name to statfs (#173) 2020-03-01 21:47:43 +01:00
Michele Caini
da3b5f00f5 updated copyright 2020-01-17 23:17:07 +01:00
Michele Caini
bc88511a29 updated version 2020-01-17 23:14:58 +01:00
Michele Caini
1b6d9625e8 updated libuv reference 2020-01-17 23:14:49 +01:00
Michele Caini
ba618cf070 stream: fixed extended write functions (close #169) 2020-01-16 19:37:39 +01:00
Michele Caini
3e6b09eb8d build system: fixed cmake option to generate documentation 2019-12-05 14:12:37 +01:00
Michele Caini
ed766f709a now working with libuv v1.34.x 2019-12-04 23:05:47 +01:00
Michele Caini
8f868995fc updated workflow 2019-11-08 09:56:22 +01:00
Fabrice Fontaine
2cef4d6f20 CMakeLists.txt: add BUILD_DOC option (#165)
Signed-off-by: Fabrice Fontaine <fontaine.fabrice@gmail.com>
2019-11-07 23:13:11 +01:00
Adam Leskis
7a8e526adf update typos and ambiguous phrases in README (#163)
* update typos and ambiguous phrases in README
* add name to authors list
2019-10-23 23:47:44 +02:00
Michele Caini
337bd035a8 now working with libuv v1.33.x 2019-10-17 23:42:48 +02:00
Michele Caini
3fae068b9b added Utilities::OS::env overload to iterate all env variables 2019-10-17 22:47:57 +02:00
Michele Caini
0890c29b7e updated version/libuv 2019-10-17 22:46:46 +02:00
Michele Caini
852b9e2489 GH sponsorship 2019-10-08 22:54:19 +02:00
Michele Caini
6dfe8f839a added become-patron badge 2019-09-25 15:18:36 +02:00
Michele Caini
2cb056ec7b removed appveyor.yml 2019-09-25 15:11:22 +02:00
Michele Caini
b117c6118b conan deploy through GH actions 2019-09-25 14:58:16 +02:00
Michele Caini
90affa4d51 removed appveyor.yml 2019-09-25 14:52:52 +02:00
Michele Caini
866beeda31 updated build.yml 2019-09-25 14:22:19 +02:00
Michele Caini
8a8610a928 make dns test optional 2019-09-25 14:15:15 +02:00
Michele Caini
87e23c74b5 GH actions 2019-09-25 14:04:54 +02:00
Michele Caini
6f6294ebc2 now working with libuv v1.32.x 2019-09-10 15:54:59 +02:00
Michele Caini
3264417f15 added Loop::create overload to allow using external resources (close #157) 2019-08-28 16:40:24 +02:00
Michele Caini
ea4b6c84d0 now working with libuv v1.31.x 2019-08-10 22:55:15 +02:00
Michele Caini
749581309e uvw is now a C++17 only library (close #155) 2019-07-31 14:56:51 +02:00
Michele Caini
6ce60d4088 now working with libuv v1.30.x (close #153) 2019-06-28 14:47:06 +02:00
Michele Caini
6852f49401
Update FUNDING.yml 2019-06-07 09:54:38 +02:00
Michele Caini
af80bf5ef1
Create FUNDING.yml 2019-06-06 11:55:01 +02:00
Michele Caini
e59a989521 Revert "minor changes"
This reverts commit 8a93cb6e31.
2019-05-27 15:00:13 +02:00
Michele Caini
ff19fe0bc1 updated version 2019-05-27 14:52:25 +02:00
Michele Caini
0186f0444c reset the loop on close (fix #152) 2019-05-27 14:46:36 +02:00
Michele Caini
8a93cb6e31 minor changes 2019-05-27 14:45:33 +02:00
Michele Caini
529ebf3b49 updated doc 2019-05-20 23:03:00 +02:00
Michele Caini
aa43978b3e updated list of authors 2019-05-20 14:12:30 +02:00
Michele Caini
34042167eb updated README.md (as suggested by RevRagnarok from Reddit) 2019-05-17 08:37:45 +02:00
Michele Caini
1dc61d523d now working with libuv v1.29.x (close #151) 2019-05-16 14:22:40 +02:00
Michele Caini
b127f58e41 avoid narrowing conversions 2019-04-25 09:18:40 +02:00
Michele Caini
00c19381fc now working with libuv v1.28.x (close #149) 2019-04-25 00:05:36 +02:00
Michele Caini
6f5c43ae8e now working with libuv v1.27.x (close #148) 2019-03-20 17:31:51 +01:00
Michele Caini
8349a74801 bug fixing 2019-03-17 17:23:57 +01:00
Michele Caini
995603f6fe updated version 2019-03-09 23:46:02 +01:00
Michele Caini
6b64e20161 doc: thread.hpp 2019-03-09 23:44:02 +01:00
Michele Caini
c053e861e8 updated list of patrons 2019-03-08 15:28:57 +01:00
Michele Caini
e9685cfbe7 updated README 2019-03-01 08:40:35 +01:00
Michele Caini
78df87fddd minor changes (close #147) 2019-02-27 17:07:11 +01:00
Michele Caini
b269d9cfa6 now working with libuv v1.26.x (close #146) 2019-02-14 22:40:21 +01:00
Uilian Ries
7c043fae5e #144 Remove Conan remotes (#145)
- Since liuv is official in Conan Center, we no loger need to force
  Bincrafters' remote in CPT list.
- Add topics attribute. All topics are present on Github project page.

Signed-off-by: Uilian Ries <uilianries@gmail.com>
2019-01-29 22:29:28 +01:00
Michele Caini
8fbb39fd52 updated conan stuff 2019-01-24 14:27:26 +01:00
Michele Caini
27dc68dd84 now working with libuv v1.25.x 2019-01-24 14:03:33 +01:00
Michele Caini
af3d06f3b4 updated copyright 2019-01-02 15:34:08 +01:00
Michele Caini
183ebc0333 fix #142 2018-11-20 14:38:49 +01:00
Michele Caini
41913613c0 updated conanfile.py 2018-11-17 15:09:51 +01:00
Michele Caini
2167cddb01 now working with libuv v1.24.x 2018-11-17 15:02:31 +01:00
Michele Caini
ae8f5c81d4 skip tests from libuv 2018-11-06 15:07:31 +01:00
Michele Caini
c11d30ddba fixed appveyor configuration 2018-11-06 14:53:55 +01:00
Michele Caini
c0edac3cf3 fixed conan cpp 2018-11-06 14:49:10 +01:00
Michele Caini
a9bad602df renaming 2018-11-06 14:34:49 +01:00
163 changed files with 11617 additions and 9103 deletions

41
.clang-format Normal file
View File

@ -0,0 +1,41 @@
BasedOnStyle: llvm
---
AccessModifierOffset: -4
AlignEscapedNewlines: DontAlign
AllowShortBlocksOnASingleLine: Empty
AllowShortEnumsOnASingleLine: true
AllowShortFunctionsOnASingleLine: Empty
AllowShortIfStatementsOnASingleLine: WithoutElse
AllowShortLoopsOnASingleLine: true
AlwaysBreakTemplateDeclarations: Yes
BreakBeforeBinaryOperators: NonAssignment
BreakBeforeTernaryOperators: true
ColumnLimit: 0
DerivePointerAlignment: false
IncludeCategories:
- Regex: '<[[:alnum:]_]+>'
Priority: 1
- Regex: '<(gtest|gmock)/'
Priority: 2
- Regex: '<[[:alnum:]_./]+>'
Priority: 3
- Regex: '<entt/'
Priority: 4
- Regex: '.*'
Priority: 5
IndentPPDirectives: AfterHash
IndentWidth: 4
KeepEmptyLinesAtTheStartOfBlocks: false
Language: Cpp
PointerAlignment: Right
SpaceAfterCStyleCast: false
SpaceAfterTemplateKeyword: false
SpaceAroundPointerQualifiers: After
SpaceBeforeCaseColon: false
SpaceBeforeCtorInitializerColon: false
SpaceBeforeInheritanceColon: false
SpaceBeforeParens: Never
SpaceBeforeRangeBasedForLoopColon: false
Standard: Latest
TabWidth: 4
UseTab: Never

12
.clang-tidy Normal file
View File

@ -0,0 +1,12 @@
Checks: >
bugprone-*,
-bugprone-easily-swappable-parameters,
concurrency-*,
modernize-*,
-modernize-avoid-c-arrays,
-modernize-use-trailing-return-type,
performance-*,
portability-*,
CheckOptions:
- key: bugprone-suspicious-include.HeaderFileExtensions
value: ";h;hpp;ipp"

View File

@ -13,7 +13,9 @@ def get_version():
match = re.search(r'project\(uvw VERSION (.*)\)', content)
if match:
return match.group(1)
return os.getenv("TRAVIS_BRANCH", "master")
tag_version = os.getenv("GITHUB_REF")
package_version = tag_version.replace("refs/tags/v", "")
return package_version
def get_username():
return os.getenv("CONAN_USERNAME", "skypjack")
@ -28,7 +30,7 @@ def get_reference():
def get_upload():
username = get_username()
url = "https://api.bintray.com/conan/{}/conan".format(username)
default_upload = url if os.getenv("TRAVIS_TAG") else False
default_upload = url if os.getenv("GITHUB_REF") else False
return os.getenv("CONAN_UPLOAD", default_upload)
@ -41,7 +43,6 @@ if __name__ == "__main__":
builder = ConanMultiPackager(reference=get_reference(),
username=get_username(),
upload=get_upload(),
remotes="https://api.bintray.com/conan/bincrafters/public-conan",
test_folder=test_folder,
stable_branch_pattern=r'v?\d+\.\d+\.\d+.*',
upload_only_when_stable=upload_when_stable())

View File

@ -4,8 +4,6 @@
import os
from conans import ConanFile, CMake
class TestPackageConan(ConanFile):
settings = "os", "compiler", "build_type", "arch"
generators = "cmake"

View File

@ -3,14 +3,14 @@
#include <uvw.hpp>
#include <memory>
void listen(uvw::Loop &loop) {
std::shared_ptr<uvw::TCPHandle> tcp = loop.resource<uvw::TCPHandle>();
void listen(uvw::loop &loop) {
std::shared_ptr<uvw::tcp_handle> tcp = loop.resource<uvw::tcp_handle>();
tcp->once<uvw::ListenEvent>([](const uvw::ListenEvent &, uvw::TCPHandle &srv) {
std::shared_ptr<uvw::TCPHandle> client = srv.loop().resource<uvw::TCPHandle>();
tcp->on<uvw::listen_event>([](const uvw::listen_event &, uvw::tcp_handle &srv) {
std::shared_ptr<uvw::tcp_handle> client = srv.loop().resource<uvw::tcp_handle>();
client->on<uvw::CloseEvent>([ptr = srv.shared_from_this()](const uvw::CloseEvent &, uvw::TCPHandle &) { ptr->close(); });
client->on<uvw::EndEvent>([](const uvw::EndEvent &, uvw::TCPHandle &client) { client.close(); });
client->on<uvw::close_event>([ptr = srv.shared_from_this()](const uvw::close_event &, uvw::tcp_handle &) { ptr->close(); });
client->on<uvw::end_event>([](const uvw::end_event &, uvw::tcp_handle &client) { client.close(); });
srv.accept(*client);
client->read();
@ -20,12 +20,12 @@ void listen(uvw::Loop &loop) {
tcp->listen();
}
void conn(uvw::Loop &loop) {
auto tcp = loop.resource<uvw::TCPHandle>();
void conn(uvw::loop &loop) {
auto tcp = loop.resource<uvw::tcp_handle>();
tcp->on<uvw::ErrorEvent>([](const uvw::ErrorEvent &, uvw::TCPHandle &) { /* handle errors */ });
tcp->on<uvw::error_event>([](const uvw::error_event &, uvw::tcp_handle &) { /* handle errors */ });
tcp->once<uvw::ConnectEvent>([](const uvw::ConnectEvent &, uvw::TCPHandle &tcp) {
tcp->on<uvw::connect_event>([](const uvw::connect_event &, uvw::tcp_handle &tcp) {
auto dataWrite = std::unique_ptr<char[]>(new char[2]{ 'b', 'c' });
tcp.write(std::move(dataWrite), 2);
tcp.close();
@ -36,7 +36,7 @@ void conn(uvw::Loop &loop) {
int main() {
std::cout << "Getting UVW loop ...\n";
auto loop = uvw::Loop::getDefault();
auto loop = uvw::loop::get_default();
std::cout << "Staring UVW listener ...\n";
listen(*loop);
std::cout << "Connecting ...\n";

12
.github/FUNDING.yml vendored Normal file
View File

@ -0,0 +1,12 @@
# These are supported funding model platforms
github: skypjack
patreon:
open_collective:
ko_fi:
tidelift:
community_bridge:
liberapay:
issuehunt:
otechie:
custom: https://www.paypal.me/skypjack

21
.github/workflows/build-macos.yml vendored Normal file
View File

@ -0,0 +1,21 @@
name: build-macos
on: [push, pull_request]
jobs:
macos:
timeout-minutes: 60
runs-on: macOS-latest
strategy:
matrix:
mode: [-DUVW_BUILD_SHARED_LIB=ON, -DUVW_BUILD_LIBS=ON, -DUVW_BUILD_LIBS=OFF]
steps:
- uses: actions/checkout@v4
- name: Compile tests
working-directory: build
run: |
cmake ${{ matrix.mode }} -DUVW_BUILD_TESTING=ON -Dlibuv_buildtests=OFF ..
make -j2

25
.github/workflows/build-meson.yml vendored Normal file
View File

@ -0,0 +1,25 @@
name: build-meson
on: [push, pull_request]
jobs:
meson:
timeout-minutes: 60
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
- name: Install Meson
env:
DEBIAN_FRONTEND: noninteractive
run: |
sudo apt-get update --fix-missing
sudo apt-get install -y meson
- name: Meson Build (shared)
run: |
meson setup build
meson compile -C build
- name: Meson Build (static)
run: |
rm -rf build/
meson setup build --default-library=static
meson compile -C build

View File

@ -0,0 +1,39 @@
name: build-ubuntu-20.04
on: [push, pull_request]
jobs:
linux:
timeout-minutes: 60
strategy:
matrix:
compiler:
- { pkg: g++, exe: 'g++', version: 7 }
- { pkg: g++, exe: 'g++', version: 8 }
- { pkg: g++, exe: 'g++', version: 9 }
- { pkg: clang, exe: 'clang++', version: 8 }
- { pkg: clang, exe: 'clang++', version: 9 }
- { pkg: clang, exe: 'clang++', version: 10 }
- { pkg: clang, exe: 'clang++', version: 11 }
mode: [-DUVW_BUILD_SHARED_LIB=ON, -DUVW_BUILD_LIBS=ON, -DUVW_BUILD_LIBS=OFF]
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v4
- name: Install ${{ matrix.compiler.exe }}
run: |
sudo apt-get update --fix-missing
sudo apt install -y ${{ matrix.compiler.pkg }}-${{ matrix.compiler.version }}
- name: Compile tests
env:
CXX: ${{ matrix.compiler.exe }}-${{ matrix.compiler.version }}
run: |
cmake ${{ matrix.mode }} --preset ci-ubuntu
cmake --build build/ --parallel 2
- name: Run tests
working-directory: build
env:
CTEST_OUTPUT_ON_FAILURE: 1
run: ctest --timeout 5 -C Debug -j2

View File

@ -0,0 +1,38 @@
name: build-ubuntu-latest
on: [push, pull_request]
jobs:
linux:
timeout-minutes: 60
strategy:
matrix:
compiler:
- { pkg: g++, exe: 'g++', version: 10 }
- { pkg: g++, exe: 'g++', version: 11 }
- { pkg: g++, exe: 'g++', version: 12 }
- { pkg: clang, exe: 'clang++', version: 12 }
- { pkg: clang, exe: 'clang++', version: 13 }
- { pkg: clang, exe: 'clang++', version: 14 }
mode: [-DUVW_BUILD_SHARED_LIB=ON, -DUVW_BUILD_LIBS=ON, -DUVW_BUILD_LIBS=OFF]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install ${{ matrix.compiler.exe }}
run: |
sudo apt-get update --fix-missing
sudo apt install -y ${{ matrix.compiler.pkg }}-${{ matrix.compiler.version }}
- name: Compile tests
env:
CXX: ${{ matrix.compiler.exe }}-${{ matrix.compiler.version }}
run: |
cmake ${{ matrix.mode }} --preset ci-ubuntu
cmake --build build/ --parallel 2
- name: Run tests
working-directory: build
env:
CTEST_OUTPUT_ON_FAILURE: 1
run: ctest --timeout 5 -C Debug -j2

22
.github/workflows/build-win.yml vendored Normal file
View File

@ -0,0 +1,22 @@
name: build-win
on: [push, pull_request]
jobs:
windows:
timeout-minutes: 60
runs-on: windows-latest
strategy:
matrix:
generator: [Visual Studio 17 2022]
mode: [-DUVW_BUILD_SHARED_LIB=ON, -DUVW_BUILD_LIBS=ON, -DUVW_BUILD_LIBS=OFF]
steps:
- uses: actions/checkout@v4
- name: Compile tests
working-directory: build
run: |
cmake -DCMAKE_WINDOWS_EXPORT_ALL_SYMBOLS=TRUE ${{ matrix.mode }} -DUVW_BUILD_TESTING=ON -Dlibuv_buildtests=OFF -DCMAKE_CXX_FLAGS=/W1 -G"${{ matrix.generator }}" ..
cmake --build . -j 2

33
.github/workflows/coverage.yml vendored Normal file
View File

@ -0,0 +1,33 @@
name: coverage
on: [push, pull_request]
jobs:
codecov:
timeout-minutes: 30
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Compile tests
working-directory: build
env:
CXXFLAGS: "-O0 --coverage -fno-inline -fno-inline-small-functions -fno-default-inline"
CXX: g++
run: |
cmake -DUVW_BUILD_TESTING=ON -Dlibuv_buildtests=OFF ..
make -j4
- name: Run tests
working-directory: build
env:
CTEST_OUTPUT_ON_FAILURE: 1
run: ctest --timeout 5 -C Debug -j4
- name: Upload coverage to Codecov
working-directory: build
env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
run: |
wget https://codecov.io/bash -O codecov
chmod +x codecov
./codecov -t $CODECOV_TOKEN -B $GITHUB_REF -s .

32
.github/workflows/deploy.yml vendored Normal file
View File

@ -0,0 +1,32 @@
name: deploy
on:
push:
tags:
- v*
jobs:
conan:
timeout-minutes: 5
runs-on: ubuntu-latest
steps:
- uses: docker://conanio/gcc8
- uses: actions/checkout@v4
- name: Setup Python
uses: actions/setup-python@master
with:
version: 3.7
- name: Install
run: |
pip install --upgrade wheel
pip install conan_package_tools
- name: Deploy
env:
CONAN_LOGIN_USERNAME: ${{ secrets.CONAN_LOGIN_USERNAME }}
CONAN_PASSWORD: ${{ secrets.CONAN_PASSWORD }}
CONAN_UPLOAD: ${{ secrets.CONAN_UPLOAD }}
CONAN_GCC_VERSIONS: 8
run: |
python .conan/build.py

30
.github/workflows/sanitizer.yml vendored Normal file
View File

@ -0,0 +1,30 @@
name: sanitizer
on: [push, pull_request]
jobs:
clang:
timeout-minutes: 15
strategy:
matrix:
compiler: [clang++]
sanitizer: [ASAN, UBSAN]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Compile tests
working-directory: build
env:
CXX: ${{ matrix.compiler }}
run: |
cmake ${{ matrix.mode }} -DUVW_BUILD_TESTING=ON -DUVW_USE_${{ matrix.sanitizer }}=ON -Dlibuv_buildtests=OFF ..
make -j2
- name: Run tests
working-directory: build
env:
CTEST_OUTPUT_ON_FAILURE: 1
run: ctest --timeout 5 -C Debug -j2

78
.github/workflows/tools.yml vendored Normal file
View File

@ -0,0 +1,78 @@
name: tools
on:
push:
branches:
- tools
jobs:
iwyu:
timeout-minutes: 60
env:
IWYU: "0.22"
LLVM: "18"
runs-on: ubuntu-latest
continue-on-error: true
steps:
- uses: actions/checkout@v4
- name: Install llvm/clang
# see: https://apt.llvm.org/
run: |
wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add -
sudo add-apt-repository "deb http://apt.llvm.org/focal/ llvm-toolchain-focal-$LLVM main"
sudo apt update
sudo apt remove -y "llvm*"
sudo apt remove -y "libclang-dev*"
sudo apt remove -y "clang*"
sudo apt install -y llvm-$LLVM-dev
sudo apt install -y libclang-$LLVM-dev
sudo apt install -y clang-$LLVM
- name: Compile iwyu
# see: https://github.com/include-what-you-use/include-what-you-use
working-directory: build
run: |
git clone https://github.com/include-what-you-use/include-what-you-use.git --branch $IWYU --depth 1
mkdir include-what-you-use/build
cd include-what-you-use/build
cmake -DCMAKE_C_COMPILER=clang-$LLVM \
-DCMAKE_CXX_COMPILER=clang++-$LLVM \
-DCMAKE_INSTALL_PREFIX=./ \
..
make -j4
bin/include-what-you-use --version
- name: Compile tests
working-directory: build
run: |
export PATH=$PATH:${GITHUB_WORKSPACE}/build/include-what-you-use/build/bin
cmake -DUVW_BUILD_TESTING=ON \
-Dlibuv_buildtests=OFF \
-DCMAKE_C_COMPILER=clang-$LLVM \
-DCMAKE_CXX_COMPILER=clang++-$LLVM \
-DCMAKE_CXX_INCLUDE_WHAT_YOU_USE="include-what-you-use;-Xiwyu;--mapping_file=${GITHUB_WORKSPACE}/uvw.imp;-Xiwyu;--no_fwd_decls;-Xiwyu;--verbose=1" \
..
make -j4
clang-tidy:
timeout-minutes: 60
runs-on: ubuntu-latest
continue-on-error: true
steps:
- uses: actions/checkout@v4
- name: Compile tests
working-directory: build
env:
CXX: clang++
run: |
cmake -DUVW_BUILD_TESTING=ON -DUVW_USE_CLANG_TIDY=ON -Dlibuv_buildtests=OFF ..
make -j4
- name: Run tests
working-directory: build
env:
CTEST_OUTPUT_ON_FAILURE: 1
run: ctest -C Debug -j4

7
.gitignore vendored
View File

@ -1,2 +1,9 @@
*.user
CMakeSettings.json
.conan/test_package/build
cmake-build-debug/
.idea/
.vs/
.vscode/
.cache/
out/

View File

@ -1,71 +0,0 @@
language: cpp
dist: trusty
sudo: false
matrix:
include:
- os: linux
compiler: gcc
addons:
apt:
sources: ['ubuntu-toolchain-r-test']
packages: ['g++-6']
env: COMPILER=g++-6
- os: linux
compiler: clang
addons:
apt:
sources: ['ubuntu-toolchain-r-test', 'llvm-toolchain-trusty-4.0']
packages: ['clang-4.0', 'libstdc++-4.9-dev']
env: COMPILER=clang++-4.0
- os: osx
osx_image: xcode8.3
compiler: clang
env: COMPILER=clang++
- os: linux
compiler: gcc
addons:
apt:
sources: ['ubuntu-toolchain-r-test']
packages: ['g++-6']
env:
- C_COMPILER=gcc-6
- COMPILER=g++-6
- CXXFLAGS="-O0 --coverage -fno-inline -fno-inline-small-functions -fno-default-inline"
before_script:
- pip install --user cpp-coveralls
after_success:
- coveralls --gcov gcov-6 --gcov-options '\-lp' --root ${TRAVIS_BUILD_DIR} --build-root ${TRAVIS_BUILD_DIR}/build --extension cpp --extension hpp --exclude deps --include src
- os: linux
dist: xenial
sudo: true
language: python
python: "3.7"
services:
- docker
env:
- CONAN_GCC_VERSIONS=8
- CONAN_DOCKER_IMAGE=conanio/gcc8
install:
- pip install conan_package_tools
script:
- python .conan/build.py
notifications:
email:
on_success: never
on_failure: always
install:
- echo ${PATH}
- cmake --version
- export CC=${C_COMPILER}
- export CXX=${COMPILER}
- echo ${CXX}
- ${CXX} --version
- ${CXX} -v
script:
- mkdir -p build && cd build
- cmake .. -DBUILD_TESTING=ON && make -j4
- CTEST_OUTPUT_ON_FAILURE=1 ctest -j4 -R uvw

40
AUTHORS
View File

@ -1,27 +1,29 @@
# Author
Michele Caini aka skypjack
skypjack
# Collaborators
Paolo Monteverde aka morbo84
morbo84
stefanofiorentino
# Contributors
Federico Bertolucci aka lessness
Luca Martini aka lordlukas
Elia Mazzuoli aka Zikoel
Francesco De Felice aka fradefe
Tushar Maheshwari aka tusharpm
Jan Vcelak aka fcelda
Raoul Hecky aka raoul
Daniel Filonik aka filonik
Wojciech Bartnik aka yisonPylkita
Miigon aka Miigon
Slyshyk Oleksiy aka slyshykO
bmagistro aka bmagistro
Richard Caseres aka richardbmx
# Special thanks for patrons
Arn aka ArnCarveris
lessness
lordlukas
lpmi-13
Zikoel
fradefe
tusharpm
fcelda
raoul
filonik
yisonPylkita
Miigon
slyshykO
bmagistro
richardbmx
wnsgml972
ffontaine
elindsey
erez-o

View File

@ -2,7 +2,7 @@
# uvw
#
cmake_minimum_required(VERSION 3.2)
cmake_minimum_required(VERSION 3.13)
#
# Building in-tree is not allowed (we take care of your craziness).
@ -15,138 +15,250 @@ endif()
#
# Project configuration
#
set(UVW_VERSION_MAJOR 3)
set(UVW_VERSION_MINOR 5)
set(UVW_VERSION_PATCH 0)
project(uvw VERSION 1.11.3)
project(
uvw
VERSION ${UVW_VERSION_MAJOR}.${UVW_VERSION_MINOR}.${UVW_VERSION_PATCH}
DESCRIPTION "Header-only, event based, tiny and easy to use libuv wrapper in modern C++ - now available also as static library!"
HOMEPAGE_URL "https://github.com/skypjack/uvw"
LANGUAGES C CXX
)
if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE Debug)
endif()
set(PROJECT_AUTHOR "Michele Caini")
set(PROJECT_AUTHOR_EMAIL "michele.caini@gmail.com")
option(UVW_USE_LIBCPP "Use libc++ by adding -stdlib=libc++ flag if available." ON)
option(UVW_USE_ASAN "Use address sanitizer by adding -fsanitize=address -fno-omit-frame-pointer flags" OFF)
option(UVW_USE_UBSAN "Use address sanitizer by adding -fsanitize=undefined -fno-sanitize-recover=all -fno-omit-frame-pointer flags" OFF)
option(UVW_USE_CLANG_TIDY "Enable static analysis with clang-tidy" OFF)
option(UVW_BUILD_LIBS "Prepare targets for static library rather than for a header-only library." OFF)
option(UVW_BUILD_SHARED_LIB "Prepare targets for shared library rather than for a header-only library." OFF)
option(UVW_FIND_LIBUV "Try finding libuv library development files in the system" OFF)
message("*")
message("* ${PROJECT_NAME} v${PROJECT_VERSION} (${CMAKE_BUILD_TYPE})")
message("* Copyright (c) 2016-2018 ${PROJECT_AUTHOR} <${PROJECT_AUTHOR_EMAIL}>")
message("*")
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
if(NOT MSVC)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall")
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -O0 -g -DDEBUG")
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -Os -DRELEASE")
if(UVW_BUILD_SHARED_LIB)
set(UVW_BUILD_LIBS BOOL:ON)
endif()
#
# CMake configuration
# Compiler stuff
#
set(PROJECT_CMAKE_IN ${uvw_SOURCE_DIR}/cmake/in)
set(PROJECT_DEPS_DIR ${uvw_SOURCE_DIR}/deps)
set(PROJECT_SRC_DIR ${uvw_SOURCE_DIR}/src)
if(NOT WIN32 AND UVW_USE_LIBCPP)
include(CheckCXXSourceCompiles)
include(CMakePushCheckState)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY bin)
cmake_push_check_state()
#
# Referenced packages
#
set(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} -stdlib=libc++")
set(THREADS_PREFER_PTHREAD_FLAG ON)
check_cxx_source_compiles("
#include<type_traits>
int main() { return std::is_same_v<int, char>; }
" UVW_HAS_LIBCPP)
include(FindThreads)
find_package(Doxygen 1.8)
#
# Referenced directories and targets
#
if(DOXYGEN_FOUND)
add_subdirectory(docs)
if(NOT UVW_HAS_LIBCPP)
message(WARNING "The option UVW_USE_LIBCPP is set (by default) but libc++ is not available. The flag will not be added to the target.")
endif()
if(BUILD_TESTING)
set(BUILD_TESTING OFF)
cmake_pop_check_state()
endif()
set(GOOGLETEST_DEPS_DIR ${PROJECT_DEPS_DIR}/googletest)
set(LIBUV_DEPS_DIR ${PROJECT_DEPS_DIR}/libuv)
if(UVW_USE_CLANG_TIDY)
find_program(UVW_CLANG_TIDY_EXECUTABLE "clang-tidy")
configure_file(${PROJECT_CMAKE_IN}/deps.in ${PROJECT_DEPS_DIR}/CMakeLists.txt)
execute_process(COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}" . WORKING_DIRECTORY ${PROJECT_DEPS_DIR})
execute_process(COMMAND ${CMAKE_COMMAND} --build . WORKING_DIRECTORY ${PROJECT_DEPS_DIR})
if(NOT UVW_CLANG_TIDY_EXECUTABLE)
message(VERBOSE "The option UVW_USE_CLANG_TIDY is set but clang-tidy executable is not available.")
endif()
endif()
# gtest, gtest_main, gmock and gmock_main targets are available from now on
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
add_subdirectory(${GOOGLETEST_DEPS_DIR})
# Required minimal libuv version
set(UVW_LIBUV_VERSION 1.49.0)
# uv and uv_a targets are available from now on
add_subdirectory(${LIBUV_DEPS_DIR})
include_directories(${LIBUV_DEPS_DIR}/include)
function(fetch_libuv)
if (UVW_FETCH_LIBUV)
include(FetchContent)
set(BUILD_TESTING ON)
enable_testing()
FetchContent_Declare(
libuv
GIT_REPOSITORY https://github.com/libuv/libuv.git
GIT_TAG "v${UVW_LIBUV_VERSION}"
GIT_SHALLOW 1
)
add_subdirectory(test)
FetchContent_GetProperties(libuv)
if(NOT libuv_POPULATED)
FetchContent_Populate(libuv)
add_subdirectory(${libuv_SOURCE_DIR} ${libuv_BINARY_DIR} EXCLUDE_FROM_ALL)
endif()
if(UVW_BUILD_SHARED_LIB)
add_library(uv::uv-shared ALIAS uv)
set_target_properties(uv PROPERTIES POSITION_INDEPENDENT_CODE 1)
else()
add_library(uv::uv-static ALIAS uv_a)
set_target_properties(uv_a PROPERTIES POSITION_INDEPENDENT_CODE 1)
endif()
endif(UVW_FETCH_LIBUV)
endfunction()
function(use_libuv)
set(UVW_FETCH_LIBUV_DEFAULT ON)
if (UVW_FIND_LIBUV)
find_package(libuv ${LIBUV_VERSION} QUIET)
if (libuv_FOUND)
add_library(uv::uv-shared ALIAS uv)
set(UVW_FETCH_LIBUV_DEFAULT OFF)
message(STATUS "libuv ${libuv_VERSION} found via cmake")
else(libuv_FOUND)
find_package(PkgConfig QUIET)
if (PkgConfig_FOUND)
pkg_check_modules(libuv IMPORTED_TARGET libuv>=${LIBUV_VERSION})
if (libuv_FOUND)
add_library(uv::uv-shared ALIAS PkgConfig::libuv)
set(UVW_FETCH_LIBUV_DEFAULT OFF)
message(STATUS "libuv ${libuv_VERSION} found via pkg-config")
endif(libuv_FOUND)
endif(PkgConfig_FOUND)
endif(libuv_FOUND)
endif(UVW_FIND_LIBUV)
option(UVW_FETCH_LIBUV "Fetch the libuv repo using CMake FetchContent facility" ${UVW_FETCH_LIBUV_DEFAULT})
fetch_libuv()
endfunction()
#
# Add uvw target
#
include(GNUInstallDirs)
if(UVW_BUILD_LIBS)
use_libuv()
add_subdirectory(src)
file(GLOB HEADERS src/uvw/*.h src/uvw/*.hpp)
else()
add_library(uvw INTERFACE)
add_library(uvw::uvw ALIAS uvw)
target_compile_features(uvw INTERFACE cxx_std_17)
target_include_directories(
uvw
INTERFACE
$<BUILD_INTERFACE:${uvw_SOURCE_DIR}/src>
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
)
if(UVW_USE_ASAN)
target_compile_options(uvw INTERFACE $<$<CONFIG:Debug>:-fsanitize=address -fno-omit-frame-pointer>)
target_link_libraries(uvw INTERFACE $<$<CONFIG:Debug>:-fsanitize=address>)
endif()
if(UVW_USE_UBSAN)
target_compile_options(uvw INTERFACE $<$<CONFIG:Debug>:-fsanitize=undefined -fno-sanitize-recover=all -fno-omit-frame-pointer>)
target_link_libraries(uvw INTERFACE $<$<CONFIG:Debug>:-fsanitize=undefined>)
endif()
if(UVW_CLANG_TIDY_EXECUTABLE)
set(CMAKE_CXX_CLANG_TIDY "${UVW_CLANG_TIDY_EXECUTABLE};--config-file=${uvw_SOURCE_DIR}/.clang-tidy;--header-filter=${uvw_SOURCE_DIR}/src/uvw/.*")
endif()
if(UVW_HAS_LIBCPP)
target_compile_options(uvw BEFORE INTERFACE -stdlib=libc++)
endif()
file(GLOB HEADERS src/uvw/*.h src/uvw/*.hpp)
endif()
#
# Keep your stuff and go further away, foolish.
# Install targets
#
set(CPACK_PACKAGE_NAME ${PROJECT_NAME})
set(CPACK_PACKAGE_VENDOR ${PROJECT_AUTHOR})
set(CPACK_PACKAGE_VERSION_MAJOR ${PROJECT_VERSION_MAJOR})
set(CPACK_PACKAGE_VERSION_MINOR ${PROJECT_VERSION_MINOR})
set(CPACK_PACKAGE_VERSION_PATCH ${PROJECT_VERSION_PATCH})
set(CPACK_PACKAGE_VERSION ${CPACK_PACKAGE_VERSION_MAJOR}.${CPACK_PACKAGE_VERSION_MINOR}.${CPACK_PACKAGE_VERSION_PATCH})
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY ${PROJECT_NAME})
set(CPACK_PACKAGE_FILE_NAME ${CPACK_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION})
set(CPACK_SOURCE_PACKAGE_FILE_NAME ${CPACK_PACKAGE_FILE_NAME}-src)
set(CPACK_RESOURCE_FILE_LICENSE ${uvw_SOURCE_DIR}/LICENSE)
set(CPACK_RESOURCE_FILE_README ${uvw_SOURCE_DIR}/README.md)
set(CPACK_GENERATOR TGZ)
set(CPACK_SOURCE_GENERATOR TGZ)
set(CPACK_PACKAGING_INSTALL_DIRECTORY "uvw-${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}")
include(CPack)
#
# Install headers
#
INCLUDE(GNUInstallDirs)
IF(NOT DEFINED INCLUDE_INSTALL_DIR)
SET(INCLUDE_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_INCLUDEDIR}")
ENDIF()
file(GLOB HEADERS src/uvw/*.hpp)
install(
FILES ${HEADERS}
COMPONENT ${PROJECT_NAME}
DESTINATION ${INCLUDE_INSTALL_DIR}/uvw
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/uvw
PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ
)
install(
FILES src/uvw.hpp
COMPONENT ${PROJECT_NAME}
DESTINATION ${INCLUDE_INSTALL_DIR}
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ
)
#
# AOB
# Install targets
#
add_custom_target(
uvw_aob
SOURCES
appveyor.yml
AUTHORS
LICENSE
README.md
.travis.yml
if (UVW_BUILD_LIBS)
set_target_properties(
uvw PROPERTIES
VERSION ${UVW_VERSION_MAJOR}.${UVW_VERSION_MINOR}.${UVW_VERSION_PATCH}
SOVERSION ${UVW_VERSION_MAJOR}
)
endif()
install(
EXPORT uvwConfig
NAMESPACE uvw::
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/uvw
)
install(
TARGETS uvw
EXPORT uvwConfig
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
)
if(UVW_FETCH_LIBUV AND UVW_BUILD_LIBS)
# libuv is only fetched when both above conditions are true
install(DIRECTORY ${libuv_SOURCE_DIR}/include/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/uvw/uv/include)
if (UVW_BUILD_SHARED_LIB)
install(TARGETS uv EXPORT uvwConfig LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}/uvw)
else()
install(TARGETS uv_a EXPORT uvwConfig ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}/uvw)
endif()
endif(UVW_FETCH_LIBUV AND UVW_BUILD_LIBS)
export(EXPORT uvwConfig)
### Testing
option(UVW_BUILD_TESTING "Enable testing with ctest." OFF)
if(UVW_BUILD_TESTING)
option(UVW_FIND_GTEST_PACKAGE "Enable finding gtest package." OFF)
if (NOT UVW_BUILD_LIBS)
use_libuv()
endif()
enable_testing()
add_subdirectory(test)
endif()
#
# Documentation
#
option(UVW_BUILD_DOCS "Enable building with documentation." OFF)
if(UVW_BUILD_DOCS)
find_package(Doxygen 1.10)
if(DOXYGEN_FOUND)
add_subdirectory(docs)
endif()
endif()

59
CMakePresets.json Normal file
View File

@ -0,0 +1,59 @@
{
"version": 2,
"cmakeMinimumRequired": {
"major": 3,
"minor": 13,
"patch": 0
},
"configurePresets": [
{
"name": "cmake-pedantic",
"description": "Enables all CMake warnings.`",
"hidden": true,
"warnings": {
"dev": true,
"deprecated": true,
"uninitialized": true,
"unusedCli": true,
"systemVars": false
}
},
{
"name": "dev-mode",
"hidden": true,
"description": "Common (non-OS specific) mode for development",
"inherits": "cmake-pedantic",
"cacheVariables": {
"UVW_BUILD_TESTING": true,
"libuv_buildtests": false
}
},
{
"name": "flags-linux",
"hidden": true,
"description": "Compiler flags for GNU and Clang compilers. When compiling in DEBUG mode, all warnings will be converted into errors.",
"cacheVariables": {
"CMAKE_CXX_FLAGS": "-Wall -Wextra -Wpedantic -Wconversion -Wsign-conversion -Wcast-qual -Wformat=2 -Wundef -Wshadow -Wcast-align -Wunused -Wnull-dereference -Wimplicit-fallthrough -Woverloaded-virtual -Wnon-virtual-dtor -Wold-style-cast",
"CMAKE_CXX_FLAGS_DEBUG": "-Werror"
}
},
{
"name": "ci-linux",
"generator": "Unix Makefiles",
"hidden": true,
"inherits": ["flags-linux"],
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Debug"
}
},
{
"name": "ci-build",
"binaryDir": "${sourceDir}/build",
"hidden": true
},
{
"name": "ci-ubuntu",
"inherits": ["ci-build", "ci-linux", "dev-mode"]
}
]
}

View File

@ -1,6 +1,6 @@
The MIT License (MIT)
Copyright (c) 2016-2018 Michele Caini
Copyright (c) 2016-2024 Michele Caini
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

397
README.md
View File

@ -1,27 +1,34 @@
![uvw - libuv wrapper in modern C++](https://user-images.githubusercontent.com/1812216/46069406-c977a600-c17b-11e8-9a47-9bba6f412c57.png)
<!--
@cond TURN_OFF_DOXYGEN
-->
[![Build Status](https://travis-ci.org/skypjack/uvw.svg?branch=master)](https://travis-ci.org/skypjack/uvw)
[![Build status](https://ci.appveyor.com/api/projects/status/m5ndm8gnu8isg2to?svg=true)](https://ci.appveyor.com/project/skypjack/uvw)
[![Coverage Status](https://coveralls.io/repos/github/skypjack/uvw/badge.svg?branch=master)](https://coveralls.io/github/skypjack/uvw?branch=master)
[![Download](https://api.bintray.com/packages/skypjack/conan/uvw%3Askypjack/images/download.svg)](https://bintray.com/skypjack/conan/uvw%3Askypjack/_latestVersion)
[![Build Status](https://github.com/skypjack/uvw/workflows/build/badge.svg)](https://github.com/skypjack/uvw/actions)
[![Coverage](https://codecov.io/gh/skypjack/uvw/branch/master/graph/badge.svg)](https://codecov.io/gh/skypjack/uvw)
[![Documentation](https://img.shields.io/badge/docs-doxygen-blue)](https://skypjack.github.io/uvw/)
[![Vcpkg port](https://img.shields.io/vcpkg/v/uvw)](https://vcpkg.link/ports/uvw)
[![Gitter chat](https://badges.gitter.im/skypjack/uvw.png)](https://gitter.im/skypjack/uvw)
[![Donate](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=W2HF9FESD5LJY&lc=IT&item_name=Michele%20Caini&currency_code=EUR&bn=PP%2dDonationsBF%3abtn_donateCC_LG%2egif%3aNonHosted)
[![Donate](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.me/skypjack)
[![Patreon](https://c5.patreon.com/external/logo/become_a_patron_button.png)](https://www.patreon.com/bePatron?u=11330786)
<!--
@endcond TURN_OFF_DOXYGEN
-->
Do you have a **question** that doesn't require you to open an issue? Join the
[gitter channel](https://gitter.im/skypjack/uvw).<br/>
If you use `uvw` and you want to say thanks or support the project, please
**consider becoming a
[sponsor](https://github.com/users/skypjack/sponsorship)**.<br/>
You can help me make the difference.
[Many thanks](https://skypjack.github.io/sponsorship/) to those who supported me
and still support me today.
# Introduction
`uvw` is a header-only, event based, tiny and easy to use *libuv* wrapper in modern C++.<br/>
The basic idea is to hide completely the *C-ish* interface of *libuv* behind a graceful C++ API. Currently, no `uv_*_t` data structure is actually exposed by the library.<br/>
Note that `uvw` stays true to the API of *libuv* and it doesn't add anything to its interface. For the same reasons, users of the library must follow the same rules who are used to follow with *libuv*.<br/>
As an example, a *handle* should be initialized before any other operation and closed once it is no longer in use.
`uvw` started as a header-only, event based, tiny and easy to use wrapper for
[`libuv`](https://github.com/libuv/libuv) written in modern C++.<br/>
Now it's finally available also as a compilable static library.
The basic idea is to wrap the *C-ish* interface of `libuv` behind a graceful C++
API.<br/>
Note that `uvw` stays true to the API of `libuv` and it doesn't add anything to
its interface. For the same reasons, users of the library must follow the same
rules which are used with `libuv`.<br/>
As an example, a *handle* should be initialized before any other operation and
closed once it is no longer in use.
## Code Example
@ -29,15 +36,15 @@ As an example, a *handle* should be initialized before any other operation and c
#include <uvw.hpp>
#include <memory>
void listen(uvw::Loop &loop) {
std::shared_ptr<uvw::TCPHandle> tcp = loop.resource<uvw::TCPHandle>();
void listen(uvw::loop &loop) {
std::shared_ptr<uvw::tcp_handle> tcp = loop.resource<uvw::tcp_handle>();
tcp->once<uvw::ListenEvent>([](const uvw::ListenEvent &, uvw::TCPHandle &srv) {
std::shared_ptr<uvw::TCPHandle> client = srv.loop().resource<uvw::TCPHandle>();
tcp->on<uvw::listen_event>([](const uvw::listen_event &, uvw::tcp_handle &srv) {
std::shared_ptr<uvw::tcp_handle> client = srv.parent().resource<uvw::tcp_handle>();
client->on<uvw::close_event>([ptr = srv.shared_from_this()](const uvw::close_event &, uvw::tcp_handle &) { ptr->close(); });
client->on<uvw::end_event>([](const uvw::end_event &, uvw::tcp_handle &client) { client.close(); });
auto ptr = srv.shared_from_this();
client->on<uvw::CloseEvent>([ptr](const uvw::CloseEvent &, uvw::TCPHandle &) { ptr->close(); });
client->on<uvw::EndEvent>([](const uvw::EndEvent &, uvw::TCPHandle &client) { client.close(); });
srv.accept(*client);
client->read();
});
@ -46,12 +53,12 @@ void listen(uvw::Loop &loop) {
tcp->listen();
}
void conn(uvw::Loop &loop) {
auto tcp = loop.resource<uvw::TCPHandle>();
void conn(uvw::loop &loop) {
auto tcp = loop.resource<uvw::tcp_handle>();
tcp->on<uvw::ErrorEvent>([](const uvw::ErrorEvent &, uvw::TCPHandle &) { /* handle errors */ });
tcp->on<uvw::error_event>([](const uvw::error_event &, uvw::tcp_handle &) { /* handle errors */ });
tcp->once<uvw::ConnectEvent>([](const uvw::ConnectEvent &, uvw::TCPHandle &tcp) {
tcp->on<uvw::connect_event>([](const uvw::connect_event &, uvw::tcp_handle &tcp) {
auto dataWrite = std::unique_ptr<char[]>(new char[2]{ 'b', 'c' });
tcp.write(std::move(dataWrite), 2);
tcp.close();
@ -61,7 +68,7 @@ void conn(uvw::Loop &loop) {
}
int main() {
auto loop = uvw::Loop::getDefault();
auto loop = uvw::loop::get_default();
listen(*loop);
conn(*loop);
loop->run();
@ -70,7 +77,8 @@ int main() {
## Motivation
The main reason for which `uvw` has been written is the fact that it does not exist a valid *libuv* wrapper in C++. That's all.
The main reason for which `uvw` has been written is the fact that there does not
exist a valid `libuv` wrapper in C++. That's all.
# Build Instructions
@ -78,79 +86,130 @@ The main reason for which `uvw` has been written is the fact that it does not ex
To be able to use `uvw`, users must provide the following system-wide tools:
* A full-featured compiler that supports at least C++11.
* `libuv` (which version depends on the tag of `uvw` in use).
* A full-featured compiler that supports at least C++17.
* `libuv` (which version depends on the tag of `uvw` in use)
* If you use `meson`, libuv will be downloaded for you
The requirements below are mandatory to compile the tests and to extract the documentation:
The requirements below are mandatory to compile the tests and to extract the
documentation:
* CMake version 3.2 or later.
* CMake version 3.13 or later.
* Doxygen version 1.8 or later.
Note that `libuv` is part of the dependencies of the project and it will be cloned by `cmake` (see below for further details).<br/>
Because of that, users have not to install it to compile and execute the tests.
Note that `libuv` is part of the dependencies of the project and may be cloned
by `CMake` in some cases (see below for further details).<br/>
Because of that, users don't have to install it to run the tests or when `uvw`
libraries are compiled through `CMake`.
## Meson
You can use `uvw` with [meson](https://mesonbuild.com/) by simply adding it to
your `subprojects` directory in your project.
To compile `uvw` from source without using it as a subproject, in the `uvw`
source directory, run:
* `$ meson setup build`
* If you want a static library, add `--default-library=static`
* `$ cd build`
* `$ meson compile`
## Library
`uvw` is a header-only library.<br/>
This means that including the `uvw.hpp` header or one of the other `uvw/*.hpp` headers is enough to use it.<br/>
`uvw` is a dual-mode library. It can be used in its header-only form or as a
compiled static library.<br/>
The following sections describe what to do in both cases to get `uvw` up and
runningin your own project.
### Header-only
To use `uvw` as a header-only library, all is needed is to include the `uvw.hpp`
header or one of the other `uvw/*.hpp` files.<br/>
It's a matter of adding the following line at the top of a file:
```cpp
#include <uvw.hpp>
```
Then pass the proper `-I` argument to the compiler to add the `src` directory to the include paths.<br/>
Note that users are demanded to correctly setup include directories and libraries search paths for *libuv*.
Then pass the proper `-I` argument to the compiler to add the `src` directory to
the include paths.<br/>
Note that users are required to correctly setup the include directories and
libraries search paths for `libuv` in this case.
When used through `CMake`, the `uvw::uvw` target is exported for convenience.
### Static
To use `uvw` as a compiled library, set the `UVW_BUILD_LIBS` options in cmake
before including the project.<br/>
This option triggers the generation of a targets named
`uvw::uvw-static`. The matching version of `libuv` is also
compiled and exported as `uv::uv-static` for convenience.
In case you don't use or don't want to use `CMake`, you can still compile all
`.cpp` files and include all `.h` files to get the job done. In this case, users
are required to correctly setup the include directories and libraries search
paths for `libuv`.
## Versioning
Starting with tag _v1.12.0_ of `libuv`, `uvw` follows the [semantic versioning](http://semver.org/) scheme.<br/>
The problem is that any version of `uvw` also requires to track explicitly the version of `libuv` to which it is bound.<br/>
Because of that, the latter wil be appended to the version of `uvw`. As an example:
Starting with tag _v1.12.0_ of `libuv`, `uvw` follows the
[semantic versioning](http://semver.org/) scheme.<br/>
The problem is that any version of `uvw` also requires to track explicitly the
version of `libuv` to which it is bound.<br/>
Because of that, the latter wil be appended to the version of `uvw`. As an
example:
vU.V.W_libuv-vX.Y
In particular, the following applies:
* _U.V.W_ are major, minor and patch versions of `uvw`.
* _X.Y_ is the version of `libuv` to which to refer (where any patch version is valid).
* _X.Y_ is the version of `libuv` to which to refer (where any patch version is
valid).
In other terms, tags will look like this from now on:
v1.0.0_libuv-v1.12
Branch `master` of `uvw` will be a work in progress branch that follows branch _v1.x_ of `libuv` (at least as long as it remains their _master_ branch).<br/>
Branch `master` of `uvw` will be a work in progress branch that follows branch
_v1.x_ of `libuv` (at least as long as it remains their _master_ branch).<br/>
## Documentation
The documentation is based on [`doxygen`](http://www.stack.nl/~dimitri/doxygen/). To build it:
The documentation is based on
[`doxygen`](https://www.doxygen.nl/). To build it:
* `$ cd build`
* `$ cmake ..`
* `$ make docs`
The API reference will be created in HTML format within the directory `build/docs/html`.<br/>
The API reference will be created in HTML format within the directory
`build/docs/html`.<br/>
To navigate it with your favorite browser:
* `$ cd build`
* `$ your_favorite_browser docs/html/index.html`
The API reference is also available [online](https://skypjack.github.io/uvw/) for the latest version.
The same version is also available [online](https://skypjack.github.io/uvw/)
for the latest release, that is the last stable tag.
### Note
The documentation is mostly inspired by the official [libuv API documentation](http://docs.libuv.org/en/v1.x/) for obvious reasons.<br/>
If you are mainly interested in the way `uvw` imports `libuv` in a `cmake` based project, I suggest you to take a look at [this](https://github.com/skypjack/libuv_cmake) repository instead.
The documentation is mostly inspired by the official
[libuv API documentation](http://docs.libuv.org/en/v1.x/) for obvious
reasons.
## Tests
To compile and run the tests, `uvw` requires *libuv* and *googletest*.<br/>
`cmake` will download and compile both the libraries before to compile anything else.
To compile and run the tests, `uvw` requires `libuv` and `googletest`.<br/>
`CMake` will download and compile both the libraries before compiling anything
else.
To build the tests:
* `$ cd build`
* `$ cmake .. -DBUILD_TESTING=ON`
* `$ cmake .. -DUVW_BUILD_TESTING=ON`
* `$ make`
* `$ ctest -j4 -R uvw`
@ -160,63 +219,83 @@ Omit `-R uvw` if you also want to test `libuv` and other dependencies.
## Vademecum
There is only one rule when using `uvw`: always initialize the resources and terminate them.
There is only one rule when using `uvw`: always initialize the resources and
terminate them.
Resources belong mainly to two families: _handles_ and _requests_.<br/>
Handles represent long-lived objects capable of performing certain operations while active.<br/>
Requests represent (typically) short-lived operations performed either over a handle or standalone.
Handles represent long-lived objects capable of performing certain operations
while active.<br/>
Requests represent (typically) short-lived operations performed either over a
handle or standalone.
The following sections will explain in short what it means to initialize and terminate these kinds of resources.<br/>
For more details, please refer to the [online documentation](https://skypjack.github.io/uvw/).
The following sections will explain in short what it means to initialize and
terminate these kinds of resources.<br/>
For more details, please refer to the
[online documentation](https://skypjack.github.io/uvw/).
## Handles
Initialization is usually performed under the hood and can be even passed over, as far as handles are created using the `Loop::resource` member function.<br/>
On the other side, handles keep themselves alive until one explicitly closes them. Because of that, memory usage will grow up if users simply forget about a handle.<br/>
Therefore the rule quickly becomes *always close your handles*. It's as simple as calling the `close` member function on them.
Initialization is usually performed under the hood and can be even passed over,
as far as handles are created using the `loop::resource` member function.<br/>
On the other side, handles keep themselves alive until one explicitly closes
them. Because of that, memory usage will grow if users simply forget about a
handle.<br/>
Therefore the rule quickly becomes *always close your handles*. It's as simple
as calling the `close` member function on them.
## Requests
Usually initializing a request object is not required. Anyway, the recommended way to create a request is still through the `Loop::resource` member function.<br/>
Requests will keep themselves alive as long as they are bound to unfinished underlying activities. This means that users have not to discard explicitly a request.<br/>
Therefore the rule quickly becomes *feel free to make a request and forget about it*. It's as simple as calling a member function on them.
Usually initializing a request object is not required. Anyway, the recommended
way to create a request is still through the `loop::resource` member
function.<br/>
Requests will keep themselves alive as long as they are bound to unfinished
underlying activities. This means that users don't have to discard a
request explicitly .<br/>
Therefore the rule quickly becomes *feel free to make a request and forget about
it*. It's as simple as calling a member function on them.
## The Loop and the Resource
The first thing to do to use `uvw` is to create a loop. In case the default one is enough, it's easy as doing this:
The first thing to do to use `uvw` is to create a loop. In case the default one
is enough, it's easy as doing this:
```cpp
auto loop = uvw::Loop::getDefault();
auto loop = uvw::loop::get_default();
```
Note that loop objects don't require to be closed explicitly, even if they offer the `close` member function in case an user wants to do that.<br/>
Loops can be started using the `run` member function. The two calls below are equivalent:
Note that loop objects don't require being closed explicitly, even if they offer
the `close` member function in case a user wants to do that.<br/>
Loops can be started using the `run` member function. The two calls below are
equivalent:
```cpp
loop->run();
loop->run<uvw::Loop::Mode::DEFAULT>();
loop->run(uvw::loop::run_mode::DEFAULT);
```
Available modes are: `DEFAULT`, `ONCE`, `NOWAIT`. Please refer to the documentation of *libuv* for further details.
Available modes are: `DEFAULT`, `ONCE`, `NOWAIT`. Please refer to the
documentation of `libuv` for further details.
In order to create a resource and to bind it to the given loop, just do the following:
In order to create a resource and to bind it to the given loop, just do the
following:
```cpp
auto tcp = loop.resource<uvw::TCPHandle>();
auto tcp = loop->resource<uvw::tcp_handle>();
```
The line above will create and initialize a tcp handle, then a shared pointer to that resource will be returned.<br/>
Users should check if pointers have been correctly initialized: in case of errors, they won't be.<br/>
Another way to create a resource is:
The line above creates and initializes a tcp handle, then a shared pointer to
that resource is returned.<br/>
Users should check if pointers have been correctly initialized: in case of
errors, they won't be.<br/>
It also is possible to create uninitialized resources to init later on as:
```cpp
auto tcp = TCPHandle::create(loop);
auto tcp = loop->uninitialized_resource<uvw::tcp_handle>();
tcp->init();
```
Pretty annoying indeed. Using a loop is the recommended approach.
The resources also accept arbitrary user-data that won't be touched in any case.<br/>
All resources also accept arbitrary user-data that won't be touched in any
case.<br/>
Users can set and get them through the `data` member function as it follows:
```cpp
@ -224,73 +303,86 @@ resource->data(std::make_shared<int>(42));
std::shared_ptr<void> data = resource->data();
```
Resources expect a `std::shared_pointer<void>` and return it, therefore any kind of data is welcome.<br/>
Users can explicitly specify a type other than `void` when calling the `data` member function:
Resources expect a `std::shared_pointer<void>` and return it, therefore any kind
of data is welcome.<br/>
Users can explicitly specify a type other than `void` when calling the `data`
member function:
```cpp
std::shared_ptr<int> data = resource->data<int>();
```
Remember from the previous section that a handle will keep itself alive until one invokes the `close` member function on it.<br/>
To know what are the handles that are still alive and bound to a given loop, just do the following:
Remember from the previous section that a handle will keep itself alive until
one invokes the `close` member function on it.<br/>
To know what are the handles that are still alive and bound to a given loop,
there exists the `walk` member function. It returns handles with their types.
Therefore, the use of `overloaded` is recommended to be able to intercept all
types of interest:
```cpp
loop.walk([](uvw::BaseHandle &){ /* application code here */ });
handle.parent().walk(uvw::overloaded{
[](uvw::timer_handle &h){ /* application code for timers here */ },
[](auto &&){ /* ignore all other types */ }
});
```
`BaseHandle` exposes a few methods and cannot be promoted to the original type of the handle (even though `type` and `category` member functions fill the gap somehow).<br/>
Anyway, it can be used to close the handle that originated from it. As an example, all the pending handles can be closed easily as it follows:
This function can also be used for a completely generic approach. For example,
all the pending handles can be closed easily as it follows:
```cpp
loop.walk([](uvw::BaseHandle &h){ h.close(); });
loop->walk([](auto &&h){ h.close(); });
```
No need to keep track of them.
To know what are the available resources' types, please refer the API reference.
## The event-based approach
For `uvw` offers an event-based approach, resources are small event emitters to which listeners can be attached.<br/>
Attaching a listener to a resource is the recommended way to be notified about changes.<br/>
Listeners must be callable objects of type `void(EventType &, ResourceType &)`, where:
`uvw` offers an event-based approach where resources are small event emitters to
which listeners are attached.<br/>
Attaching listeners to resources is the recommended way to receive notifications
about their operations.<br/>
Listeners are callable objects of type `void(event_type &, resource_type &)`,
where:
* `EventType` is the type of the event for which they have been designed.
* `ResourceType` is the type of the resource that has originated the event.
* `event_type` is the type of the event for which they have been designed.
* `resource_type` is the type of the resource that has originated the event.
It means that the following function types are all valid:
* `void(EventType &, ResourceType &)`
* `void(const EventType &, ResourceType &)`
* `void(EventType &, const ResourceType &)`
* `void(const EventType &, const ResourceType &)`
* `void(event_type &, resource_type &)`
* `void(const event_type &, resource_type &)`
* `void(event_type &, const resource_type &)`
* `void(const event_type &, const resource_type &)`
Once more, please note that there is no need to keep around references to the resources: they will pass themselves as an argument whenever an event is published.
Please note that there is no need to keep around references to the resources,
since they pass themselves as an argument whenever an event is published.<br/>
The `on` member function is the way to go to register long-running listeners:
There exist two methods to attach an event to a resource:
```cpp
resource.on<event_type>(listener)
```
* `resource.once<EventType>(listener)`: the listener will be automatically removed after the first event of the given type.
* `resource.on<EventType>(listener)`: to be used for long-running listeners.
To know if a listener exists for a given type, the class offers a `has` function
template. Similarly, the `reset` function template is be used to reset and thus
disconnect listeners, if any. A non-template version of `reset` also exists to
clear an emitter as a whole.
Both of them return an object of type `ResourceType::Connection` (as an example, `TCPHandle::Connection`).<br/>
A connection object can be used later as an argument to the `erase` member function of the resource to remove the listener.<br/>
There exists also the `clear` member function to drop all the listeners at once.
Almost all the resources use to emit `ErrorEvent` events in case of errors.<br/>
All the other events are specific for the given resource and documented in the API reference.
Almost all the resources emit `error_event` in case of errors.<br/>
All the other events are specific for the given resource and documented in the
API reference.
The code below shows how to create a simple tcp server using `uvw`:
```cpp
auto loop = uvw::Loop::getDefault();
auto tcp = loop.resource<uvw::TCPHandle>();
auto loop = uvw::loop::get_default();
auto tcp = loop->resource<uvw::tcp_handle>();
tcp->on<uvw::ErrorEvent>([](const uvw::ErrorEvent &, uvw::TCPHandle &) { /* something went wrong */ });
tcp->on<uvw::error_event>([](const uvw::error_event &, uvw::tcp_handle &) { /* something went wrong */ });
tcp->on<uvw::ListenEvent>([](const uvw::ListenEvent &, uvw::TCPHandle &srv) {
std::shared_ptr<uvw::TCPHandle> client = srv.loop().resource<uvw::TCPHandle>();
client->once<uvw::EndEvent>([](const uvw::EndEvent &, uvw::TCPHandle &client) { client.close(); });
client->on<uvw::DataEvent>([](const uvw::DataEvent &, uvw::TCPHandle &) { /* data received */ });
tcp->on<uvw::listen_event>([](const uvw::listen_event &, uvw::tcp_handle &srv) {
std::shared_ptr<uvw::tcp_handle> client = srv.parent().resource<uvw::tcp_handle>();
client->on<uvw::end_event>([](const uvw::end_event &, uvw::tcp_handle &client) { client.close(); });
client->on<uvw::data_event>([](const uvw::data_event &, uvw::tcp_handle &) { /* data received */ });
srv.accept(*client);
client->read();
});
@ -299,10 +391,9 @@ tcp->bind("127.0.0.1", 4242);
tcp->listen();
```
Note also that `uvw::TCPHandle` already supports _IPv6_ out-of-the-box. The statement above is equivalent to `tcp->bind<uvw::IPv4>("127.0.0.1", 4242)`.<br/>
It's suffice to explicitly specify `uvw::IPv6` as the underlying protocol to use it.
The API reference is the recommended documentation for further details about resources and their methods.
Note also that `uvw::tcp_handle` already supports _IPv6_ out-of-the-box.<br/>
The API reference is the recommended documentation for further details about
resources and their methods.
## Going raw
@ -312,14 +403,14 @@ reasons, almost all the classes in `uvw` give direct access to them.<br/>
Please, note that this functions should not be used directly unless users know
exactly what they are doing and what are the risks. Going raw is dangerous,
mainly because the lifetime management of a loop, a handle or a request is
completely in charge to the library and working around it could quickly break
completely controlled by the library and working around it could quickly break
things.
That being said, _going raw_ is a matter of using the `raw` member functions:
```cpp
auto loop = uvw::Loop::getDefault();
auto tcp = loop.resource<uvw::TCPHandle>();
auto loop = uvw::loop::get_default();
auto tcp = loop->resource<uvw::tcp_handle>();
uv_loop_t *raw = loop->raw();
uv_tcp_t *handle = tcp->raw();
@ -327,51 +418,39 @@ uv_tcp_t *handle = tcp->raw();
Go the raw way at your own risk, but do not expect any support in case of bugs.
# Related projects
Interested in additional tools and libraries that build upon `uvw`? You might
find the following useful then:
- [`uvw_net`](https://github.com/mincequi/uvw_net): a networking library with a
collection of clients (HTTP/Modbus/SunSpec) that also includes discovery
impementations like dns-sd/mdns.
Feel free to add your tool to the list if you like.
# Contributors
If you want to contribute, please send patches as pull requests against the branch master.<br/>
Check the [contributors list](https://github.com/skypjack/uvw/blob/master/AUTHORS) to see who has partecipated so far.
If you want to contribute, please send patches as pull requests against the
branch master.<br/>
Check the
[contributors list](https://github.com/skypjack/uvw/blob/master/AUTHORS) to see
who has partecipated so far.
# License
Code and documentation Copyright (c) 2016-2018 Michele Caini.<br/>
Logo Copyright (c) 2018 Richard Caseres.
Code and documentation Copyright (c) 2016-2024 Michele Caini.<br/>
Logo Copyright (c) 2018-2021 Richard Caseres.
Code released under
[the MIT license](https://github.com/skypjack/uvw/blob/master/LICENSE).
Documentation released under
[CC BY 4.0](https://creativecommons.org/licenses/by/4.0/).<br/>
Code and documentation released under
[the MIT license](https://github.com/skypjack/uvw/blob/master/LICENSE).<br/>
Logo released under
[CC BY-SA 4.0](https://creativecommons.org/licenses/by-sa/4.0/).
<!--
@cond TURN_OFF_DOXYGEN
-->
# Support
## Patreon
Become a [patron](https://www.patreon.com/bePatron?c=1772573) and get access to
extra content, help me spend more time on the projects you love and create new
ones for you. Your support will help me to continue the work done so far and
make it more professional and feature-rich every day.<br/>
It takes very little to
[become a patron](https://www.patreon.com/bePatron?c=1772573) and thus help the
software you use every day. Don't miss the chance.
## Donation
Developing and maintaining `uvw` takes some time and lots of coffee. It still lacks a proper test suite, documentation is partially incomplete and not all functionalities have been fully implemented yet.<br/>
If you want to support this project, you can offer me an espresso. I'm from Italy, we're used to turning the best coffee ever in code. If you find that it's not enough, feel free to support me the way you prefer.<br/>
Take a look at the donation button at the top of the page for more details or just click [here](https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=W2HF9FESD5LJY&lc=IT&item_name=Michele%20Caini&currency_code=EUR&bn=PP%2dDonationsBF%3abtn_donateCC_LG%2egif%3aNonHosted).
## Hire me
If you start using `uvw` and need help, if you want a new feature and want me
to give it the highest priority, if you have any other reason to contact me:
do not hesitate. I'm available for hiring.<br/>
Feel free to take a look at my [profile](https://github.com/skypjack) and
contact me by mail.
<!--
@endcond TURN_OFF_DOXYGEN
-->
If you want to support this project, you can
[offer me](https://github.com/users/skypjack/sponsorship) an espresso.<br/>
If you find that it's not enough, feel free to
[help me](https://www.paypal.me/skypjack) the way you prefer.

5
TODO Normal file
View File

@ -0,0 +1,5 @@
* do not send error events when the return value is enough (still wip)
* also cleanup error event mentions in the doc
* Make all tests pass on all platforms
* add iwyu and clean up everything
* Allocator support

View File

@ -1,22 +0,0 @@
# can use variables like {build} and {branch}
version: 1.0.{build}
image: Visual Studio 2017
environment:
BUILD_DIR: "%APPVEYOR_BUILD_FOLDER%\\build"
platform:
- Win32
configuration:
- Release
before_build:
- cd %BUILD_DIR%
- cmake .. -DBUILD_TESTING=ON -G"Visual Studio 15 2017"
build:
parallel: true
project: build/uvw.sln
verbosity: minimal

View File

@ -1,26 +0,0 @@
project(deps-download NONE)
cmake_minimum_required(VERSION 3.2)
include(ExternalProject)
ExternalProject_Add(
googletest
GIT_REPOSITORY https://github.com/google/googletest.git
GIT_TAG master
SOURCE_DIR @GOOGLETEST_DEPS_DIR@
CONFIGURE_COMMAND ""
BUILD_COMMAND ""
INSTALL_COMMAND ""
TEST_COMMAND ""
)
ExternalProject_Add(
libuv
GIT_REPOSITORY https://github.com/libuv/libuv.git
GIT_TAG v1.23.2
SOURCE_DIR @LIBUV_DEPS_DIR@
CONFIGURE_COMMAND ""
BUILD_COMMAND ""
INSTALL_COMMAND ""
TEST_COMMAND ""
)

View File

@ -2,18 +2,18 @@
# -*- coding: utf-8 -*-
from conans import ConanFile
class UVMConan(ConanFile):
class UVWConan(ConanFile):
name = "uvw"
description = "Header-only, event based, tiny and easy to use libuv wrapper in modern C++"
homepage = "https://github.com/skypjack/uvw"
url = homepage
license = "MIT"
topics = ("conan", "uvw", "libuv", "header-only", "wrapper", "event-loop")
author = "Michele Caini <michele.caini@gmail.com>"
exports = "LICENSE"
exports_sources = "src/*"
no_copy_source = True
requires = "libuv/1.23.2@bincrafters/stable"
requires = "libuv/1.49.0@bincrafters/stable"
def package(self):
self.copy(pattern="LICENSE", dst="licenses")

View File

@ -2,23 +2,19 @@
# Doxygen configuration (documentation)
#
set(TARGET_DOCS docs)
set(DOXY_IN_FILE doxy.in)
set(DOXY_SOURCE_DIRECTORY ${PROJECT_SRC_DIR})
set(DOXY_DEPS_DIRECTORY ${uvw_SOURCE_DIR}/deps)
set(DOXY_SOURCE_DIRECTORY ${uvw_SOURCE_DIR}/src)
set(DOXY_DOCS_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
set(DOXY_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
set(DOXY_CFG_FILE doxy.cfg)
configure_file(${DOXY_IN_FILE} ${DOXY_CFG_FILE} @ONLY)
configure_file(doxy.in doxy.cfg @ONLY)
add_custom_target(
${TARGET_DOCS}
COMMAND ${DOXYGEN_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/${DOXY_CFG_FILE}
docs ALL
COMMAND ${DOXYGEN_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/doxy.cfg
WORKING_DIRECTORY ${uvw_SOURCE_DIR}
VERBATIM
SOURCES ${DOXY_IN_FILE}
SOURCES doxy.in
)
install(

File diff suppressed because it is too large Load Diff

54
meson.build Normal file
View File

@ -0,0 +1,54 @@
project(
'uvw',
'cpp',
version: '3.3.0',
license: 'MIT',
default_options: ['cpp_std=c++17'],
)
libuv_dep = dependency('libuv', version: '1.48.0', required: true)
sources = [
'src/uvw/async.cpp',
'src/uvw/check.cpp',
'src/uvw/dns.cpp',
'src/uvw/emitter.cpp',
'src/uvw/fs.cpp',
'src/uvw/fs_event.cpp',
'src/uvw/fs_poll.cpp',
'src/uvw/idle.cpp',
'src/uvw/lib.cpp',
'src/uvw/loop.cpp',
'src/uvw/pipe.cpp',
'src/uvw/poll.cpp',
'src/uvw/prepare.cpp',
'src/uvw/process.cpp',
'src/uvw/signal.cpp',
'src/uvw/stream.cpp',
'src/uvw/tcp.cpp',
'src/uvw/thread.cpp',
'src/uvw/timer.cpp',
'src/uvw/tty.cpp',
'src/uvw/udp.cpp',
'src/uvw/util.cpp',
'src/uvw/work.cpp',
]
uvw_lib = library(
'uvw',
sources,
include_directories: 'src',
dependencies: [libuv_dep],
cpp_args: ['-DUVW_AS_LIB'],
install: true,
)
uvw_dep = declare_dependency(
include_directories: ['src'],
dependencies: [libuv_dep],
link_with: [uvw_lib],
)
if meson.version().version_compare('>=0.54.0')
meson.override_dependency('uvw', uvw_dep)
endif

82
src/CMakeLists.txt Normal file
View File

@ -0,0 +1,82 @@
#
# Setup libraries
#
function(add_uvw_library LIB_NAME)
target_sources(
${LIB_NAME}
PRIVATE
uvw/async.cpp
uvw/check.cpp
uvw/dns.cpp
uvw/emitter.cpp
uvw/fs.cpp
uvw/fs_event.cpp
uvw/fs_poll.cpp
uvw/idle.cpp
uvw/lib.cpp
uvw/loop.cpp
uvw/pipe.cpp
uvw/poll.cpp
uvw/prepare.cpp
uvw/process.cpp
uvw/signal.cpp
uvw/stream.cpp
uvw/tcp.cpp
uvw/thread.cpp
uvw/timer.cpp
uvw/tty.cpp
uvw/udp.cpp
uvw/util.cpp
uvw/work.cpp
)
set_target_properties(${LIB_NAME} PROPERTIES POSITION_INDEPENDENT_CODE 1)
target_compile_definitions(${LIB_NAME} PUBLIC UVW_AS_LIB)
target_compile_features(${LIB_NAME} PUBLIC cxx_std_17)
target_include_directories(
${LIB_NAME}
PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
)
if(UVW_USE_ASAN)
target_compile_options(${LIB_NAME} PUBLIC $<$<CONFIG:Debug>:-fsanitize=address -fno-omit-frame-pointer>)
target_link_libraries(${LIB_NAME} PUBLIC $<$<CONFIG:Debug>:-fsanitize=address>)
endif()
if(UVW_USE_UBSAN)
target_compile_options(${LIB_NAME} PUBLIC $<$<CONFIG:Debug>:-fsanitize=undefined -fno-sanitize-recover=all -fno-omit-frame-pointer>)
target_link_libraries(${LIB_NAME} PUBLIC $<$<CONFIG:Debug>:-fsanitize=undefined>)
endif()
if(UVW_HAS_LIBCPP)
target_compile_options(${LIB_NAME} BEFORE PUBLIC -stdlib=libc++)
endif()
endfunction()
#
# Build and install libraries
#
if (UVW_BUILD_SHARED_LIB)
add_library(uvw SHARED)
add_library(uvw::uvw-shared ALIAS uvw)
# If libuv is not fetched by ourselves, it's the caller's responsibility to make sure of the linkage.
if(UVW_FETCH_LIBUV OR libuv_FOUND)
target_link_libraries(uvw PUBLIC uv::uv-shared)
endif()
else()
add_library(uvw STATIC)
add_library(uvw::uvw-static ALIAS uvw)
# If libuv is not fetched by ourselves, it's the caller's responsibility to make sure of the linkage.
if(UVW_FETCH_LIBUV OR libuv_FOUND)
target_link_libraries(uvw PUBLIC uv::uv-static)
endif()
endif()
add_library(uvw::uvw ALIAS uvw)
set_target_properties(uvw PROPERTIES EXCLUDE_FROM_DEFAULT_BUILD 1)
add_uvw_library(uvw)

View File

@ -1,21 +1,28 @@
#include "uvw/async.hpp"
#include "uvw/check.hpp"
#include "uvw/dns.hpp"
#include "uvw/fs.hpp"
#include "uvw/fs_event.hpp"
#include "uvw/fs_poll.hpp"
#include "uvw/idle.hpp"
#include "uvw/lib.hpp"
#include "uvw/loop.hpp"
#include "uvw/pipe.hpp"
#include "uvw/poll.hpp"
#include "uvw/prepare.hpp"
#include "uvw/process.hpp"
#include "uvw/signal.hpp"
#include "uvw/tcp.hpp"
#include "uvw/thread.hpp"
#include "uvw/timer.hpp"
#include "uvw/tty.hpp"
#include "uvw/udp.hpp"
#include "uvw/util.hpp"
#include "uvw/work.hpp"
#include "uvw/async.h"
#include "uvw/check.h"
#include "uvw/config.h"
#include "uvw/dns.h"
#include "uvw/emitter.h"
#include "uvw/enum.hpp"
#include "uvw/fs.h"
#include "uvw/fs_event.h"
#include "uvw/fs_poll.h"
#include "uvw/handle.hpp"
#include "uvw/idle.h"
#include "uvw/lib.h"
#include "uvw/loop.h"
#include "uvw/pipe.h"
#include "uvw/poll.h"
#include "uvw/prepare.h"
#include "uvw/process.h"
#include "uvw/request.hpp"
#include "uvw/resource.hpp"
#include "uvw/signal.h"
#include "uvw/tcp.h"
#include "uvw/thread.h"
#include "uvw/timer.h"
#include "uvw/tty.h"
#include "uvw/udp.h"
#include "uvw/util.h"
#include "uvw/uv_type.hpp"
#include "uvw/work.h"

2
src/uvw/async.cpp Normal file
View File

@ -0,0 +1,2 @@
#include "async.h"
#include "async.ipp"

58
src/uvw/async.h Normal file
View File

@ -0,0 +1,58 @@
#ifndef UVW_ASYNC_INCLUDE_H
#define UVW_ASYNC_INCLUDE_H
#include <uv.h>
#include "handle.hpp"
#include "loop.h"
namespace uvw {
/*! @brief Async event. */
struct async_event {};
/**
* @brief The async handle.
*
* Async handles allow the user to _wakeup_ the event loop and get an event
* emitted from another thread.
*
* To create an `async_handle` through a `loop`, no arguments are required.
*/
class async_handle final: public handle<async_handle, uv_async_t, async_event> {
static void send_callback(uv_async_t *hndl);
public:
using handle::handle;
/**
* @brief Initializes the handle.
*
* Unlike other handle initialization functions, it immediately starts the
* handle.
*
* @return Underlying return value.
*/
int init();
/**
* @brief Wakeups the event loop and emits the async event.
*
* Its safe to call this function from any thread.<br/>
* An async event is emitted on the loop thread.
*
* See the official
* [documentation](http://docs.libuv.org/en/v1.x/async.html#c.uv_async_send)
* for further details.
*
* @return Underlying return value.
*/
int send();
};
} // namespace uvw
#ifndef UVW_AS_LIB
# include "async.ipp"
#endif
#endif // UVW_ASYNC_INCLUDE_H

View File

@ -1,67 +0,0 @@
#pragma once
#include <utility>
#include <memory>
#include <uv.h>
#include "handle.hpp"
#include "loop.hpp"
namespace uvw {
/**
* @brief AsyncEvent event.
*
* It will be emitted by AsyncHandle according with its functionalities.
*/
struct AsyncEvent {};
/**
* @brief The AsyncHandle handle.
*
* Async handles allow the user to _wakeup_ the event loop and get an event
* emitted from another thread.
*
* To create an `AsyncHandle` through a `Loop`, no arguments are required.
*/
class AsyncHandle final: public Handle<AsyncHandle, uv_async_t> {
static void sendCallback(uv_async_t *handle) {
AsyncHandle &async = *(static_cast<AsyncHandle*>(handle->data));
async.publish(AsyncEvent{});
}
public:
using Handle::Handle;
/**
* @brief Initializes the handle.
*
* Unlike other handle initialization functions, it immediately starts the
* handle.
*
* @return True in case of success, false otherwise.
*/
bool init() {
return initialize(&uv_async_init, &sendCallback);
}
/**
* @brief Wakeups the event loop and emits the AsyncEvent event.
*
* Its safe to call this function from any thread.<br/>
* An AsyncEvent event will be emitted on the loop thread.
*
* See the official
* [documentation](http://docs.libuv.org/en/v1.x/async.html#c.uv_async_send)
* for further details.
*/
void send() {
invoke(&uv_async_send, get());
}
};
}

18
src/uvw/async.ipp Normal file
View File

@ -0,0 +1,18 @@
#include "config.h"
namespace uvw {
UVW_INLINE void async_handle::send_callback(uv_async_t *hndl) {
async_handle &async = *(static_cast<async_handle *>(hndl->data));
async.publish(async_event{});
}
UVW_INLINE int async_handle::init() {
return leak_if(uv_async_init(parent().raw(), raw(), &send_callback));
}
UVW_INLINE int async_handle::send() {
return uv_async_send(raw());
}
} // namespace uvw

2
src/uvw/check.cpp Normal file
View File

@ -0,0 +1,2 @@
#include "check.h"
#include "check.ipp"

56
src/uvw/check.h Normal file
View File

@ -0,0 +1,56 @@
#ifndef UVW_CHECK_INCLUDE_H
#define UVW_CHECK_INCLUDE_H
#include <uv.h>
#include "handle.hpp"
#include "loop.h"
namespace uvw {
/*! @brief Check event. */
struct check_event {};
/**
* @brief The check handle.
*
* Check handles will emit a check event once per loop iteration, right after
* polling for I/O.
*
* To create a `check_handle` through a `loop`, no arguments are required.
*/
class check_handle final: public handle<check_handle, uv_check_t, check_event> {
static void start_callback(uv_check_t *hndl);
public:
using handle::handle;
/**
* @brief Initializes the handle.
* @return Underlying return value.
*/
int init();
/**
* @brief Starts the handle.
*
* A check event will be emitted once per loop iteration, right after
* polling for I/O.
*
* @return Underlying return value.
*/
int start();
/**
* @brief Stops the handle.
* @return Underlying return value.
*/
int stop();
};
} // namespace uvw
#ifndef UVW_AS_LIB
# include "check.ipp"
#endif
#endif // UVW_CHECK_INCLUDE_H

View File

@ -1,66 +0,0 @@
#pragma once
#include <utility>
#include <memory>
#include <uv.h>
#include "handle.hpp"
#include "loop.hpp"
namespace uvw {
/**
* @brief CheckEvent event.
*
* It will be emitted by CheckHandle according with its functionalities.
*/
struct CheckEvent {};
/**
* @brief The CheckHandle handle.
*
* Check handles will emit a CheckEvent event once per loop iteration, right
* after polling for I/O.
*
* To create a `CheckHandle` through a `Loop`, no arguments are required.
*/
class CheckHandle final: public Handle<CheckHandle, uv_check_t> {
static void startCallback(uv_check_t *handle) {
CheckHandle &check = *(static_cast<CheckHandle*>(handle->data));
check.publish(CheckEvent{});
}
public:
using Handle::Handle;
/**
* @brief Initializes the handle.
* @return True in case of success, false otherwise.
*/
bool init() {
return initialize(&uv_check_init);
}
/**
* @brief Starts the handle.
*
* A CheckEvent event will be emitted once per loop iteration, right after
* polling for I/O.
*/
void start() {
invoke(&uv_check_start, get(), &startCallback);
}
/**
* @brief Stops the handle.
*/
void stop() {
invoke(&uv_check_stop, get());
}
};
}

22
src/uvw/check.ipp Normal file
View File

@ -0,0 +1,22 @@
#include "config.h"
namespace uvw {
UVW_INLINE void check_handle::start_callback(uv_check_t *hndl) {
check_handle &check = *(static_cast<check_handle *>(hndl->data));
check.publish(check_event{});
}
UVW_INLINE int check_handle::init() {
return leak_if(uv_check_init(parent().raw(), raw()));
}
UVW_INLINE int check_handle::start() {
return uv_check_start(raw(), &start_callback);
}
UVW_INLINE int check_handle::stop() {
return uv_check_stop(raw());
}
} // namespace uvw

10
src/uvw/config.h Normal file
View File

@ -0,0 +1,10 @@
#ifndef UVW_CONFIG_H
#define UVW_CONFIG_H
#ifndef UVW_AS_LIB
# define UVW_INLINE inline
#else
# define UVW_INLINE
#endif
#endif

2
src/uvw/dns.cpp Normal file
View File

@ -0,0 +1,2 @@
#include "dns.h"
#include "dns.ipp"

View File

@ -1,29 +1,21 @@
#pragma once
#ifndef UVW_DNS_INCLUDE_H
#define UVW_DNS_INCLUDE_H
#include <utility>
#include <memory>
#include <string>
#include <utility>
#include <uv.h>
#include "loop.h"
#include "request.hpp"
#include "util.hpp"
#include "loop.hpp"
#include "util.h"
namespace uvw {
/*! @brief The addrinfo event. */
struct addr_info_event {
using deleter = void (*)(addrinfo *);
/**
* @brief AddrInfoEvent event.
*
* It will be emitted by GetAddrInfoReq according with its functionalities.
*/
struct AddrInfoEvent {
using Deleter = void(*)(addrinfo *);
AddrInfoEvent(std::unique_ptr<addrinfo, Deleter> addr)
: data{std::move(addr)}
{}
addr_info_event(std::unique_ptr<addrinfo, deleter> addr);
/**
* @brief An initialized instance of `addrinfo`.
@ -31,19 +23,12 @@ struct AddrInfoEvent {
* See [getaddrinfo](http://linux.die.net/man/3/getaddrinfo) for further
* details.
*/
std::unique_ptr<addrinfo, Deleter> data;
std::unique_ptr<addrinfo, deleter> data;
};
/**
* @brief NameInfoEvent event.
*
* It will be emitted by GetNameInfoReq according with its functionalities.
*/
struct NameInfoEvent {
NameInfoEvent(const char *host, const char *serv)
: hostname{host}, service{serv}
{}
/*! @brief The nameinfo event. */
struct name_info_event {
name_info_event(const char *host, const char *serv);
/**
* @brief A valid hostname.
@ -62,54 +47,32 @@ struct NameInfoEvent {
const char *service;
};
/**
* @brief The GetAddrInfoReq request.
* @brief The getaddrinfo request.
*
* Wrapper for [getaddrinfo](http://linux.die.net/man/3/getaddrinfo).<br/>
* It offers either asynchronous and synchronous access methods.
*
* To create a `GetAddrInfoReq` through a `Loop`, no arguments are required.
* To create a `get_addr_info_req` through a `loop`, no arguments are required.
*/
class GetAddrInfoReq final: public Request<GetAddrInfoReq, uv_getaddrinfo_t> {
static void addrInfoCallback(uv_getaddrinfo_t *req, int status, addrinfo *res) {
auto ptr = reserve(req);
if(status) {
ptr->publish(ErrorEvent{status});
} else {
auto data = std::unique_ptr<addrinfo, void(*)(addrinfo *)>{
res, [](addrinfo *addr){ uv_freeaddrinfo(addr); }};
ptr->publish(AddrInfoEvent{std::move(data)});
}
}
void nodeAddrInfo(const char *node, const char *service, addrinfo *hints = nullptr) {
invoke(&uv_getaddrinfo, parent(), get(), &addrInfoCallback, node, service, hints);
}
auto nodeAddrInfoSync(const char *node, const char *service, addrinfo *hints = nullptr) -> std::pair<int, std::unique_ptr<addrinfo, void(*)(addrinfo *)>> {
auto req = get();
auto err = uv_getaddrinfo(parent(), req, nullptr, node, service, hints);
auto data = std::unique_ptr<addrinfo, void(*)(addrinfo *)>{req->addrinfo, [](addrinfo *addr){ uv_freeaddrinfo(addr); }};
return std::make_pair(!err, std::move(data));
}
class get_addr_info_req final: public request<get_addr_info_req, uv_getaddrinfo_t, addr_info_event> {
static void addr_info_callback(uv_getaddrinfo_t *req, int status, addrinfo *res);
int node_addr_info(const char *node, const char *service, addrinfo *hints = nullptr);
auto node_addr_info_sync(const char *node, const char *service, addrinfo *hints = nullptr);
public:
using Deleter = void(*)(addrinfo *);
using deleter = void (*)(addrinfo *);
using Request::Request;
using request::request;
/**
* @brief Async [getaddrinfo](http://linux.die.net/man/3/getaddrinfo).
* @param node Either a numerical network address or a network hostname.
* @param hints Optional `addrinfo` data structure with additional address
* type constraints.
* @return Underlying return value.
*/
void nodeAddrInfo(std::string node, addrinfo *hints = nullptr) {
nodeAddrInfo(node.data(), nullptr, hints);
}
int node_addr_info(const std::string &node, addrinfo *hints = nullptr);
/**
* @brief Sync [getaddrinfo](http://linux.die.net/man/3/getaddrinfo).
@ -120,22 +83,18 @@ public:
*
* @return A `std::pair` composed as it follows:
* * A boolean value that is true in case of success, false otherwise.
* * A `std::unique_ptr<addrinfo, Deleter>` containing the data requested.
* * A `std::unique_ptr<addrinfo, deleter>` containing the data requested.
*/
std::pair<bool, std::unique_ptr<addrinfo, Deleter>>
nodeAddrInfoSync(std::string node, addrinfo *hints = nullptr) {
return nodeAddrInfoSync(node.data(), nullptr, hints);
}
std::pair<bool, std::unique_ptr<addrinfo, deleter>> node_addr_info_sync(const std::string &node, addrinfo *hints = nullptr);
/**
* @brief Async [getaddrinfo](http://linux.die.net/man/3/getaddrinfo).
* @param service Either a service name or a port number as a string.
* @param hints Optional `addrinfo` data structure with additional address
* type constraints.
* @return Underlying return value.
*/
void serviceAddrInfo(std::string service, addrinfo *hints = nullptr) {
nodeAddrInfo(nullptr, service.data(), hints);
}
int service_addr_info(const std::string &service, addrinfo *hints = nullptr);
/**
* @brief Sync [getaddrinfo](http://linux.die.net/man/3/getaddrinfo).
@ -146,12 +105,9 @@ public:
*
* @return A `std::pair` composed as it follows:
* * A boolean value that is true in case of success, false otherwise.
* * A `std::unique_ptr<addrinfo, Deleter>` containing the data requested.
* * A `std::unique_ptr<addrinfo, deleter>` containing the data requested.
*/
std::pair<bool, std::unique_ptr<addrinfo, Deleter>>
serviceAddrInfoSync(std::string service, addrinfo *hints = nullptr) {
return nodeAddrInfoSync(nullptr, service.data(), hints);
}
std::pair<bool, std::unique_ptr<addrinfo, deleter>> service_addr_info_sync(const std::string &service, addrinfo *hints = nullptr);
/**
* @brief Async [getaddrinfo](http://linux.die.net/man/3/getaddrinfo).
@ -159,10 +115,9 @@ public:
* @param service Either a service name or a port number as a string.
* @param hints Optional `addrinfo` data structure with additional address
* type constraints.
* @return Underlying return value.
*/
void addrInfo(std::string node, std::string service, addrinfo *hints = nullptr) {
nodeAddrInfo(node.data(), service.data(), hints);
}
int addr_info(const std::string &node, const std::string &service, addrinfo *hints = nullptr);
/**
* @brief Sync [getaddrinfo](http://linux.die.net/man/3/getaddrinfo).
@ -174,64 +129,49 @@ public:
*
* @return A `std::pair` composed as it follows:
* * A boolean value that is true in case of success, false otherwise.
* * A `std::unique_ptr<addrinfo, Deleter>` containing the data requested.
* * A `std::unique_ptr<addrinfo, deleter>` containing the data requested.
*/
std::pair<bool, std::unique_ptr<addrinfo, Deleter>>
addrInfoSync(std::string node, std::string service, addrinfo *hints = nullptr) {
return nodeAddrInfoSync(node.data(), service.data(), hints);
}
std::pair<bool, std::unique_ptr<addrinfo, deleter>> addr_info_sync(const std::string &node, const std::string &service, addrinfo *hints = nullptr);
};
/**
* @brief The GetNameInfoReq request.
* @brief The getnameinfo request.
*
* Wrapper for [getnameinfo](http://linux.die.net/man/3/getnameinfo).<br/>
* It offers either asynchronous and synchronous access methods.
*
* To create a `GetNameInfoReq` through a `Loop`, no arguments are required.
* To create a `get_name_info_req` through a `loop`, no arguments are required.
*/
class GetNameInfoReq final: public Request<GetNameInfoReq, uv_getnameinfo_t> {
static void nameInfoCallback(uv_getnameinfo_t *req, int status, const char *hostname, const char *service) {
auto ptr = reserve(req);
if(status) { ptr->publish(ErrorEvent{status}); }
else { ptr->publish(NameInfoEvent{hostname, service}); }
}
class get_name_info_req final: public request<get_name_info_req, uv_getnameinfo_t, name_info_event> {
static void name_info_callback(uv_getnameinfo_t *req, int status, const char *hostname, const char *service);
public:
using Request::Request;
using request::request;
/**
* @brief Async [getnameinfo](http://linux.die.net/man/3/getnameinfo).
* @param addr Initialized `sockaddr_in` or `sockaddr_in6` data structure.
* @param flags Optional flags that modify the behavior of `getnameinfo`.
* @return Underlying return value.
*/
void nameInfo(const sockaddr &addr, int flags = 0) {
invoke(&uv_getnameinfo, parent(), get(), &nameInfoCallback, &addr, flags);
}
int name_info(const sockaddr &addr, int flags = 0);
/**
* @brief Async [getnameinfo](http://linux.die.net/man/3/getnameinfo).
* @param ip A valid IP address.
* @param port A valid port number.
* @param flags Optional flags that modify the behavior of `getnameinfo`.
* @return Underlying return value.
*/
template<typename I = IPv4>
void nameInfo(std::string ip, unsigned int port, int flags = 0) {
typename details::IpTraits<I>::Type addr;
details::IpTraits<I>::addrFunc(ip.data(), port, &addr);
nameInfo(reinterpret_cast<const sockaddr &>(addr), flags);
}
int name_info(const std::string &ip, unsigned int port, int flags = 0);
/**
* @brief Async [getnameinfo](http://linux.die.net/man/3/getnameinfo).
* @param addr A valid instance of Addr.
* @param addr A valid instance of socket_address.
* @param flags Optional flags that modify the behavior of `getnameinfo`.
* @return Underlying return value.
*/
template<typename I = IPv4>
void nameInfo(Addr addr, int flags = 0) {
nameInfo<I>(std::move(addr.ip), addr.port, flags);
}
int name_info(const socket_address &addr, int flags = 0);
/**
* @brief Sync [getnameinfo](http://linux.die.net/man/3/getnameinfo).
@ -245,12 +185,7 @@ public:
* * A `const char *` containing a valid hostname.
* * A `const char *` containing a valid service name.
*/
std::pair<bool, std::pair<const char *, const char *>>
nameInfoSync(const sockaddr &addr, int flags = 0) {
auto req = get();
auto err = uv_getnameinfo(parent(), req, nullptr, &addr, flags);
return std::make_pair(!err, std::make_pair(req->host, req->service));
}
std::pair<bool, std::pair<const char *, const char *>> name_info_sync(const sockaddr &addr, int flags = 0);
/**
* @brief Sync [getnameinfo](http://linux.die.net/man/3/getnameinfo).
@ -265,18 +200,12 @@ public:
* * A `const char *` containing a valid hostname.
* * A `const char *` containing a valid service name.
*/
template<typename I = IPv4>
std::pair<bool, std::pair<const char *, const char *>>
nameInfoSync(std::string ip, unsigned int port, int flags = 0) {
typename details::IpTraits<I>::Type addr;
details::IpTraits<I>::addrFunc(ip.data(), port, &addr);
return nameInfoSync(reinterpret_cast<const sockaddr &>(addr), flags);
}
std::pair<bool, std::pair<const char *, const char *>> name_info_sync(const std::string &ip, unsigned int port, int flags = 0);
/**
* @brief Sync [getnameinfo](http://linux.die.net/man/3/getnameinfo).
*
* @param addr A valid instance of Addr.
* @param addr A valid instance of socket_address.
* @param flags Optional flags that modify the behavior of `getnameinfo`.
*
* @return A `std::pair` composed as it follows:
@ -285,12 +214,13 @@ public:
* * A `const char *` containing a valid hostname.
* * A `const char *` containing a valid service name.
*/
template<typename I = IPv4>
std::pair<bool, std::pair<const char *, const char *>>
nameInfoSync(Addr addr, int flags = 0) {
return nameInfoSync<I>(std::move(addr.ip), addr.port, flags);
}
std::pair<bool, std::pair<const char *, const char *>> name_info_sync(const socket_address &addr, int flags = 0);
};
} // namespace uvw
}
#ifndef UVW_AS_LIB
# include "dns.ipp"
#endif
#endif // UVW_DNS_INCLUDE_H

89
src/uvw/dns.ipp Normal file
View File

@ -0,0 +1,89 @@
#include "config.h"
namespace uvw {
UVW_INLINE addr_info_event::addr_info_event(std::unique_ptr<addrinfo, deleter> addr)
: data{std::move(addr)} {}
UVW_INLINE name_info_event::name_info_event(const char *host, const char *serv)
: hostname{host}, service{serv} {}
UVW_INLINE void get_addr_info_req::addr_info_callback(uv_getaddrinfo_t *req, int status, addrinfo *res) {
if(auto ptr = reserve(req); status) {
ptr->publish(error_event{status});
} else {
auto data = std::unique_ptr<addrinfo, void (*)(addrinfo *)>{res, [](addrinfo *addr) { uv_freeaddrinfo(addr); }};
ptr->publish(addr_info_event{std::move(data)});
}
}
UVW_INLINE int get_addr_info_req::node_addr_info(const char *node, const char *service, addrinfo *hints) {
return this->leak_if(uv_getaddrinfo(parent().raw(), raw(), &addr_info_callback, node, service, hints));
}
UVW_INLINE auto get_addr_info_req::node_addr_info_sync(const char *node, const char *service, addrinfo *hints) {
auto req = raw();
auto err = uv_getaddrinfo(parent().raw(), req, nullptr, node, service, hints);
auto data = std::unique_ptr<addrinfo, void (*)(addrinfo *)>{req->addrinfo, [](addrinfo *addr) { uv_freeaddrinfo(addr); }};
return std::make_pair(!err, std::move(data));
}
UVW_INLINE int get_addr_info_req::node_addr_info(const std::string &node, addrinfo *hints) {
return node_addr_info(node.data(), nullptr, hints);
}
UVW_INLINE std::pair<bool, std::unique_ptr<addrinfo, get_addr_info_req::deleter>> get_addr_info_req::node_addr_info_sync(const std::string &node, addrinfo *hints) {
return node_addr_info_sync(node.data(), nullptr, hints);
}
UVW_INLINE int get_addr_info_req::service_addr_info(const std::string &service, addrinfo *hints) {
return node_addr_info(nullptr, service.data(), hints);
}
UVW_INLINE std::pair<bool, std::unique_ptr<addrinfo, get_addr_info_req::deleter>> get_addr_info_req::service_addr_info_sync(const std::string &service, addrinfo *hints) {
return node_addr_info_sync(nullptr, service.data(), hints);
}
UVW_INLINE int get_addr_info_req::addr_info(const std::string &node, const std::string &service, addrinfo *hints) {
return node_addr_info(node.data(), service.data(), hints);
}
UVW_INLINE std::pair<bool, std::unique_ptr<addrinfo, get_addr_info_req::deleter>> get_addr_info_req::addr_info_sync(const std::string &node, const std::string &service, addrinfo *hints) {
return node_addr_info_sync(node.data(), service.data(), hints);
}
UVW_INLINE void get_name_info_req::name_info_callback(uv_getnameinfo_t *req, int status, const char *hostname, const char *service) {
if(auto ptr = reserve(req); status) {
ptr->publish(error_event{status});
} else {
ptr->publish(name_info_event{hostname, service});
}
}
UVW_INLINE int get_name_info_req::name_info(const sockaddr &addr, int flags) {
return this->leak_if(uv_getnameinfo(parent().raw(), raw(), &name_info_callback, &addr, flags));
}
UVW_INLINE int get_name_info_req::name_info(const std::string &ip, unsigned int port, int flags) {
return name_info(details::ip_addr(ip.data(), port), flags);
}
UVW_INLINE int get_name_info_req::name_info(const socket_address &addr, int flags) {
return name_info(addr.ip, addr.port, flags);
}
UVW_INLINE std::pair<bool, std::pair<const char *, const char *>> get_name_info_req::name_info_sync(const sockaddr &addr, int flags) {
auto req = raw();
auto err = uv_getnameinfo(parent().raw(), req, nullptr, &addr, flags);
return std::make_pair(!err, std::make_pair(req->host, req->service));
}
UVW_INLINE std::pair<bool, std::pair<const char *, const char *>> get_name_info_req::name_info_sync(const std::string &ip, unsigned int port, int flags) {
return name_info_sync(details::ip_addr(ip.data(), port), flags);
}
UVW_INLINE std::pair<bool, std::pair<const char *, const char *>> get_name_info_req::name_info_sync(const socket_address &addr, int flags) {
return name_info_sync(addr.ip, addr.port, flags);
}
} // namespace uvw

2
src/uvw/emitter.cpp Normal file
View File

@ -0,0 +1,2 @@
#include "emitter.h"
#include "emitter.ipp"

159
src/uvw/emitter.h Normal file
View File

@ -0,0 +1,159 @@
#ifndef UVW_EMITTER_INCLUDE_H
#define UVW_EMITTER_INCLUDE_H
#include <cstddef>
#include <cstdint>
#include <functional>
#include <list>
#include <memory>
#include <type_traits>
#include <unordered_map>
#include <utility>
#include <uv.h>
#include "config.h"
#include "type_info.hpp"
namespace uvw {
/**
* @brief Error event.
*
* Custom wrapper around error constants of `libuv`.
*/
struct error_event {
template<typename Type, typename = std::enable_if_t<std::is_integral_v<Type>>>
explicit error_event(Type val) noexcept
: ec{static_cast<int>(val)} {}
/**
* @brief Returns the `libuv` error code equivalent to the given platform dependent error code.
*
* It returns:
* * POSIX error codes on Unix (the ones stored in errno).
* * Win32 error codes on Windows (those returned by GetLastError() or WSAGetLastError()).
*
* If `sys` is already a `libuv` error code, it is simply returned.
*
* @param sys A platform dependent error code.
* @return The `libuv` error code equivalent to the given platform dependent error code.
*/
[[nodiscard]] static int translate(int sys) noexcept;
/**
* @brief Returns the error message for the given error code.
*
* Leaks a few bytes of memory when you call it with an unknown error code.
*
* @return The error message for the given error code.
*/
[[nodiscard]] const char *what() const noexcept;
/**
* @brief Returns the error name for the given error code.
*
* Leaks a few bytes of memory when you call it with an unknown error code.
*
* @return The error name for the given error code.
*/
[[nodiscard]] const char *name() const noexcept;
/**
* @brief Gets the underlying error code, that is an error constant of `libuv`.
* @return The underlying error code.
*/
[[nodiscard]] int code() const noexcept;
/**
* @brief Checks if the event contains a valid error code.
* @return True in case of success, false otherwise.
*/
explicit operator bool() const noexcept;
private:
int ec;
};
/**
* @brief Event emitter base class.
*
* Almost everything in `uvw` is an event emitter.<br/>
* This is the base class from which resources and loops inherit.
*/
template<typename Elem, typename... Event>
class emitter {
public:
template<typename Type>
using listener_t = std::function<void(Type &, Elem &)>;
private:
template<typename Type>
[[nodiscard]] const auto &handler() const noexcept {
return std::get<listener_t<Type>>(handlers);
}
template<typename Type>
[[nodiscard]] auto &handler() noexcept {
return std::get<listener_t<Type>>(handlers);
}
protected:
template<typename Type>
void publish(Type event) {
if(auto &listener = handler<Type>(); listener) {
listener(event, *static_cast<Elem *>(this));
}
}
public:
virtual ~emitter() noexcept {
static_assert(std::is_base_of_v<emitter<Elem, Event...>, Elem>);
}
/**
* @brief Registers a long-lived listener with the event emitter.
*
* This method is used to register a listener with the emitter.<br/>
* A listener is usually defined as a callable object assignable to a
* `std::function<void(const Event &, Elem &)`, where `Event` is the type of
* the event and `Elem` is the type of the resource.
*
* @param f A valid listener to be registered.
*/
template<typename Type>
void on(listener_t<Type> f) {
handler<Type>() = std::move(f);
}
/*! @brief Disconnects the listener for the given event type. */
template<typename Type>
void reset() noexcept {
handler<Type>() = nullptr;
}
/*! @brief Disconnects all listeners. */
void reset() noexcept {
reset<error_event>();
(reset<Event>(), ...);
}
/**
* @brief Checks if there is a listener registered for the specific event.
* @return True if there is a listener registered for the specific event,
* false otherwise.
*/
template<typename Type>
[[nodiscard]] bool has() const noexcept {
return static_cast<bool>(handler<Type>());
}
private:
std::tuple<listener_t<error_event>, listener_t<Event>...> handlers{};
};
} // namespace uvw
#ifndef UVW_AS_LIB
# include "emitter.ipp"
#endif
#endif // UVW_EMITTER_INCLUDE_H

View File

@ -1,318 +0,0 @@
#pragma once
#include <type_traits>
#include <functional>
#include <algorithm>
#include <utility>
#include <cstddef>
#include <vector>
#include <memory>
#include <list>
#include <uv.h>
namespace uvw {
/**
* @brief The ErrorEvent event.
*
* Custom wrapper around error constants of `libuv`.
*/
struct ErrorEvent {
template<typename U, typename V = typename std::enable_if<std::is_integral<U>::value>::type>
explicit ErrorEvent(U val) noexcept
: ec{static_cast<int>(val)}
{}
/**
* @brief Returns the `libuv` error code equivalent to the given platform dependent error code.
*
* It returns:
* * POSIX error codes on Unix (the ones stored in errno).
* * Win32 error codes on Windows (those returned by GetLastError() or WSAGetLastError()).
*
* If `sys` is already a `libuv` error code, it is simply returned.
*
* @param sys A platform dependent error code.
* @return The `libuv` error code equivalent to the given platform dependent error code.
*/
static int translate(int sys) noexcept {
return uv_translate_sys_error(sys);
}
/**
* @brief Returns the error message for the given error code.
*
* Leaks a few bytes of memory when you call it with an unknown error code.
*
* @return The error message for the given error code.
*/
const char * what() const noexcept { return uv_strerror(ec); }
/**
* @brief Returns the error name for the given error code.
*
* Leaks a few bytes of memory when you call it with an unknown error code.
*
* @return The error name for the given error code.
*/
const char * name() const noexcept { return uv_err_name(ec); }
/**
* @brief Gets the underlying error code, that is an error constant of `libuv`.
* @return The underlying error code.
*/
int code() const noexcept { return ec; }
/**
* @brief Checks if the event contains a valid error code.
* @return True in case of success, false otherwise.
*/
explicit operator bool() const noexcept { return ec < 0; }
private:
const int ec;
};
/**
* @brief Event emitter base class.
*
* Almost everything in `uvw` is an event emitter.<br/>
* This is the base class from which resources and loops inherit.
*/
template<typename T>
class Emitter {
struct BaseHandler {
virtual ~BaseHandler() noexcept = default;
virtual bool empty() const noexcept = 0;
virtual void clear() noexcept = 0;
};
template<typename E>
struct Handler final: BaseHandler {
using Listener = std::function<void(E &, T &)>;
using Element = std::pair<bool, Listener>;
using ListenerList = std::list<Element>;
using Connection = typename ListenerList::iterator;
bool empty() const noexcept override {
auto pred = [](const Element &element){ return element.first; };
return std::all_of(onceL.cbegin(), onceL.cend(), pred) &&
std::all_of(onL.cbegin(), onL.cend(), pred);
}
void clear() noexcept override {
if(publishing) {
auto func = [](Element &element){ element.first = true; };
std::for_each(onceL.begin(), onceL.end(), func);
std::for_each(onL.begin(), onL.end(), func);
} else {
onceL.clear();
onL.clear();
}
}
Connection once(Listener f) {
return onceL.emplace(onceL.end(), false, std::move(f));
}
Connection on(Listener f) {
return onL.emplace(onL.end(), false, std::move(f));
}
void erase(Connection conn) noexcept {
conn->first = true;
if(!publishing) {
auto pred = [](const Element &element){ return element.first; };
onceL.remove_if(pred);
onL.remove_if(pred);
}
}
void publish(E event, T &ref) {
ListenerList currentL;
onceL.swap(currentL);
auto func = [&event, &ref](Element &element) {
return element.first ? void() : element.second(event, ref);
};
publishing = true;
std::for_each(onL.rbegin(), onL.rend(), func);
std::for_each(currentL.rbegin(), currentL.rend(), func);
publishing = false;
onL.remove_if([](const Element &element){ return element.first; });
}
private:
bool publishing{false};
ListenerList onceL{};
ListenerList onL{};
};
static std::size_t next_type() noexcept {
static std::size_t counter = 0;
return counter++;
}
template<typename>
static std::size_t event_type() noexcept {
static std::size_t value = next_type();
return value;
}
template<typename E>
Handler<E> & handler() noexcept {
std::size_t type = event_type<E>();
if(!(type < handlers.size())) {
handlers.resize(type+1);
}
if(!handlers[type]) {
handlers[type] = std::unique_ptr<Handler<E>>(new(std::nothrow) Handler<E>);
}
return static_cast<Handler<E>&>(*handlers[type]);
}
protected:
template<typename E>
void publish(E event) {
handler<E>().publish(std::move(event), *static_cast<T*>(this));
}
public:
template<typename E>
using Listener = typename Handler<E>::Listener;
/**
* @brief Connection type for a given event type.
*
* Given an event type `E`, `Connection<E>` is the type of the connection
* object returned by the event emitter whenever a listener for the given
* type is registered.
*/
template<typename E>
struct Connection: private Handler<E>::Connection {
template<typename> friend class Emitter;
Connection() = default;
Connection(const Connection &) = default;
Connection(Connection &&) = default;
Connection(typename Handler<E>::Connection conn)
: Handler<E>::Connection{std::move(conn)}
{}
Connection & operator=(const Connection &) = default;
Connection & operator=(Connection &&) = default;
};
virtual ~Emitter() noexcept {
static_assert(std::is_base_of<Emitter<T>, T>::value, "!");
}
/**
* @brief Registers a long-lived listener with the event emitter.
*
* This method can be used to register a listener that is meant to be
* invoked more than once for the given event type.<br/>
* The Connection object returned by the method can be freely discarded. It
* can be used later to disconnect the listener, if needed.
*
* Listener is usually defined as a callable object assignable to a
* `std::function<void(const E &, T &)`, where `E` is the type of the event
* and `T` is the type of the resource.
*
* @param f A valid listener to be registered.
* @return Connection object to be used later to disconnect the listener.
*/
template<typename E>
Connection<E> on(Listener<E> f) {
return handler<E>().on(std::move(f));
}
/**
* @brief Registers a short-lived listener with the event emitter.
*
* This method can be used to register a listener that is meant to be
* invoked only once for the given event type.<br/>
* The Connection object returned by the method can be freely discarded. It
* can be used later to disconnect the listener, if needed.
*
* Listener is usually defined as a callable object assignable to a
* `std::function<void(const E &, T &)`, where `E` is the type of the event
* and `T` is the type of the resource.
*
* @param f A valid listener to be registered.
* @return Connection object to be used later to disconnect the listener.
*/
template<typename E>
Connection<E> once(Listener<E> f) {
return handler<E>().once(std::move(f));
}
/**
* @brief Disconnects a listener from the event emitter.
* @param conn A valid Connection object
*/
template<typename E>
void erase(Connection<E> conn) noexcept {
handler<E>().erase(std::move(conn));
}
/**
* @brief Disconnects all the listeners for the given event type.
*/
template<typename E>
void clear() noexcept {
handler<E>().clear();
}
/**
* @brief Disconnects all the listeners.
*/
void clear() noexcept {
std::for_each(handlers.begin(), handlers.end(),
[](std::unique_ptr<BaseHandler> &hdlr){ if(hdlr) { hdlr->clear(); } });
}
/**
* @brief Checks if there are listeners registered for the specific event.
* @return True if there are no listeners registered for the specific event,
* false otherwise.
*/
template<typename E>
bool empty() const noexcept {
std::size_t type = event_type<E>();
return (!(type < handlers.size()) ||
!handlers[type] ||
static_cast<Handler<E>&>(*handlers[type]).empty());
}
/**
* @brief Checks if there are listeners registered with the event emitter.
* @return True if there are no listeners registered with the event emitter,
* false otherwise.
*/
bool empty() const noexcept {
return std::all_of(handlers.cbegin(), handlers.cend(),
[](const std::unique_ptr<BaseHandler> &hdlr){ return !hdlr || hdlr->empty(); });
}
private:
std::vector<std::unique_ptr<BaseHandler>> handlers{};
};
}

25
src/uvw/emitter.ipp Normal file
View File

@ -0,0 +1,25 @@
#include "config.h"
namespace uvw {
UVW_INLINE int error_event::translate(int sys) noexcept {
return uv_translate_sys_error(sys);
}
UVW_INLINE const char *error_event::what() const noexcept {
return uv_strerror(ec);
}
UVW_INLINE const char *error_event::name() const noexcept {
return uv_err_name(ec);
}
UVW_INLINE int error_event::code() const noexcept {
return ec;
}
UVW_INLINE error_event::operator bool() const noexcept {
return ec < 0;
}
} // namespace uvw

76
src/uvw/enum.hpp Normal file
View File

@ -0,0 +1,76 @@
#ifndef UVW_ENUM_INCLUDE_HPP
#define UVW_ENUM_INCLUDE_HPP
#include <type_traits>
#include "config.h"
/**
* @brief Operator available for enums for which bitmask support is enabled.
* @tparam Type Enum class type.
* @param lhs The first value to use.
* @param rhs The second value to use.
* @return The result of invoking the operator on the underlying types of the
* two values provided.
*/
template<typename Type>
[[nodiscard]] constexpr std::enable_if_t<std::is_enum_v<Type>, decltype(Type::UVW_ENUM)>
operator|(const Type lhs, const Type rhs) noexcept {
return static_cast<Type>(static_cast<std::underlying_type_t<Type>>(lhs) | static_cast<std::underlying_type_t<Type>>(rhs));
}
/*! @copydoc operator| */
template<typename Type>
[[nodiscard]] constexpr std::enable_if_t<std::is_enum_v<Type>, decltype(Type::UVW_ENUM)>
operator&(const Type lhs, const Type rhs) noexcept {
return static_cast<Type>(static_cast<std::underlying_type_t<Type>>(lhs) & static_cast<std::underlying_type_t<Type>>(rhs));
}
/*! @copydoc operator| */
template<typename Type>
[[nodiscard]] constexpr std::enable_if_t<std::is_enum_v<Type>, decltype(Type::UVW_ENUM)>
operator^(const Type lhs, const Type rhs) noexcept {
return static_cast<Type>(static_cast<std::underlying_type_t<Type>>(lhs) ^ static_cast<std::underlying_type_t<Type>>(rhs));
}
/**
* @brief Operator available for enums for which bitmask support is enabled.
* @tparam Type Enum class type.
* @param value The value to use.
* @return The result of invoking the operator on the underlying types of the
* value provided.
*/
template<typename Type>
[[nodiscard]] constexpr std::enable_if_t<std::is_enum_v<Type>, decltype(Type::UVW_ENUM)>
operator~(const Type value) noexcept {
return static_cast<Type>(~static_cast<std::underlying_type_t<Type>>(value));
}
/*! @copydoc operator~ */
template<typename Type>
[[nodiscard]] constexpr std::enable_if_t<std::is_enum_v<Type>, decltype(Type::UVW_ENUM, bool{})>
operator!(const Type value) noexcept {
return !static_cast<std::underlying_type_t<Type>>(value);
}
/*! @copydoc operator| */
template<typename Type>
constexpr std::enable_if_t<std::is_enum_v<Type>, decltype(Type::UVW_ENUM) &>
operator|=(Type &lhs, const Type rhs) noexcept {
return (lhs = (lhs | rhs));
}
/*! @copydoc operator| */
template<typename Type>
constexpr std::enable_if_t<std::is_enum_v<Type>, decltype(Type::UVW_ENUM) &>
operator&=(Type &lhs, const Type rhs) noexcept {
return (lhs = (lhs & rhs));
}
/*! @copydoc operator| */
template<typename Type>
constexpr std::enable_if_t<std::is_enum_v<Type>, decltype(Type::UVW_ENUM) &>
operator^=(Type &lhs, const Type rhs) noexcept {
return (lhs = (lhs ^ rhs));
}
#endif

2
src/uvw/fs.cpp Normal file
View File

@ -0,0 +1,2 @@
#include "fs.h"
#include "fs.ipp"

1235
src/uvw/fs.h Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

550
src/uvw/fs.ipp Normal file
View File

@ -0,0 +1,550 @@
#include <array>
#include "config.h"
namespace uvw {
UVW_INLINE void file_req::fs_open_callback(uv_fs_t *req) {
if(auto ptr = reserve(req); req->result < 0) {
ptr->publish(error_event{req->result});
} else {
ptr->file = static_cast<uv_file>(req->result);
ptr->publish(fs_event{*req});
}
}
UVW_INLINE void file_req::fs_close_callback(uv_fs_t *req) {
if(auto ptr = reserve(req); req->result < 0) {
ptr->publish(error_event{req->result});
} else {
ptr->file = BAD_FD;
ptr->publish(fs_event{*req});
}
}
UVW_INLINE void file_req::fs_read_callback(uv_fs_t *req) {
if(auto ptr = reserve(req); req->result < 0) {
ptr->publish(error_event{req->result});
} else {
ptr->publish(fs_event{*req, std::move(ptr->current)});
}
}
UVW_INLINE file_req::~file_req() noexcept {
uv_fs_req_cleanup(raw());
}
UVW_INLINE void file_req::close() {
uv_fs_req_cleanup(this->raw());
uv_fs_close(parent().raw(), raw(), file, &fs_close_callback);
}
UVW_INLINE bool file_req::close_sync() {
auto req = raw();
uv_fs_req_cleanup(this->raw());
uv_fs_close(parent().raw(), req, file, nullptr);
if(req->result >= 0) {
file = BAD_FD;
}
return !(req->result < 0);
}
UVW_INLINE void file_req::open(const std::string &path, file_open_flags flags, int mode) {
uv_fs_req_cleanup(this->raw());
uv_fs_open(parent().raw(), raw(), path.data(), static_cast<int>(flags), mode, &fs_open_callback);
}
UVW_INLINE bool file_req::open_sync(const std::string &path, file_open_flags flags, int mode) {
auto req = raw();
uv_fs_req_cleanup(this->raw());
uv_fs_open(parent().raw(), req, path.data(), static_cast<int>(flags), mode, nullptr);
if(req->result >= 0) {
file = static_cast<uv_file>(req->result);
}
return !(req->result < 0);
}
UVW_INLINE void file_req::read(int64_t offset, unsigned int len) {
current = std::make_unique<char[]>(len);
buffer = uv_buf_init(current.get(), len);
std::array bufs{buffer};
uv_fs_req_cleanup(this->raw());
uv_fs_read(parent().raw(), raw(), file, bufs.data(), 1, offset, &fs_read_callback);
}
UVW_INLINE std::pair<bool, std::pair<std::unique_ptr<const char[]>, std::size_t>> file_req::read_sync(int64_t offset, unsigned int len) {
current = std::make_unique<char[]>(len);
buffer = uv_buf_init(current.get(), len);
std::array bufs{buffer};
auto req = raw();
uv_fs_req_cleanup(this->raw());
uv_fs_read(parent().raw(), req, file, bufs.data(), 1, offset, nullptr);
bool err = req->result < 0;
return std::make_pair(!err, std::make_pair(std::move(current), err ? 0 : std::size_t(req->result)));
}
UVW_INLINE void file_req::write(std::unique_ptr<char[]> buf, unsigned int len, int64_t offset) {
current = std::move(buf);
std::array bufs{uv_buf_init(current.get(), len)};
uv_fs_req_cleanup(this->raw());
uv_fs_write(parent().raw(), raw(), file, bufs.data(), 1, offset, &fs_request_callback);
}
UVW_INLINE void file_req::write(char *buf, unsigned int len, int64_t offset) {
std::array bufs{uv_buf_init(buf, len)};
uv_fs_req_cleanup(this->raw());
uv_fs_write(parent().raw(), raw(), file, bufs.data(), 1, offset, &fs_request_callback);
}
UVW_INLINE std::pair<bool, std::size_t> file_req::write_sync(std::unique_ptr<char[]> buf, unsigned int len, int64_t offset) {
current = std::move(buf);
std::array bufs{uv_buf_init(current.get(), len)};
auto req = raw();
uv_fs_req_cleanup(this->raw());
uv_fs_write(parent().raw(), req, file, bufs.data(), 1, offset, nullptr);
bool err = req->result < 0;
return std::make_pair(!err, err ? 0 : std::size_t(req->result));
}
UVW_INLINE void file_req::stat() {
uv_fs_req_cleanup(this->raw());
uv_fs_fstat(parent().raw(), raw(), file, &fs_request_callback);
}
UVW_INLINE std::pair<bool, file_info> file_req::stat_sync() {
auto req = raw();
uv_fs_req_cleanup(this->raw());
uv_fs_fstat(parent().raw(), req, file, nullptr);
return std::make_pair(!(req->result < 0), req->statbuf);
}
UVW_INLINE void file_req::sync() {
uv_fs_req_cleanup(this->raw());
uv_fs_fsync(parent().raw(), raw(), file, &fs_request_callback);
}
UVW_INLINE bool file_req::sync_sync() {
auto req = raw();
uv_fs_req_cleanup(this->raw());
uv_fs_fsync(parent().raw(), req, file, nullptr);
return !(req->result < 0);
}
UVW_INLINE void file_req::datasync() {
uv_fs_req_cleanup(this->raw());
uv_fs_fdatasync(parent().raw(), raw(), file, &fs_request_callback);
}
UVW_INLINE bool file_req::datasync_sync() {
auto req = raw();
uv_fs_req_cleanup(this->raw());
uv_fs_fdatasync(parent().raw(), req, file, nullptr);
return !(req->result < 0);
}
UVW_INLINE void file_req::truncate(int64_t offset) {
uv_fs_req_cleanup(this->raw());
uv_fs_ftruncate(parent().raw(), raw(), file, offset, &fs_request_callback);
}
UVW_INLINE bool file_req::truncate_sync(int64_t offset) {
auto req = raw();
uv_fs_req_cleanup(this->raw());
uv_fs_ftruncate(parent().raw(), req, file, offset, nullptr);
return !(req->result < 0);
}
UVW_INLINE void file_req::sendfile(file_handle out, int64_t offset, std::size_t length) {
uv_fs_req_cleanup(this->raw());
uv_fs_sendfile(parent().raw(), raw(), out, file, offset, length, &fs_request_callback);
}
UVW_INLINE std::pair<bool, std::size_t> file_req::sendfile_sync(file_handle out, int64_t offset, std::size_t length) {
auto req = raw();
uv_fs_req_cleanup(this->raw());
uv_fs_sendfile(parent().raw(), req, out, file, offset, length, nullptr);
bool err = req->result < 0;
return std::make_pair(!err, err ? 0 : std::size_t(req->result));
}
UVW_INLINE void file_req::chmod(int mode) {
uv_fs_req_cleanup(this->raw());
uv_fs_fchmod(parent().raw(), raw(), file, mode, &fs_request_callback);
}
UVW_INLINE bool file_req::chmod_sync(int mode) {
auto req = raw();
uv_fs_req_cleanup(this->raw());
uv_fs_fchmod(parent().raw(), req, file, mode, nullptr);
return !(req->result < 0);
}
UVW_INLINE void file_req::futime(fs_request::time atime, fs_request::time mtime) {
uv_fs_req_cleanup(this->raw());
uv_fs_futime(parent().raw(), raw(), file, atime.count(), mtime.count(), &fs_request_callback);
}
UVW_INLINE bool file_req::futime_sync(fs_request::time atime, fs_request::time mtime) {
auto req = raw();
uv_fs_req_cleanup(this->raw());
uv_fs_futime(parent().raw(), req, file, atime.count(), mtime.count(), nullptr);
return !(req->result < 0);
}
UVW_INLINE void file_req::chown(uid_type uid, gid_type gid) {
uv_fs_req_cleanup(this->raw());
uv_fs_fchown(parent().raw(), raw(), file, uid, gid, &fs_request_callback);
}
UVW_INLINE bool file_req::chown_sync(uid_type uid, gid_type gid) {
auto req = raw();
uv_fs_req_cleanup(this->raw());
uv_fs_fchown(parent().raw(), req, file, uid, gid, nullptr);
return !(req->result < 0);
}
UVW_INLINE file_req::operator file_handle() const noexcept {
return file;
}
UVW_INLINE fs_req::~fs_req() noexcept {
uv_fs_req_cleanup(raw());
}
UVW_INLINE void fs_req::unlink(const std::string &path) {
uv_fs_req_cleanup(this->raw());
uv_fs_unlink(parent().raw(), raw(), path.data(), &fs_request_callback);
}
UVW_INLINE bool fs_req::unlink_sync(const std::string &path) {
auto req = raw();
uv_fs_req_cleanup(this->raw());
uv_fs_unlink(parent().raw(), req, path.data(), nullptr);
return !(req->result < 0);
}
UVW_INLINE void fs_req::mkdir(const std::string &path, int mode) {
uv_fs_req_cleanup(this->raw());
uv_fs_mkdir(parent().raw(), raw(), path.data(), mode, &fs_request_callback);
}
UVW_INLINE bool fs_req::mkdir_sync(const std::string &path, int mode) {
auto req = raw();
uv_fs_req_cleanup(this->raw());
uv_fs_mkdir(parent().raw(), req, path.data(), mode, nullptr);
return !(req->result < 0);
}
UVW_INLINE void fs_req::mkdtemp(const std::string &tpl) {
uv_fs_req_cleanup(this->raw());
uv_fs_mkdtemp(parent().raw(), raw(), tpl.data(), &fs_request_callback);
}
UVW_INLINE std::pair<bool, const char *> fs_req::mkdtemp_sync(const std::string &tpl) {
auto req = raw();
uv_fs_req_cleanup(this->raw());
uv_fs_mkdtemp(parent().raw(), req, tpl.data(), nullptr);
return std::make_pair(!(req->result < 0), req->path);
}
UVW_INLINE void fs_req::mkstemp(const std::string &tpl) {
uv_fs_req_cleanup(this->raw());
uv_fs_mkstemp(parent().raw(), raw(), tpl.data(), &fs_request_callback);
}
UVW_INLINE std::pair<bool, std::pair<std::string, std::size_t>> fs_req::mkstemp_sync(const std::string &tpl) {
std::pair<bool, std::pair<std::string, std::size_t>> ret{false, {}};
auto req = raw();
uv_fs_req_cleanup(this->raw());
uv_fs_mkstemp(parent().raw(), req, tpl.data(), nullptr);
ret.first = !(req->result < 0);
if(ret.first) {
ret.second.first = req->path;
ret.second.second = static_cast<std::size_t>(req->result);
}
return ret;
}
UVW_INLINE void fs_req::lutime(const std::string &path, time atime, time mtime) {
uv_fs_req_cleanup(this->raw());
uv_fs_lutime(parent().raw(), raw(), path.data(), atime.count(), mtime.count(), &fs_request_callback);
}
UVW_INLINE bool fs_req::lutime_sync(const std::string &path, time atime, time mtime) {
auto req = raw();
uv_fs_req_cleanup(this->raw());
uv_fs_lutime(parent().raw(), req, path.data(), atime.count(), mtime.count(), nullptr);
return !(req->result < 0);
}
UVW_INLINE void fs_req::rmdir(const std::string &path) {
uv_fs_req_cleanup(this->raw());
uv_fs_rmdir(parent().raw(), raw(), path.data(), &fs_request_callback);
}
UVW_INLINE bool fs_req::rmdir_sync(const std::string &path) {
auto req = raw();
uv_fs_req_cleanup(this->raw());
uv_fs_rmdir(parent().raw(), req, path.data(), nullptr);
return !(req->result < 0);
}
UVW_INLINE void fs_req::scandir(const std::string &path, int flags) {
uv_fs_req_cleanup(this->raw());
uv_fs_scandir(parent().raw(), raw(), path.data(), flags, &fs_request_callback);
}
UVW_INLINE std::pair<bool, std::size_t> fs_req::scandir_sync(const std::string &path, int flags) {
auto req = raw();
uv_fs_req_cleanup(this->raw());
uv_fs_scandir(parent().raw(), req, path.data(), flags, nullptr);
bool err = req->result < 0;
return std::make_pair(!err, err ? 0 : std::size_t(req->result));
}
UVW_INLINE std::pair<bool, std::pair<fs_req::entry_type, const char *>> fs_req::scandir_next() {
std::pair<bool, std::pair<entry_type, const char *>> ret{false, {entry_type::UNKNOWN, nullptr}};
uv_fs_req_cleanup(raw());
auto res = uv_fs_scandir_next(raw(), &dirents);
if(UV_EOF != res) {
ret.second.first = static_cast<entry_type>(dirents.type);
ret.second.second = dirents.name;
ret.first = true;
}
return ret;
}
UVW_INLINE void fs_req::stat(const std::string &path) {
uv_fs_req_cleanup(this->raw());
uv_fs_stat(parent().raw(), raw(), path.data(), &fs_request_callback);
}
UVW_INLINE std::pair<bool, file_info> fs_req::stat_sync(const std::string &path) {
auto req = raw();
uv_fs_req_cleanup(this->raw());
uv_fs_stat(parent().raw(), req, path.data(), nullptr);
return std::make_pair(!(req->result < 0), req->statbuf);
}
UVW_INLINE void fs_req::lstat(const std::string &path) {
uv_fs_req_cleanup(this->raw());
uv_fs_lstat(parent().raw(), raw(), path.data(), &fs_request_callback);
}
UVW_INLINE std::pair<bool, file_info> fs_req::lstat_sync(const std::string &path) {
auto req = raw();
uv_fs_req_cleanup(this->raw());
uv_fs_lstat(parent().raw(), req, path.data(), nullptr);
return std::make_pair(!(req->result < 0), req->statbuf);
}
UVW_INLINE void fs_req::statfs(const std::string &path) {
uv_fs_req_cleanup(this->raw());
uv_fs_statfs(parent().raw(), raw(), path.data(), &fs_request_callback);
}
UVW_INLINE std::pair<bool, fs_info> fs_req::statfs_sync(const std::string &path) {
auto req = raw();
uv_fs_req_cleanup(this->raw());
uv_fs_statfs(parent().raw(), req, path.data(), nullptr);
return std::make_pair(!(req->result < 0), *static_cast<uv_statfs_t *>(req->ptr));
}
UVW_INLINE void fs_req::rename(const std::string &old, const std::string &path) {
uv_fs_req_cleanup(this->raw());
uv_fs_rename(parent().raw(), raw(), old.data(), path.data(), &fs_request_callback);
}
UVW_INLINE bool fs_req::rename_sync(const std::string &old, const std::string &path) {
auto req = raw();
uv_fs_req_cleanup(this->raw());
uv_fs_rename(parent().raw(), req, old.data(), path.data(), nullptr);
return !(req->result < 0);
}
UVW_INLINE void fs_req::copyfile(const std::string &old, const std::string &path, copy_file_flags flags) {
uv_fs_req_cleanup(this->raw());
uv_fs_copyfile(parent().raw(), raw(), old.data(), path.data(), static_cast<int>(flags), &fs_request_callback);
}
UVW_INLINE bool fs_req::copyfile_sync(const std::string &old, const std::string &path, copy_file_flags flags) {
auto req = raw();
uv_fs_req_cleanup(this->raw());
uv_fs_copyfile(parent().raw(), raw(), old.data(), path.data(), static_cast<int>(flags), nullptr);
return !(req->result < 0);
}
UVW_INLINE void fs_req::access(const std::string &path, int mode) {
uv_fs_req_cleanup(this->raw());
uv_fs_access(parent().raw(), raw(), path.data(), mode, &fs_request_callback);
}
UVW_INLINE bool fs_req::access_sync(const std::string &path, int mode) {
auto req = raw();
uv_fs_req_cleanup(this->raw());
uv_fs_access(parent().raw(), req, path.data(), mode, nullptr);
return !(req->result < 0);
}
UVW_INLINE void fs_req::chmod(const std::string &path, int mode) {
uv_fs_req_cleanup(this->raw());
uv_fs_chmod(parent().raw(), raw(), path.data(), mode, &fs_request_callback);
}
UVW_INLINE bool fs_req::chmod_sync(const std::string &path, int mode) {
auto req = raw();
uv_fs_req_cleanup(this->raw());
uv_fs_chmod(parent().raw(), req, path.data(), mode, nullptr);
return !(req->result < 0);
}
UVW_INLINE void fs_req::utime(const std::string &path, fs_request::time atime, fs_request::time mtime) {
uv_fs_req_cleanup(this->raw());
uv_fs_utime(parent().raw(), raw(), path.data(), atime.count(), mtime.count(), &fs_request_callback);
}
UVW_INLINE bool fs_req::utime_sync(const std::string &path, fs_request::time atime, fs_request::time mtime) {
auto req = raw();
uv_fs_req_cleanup(this->raw());
uv_fs_utime(parent().raw(), req, path.data(), atime.count(), mtime.count(), nullptr);
return !(req->result < 0);
}
UVW_INLINE void fs_req::link(const std::string &old, const std::string &path) {
uv_fs_req_cleanup(this->raw());
uv_fs_link(parent().raw(), raw(), old.data(), path.data(), &fs_request_callback);
}
UVW_INLINE bool fs_req::link_sync(const std::string &old, const std::string &path) {
auto req = raw();
uv_fs_req_cleanup(this->raw());
uv_fs_link(parent().raw(), req, old.data(), path.data(), nullptr);
return !(req->result < 0);
}
UVW_INLINE void fs_req::symlink(const std::string &old, const std::string &path, symlink_flags flags) {
uv_fs_req_cleanup(this->raw());
uv_fs_symlink(parent().raw(), raw(), old.data(), path.data(), static_cast<int>(flags), &fs_request_callback);
}
UVW_INLINE bool fs_req::symlink_sync(const std::string &old, const std::string &path, symlink_flags flags) {
auto req = raw();
uv_fs_req_cleanup(this->raw());
uv_fs_symlink(parent().raw(), req, old.data(), path.data(), static_cast<int>(flags), nullptr);
return !(req->result < 0);
}
UVW_INLINE void fs_req::readlink(const std::string &path) {
uv_fs_req_cleanup(this->raw());
uv_fs_readlink(parent().raw(), raw(), path.data(), &fs_request_callback);
}
UVW_INLINE std::pair<bool, std::pair<const char *, std::size_t>> fs_req::readlink_sync(const std::string &path) {
auto req = raw();
uv_fs_req_cleanup(this->raw());
uv_fs_readlink(parent().raw(), req, path.data(), nullptr);
bool err = req->result < 0;
return std::make_pair(!err, std::make_pair(static_cast<char *>(req->ptr), err ? 0 : std::size_t(req->result)));
}
UVW_INLINE void fs_req::realpath(const std::string &path) {
uv_fs_req_cleanup(this->raw());
uv_fs_realpath(parent().raw(), raw(), path.data(), &fs_request_callback);
}
UVW_INLINE std::pair<bool, const char *> fs_req::realpath_sync(const std::string &path) {
auto req = raw();
uv_fs_req_cleanup(this->raw());
uv_fs_realpath(parent().raw(), req, path.data(), nullptr);
return std::make_pair(!(req->result < 0), req->path);
}
UVW_INLINE void fs_req::chown(const std::string &path, uid_type uid, gid_type gid) {
uv_fs_req_cleanup(this->raw());
uv_fs_chown(parent().raw(), raw(), path.data(), uid, gid, &fs_request_callback);
}
UVW_INLINE bool fs_req::chown_sync(const std::string &path, uid_type uid, gid_type gid) {
auto req = raw();
uv_fs_req_cleanup(this->raw());
uv_fs_chown(parent().raw(), req, path.data(), uid, gid, nullptr);
return !(req->result < 0);
}
UVW_INLINE void fs_req::lchown(const std::string &path, uid_type uid, gid_type gid) {
uv_fs_req_cleanup(this->raw());
uv_fs_lchown(parent().raw(), raw(), path.data(), uid, gid, &fs_request_callback);
}
UVW_INLINE bool fs_req::lchown_sync(const std::string &path, uid_type uid, gid_type gid) {
auto req = raw();
uv_fs_req_cleanup(this->raw());
uv_fs_lchown(parent().raw(), req, path.data(), uid, gid, nullptr);
return !(req->result < 0);
}
UVW_INLINE void fs_req::opendir(const std::string &path) {
uv_fs_req_cleanup(this->raw());
uv_fs_opendir(parent().raw(), raw(), path.data(), &fs_request_callback);
}
UVW_INLINE bool fs_req::opendir_sync(const std::string &path) {
auto req = raw();
uv_fs_req_cleanup(this->raw());
uv_fs_opendir(parent().raw(), req, path.data(), nullptr);
return !(req->result < 0);
}
UVW_INLINE void fs_req::closedir() {
auto req = raw();
auto *dir = static_cast<uv_dir_t *>(req->ptr);
uv_fs_req_cleanup(this->raw());
uv_fs_closedir(parent().raw(), req, dir, &fs_request_callback);
}
UVW_INLINE bool fs_req::closedir_sync() {
auto req = raw();
auto *dir = static_cast<uv_dir_t *>(req->ptr);
uv_fs_req_cleanup(this->raw());
uv_fs_closedir(parent().raw(), req, dir, nullptr);
return !(req->result < 0);
}
UVW_INLINE void fs_req::readdir() {
auto req = raw();
auto *dir = static_cast<uv_dir_t *>(req->ptr);
dir->dirents = &dirents;
dir->nentries = 1;
uv_fs_req_cleanup(this->raw());
uv_fs_readdir(parent().raw(), req, dir, &fs_request_callback);
}
UVW_INLINE std::pair<bool, std::pair<fs_req::entry_type, const char *>> fs_req::readdir_sync() {
auto req = raw();
auto *dir = static_cast<uv_dir_t *>(req->ptr);
dir->dirents = &dirents;
dir->nentries = 1;
uv_fs_req_cleanup(this->raw());
uv_fs_readdir(parent().raw(), req, dir, nullptr);
return {req->result != 0, {static_cast<entry_type>(dirents.type), dirents.name}};
}
UVW_INLINE os_file_descriptor fs_helper::handle(file_handle file) noexcept {
return uv_get_osfhandle(file);
}
UVW_INLINE file_handle fs_helper::open(os_file_descriptor descriptor) noexcept {
return uv_open_osfhandle(descriptor);
}
} // namespace uvw

2
src/uvw/fs_event.cpp Normal file
View File

@ -0,0 +1,2 @@
#include "fs_event.h"
#include "fs_event.ipp"

120
src/uvw/fs_event.h Normal file
View File

@ -0,0 +1,120 @@
#ifndef UVW_FS_EVENT_INCLUDE_H
#define UVW_FS_EVENT_INCLUDE_H
#include <string>
#include <type_traits>
#include <uv.h>
#include "config.h"
#include "enum.hpp"
#include "handle.hpp"
#include "loop.h"
#include "util.h"
namespace uvw {
namespace details {
enum class uvw_fs_event_flags : std::underlying_type_t<uv_fs_event_flags> {
WATCH_ENTRY = UV_FS_EVENT_WATCH_ENTRY,
STAT = UV_FS_EVENT_STAT,
RECURSIVE = UV_FS_EVENT_RECURSIVE,
UVW_ENUM = 0
};
enum class uvw_fs_event : std::underlying_type_t<uv_fs_event> {
RENAME = UV_RENAME,
CHANGE = UV_CHANGE
};
} // namespace details
/*! @brief Fs event event. */
struct fs_event_event {
fs_event_event(const char *pathname, details::uvw_fs_event events);
/**
* @brief The path to the file being monitored.
*
* If the handle was started with a directory, the filename parameter will
* be a relative path to a file contained in the directory.
*/
const char *filename;
/**
* @brief Detected events all in one.
*
* Available flags are:
*
* * `fs_event_handle::watch::RENAME`
* * `fs_event_handle::watch::CHANGE`
*/
details::uvw_fs_event flags;
};
/**
* @brief The fs event handle.
*
* These handles allow the user to monitor a given path for changes, for
* example, if the file was renamed or there was a generic change in it. The
* best backend for the job on each platform is chosen by the handle.
*
* To create a `fs_event_handle` through a `loop`, no arguments are required.
*
* See the official
* [documentation](http://docs.libuv.org/en/v1.x/fs_event.html)
* for further details.
*/
class fs_event_handle final: public handle<fs_event_handle, uv_fs_event_t, fs_event_event> {
static void start_callback(uv_fs_event_t *hndl, const char *filename, int events, int status);
public:
using watch = details::uvw_fs_event;
using event_flags = details::uvw_fs_event_flags;
using handle::handle;
/**
* @brief Initializes the handle.
* @return Underlying return value.
*/
int init();
/**
* @brief Starts watching the specified path.
*
* It will watch the specified path for changes.<br/>
* As soon as a change is observed, a fs_event_event is emitted by the
* handle.
*
* Available flags are:
*
* * `fs_event_handle::event_flags::WATCH_ENTRY`
* * `fs_event_handle::event_flags::STAT`
* * `fs_event_handle::event_flags::RECURSIVE`
*
* @param path The file or directory to be monitored.
* @param flags Additional flags to control the behavior.
* @return Underlying return value.
*/
int start(const std::string &path, event_flags flags = event_flags::UVW_ENUM);
/**
* @brief Stops polling the file descriptor.
* @return Underlying return value.
*/
int stop();
/**
* @brief Gets the path being monitored.
* @return The path being monitored, an empty string in case of errors.
*/
std::string path() noexcept;
};
} // namespace uvw
#ifndef UVW_AS_LIB
# include "fs_event.ipp"
#endif
#endif // UVW_FS_EVENT_INCLUDE_H

View File

@ -1,158 +0,0 @@
#pragma once
#include <utility>
#include <string>
#include <memory>
#include <uv.h>
#include "handle.hpp"
#include "util.hpp"
#include "loop.hpp"
namespace uvw {
namespace details {
enum class UVFsEventFlags: typename std::underlying_type<uv_fs_event_flags>::type {
WATCH_ENTRY = UV_FS_EVENT_WATCH_ENTRY,
STAT = UV_FS_EVENT_STAT,
RECURSIVE = UV_FS_EVENT_RECURSIVE
};
enum class UVFsEvent: typename std::underlying_type<uv_fs_event>::type {
RENAME = UV_RENAME,
CHANGE = UV_CHANGE
};
}
/**
* @brief FsEventEvent event.
*
* It will be emitted by FsEventHandle according with its functionalities.
*/
struct FsEventEvent {
FsEventEvent(const char * pathname, Flags<details::UVFsEvent> events)
: filename{pathname}, flags{std::move(events)}
{}
/**
* @brief The path to the file being monitored.
*
* If the handle was started with a directory, the filename parameter will
* be a relative path to a file contained in the directory.
*/
const char * filename;
/**
* @brief Detected events all in one.
*
* Available flags are:
*
* * `FsEventHandle::Watch::RENAME`
* * `FsEventHandle::Watch::CHANGE`
*/
Flags<details::UVFsEvent> flags;
};
/**
* @brief The FsEventHandle handle.
*
* These handles allow the user to monitor a given path for changes, for
* example, if the file was renamed or there was a generic change in it. The
* best backend for the job on each platform is chosen by the handle.
*
* To create a `FsEventHandle` through a `Loop`, no arguments are required.
*
* See the official
* [documentation](http://docs.libuv.org/en/v1.x/fs_event.html)
* for further details.
*/
class FsEventHandle final: public Handle<FsEventHandle, uv_fs_event_t> {
static void startCallback(uv_fs_event_t *handle, const char *filename, int events, int status) {
FsEventHandle &fsEvent = *(static_cast<FsEventHandle*>(handle->data));
if(status) { fsEvent.publish(ErrorEvent{status}); }
else { fsEvent.publish(FsEventEvent{filename, static_cast<typename std::underlying_type<details::UVFsEvent>::type>(events)}); }
}
public:
using Watch = details::UVFsEvent;
using Event = details::UVFsEventFlags;
using Handle::Handle;
/**
* @brief Initializes the handle.
* @return True in case of success, false otherwise.
*/
bool init() {
return initialize(&uv_fs_event_init);
}
/**
* @brief Starts watching the specified path.
*
* It will watch the specified path for changes.<br/>
* As soon as a change is observed, a FsEventEvent is emitted by the
* handle.<br>
* It could happen that ErrorEvent events are emitted while running.
*
* Available flags are:
*
* * `FsEventHandle::Event::WATCH_ENTRY`
* * `FsEventHandle::Event::STAT`
* * `FsEventHandle::Event::RECURSIVE`
*
* @param path The file or directory to be monitored.
* @param flags Additional flags to control the behavior.
*/
void start(std::string path, Flags<Event> flags = Flags<Event>{}) {
invoke(&uv_fs_event_start, get(), &startCallback, path.data(), flags);
}
/**
* @brief Starts watching the specified path.
*
* It will watch the specified path for changes.<br/>
* As soon as a change is observed, a FsEventEvent is emitted by the
* handle.<br>
* It could happen that ErrorEvent events are emitted while running.
*
* Available flags are:
*
* * `FsEventHandle::Event::WATCH_ENTRY`
* * `FsEventHandle::Event::STAT`
* * `FsEventHandle::Event::RECURSIVE`
*
* @param path The file or directory to be monitored.
* @param flag Additional flag to control the behavior.
*/
void start(std::string path, Event flag) {
start(std::move(path), Flags<Event>{flag});
}
/**
* @brief Stops polling the file descriptor.
*/
void stop() {
invoke(&uv_fs_event_stop, get());
}
/**
* @brief Gets the path being monitored.
* @return The path being monitored, an empty string in case of errors.
*/
std::string path() noexcept {
return details::tryRead(&uv_fs_event_getpath, get());
}
};
}

33
src/uvw/fs_event.ipp Normal file
View File

@ -0,0 +1,33 @@
#include "config.h"
namespace uvw {
UVW_INLINE fs_event_event::fs_event_event(const char *pathname, details::uvw_fs_event events)
: filename{pathname}, flags{events} {}
// NOLINTNEXTLINE(bugprone-easily-swappable-parameters)
UVW_INLINE void fs_event_handle::start_callback(uv_fs_event_t *hndl, const char *filename, int events, int status) {
if(fs_event_handle &fsEvent = *(static_cast<fs_event_handle *>(hndl->data)); status) {
fsEvent.publish(error_event{status});
} else {
fsEvent.publish(fs_event_event{filename, details::uvw_fs_event(events)});
}
}
UVW_INLINE int fs_event_handle::init() {
return leak_if(uv_fs_event_init(parent().raw(), raw()));
}
UVW_INLINE int fs_event_handle::start(const std::string &path, event_flags flags) {
return uv_fs_event_start(raw(), &start_callback, path.data(), static_cast<uv_fs_event_flags>(flags));
}
UVW_INLINE int fs_event_handle::stop() {
return uv_fs_event_stop(raw());
}
UVW_INLINE std::string fs_event_handle::path() noexcept {
return details::try_read(&uv_fs_event_getpath, raw());
}
} // namespace uvw

2
src/uvw/fs_poll.cpp Normal file
View File

@ -0,0 +1,2 @@
#include "fs_poll.h"
#include "fs_poll.ipp"

76
src/uvw/fs_poll.h Normal file
View File

@ -0,0 +1,76 @@
#ifndef UVW_FS_POLL_INCLUDE_H
#define UVW_FS_POLL_INCLUDE_H
#include <chrono>
#include <string>
#include <uv.h>
#include "config.h"
#include "handle.hpp"
#include "loop.h"
#include "util.h"
namespace uvw {
/*! @brief Fs pos event. */
struct fs_poll_event {
explicit fs_poll_event(file_info previous, file_info current) noexcept;
file_info prev; /*!< The old file_info struct. */
file_info curr; /*!< The new file_info struct. */
};
/**
* @brief The fs poll handle.
*
* It allows users to monitor a given path for changes. Unlike fs_event_handle,
* fs_poll_handle uses stat to detect when a file has changed so it can work on
* file systems where fs_event_handle handles cant.
*
* To create a `fs_poll_handle` through a `loop`, no arguments are required.
*/
class fs_poll_handle final: public handle<fs_poll_handle, uv_fs_poll_t, fs_poll_event> {
static void start_callback(uv_fs_poll_t *hndl, int status, const uv_stat_t *prev, const uv_stat_t *curr);
public:
using time = std::chrono::duration<unsigned int, std::milli>;
using handle::handle;
/**
* @brief Initializes the handle.
* @return Underlying return value.
*/
int init();
/**
* @brief Starts the handle.
*
* The handle will start emitting fs_poll_event when needed.
*
* @param file The path to the file to be checked.
* @param interval Milliseconds between successive checks.
* @return Underlying return value.
*/
int start(const std::string &file, time interval);
/**
* @brief Stops the handle.
* @return Underlying return value.
*/
int stop();
/**
* @brief Gets the path being monitored by the handle.
* @return The path being monitored by the handle, an empty string in case
* of errors.
*/
std::string path() noexcept;
};
} // namespace uvw
#ifndef UVW_AS_LIB
# include "fs_poll.ipp"
#endif
#endif // UVW_FS_POLL_INCLUDE_H

View File

@ -1,91 +0,0 @@
#pragma once
#include <utility>
#include <string>
#include <memory>
#include <chrono>
#include <uv.h>
#include "handle.hpp"
#include "util.hpp"
#include "loop.hpp"
namespace uvw {
/**
* @brief FsPollEvent event.
*
* It will be emitted by FsPollHandle according with its functionalities.
*/
struct FsPollEvent {
explicit FsPollEvent(Stat previous, Stat current) noexcept
: prev(std::move(previous)), curr(std::move(current))
{}
Stat prev; /*!< The old Stat struct. */
Stat curr; /*!< The new Stat struct. */
};
/**
* @brief The FsPollHandle handle.
*
* It allows the user to monitor a given path for changes. Unlike FsEventHandle
* handles, FsPollHandle handles use stat to detect when a file has changed so
* they can work on file systems where FsEventHandle handles cant.
*
* To create a `FsPollHandle` through a `Loop`, no arguments are required.
*/
class FsPollHandle final: public Handle<FsPollHandle, uv_fs_poll_t> {
static void startCallback(uv_fs_poll_t *handle, int status, const uv_stat_t *prev, const uv_stat_t *curr) {
FsPollHandle &fsPoll = *(static_cast<FsPollHandle*>(handle->data));
if(status) { fsPoll.publish(ErrorEvent{status}); }
else { fsPoll.publish(FsPollEvent{ *prev, *curr }); }
}
public:
using Time = std::chrono::duration<unsigned int, std::milli>;
using Handle::Handle;
/**
* @brief Initializes the handle.
* @return True in case of success, false otherwise.
*/
bool init() {
return initialize(&uv_fs_poll_init);
}
/**
* @brief Starts the handle.
*
* The handle will start emitting FsPollEvent when needed.
*
* @param file The path to the file to be checked.
* @param interval Milliseconds between successive checks.
*/
void start(std::string file, Time interval) {
invoke(&uv_fs_poll_start, get(), &startCallback, file.data(), interval.count());
}
/**
* @brief Stops the handle.
*/
void stop() {
invoke(&uv_fs_poll_stop, get());
}
/**
* @brief Gets the path being monitored by the handle.
* @return The path being monitored by the handle, an empty string in case
* of errors.
*/
std::string path() noexcept {
return details::tryRead(&uv_fs_poll_getpath, get());
}
};
}

32
src/uvw/fs_poll.ipp Normal file
View File

@ -0,0 +1,32 @@
#include "config.h"
namespace uvw {
UVW_INLINE fs_poll_event::fs_poll_event(file_info previous, file_info current) noexcept
: prev{previous}, curr{current} {}
UVW_INLINE void fs_poll_handle::start_callback(uv_fs_poll_t *hndl, int status, const uv_stat_t *prev, const uv_stat_t *curr) {
if(fs_poll_handle &fsPoll = *(static_cast<fs_poll_handle *>(hndl->data)); status) {
fsPoll.publish(error_event{status});
} else {
fsPoll.publish(fs_poll_event{*prev, *curr});
}
}
UVW_INLINE int fs_poll_handle::init() {
return leak_if(uv_fs_poll_init(parent().raw(), raw()));
}
UVW_INLINE int fs_poll_handle::start(const std::string &file, fs_poll_handle::time interval) {
return uv_fs_poll_start(raw(), &start_callback, file.data(), interval.count());
}
UVW_INLINE int fs_poll_handle::stop() {
return uv_fs_poll_stop(raw());
}
UVW_INLINE std::string fs_poll_handle::path() noexcept {
return details::try_read(&uv_fs_poll_getpath, raw());
}
} // namespace uvw

View File

@ -1,70 +1,44 @@
#pragma once
#ifndef UVW_HANDLE_INCLUDE_H
#define UVW_HANDLE_INCLUDE_H
#include <cstddef>
#include <utility>
#include <memory>
#include <utility>
#include <uv.h>
#include "config.h"
#include "resource.hpp"
#include "util.hpp"
#include "util.h"
namespace uvw {
/**
* @brief CloseEvent event.
*
* It will be emitted by the handles according with their functionalities.
*/
struct CloseEvent {};
/*! @brief Close event. */
struct close_event {};
/**
* @brief Handle base class.
*
* Base type for all `uvw` handle types.
*/
template<typename T, typename U>
class Handle: public BaseHandle, public Resource<T, U>
{
static void closeCallback(uv_handle_t *handle) {
Handle<T, U> &ref = *(static_cast<T*>(handle->data));
auto ptr = ref.shared_from_this();
(void)ptr;
ref.reset();
ref.publish(CloseEvent{});
}
template<typename T, typename U, typename... E>
class handle: public resource<T, U, close_event, E...> {
protected:
static void allocCallback(uv_handle_t *, std::size_t suggested, uv_buf_t *buf) {
auto size = static_cast<unsigned int>(suggested);
*buf = uv_buf_init(new char[size], size);
static void close_callback(uv_handle_t *hndl) {
handle<T, U, E...> &ref = *(static_cast<T *>(hndl->data));
[[maybe_unused]] auto ptr = ref.shared_from_this();
ref.self_reset();
ref.publish(close_event{});
}
template<typename F, typename... Args>
bool initialize(F &&f, Args&&... args) {
if(!this->self()) {
auto err = std::forward<F>(f)(this->parent(), this->get(), std::forward<Args>(args)...);
if(err) {
this->publish(ErrorEvent{err});
} else {
this->leak();
}
[[nodiscard]] uv_handle_t *as_uv_handle() {
return reinterpret_cast<uv_handle_t *>(this->raw());
}
return this->self();
}
template<typename F, typename... Args>
void invoke(F &&f, Args&&... args) {
auto err = std::forward<F>(f)(std::forward<Args>(args)...);
if(err) { Emitter<T>::publish(ErrorEvent{err}); }
[[nodiscard]] const uv_handle_t *as_uv_handle() const {
return reinterpret_cast<const uv_handle_t *>(this->raw());
}
public:
using Resource<T, U>::Resource;
using resource<T, U, close_event, E...>::resource;
/**
* @brief Gets the category of the handle.
@ -75,8 +49,8 @@ public:
*
* @return The actual category of the handle.
*/
HandleCategory category() const noexcept override {
return HandleCategory{this->template get<uv_handle_t>()->type};
[[nodiscard]] handle_category category() const noexcept {
return handle_category{as_uv_handle()->type};
}
/**
@ -84,12 +58,12 @@ public:
*
* A base handle offers no functionality to promote it to the actual handle
* type. By means of this function, the type of the underlying handle as
* specified by HandleType is made available to the users.
* specified by handle_type is made available to the users.
*
* @return The actual type of the handle.
*/
HandleType type() const noexcept override {
return Utilities::guessHandle(category());
[[nodiscard]] handle_type type() const noexcept {
return utilities::guess_handle(category());
}
/**
@ -97,22 +71,22 @@ public:
*
* What _active_ means depends on the type of handle:
*
* * An AsyncHandle handle is always active and cannot be deactivated,
* * An async_handle handle is always active and cannot be deactivated,
* except by closing it with uv_close().
* * A PipeHandle, TCPHandle, UDPHandle, etc. handle - basically any handle
* that deals with I/O - is active when it is doing something that involves
* I/O, like reading, writing, connecting, accepting new connections, etc.
* * A CheckHandle, IdleHandle, TimerHandle, etc. handle is active when it
* has been started with a call to `start()`.
* * A pipe, tcp, udp, etc. handle - basically any handle that deals with
* I/O - is active when it is doing something that involves I/O, like
* reading, writing, connecting, accepting new connections, etc.
* * A check, idle, timer, etc. handle is active when it has been started
* with a call to `start()`.
*
* Rule of thumb: if a handle of type `FooHandle` has a `start()` member
* Rule of thumb: if a handle of type `foo_handle` has a `start()` member
* method, then its active from the moment that method is called. Likewise,
* `stop()` deactivates the handle again.
*
* @return True if the handle is active, false otherwise.
*/
bool active() const noexcept override {
return !(uv_is_active(this->template get<uv_handle_t>()) == 0);
[[nodiscard]] bool active() const noexcept {
return !!uv_is_active(as_uv_handle());
}
/**
@ -123,22 +97,21 @@ public:
*
* @return True if the handle is closing or closed, false otherwise.
*/
bool closing() const noexcept override {
return !(uv_is_closing(this->template get<uv_handle_t>()) == 0);
[[nodiscard]] bool closing() const noexcept {
return !!uv_is_closing(as_uv_handle());
}
/**
* @brief Request handle to be closed.
*
* This **must** be called on each handle before memory is released.<br/>
* In-progress requests are cancelled and this can result in an ErrorEvent
* emitted.
* In-progress requests are cancelled and this can result in errors.
*
* The handle will emit a CloseEvent when finished.
* The handle will emit a close event when finished.
*/
void close() noexcept override {
void close() noexcept {
if(!closing()) {
uv_close(this->template get<uv_handle_t>(), &Handle<T, U>::closeCallback);
uv_close(as_uv_handle(), &handle<T, U, E...>::close_callback);
}
}
@ -148,8 +121,8 @@ public:
* References are idempotent, that is, if a handle is already referenced
* calling this function again will have no effect.
*/
void reference() noexcept override {
uv_ref(this->template get<uv_handle_t>());
void reference() noexcept {
uv_ref(as_uv_handle());
}
/**
@ -158,24 +131,24 @@ public:
* References are idempotent, that is, if a handle is not referenced calling
* this function again will have no effect.
*/
void unreference() noexcept override {
uv_unref(this->template get<uv_handle_t>());
void unreference() noexcept {
uv_unref(as_uv_handle());
}
/**
* @brief Checks if the given handle referenced.
* @return True if the handle referenced, false otherwise.
*/
bool referenced() const noexcept override {
return !(uv_has_ref(this->template get<uv_handle_t>()) == 0);
[[nodiscard]] bool referenced() const noexcept {
return !!uv_has_ref(as_uv_handle());
}
/**
* @brief Returns the size of the underlying handle type.
* @return The size of the underlying handle type.
*/
std::size_t size() const noexcept {
return uv_handle_size(this->template get<uv_handle_t>()->type);
[[nodiscard]] std::size_t size() const noexcept {
return uv_handle_size(as_uv_handle()->type);
}
/**
@ -183,16 +156,17 @@ public:
*
* Gets the size of the send buffer that the operating system uses for the
* socket.<br/>
* This function works for TCPHandle, PipeHandle and UDPHandle handles on
* Unix and for TCPHandle and UDPHandle handles on Windows.<br/>
* This function works for tcp, pipeand udp handles on Unix and for tcp and
* udp handles on Windows.<br/>
* Note that Linux will return double the size of the original set value.
*
* @return The size of the send buffer, 0 in case of errors.
* @return The size of the send buffer, the underlying return value in case
* of errors.
*/
int sendBufferSize() {
[[nodiscard]] int send_buffer_size() {
int value = 0;
auto err = uv_send_buffer_size(this->template get<uv_handle_t>(), &value);
return err ? 0 : value;
auto err = uv_send_buffer_size(as_uv_handle(), &value);
return err ? err : value;
}
/**
@ -200,14 +174,14 @@ public:
*
* Sets the size of the send buffer that the operating system uses for the
* socket.<br/>
* This function works for TCPHandle, PipeHandle and UDPHandle handles on
* Unix and for TCPHandle and UDPHandle handles on Windows.<br/>
* This function works for tcp, pipe and udp handles on Unix and for tcp and
* udp handles on Windows.<br/>
* Note that Linux will set double the size.
*
* @return True in case of success, false otherwise.
* @return Underlying return value.
*/
bool sendBufferSize(int value) {
return (0 == uv_send_buffer_size(this->template get<uv_handle_t>(), &value));
[[nodiscard]] int send_buffer_size(int value) {
return uv_send_buffer_size(as_uv_handle(), &value);
}
/**
@ -215,16 +189,17 @@ public:
*
* Gets the size of the receive buffer that the operating system uses for
* the socket.<br/>
* This function works for TCPHandle, PipeHandle and UDPHandle handles on
* Unix and for TCPHandle and UDPHandle handles on Windows.<br/>
* This function works for tcp, pipe and udp handles on Unix and for tcp and
* udp handles on Windows.<br/>
* Note that Linux will return double the size of the original set value.
*
* @return The size of the receive buffer, 0 in case of errors.
* @return The size of the receive buffer, the underlying return value in
* case of errors.
*/
int recvBufferSize() {
[[nodiscard]] int recv_buffer_size() {
int value = 0;
auto err = uv_recv_buffer_size(this->template get<uv_handle_t>(), &value);
return err ? 0 : value;
auto err = uv_recv_buffer_size(as_uv_handle(), &value);
return err ? err : value;
}
/**
@ -232,14 +207,14 @@ public:
*
* Sets the size of the receive buffer that the operating system uses for
* the socket.<br/>
* This function works for TCPHandle, PipeHandle and UDPHandle handles on
* Unix and for TCPHandle and UDPHandle handles on Windows.<br/>
* This function works for tcp, pipe and udp handles on Unix and for tcp and
* udp handles on Windows.<br/>
* Note that Linux will set double the size.
*
* @return True in case of success, false otherwise.
* @return Underlying return value.
*/
bool recvBufferSize(int value) {
return (0 == uv_recv_buffer_size(this->template get<uv_handle_t>(), &value));
[[nodiscard]] int recv_buffer_size(int value) {
return uv_recv_buffer_size(as_uv_handle(), &value);
}
/**
@ -247,15 +222,14 @@ public:
*
* Supported handles:
*
* * TCPHandle
* * PipeHandle
* * TTYHandle
* * UDPHandle
* * PollHandle
* * tcp_handle
* * pipe_handle
* * tty_handle
* * udp_handle
* * poll_handle
*
* It will emit an ErrorEvent event if invoked on any other handle.<br/>
* If a handle doesnt have an attached file descriptor yet or the handle
* itself has been closed, an ErrorEvent event will be emitted.
* If invoked on a different handle, one that doesnt have an attached file
* descriptor yet or one which was closed, an invalid value is returned.
*
* See the official
* [documentation](http://docs.libuv.org/en/v1.x/handle.html#c.uv_fileno)
@ -264,12 +238,13 @@ public:
* @return The file descriptor attached to the hande or a negative value in
* case of errors.
*/
OSFileDescriptor fileno() const {
[[nodiscard]] os_file_descriptor fd() const {
uv_os_fd_t fd;
uv_fileno(this->template get<uv_handle_t>(), &fd);
uv_fileno(as_uv_handle(), &fd);
return fd;
}
};
} // namespace uvw
}
#endif // UVW_HANDLE_INCLUDE_H

2
src/uvw/idle.cpp Normal file
View File

@ -0,0 +1,2 @@
#include "idle.h"
#include "idle.ipp"

65
src/uvw/idle.h Normal file
View File

@ -0,0 +1,65 @@
#ifndef UVW_IDLE_INCLUDE_H
#define UVW_IDLE_INCLUDE_H
#include <uv.h>
#include "handle.hpp"
#include "loop.h"
namespace uvw {
/*! @brief Idle event. */
struct idle_event {};
/**
* @brief The idle handle.
*
* Idle handles will emit a idle event once per loop iteration, right before the
* prepare handles.
*
* The notable difference with prepare handles is that when there are active
* idle handles, the loop will perform a zero timeout poll instead of blocking
* for I/O.
*
* @note
* Despite the name, idle handles will emit events on every loop iteration, not
* when the loop is actually _idle_.
*
* To create an `idle_handle` through a `loop`, no arguments are required.
*/
class idle_handle final: public handle<idle_handle, uv_idle_t, idle_event> {
static void start_callback(uv_idle_t *hndl);
public:
using handle::handle;
/**
* @brief Initializes the handle.
* @return Underlying return value.
*/
int init();
/**
* @brief Starts the handle.
*
* An idle event will be emitted once per loop iteration, right before
* polling the prepare handles.
*
* @return Underlying return value.
*/
int start();
/**
* @brief Stops the handle.
*
* @return Underlying return value.
*/
int stop();
};
} // namespace uvw
#ifndef UVW_AS_LIB
# include "idle.ipp"
#endif
#endif // UVW_IDLE_INCLUDE_H

View File

@ -1,74 +0,0 @@
#pragma once
#include <utility>
#include <memory>
#include <uv.h>
#include "handle.hpp"
#include "loop.hpp"
namespace uvw {
/**
* @brief IdleEvent event.
*
* It will be emitted by IdleHandle according with its functionalities.
*/
struct IdleEvent {};
/**
* @brief The IdleHandle handle.
*
* Idle handles will emit a IdleEvent event once per loop iteration, right
* before the PrepareHandle handles.
*
* The notable difference with prepare handles is that when there are active
* idle handles, the loop will perform a zero timeout poll instead of blocking
* for I/O.
*
* @note
* Despite the name, idle handles will emit events on every loop iteration, not
* when the loop is actually _idle_.
*
* To create an `IdleHandle` through a `Loop`, no arguments are required.
*/
class IdleHandle final: public Handle<IdleHandle, uv_idle_t> {
static void startCallback(uv_idle_t *handle) {
IdleHandle &idle = *(static_cast<IdleHandle*>(handle->data));
idle.publish(IdleEvent{});
}
public:
using Handle::Handle;
/**
* @brief Initializes the handle.
* @return True in case of success, false otherwise.
*/
bool init() {
return initialize(&uv_idle_init);
}
/**
* @brief Starts the handle.
*
* A IdleEvent event will be emitted once per loop iteration, right before
* polling the PrepareHandle handles.
*/
void start() {
invoke(&uv_idle_start, get(), &startCallback);
}
/**
* @brief Stops the handle.
*/
void stop() {
invoke(&uv_idle_stop, get());
}
};
}

22
src/uvw/idle.ipp Normal file
View File

@ -0,0 +1,22 @@
#include "config.h"
namespace uvw {
UVW_INLINE void idle_handle::start_callback(uv_idle_t *hndl) {
idle_handle &idle = *(static_cast<idle_handle *>(hndl->data));
idle.publish(idle_event{});
}
UVW_INLINE int idle_handle::init() {
return leak_if(uv_idle_init(parent().raw(), raw()));
}
UVW_INLINE int idle_handle::start() {
return uv_idle_start(raw(), &start_callback);
}
UVW_INLINE int idle_handle::stop() {
return uv_idle_stop(raw());
}
} // namespace uvw

2
src/uvw/lib.cpp Normal file
View File

@ -0,0 +1,2 @@
#include "lib.h"
#include "lib.ipp"

View File

@ -1,41 +1,33 @@
#pragma once
#ifndef UVW_LIB_INCLUDE_H
#define UVW_LIB_INCLUDE_H
#include <memory>
#include <string>
#include <type_traits>
#include <utility>
#include <uv.h>
#include "loop.hpp"
#include "underlying_type.hpp"
#include "config.h"
#include "loop.h"
#include "uv_type.hpp"
namespace uvw {
/**
* @brief The SharedLib class.
* @brief The shared lib class.
*
* `uvw` provides cross platform utilities for loading shared libraries and
* retrieving symbols from them, by means of the API offered by `libuv`.
*/
class SharedLib final: public UnderlyingType<SharedLib, uv_lib_t> {
class shared_lib final: public uv_type<uv_lib_t> {
public:
explicit SharedLib(ConstructorAccess ca, std::shared_ptr<Loop> ref, std::string filename) noexcept
: UnderlyingType{ca, std::move(ref)}
{
opened = (0 == uv_dlopen(filename.data(), get()));
}
explicit shared_lib(loop::token token, std::shared_ptr<loop> ref, const std::string &filename) noexcept;
~SharedLib() noexcept {
uv_dlclose(get());
}
~shared_lib() noexcept;
/**
* @brief Checks if the library has been correctly opened.
* @return True if the library is opened, false otherwise.
*/
explicit operator bool() const noexcept { return opened; }
explicit operator bool() const noexcept;
/**
* @brief Retrieves a data pointer from a dynamic library.
@ -47,11 +39,13 @@ public:
* @return A valid function pointer in case of success, `nullptr` otherwise.
*/
template<typename F>
F * sym(std::string name) {
static_assert(std::is_function<F>::value, "!");
F *sym(const std::string &name) {
static_assert(std::is_function_v<F>);
F *func;
auto err = uv_dlsym(get(), name.data(), reinterpret_cast<void**>(&func));
if(err) { func = nullptr; }
auto err = uv_dlsym(raw(), name.data(), reinterpret_cast<void **>(&func));
if(err) {
func = nullptr;
}
return func;
}
@ -59,13 +53,16 @@ public:
* @brief Returns the last error message, if any.
* @return The last error message, if any.
*/
const char * error() const noexcept {
return uv_dlerror(get());
}
[[nodiscard]] const char *error() const noexcept;
private:
bool opened;
};
} // namespace uvw
}
#ifndef UVW_AS_LIB
# include "lib.ipp"
#endif
#endif // UVW_LIB_INCLUDE_H

23
src/uvw/lib.ipp Normal file
View File

@ -0,0 +1,23 @@
#include <utility>
#include "config.h"
namespace uvw {
UVW_INLINE shared_lib::shared_lib(loop::token token, std::shared_ptr<loop> ref, const std::string &filename) noexcept
: uv_type{token, std::move(ref)} {
opened = (0 == uv_dlopen(filename.data(), raw()));
}
UVW_INLINE shared_lib::~shared_lib() noexcept {
uv_dlclose(raw());
}
UVW_INLINE shared_lib::operator bool() const noexcept {
return opened;
}
UVW_INLINE const char *shared_lib::error() const noexcept {
return uv_dlerror(raw());
}
} // namespace uvw

2
src/uvw/loop.cpp Normal file
View File

@ -0,0 +1,2 @@
#include "loop.h"
#include "loop.ipp"

439
src/uvw/loop.h Normal file
View File

@ -0,0 +1,439 @@
#ifndef UVW_LOOP_INCLUDE_H
#define UVW_LOOP_INCLUDE_H
#ifdef _WIN32
# include <ciso646>
#endif
#include <chrono>
#include <functional>
#include <memory>
#include <type_traits>
#include <utility>
#include <uv.h>
#include "config.h"
#include "emitter.h"
#include "util.h"
namespace uvw {
class async_handle;
class check_handle;
class fs_event_handle;
class fs_poll_handle;
class idle_handle;
class pipe_handle;
class poll_handle;
class prepare_handle;
class process_handle;
class signal_handle;
class tcp_handle;
class timer_handle;
class tty_handle;
class udp_handle;
namespace details {
enum class uvw_loop_option : std::underlying_type_t<uv_loop_option> {
BLOCK_SIGNAL = UV_LOOP_BLOCK_SIGNAL,
IDLE_TIME = UV_METRICS_IDLE_TIME
};
enum class uvw_run_mode : std::underlying_type_t<uv_run_mode> {
DEFAULT = UV_RUN_DEFAULT,
ONCE = UV_RUN_ONCE,
NOWAIT = UV_RUN_NOWAIT
};
} // namespace details
using metrics_type = uv_metrics_t; /*!< Library equivalent for uv_metrics_t. */
/**
* @brief The loop class.
*
* The event loop is the central part of `uvw`'s functionalities, as well as
* `libuv`'s ones.<br/>
* It takes care of polling for I/O and scheduling callbacks to be run based on
* different sources of events.
*/
class loop final: public emitter<loop>, public std::enable_shared_from_this<loop> {
using deleter = void (*)(uv_loop_t *);
template<typename, typename, typename...>
friend class resource;
class uv_token {
friend class loop;
explicit uv_token(int) {}
};
template<typename Type>
auto init(int, Type &value) -> decltype(value.init()) {
return value.init();
}
template<typename Type>
int init(char, Type &) {
return 0;
}
loop(std::unique_ptr<uv_loop_t, deleter> ptr) noexcept;
public:
using token = uv_token;
using time = std::chrono::duration<uint64_t, std::milli>;
using option = details::uvw_loop_option;
using run_mode = details::uvw_run_mode;
/**
* @brief Initializes a new loop instance.
* @return A pointer to the newly created loop.
*/
static std::shared_ptr<loop> create();
/**
* @brief Initializes a new loop instance from an existing resource.
*
* The lifetime of the resource must exceed that of the instance to which
* it's associated. Management of the memory associated with the resource is
* in charge of the user.
*
* @param res A valid pointer to a correctly initialized resource.
* @return A pointer to the newly created loop.
*/
static std::shared_ptr<loop> create(uv_loop_t *res);
/**
* @brief Gets the initialized default loop.
*
* It may return an empty pointer in case of failure.<br>
* This function is just a convenient way for having a global loop
* throughout an application, the default loop is in no way different than
* the ones initialized with `create()`.<br>
* As such, the default loop can be closed with `close()` so the resources
* associated with it are freed (even if it is not strictly necessary).
*
* @return The initialized default loop.
*/
static std::shared_ptr<loop> get_default();
loop(const loop &) = delete;
loop(loop &&other) = delete;
loop &operator=(const loop &) = delete;
loop &operator=(loop &&other) = delete;
~loop() noexcept override;
/**
* @brief Sets additional loop options.
*
* You should normally call this before the first call to uv_run() unless
* mentioned otherwise.<br/>
* Supported options:
*
* * `loop::option::BLOCK_SIGNAL`: Block a signal when polling for new
* events. A second argument is required and it is the signal number.
* * `loop::option::IDLE_TIME`: Accumulate the amount of idle time the event
* loop spends in the event provider. This option is necessary to use
* `idle_time()`.
*
* See the official
* [documentation](http://docs.libuv.org/en/v1.x/loop.html#c.uv_loop_configure)
* for further details.
*
* @return Underlying return value.
*/
template<typename... Args>
int configure(option flag, Args &&...args) {
return uv_loop_configure(uv_loop.get(), static_cast<uv_loop_option>(flag), std::forward<Args>(args)...);
}
/**
* @brief Creates resources of any type.
*
* This should be used as a default method to create resources.<br/>
* The arguments are the ones required for the specific resource.
*
* Use it as `loop->resource<uvw::timer_handle>()`.
*
* @return A pointer to the newly created resource.
*/
template<typename R, typename... Args>
std::shared_ptr<R> resource(Args &&...args) {
auto ptr = uninitialized_resource<R>(std::forward<Args>(args)...);
return (init(0, *ptr) == 0) ? ptr : nullptr;
}
/**
* @brief Creates uninitialized resources of any type.
* @return A pointer to the newly created resource.
*/
template<typename R, typename... Args>
std::shared_ptr<R> uninitialized_resource(Args &&...args) {
return std::make_shared<R>(token{0}, shared_from_this(), std::forward<Args>(args)...);
}
/**
* @brief Releases all internal loop resources.
*
* Call this function only when the loop has finished executing and all open
* handles and requests have been closed, or the loop will error.
*
* @return Underlying return value.
*/
int close();
/**
* @brief Runs the event loop.
*
* Available modes are:
*
* * `loop::run_mode::DEFAULT`: Runs the event loop until there are no more
* active and referenced handles or requests.
* * `loop::run_mode::ONCE`: Poll for i/o once. Note that this function
* blocks if there are no pending callbacks.
* * `loop::run_mode::NOWAIT`: Poll for i/o once but dont block if there
* are no pending callbacks.
*
* See the official
* [documentation](http://docs.libuv.org/en/v1.x/loop.html#c.uv_run)
* for further details.
*
* @return Underlying return value.
*/
int run(run_mode mode = run_mode::DEFAULT) noexcept;
/**
* @brief Checks if there are active resources.
* @return True if there are active resources in the loop.
*/
bool alive() const noexcept;
/**
* @brief Stops the event loop.
*
* It causes `run()` to end as soon as possible.<br/>
* This will happen not sooner than the next loop iteration.<br/>
* If this function was called before blocking for I/O, the loop wont block
* for I/O on this iteration.
*/
void stop() noexcept;
/**
* @brief Get backend file descriptor.
*
* Only kqueue, epoll and event ports are supported.<br/>
* This can be used in conjunction with `run(loop::run_mode::NOWAIT)` to
* poll in one thread and run the event loops callbacks in another.
*
* @return The backend file descriptor.
*/
int descriptor() const noexcept;
/**
* @brief Gets the poll timeout.
* @return A `std::pair` composed as it follows:
* * A boolean value that is true in case of valid timeout, false otherwise.
* * Milliseconds (`std::chrono::duration<uint64_t, std::milli>`).
*/
std::pair<bool, time> timeout() const noexcept;
/**
* @brief Returns the amount of time the event loop has been idle. The call
* is thread safe.
* @return The accumulated time spent idle.
*/
time idle_time() const noexcept;
/**
* @brief Tracks various internal operations of the event loop.
* @return Event loop metrics.
*/
metrics_type metrics() const noexcept;
/**
* @brief Returns the current timestamp in milliseconds.
*
* The timestamp is cached at the start of the event loop tick.<br/>
* The timestamp increases monotonically from some arbitrary point in
* time.<br/>
* Dont make assumptions about the starting point, you will only get
* disappointed.
*
* @return The current timestamp in milliseconds (actual type is
* `std::chrono::duration<uint64_t, std::milli>`).
*/
time now() const noexcept;
/**
* @brief Updates the event loops concept of _now_.
*
* The current time is cached at the start of the event loop tick in order
* to reduce the number of time-related system calls.<br/>
* You wont normally need to call this function unless you have callbacks
* that block the event loop for longer periods of time, where _longer_ is
* somewhat subjective but probably on the order of a millisecond or more.
*/
void update() const noexcept;
/**
* @brief Walks the list of handles.
*
* The callback is invoked once for each handle that is still active.
*
* @param callback A function to invoke once for each active handle.
*/
template<typename Func>
void walk(Func callback) {
auto func = [](uv_handle_t *hndl, void *callback_func) {
if(hndl->data) {
auto &cb = *static_cast<Func *>(callback_func);
switch(utilities::guess_handle(handle_category{hndl->type})) {
case handle_type::ASYNC:
cb(*static_cast<async_handle *>(hndl->data));
break;
case handle_type::CHECK:
cb(*static_cast<check_handle *>(hndl->data));
break;
case handle_type::FS_EVENT:
cb(*static_cast<fs_event_handle *>(hndl->data));
break;
case handle_type::FS_POLL:
cb(*static_cast<fs_poll_handle *>(hndl->data));
break;
case handle_type::IDLE:
cb(*static_cast<idle_handle *>(hndl->data));
break;
case handle_type::PIPE:
cb(*static_cast<pipe_handle *>(hndl->data));
break;
case handle_type::POLL:
cb(*static_cast<poll_handle *>(hndl->data));
break;
case handle_type::PREPARE:
cb(*static_cast<prepare_handle *>(hndl->data));
break;
case handle_type::PROCESS:
cb(*static_cast<process_handle *>(hndl->data));
break;
case handle_type::SIGNAL:
cb(*static_cast<signal_handle *>(hndl->data));
break;
case handle_type::TCP:
cb(*static_cast<tcp_handle *>(hndl->data));
break;
case handle_type::TIMER:
cb(*static_cast<timer_handle *>(hndl->data));
break;
case handle_type::TTY:
cb(*static_cast<tty_handle *>(hndl->data));
break;
case handle_type::UDP:
cb(*static_cast<udp_handle *>(hndl->data));
break;
default:
// this handle isn't managed by uvw, let it be...
break;
}
}
};
uv_walk(uv_loop.get(), func, &callback);
}
/**
* @brief Reinitialize any kernel state necessary in the child process after
* a fork(2) system call.
*
* Previously started watchers will continue to be started in the child
* process.
*
* It is necessary to explicitly call this function on every event loop
* created in the parent process that you plan to continue to use in the
* child, including the default loop (even if you dont continue to use it
* in the parent). This function must be called before calling any API
* function using the loop in the child. Failure to do so will result in
* undefined behaviour, possibly including duplicate events delivered to
* both parent and child or aborting the child process.
*
* When possible, it is preferred to create a new loop in the child process
* instead of reusing a loop created in the parent. New loops created in the
* child process after the fork should not use this function.
*
* Note that this function is not implemented on Windows.<br/>
* Note also that this function is experimental in `libuv`. It may contain
* bugs, and is subject to change or removal. API and ABI stability is not
* guaranteed.
*
* See the official
* [documentation](http://docs.libuv.org/en/v1.x/loop.html#c.uv_loop_fork)
* for further details.
*
* @return Underlying return value.
*/
int fork() noexcept;
/**
* @brief Gets user-defined data. `uvw` won't use this field in any case.
* @return User-defined data if any, an invalid pointer otherwise.
*/
template<typename R = void>
std::shared_ptr<R> data() const {
return std::static_pointer_cast<R>(user_data);
}
/**
* @brief Sets arbitrary data. `uvw` won't use this field in any case.
* @param ud User-defined arbitrary data.
*/
void data(std::shared_ptr<void> ud);
/**
* @brief Gets the underlying raw data structure.
*
* This function should not be used, unless you know exactly what you are
* doing and what are the risks.<br/>
* Going raw is dangerous, mainly because the lifetime management of a loop,
* a handle or a request is in charge to the library itself and users should
* not work around it.
*
* @warning
* Use this function at your own risk, but do not expect any support in case
* of bugs.
*
* @return The underlying raw data structure.
*/
const uv_loop_t *raw() const noexcept;
/**
* @brief Gets the underlying raw data structure.
*
* This function should not be used, unless you know exactly what you are
* doing and what are the risks.<br/>
* Going raw is dangerous, mainly because the lifetime management of a loop,
* a handle or a request is in charge to the library itself and users should
* not work around it.
*
* @warning
* Use this function at your own risk, but do not expect any support in case
* of bugs.
*
* @return The underlying raw data structure.
*/
uv_loop_t *raw() noexcept;
private:
std::unique_ptr<uv_loop_t, deleter> uv_loop;
std::shared_ptr<void> user_data{nullptr};
};
} // namespace uvw
#ifndef UVW_AS_LIB
# include "loop.ipp"
#endif
#endif // UVW_LOOP_INCLUDE_H

View File

@ -1,496 +0,0 @@
#pragma once
#ifdef _WIN32
#include <ciso646>
#endif
#include <functional>
#include <memory>
#include <utility>
#include <type_traits>
#include <chrono>
#include <uv.h>
#include "emitter.hpp"
#include "util.hpp"
namespace uvw {
namespace details {
enum class UVLoopOption: typename std::underlying_type<uv_loop_option>::type {
BLOCK_SIGNAL = UV_LOOP_BLOCK_SIGNAL
};
enum class UVRunMode: typename std::underlying_type<uv_run_mode>::type {
DEFAULT = UV_RUN_DEFAULT,
ONCE = UV_RUN_ONCE,
NOWAIT = UV_RUN_NOWAIT
};
}
/**
* @brief Untyped handle class
*
* Handles' types are unknown from the point of view of the loop.<br/>
* Anyway, a loop maintains a list of all the associated handles and let the
* users walk them as untyped instances.<br/>
* This can help to end all the pending requests by closing the handles.
*/
struct BaseHandle {
/**
* @brief Gets the category of the handle.
*
* A base handle offers no functionality to promote it to the actual handle
* type. By means of this function, an opaque value that identifies the
* category of the handle is made available to the users.
*
* @return The actual category of the handle.
*/
virtual HandleCategory category() const noexcept = 0;
/**
* @brief Gets the type of the handle.
*
* A base handle offers no functionality to promote it to the actual handle
* type. By means of this function, the type of the underlying handle as
* specified by HandleType is made available to the user.
*
* @return The actual type of the handle.
*/
virtual HandleType type() const noexcept = 0;
/**
* @brief Checks if the handle is active.
*
* What _active_ means depends on the type of handle:
*
* * An AsyncHandle handle is always active and cannot be deactivated,
* except by closing it with uv_close().
* * A PipeHandle, TCPHandle, UDPHandle, etc. handle - basically any handle
* that deals with I/O - is active when it is doing something that involves
* I/O, like reading, writing, connecting, accepting new connections, etc.
* * A CheckHandle, IdleHandle, TimerHandle, etc. handle is active when it
* has been started with a call to `start()`.
*
* Rule of thumb: if a handle of type `FooHandle` has a `start()` member
* method, then its active from the moment that method is called. Likewise,
* `stop()` deactivates the handle again.
*
* @return True if the handle is active, false otherwise.
*/
virtual bool active() const noexcept = 0;
/**
* @brief Checks if a handle is closing or closed.
*
* This function should only be used between the initialization of the
* handle and the arrival of the close callback.
*
* @return True if the handle is closing or closed, false otherwise.
*/
virtual bool closing() const noexcept = 0;
/**
* @brief Reference the given handle.
*
* References are idempotent, that is, if a handle is already referenced
* calling this function again will have no effect.
*/
virtual void reference() noexcept = 0;
/**
* @brief Unreference the given handle.
*
* References are idempotent, that is, if a handle is not referenced calling
* this function again will have no effect.
*/
virtual void unreference() noexcept = 0;
/**
* @brief Checks if the given handle referenced.
* @return True if the handle referenced, false otherwise.
*/
virtual bool referenced() const noexcept = 0;
/**
* @brief Request handle to be closed.
*
* This **must** be called on each handle before memory is released.<br/>
* In-progress requests are cancelled and this can result in an ErrorEvent
* emitted.
*/
virtual void close() noexcept = 0;
};
/**
* @brief The Loop class.
*
* The event loop is the central part of `uvw`'s functionalities, as well as
* `libuv`'s ones.<br/>
* It takes care of polling for I/O and scheduling callbacks to be run based on
* different sources of events.
*/
class Loop final: public Emitter<Loop>, public std::enable_shared_from_this<Loop> {
using Deleter = void(*)(uv_loop_t *);
template<typename, typename>
friend class Resource;
Loop(std::unique_ptr<uv_loop_t, Deleter> ptr) noexcept
: loop{std::move(ptr)}
{}
public:
using Time = std::chrono::duration<uint64_t, std::milli>;
using Configure = details::UVLoopOption;
using Mode = details::UVRunMode;
/**
* @brief Initializes a new Loop instance.
* @return A pointer to the newly created loop.
*/
static std::shared_ptr<Loop> create() {
auto ptr = std::unique_ptr<uv_loop_t, Deleter>{new uv_loop_t, [](uv_loop_t *l){ delete l; }};
auto loop = std::shared_ptr<Loop>{new Loop{std::move(ptr)}};
if(uv_loop_init(loop->loop.get())) {
loop = nullptr;
}
return loop;
}
/**
* @brief Gets the initialized default loop.
*
* It may return an empty pointer in case of failure.<br>
* This function is just a convenient way for having a global loop
* throughout an application, the default loop is in no way different than
* the ones initialized with `create()`.<br>
* As such, the default loop can be closed with `close()` so the resources
* associated with it are freed (even if it is not strictly necessary).
*
* @return The initialized default loop.
*/
static std::shared_ptr<Loop> getDefault() {
static std::weak_ptr<Loop> ref;
std::shared_ptr<Loop> loop;
if(ref.expired()) {
auto def = uv_default_loop();
if(def) {
auto ptr = std::unique_ptr<uv_loop_t, Deleter>(def, [](uv_loop_t *){});
loop = std::shared_ptr<Loop>{new Loop{std::move(ptr)}};
}
ref = loop;
} else {
loop = ref.lock();
}
return loop;
}
Loop(const Loop &) = delete;
Loop(Loop &&other) = delete;
Loop & operator=(const Loop &) = delete;
Loop & operator=(Loop &&other) = delete;
~Loop() noexcept {
if(loop) {
close();
}
}
/**
* @brief Sets additional loop options.
*
* You should normally call this before the first call to uv_run() unless
* mentioned otherwise.<br/>
* Supported options:
*
* * `Loop::Configure::BLOCK_SIGNAL`: Block a signal when polling for new
* events. A second argument is required and it is the signal number.
*
* An ErrorEvent will be emitted in case of errors.
*
* See the official
* [documentation](http://docs.libuv.org/en/v1.x/loop.html#c.uv_loop_configure)
* for further details.
*/
template<typename... Args>
void configure(Configure flag, Args&&... args) {
auto option = static_cast<typename std::underlying_type<Configure>::type>(flag);
auto err = uv_loop_configure(loop.get(), static_cast<uv_loop_option>(option), std::forward<Args>(args)...);
if(err) { publish(ErrorEvent{err}); }
}
/**
* @brief Creates resources of handles' types.
*
* This should be used as a default method to create resources.<br/>
* The arguments are the ones required for the specific resource.
*
* Use it as `loop->resource<uvw::TimerHandle>()`.
*
* @return A pointer to the newly created resource.
*/
template<typename R, typename... Args>
typename std::enable_if<std::is_base_of<BaseHandle, R>::value, std::shared_ptr<R>>::type
resource(Args&&... args) {
auto ptr = R::create(shared_from_this(), std::forward<Args>(args)...);
ptr = ptr->init() ? ptr : nullptr;
return ptr;
}
/**
* @brief Creates resources of types other than handles' ones.
*
* This should be used as a default method to create resources.<br/>
* The arguments are the ones required for the specific resource.
*
* Use it as `loop->resource<uvw::WorkReq>()`.
*
* @return A pointer to the newly created resource.
*/
template<typename R, typename... Args>
typename std::enable_if<not std::is_base_of<BaseHandle, R>::value, std::shared_ptr<R>>::type
resource(Args&&... args) {
return R::create(shared_from_this(), std::forward<Args>(args)...);
}
/**
* @brief Releases all internal loop resources.
*
* Call this function only when the loop has finished executing and all open
* handles and requests have been closed, or the loop will emit an error.
*
* An ErrorEvent will be emitted in case of errors.
*/
void close() {
auto err = uv_loop_close(loop.get());
if(err) { publish(ErrorEvent{err}); }
}
/**
* @brief Runs the event loop.
*
* Available modes are:
*
* * `Loop::Mode::DEFAULT`: Runs the event loop until there are no more
* active and referenced handles or requests.
* * `Loop::Mode::ONCE`: Poll for i/o once. Note that this function blocks
* if there are no pending callbacks.
* * `Loop::Mode::NOWAIT`: Poll for i/o once but dont block if there are no
* pending callbacks.
*
* See the official
* [documentation](http://docs.libuv.org/en/v1.x/loop.html#c.uv_run)
* for further details.
*
* @return True when done, false in all other cases.
*/
template<Mode mode = Mode::DEFAULT>
bool run() noexcept {
auto utm = static_cast<typename std::underlying_type<Mode>::type>(mode);
auto uvrm = static_cast<uv_run_mode>(utm);
return (uv_run(loop.get(), uvrm) == 0);
}
/**
* @brief Checks if there are active resources.
* @return True if there are active resources in the loop.
*/
bool alive() const noexcept {
return !(uv_loop_alive(loop.get()) == 0);
}
/**
* @brief Stops the event loop.
*
* It causes `run()` to end as soon as possible.<br/>
* This will happen not sooner than the next loop iteration.<br/>
* If this function was called before blocking for I/O, the loop wont block
* for I/O on this iteration.
*/
void stop() noexcept {
uv_stop(loop.get());
}
/**
* @brief Get backend file descriptor.
*
* Only kqueue, epoll and event ports are supported.<br/>
* This can be used in conjunction with `run<Loop::Mode::NOWAIT>()` to poll
* in one thread and run the event loops callbacks in another.
*
* @return The backend file descriptor.
*/
int descriptor() const noexcept {
return uv_backend_fd(loop.get());
}
/**
* @brief Gets the poll timeout.
* @return A `std::pair` composed as it follows:
* * A boolean value that is true in case of valid timeout, false otherwise.
* * Milliseconds (`std::chrono::duration<uint64_t, std::milli>`).
*/
std::pair<bool, Time> timeout() const noexcept {
auto to = uv_backend_timeout(loop.get());
return std::make_pair(to == -1, Time{to});
}
/**
* @brief Returns the current timestamp in milliseconds.
*
* The timestamp is cached at the start of the event loop tick.<br/>
* The timestamp increases monotonically from some arbitrary point in
* time.<br/>
* Dont make assumptions about the starting point, you will only get
* disappointed.
*
* @return The current timestamp in milliseconds (actual type is
* `std::chrono::duration<uint64_t, std::milli>`).
*/
Time now() const noexcept {
return Time{uv_now(loop.get())};
}
/**
* @brief Updates the event loops concept of _now_.
*
* The current time is cached at the start of the event loop tick in order
* to reduce the number of time-related system calls.<br/>
* You wont normally need to call this function unless you have callbacks
* that block the event loop for longer periods of time, where _longer_ is
* somewhat subjective but probably on the order of a millisecond or more.
*/
void update() const noexcept {
return uv_update_time(loop.get());
}
/**
* @brief Walks the list of handles.
*
* The callback will be executed once for each handle that is still active.
*
* @param callback A function to be invoked once for each active handle.
*/
void walk(std::function<void(BaseHandle &)> callback) {
// remember: non-capturing lambdas decay to pointers to functions
uv_walk(loop.get(), [](uv_handle_t *handle, void *func) {
BaseHandle &ref = *static_cast<BaseHandle*>(handle->data);
std::function<void(BaseHandle &)> &f =
*static_cast<std::function<void(BaseHandle &)>*>(func);
f(ref);
}, &callback);
}
/**
* @brief Reinitialize any kernel state necessary in the child process after
* a fork(2) system call.
*
* Previously started watchers will continue to be started in the child
* process.
*
* It is necessary to explicitly call this function on every event loop
* created in the parent process that you plan to continue to use in the
* child, including the default loop (even if you dont continue to use it
* in the parent). This function must be called before calling any API
* function using the loop in the child. Failure to do so will result in
* undefined behaviour, possibly including duplicate events delivered to
* both parent and child or aborting the child process.
*
* When possible, it is preferred to create a new loop in the child process
* instead of reusing a loop created in the parent. New loops created in the
* child process after the fork should not use this function.
*
* Note that this function is not implemented on Windows.<br/>
* Note also that this function is experimental in `libuv`. It may contain
* bugs, and is subject to change or removal. API and ABI stability is not
* guaranteed.
*
* An ErrorEvent will be emitted in case of errors.
*
* See the official
* [documentation](http://docs.libuv.org/en/v1.x/loop.html#c.uv_loop_fork)
* for further details.
*/
void fork() noexcept {
auto err = uv_loop_fork(loop.get());
if(err) { publish(ErrorEvent{err}); }
}
/**
* @brief Gets user-defined data. `uvw` won't use this field in any case.
* @return User-defined data if any, an invalid pointer otherwise.
*/
template<typename R = void>
std::shared_ptr<R> data() const {
return std::static_pointer_cast<R>(userData);
}
/**
* @brief Sets arbitrary data. `uvw` won't use this field in any case.
* @param uData User-defined arbitrary data.
*/
void data(std::shared_ptr<void> uData) {
userData = std::move(uData);
}
/**
* @brief Gets the underlying raw data structure.
*
* This function should not be used, unless you know exactly what you are
* doing and what are the risks.<br/>
* Going raw is dangerous, mainly because the lifetime management of a loop,
* a handle or a request is in charge to the library itself and users should
* not work around it.
*
* @warning
* Use this function at your own risk, but do not expect any support in case
* of bugs.
*
* @return The underlying raw data structure.
*/
const uv_loop_t * raw() const noexcept {
return loop.get();
}
/**
* @brief Gets the underlying raw data structure.
*
* This function should not be used, unless you know exactly what you are
* doing and what are the risks.<br/>
* Going raw is dangerous, mainly because the lifetime management of a loop,
* a handle or a request is in charge to the library itself and users should
* not work around it.
*
* @warning
* Use this function at your own risk, but do not expect any support in case
* of bugs.
*
* @return The underlying raw data structure.
*/
uv_loop_t * raw() noexcept {
return const_cast<uv_loop_t *>(const_cast<const Loop *>(this)->raw());
}
private:
std::unique_ptr<uv_loop_t, Deleter> loop;
std::shared_ptr<void> userData{nullptr};
};
}

116
src/uvw/loop.ipp Normal file
View File

@ -0,0 +1,116 @@
#include "config.h"
namespace uvw {
UVW_INLINE loop::loop(std::unique_ptr<uv_loop_t, deleter> ptr) noexcept
: uv_loop{std::move(ptr)} {}
UVW_INLINE std::shared_ptr<loop> loop::create() {
auto ptr = std::unique_ptr<uv_loop_t, deleter>{new uv_loop_t, [](uv_loop_t *l) { delete l; }};
auto curr = std::shared_ptr<loop>{new loop{std::move(ptr)}};
if(uv_loop_init(curr->uv_loop.get())) {
curr = nullptr;
}
return curr;
}
UVW_INLINE std::shared_ptr<loop> loop::create(uv_loop_t *res) {
auto ptr = std::unique_ptr<uv_loop_t, deleter>{res, [](uv_loop_t *) {}};
return std::shared_ptr<loop>{new loop{std::move(ptr)}};
}
UVW_INLINE std::shared_ptr<loop> loop::get_default() {
static std::weak_ptr<loop> ref;
std::shared_ptr<loop> curr;
if(ref.expired()) {
auto def = uv_default_loop();
if(def) {
auto ptr = std::unique_ptr<uv_loop_t, deleter>(def, [](uv_loop_t *) {});
curr = std::shared_ptr<loop>{new loop{std::move(ptr)}};
}
ref = curr;
} else {
curr = ref.lock();
}
return curr;
}
UVW_INLINE loop::~loop() noexcept {
if(uv_loop) {
close();
}
}
UVW_INLINE int loop::close() {
int ret = 0;
if(uv_loop) {
ret = uv_loop_close(uv_loop.get());
uv_loop.reset();
}
return ret;
}
UVW_INLINE int loop::run(run_mode mode) noexcept {
return uv_run(uv_loop.get(), static_cast<uv_run_mode>(mode));
}
UVW_INLINE bool loop::alive() const noexcept {
return !!uv_loop_alive(uv_loop.get());
}
UVW_INLINE void loop::stop() noexcept {
uv_stop(uv_loop.get());
}
UVW_INLINE int loop::descriptor() const noexcept {
return uv_backend_fd(uv_loop.get());
}
UVW_INLINE std::pair<bool, loop::time> loop::timeout() const noexcept {
auto to = uv_backend_timeout(uv_loop.get());
return std::make_pair(to == -1, time{to});
}
UVW_INLINE loop::time loop::idle_time() const noexcept {
return time{uv_metrics_idle_time(uv_loop.get())};
}
UVW_INLINE metrics_type loop::metrics() const noexcept {
metrics_type res{};
uv_metrics_info(uv_loop.get(), &res);
return res;
}
UVW_INLINE loop::time loop::now() const noexcept {
return time{uv_now(uv_loop.get())};
}
UVW_INLINE void loop::update() const noexcept {
return uv_update_time(uv_loop.get());
}
UVW_INLINE int loop::fork() noexcept {
return uv_loop_fork(uv_loop.get());
}
UVW_INLINE void loop::data(std::shared_ptr<void> ud) {
user_data = std::move(ud);
}
UVW_INLINE const uv_loop_t *loop::raw() const noexcept {
return uv_loop.get();
}
UVW_INLINE uv_loop_t *loop::raw() noexcept {
return const_cast<uv_loop_t *>(const_cast<const loop *>(this)->raw());
}
} // namespace uvw

2
src/uvw/pipe.cpp Normal file
View File

@ -0,0 +1,2 @@
#include "pipe.h"
#include "pipe.ipp"

View File

@ -1,117 +1,93 @@
#pragma once
#ifndef UVW_PIPE_INCLUDE_H
#define UVW_PIPE_INCLUDE_H
#include <type_traits>
#include <utility>
#include <memory>
#include <string>
#include <type_traits>
#include <uv.h>
#include "config.h"
#include "enum.hpp"
#include "loop.h"
#include "request.hpp"
#include "stream.hpp"
#include "util.hpp"
#include "loop.hpp"
#include "stream.h"
#include "util.h"
namespace uvw {
namespace details {
enum class UVChmodFlags: typename std::underlying_type<uv_poll_event>::type {
enum class uvw_chmod_flags : std::underlying_type_t<uv_poll_event> {
READABLE = UV_READABLE,
WRITABLE = UV_WRITABLE
WRITABLE = UV_WRITABLE,
UVW_ENUM = 0
};
}
/**
* @brief The PipeHandle handle.
* @brief The pipe handle.
*
* Pipe handles provide an abstraction over local domain sockets on Unix and
* named pipes on Windows.
*
* To create a `PipeHandle` through a `Loop`, arguments follow:
* To create a `pipe_handle` through a `loop`, arguments follow:
*
* * An optional boolean value that indicates if this pipe will be used for
* handle passing between processes.
*/
class PipeHandle final: public StreamHandle<PipeHandle, uv_pipe_t> {
class pipe_handle final: public stream_handle<pipe_handle, uv_pipe_t> {
public:
using Chmod = details::UVChmodFlags;
using chmod_flags = details::uvw_chmod_flags;
explicit PipeHandle(ConstructorAccess ca, std::shared_ptr<Loop> ref, bool pass = false)
: StreamHandle{ca, std::move(ref)}, ipc{pass}
{}
explicit pipe_handle(loop::token token, std::shared_ptr<loop> ref, bool pass = false);
/**
* @brief Initializes the handle.
* @return True in case of success, false otherwise.
* @return Underlying return value.
*/
bool init() {
return initialize(&uv_pipe_init, ipc);
}
int init();
/**
* @brief Opens an existing file descriptor or HANDLE as a pipe.
*
* The passed file descriptor or HANDLE is not checked for its type, but
* its required that it represents a valid pipe.<br/>
* An ErrorEvent event is emitted in case of errors.
* its required that it represents a valid pipe.
*
* @param file A valid file handle (either a file descriptor or a HANDLE).
* @return Underlying return value.
*/
void open(FileHandle file) {
invoke(&uv_pipe_open, get(), file);
}
int open(file_handle file);
/**
* @brief bind Binds the pipe to a file path (Unix) or a name (Windows).
*
* Paths on Unix get truncated typically between 92 and 108 bytes.<br/>
* An ErrorEvent event is emitted in case of errors.
* Paths on Unix get truncated typically between 92 and 108 bytes.
*
* @param name A valid file path.
* @param no_truncate Force an error rather than allow truncating a path.
* @return Underlying return value.
*/
void bind(std::string name) {
invoke(&uv_pipe_bind, get(), name.data());
}
int bind(const std::string &name, const bool no_truncate = false);
/**
* @brief Connects to the Unix domain socket or the named pipe.
*
* Paths on Unix get truncated typically between 92 and 108 bytes.<br/>
* A ConnectEvent event is emitted when the connection has been
* established.<br/>
* An ErrorEvent event is emitted in case of errors during the connection.
* A connect event is emitted when the connection has been
* established.
*
* @param name A valid domain socket or named pipe.
* @param no_truncate Force an error rather than allow truncating a path.
* @return Underlying return value.
*/
void connect(std::string name) {
auto ptr = shared_from_this();
auto errorEventListener = [ptr](const ErrorEvent &event, const details::ConnectReq &) {
ptr->publish(event);
};
auto connectEventListener = [ptr](const ConnectEvent &event, const details::ConnectReq &) {
ptr->publish(event);
};
auto connect = loop().resource<details::ConnectReq>();
connect->once<ErrorEvent>(errorEventListener);
connect->once<ConnectEvent>(connectEventListener);
connect->connect(&uv_pipe_connect, get(), name.data());
}
int connect(const std::string &name, const bool no_truncate = false);
/**
* @brief Gets the name of the Unix domain socket or the named pipe.
* @return The name of the Unix domain socket or the named pipe, an empty
* string in case of errors.
*/
std::string sock() const noexcept {
return details::tryRead(&uv_pipe_getsockname, get());
}
std::string sock() const noexcept;
/**
* @brief Gets the name of the Unix domain socket or the named pipe to which
@ -119,9 +95,7 @@ public:
* @return The name of the Unix domain socket or the named pipe to which
* the handle is connected, an empty string in case of errors.
*/
std::string peer() const noexcept {
return details::tryRead(&uv_pipe_getpeername, get());
}
std::string peer() const noexcept;
/**
* @brief Sets the number of pending pipe this instance can handle.
@ -132,17 +106,13 @@ public:
*
* @param count The number of accepted pending pipe.
*/
void pending(int count) noexcept {
uv_pipe_pending_instances(get(), count);
}
void pending(int count) noexcept;
/**
* @brief Gets the number of pending pipe this instance can handle.
* @return The number of pending pipe this instance can handle.
*/
int pending() noexcept {
return uv_pipe_pending_count(get());
}
int pending() noexcept;
/**
* @brief Used to receive handles over IPC pipes.
@ -155,15 +125,12 @@ public:
*
* @return The type of the pending handle. Possible values are:
*
* * `HandleType::PIPE`
* * `HandleType::TCP`
* * `HandleType::UDP`
* * `HandleType::UNKNOWN`
* * `handle_type::PIPE`
* * `handle_type::TCP`
* * `handle_type::UDP`
* * `handle_type::UNKNOWN`
*/
HandleType receive() noexcept {
HandleCategory category = uv_pipe_pending_type(get());
return Utilities::guessHandle(category);
}
handle_type receive() noexcept;
/**
* @brief Alters pipe permissions.
@ -172,23 +139,26 @@ public:
*
* Available flags are:
*
* * `PipeHandle::Chmod::READABLE`
* * `PipeHandle::Chmod::WRITABLE`
* * `pipe_handle::chmod_flags::READABLE`
* * `pipe_handle::chmod_flags::WRITABLE`
*
* See the official
* [documentation](http://docs.libuv.org/en/v1.x/pipe.html#c.uv_pipe_chmod)
* for further details.
*
* @param flags A valid set of flags.
* @return True in case of success, false otherwise.
* @return Underlying return value.
*/
bool chmod(Flags<Chmod> flags) noexcept {
return (0 == uv_pipe_chmod(get(), flags));
}
int chmod(chmod_flags flags) noexcept;
private:
bool ipc;
};
} // namespace uvw
}
#ifndef UVW_AS_LIB
# include "pipe.ipp"
#endif
#endif // UVW_PIPE_INCLUDE_H

59
src/uvw/pipe.ipp Normal file
View File

@ -0,0 +1,59 @@
#include <utility>
#include "config.h"
namespace uvw {
UVW_INLINE pipe_handle::pipe_handle(loop::token token, std::shared_ptr<loop> ref, bool pass)
: stream_handle{token, std::move(ref)}, ipc{pass} {}
UVW_INLINE int pipe_handle::init() {
return leak_if(uv_pipe_init(parent().raw(), raw(), ipc));
}
UVW_INLINE int pipe_handle::open(file_handle file) {
return uv_pipe_open(raw(), file);
}
UVW_INLINE int pipe_handle::bind(const std::string &name, const bool no_truncate) {
return uv_pipe_bind2(raw(), name.data(), name.size(), no_truncate * UV_PIPE_NO_TRUNCATE);
}
UVW_INLINE int pipe_handle::connect(const std::string &name, const bool no_truncate) {
auto listener = [ptr = shared_from_this()](const auto &event, const auto &) {
ptr->publish(event);
};
auto connect = parent().resource<details::connect_req>();
connect->on<error_event>(listener);
connect->on<connect_event>(listener);
unsigned int flags = no_truncate * UV_PIPE_NO_TRUNCATE;
return connect->connect(&uv_pipe_connect2, raw(), name.data(), name.size(), flags);
}
UVW_INLINE std::string pipe_handle::sock() const noexcept {
return details::try_read(&uv_pipe_getsockname, raw());
}
UVW_INLINE std::string pipe_handle::peer() const noexcept {
return details::try_read(&uv_pipe_getpeername, raw());
}
UVW_INLINE void pipe_handle::pending(int count) noexcept {
uv_pipe_pending_instances(raw(), count);
}
UVW_INLINE int pipe_handle::pending() noexcept {
return uv_pipe_pending_count(raw());
}
UVW_INLINE handle_type pipe_handle::receive() noexcept {
handle_category category = uv_pipe_pending_type(raw());
return utilities::guess_handle(category);
}
UVW_INLINE int pipe_handle::chmod(chmod_flags flags) noexcept {
return uv_pipe_chmod(raw(), static_cast<uv_poll_event>(flags));
}
} // namespace uvw

2
src/uvw/poll.cpp Normal file
View File

@ -0,0 +1,2 @@
#include "poll.h"
#include "poll.ipp"

119
src/uvw/poll.h Normal file
View File

@ -0,0 +1,119 @@
#ifndef UVW_POLL_INCLUDE_H
#define UVW_POLL_INCLUDE_H
#include <memory>
#include <type_traits>
#include <uv.h>
#include "config.h"
#include "enum.hpp"
#include "handle.hpp"
#include "util.h"
namespace uvw {
namespace details {
enum class uvw_poll_event : std::underlying_type_t<uv_poll_event> {
READABLE = UV_READABLE,
WRITABLE = UV_WRITABLE,
DISCONNECT = UV_DISCONNECT,
PRIORITIZED = UV_PRIORITIZED,
UVW_ENUM = 0
};
}
/*! @brief Poll event. */
struct poll_event {
explicit poll_event(details::uvw_poll_event events) noexcept;
/**
* @brief Detected events all in one.
*
* Available flags are:
*
* * `poll_handle::event::READABLE`
* * `poll_handle::event::WRITABLE`
* * `poll_handle::event::DISCONNECT`
* * `poll_handle::event::PRIORITIZED`
*/
details::uvw_poll_event flags;
};
/**
* @brief The poll handle.
*
* Poll handles are used to watch file descriptors for readability, writability
* and disconnection.
*
* To create a `poll_handle` through a `loop`, arguments follow:
*
* * A descriptor that can be:
* * either an `int` file descriptor
* * or a `os_socket_handle` socket descriptor
*
* See the official
* [documentation](http://docs.libuv.org/en/v1.x/poll.html)
* for further details.
*/
class poll_handle final: public handle<poll_handle, uv_poll_t, poll_event> {
static void start_callback(uv_poll_t *hndl, int status, int events);
public:
using poll_event_flags = details::uvw_poll_event;
explicit poll_handle(loop::token token, std::shared_ptr<loop> ref, int desc);
explicit poll_handle(loop::token token, std::shared_ptr<loop> ref, os_socket_handle sock);
/**
* @brief Initializes the handle.
* @return Underlying return value.
*/
int init();
/**
* @brief Starts polling the file descriptor.
*
* Available flags are:
*
* * `poll_handle::event::READABLE`
* * `poll_handle::event::WRITABLE`
* * `poll_handle::event::DISCONNECT`
* * `poll_handle::event::PRIORITIZED`
*
* As soon as an event is detected, a poll event is emitted by the
* handle.
*
* Calling more than once this method will update the flags to which the
* caller is interested.
*
* @param flags The events to which the caller is interested.
* @return Underlying return value.
*/
int start(poll_event_flags flags);
/**
* @brief Stops polling the file descriptor.
* @return Underlying return value.
*/
int stop();
private:
enum {
FD,
SOCKET
} tag;
union {
int file_desc;
os_socket_handle socket;
};
};
} // namespace uvw
#ifndef UVW_AS_LIB
# include "poll.ipp"
#endif
#endif // UVW_POLL_INCLUDE_H

View File

@ -1,159 +0,0 @@
#pragma once
#include <type_traits>
#include <utility>
#include <memory>
#include <uv.h>
#include "handle.hpp"
#include "util.hpp"
namespace uvw {
namespace details {
enum class UVPollEvent: typename std::underlying_type<uv_poll_event>::type {
READABLE = UV_READABLE,
WRITABLE = UV_WRITABLE,
DISCONNECT = UV_DISCONNECT,
PRIORITIZED = UV_PRIORITIZED
};
}
/**
* @brief PollEvent event.
*
* It will be emitted by PollHandle according with its functionalities.
*/
struct PollEvent {
explicit PollEvent(Flags<details::UVPollEvent> events) noexcept
: flags{std::move(events)}
{}
/**
* @brief Detected events all in one.
*
* Available flags are:
*
* * `PollHandle::Event::READABLE`
* * `PollHandle::Event::WRITABLE`
* * `PollHandle::Event::DISCONNECT`
* * `PollHandle::Event::PRIORITIZED`
*/
Flags<details::UVPollEvent> flags;
};
/**
* @brief The PollHandle handle.
*
* Poll handles are used to watch file descriptors for readability, writability
* and disconnection.
*
* To create a `PollHandle` through a `Loop`, arguments follow:
*
* * A descriptor that can be:
* * either an `int` file descriptor
* * or a `OSSocketHandle` socket descriptor
*
* See the official
* [documentation](http://docs.libuv.org/en/v1.x/poll.html)
* for further details.
*/
class PollHandle final: public Handle<PollHandle, uv_poll_t> {
static void startCallback(uv_poll_t *handle, int status, int events) {
PollHandle &poll = *(static_cast<PollHandle*>(handle->data));
if(status) { poll.publish(ErrorEvent{status}); }
else { poll.publish(PollEvent{static_cast<typename std::underlying_type<Event>::type>(events)}); }
}
public:
using Event = details::UVPollEvent;
explicit PollHandle(ConstructorAccess ca, std::shared_ptr<Loop> ref, int desc)
: Handle{ca, std::move(ref)}, tag{FD}, fd{desc}
{}
explicit PollHandle(ConstructorAccess ca, std::shared_ptr<Loop> ref, OSSocketHandle sock)
: Handle{ca, std::move(ref)}, tag{SOCKET}, socket{sock}
{}
/**
* @brief Initializes the handle.
* @return True in case of success, false otherwise.
*/
bool init() {
return (tag == SOCKET)
? initialize(&uv_poll_init_socket, socket)
: initialize(&uv_poll_init, fd);
}
/**
* @brief Starts polling the file descriptor.
*
* Available flags are:
*
* * `PollHandle::Event::READABLE`
* * `PollHandle::Event::WRITABLE`
* * `PollHandle::Event::DISCONNECT`
* * `PollHandle::Event::PRIORITIZED`
*
* As soon as an event is detected, a PollEvent is emitted by the
* handle.<br>
* It could happen that ErrorEvent events are emitted while running.
*
* Calling more than once this method will update the flags to which the
* caller is interested.
*
* @param flags The events to which the caller is interested.
*/
void start(Flags<Event> flags) {
invoke(&uv_poll_start, get(), flags, &startCallback);
}
/**
* @brief Starts polling the file descriptor.
*
* Available flags are:
*
* * `PollHandle::Event::READABLE`
* * `PollHandle::Event::WRITABLE`
* * `PollHandle::Event::DISCONNECT`
* * `PollHandle::Event::PRIORITIZED`
*
* As soon as an event is detected, a PollEvent is emitted by the
* handle.<br>
* It could happen that ErrorEvent events are emitted while running.
*
* Calling more than once this method will update the flags to which the
* caller is interested.
*
* @param event The event to which the caller is interested.
*/
void start(Event event) {
start(Flags<Event>{event});
}
/**
* @brief Stops polling the file descriptor.
*/
void stop() {
invoke(&uv_poll_stop, get());
}
private:
enum { FD, SOCKET } tag;
union {
int fd;
OSSocketHandle::Type socket;
};
};
}

40
src/uvw/poll.ipp Normal file
View File

@ -0,0 +1,40 @@
#include <utility>
#include "config.h"
namespace uvw {
UVW_INLINE poll_event::poll_event(details::uvw_poll_event events) noexcept
: flags{events} {}
UVW_INLINE poll_handle::poll_handle(loop::token token, std::shared_ptr<loop> ref, int desc)
: handle{token, std::move(ref)}, tag{FD}, file_desc{desc} {}
UVW_INLINE poll_handle::poll_handle(loop::token token, std::shared_ptr<loop> ref, os_socket_handle sock)
: handle{token, std::move(ref)}, tag{SOCKET}, socket{sock} {}
// NOLINTNEXTLINE(bugprone-easily-swappable-parameters)
UVW_INLINE void poll_handle::start_callback(uv_poll_t *hndl, int status, int events) {
if(poll_handle &poll = *(static_cast<poll_handle *>(hndl->data)); status) {
poll.publish(error_event{status});
} else {
poll.publish(poll_event{poll_event_flags(events)});
}
}
UVW_INLINE int poll_handle::init() {
if(tag == SOCKET) {
return leak_if(uv_poll_init_socket(parent().raw(), raw(), socket));
} else {
return leak_if(uv_poll_init(parent().raw(), raw(), file_desc));
}
}
UVW_INLINE int poll_handle::start(poll_event_flags flags) {
return uv_poll_start(raw(), static_cast<uv_poll_event>(flags), &start_callback);
}
UVW_INLINE int poll_handle::stop() {
return uv_poll_stop(raw());
}
} // namespace uvw

2
src/uvw/prepare.cpp Normal file
View File

@ -0,0 +1,2 @@
#include "prepare.h"
#include "prepare.ipp"

58
src/uvw/prepare.h Normal file
View File

@ -0,0 +1,58 @@
#ifndef UVW_PREPARE_INCLUDE_H
#define UVW_PREPARE_INCLUDE_H
#include <uv.h>
#include "handle.hpp"
#include "loop.h"
namespace uvw {
/*! @brief Prepare event. */
struct prepare_event {};
/**
* @brief The prepare handle.
*
* Prepare handles will emit a prepare event once per loop iteration, right
* before polling for I/O.
*
* To create a `prepare_handle` through a `loop`, no arguments are required.
*/
class prepare_handle final: public handle<prepare_handle, uv_prepare_t, prepare_event> {
static void start_callback(uv_prepare_t *hndl);
public:
using handle::handle;
/**
* @brief Initializes the handle.
* @return Underlying return value.
*/
int init();
/**
* @brief Starts the handle.
*
* A prepare event will be emitted once per loop iteration, right before
* polling for I/O.
*
* The handle will start emitting prepare events when needed.
*
* @return Underlying return value.
*/
int start();
/**
* @brief Stops the handle.
* @return Underlying return value.
*/
int stop();
};
} // namespace uvw
#ifndef UVW_AS_LIB
# include "prepare.ipp"
#endif
#endif // UVW_PREPARE_INCLUDE_H

View File

@ -1,68 +0,0 @@
#pragma once
#include <utility>
#include <memory>
#include <uv.h>
#include "handle.hpp"
#include "loop.hpp"
namespace uvw {
/**
* @brief PrepareEvent event.
*
* It will be emitted by PrepareHandle according with its functionalities.
*/
struct PrepareEvent {};
/**
* @brief The PrepareHandle handle.
*
* Prepare handles will emit a PrepareEvent event once per loop iteration, right
* before polling for I/O.
*
* To create a `PrepareHandle` through a `Loop`, no arguments are required.
*/
class PrepareHandle final: public Handle<PrepareHandle, uv_prepare_t> {
static void startCallback(uv_prepare_t *handle) {
PrepareHandle &prepare = *(static_cast<PrepareHandle*>(handle->data));
prepare.publish(PrepareEvent{});
}
public:
using Handle::Handle;
/**
* @brief Initializes the handle.
* @return True in case of success, false otherwise.
*/
bool init() {
return initialize(&uv_prepare_init);
}
/**
* @brief Starts the handle.
*
* A PrepareEvent event will be emitted once per loop iteration, right
* before polling for I/O.
*
* The handle will start emitting PrepareEvent when needed.
*/
void start() {
invoke(&uv_prepare_start, get(), &startCallback);
}
/**
* @brief Stops the handle.
*/
void stop() {
invoke(&uv_prepare_stop, get());
}
};
}

22
src/uvw/prepare.ipp Normal file
View File

@ -0,0 +1,22 @@
#include "config.h"
namespace uvw {
UVW_INLINE void prepare_handle::start_callback(uv_prepare_t *hndl) {
prepare_handle &prepare = *(static_cast<prepare_handle *>(hndl->data));
prepare.publish(prepare_event{});
}
UVW_INLINE int prepare_handle::init() {
return leak_if(uv_prepare_init(parent().raw(), raw()));
}
UVW_INLINE int prepare_handle::start() {
return uv_prepare_start(raw(), &start_callback);
}
UVW_INLINE int prepare_handle::stop() {
return uv_prepare_stop(raw());
}
} // namespace uvw

2
src/uvw/process.cpp Normal file
View File

@ -0,0 +1,2 @@
#include "process.h"
#include "process.ipp"

245
src/uvw/process.h Normal file
View File

@ -0,0 +1,245 @@
#ifndef UVW_PROCESS_INCLUDE_H
#define UVW_PROCESS_INCLUDE_H
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include <uv.h>
#include "config.h"
#include "enum.hpp"
#include "handle.hpp"
#include "loop.h"
#include "stream.h"
#include "util.h"
namespace uvw {
namespace details {
enum class uvw_process_flags : std::underlying_type_t<uv_process_flags> {
SETUID = UV_PROCESS_SETUID,
SETGID = UV_PROCESS_SETGID,
WINDOWS_VERBATIM_ARGUMENTS = UV_PROCESS_WINDOWS_VERBATIM_ARGUMENTS,
DETACHED = UV_PROCESS_DETACHED,
WINDOWS_HIDE = UV_PROCESS_WINDOWS_HIDE,
WINDOWS_HIDE_CONSOLE = UV_PROCESS_WINDOWS_HIDE_CONSOLE,
WINDOWS_HIDE_GUI = UV_PROCESS_WINDOWS_HIDE_GUI,
WINDOWS_FILE_PATH_EXACT_NAME = UV_PROCESS_WINDOWS_FILE_PATH_EXACT_NAME,
UVW_ENUM = 0
};
enum class uvw_stdio_flags : std::underlying_type_t<uv_stdio_flags> {
IGNORE_STREAM = UV_IGNORE,
CREATE_PIPE = UV_CREATE_PIPE,
INHERIT_FD = UV_INHERIT_FD,
INHERIT_STREAM = UV_INHERIT_STREAM,
READABLE_PIPE = UV_READABLE_PIPE,
WRITABLE_PIPE = UV_WRITABLE_PIPE,
OVERLAPPED_PIPE = UV_OVERLAPPED_PIPE,
UVW_ENUM = 0
};
} // namespace details
/*! @brief Exit event. */
struct exit_event {
explicit exit_event(int64_t code, int sig) noexcept;
int64_t status; /*!< The exit status. */
int signal; /*!< The signal that caused the process to terminate, if any. */
};
/**
* @brief The process handle.
*
* Process handles will spawn a new process and allow the user to control it and
* establish communication channels with it using streams.
*/
class process_handle final: public handle<process_handle, uv_process_t, exit_event> {
static void exit_callback(uv_process_t *hndl, int64_t exit_status, int term_signal);
public:
using process_flags = details::uvw_process_flags;
using stdio_flags = details::uvw_stdio_flags;
process_handle(loop::token token, std::shared_ptr<loop> ref);
/**
* @brief Disables inheritance for file descriptors/handles.
*
* Disables inheritance for file descriptors/handles that this process
* inherited from its parent. The effect is that child processes spawned by
* this process dont accidentally inherit these handles.<br/>
* It is recommended to call this function as early in your program as
* possible, before the inherited file descriptors can be closed or
* duplicated.
*
* See the official
* [documentation](http://docs.libuv.org/en/v1.x/process.html#c.uv_disable_stdio_inheritance)
* for further details.
*/
static void disable_stdio_inheritance() noexcept;
/**
* @brief kill Sends the specified signal to the given PID.
* @param pid A valid process id.
* @param signum A valid signal identifier.
* @return True in case of success, false otherwise.
*/
static bool kill(int pid, int signum) noexcept;
/**
* @brief Initializes the handle.
* @return Underlying return value.
*/
int init();
/**
* @brief spawn Starts the process.
*
* See the official
* [documentation](http://docs.libuv.org/en/v1.x/process.html)
* for further details.
*
* @param file Path pointing to the program to be executed.
* @param args Command line arguments.
* @param env Optional environment for the new process.
* @return Underlying return value.
*/
int spawn(const char *file, char **args, char **env = nullptr);
/**
* @brief Sends the specified signal to the internal process handle.
* @param signum A valid signal identifier.
* @return Underlying return value.
*/
int kill(int signum);
/**
* @brief Gets the PID of the spawned process.
*
* Its set after calling `spawn()`.
*
* @return The PID of the spawned process.
*/
int pid() noexcept;
/**
* @brief Sets the current working directory for the subprocess.
* @param path The working directory to be used when `spawn()` is invoked.
* @return A reference to this process handle.
*/
process_handle &cwd(const std::string &path) noexcept;
/**
* @brief Sets flags that control how `spawn()` behaves.
*
* Available flags are:
*
* * `process_handle::process_flags::SETUID`
* * `process_handle::process_flags::SETGID`
* * `process_handle::process_flags::WINDOWS_VERBATIM_ARGUMENTS`
* * `process_handle::process_flags::DETACHED`
* * `process_handle::process_flags::WINDOWS_HIDE`
* * `process_handle::process_flags::WINDOWS_HIDE_CONSOLE`
* * `process_handle::process_flags::WINDOWS_HIDE_GUI`
* * `process_handle::process_flags::WINDOWS_FILE_PATH_EXACT_NAME`
*
* See the official
* [documentation](http://docs.libuv.org/en/v1.x/process.html#c.uv_process_flags)
* for further details.
*
* @param flags A valid set of flags.
* @return A reference to this process handle.
*/
process_handle &flags(process_flags flags) noexcept;
/**
* @brief Makes a `stdio` handle available to the child process.
*
* Available flags are:
*
* * `process_handle::stdio_flags::IGNORE_STREAM`
* * `process_handle::stdio_flags::CREATE_PIPE`
* * `process_handle::stdio_flags::INHERIT_FD`
* * `process_handle::stdio_flags::INHERIT_STREAM`
* * `process_handle::stdio_flags::READABLE_PIPE`
* * `process_handle::stdio_flags::WRITABLE_PIPE`
* * `process_handle::stdio_flags::OVERLAPPED_PIPE`
*
* See the official
* [documentation](http://docs.libuv.org/en/v1.x/process.html#c.uv_stdio_flags)
* for further details.
*
* @param stream A valid `stdio` handle.
* @param flags A valid set of flags.
* @return A reference to this process handle.
*/
template<typename T, typename U, typename... E>
process_handle &stdio(stream_handle<T, U, E...> &stream, stdio_flags flags) {
uv_stdio_container_t container;
container.flags = static_cast<uv_stdio_flags>(flags);
container.data.stream = reinterpret_cast<uv_stream_t *>(stream.raw());
po_stream_stdio.push_back(container);
return *this;
}
/**
* @brief Makes a file descriptor available to the child process.
*
* Available flags are:
*
* * `process_handle::stdio_flags::IGNORE_STREAM`
* * `process_handle::stdio_flags::CREATE_PIPE`
* * `process_handle::stdio_flags::INHERIT_FD`
* * `process_handle::stdio_flags::INHERIT_STREAM`
* * `process_handle::stdio_flags::READABLE_PIPE`
* * `process_handle::stdio_flags::WRITABLE_PIPE`
* * `process_handle::stdio_flags::OVERLAPPED_PIPE`
*
* Default file descriptors are:
* * `uvw::std_in` for `stdin`
* * `uvw::std_out` for `stdout`
* * `uvw::std_err` for `stderr`
*
* See the official
* [documentation](http://docs.libuv.org/en/v1.x/process.html#c.uv_stdio_flags)
* for further details.
*
* @param fd A valid file descriptor.
* @param flags A valid set of flags.
* @return A reference to this process handle.
*/
process_handle &stdio(file_handle fd, stdio_flags flags);
/**
* @brief Sets the child process' user id.
* @param id A valid user id to be used.
* @return A reference to this process handle.
*/
process_handle &uid(uid_type id);
/**
* @brief Sets the child process' group id.
* @param id A valid group id to be used.
* @return A reference to this process handle.
*/
process_handle &gid(gid_type id);
private:
std::string po_cwd;
process_flags po_flags;
std::vector<uv_stdio_container_t> po_fd_stdio;
std::vector<uv_stdio_container_t> po_stream_stdio;
uid_type po_uid;
gid_type po_gid;
};
} // namespace uvw
#ifndef UVW_AS_LIB
# include "process.ipp"
#endif
#endif // UVW_PROCESS_INCLUDE_H

View File

@ -1,319 +0,0 @@
#pragma once
#include <algorithm>
#include <utility>
#include <memory>
#include <string>
#include <vector>
#include <uv.h>
#include "handle.hpp"
#include "stream.hpp"
#include "util.hpp"
#include "loop.hpp"
namespace uvw {
namespace details {
enum class UVProcessFlags: typename std::underlying_type<uv_process_flags>::type {
SETUID = UV_PROCESS_SETUID,
SETGID = UV_PROCESS_SETGID,
WINDOWS_VERBATIM_ARGUMENTS = UV_PROCESS_WINDOWS_VERBATIM_ARGUMENTS,
DETACHED = UV_PROCESS_DETACHED,
WINDOWS_HIDE = UV_PROCESS_WINDOWS_HIDE
};
enum class UVStdIOFlags: typename std::underlying_type<uv_stdio_flags>::type {
IGNORE_STREAM = UV_IGNORE,
CREATE_PIPE = UV_CREATE_PIPE,
INHERIT_FD = UV_INHERIT_FD,
INHERIT_STREAM = UV_INHERIT_STREAM,
READABLE_PIPE = UV_READABLE_PIPE,
WRITABLE_PIPE = UV_WRITABLE_PIPE,
OVERLAPPED_PIPE = UV_OVERLAPPED_PIPE
};
}
/**
* @brief ExitEvent event.
*
* It will be emitted by ProcessHandle according with its functionalities.
*/
struct ExitEvent {
explicit ExitEvent(int64_t code, int sig) noexcept
: status{code}, signal{sig}
{}
int64_t status; /*!< The exit status. */
int signal; /*!< The signal that caused the process to terminate, if any. */
};
/**
* @brief The ProcessHandle handle.
*
* Process handles will spawn a new process and allow the user to control it and
* establish communication channels with it using streams.
*/
class ProcessHandle final: public Handle<ProcessHandle, uv_process_t> {
static void exitCallback(uv_process_t *handle, int64_t exitStatus, int termSignal) {
ProcessHandle &process = *(static_cast<ProcessHandle*>(handle->data));
process.publish(ExitEvent{exitStatus, termSignal});
}
public:
using Process = details::UVProcessFlags;
using StdIO = details::UVStdIOFlags;
ProcessHandle(ConstructorAccess ca, std::shared_ptr<Loop> ref)
: Handle{ca, std::move(ref)}
{}
/**
* @brief Disables inheritance for file descriptors/handles.
*
* Disables inheritance for file descriptors/handles that this process
* inherited from its parent. The effect is that child processes spawned by
* this process dont accidentally inherit these handles.<br/>
* It is recommended to call this function as early in your program as
* possible, before the inherited file descriptors can be closed or
* duplicated.
*
* See the official
* [documentation](http://docs.libuv.org/en/v1.x/process.html#c.uv_disable_stdio_inheritance)
* for further details.
*/
static void disableStdIOInheritance() noexcept {
uv_disable_stdio_inheritance();
}
/**
* @brief kill Sends the specified signal to the given PID.
* @param pid A valid process id.
* @param signum A valid signal identifier.
* @return True in case of success, false otherwise.
*/
static bool kill(int pid, int signum) noexcept {
return (0 == uv_kill(pid, signum));
}
/**
* @brief Initializes the handle.
* @return True in case of success, false otherwise.
*/
bool init() {
// deferred initialization: libuv initializes process handles only when
// uv_spawn is invoked and uvw stays true to the underlying library
return true;
}
/**
* @brief spawn Starts the process.
*
* If the process isn't successfully spawned, an ErrorEvent event will be
* emitted by the handle.
*
* See the official
* [documentation](http://docs.libuv.org/en/v1.x/process.html)
* for further details.
*
* @param file Path pointing to the program to be executed.
* @param args Command line arguments.
* @param env Optional environment for the new process.
*/
void spawn(const char *file, char **args, char **env = nullptr) {
uv_process_options_t po;
po.exit_cb = &exitCallback;
po.file = file;
po.args = args;
po.env = env;
po.cwd = poCwd.empty() ? nullptr : poCwd.data();
po.flags = poFlags;
po.uid = poUid;
po.gid = poGid;
std::vector<uv_stdio_container_t> poStdio;
poStdio.reserve(poFdStdio.size() + poStreamStdio.size());
poStdio.insert(poStdio.begin(), poFdStdio.cbegin(), poFdStdio.cend());
poStdio.insert(poStdio.end(), poStreamStdio.cbegin(), poStreamStdio.cend());
po.stdio_count = static_cast<decltype(po.stdio_count)>(poStdio.size());
po.stdio = poStdio.data();
// fake initialization so as to have leak invoked
// see init member function for more details
initialize([](uv_loop_t*, uv_process_t*){ return 0; });
invoke(&uv_spawn, parent(), get(), &po);
}
/**
* @brief Sends the specified signal to the internal process handle.
* @param signum A valid signal identifier.
*/
void kill(int signum) {
invoke(&uv_process_kill, get(), signum);
}
/**
* @brief Gets the PID of the spawned process.
*
* Its set after calling `spawn()`.
*
* @return The PID of the spawned process.
*/
int pid() noexcept {
return get()->pid;
}
/**
* @brief Sets the current working directory for the subprocess.
* @param path The working directory to be used when `spawn()` is invoked.
* @return A reference to this process handle.
*/
ProcessHandle & cwd(std::string path) noexcept {
poCwd = path;
return *this;
}
/**
* @brief Sets flags that control how `spawn()` behaves.
*
* Available flags are:
*
* * `ProcessHandle::Process::SETUID`
* * `ProcessHandle::Process::SETGID`
* * `ProcessHandle::Process::WINDOWS_VERBATIM_ARGUMENTS`
* * `ProcessHandle::Process::DETACHED`
* * `ProcessHandle::Process::WINDOWS_HIDE`
*
* See the official
* [documentation](http://docs.libuv.org/en/v1.x/process.html#c.uv_process_flags)
* for further details.
*
* @param flags A valid set of flags.
* @return A reference to this process handle.
*/
ProcessHandle & flags(Flags<Process> flags) noexcept {
poFlags = flags;
return *this;
}
/**
* @brief Makes a `stdio` handle available to the child process.
*
* Available flags are:
*
* * `ProcessHandle::StdIO::IGNORE_STREAM`
* * `ProcessHandle::StdIO::CREATE_PIPE`
* * `ProcessHandle::StdIO::INHERIT_FD`
* * `ProcessHandle::StdIO::INHERIT_STREAM`
* * `ProcessHandle::StdIO::READABLE_PIPE`
* * `ProcessHandle::StdIO::WRITABLE_PIPE`
* * `ProcessHandle::StdIO::OVERLAPPED_PIPE`
*
* See the official
* [documentation](http://docs.libuv.org/en/v1.x/process.html#c.uv_stdio_flags)
* for further details.
*
* @param stream A valid `stdio` handle.
* @param flags A valid set of flags.
* @return A reference to this process handle.
*/
template<typename T, typename U>
ProcessHandle & stdio(StreamHandle<T, U> &stream, Flags<StdIO> flags) {
uv_stdio_container_t container;
Flags<StdIO>::Type fgs = flags;
container.flags = static_cast<uv_stdio_flags>(fgs);
container.data.stream = get<uv_stream_t>(stream);
poStreamStdio.push_back(std::move(container));
return *this;
}
/**
* @brief Makes a file descriptor available to the child process.
*
* Available flags are:
*
* * `ProcessHandle::StdIO::IGNORE_STREAM`
* * `ProcessHandle::StdIO::CREATE_PIPE`
* * `ProcessHandle::StdIO::INHERIT_FD`
* * `ProcessHandle::StdIO::INHERIT_STREAM`
* * `ProcessHandle::StdIO::READABLE_PIPE`
* * `ProcessHandle::StdIO::WRITABLE_PIPE`
* * `ProcessHandle::StdIO::OVERLAPPED_PIPE`
*
* Default file descriptors are:
* * `uvw::StdIN` for `stdin`
* * `uvw::StdOUT` for `stdout`
* * `uvw::StdERR` for `stderr`
*
* See the official
* [documentation](http://docs.libuv.org/en/v1.x/process.html#c.uv_stdio_flags)
* for further details.
*
* @param fd A valid file descriptor.
* @param flags A valid set of flags.
* @return A reference to this process handle.
*/
ProcessHandle & stdio(FileHandle fd, Flags<StdIO> flags) {
auto fgs = static_cast<uv_stdio_flags>(Flags<StdIO>::Type{flags});
auto actual = FileHandle::Type{fd};
auto it = std::find_if(poFdStdio.begin(), poFdStdio.end(), [actual](const uv_stdio_container_t &container){
return container.data.fd == actual;
});
if(it == poFdStdio.cend()) {
uv_stdio_container_t container;
container.flags = fgs;
container.data.fd = actual;
poFdStdio.push_back(std::move(container));
} else {
it->flags = fgs;
it->data.fd = actual;
}
return *this;
}
/**
* @brief Sets the child process' user id.
* @param id A valid user id to be used.
* @return A reference to this process handle.
*/
ProcessHandle & uid(Uid id) {
poUid = id;
return *this;
}
/**
* @brief Sets the child process' group id.
* @param id A valid group id to be used.
* @return A reference to this process handle.
*/
ProcessHandle & gid(Gid id) {
poGid = id;
return *this;
}
private:
std::string poCwd;
Flags<Process> poFlags;
std::vector<uv_stdio_container_t> poFdStdio;
std::vector<uv_stdio_container_t> poStreamStdio;
Uid poUid;
Gid poGid;
};
}

107
src/uvw/process.ipp Normal file
View File

@ -0,0 +1,107 @@
#include <algorithm>
#include "config.h"
namespace uvw {
UVW_INLINE exit_event::exit_event(int64_t code, int sig) noexcept
: status{code}, signal{sig} {}
UVW_INLINE void process_handle::exit_callback(uv_process_t *hndl, int64_t exit_status, int term_signal) {
process_handle &process = *(static_cast<process_handle *>(hndl->data));
process.publish(exit_event{exit_status, term_signal});
}
UVW_INLINE process_handle::process_handle(loop::token token, std::shared_ptr<loop> ref)
: handle{token, std::move(ref)} {}
UVW_INLINE void process_handle::disable_stdio_inheritance() noexcept {
uv_disable_stdio_inheritance();
}
UVW_INLINE bool process_handle::kill(int pid, int signum) noexcept {
return (0 == uv_kill(pid, signum));
}
UVW_INLINE int process_handle::init() {
// deferred initialization: libuv initializes process handles only when
// uv_spawn is invoked and uvw stays true to the underlying library
return 0;
}
UVW_INLINE int process_handle::spawn(const char *file, char **args, char **env) {
uv_process_options_t po;
po.exit_cb = &exit_callback;
po.file = file;
po.args = args;
po.env = env;
po.cwd = po_cwd.empty() ? nullptr : po_cwd.data();
po.flags = static_cast<uv_process_flags>(po_flags);
po.uid = po_uid;
po.gid = po_gid;
std::vector<uv_stdio_container_t> poStdio;
poStdio.reserve(po_fd_stdio.size() + po_stream_stdio.size());
poStdio.insert(poStdio.begin(), po_fd_stdio.cbegin(), po_fd_stdio.cend());
poStdio.insert(poStdio.end(), po_stream_stdio.cbegin(), po_stream_stdio.cend());
po.stdio_count = static_cast<decltype(po.stdio_count)>(poStdio.size());
po.stdio = poStdio.data();
// see init member function for more details
static_cast<void>(leak_if(0));
return uv_spawn(parent().raw(), raw(), &po);
}
UVW_INLINE int process_handle::kill(int signum) {
return uv_process_kill(raw(), signum);
}
UVW_INLINE int process_handle::pid() noexcept {
return raw()->pid;
}
UVW_INLINE process_handle &process_handle::cwd(const std::string &path) noexcept {
po_cwd = path;
return *this;
}
UVW_INLINE process_handle &process_handle::flags(process_flags flags) noexcept {
po_flags = flags;
return *this;
}
UVW_INLINE process_handle &process_handle::stdio(file_handle fd, stdio_flags flags) {
auto fgs = static_cast<uv_stdio_flags>(flags);
auto actual = uvw::file_handle{fd};
auto it = std::find_if(po_fd_stdio.begin(), po_fd_stdio.end(), [actual](auto &&container) {
return static_cast<const uvw::details::uv_type_wrapper<int>>(container.data.fd) == static_cast<const uvw::details::uv_type_wrapper<int>>(actual);
});
if(it == po_fd_stdio.cend()) {
uv_stdio_container_t container;
container.flags = fgs;
container.data.fd = actual;
po_fd_stdio.push_back(container);
} else {
it->flags = fgs;
it->data.fd = actual;
}
return *this;
}
UVW_INLINE process_handle &process_handle::uid(uid_type id) {
po_uid = id;
return *this;
}
UVW_INLINE process_handle &process_handle::gid(gid_type id) {
po_gid = id;
return *this;
}
} // namespace uvw

View File

@ -1,75 +1,57 @@
#pragma once
#ifndef UVW_REQUEST_INCLUDE_H
#define UVW_REQUEST_INCLUDE_H
#include <memory>
#include <type_traits>
#include <utility>
#include <memory>
#include <uv.h>
#include "config.h"
#include "resource.hpp"
namespace uvw {
template<typename T, typename U>
class Request: public Resource<T, U> {
/**
* @brief Request base class.
*
* Base type for all `uvw` request types.
*/
template<typename T, typename U, typename... E>
class request: public resource<T, U, E...> {
protected:
static auto reserve(U *req) -> std::shared_ptr<T> {
[[nodiscard]] static auto reserve(U *req) {
auto ptr = static_cast<T *>(req->data)->shared_from_this();
ptr->reset();
ptr->self_reset();
return ptr;
}
template<typename E>
static void defaultCallback(U *req, int status) {
auto ptr = reserve(req);
if(status) { ptr->publish(ErrorEvent{status}); }
else { ptr->publish(E{}); }
}
template<typename F, typename... Args>
auto invoke(F &&f, Args&&... args)
-> typename std::enable_if<not std::is_void<typename std::result_of<F(Args...)>::type>::value>::type {
auto err = std::forward<F>(f)(std::forward<Args>(args)...);
if(err) { Emitter<T>::publish(ErrorEvent{err}); }
else { this->leak(); }
}
template<typename F, typename... Args>
auto invoke(F &&f, Args&&... args)
-> typename std::enable_if<std::is_void<typename std::result_of<F(Args...)>::type>::value>::type {
std::forward<F>(f)(std::forward<Args>(args)...);
this->leak();
}
public:
using Resource<T, U>::Resource;
using resource<T, U, E...>::resource;
/**
* @brief Cancels a pending request.
*
* This method fails if the request is executing or has finished
* executing.<br/>
* It can emit an ErrorEvent event in case of errors.
* executing.
*
* See the official
* [documentation](http://docs.libuv.org/en/v1.x/request.html#c.uv_cancel)
* for further details.
*
* @return True in case of success, false otherwise.
* @return Underlying return value.
*/
bool cancel() {
return (0 == uv_cancel(this->template get<uv_req_t>()));
int cancel() {
return uv_cancel(reinterpret_cast<uv_req_t *>(this->raw()));
}
/**
* @brief Returns the size of the underlying request type.
* @return The size of the underlying request type.
*/
std::size_t size() const noexcept {
return uv_req_size(this->template get<uv_req_t>()->type);
[[nodiscard]] std::size_t size() const noexcept {
return uv_req_size(reinterpret_cast<const uv_req_t *>(this->raw())->type);
}
};
} // namespace uvw
}
#endif // UVW_REQUEST_INCLUDE_H

View File

@ -1,48 +1,42 @@
#pragma once
#ifndef UVW_RESOURCE_INCLUDE_H
#define UVW_RESOURCE_INCLUDE_H
#include <memory>
#include <utility>
#include "emitter.hpp"
#include "underlying_type.hpp"
#include "config.h"
#include "emitter.h"
#include "uv_type.hpp"
namespace uvw {
/**
* @brief Common class for almost all the resources available in `uvw`.
*
* This is the base class for handles and requests.
*/
template<typename T, typename U>
class Resource: public UnderlyingType<T, U>, public Emitter<T>, public std::enable_shared_from_this<T> {
template<typename T, typename U, typename... E>
class resource: public uv_type<U>, public emitter<T, E...>, public std::enable_shared_from_this<T> {
protected:
using ConstructorAccess = typename UnderlyingType<T, U>::ConstructorAccess;
auto parent() const noexcept -> uv_loop_t * {
return this->loop().loop.get();
[[nodiscard]] int leak_if(int err) noexcept {
if(err == 0) {
self_ptr = this->shared_from_this();
}
void leak() noexcept {
sPtr = this->shared_from_this();
return err;
}
void reset() noexcept {
sPtr.reset();
void self_reset() noexcept {
self_ptr.reset();
}
bool self() const noexcept {
return static_cast<bool>(sPtr);
[[nodiscard]] bool has_self() const noexcept {
return static_cast<bool>(self_ptr);
}
public:
explicit Resource(ConstructorAccess ca, std::shared_ptr<Loop> ref)
: UnderlyingType<T, U>{ca, std::move(ref)},
Emitter<T>{},
std::enable_shared_from_this<T>{}
{
this->get()->data = static_cast<T*>(this);
explicit resource(loop::token token, std::shared_ptr<loop> ref)
: uv_type<U>{token, std::move(ref)} {
this->raw()->data = this;
}
/**
@ -50,22 +44,23 @@ public:
* @return User-defined data if any, an invalid pointer otherwise.
*/
template<typename R = void>
std::shared_ptr<R> data() const {
return std::static_pointer_cast<R>(userData);
[[nodiscard]] std::shared_ptr<R> data() const {
return std::static_pointer_cast<R>(user_data);
}
/**
* @brief Sets arbitrary data. `uvw` won't use this field in any case.
* @param uData User-defined arbitrary data.
* @param udata User-defined arbitrary data.
*/
void data(std::shared_ptr<void> uData) {
userData = std::move(uData);
void data(std::shared_ptr<void> udata) {
user_data = std::move(udata);
}
private:
std::shared_ptr<void> userData{nullptr};
std::shared_ptr<void> sPtr{nullptr};
std::shared_ptr<void> user_data{nullptr};
std::shared_ptr<void> self_ptr{nullptr};
};
} // namespace uvw
}
#endif // UVW_RESOURCE_INCLUDE_H

2
src/uvw/signal.cpp Normal file
View File

@ -0,0 +1,2 @@
#include "signal.h"
#include "signal.ipp"

83
src/uvw/signal.h Normal file
View File

@ -0,0 +1,83 @@
#ifndef UVW_SIGNAL_INCLUDE_H
#define UVW_SIGNAL_INCLUDE_H
#include <uv.h>
#include "config.h"
#include "handle.hpp"
#include "loop.h"
namespace uvw {
/*! @brief Signal event. */
struct signal_event {
explicit signal_event(int sig) noexcept;
int signum; /*!< The signal being monitored by this handle. */
};
/**
* @brief The signal handle.
*
* Signal handles implement Unix style signal handling on a per-event loop
* bases.<br/>
* Reception of some signals is emulated on Windows.
*
* To create a `signal_handle` through a `loop`, no arguments are required.
*
* See the official
* [documentation](http://docs.libuv.org/en/v1.x/signal.html)
* for further details.
*/
class signal_handle final: public handle<signal_handle, uv_signal_t, signal_event> {
static void start_callback(uv_signal_t *hndl, int signum);
public:
using handle::handle;
/**
* @brief Initializes the handle.
* @return Underlying return value.
*/
int init();
/**
* @brief Starts the handle.
*
* The handle will start emitting signal events when needed.
*
* @param signum The signal to be monitored.
* @return Underlying return value.
*/
int start(int signum);
/**
* @brief Starts the handle.
*
* Same functionality as signal_handle::start but the signal handler is
* reset the moment the signal is received.
*
* @param signum The signal to be monitored.
* @return Underlying return value.
*/
int one_shot(int signum);
/**
* @brief Stops the handle.
* @return Underlying return value.
*/
int stop();
/**
* @brief Gets the signal being monitored.
* @return The signal being monitored.
*/
int signal() const noexcept;
};
} // namespace uvw
#ifndef UVW_AS_LIB
# include "signal.ipp"
#endif
#endif // UVW_SIGNAL_INCLUDE_H

View File

@ -1,96 +0,0 @@
#pragma once
#include <utility>
#include <memory>
#include <uv.h>
#include "handle.hpp"
#include "loop.hpp"
namespace uvw {
/**
* @brief SignalEvent event.
*
* It will be emitted by SignalHandle according with its functionalities.
*/
struct SignalEvent {
explicit SignalEvent(int sig) noexcept: signum{sig} {}
int signum; /*!< The signal being monitored by this handle. */
};
/**
* @brief The SignalHandle handle.
*
* Signal handles implement Unix style signal handling on a per-event loop
* bases.<br/>
* Reception of some signals is emulated on Windows.
*
* To create a `SignalHandle` through a `Loop`, no arguments are required.
*
* See the official
* [documentation](http://docs.libuv.org/en/v1.x/signal.html)
* for further details.
*/
class SignalHandle final: public Handle<SignalHandle, uv_signal_t> {
static void startCallback(uv_signal_t *handle, int signum) {
SignalHandle &signal = *(static_cast<SignalHandle*>(handle->data));
signal.publish(SignalEvent{signum});
}
public:
using Handle::Handle;
/**
* @brief Initializes the handle.
* @return True in case of success, false otherwise.
*/
bool init() {
return initialize(&uv_signal_init);
}
/**
* @brief Starts the handle.
*
* The handle will start emitting SignalEvent when needed.
*
* @param signum The signal to be monitored.
*/
void start(int signum) {
invoke(&uv_signal_start, get(), &startCallback, signum);
}
/**
* @brief Starts the handle.
*
* Same functionality as SignalHandle::start but the signal handler is reset
* the moment the signal is received.
*
* @param signum
*/
void oneShot(int signum) {
invoke(&uv_signal_start_oneshot, get(), &startCallback, signum);
}
/**
* @brief Stops the handle.
*/
void stop() {
invoke(&uv_signal_stop, get());
}
/**
* @brief Gets the signal being monitored.
* @return The signal being monitored.
*/
int signal() const noexcept {
return get()->signum;
}
};
}

33
src/uvw/signal.ipp Normal file
View File

@ -0,0 +1,33 @@
#include "config.h"
namespace uvw {
UVW_INLINE signal_event::signal_event(int sig) noexcept
: signum{sig} {}
UVW_INLINE void signal_handle::start_callback(uv_signal_t *hndl, int signum) {
signal_handle &signal = *(static_cast<signal_handle *>(hndl->data));
signal.publish(signal_event{signum});
}
UVW_INLINE int signal_handle::init() {
return leak_if(uv_signal_init(parent().raw(), raw()));
}
UVW_INLINE int signal_handle::start(int signum) {
return uv_signal_start(raw(), &start_callback, signum);
}
UVW_INLINE int signal_handle::one_shot(int signum) {
return uv_signal_start_oneshot(raw(), &start_callback, signum);
}
UVW_INLINE int signal_handle::stop() {
return uv_signal_stop(raw());
}
UVW_INLINE int signal_handle::signal() const noexcept {
return raw()->signum;
}
} // namespace uvw

2
src/uvw/stream.cpp Normal file
View File

@ -0,0 +1,2 @@
#include "stream.h"
#include "stream.ipp"

482
src/uvw/stream.h Normal file
View File

@ -0,0 +1,482 @@
#ifndef UVW_STREAM_INCLUDE_H
#define UVW_STREAM_INCLUDE_H
#include <algorithm>
#include <array>
#include <cstddef>
#include <iterator>
#include <memory>
#include <utility>
#include <uv.h>
#include "config.h"
#include "handle.hpp"
#include "loop.h"
#include "request.hpp"
namespace uvw {
/*! @brief Connect event. */
struct connect_event {};
/*! @brief End event. */
struct end_event {};
/*! @brief Listen event. */
struct listen_event {};
/*! @brief Shutdown event. */
struct shutdown_event {};
/*! @brief Write event. */
struct write_event {};
/*! @brief Data event. */
struct data_event {
explicit data_event(std::unique_ptr<char[]> buf, std::size_t len) noexcept;
std::unique_ptr<char[]> data; /*!< A bunch of data read on the stream. */
std::size_t length; /*!< The amount of data read on the stream. */
};
namespace details {
class connect_req final: public request<connect_req, uv_connect_t, connect_event> {
static void connect_callback(uv_connect_t *req, int status);
public:
using request::request;
template<typename F, typename... Args>
auto connect(F &&f, Args &&...args) -> std::enable_if_t<std::is_same_v<decltype(std::forward<F>(f)(raw(), std::forward<Args>(args)..., &connect_callback)), void>, int> {
std::forward<F>(f)(raw(), std::forward<Args>(args)..., &connect_callback);
return this->leak_if(0);
}
template<typename F, typename... Args>
auto connect(F &&f, Args &&...args) -> std::enable_if_t<!std::is_same_v<decltype(std::forward<F>(f)(raw(), std::forward<Args>(args)..., &connect_callback)), void>, int> {
return this->leak_if(std::forward<F>(f)(raw(), std::forward<Args>(args)..., &connect_callback));
}
};
class shutdown_req final: public request<shutdown_req, uv_shutdown_t, shutdown_event> {
static void shoutdown_callback(uv_shutdown_t *req, int status);
public:
using request::request;
int shutdown(uv_stream_t *hndl);
};
template<typename Deleter>
class write_req final: public request<write_req<Deleter>, uv_write_t, write_event> {
static void write_callback(uv_write_t *req, int status) {
if(auto ptr = request<write_req<Deleter>, uv_write_t, write_event>::reserve(req); status) {
ptr->publish(error_event{status});
} else {
ptr->publish(write_event{});
}
}
public:
write_req(loop::token token, std::shared_ptr<loop> parent, std::unique_ptr<char[], Deleter> dt, unsigned int len)
: request<write_req<Deleter>, uv_write_t, write_event>{token, std::move(parent)},
data{std::move(dt)},
buf{uv_buf_init(data.get(), len)} {}
int write(uv_stream_t *hndl) {
return this->leak_if(uv_write(this->raw(), hndl, &buf, 1, &write_callback));
}
int write(uv_stream_t *hndl, uv_stream_t *send) {
return this->leak_if(uv_write2(this->raw(), hndl, &buf, 1, send, &write_callback));
}
private:
std::unique_ptr<char[], Deleter> data;
uv_buf_t buf;
};
} // namespace details
/**
* @brief The stream handle.
*
* Stream handles provide an abstraction of a duplex communication channel.
* The stream handle is an intermediate type, `uvw` provides three stream
* implementations: tcp, pipe and tty handles.
*/
template<typename T, typename U, typename... E>
class stream_handle: public handle<T, U, listen_event, end_event, connect_event, shutdown_event, data_event, write_event, E...> {
using base = handle<T, U, listen_event, end_event, connect_event, shutdown_event, data_event, write_event, E...>;
template<typename, typename, typename...>
friend class stream_handle;
static constexpr unsigned int DEFAULT_BACKLOG = 128;
static void read_callback(uv_stream_t *hndl, ssize_t nread, const uv_buf_t *buf) {
T &ref = *(static_cast<T *>(hndl->data));
// data will be destroyed no matter of what the value of nread is
std::unique_ptr<char[]> data{buf->base};
// nread == 0 is ignored (see http://docs.libuv.org/en/v1.x/stream.html)
// equivalent to EAGAIN/EWOULDBLOCK, it shouldn't be treated as an error
// for we don't have data to emit though, it's fine to suppress it
if(nread == UV_EOF) {
// end of stream
ref.publish(end_event{});
} else if(nread > 0) {
// data available
ref.publish(data_event{std::move(data), static_cast<std::size_t>(nread)});
} else if(nread < 0) {
// transmission error
ref.publish(error_event(nread));
}
}
static void listen_callback(uv_stream_t *hndl, int status) {
if(T &ref = *(static_cast<T *>(hndl->data)); status) {
ref.publish(error_event{status});
} else {
ref.publish(listen_event{});
}
}
[[nodiscard]] uv_stream_t *as_uv_stream() {
return reinterpret_cast<uv_stream_t *>(this->raw());
}
[[nodiscard]] const uv_stream_t *as_uv_stream() const {
return reinterpret_cast<const uv_stream_t *>(this->raw());
}
public:
#ifdef _MSC_VER
stream_handle(loop::token token, std::shared_ptr<loop> ref)
: base{token, std::move(ref)} {}
#else
using base::base;
#endif
/**
* @brief Shutdowns the outgoing (write) side of a duplex stream.
*
* It waits for pending write requests to complete. The handle should refer
* to a initialized stream.<br/>
* A shutdown event will be emitted after shutdown is complete.
*
* @return Underlying return value.
*/
int shutdown() {
auto listener = [ptr = this->shared_from_this()](const auto &event, const auto &) {
ptr->publish(event);
};
auto shutdown = this->parent().template resource<details::shutdown_req>();
shutdown->template on<error_event>(listener);
shutdown->template on<shutdown_event>(listener);
return shutdown->shutdown(as_uv_stream());
}
/**
* @brief Starts listening for incoming connections.
*
* When a new incoming connection is received, a listen event is
* emitted.
*
* @param backlog Indicates the number of connections the kernel might
* queue, same as listen(2).
*
* @return Underlying return value.
*/
int listen(int backlog = DEFAULT_BACKLOG) {
return uv_listen(as_uv_stream(), backlog, &listen_callback);
}
/**
* @brief Accepts incoming connections.
*
* This call is used in conjunction with `listen()` to accept incoming
* connections. Call this function after receiving a listen event to accept
* the connection. Before calling this function, the submitted handle must
* be initialized.
*
* When the listen event is emitted it is guaranteed that this function will
* complete successfully the first time. If you attempt to use it more than
* once, it may fail.<br/>
* It is suggested to only call this function once per listen event.
*
* @note
* Both the handles must be running on the same loop.
*
* @param ref An initialized handle to be used to accept the connection.
* @return Underlying return value.
*/
template<typename S>
int accept(S &ref) {
return uv_accept(as_uv_stream(), ref.as_uv_stream());
}
/**
* @brief Starts reading data from an incoming stream.
*
* A data event will be emitted several times until there is no more data to
* read or `stop()` is called.<br/>
* An end event will be emitted when there is no more data to read.
*
* @return Underlying return value.
*/
int read() {
return uv_read_start(as_uv_stream(), &details::common_alloc_callback, &read_callback);
}
/**
* @brief Starts reading data from an incoming stream.
* @sa read
* @tparam Alloc Custom allocation function.
* @return Underlying return value.
*/
template<auto Alloc>
int read() {
return uv_read_start(as_uv_stream(), &details::common_alloc_callback<T, Alloc>, &read_callback);
}
/**
* @brief Stops reading data from the stream.
*
* This function is idempotent and may be safely called on a stopped stream.
*
* @return Underlying return value.
*/
int stop() {
return uv_read_stop(as_uv_stream());
}
/**
* @brief Writes data to the stream.
*
* Data are written in order. The handle takes the ownership of the data and
* it is in charge of delete them.
*
* A write event will be emitted when the data have been written.
*
* @param data The data to be written to the stream.
* @param len The lenght of the submitted data.
* @return Underlying return value.
*/
template<typename Deleter>
int write(std::unique_ptr<char[], Deleter> data, unsigned int len) {
auto req = this->parent().template resource<details::write_req<Deleter>>(std::move(data), len);
auto listener = [ptr = this->shared_from_this()](const auto &event, const auto &) {
ptr->publish(event);
};
req->template on<error_event>(listener);
req->template on<write_event>(listener);
return req->write(as_uv_stream());
}
/**
* @brief Writes data to the stream.
*
* Data are written in order. The handle doesn't take the ownership of the
* data. Be sure that their lifetime overcome the one of the request.
*
* A write event will be emitted when the data have been written.
*
* @param data The data to be written to the stream.
* @param len The lenght of the submitted data.
* @return Underlying return value.
*/
int write(char *data, unsigned int len) {
auto req = this->parent().template resource<details::write_req<void (*)(char *)>>(std::unique_ptr<char[], void (*)(char *)>{data, [](char *) {}}, len);
auto listener = [ptr = this->shared_from_this()](const auto &event, const auto &) {
ptr->publish(event);
};
req->template on<error_event>(listener);
req->template on<write_event>(listener);
return req->write(as_uv_stream());
}
/**
* @brief Extended write function for sending handles over a pipe handle.
*
* The pipe must be initialized with `ipc == true`.
*
* `send` must be a tcp or pipe handle, which is a server or a connection
* (listening or connected state). Bound sockets or pipes will be assumed to
* be servers.
*
* The handle takes the ownership of the data and it is in charge of delete
* them.
*
* A write event will be emitted when the data have been written.
*
* @param send The handle over which to write data.
* @param data The data to be written to the stream.
* @param len The lenght of the submitted data.
* @return Underlying return value.
*/
template<typename S, typename Deleter>
int write(S &send, std::unique_ptr<char[], Deleter> data, unsigned int len) {
auto req = this->parent().template resource<details::write_req<Deleter>>(std::move(data), len);
auto listener = [ptr = this->shared_from_this()](const auto &event, const auto &) {
ptr->publish(event);
};
req->template on<error_event>(listener);
req->template on<write_event>(listener);
return req->write(as_uv_stream(), send.as_uv_stream());
}
/**
* @brief Extended write function for sending handles over a pipe handle.
*
* The pipe must be initialized with `ipc == true`.
*
* `send` must be a tcp or pipe handle, which is a server or a connection
* (listening or connected state). Bound sockets or pipes will be assumed to
* be servers.
*
* The handle doesn't take the ownership of the data. Be sure that their
* lifetime overcome the one of the request.
*
* A write event will be emitted when the data have been written.
*
* @param send The handle over which to write data.
* @param data The data to be written to the stream.
* @param len The lenght of the submitted data.
* @return Underlying return value.
*/
template<typename S>
int write(S &send, char *data, unsigned int len) {
auto req = this->parent().template resource<details::write_req<void (*)(char *)>>(std::unique_ptr<char[], void (*)(char *)>{data, [](char *) {}}, len);
auto listener = [ptr = this->shared_from_this()](const auto &event, const auto &) {
ptr->publish(event);
};
req->template on<error_event>(listener);
req->template on<write_event>(listener);
return req->write(as_uv_stream(), send.as_uv_stream());
}
/**
* @brief Queues a write request if it can be completed immediately.
*
* Same as `write()`, but wont queue a write request if it cant be
* completed immediately.
*
* @param data The data to be written to the stream.
* @param len The lenght of the submitted data.
* @return Underlying return value.
*/
int try_write(std::unique_ptr<char[]> data, unsigned int len) {
std::array bufs{uv_buf_init(data.get(), len)};
return uv_try_write(as_uv_stream(), bufs.data(), 1);
}
/**
* @brief Queues a write request if it can be completed immediately.
*
* Same as `try_write` for sending handles over a pipe.
*
* @param data The data to be written to the stream.
* @param len The lenght of the submitted data.
* @param send A valid handle suitable for the purpose.
* @return Underlying return value.
*/
template<typename V, typename W>
int try_write(std::unique_ptr<char[]> data, unsigned int len, stream_handle<V, W> &send) {
std::array bufs{uv_buf_init(data.get(), len)};
return uv_try_write2(as_uv_stream(), bufs.data(), 1, send.raw());
}
/**
* @brief Queues a write request if it can be completed immediately.
*
* Same as `write()`, but wont queue a write request if it cant be
* completed immediately.
*
* @param data The data to be written to the stream.
* @param len The lenght of the submitted data.
* @return Underlying return value.
*/
int try_write(char *data, unsigned int len) {
std::array bufs{uv_buf_init(data, len)};
return uv_try_write(as_uv_stream(), bufs.data(), 1);
}
/**
* @brief Queues a write request if it can be completed immediately.
*
* Same as `try_write` for sending handles over a pipe.
*
* @param data The data to be written to the stream.
* @param len The lenght of the submitted data.
* @param send A valid handle suitable for the purpose.
* @return Underlying return value.
*/
template<typename V, typename W>
int try_write(char *data, unsigned int len, stream_handle<V, W> &send) {
std::array bufs{uv_buf_init(data, len)};
return uv_try_write2(as_uv_stream(), bufs.data(), 1, send.raw());
}
/**
* @brief Checks if the stream is readable.
* @return True if the stream is readable, false otherwise.
*/
[[nodiscard]] bool readable() const noexcept {
return (uv_is_readable(as_uv_stream()) == 1);
}
/**
* @brief Checks if the stream is writable.
* @return True if the stream is writable, false otherwise.
*/
[[nodiscard]] bool writable() const noexcept {
return (uv_is_writable(as_uv_stream()) == 1);
}
/**
* @brief Enables or disables blocking mode for a stream.
*
* When blocking mode is enabled all writes complete synchronously. The
* interface remains unchanged otherwise, e.g. completion or failure of the
* operation will still be reported through events which are emitted
* asynchronously.
*
* See the official
* [documentation](http://docs.libuv.org/en/v1.x/stream.html#c.uv_stream_set_blocking)
* for further details.
*
* @param enable True to enable blocking mode, false otherwise.
* @return True in case of success, false otherwise.
*/
bool blocking(bool enable = false) {
return (0 == uv_stream_set_blocking(as_uv_stream(), enable));
}
/**
* @brief Gets the amount of queued bytes waiting to be sent.
* @return Amount of queued bytes waiting to be sent.
*/
[[nodiscard]] size_t write_queue_size() const noexcept {
return uv_stream_get_write_queue_size(as_uv_stream());
}
};
} // namespace uvw
#ifndef UVW_AS_LIB
# include "stream.ipp"
#endif
#endif // UVW_STREAM_INCLUDE_H

View File

@ -1,481 +0,0 @@
#pragma once
#include <algorithm>
#include <iterator>
#include <cstddef>
#include <utility>
#include <memory>
#include <uv.h>
#include "request.hpp"
#include "handle.hpp"
#include "loop.hpp"
namespace uvw {
/**
* @brief ConnectEvent event.
*
* It will be emitted by StreamHandle according with its functionalities.
*/
struct ConnectEvent {};
/**
* @brief EndEvent event.
*
* It will be emitted by StreamHandle according with its functionalities.
*/
struct EndEvent {};
/**
* @brief ListenEvent event.
*
* It will be emitted by StreamHandle according with its functionalities.
*/
struct ListenEvent {};
/**
* @brief ShutdownEvent event.
*
* It will be emitted by StreamHandle according with its functionalities.
*/
struct ShutdownEvent {};
/**
* @brief WriteEvent event.
*
* It will be emitted by StreamHandle according with its functionalities.
*/
struct WriteEvent {};
/**
* @brief DataEvent event.
*
* It will be emitted by StreamHandle according with its functionalities.
*/
struct DataEvent {
explicit DataEvent(std::unique_ptr<char[]> buf, std::size_t len) noexcept
: data{std::move(buf)}, length{len}
{}
std::unique_ptr<char[]> data; /*!< A bunch of data read on the stream. */
std::size_t length; /*!< The amount of data read on the stream. */
};
namespace details {
struct ConnectReq final: public Request<ConnectReq, uv_connect_t> {
using Request::Request;
template<typename F, typename... Args>
void connect(F &&f, Args&&... args) {
invoke(std::forward<F>(f), get(), std::forward<Args>(args)..., &defaultCallback<ConnectEvent>);
}
};
struct ShutdownReq final: public Request<ShutdownReq, uv_shutdown_t> {
using Request::Request;
void shutdown(uv_stream_t *handle) {
invoke(&uv_shutdown, get(), handle, &defaultCallback<ShutdownEvent>);
}
};
class WriteReq final: public Request<WriteReq, uv_write_t> {
public:
using Deleter = void(*)(char *);
WriteReq(ConstructorAccess ca, std::shared_ptr<Loop> loop, std::unique_ptr<char[], Deleter> dt, unsigned int len)
: Request<WriteReq, uv_write_t>{ca, std::move(loop)},
data{std::move(dt)},
buf(uv_buf_init(data.get(), len))
{}
void write(uv_stream_t *handle) {
invoke(&uv_write, get(), handle, &buf, 1, &defaultCallback<WriteEvent>);
}
void write(uv_stream_t *handle, uv_stream_t *send) {
invoke(&uv_write2, get(), handle, &buf, 1, send, &defaultCallback<WriteEvent>);
}
private:
std::unique_ptr<char[], Deleter> data;
uv_buf_t buf;
};
}
/**
* @brief The StreamHandle handle.
*
* Stream handles provide an abstraction of a duplex communication channel.
* StreamHandle is an intermediate type, `uvw` provides three stream
* implementations: TCPHandle, PipeHandle and TTYHandle.
*/
template<typename T, typename U>
class StreamHandle: public Handle<T, U> {
static constexpr unsigned int DEFAULT_BACKLOG = 128;
static void readCallback(uv_stream_t *handle, ssize_t nread, const uv_buf_t *buf) {
T &ref = *(static_cast<T*>(handle->data));
// data will be destroyed no matter of what the value of nread is
std::unique_ptr<char[]> data{buf->base};
// nread == 0 is ignored (see http://docs.libuv.org/en/v1.x/stream.html)
// equivalent to EAGAIN/EWOULDBLOCK, it shouldn't be treated as an error
// for we don't have data to emit though, it's fine to suppress it
if(nread == UV_EOF) {
// end of stream
ref.publish(EndEvent{});
} else if(nread > 0) {
// data available
ref.publish(DataEvent{std::move(data), static_cast<std::size_t>(nread)});
} else if(nread < 0) {
// transmission error
ref.publish(ErrorEvent(nread));
}
}
static void listenCallback(uv_stream_t *handle, int status) {
T &ref = *(static_cast<T*>(handle->data));
if(status) { ref.publish(ErrorEvent{status}); }
else { ref.publish(ListenEvent{}); }
}
public:
#ifdef _MSC_VER
StreamHandle(typename Handle<T, U>::ConstructorAccess ca, std::shared_ptr<Loop> ref)
: Handle<T, U>{ca, std::move(ref)}
{}
#else
using Handle<T, U>::Handle;
#endif
/**
* @brief Shutdowns the outgoing (write) side of a duplex stream.
*
* It waits for pending write requests to complete. The handle should refer
* to a initialized stream.<br/>
* A ShutdownEvent event will be emitted after shutdown is complete.
*/
void shutdown() {
auto ptr = this->shared_from_this();
auto errorEventListener = [ptr](const ErrorEvent &event, const details::ShutdownReq &) {
ptr->publish(event);
};
auto shutdownEventListener = [ptr](const ShutdownEvent &event, const details::ShutdownReq &) {
ptr->publish(event);
};
auto shutdown = this->loop().template resource<details::ShutdownReq>();
shutdown->template once<ErrorEvent>(errorEventListener);
shutdown->template once<ShutdownEvent>(shutdownEventListener);
shutdown->shutdown(this->template get<uv_stream_t>());
}
/**
* @brief Starts listening for incoming connections.
*
* When a new incoming connection is received, a ListenEvent event is
* emitted.<br/>
* An ErrorEvent event will be emitted in case of errors.
*
* @param backlog Indicates the number of connections the kernel might
* queue, same as listen(2).
*/
void listen(int backlog = DEFAULT_BACKLOG) {
this->invoke(&uv_listen, this->template get<uv_stream_t>(), backlog, &listenCallback);
}
/**
* @brief Accepts incoming connections.
*
* This call is used in conjunction with `listen()` to accept incoming
* connections. Call this function after receiving a ListenEvent event to
* accept the connection. Before calling this function, the submitted handle
* must be initialized.<br>
* An ErrorEvent event will be emitted in case of errors.
*
* When the ListenEvent event is emitted it is guaranteed that this
* function will complete successfully the first time. If you attempt to use
* it more than once, it may fail.<br/>
* It is suggested to only call this function once per ListenEvent event.
*
* @note
* Both the handles must be running on the same loop.
*
* @param ref An initialized handle to be used to accept the connection.
*/
template<typename S>
void accept(S &ref) {
this->invoke(&uv_accept, this->template get<uv_stream_t>(), ref.template get<uv_stream_t>());
}
/**
* @brief Starts reading data from an incoming stream.
*
* A DataEvent event will be emitted several times until there is no more
* data to read or `stop()` is called.<br/>
* An EndEvent event will be emitted when there is no more data to read.
*/
void read() {
this->invoke(&uv_read_start, this->template get<uv_stream_t>(), &this->allocCallback, &readCallback);
}
/**
* @brief Stops reading data from the stream.
*
* This function is idempotent and may be safely called on a stopped stream.
*/
void stop() {
this->invoke(&uv_read_stop, this->template get<uv_stream_t>());
}
/**
* @brief Writes data to the stream.
*
* Data are written in order. The handle takes the ownership of the data and
* it is in charge of delete them.
*
* A WriteEvent event will be emitted when the data have been written.<br/>
* An ErrorEvent event will be emitted in case of errors.
*
* @param data The data to be written to the stream.
* @param len The lenght of the submitted data.
*/
void write(std::unique_ptr<char[]> data, unsigned int len) {
auto req = this->loop().template resource<details::WriteReq>(
std::unique_ptr<char[], details::WriteReq::Deleter>{
data.release(), [](char *ptr) { delete[] ptr; }
}, len);
auto ptr = this->shared_from_this();
auto errorEventListener = [ptr](const ErrorEvent &event, const details::WriteReq &) {
ptr->publish(event);
};
auto writeEventListener = [ptr](const WriteEvent &event, const details::WriteReq &) {
ptr->publish(event);
};
req->template once<ErrorEvent>(errorEventListener);
req->template once<WriteEvent>(writeEventListener);
req->write(this->template get<uv_stream_t>());
}
/**
* @brief Writes data to the stream.
*
* Data are written in order. The handle doesn't take the ownership of the
* data. Be sure that their lifetime overcome the one of the request.
*
* A WriteEvent event will be emitted when the data have been written.<br/>
* An ErrorEvent event will be emitted in case of errors.
*
* @param data The data to be written to the stream.
* @param len The lenght of the submitted data.
*/
void write(char *data, unsigned int len) {
auto req = this->loop().template resource<details::WriteReq>(
std::unique_ptr<char[], details::WriteReq::Deleter>{
data, [](char *) {}
}, len);
auto ptr = this->shared_from_this();
auto errorEventListener = [ptr](const ErrorEvent &event, const details::WriteReq &) {
ptr->publish(event);
};
auto writeEventListener = [ptr](const WriteEvent &event, const details::WriteReq &) {
ptr->publish(event);
};
req->template once<ErrorEvent>(errorEventListener);
req->template once<WriteEvent>(writeEventListener);
req->write(this->template get<uv_stream_t>());
}
/**
* @brief Extended write function for sending handles over a pipe handle.
*
* The pipe must be initialized with `ipc == true`.
*
* `send` must be a TCPHandle or PipeHandle handle, which is a server or a
* connection (listening or connected state). Bound sockets or pipes will be
* assumed to be servers.
*
* The handle takes the ownership of the data and it is in charge of delete
* them.
*
* A WriteEvent event will be emitted when the data have been written.<br/>
* An ErrorEvent wvent will be emitted in case of errors.
*
* @param send The handle over which to write data.
* @param data The data to be written to the stream.
* @param len The lenght of the submitted data.
*/
template<typename S>
void write(S &send, std::unique_ptr<char[]> data, unsigned int len) {
auto req = this->loop().template resource<details::WriteReq>(
std::unique_ptr<char[], details::WriteReq::Deleter>{
data.release(), [](char *ptr) { delete[] ptr; }
}, len);
auto ptr = this->shared_from_this();
auto errorEventListener = [ptr](const ErrorEvent &event, const details::WriteReq &) {
ptr->publish(event);
};
auto writeEventListener = [ptr](const WriteEvent &event, const details::WriteReq &) {
ptr->publish(event);
};
req->template once<ErrorEvent>(errorEventListener);
req->template once<WriteEvent>(writeEventListener);
req->write(this->template get<uv_stream_t>(), send.template get<uv_stream_t>());
}
/**
* @brief Extended write function for sending handles over a pipe handle.
*
* The pipe must be initialized with `ipc == true`.
*
* `send` must be a TCPHandle or PipeHandle handle, which is a server or a
* connection (listening or connected state). Bound sockets or pipes will be
* assumed to be servers.
*
* The handle doesn't take the ownership of the data. Be sure that their
* lifetime overcome the one of the request.
*
* A WriteEvent event will be emitted when the data have been written.<br/>
* An ErrorEvent wvent will be emitted in case of errors.
*
* @param send The handle over which to write data.
* @param data The data to be written to the stream.
* @param len The lenght of the submitted data.
*/
template<typename S>
void write(S &send, char *data, unsigned int len) {
auto req = this->loop().template resource<details::WriteReq>(
std::unique_ptr<char[], details::WriteReq::Deleter>{
data, [](char *) {}
}, len);
auto ptr = this->shared_from_this();
auto errorEventListener = [ptr](const ErrorEvent &event, const details::WriteReq &) {
ptr->publish(event);
};
auto writeEventListener = [ptr](const WriteEvent &event, const details::WriteReq &) {
ptr->publish(event);
};
req->template once<ErrorEvent>(errorEventListener);
req->template once<WriteEvent>(writeEventListener);
req->write(this->template get<uv_stream_t>(), send.template get<uv_stream_t>());
}
/**
* @brief Queues a write request if it can be completed immediately.
*
* Same as `write()`, but wont queue a write request if it cant be
* completed immediately.<br/>
* An ErrorEvent event will be emitted in case of errors.
*
* @param data The data to be written to the stream.
* @param len The lenght of the submitted data.
* @return Number of bytes written.
*/
int tryWrite(std::unique_ptr<char[]> data, unsigned int len) {
uv_buf_t bufs[] = { uv_buf_init(data.get(), len) };
auto bw = uv_try_write(this->template get<uv_stream_t>(), bufs, 1);
if(bw < 0) {
this->publish(ErrorEvent{bw});
bw = 0;
}
return bw;
}
/**
* @brief Queues a write request if it can be completed immediately.
*
* Same as `write()`, but wont queue a write request if it cant be
* completed immediately.<br/>
* An ErrorEvent event will be emitted in case of errors.
*
* @param data The data to be written to the stream.
* @param len The lenght of the submitted data.
* @return Number of bytes written.
*/
int tryWrite(char *data, unsigned int len) {
uv_buf_t bufs[] = { uv_buf_init(data, len) };
auto bw = uv_try_write(this->template get<uv_stream_t>(), bufs, 1);
if(bw < 0) {
this->publish(ErrorEvent{bw});
bw = 0;
}
return bw;
}
/**
* @brief Checks if the stream is readable.
* @return True if the stream is readable, false otherwise.
*/
bool readable() const noexcept {
return (uv_is_readable(this->template get<uv_stream_t>()) == 1);
}
/**
* @brief Checks if the stream is writable.
* @return True if the stream is writable, false otherwise.
*/
bool writable() const noexcept {
return (uv_is_writable(this->template get<uv_stream_t>()) == 1);
}
/**
* @brief Enables or disables blocking mode for a stream.
*
* When blocking mode is enabled all writes complete synchronously. The
* interface remains unchanged otherwise, e.g. completion or failure of the
* operation will still be reported through events which are emitted
* asynchronously.
*
* See the official
* [documentation](http://docs.libuv.org/en/v1.x/stream.html#c.uv_stream_set_blocking)
* for further details.
*
* @param enable True to enable blocking mode, false otherwise.
* @return True in case of success, false otherwise.
*/
bool blocking(bool enable = false) {
return (0 == uv_stream_set_blocking(this->template get<uv_stream_t>(), enable));
}
/**
* @brief Gets the amount of queued bytes waiting to be sent.
* @return Amount of queued bytes waiting to be sent.
*/
size_t writeQueueSize() const noexcept {
return uv_stream_get_write_queue_size(this->template get<uv_stream_t>());
}
};
}

29
src/uvw/stream.ipp Normal file
View File

@ -0,0 +1,29 @@
#include "config.h"
namespace uvw {
UVW_INLINE data_event::data_event(std::unique_ptr<char[]> buf, std::size_t len) noexcept
: data{std::move(buf)},
length{len} {}
UVW_INLINE void details::connect_req::connect_callback(uv_connect_t *req, int status) {
if(auto ptr = reserve(req); status) {
ptr->publish(error_event{status});
} else {
ptr->publish(connect_event{});
}
}
UVW_INLINE void details::shutdown_req::shoutdown_callback(uv_shutdown_t *req, int status) {
if(auto ptr = reserve(req); status) {
ptr->publish(error_event{status});
} else {
ptr->publish(shutdown_event{});
}
}
UVW_INLINE int details::shutdown_req::shutdown(uv_stream_t *hndl) {
return this->leak_if(uv_shutdown(raw(), hndl, &shoutdown_callback));
}
} // namespace uvw

2
src/uvw/tcp.cpp Normal file
View File

@ -0,0 +1,2 @@
#include "tcp.h"
#include "tcp.ipp"

233
src/uvw/tcp.h Normal file
View File

@ -0,0 +1,233 @@
#ifndef UVW_TCP_INCLUDE_H
#define UVW_TCP_INCLUDE_H
#include <chrono>
#include <memory>
#include <string>
#include <type_traits>
#include <utility>
#include <uv.h>
#include "config.h"
#include "enum.hpp"
#include "request.hpp"
#include "stream.h"
#include "util.h"
namespace uvw {
namespace details {
enum class uvw_tcp_flags : std::underlying_type_t<uv_tcp_flags> {
IPV6ONLY = UV_TCP_IPV6ONLY,
UVW_ENUM = 0
};
}
/**
* @brief The TCP handle.
*
* TCP handles are used to represent both TCP streams and servers.<br/>
* By default, _ipv4_ is used as a template parameter. The handle already
* supports _IPv6_ out-of-the-box by using `uvw::ipv6`.
*
* To create a `tcp_handle` through a `loop`, arguments follow:
*
* * An optional integer value that indicates the flags used to initialize
* the socket.
*
* See the official
* [documentation](http://docs.libuv.org/en/v1.x/tcp.html#c.uv_tcp_init_ex)
* for further details.
*/
class tcp_handle final: public stream_handle<tcp_handle, uv_tcp_t> {
public:
using time = std::chrono::duration<unsigned int>;
using tcp_flags = details::uvw_tcp_flags;
using ipv4 = uvw::ipv4;
using ipv6 = uvw::ipv6;
explicit tcp_handle(loop::token token, std::shared_ptr<loop> ref, unsigned int f = {});
/**
* @brief Initializes the handle. No socket is created as of yet.
* @return Underlying return value.
*/
int init();
/**
* @brief Opens an existing file descriptor or SOCKET as a TCP handle.
*
* The passed file descriptor or SOCKET is not checked for its type, but
* its required that it represents a valid stream socket.
*
* @param socket A valid socket handle (either a file descriptor or a
* SOCKET).
*
* @return Underlying return value.
*/
int open(os_socket_handle socket);
/**
* @brief Enables/Disables Nagles algorithm.
* @param value True to enable it, false otherwise.
* @return True in case of success, false otherwise.
*/
bool no_delay(bool value = false);
/**
* @brief Enables/Disables TCP keep-alive.
* @param enable True to enable it, false otherwise.
* @param val Initial delay in seconds (use
* `std::chrono::duration<unsigned int>`).
* @return True in case of success, false otherwise.
*/
bool keep_alive(bool enable = false, time val = time{0});
/**
* @brief Enables/Disables simultaneous asynchronous accept requests.
*
* Enables/Disables simultaneous asynchronous accept requests that are
* queued by the operating system when listening for new TCP
* connections.<br/>
* This setting is used to tune a TCP server for the desired performance.
* Having simultaneous accepts can significantly improve the rate of
* accepting connections (which is why it is enabled by default) but may
* lead to uneven load distribution in multi-process setups.
*
* @param enable True to enable it, false otherwise.
* @return True in case of success, false otherwise.
*/
bool simultaneous_accepts(bool enable = true);
/**
* @brief Binds the handle to an address and port.
*
* A successful call to this function does not guarantee that the call to
* `listen()` or `connect()` will work properly.
*
* Available flags are:
*
* * `tcp_handle::tcp_flags::IPV6ONLY`: it disables dual-stack support and
* only IPv6 is used.
*
* @param addr Initialized `sockaddr_in` or `sockaddr_in6` data structure.
* @param opts Optional additional flags.
* @return Underlying return value.
*/
int bind(const sockaddr &addr, tcp_flags opts = tcp_flags::UVW_ENUM);
/**
* @brief Binds the handle to an address and port.
*
* A successful call to this function does not guarantee that the call to
* `listen()` or `connect()` will work properly.
*
* Available flags are:
*
* * `tcp_handle::tcp_flags::IPV6ONLY`: it disables dual-stack support and
* only IPv6 is used.
*
* @param ip The address to which to bind.
* @param port The port to which to bind.
* @param opts Optional additional flags.
* @return Underlying return value.
*/
int bind(const std::string &ip, unsigned int port, tcp_flags opts = tcp_flags::UVW_ENUM);
/**
* @brief Binds the handle to an address and port.
*
* A successful call to this function does not guarantee that the call to
* `listen()` or `connect()` will work properly.
*
* Available flags are:
*
* * `tcp_handle::tcp_flags::IPV6ONLY`: it disables dual-stack support and
* only IPv6 is used.
*
* @param addr A valid instance of socket_address.
* @param opts Optional additional flags.
* @return Underlying return value.
*/
int bind(const socket_address &addr, tcp_flags opts = tcp_flags::UVW_ENUM);
/**
* @brief Gets the current address to which the handle is bound.
* @return A valid instance of socket_address, an empty one in case of
* errors.
*/
socket_address sock() const noexcept;
/**
* @brief Gets the address of the peer connected to the handle.
* @return A valid instance of socket_address, an empty one in case of
* errors.
*/
socket_address peer() const noexcept;
/**
* @brief Establishes an IPv4 or IPv6 TCP connection.
*
* On Windows if the addr is initialized to point to an unspecified address
* (`0.0.0.0` or `::`) it will be changed to point to localhost. This is
* done to match the behavior of Linux systems.
*
* A connect event is emitted when the connection has been established.
*
* @param addr Initialized `sockaddr_in` or `sockaddr_in6` data structure.
* @return Underlying return value.
*/
int connect(const sockaddr &addr);
/**
* @brief Establishes an IPv4 or IPv6 TCP connection.
*
* A connect event is emitted when the connection has been established.
*
* @param ip The address to which to bind.
* @param port The port to which to bind.
* @return Underlying return value.
*/
int connect(const std::string &ip, unsigned int port);
/**
* @brief Establishes an IPv4 or IPv6 TCP connection.
*
* A connect event is emitted when the connection has been established.
*
* @param addr A valid instance of socket_address.
* @return Underlying return value.
*/
int connect(const socket_address &addr);
/**
* @brief Resets a TCP connection by sending a RST packet.
*
* This is accomplished by setting the `SO_LINGER` socket option with a
* linger interval of zero and then calling `close`.<br/>
* Due to some platform inconsistencies, mixing of `shutdown` and
* `close_reset` calls is not allowed.
*
* A close event is emitted when the connection has been reset.
*
* @return Underlying return value.
*/
int close_reset();
private:
enum {
DEFAULT,
FLAGS
} tag;
unsigned int flags;
};
} // namespace uvw
#ifndef UVW_AS_LIB
# include "tcp.ipp"
#endif
#endif // UVW_TCP_INCLUDE_H

View File

@ -1,265 +0,0 @@
#pragma once
#include <type_traits>
#include <utility>
#include <memory>
#include <string>
#include <chrono>
#include <uv.h>
#include "request.hpp"
#include "stream.hpp"
#include "util.hpp"
namespace uvw {
namespace details {
enum class UVTCPFlags: typename std::underlying_type<uv_tcp_flags>::type {
IPV6ONLY = UV_TCP_IPV6ONLY
};
}
/**
* @brief The TCPHandle handle.
*
* TCP handles are used to represent both TCP streams and servers.<br/>
* By default, _IPv4_ is used as a template parameter. The handle already
* supports _IPv6_ out-of-the-box by using `uvw::IPv6`.
*
* To create a `TCPHandle` through a `Loop`, arguments follow:
*
* * An optional integer value that indicates the flags used to initialize
* the socket.
*
* See the official
* [documentation](http://docs.libuv.org/en/v1.x/tcp.html#c.uv_tcp_init_ex)
* for further details.
*/
class TCPHandle final: public StreamHandle<TCPHandle, uv_tcp_t> {
public:
using Time = std::chrono::duration<unsigned int>;
using Bind = details::UVTCPFlags;
using IPv4 = uvw::IPv4;
using IPv6 = uvw::IPv6;
explicit TCPHandle(ConstructorAccess ca, std::shared_ptr<Loop> ref, unsigned int f = {})
: StreamHandle{ca, std::move(ref)}, tag{f ? FLAGS : DEFAULT}, flags{f}
{}
/**
* @brief Initializes the handle. No socket is created as of yet.
* @return True in case of success, false otherwise.
*/
bool init() {
return (tag == FLAGS)
? initialize(&uv_tcp_init_ex, flags)
: initialize(&uv_tcp_init);
}
/**
* @brief Opens an existing file descriptor or SOCKET as a TCP handle.
*
* The passed file descriptor or SOCKET is not checked for its type, but
* its required that it represents a valid stream socket.
*
* @param socket A valid socket handle (either a file descriptor or a SOCKET).
*/
void open(OSSocketHandle socket) {
invoke(&uv_tcp_open, get(), socket);
}
/**
* @brief Enables/Disables Nagles algorithm.
* @param value True to enable it, false otherwise.
* @return True in case of success, false otherwise.
*/
bool noDelay(bool value = false) {
return (0 == uv_tcp_nodelay(get(), value));
}
/**
* @brief Enables/Disables TCP keep-alive.
* @param enable True to enable it, false otherwise.
* @param time Initial delay in seconds (use
* `std::chrono::duration<unsigned int>`).
* @return True in case of success, false otherwise.
*/
bool keepAlive(bool enable = false, Time time = Time{0}) {
return (0 == uv_tcp_keepalive(get(), enable, time.count()));
}
/**
* @brief Enables/Disables simultaneous asynchronous accept requests.
*
* Enables/Disables simultaneous asynchronous accept requests that are
* queued by the operating system when listening for new TCP
* connections.<br/>
* This setting is used to tune a TCP server for the desired performance.
* Having simultaneous accepts can significantly improve the rate of
* accepting connections (which is why it is enabled by default) but may
* lead to uneven load distribution in multi-process setups.
*
* @param enable True to enable it, false otherwise.
* @return True in case of success, false otherwise.
*/
bool simultaneousAccepts(bool enable = true) {
return (0 == uv_tcp_simultaneous_accepts(get(), enable));
}
/**
* @brief Binds the handle to an address and port.
*
* A successful call to this function does not guarantee that the call to
* `listen()` or `connect()` will work properly.<br/>
* ErrorEvent events can be emitted because of either this function or the
* ones mentioned above.
*
* Available flags are:
*
* * `TCPHandle::Bind::IPV6ONLY`: it disables dual-stack support and only
* IPv6 is used.
*
* @param addr Initialized `sockaddr_in` or `sockaddr_in6` data structure.
* @param opts Optional additional flags.
*/
void bind(const sockaddr &addr, Flags<Bind> opts = Flags<Bind>{}) {
invoke(&uv_tcp_bind, get(), &addr, opts);
}
/**
* @brief Binds the handle to an address and port.
*
* A successful call to this function does not guarantee that the call to
* `listen()` or `connect()` will work properly.<br/>
* ErrorEvent events can be emitted because of either this function or the
* ones mentioned above.
*
* Available flags are:
*
* * `TCPHandle::Bind::IPV6ONLY`: it disables dual-stack support and only
* IPv6 is used.
*
* @param ip The address to which to bind.
* @param port The port to which to bind.
* @param opts Optional additional flags.
*/
template<typename I = IPv4>
void bind(std::string ip, unsigned int port, Flags<Bind> opts = Flags<Bind>{}) {
typename details::IpTraits<I>::Type addr;
details::IpTraits<I>::addrFunc(ip.data(), port, &addr);
bind(reinterpret_cast<const sockaddr &>(addr), std::move(opts));
}
/**
* @brief Binds the handle to an address and port.
*
* A successful call to this function does not guarantee that the call to
* `listen()` or `connect()` will work properly.<br/>
* ErrorEvent events can be emitted because of either this function or the
* ones mentioned above.
*
* Available flags are:
*
* * `TCPHandle::Bind::IPV6ONLY`: it disables dual-stack support and only
* IPv6 is used.
*
* @param addr A valid instance of Addr.
* @param opts Optional additional flags.
*/
template<typename I = IPv4>
void bind(Addr addr, Flags<Bind> opts = Flags<Bind>{}) {
bind<I>(std::move(addr.ip), addr.port, std::move(opts));
}
/**
* @brief Gets the current address to which the handle is bound.
* @return A valid instance of Addr, an empty one in case of errors.
*/
template<typename I = IPv4>
Addr sock() const noexcept {
return details::address<I>(&uv_tcp_getsockname, get());
}
/**
* @brief Gets the address of the peer connected to the handle.
* @return A valid instance of Addr, an empty one in case of errors.
*/
template<typename I = IPv4>
Addr peer() const noexcept {
return details::address<I>(&uv_tcp_getpeername, get());
}
/**
* @brief Establishes an IPv4 or IPv6 TCP connection.
*
* On Windows if the addr is initialized to point to an unspecified address
* (`0.0.0.0` or `::`) it will be changed to point to localhost. This is
* done to match the behavior of Linux systems.
*
* A ConnectEvent event is emitted when the connection has been
* established.<br/>
* An ErrorEvent event is emitted in case of errors during the connection.
*
* @param addr Initialized `sockaddr_in` or `sockaddr_in6` data structure.
*/
void connect(const sockaddr &addr) {
auto ptr = shared_from_this();
auto errorEventListener = [ptr](const ErrorEvent &event, const details::ConnectReq &) {
ptr->publish(event);
};
auto connectEventListener = [ptr](const ConnectEvent &event, const details::ConnectReq &) {
ptr->publish(event);
};
auto req = loop().resource<details::ConnectReq>();
req->once<ErrorEvent>(errorEventListener);
req->once<ConnectEvent>(connectEventListener);
req->connect(&uv_tcp_connect, get(), &addr);
}
/**
* @brief Establishes an IPv4 or IPv6 TCP connection.
*
* A ConnectEvent event is emitted when the connection has been
* established.<br/>
* An ErrorEvent event is emitted in case of errors during the connection.
*
* @param ip The address to which to bind.
* @param port The port to which to bind.
*/
template<typename I = IPv4>
void connect(std::string ip, unsigned int port) {
typename details::IpTraits<I>::Type addr;
details::IpTraits<I>::addrFunc(ip.data(), port, &addr);
connect(reinterpret_cast<const sockaddr &>(addr));
}
/**
* @brief Establishes an IPv4 or IPv6 TCP connection.
*
* A ConnectEvent event is emitted when the connection has been
* established.<br/>
* An ErrorEvent event is emitted in case of errors during the connection.
*
* @param addr A valid instance of Addr.
*/
template<typename I = IPv4>
void connect(Addr addr) {
connect<I>(std::move(addr.ip), addr.port);
}
private:
enum { DEFAULT, FLAGS } tag;
unsigned int flags;
};
}

Some files were not shown because too many files have changed in this diff Show More