diff --git a/src/uvw/handle.hpp b/src/uvw/handle.hpp index a6625f01..cb80ea4e 100644 --- a/src/uvw/handle.hpp +++ b/src/uvw/handle.hpp @@ -27,248 +27,248 @@ struct CloseEvent {}; * Base type for all `uvw` handle types. */ template -class Handle: public Resource, public BaseHandle { +class Handle: public Resource { protected: - static void closeCallback(uv_handle_t *handle) { - Handle &ref = *(static_cast(handle->data)); - auto ptr = ref.shared_from_this(); - (void)ptr; - ref.reset(); - ref.publish(CloseEvent{}); - } + static void closeCallback(uv_handle_t *handle) { + Handle &ref = *(static_cast(handle->data)); + auto ptr = ref.shared_from_this(); + (void)ptr; + ref.reset(); + ref.publish(CloseEvent{}); + } - static void allocCallback(uv_handle_t *, std::size_t suggested, uv_buf_t *buf) { - auto size = static_cast(suggested); - *buf = uv_buf_init(new char[size], size); - } + static void allocCallback(uv_handle_t *, std::size_t suggested, uv_buf_t *buf) { + auto size = static_cast(suggested); + *buf = uv_buf_init(new char[size], size); + } - template - bool initialize(F &&f, Args&&... args) { - if(!this->self()) { - auto err = std::forward(f)(this->parent(), this->get(), std::forward(args)...); + template + bool initialize(F &&f, Args&&... args) { + if(!this->self()) { + auto err = std::forward(f)(this->parent(), this->get(), std::forward(args)...); - if(err) { - this->publish(ErrorEvent{err}); - } else { - this->leak(); - } - } + if(err) { + this->publish(ErrorEvent{err}); + } else { + this->leak(); + } + } - return this->self(); - } + return this->self(); + } - template - void invoke(F &&f, Args&&... args) { - auto err = std::forward(f)(std::forward(args)...); - if(err) { Emitter::publish(ErrorEvent{err}); } - } + template + void invoke(F &&f, Args&&... args) { + auto err = std::forward(f)(std::forward(args)...); + if(err) { Emitter::publish(ErrorEvent{err}); } + } public: - using Resource::Resource; + using Resource::Resource; - /** - * @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. - */ - HandleCategory category() const noexcept override { - return HandleCategory{this->template get()->type}; - } + /** + * @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. + */ + HandleCategory category() const noexcept { + return HandleCategory{this->template get()->type}; + } - /** - * @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 users. - * - * @return The actual type of the handle. - */ - HandleType type() const noexcept override { - return Utilities::guessHandle(category()); - } + /** + * @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 users. + * + * @return The actual type of the handle. + */ + HandleType type() const noexcept { + return Utilities::guessHandle(category()); + } - /** - * @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 it’s 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()) == 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 it’s 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 { + return !(uv_is_active(this->template get()) == 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. - */ - bool closing() const noexcept override { - return !(uv_is_closing(this->template get()) == 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. + */ + bool closing() const noexcept { + return !(uv_is_closing(this->template get()) == 0); + } - /** - * @brief Request handle to be closed. - * - * This **must** be called on each handle before memory is released.
- * In-progress requests are cancelled and this can result in an ErrorEvent - * emitted. - * - * The handle will emit a CloseEvent when finished. - */ - void close() noexcept override { - if(!closing()) { - uv_close(this->template get(), &Handle::closeCallback); - } - } + /** + * @brief Request handle to be closed. + * + * This **must** be called on each handle before memory is released.
+ * In-progress requests are cancelled and this can result in an ErrorEvent + * emitted. + * + * The handle will emit a CloseEvent when finished. + */ + void close() noexcept { + if(!closing()) { + uv_close(this->template get(), &Handle::closeCallback); + } + } - /** - * @brief Reference the given handle. - * - * 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()); - } + /** + * @brief Reference the given handle. + * + * References are idempotent, that is, if a handle is already referenced + * calling this function again will have no effect. + */ + void reference() noexcept { + uv_ref(this->template get()); + } - /** - * @brief Unreference the given handle. - * - * 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()); - } + /** + * @brief Unreference the given handle. + * + * References are idempotent, that is, if a handle is not referenced calling + * this function again will have no effect. + */ + void unreference() noexcept { + uv_unref(this->template get()); + } - /** - * @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()) == 0); - } + /** + * @brief Checks if the given handle referenced. + * @return True if the handle referenced, false otherwise. + */ + bool referenced() const noexcept { + return !(uv_has_ref(this->template get()) == 0); + } - /** - * @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()->type); - } + /** + * @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()->type); + } - /** - * @brief Gets the size of the send buffer used for the socket. - * - * Gets the size of the send buffer that the operating system uses for the - * socket.
- * This function works for TCPHandle, PipeHandle and UDPHandle handles on - * Unix and for TCPHandle and UDPHandle handles on Windows.
- * 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. - */ - int sendBufferSize() { - int value = 0; - auto err = uv_send_buffer_size(this->template get(), &value); - return err ? 0 : value; - } + /** + * @brief Gets the size of the send buffer used for the socket. + * + * Gets the size of the send buffer that the operating system uses for the + * socket.
+ * This function works for TCPHandle, PipeHandle and UDPHandle handles on + * Unix and for TCPHandle and UDPHandle handles on Windows.
+ * 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. + */ + int sendBufferSize() { + int value = 0; + auto err = uv_send_buffer_size(this->template get(), &value); + return err ? 0 : value; + } - /** - * @brief Sets the size of the send buffer used for the socket. - * - * Sets the size of the send buffer that the operating system uses for the - * socket.
- * This function works for TCPHandle, PipeHandle and UDPHandle handles on - * Unix and for TCPHandle and UDPHandle handles on Windows.
- * Note that Linux will set double the size. - * - * @return True in case of success, false otherwise. - */ - bool sendBufferSize(int value) { - return (0 == uv_send_buffer_size(this->template get(), &value)); - } + /** + * @brief Sets the size of the send buffer used for the socket. + * + * Sets the size of the send buffer that the operating system uses for the + * socket.
+ * This function works for TCPHandle, PipeHandle and UDPHandle handles on + * Unix and for TCPHandle and UDPHandle handles on Windows.
+ * Note that Linux will set double the size. + * + * @return True in case of success, false otherwise. + */ + bool sendBufferSize(int value) { + return (0 == uv_send_buffer_size(this->template get(), &value)); + } - /** - * @brief Gets the size of the receive buffer used for the socket. - * - * Gets the size of the receive buffer that the operating system uses for - * the socket.
- * This function works for TCPHandle, PipeHandle and UDPHandle handles on - * Unix and for TCPHandle and UDPHandle handles on Windows.
- * 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. - */ - int recvBufferSize() { - int value = 0; - auto err = uv_recv_buffer_size(this->template get(), &value); - return err ? 0 : value; - } + /** + * @brief Gets the size of the receive buffer used for the socket. + * + * Gets the size of the receive buffer that the operating system uses for + * the socket.
+ * This function works for TCPHandle, PipeHandle and UDPHandle handles on + * Unix and for TCPHandle and UDPHandle handles on Windows.
+ * 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. + */ + int recvBufferSize() { + int value = 0; + auto err = uv_recv_buffer_size(this->template get(), &value); + return err ? 0 : value; + } - /** - * @brief Sets the size of the receive buffer used for the socket. - * - * Sets the size of the receive buffer that the operating system uses for - * the socket.
- * This function works for TCPHandle, PipeHandle and UDPHandle handles on - * Unix and for TCPHandle and UDPHandle handles on Windows.
- * Note that Linux will set double the size. - * - * @return True in case of success, false otherwise. - */ - bool recvBufferSize(int value) { - return (0 == uv_recv_buffer_size(this->template get(), &value)); - } + /** + * @brief Sets the size of the receive buffer used for the socket. + * + * Sets the size of the receive buffer that the operating system uses for + * the socket.
+ * This function works for TCPHandle, PipeHandle and UDPHandle handles on + * Unix and for TCPHandle and UDPHandle handles on Windows.
+ * Note that Linux will set double the size. + * + * @return True in case of success, false otherwise. + */ + bool recvBufferSize(int value) { + return (0 == uv_recv_buffer_size(this->template get(), &value)); + } - /** - * @brief Gets the platform dependent file descriptor equivalent. - * - * Supported handles: - * - * * TCPHandle - * * PipeHandle - * * TTYHandle - * * UDPHandle - * * PollHandle - * - * It will emit an ErrorEvent event if invoked on any other handle.
- * If a handle doesn’t have an attached file descriptor yet or the handle - * itself has been closed, an ErrorEvent event will be emitted. - * - * See the official - * [documentation](http://docs.libuv.org/en/v1.x/handle.html#c.uv_fileno) - * for further details. - * - * @return The file descriptor attached to the hande or a negative value in - * case of errors. - */ - OSFileDescriptor fd() const { - uv_os_fd_t fd; - uv_fileno(this->template get(), &fd); - return fd; - } + /** + * @brief Gets the platform dependent file descriptor equivalent. + * + * Supported handles: + * + * * TCPHandle + * * PipeHandle + * * TTYHandle + * * UDPHandle + * * PollHandle + * + * It will emit an ErrorEvent event if invoked on any other handle.
+ * If a handle doesn’t have an attached file descriptor yet or the handle + * itself has been closed, an ErrorEvent event will be emitted. + * + * See the official + * [documentation](http://docs.libuv.org/en/v1.x/handle.html#c.uv_fileno) + * for further details. + * + * @return The file descriptor attached to the hande or a negative value in + * case of errors. + */ + OSFileDescriptor fd() const { + uv_os_fd_t fd; + uv_fileno(this->template get(), &fd); + return fd; + } }; diff --git a/src/uvw/loop.cpp b/src/uvw/loop.cpp index 1ecce14a..ff6a8946 100644 --- a/src/uvw/loop.cpp +++ b/src/uvw/loop.cpp @@ -107,16 +107,6 @@ UVW_INLINE void Loop::update() const noexcept { } -UVW_INLINE void Loop::walk(std::function callback) { - // remember: non-capturing lambdas decay to pointers to functions - uv_walk(loop.get(), [](uv_handle_t *handle, void *func) { - BaseHandle &ref = *static_cast(handle->data); - std::function &f = *static_cast *>(func); - f(ref); - }, &callback); -} - - UVW_INLINE void Loop::fork() noexcept { auto err = uv_loop_fork(loop.get()); diff --git a/src/uvw/loop.h b/src/uvw/loop.h index 98247daf..ad19b6c2 100644 --- a/src/uvw/loop.h +++ b/src/uvw/loop.h @@ -19,6 +19,22 @@ namespace uvw { +class AsyncHandle; +class CheckHandle; +class FsEventHandle; +class FsPollHandle; +class IdleHandle; +class PipeHandle; +class PollHandle; +class PrepareHandle; +class ProcessHandle; +class SignalHandle; +class TCPHandle; +class TimerHandle; +class TTYHandle; +class UDPHandle; + + namespace details { @@ -37,102 +53,6 @@ enum class UVRunMode: std::underlying_type_t { } -/** - * @brief Untyped handle class - * - * Handles' types are unknown from the point of view of the loop.
- * Anyway, a loop maintains a list of all the associated handles and let the - * users walk them as untyped instances.
- * This can help to end all the pending requests by closing the handles. - */ -struct BaseHandle { - virtual ~BaseHandle() = default; - /** - * @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 it’s 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.
- * In-progress requests are cancelled and this can result in an ErrorEvent - * emitted. - */ - virtual void close() noexcept = 0; -}; - - /** * @brief The Loop class. * @@ -147,6 +67,18 @@ class Loop final: public Emitter, public std::enable_shared_from_this friend class Resource; + template + auto create_resource(int, Args&&... args) -> decltype(std::declval().init(), std::shared_ptr{}) { + auto ptr = R::create(shared_from_this(), std::forward(args)...); + ptr = ptr->init() ? ptr : nullptr; + return ptr; + } + + template + std::shared_ptr create_resource(char, Args&&... args) { + return R::create(shared_from_this(), std::forward(args)...); + } + Loop(std::unique_ptr ptr) noexcept; public: @@ -228,13 +160,7 @@ public: */ template std::shared_ptr resource(Args&&... args) { - if constexpr(std::is_base_of_v) { - auto ptr = R::create(shared_from_this(), std::forward(args)...); - ptr = ptr->init() ? ptr : nullptr; - return ptr; - } else { - return R::create(shared_from_this(), std::forward(args)...); - } + return create_resource(0, std::forward(args)...); } /** @@ -335,7 +261,62 @@ public: * * @param callback A function to be invoked once for each active handle. */ - void walk(std::function callback); + template + void walk(Func callback) { + // remember: non-capturing lambdas decay to pointers to functions + uv_walk(loop.get(), [](uv_handle_t *handle, void *func) { + auto &cb = *static_cast(func); + + switch(Utilities::guessHandle(HandleCategory{handle->type})) { + case HandleType::ASYNC: + cb(*static_cast(handle->data)); + break; + case HandleType::CHECK: + cb(*static_cast(handle->data)); + break; + case HandleType::FS_EVENT: + cb(*static_cast(handle->data)); + break; + case HandleType::FS_POLL: + cb(*static_cast(handle->data)); + break; + case HandleType::IDLE: + cb(*static_cast(handle->data)); + break; + case HandleType::PIPE: + cb(*static_cast(handle->data)); + break; + case HandleType::POLL: + cb(*static_cast(handle->data)); + break; + case HandleType::PREPARE: + cb(*static_cast(handle->data)); + break; + case HandleType::PROCESS: + cb(*static_cast(handle->data)); + break; + case HandleType::SIGNAL: + cb(*static_cast(handle->data)); + break; + case HandleType::TCP: + cb(*static_cast(handle->data)); + break; + case HandleType::TIMER: + cb(*static_cast(handle->data)); + break; + case HandleType::TTY: + cb(*static_cast(handle->data)); + break; + case HandleType::UDP: + cb(*static_cast(handle->data)); + break; + default: + // returns the underlying handle, uvw doesn't manage it properly yet + cb(handle); + break; + } + }, &callback); + } /** * @brief Reinitialize any kernel state necessary in the child process after diff --git a/src/uvw/util.h b/src/uvw/util.h index b9514af4..49112da9 100644 --- a/src/uvw/util.h +++ b/src/uvw/util.h @@ -821,6 +821,24 @@ struct Utilities { }; +/** + * @brief Helper type for visitors. + * @tparam Func Types of function objects. + */ +template +struct Overloaded: Func... { + using Func::operator()...; +}; + + +/** + * @brief Deduction guide. + * @tparam Func Types of function objects. + */ +template +Overloaded(Func...) -> Overloaded; + + } diff --git a/test/main.cpp b/test/main.cpp index dde20ac1..50dcf593 100644 --- a/test/main.cpp +++ b/test/main.cpp @@ -36,7 +36,7 @@ void listen(uvw::Loop &loop) { client->on([](const uvw::EndEvent &, uvw::TCPHandle &handle) { std::cout << "end" << std::endl; int count = 0; - handle.loop().walk([&count](uvw::BaseHandle &) { ++count; }); + handle.loop().walk([&count](auto &) { ++count; }); std::cout << "still alive: " << count << " handles" << std::endl; handle.close(); }); diff --git a/test/uvw/loop.cpp b/test/uvw/loop.cpp index ecb2758b..92786b60 100644 --- a/test/uvw/loop.cpp +++ b/test/uvw/loop.cpp @@ -9,7 +9,7 @@ TEST(Loop, DefaultLoop) { ASSERT_FALSE(def->alive()); ASSERT_NO_THROW(def->stop()); - def->walk([](uvw::BaseHandle &) { FAIL(); }); + def->walk([](auto &) { FAIL(); }); auto def2 = uvw::Loop::getDefault(); ASSERT_EQ(def, def2); @@ -38,7 +38,7 @@ TEST(Loop, Functionalities) { handle->start(); handle->on([](const auto &, auto &hndl) { - hndl.loop().walk([](uvw::BaseHandle &) { + hndl.loop().walk([](auto &) { static bool trigger = true; ASSERT_TRUE(trigger); trigger = false; @@ -51,7 +51,7 @@ TEST(Loop, Functionalities) { ASSERT_TRUE(loop->timeout().first); ASSERT_NO_THROW(loop->run()); - loop->walk([](uvw::BaseHandle &) { FAIL(); }); + loop->walk([](auto &) { FAIL(); }); ASSERT_NO_THROW(loop->run()); ASSERT_NO_THROW(loop->run()); diff --git a/test/uvw/timer.cpp b/test/uvw/timer.cpp index e484af09..b803b406 100644 --- a/test/uvw/timer.cpp +++ b/test/uvw/timer.cpp @@ -123,16 +123,18 @@ TEST(Timer, Fake) { loop->run(); } + TEST(Timer, BaseHandleWalk) { auto loop = uvw::Loop::getDefault(); auto timer = loop->resource(); - timer->on([](const auto &event, uvw::TimerHandle &handle) { - auto &loop = handle.loop(); - loop.walk([](uvw::BaseHandle& h){ - h.type(); + + timer->on([](const auto &, uvw::TimerHandle &handle) { + handle.loop().walk(uvw::Overloaded{ + [](uvw::TimerHandle &h){ h.close(); }, + [](auto &&){} }); - handle.close(); }); - timer->start(uvw::TimerHandle::Time{1000}, uvw::TimerHandle::Time{1000}); + + timer->start(uvw::TimerHandle::Time{100}, uvw::TimerHandle::Time{100}); loop->run(); }