diff --git a/docs/lifecycle.md b/docs/lifecycle.md index 2d090a8..5bcce02 100644 --- a/docs/lifecycle.md +++ b/docs/lifecycle.md @@ -15,10 +15,10 @@ stateDiagram-v2 direction LR [*] --> Suspend - Suspend --> Running : run - Suspend --> [*] : close + Suspend --> Running: run + Suspend --> [*]: close Running --> Suspend - Running --> [*] : close + Running --> [*]: close classDef attached color:#67C23A; classDef detached stroke:red,color:#F56C6C; @@ -47,9 +47,9 @@ stateDiagram-v2 direction LR [*] --> Hanging - Hanging --> Waiting : request - Waiting --> Dealing : response - Dealing --> Waiting : request + Hanging --> Waiting: request + Waiting --> Dealing: response + Dealing --> Waiting: request Dealing --> Hanging classDef attached color:#67C23A; @@ -73,18 +73,21 @@ However, it should be noted that `request` operations can only be performed in t ## Handle +> In the new version, the strategy for the lifecycle has been modified, and it no longer automatically "detaches". +> An explicit "detach" interface is provided, allowing the status to be explicitly adjusted to "detach" when needed. + Following is the life states of `handle`. ```mermaid stateDiagram-v2 direction LR - [*] --> Allocated : set_data - Allocated --> Suspend : init - Suspend --> Running : start - Suspend --> [*] : close + [*] --> Allocated: set_data + Allocated --> Suspend: init + Suspend --> Running: start + Suspend --> [*]: close Running --> Suspend: stop - Running --> [*] : close + Running --> [*]: close classDef attached color:#67C23A; classDef detached stroke:red,color:#F56C6C; diff --git a/examples/fs_event.cpp b/examples/fs_event.cpp index 9b0908a..52cdc46 100644 --- a/examples/fs_event.cpp +++ b/examples/fs_event.cpp @@ -28,7 +28,7 @@ int main() { }); // close fs_event - uv::timer_t().start(2 * 1000, 1).call([&]() { + uv::timer_t().start(2 * 1000, 1).detach().call([&]() { fs_event.close(nullptr); throw uvcxx::close_handle(); }); diff --git a/examples/idle.cpp b/examples/idle.cpp index 6873894..8233e6b 100644 --- a/examples/idle.cpp +++ b/examples/idle.cpp @@ -8,7 +8,7 @@ int main() { uv::loop_t loop; int64_t counter = 0; - uv::idle_t(loop).start().call([&]() { + uv::idle_t(loop).start().detach().call([&]() { counter++; if (counter >= 1000) { throw uvcxx::close_handle(); @@ -19,4 +19,4 @@ int main() { loop.run(); return 0; -} \ No newline at end of file +} diff --git a/examples/pipe.cpp b/examples/pipe.cpp index ef37bd2..4e04fa6 100644 --- a/examples/pipe.cpp +++ b/examples/pipe.cpp @@ -25,7 +25,7 @@ int main() { uv::pipe_t server(server_loop, false); server.bind(pipe_name); - server.listen(128).call([=]() mutable { + server.listen(128).detach().call([=]() mutable { auto conn = server.accept(false); uv::buf_t server_buf; @@ -33,7 +33,7 @@ int main() { server_buf.resize(size); *buf = server_buf; }); - conn.read_start().call([=](ssize_t nread, const uv_buf_t *buf) mutable { + conn.read_start().detach().call([=](ssize_t nread, const uv_buf_t *buf) mutable { std::cout << "server read: " << std::string(buf->base, nread) << std::endl; }).except([]() { }).except([=]() mutable { @@ -49,7 +49,7 @@ int main() { }); std::cout << "[INFO] stop server after " << server_time_ms << "ms" << std::endl; - uv::timer_t(server_loop).start(server_time_ms, 1).call([=]() mutable { + uv::timer_t(server_loop).start(server_time_ms, 1).detach().call([=]() mutable { server.close(nullptr); throw uvcxx::close_handle(); // close timer }); @@ -60,7 +60,7 @@ int main() { auto msg = uvcxx::catstr("hello~", i); uv::pipe_t client(client_loop, false); - client.connect(pipe_name).then([=]() mutable { + client.connect(pipe_name).detach().then([=]() mutable { client.write(msg).then([=]() { std::cout << "client write: " << msg << std::endl; }); diff --git a/examples/signal.cpp b/examples/signal.cpp index 4e14a7e..913848a 100644 --- a/examples/signal.cpp +++ b/examples/signal.cpp @@ -51,7 +51,7 @@ int main(int argc, const char *argv[]) { { // start backend uv::signal_t signal(loop); - signal.start(sig).call([key, &count](int) mutable { + signal.start(sig).detach().call([key, &count](int) mutable { ++count; std::cout << "Received " << key << " " << count << std::endl; throw uvcxx::close_handle(); @@ -61,9 +61,12 @@ int main(int argc, const char *argv[]) { uvcxx_assert(loop_handle_count(loop) == 0, "count = ", loop_handle_count(loop)); // start oneshot { - uv::signal_t(loop).start_oneshot(sig).then([key, &count](int) mutable { + uv::signal_t signal(loop); + signal.start_oneshot(sig).detach().then([key, &count](int) mutable { ++count; std::cout << "Received " << key << " " << count << std::endl; + }).finally([signal]() mutable { + signal.close(nullptr); }); } loop.run(); diff --git a/examples/tcp.cpp b/examples/tcp.cpp index b6c7f74..78663db 100644 --- a/examples/tcp.cpp +++ b/examples/tcp.cpp @@ -49,7 +49,7 @@ int main() { std::cout << "[INFO] server start listen: " << server_sock << std::endl; - server.listen(128).call([=]() mutable { + server.listen(128).detach().call([=]() mutable { auto conn = server.accept(false); std::cout << "[INFO] received [" << getsockname(conn) << " <- " << getpeername(conn) << "]" << std::endl; @@ -59,7 +59,7 @@ int main() { server_buf.resize(size); *buf = server_buf; }); - conn.read_start().call([=](ssize_t nread, const uv_buf_t *buf) mutable { + conn.read_start().detach().call([=](ssize_t nread, const uv_buf_t *buf) mutable { std::cout << "server read: " << std::string(buf->base, nread) << std::endl; }).except([]() { }).except([=]() mutable { @@ -73,7 +73,7 @@ int main() { }); std::cout << "[INFO] stop server after " << server_time_ms << "ms" << std::endl; - uv::timer_t(server_loop).start(server_time_ms, 1).call([=]() mutable { + uv::timer_t(server_loop).start(server_time_ms, 1).detach().call([=]() mutable { server.close(nullptr); throw uvcxx::close_handle(); // close timer }); @@ -86,7 +86,7 @@ int main() { auto msg = uvcxx::catstr("hello~", i); uv::tcp_t client(client_loop, false); - client.connect(addr).then([=]() mutable { + client.connect(addr).detach().then([=]() mutable { client.write(msg).then([=]() { std::cout << "client write: " << msg << std::endl; }); diff --git a/examples/timer.cpp b/examples/timer.cpp index 4c91124..a698346 100644 --- a/examples/timer.cpp +++ b/examples/timer.cpp @@ -6,7 +6,7 @@ #include int main() { - uv::timer_t().start(1000, 1000).call([]() { + uv::timer_t().start(1000, 1000).detach().call([]() { std::cout << "Hello~~~" << std::endl; throw uvcxx::close_handle(); }); diff --git a/examples/udp.cpp b/examples/udp.cpp index 85ab0ed..849c26a 100644 --- a/examples/udp.cpp +++ b/examples/udp.cpp @@ -48,7 +48,7 @@ int main() { server_buf.resize(size); *buf = server_buf; }); - server.recv_start().call( + server.recv_start().detach().call( [=](ssize_t nread, const uv_buf_t *buf, const sockaddr *addr, uv_udp_flags flags) mutable { std::cout << "server read: " << std::string(buf->base, nread) << " with " << uvcxx::to_string(uv_udp_flags(flags)) << " from " << uvcxx::any_address_t(addr) @@ -60,7 +60,7 @@ int main() { }); std::cout << "[INFO] stop server after " << server_time_ms << "ms" << std::endl; - uv::timer_t(server_loop).start(server_time_ms, 1).call([=]() mutable { + uv::timer_t(server_loop).start(server_time_ms, 1).detach().call([=]() mutable { server.close(nullptr); throw uvcxx::close_handle(); // close timer }); @@ -73,7 +73,7 @@ int main() { auto msg = uvcxx::catstr("hello~", i); uv::udp_t client(client_loop, false); - client.send(msg, addr).then([=]() { + client.send(msg, addr).detach().then([=]() { std::cout << "client write: " << msg << std::endl; }).finally([=]() mutable { client.close(nullptr); diff --git a/include/uvcxx.h b/include/uvcxx.h index 3f21e9d..486077f 100644 --- a/include/uvcxx.h +++ b/include/uvcxx.h @@ -8,6 +8,8 @@ #include "uvcxx/utils/apply.h" #include "uvcxx/utils/assert.h" +#include "uvcxx/utils/attached_callback.h" +#include "uvcxx/utils/attached_promise.h" #include "uvcxx/utils/callback.h" #include "uvcxx/utils/defer.h" #include "uvcxx/utils/detach.h" diff --git a/include/uvcxx/async.h b/include/uvcxx/async.h index 5094a1a..1ec73ea 100644 --- a/include/uvcxx/async.h +++ b/include/uvcxx/async.h @@ -16,7 +16,11 @@ namespace uv { async_t() { set_data(new data_t(*this)); //< data will be deleted in close action - _attach_data_(); + } + + self &detach() { + _detach_(); + return *this; } UVCXX_NODISCARD @@ -25,14 +29,14 @@ namespace uv { } UVCXX_NODISCARD - uvcxx::callback<> init(const loop_t &loop) { + uvcxx::attached_callback<> init(const loop_t &loop) { UVCXX_APPLY(uv_async_init(loop, *this, raw_callback), nullptr); - _detach_(); - return callback(); + _initialized_(); + return {*this, callback()}; } UVCXX_NODISCARD - uvcxx::callback<> init() { + uvcxx::attached_callback<> init() { return init(default_loop()); } diff --git a/include/uvcxx/check.h b/include/uvcxx/check.h index f7293ca..582364b 100644 --- a/include/uvcxx/check.h +++ b/include/uvcxx/check.h @@ -19,7 +19,12 @@ namespace uv { explicit check_t(const loop_t &loop) { set_data(new data_t(*this)); //< data will be deleted in close action (void) uv_check_init(loop, *this); - _attach_close_(); + _initialized_(); + } + + self &detach() { + _detach_(); + return *this; } UVCXX_NODISCARD @@ -28,15 +33,13 @@ namespace uv { } UVCXX_NODISCARD - uvcxx::callback<> start() { + uvcxx::attached_callback<> start() { UVCXX_APPLY(uv_check_start(*this, raw_callback), nullptr); - _detach_(); - return callback(); + return {*this, callback()}; } void stop() { (void) uv_check_stop(*this); - _attach_close_(); } private: diff --git a/include/uvcxx/fs_event.h b/include/uvcxx/fs_event.h index 4b43028..8bf5ec4 100644 --- a/include/uvcxx/fs_event.h +++ b/include/uvcxx/fs_event.h @@ -25,7 +25,12 @@ namespace uv { #endif set_data(new data_t(*this)); //< data will be deleted in close action (void) uv_fs_event_init(loop, *this); - _attach_close_(); + _initialized_(); + } + + self &detach() { + _detach_(); + return *this; } UVCXX_NODISCARD @@ -34,15 +39,13 @@ namespace uv { } UVCXX_NODISCARD - uvcxx::callback start(uvcxx::string path, int flags) { + uvcxx::attached_callback start(uvcxx::string path, int flags) { UVCXX_APPLY(uv_fs_event_start(*this, raw_callback, path, flags), nullptr); - _detach_(); - return callback(); + return {*this, callback()}; } void stop() { (void) uv_fs_event_stop(*this); - _attach_close_(); } int getpath(char *buffer, size_t *size) { diff --git a/include/uvcxx/fs_poll.h b/include/uvcxx/fs_poll.h index 6825f08..4aeb713 100644 --- a/include/uvcxx/fs_poll.h +++ b/include/uvcxx/fs_poll.h @@ -22,7 +22,12 @@ namespace uv { explicit fs_poll_t(const loop_t &loop) { set_data(new data_t(*this)); //< data will be deleted in close action (void) uv_fs_poll_init(loop, *this); - _attach_close_(); + _initialized_(); + } + + self &detach() { + _detach_(); + return *this; } UVCXX_NODISCARD @@ -31,15 +36,13 @@ namespace uv { } UVCXX_NODISCARD - uvcxx::callback start(uvcxx::string path, unsigned int interval) { + uvcxx::attached_callback start(uvcxx::string path, unsigned int interval) { UVCXX_APPLY(uv_fs_poll_start(*this, raw_callback, path, interval), nullptr); - _detach_(); - return callback(); + return {*this, callback()}; } void stop() { (void) uv_fs_poll_stop(*this); - _attach_close_(); } int getpath(char *buffer, size_t *size) const { diff --git a/include/uvcxx/handle.h b/include/uvcxx/handle.h index 2abfa04..2839284 100644 --- a/include/uvcxx/handle.h +++ b/include/uvcxx/handle.h @@ -9,7 +9,7 @@ #include #include "inner/base.h" -#include "utils/callback.h" +#include "utils/attached_callback.h" #include "utils/promise.h" #include "utils/detach.h" @@ -165,6 +165,10 @@ namespace uv { return data && ((data_t *) data)->magic == MAGIC; } + bool is_closed() { return m_closed.load(); } + + bool is_initialized() { return m_initialized.load(); } + public: explicit data_t(const handle_t &handle) : m_handle(handle.shared_raw()) { @@ -192,19 +196,28 @@ namespace uv { * @param close close cb, could be `uv_close` or `uv_tcp_close_reset`. * @return */ - bool close_for(const std::function &close) { + bool close_for(raw_t *raw, const std::function &close) { bool closed = false; if (!m_closed.compare_exchange_strong(closed, true)) return false; - close(raw_close_callback); + if (m_initialized) { + close(raw_close_callback); + return true; + } else { + raw_non_initialized_close(raw); + return false; + } + } - return true; + void open() { + m_initialized = true; } private: // store the instance of `handle` to avoid resource release caused by no external reference std::shared_ptr m_handle; + std::atomic m_initialized{false}; std::atomic m_closed{false}; }; @@ -217,6 +230,17 @@ namespace uv { uvcxx::defer reset_data([&]() { raw->data = nullptr; }); data->close_cb.resolve(); + data->close_cb.finalize(); + } + + static void raw_non_initialized_close(raw_t *raw) { + auto data = (data_t *) raw->data; + if (!data) return; + + uvcxx::defer delete_data(std::default_delete(), data); + uvcxx::defer reset_data([&]() { raw->data = nullptr; }); + + data->close_cb.finalize(); } protected: @@ -226,7 +250,7 @@ namespace uv { throw uvcxx::errcode(UV_EPERM, "close invalid libuvcxx handle"); } - (void) data->close_for(close); + (void) data->close_for(*this, close); _detach_(); return data->close_cb.promise(); @@ -241,7 +265,7 @@ namespace uv { std::rethrow_exception(p); } catch (const uvcxx::close_handle &) { auto data = (data_t *) handle->data; - data->close_for([handle](void (*cb)(raw_t *)) { + data->close_for(handle, [handle](void (*cb)(raw_t *)) { uv_close(handle, cb); }); return true; @@ -265,46 +289,28 @@ namespace uv { uvcxx::attach_t m_attach; protected: - void _attach_data_() { - m_attach.attach(this->attach_data()); + void _attach_() { + m_attach.attach(this->attach_finalize()); } - void _attach_close_() { - m_attach.attach(this->attach_close()); + void _initialized_() { + get_data()->open(); } void _detach_() { m_attach.detach(); } - uvcxx::attach_t::count_t _attach_count_() { - return m_attach.attach_count(); - } - - void _unref_attach_() { - m_attach.unref_attach(); - } + operator uvcxx::attach_t() { return m_attach; } private: - std::function attach_data() { - auto handle = raw(); - return [handle]() { - auto data = (data_t *) handle->data; - if (!data_t::is_it(data)) return; - data->close_for([&](void (*cb)(raw_t *)) { - // directly delete data and emit close promise - cb(handle); - }); - }; - } - - std::function attach_close() { + std::function attach_finalize() { auto handle = raw(); return [handle]() { auto data = (data_t *) handle->data; if (!data_t::is_it(data)) return; - data->close_for([&](void (*cb)(raw_t *)) { + data->close_for(handle, [&](void (*cb)(raw_t *)) { uv_close(handle, cb); }); }; @@ -323,6 +329,7 @@ namespace uv { inherit_handle_t() : supper(make_shared()) { this->set_data(nullptr); + this->_attach_(); // Default attached status } operator T *() { return this->template raw(); } diff --git a/include/uvcxx/idle.h b/include/uvcxx/idle.h index 99bdbf1..feb4eb8 100644 --- a/include/uvcxx/idle.h +++ b/include/uvcxx/idle.h @@ -19,7 +19,12 @@ namespace uv { explicit idle_t(const loop_t &loop) { set_data(new data_t(*this)); //< data will be deleted in close action (void) uv_idle_init(loop, *this); - _attach_close_(); + _initialized_(); + } + + self &detach() { + _detach_(); + return *this; } UVCXX_NODISCARD @@ -28,15 +33,13 @@ namespace uv { } UVCXX_NODISCARD - uvcxx::callback<> start() { + uvcxx::attached_callback<> start() { UVCXX_APPLY(uv_idle_start(*this, raw_callback), nullptr); - _detach_(); - return callback(); + return {*this, callback()}; } void stop() { (void) uv_idle_stop(*this); - _attach_close_(); } private: diff --git a/include/uvcxx/pipe.h b/include/uvcxx/pipe.h index b6c612d..8fb0c30 100644 --- a/include/uvcxx/pipe.h +++ b/include/uvcxx/pipe.h @@ -7,6 +7,7 @@ #define LIBUVCXX_PIPE_H #include "cxx/string.h" +#include "utils/attached_promise.h" #include "connect.h" #include "stream.h" @@ -24,7 +25,12 @@ namespace uv { explicit pipe_t(const loop_t &loop, bool ipc) { set_data(new data_t(*this)); //< data will be deleted in close action (void) uv_pipe_init(loop, *this, int(ipc)); - _attach_close_(); + _initialized_(); + } + + self &detach() { + _detach_(); + return *this; } UVCXX_NODISCARD @@ -80,44 +86,38 @@ namespace uv { #endif UVCXX_NODISCARD - uvcxx::promise<> connect(const connect_t &req, uvcxx::string name) { + uvcxx::attached_promise<> connect(const connect_t &req, uvcxx::string name) { auto p = pipe_connect(req, *this, name); - if (p) { - data()->work_mode = WorkMode::Client; - _detach_(); - } - return p; + if (p) data()->work_mode = WorkMode::Client; + return {*this, p}; } UVCXX_NODISCARD - uvcxx::promise<> connect(uvcxx::string name) { + uvcxx::attached_promise<> connect(uvcxx::string name) { return this->connect({}, name); } #if UVCXX_SATISFY_VERSION(1, 46, 0) UVCXX_NODISCARD - uvcxx::promise<> connect2(const connect_t &req, const char *name, size_t namelen, unsigned int flags) { + uvcxx::attached_promise<> connect2(const connect_t &req, const char *name, size_t namelen, unsigned int flags) { auto p = pipe_connect2(req, *this, name, namelen, flags); - if (p) { - data()->work_mode = WorkMode::Client; - _detach_(); - } - return p; + if (p) data()->work_mode = WorkMode::Client; + return {*this, p}; } UVCXX_NODISCARD - uvcxx::promise<> connect2(const char *name, size_t namelen, unsigned int flags) { + uvcxx::attached_promise<> connect2(const char *name, size_t namelen, unsigned int flags) { return this->connect2({}, name, namelen, flags); } UVCXX_NODISCARD - uvcxx::promise<> connect2(const connect_t &req, uvcxx::string_view name, unsigned int flags) { + uvcxx::attached_promise<> connect2(const connect_t &req, uvcxx::string_view name, unsigned int flags) { return this->connect2(req, name.data, name.size, flags); } UVCXX_NODISCARD - uvcxx::promise<> connect2(uvcxx::string_view name, unsigned int flags) { + uvcxx::attached_promise<> connect2(uvcxx::string_view name, unsigned int flags) { return this->connect2({}, name.data, name.size, flags); } diff --git a/include/uvcxx/poll.h b/include/uvcxx/poll.h index 5930ac4..69aa339 100644 --- a/include/uvcxx/poll.h +++ b/include/uvcxx/poll.h @@ -16,32 +16,34 @@ namespace uv { poll_t() { set_data(new data_t(*this)); //< data will be deleted in close action - _attach_data_(); + } + + self &detach() { + _detach_(); + return *this; } self &init(const loop_t &loop, int fd) { auto data = get_data(); - if (data->initialized) { + if (data->is_initialized()) { UVCXX_THROW_OR_RETURN(UV_EINVAL, nullptr, "duplicated poll_t initialization"); } // To directly start after init, there is no path to return the error code instead. // So an exception is directly thrown. This feature may be modified in the future. UVCXX_APPLY_STRICT(uv_poll_init(loop, *this, fd)); - _attach_close_(); - data->initialized = true; + _initialized_(); return *this; } self &init_socket(const loop_t &loop, uv_os_sock_t socket) { auto data = get_data(); - if (data->initialized) { + if (data->is_initialized()) { UVCXX_THROW_OR_RETURN(UV_EINVAL, nullptr, "duplicated poll_t initialization"); } // To directly start after init, there is no path to return the error code instead. // So an exception is directly thrown. This feature may be modified in the future. UVCXX_APPLY_STRICT(uv_poll_init_socket(loop, *this, socket)); - _attach_close_(); - data->initialized = true; + _initialized_(); return *this; } @@ -59,28 +61,27 @@ namespace uv { } UVCXX_NODISCARD - uvcxx::callback start(int events) { + uvcxx::attached_callback start(int events) { auto data = get_data(); - if (!data->initialized) { + if (!data->is_initialized()) { UVCXX_THROW_OR_RETURN(UV_EINVAL, nullptr, "should call `init` or `init_socket` first"); } UVCXX_APPLY(uv_poll_start(*this, events, raw_callback), nullptr); _detach_(); - return data->start_cb.callback(); + return {*this, data->start_cb.callback()}; } void stop() { auto data = get_data(); - if (!data->initialized) { + if (!data->is_initialized()) { UVCXX_THROW_OR_RETURN(UV_EINVAL, nullptr, "should call `init` or `init_socket` first"); } (void) uv_poll_stop(*this); - _attach_close_(); } private: @@ -100,9 +101,8 @@ namespace uv { } } - class data_t : supper::data_t { + class data_t : public supper::data_t { public: - bool initialized{false}; uvcxx::callback_emitter start_cb; explicit data_t(poll_t &handle) diff --git a/include/uvcxx/prepare.h b/include/uvcxx/prepare.h index 3a8b9be..ad33a18 100644 --- a/include/uvcxx/prepare.h +++ b/include/uvcxx/prepare.h @@ -19,7 +19,12 @@ namespace uv { explicit prepare_t(const loop_t &loop) { set_data(new data_t(*this)); //< data will be deleted in close action (void) uv_prepare_init(loop, *this); - _attach_close_(); + _initialized_(); + } + + self &detach() { + _detach_(); + return *this; } UVCXX_NODISCARD @@ -28,15 +33,13 @@ namespace uv { } UVCXX_NODISCARD - uvcxx::callback<> start() { + uvcxx::attached_callback<> start() { UVCXX_APPLY(uv_prepare_start(*this, raw_callback), nullptr); - _detach_(); - return callback(); + return {*this, callback()}; } void stop() { (void) uv_prepare_stop(*this); - _attach_close_(); } private: diff --git a/include/uvcxx/process.h b/include/uvcxx/process.h index 5ebcc1b..49947e6 100644 --- a/include/uvcxx/process.h +++ b/include/uvcxx/process.h @@ -235,7 +235,6 @@ namespace uv { process_t() { set_data(new data_t(*this)); - _attach_data_(); } UVCXX_NODISCARD @@ -276,7 +275,7 @@ namespace uv { data->exit_cb.resolve(exit_status, term_signal); - data->close_for([&](void (*cb)(uv_handle_t *)) { + data->close_for((uv_handle_t *) handle, [&](void (*cb)(uv_handle_t *)) { uv_close((uv_handle_t *) handle, cb); }); } diff --git a/include/uvcxx/signal.h b/include/uvcxx/signal.h index a29984c..186625f 100644 --- a/include/uvcxx/signal.h +++ b/include/uvcxx/signal.h @@ -6,6 +6,8 @@ #ifndef LIBUVCXX_SIGNAL_H #define LIBUVCXX_SIGNAL_H +#include "utils/attached_promise.h" + #include "handle.h" namespace uv { @@ -19,7 +21,12 @@ namespace uv { explicit signal_t(const loop_t &loop) { set_data(new data_t(*this)); //< data will be deleted in close action (void) uv_signal_init(loop, *this); - _attach_close_(); + _initialized_(); + } + + self &detach() { + _detach_(); + return *this; } UVCXX_NODISCARD @@ -33,54 +40,26 @@ namespace uv { } UVCXX_NODISCARD - uvcxx::callback start(int signum) { + uvcxx::attached_callback start(int signum) { UVCXX_APPLY(uv_signal_start(*this, raw_callback, signum), nullptr); - _detach_(); - return callback(); + return {*this, callback()}; } #if UVCXX_SATISFY_VERSION(1, 12, 0) UVCXX_NODISCARD - uvcxx::promise start_oneshot(int signum) { + uvcxx::attached_promise start_oneshot(int signum) { UVCXX_APPLY(uv_signal_start_oneshot(*this, raw_oneshot_callback, signum), nullptr); - - _detach_(); - - auto attachment = *this; - return get_data()->start_oneshot_cb.promise().finally([attachment]() mutable { - finally_recycle_oneshot(attachment); - }); + return {*this, get_data()->start_oneshot_cb.promise()}; } #endif int stop() { UVCXX_APPLY(uv_signal_stop(*this), status); - _attach_close_(); return 0; } -#if UVCXX_SATISFY_VERSION(1, 12, 0) - - private: - /** - * recycle grab signal_t. - * if use_count == 1, close signal, because the `callback` and `signal` can not be reached ever again. - * if use_count > 1, attach_close and unref_attach, telling other refs that its attached again. and i'm done. - * @param grab - */ - static inline void finally_recycle_oneshot(signal_t &grab) { - if (grab._attach_count_() == 1) { - grab.close(nullptr); - } else { - grab._attach_close_(); - grab._unref_attach_(); - } - } - -#endif - private: static void raw_callback(raw_t *handle, int signum) { auto data = (data_t *) (handle->data); diff --git a/include/uvcxx/stream.h b/include/uvcxx/stream.h index 70ebe35..d5c3e97 100644 --- a/include/uvcxx/stream.h +++ b/include/uvcxx/stream.h @@ -52,7 +52,7 @@ namespace uv { * @throws E_ADDRINUSE, E_BADF, E_NOTSOCK */ UVCXX_NODISCARD - uvcxx::callback<> listen(int backlog) { + uvcxx::attached_callback<> listen(int backlog) { auto data = get_data(); if (data->work_mode != WorkMode::Notset && data->work_mode != WorkMode::Server) { UVCXX_THROW_OR_RETURN(UV_EPERM, nullptr, "can not listen ", work_mode_string(), " stream"); @@ -61,8 +61,7 @@ namespace uv { UVCXX_APPLY(uv_listen(*this, backlog, raw_listen_callback), nullptr); data->work_mode = WorkMode::Server; - _detach_(); - return data->listen_cb.callback(); + return {*this, data->listen_cb.callback()}; } UVCXX_NODISCARD @@ -77,7 +76,6 @@ namespace uv { */ UVCXX_NODISCARD uvcxx::callback alloc() { - // this alloc is not under Running state, so no `_detach_` applied. // memory alloc can not register multi-times callback // use call(nullptr) to avoid call alloc multi times. return get_data()->alloc_cb.callback().call(nullptr); @@ -98,7 +96,7 @@ namespace uv { * @throws E_EOF, E_AGAIN */ UVCXX_NODISCARD - uvcxx::callback read_start() { + uvcxx::attached_callback read_start() { auto data = get_data(); if (data->work_mode == WorkMode::Server) { UVCXX_THROW_OR_RETURN(UV_EPERM, nullptr, "can not read ", work_mode_string(), " stream"); @@ -106,8 +104,7 @@ namespace uv { UVCXX_APPLY(uv_read_start(*this, raw_alloc_callback, raw_read_callback), nullptr); - _detach_(); - return data->read_cb.callback(); + return {*this, data->read_cb.callback()}; } int read_stop() { @@ -116,10 +113,7 @@ namespace uv { UVCXX_THROW_OR_RETURN(UV_EPERM, nullptr, "can not stop ", work_mode_string(), " stream"); } - UVCXX_APPLY(uv_read_stop(*this), status); - - _attach_close_(); - return 0; + UVCXX_PROXY(uv_read_stop(*this)); } UVCXX_NODISCARD diff --git a/include/uvcxx/tcp.h b/include/uvcxx/tcp.h index 039b9ac..27ce8cc 100644 --- a/include/uvcxx/tcp.h +++ b/include/uvcxx/tcp.h @@ -6,6 +6,8 @@ #ifndef LIBUVCXX_TCP_H #define LIBUVCXX_TCP_H +#include "utils/attached_promise.h" + #include "connect.h" #include "stream.h" @@ -20,7 +22,7 @@ namespace uv { explicit tcp_t(const loop_t &loop) { set_data(new data_t(*this)); //< data will be deleted in close action (void) uv_tcp_init(loop, *this); - _attach_close_(); + _initialized_(); } #if UVCXX_SATISFY_VERSION(1, 7, 0) @@ -30,11 +32,16 @@ namespace uv { explicit tcp_t(const loop_t &loop, int flags) { set_data(new data_t(*this)); //< data will be deleted in close action (void) uv_tcp_init_ex(loop, *this, flags); - _attach_close_(); + _initialized_(); } #endif + self &detach() { + _detach_(); + return *this; + } + int send_buffer_size(int *value) { UVCXX_PROXY(uv_send_buffer_size(*this, value)); } @@ -101,17 +108,14 @@ namespace uv { } UVCXX_NODISCARD - uvcxx::promise<> connect(const connect_t &req, const sockaddr *addr) { + uvcxx::attached_promise<> connect(const connect_t &req, const sockaddr *addr) { auto p = tcp_connect(req, *this, addr); - if (p) { - data()->work_mode = WorkMode::Client; - _detach_(); - } - return p; + if (p) data()->work_mode = WorkMode::Client; + return {*this, p}; } UVCXX_NODISCARD - uvcxx::promise<> connect(const sockaddr *addr) { + uvcxx::attached_promise<> connect(const sockaddr *addr) { return this->connect({}, addr); } diff --git a/include/uvcxx/timer.h b/include/uvcxx/timer.h index 3bceec9..5f35a36 100644 --- a/include/uvcxx/timer.h +++ b/include/uvcxx/timer.h @@ -19,7 +19,12 @@ namespace uv { explicit timer_t(const loop_t &loop) { set_data(new data_t(*this)); //< data will be deleted in close action (void) uv_timer_init(loop, *this); - _attach_close_(); + _initialized_(); + } + + self &detach() { + _detach_(); + return *this; } UVCXX_NODISCARD @@ -28,15 +33,14 @@ namespace uv { } UVCXX_NODISCARD - uvcxx::callback<> start(uint64_t timeout, uint64_t repeat) { + uvcxx::attached_callback<> start(uint64_t timeout, uint64_t repeat) { UVCXX_APPLY(uv_timer_start(*this, raw_callback, timeout, repeat), nullptr); _detach_(); - return callback(); + return {*this, callback()}; } int stop() { UVCXX_APPLY(uv_timer_stop(*this), status); - _attach_close_(); return 0; } diff --git a/include/uvcxx/tty.h b/include/uvcxx/tty.h index 0777691..9323230 100644 --- a/include/uvcxx/tty.h +++ b/include/uvcxx/tty.h @@ -22,7 +22,7 @@ namespace uv { set_data(new data_t(*this)); //< data will be deleted in close action (void) uv_tty_init(loop, *this, fd, unused); data()->work_mode = WorkMode::Agent; - _attach_close_(); + _initialized_(); } #else @@ -33,11 +33,16 @@ namespace uv { set_data(new data_t(*this)); //< data will be deleted in close action (void) uv_tty_init(loop, *this, fd, readable); data()->work_mode = WorkMode::Agent; - _attach_close_(); + _initialized_(); } #endif + self &detach() { + _detach_(); + return *this; + } + int fileno(uv_os_fd_t *fd) const { UVCXX_PROXY(uv_fileno(*this, fd)); } diff --git a/include/uvcxx/udp.h b/include/uvcxx/udp.h index d09d8b2..15f207e 100644 --- a/include/uvcxx/udp.h +++ b/include/uvcxx/udp.h @@ -8,6 +8,7 @@ #include "cxx/buffer.h" #include "cxx/string.h" +#include "utils/attached_promise.h" #include "handle.h" #include "udp_send.h" @@ -25,7 +26,7 @@ namespace uv { explicit udp_t(const loop_t &loop) { set_data(new data_t(*this)); //< data will be deleted in close action (void) uv_udp_init(loop, *this); - _attach_close_(); + _initialized_(); } #if UVCXX_SATISFY_VERSION(1, 7, 0) @@ -35,11 +36,16 @@ namespace uv { explicit udp_t(const loop_t &loop, int flags) { set_data(new data_t(*this)); //< data will be deleted in close action (void) uv_udp_init_ex(loop, *this, flags); - _attach_close_(); + _initialized_(); } #endif + self &detach() { + _detach_(); + return *this; + } + UVCXX_NODISCARD size_t send_queue_size() const { return raw()->send_queue_size; @@ -125,18 +131,18 @@ namespace uv { } UVCXX_NODISCARD - uvcxx::promise<> send( + uvcxx::attached_promise<> send( const udp_send_t &req, const uv_buf_t bufs[], unsigned int nbufs, const sockaddr *addr = nullptr) { - return udp_send(req, *this, bufs, nbufs, addr); + return {*this, udp_send(req, *this, bufs, nbufs, addr)}; } UVCXX_NODISCARD - uvcxx::promise<> send(const udp_send_t &req, uvcxx::buffer buf, const sockaddr *addr = nullptr) { + uvcxx::attached_promise<> send(const udp_send_t &req, uvcxx::buffer buf, const sockaddr *addr = nullptr) { return this->send(req, &buf.buf, 1, addr); } UVCXX_NODISCARD - uvcxx::promise<> send( + uvcxx::attached_promise<> send( const udp_send_t &req, std::initializer_list bufs, const sockaddr *addr = nullptr) { std::vector buffers; buffers.reserve(bufs.size()); @@ -145,17 +151,17 @@ namespace uv { } UVCXX_NODISCARD - uvcxx::promise<> send(const uv_buf_t bufs[], unsigned int nbufs, const sockaddr *addr = nullptr) { + uvcxx::attached_promise<> send(const uv_buf_t bufs[], unsigned int nbufs, const sockaddr *addr = nullptr) { return this->send({}, bufs, nbufs, addr); } UVCXX_NODISCARD - uvcxx::promise<> send(uvcxx::buffer buf, const sockaddr *addr = nullptr) { + uvcxx::attached_promise<> send(uvcxx::buffer buf, const sockaddr *addr = nullptr) { return this->send(&buf.buf, 1, addr); } UVCXX_NODISCARD - uvcxx::promise<> send(std::initializer_list bufs, const sockaddr *addr = nullptr) { + uvcxx::attached_promise<> send(std::initializer_list bufs, const sockaddr *addr = nullptr) { std::vector buffers; buffers.reserve(bufs.size()); for (auto &buf: bufs) { buffers.emplace_back(buf.buf); } @@ -201,11 +207,11 @@ namespace uv { } UVCXX_NODISCARD - uvcxx::callback + uvcxx::attached_callback recv_start() { UVCXX_APPLY(uv_udp_recv_start(*this, raw_alloc_callback, raw_recv_callback), nullptr); _detach_(); - return recv_callback(); + return {*this, recv_callback()}; } #if UVCXX_SATISFY_VERSION(1, 39, 0) @@ -218,9 +224,7 @@ namespace uv { #endif int recv_stop() { - UVCXX_APPLY(uv_udp_recv_stop(*this), status); - _attach_close_(); - return 0; + UVCXX_PROXY(uv_udp_recv_stop(*this)); } #if UVCXX_SATISFY_VERSION(1, 19, 0) diff --git a/include/uvcxx/utils/attached_callback.h b/include/uvcxx/utils/attached_callback.h new file mode 100644 index 0000000..5127549 --- /dev/null +++ b/include/uvcxx/utils/attached_callback.h @@ -0,0 +1,33 @@ +// +// Created by Levalup. +// L.eval: Let programmer get rid of only work jobs. +// + +#ifndef LIBUVCXX_ATTACHED_CALLBACK_H +#define LIBUVCXX_ATTACHED_CALLBACK_H + +#include "callback.h" +#include "detach.h" + +namespace uvcxx { + template + class attached_callback : public callback { + public: + using self = attached_callback; + using supper = callback; + + attached_callback(attach_t attach, supper cb) + : supper(std::move(cb)), m_attach(std::move(attach)) { + } + + callback &detach() { + m_attach.detach(); + return *this; + } + + private: + uvcxx::attach_t m_attach; + }; +} + +#endif //LIBUVCXX_ATTACHED_CALLBACK_H diff --git a/include/uvcxx/utils/attached_promise.h b/include/uvcxx/utils/attached_promise.h new file mode 100644 index 0000000..0996641 --- /dev/null +++ b/include/uvcxx/utils/attached_promise.h @@ -0,0 +1,33 @@ +// +// Created by Levalup. +// L.eval: Let programmer get rid of only work jobs. +// + +#ifndef LIBUVCXX_ATTACHED_PROMISE_H +#define LIBUVCXX_ATTACHED_PROMISE_H + +#include "promise.h" +#include "detach.h" + +namespace uvcxx { + template + class attached_promise : public promise { + public: + using self = attached_promise; + using supper = promise; + + attached_promise(attach_t attach, supper cb) + : supper(std::move(cb)), m_attach(std::move(attach)) { + } + + promise &detach() { + m_attach.detach(); + return *this; + } + + private: + uvcxx::attach_t m_attach; + }; +} + +#endif //LIBUVCXX_ATTACHED_PROMISE_H diff --git a/tests/attch.cpp b/tests/attch.cpp index 06b3d6f..708aa7a 100644 --- a/tests/attch.cpp +++ b/tests/attch.cpp @@ -28,7 +28,7 @@ int main() { int idle_times = 0; { uv::idle_t idle(loop); - (void) idle.start().call([&]() mutable { + (void) idle.start().detach().call([&]() mutable { ++idle_times; if (idle_times > 1) throw uvcxx::close_handle(); }); @@ -62,12 +62,12 @@ int main() { { // close it in any time after not use it uv::idle_t idle(loop); - (void) idle.start().call([&, idle]() mutable { + (void) idle.start().detach().call([&, idle]() mutable { ++idle_times; if (idle_times > 3) idle.close(nullptr); }); // especially using temporary objects - (void) uv::idle_t(loop).start().call([&]() mutable { + (void) uv::idle_t(loop).start().detach().call([&]() mutable { ++idle_times; if (idle_times > 3) throw uvcxx::close_handle(); }); diff --git a/tests/close_handle.cpp b/tests/close_handle.cpp index 3d1e88b..9f7729c 100644 --- a/tests/close_handle.cpp +++ b/tests/close_handle.cpp @@ -7,7 +7,7 @@ int main() { int v = 0; - uv::idle_t().start().call([&]() { + uv::idle_t().start().detach().call([&]() { ++v; throw uvcxx::close_handle(); });