get rid of base handle, loop::walk returns actual handles (see #212, close #214)

This commit is contained in:
Michele Caini 2020-07-28 22:08:50 +02:00
parent a10844d4bf
commit 0b329720bf
7 changed files with 335 additions and 344 deletions

View File

@ -27,248 +27,248 @@ struct CloseEvent {};
* Base type for all `uvw` handle types. * Base type for all `uvw` handle types.
*/ */
template<typename T, typename U> template<typename T, typename U>
class Handle: public Resource<T, U>, public BaseHandle { class Handle: public Resource<T, U> {
protected: protected:
static void closeCallback(uv_handle_t *handle) { static void closeCallback(uv_handle_t *handle) {
Handle<T, U> &ref = *(static_cast<T*>(handle->data)); Handle<T, U> &ref = *(static_cast<T*>(handle->data));
auto ptr = ref.shared_from_this(); auto ptr = ref.shared_from_this();
(void)ptr; (void)ptr;
ref.reset(); ref.reset();
ref.publish(CloseEvent{}); ref.publish(CloseEvent{});
} }
static void allocCallback(uv_handle_t *, std::size_t suggested, uv_buf_t *buf) { static void allocCallback(uv_handle_t *, std::size_t suggested, uv_buf_t *buf) {
auto size = static_cast<unsigned int>(suggested); auto size = static_cast<unsigned int>(suggested);
*buf = uv_buf_init(new char[size], size); *buf = uv_buf_init(new char[size], size);
} }
template<typename F, typename... Args> template<typename F, typename... Args>
bool initialize(F &&f, Args&&... args) { bool initialize(F &&f, Args&&... args) {
if(!this->self()) { if(!this->self()) {
auto err = std::forward<F>(f)(this->parent(), this->get(), std::forward<Args>(args)...); auto err = std::forward<F>(f)(this->parent(), this->get(), std::forward<Args>(args)...);
if(err) { if(err) {
this->publish(ErrorEvent{err}); this->publish(ErrorEvent{err});
} else { } else {
this->leak(); this->leak();
} }
} }
return this->self(); return this->self();
} }
template<typename F, typename... Args> template<typename F, typename... Args>
void invoke(F &&f, Args&&... args) { void invoke(F &&f, Args&&... args) {
auto err = std::forward<F>(f)(std::forward<Args>(args)...); auto err = std::forward<F>(f)(std::forward<Args>(args)...);
if(err) { Emitter<T>::publish(ErrorEvent{err}); } if(err) { Emitter<T>::publish(ErrorEvent{err}); }
} }
public: public:
using Resource<T, U>::Resource; using Resource<T, U>::Resource;
/** /**
* @brief Gets the category of the handle. * @brief Gets the category of the handle.
* *
* A base handle offers no functionality to promote it to the actual 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 * type. By means of this function, an opaque value that identifies the
* category of the handle is made available to the users. * category of the handle is made available to the users.
* *
* @return The actual category of the handle. * @return The actual category of the handle.
*/ */
HandleCategory category() const noexcept override { HandleCategory category() const noexcept {
return HandleCategory{this->template get<uv_handle_t>()->type}; return HandleCategory{this->template get<uv_handle_t>()->type};
} }
/** /**
* @brief Gets the type of the handle. * @brief Gets the type of the handle.
* *
* A base handle offers no functionality to promote it to the actual 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 * type. By means of this function, the type of the underlying handle as
* specified by HandleType is made available to the users. * specified by HandleType is made available to the users.
* *
* @return The actual type of the handle. * @return The actual type of the handle.
*/ */
HandleType type() const noexcept override { HandleType type() const noexcept {
return Utilities::guessHandle(category()); return Utilities::guessHandle(category());
} }
/** /**
* @brief Checks if the handle is active. * @brief Checks if the handle is active.
* *
* What _active_ means depends on the type of handle: * What _active_ means depends on the type of handle:
* *
* * An AsyncHandle handle is always active and cannot be deactivated, * * An AsyncHandle handle is always active and cannot be deactivated,
* except by closing it with uv_close(). * except by closing it with uv_close().
* * A PipeHandle, TCPHandle, UDPHandle, etc. handle - basically any handle * * A PipeHandle, TCPHandle, UDPHandle, etc. handle - basically any handle
* that deals with I/O - is active when it is doing something that involves * that deals with I/O - is active when it is doing something that involves
* I/O, like reading, writing, connecting, accepting new connections, etc. * I/O, like reading, writing, connecting, accepting new connections, etc.
* * A CheckHandle, IdleHandle, TimerHandle, etc. handle is active when it * * A CheckHandle, IdleHandle, TimerHandle, etc. handle is active when it
* has been started with a call to `start()`. * 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 `FooHandle` has a `start()` member
* method, then its active from the moment that method is called. Likewise, * method, then its active from the moment that method is called. Likewise,
* `stop()` deactivates the handle again. * `stop()` deactivates the handle again.
* *
* @return True if the handle is active, false otherwise. * @return True if the handle is active, false otherwise.
*/ */
bool active() const noexcept override { bool active() const noexcept {
return !(uv_is_active(this->template get<uv_handle_t>()) == 0); return !(uv_is_active(this->template get<uv_handle_t>()) == 0);
} }
/** /**
* @brief Checks if a handle is closing or closed. * @brief Checks if a handle is closing or closed.
* *
* This function should only be used between the initialization of the * This function should only be used between the initialization of the
* handle and the arrival of the close callback. * handle and the arrival of the close callback.
* *
* @return True if the handle is closing or closed, false otherwise. * @return True if the handle is closing or closed, false otherwise.
*/ */
bool closing() const noexcept override { bool closing() const noexcept {
return !(uv_is_closing(this->template get<uv_handle_t>()) == 0); return !(uv_is_closing(this->template get<uv_handle_t>()) == 0);
} }
/** /**
* @brief Request handle to be closed. * @brief Request handle to be closed.
* *
* This **must** be called on each handle before memory is released.<br/> * This **must** be called on each handle before memory is released.<br/>
* In-progress requests are cancelled and this can result in an ErrorEvent * In-progress requests are cancelled and this can result in an ErrorEvent
* emitted. * emitted.
* *
* The handle will emit a CloseEvent when finished. * The handle will emit a CloseEvent when finished.
*/ */
void close() noexcept override { void close() noexcept {
if(!closing()) { if(!closing()) {
uv_close(this->template get<uv_handle_t>(), &Handle<T, U>::closeCallback); uv_close(this->template get<uv_handle_t>(), &Handle<T, U>::closeCallback);
} }
} }
/** /**
* @brief Reference the given handle. * @brief Reference the given handle.
* *
* References are idempotent, that is, if a handle is already referenced * References are idempotent, that is, if a handle is already referenced
* calling this function again will have no effect. * calling this function again will have no effect.
*/ */
void reference() noexcept override { void reference() noexcept {
uv_ref(this->template get<uv_handle_t>()); uv_ref(this->template get<uv_handle_t>());
} }
/** /**
* @brief Unreference the given handle. * @brief Unreference the given handle.
* *
* References are idempotent, that is, if a handle is not referenced calling * References are idempotent, that is, if a handle is not referenced calling
* this function again will have no effect. * this function again will have no effect.
*/ */
void unreference() noexcept override { void unreference() noexcept {
uv_unref(this->template get<uv_handle_t>()); uv_unref(this->template get<uv_handle_t>());
} }
/** /**
* @brief Checks if the given handle referenced. * @brief Checks if the given handle referenced.
* @return True if the handle referenced, false otherwise. * @return True if the handle referenced, false otherwise.
*/ */
bool referenced() const noexcept override { bool referenced() const noexcept {
return !(uv_has_ref(this->template get<uv_handle_t>()) == 0); return !(uv_has_ref(this->template get<uv_handle_t>()) == 0);
} }
/** /**
* @brief Returns the size of the underlying handle type. * @brief Returns the size of the underlying handle type.
* @return The size of the underlying handle type. * @return The size of the underlying handle type.
*/ */
std::size_t size() const noexcept { std::size_t size() const noexcept {
return uv_handle_size(this->template get<uv_handle_t>()->type); return uv_handle_size(this->template get<uv_handle_t>()->type);
} }
/** /**
* @brief Gets the size of the send buffer used for the socket. * @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 * Gets the size of the send buffer that the operating system uses for the
* socket.<br/> * socket.<br/>
* This function works for TCPHandle, PipeHandle and UDPHandle handles on * This function works for TCPHandle, PipeHandle and UDPHandle handles on
* Unix and for TCPHandle and UDPHandle handles on Windows.<br/> * Unix and for TCPHandle and UDPHandle handles on Windows.<br/>
* Note that Linux will return double the size of the original set value. * 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, 0 in case of errors.
*/ */
int sendBufferSize() { int sendBufferSize() {
int value = 0; int value = 0;
auto err = uv_send_buffer_size(this->template get<uv_handle_t>(), &value); auto err = uv_send_buffer_size(this->template get<uv_handle_t>(), &value);
return err ? 0 : value; return err ? 0 : value;
} }
/** /**
* @brief Sets the size of the send buffer used for the socket. * @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 * Sets the size of the send buffer that the operating system uses for the
* socket.<br/> * socket.<br/>
* This function works for TCPHandle, PipeHandle and UDPHandle handles on * This function works for TCPHandle, PipeHandle and UDPHandle handles on
* Unix and for TCPHandle and UDPHandle handles on Windows.<br/> * Unix and for TCPHandle and UDPHandle handles on Windows.<br/>
* Note that Linux will set double the size. * Note that Linux will set double the size.
* *
* @return True in case of success, false otherwise. * @return True in case of success, false otherwise.
*/ */
bool sendBufferSize(int value) { bool sendBufferSize(int value) {
return (0 == uv_send_buffer_size(this->template get<uv_handle_t>(), &value)); return (0 == uv_send_buffer_size(this->template get<uv_handle_t>(), &value));
} }
/** /**
* @brief Gets the size of the receive buffer used for the socket. * @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 * Gets the size of the receive buffer that the operating system uses for
* the socket.<br/> * the socket.<br/>
* This function works for TCPHandle, PipeHandle and UDPHandle handles on * This function works for TCPHandle, PipeHandle and UDPHandle handles on
* Unix and for TCPHandle and UDPHandle handles on Windows.<br/> * Unix and for TCPHandle and UDPHandle handles on Windows.<br/>
* Note that Linux will return double the size of the original set value. * 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, 0 in case of errors.
*/ */
int recvBufferSize() { int recvBufferSize() {
int value = 0; int value = 0;
auto err = uv_recv_buffer_size(this->template get<uv_handle_t>(), &value); auto err = uv_recv_buffer_size(this->template get<uv_handle_t>(), &value);
return err ? 0 : value; return err ? 0 : value;
} }
/** /**
* @brief Sets the size of the receive buffer used for the socket. * @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 * Sets the size of the receive buffer that the operating system uses for
* the socket.<br/> * the socket.<br/>
* This function works for TCPHandle, PipeHandle and UDPHandle handles on * This function works for TCPHandle, PipeHandle and UDPHandle handles on
* Unix and for TCPHandle and UDPHandle handles on Windows.<br/> * Unix and for TCPHandle and UDPHandle handles on Windows.<br/>
* Note that Linux will set double the size. * Note that Linux will set double the size.
* *
* @return True in case of success, false otherwise. * @return True in case of success, false otherwise.
*/ */
bool recvBufferSize(int value) { bool recvBufferSize(int value) {
return (0 == uv_recv_buffer_size(this->template get<uv_handle_t>(), &value)); return (0 == uv_recv_buffer_size(this->template get<uv_handle_t>(), &value));
} }
/** /**
* @brief Gets the platform dependent file descriptor equivalent. * @brief Gets the platform dependent file descriptor equivalent.
* *
* Supported handles: * Supported handles:
* *
* * TCPHandle * * TCPHandle
* * PipeHandle * * PipeHandle
* * TTYHandle * * TTYHandle
* * UDPHandle * * UDPHandle
* * PollHandle * * PollHandle
* *
* It will emit an ErrorEvent event if invoked on any other handle.<br/> * 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 * If a handle doesnt have an attached file descriptor yet or the handle
* itself has been closed, an ErrorEvent event will be emitted. * itself has been closed, an ErrorEvent event will be emitted.
* *
* See the official * See the official
* [documentation](http://docs.libuv.org/en/v1.x/handle.html#c.uv_fileno) * [documentation](http://docs.libuv.org/en/v1.x/handle.html#c.uv_fileno)
* for further details. * for further details.
* *
* @return The file descriptor attached to the hande or a negative value in * @return The file descriptor attached to the hande or a negative value in
* case of errors. * case of errors.
*/ */
OSFileDescriptor fd() const { OSFileDescriptor fd() const {
uv_os_fd_t fd; uv_os_fd_t fd;
uv_fileno(this->template get<uv_handle_t>(), &fd); uv_fileno(this->template get<uv_handle_t>(), &fd);
return fd; return fd;
} }
}; };

View File

@ -107,16 +107,6 @@ UVW_INLINE void Loop::update() const noexcept {
} }
UVW_INLINE void Loop::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);
}
UVW_INLINE void Loop::fork() noexcept { UVW_INLINE void Loop::fork() noexcept {
auto err = uv_loop_fork(loop.get()); auto err = uv_loop_fork(loop.get());

View File

@ -19,6 +19,22 @@
namespace uvw { 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 { namespace details {
@ -37,102 +53,6 @@ enum class UVRunMode: std::underlying_type_t<uv_run_mode> {
} }
/**
* @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 {
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 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. * @brief The Loop class.
* *
@ -147,6 +67,18 @@ class Loop final: public Emitter<Loop>, public std::enable_shared_from_this<Loop
template<typename, typename> template<typename, typename>
friend class Resource; friend class Resource;
template<typename R, typename... Args>
auto create_resource(int, Args&&... args) -> decltype(std::declval<R>().init(), std::shared_ptr<R>{}) {
auto ptr = R::create(shared_from_this(), std::forward<Args>(args)...);
ptr = ptr->init() ? ptr : nullptr;
return ptr;
}
template<typename R, typename... Args>
std::shared_ptr<R> create_resource(char, Args&&... args) {
return R::create(shared_from_this(), std::forward<Args>(args)...);
}
Loop(std::unique_ptr<uv_loop_t, Deleter> ptr) noexcept; Loop(std::unique_ptr<uv_loop_t, Deleter> ptr) noexcept;
public: public:
@ -228,13 +160,7 @@ public:
*/ */
template<typename R, typename... Args> template<typename R, typename... Args>
std::shared_ptr<R> resource(Args&&... args) { std::shared_ptr<R> resource(Args&&... args) {
if constexpr(std::is_base_of_v<BaseHandle, R>) { return create_resource<R>(0, std::forward<Args>(args)...);
auto ptr = R::create(shared_from_this(), std::forward<Args>(args)...);
ptr = ptr->init() ? ptr : nullptr;
return ptr;
} else {
return R::create(shared_from_this(), std::forward<Args>(args)...);
}
} }
/** /**
@ -335,7 +261,62 @@ public:
* *
* @param callback A function to be invoked once for each active handle. * @param callback A function to be invoked once for each active handle.
*/ */
void walk(std::function<void(BaseHandle &)> callback); template<typename Func>
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 *>(func);
switch(Utilities::guessHandle(HandleCategory{handle->type})) {
case HandleType::ASYNC:
cb(*static_cast<AsyncHandle *>(handle->data));
break;
case HandleType::CHECK:
cb(*static_cast<CheckHandle *>(handle->data));
break;
case HandleType::FS_EVENT:
cb(*static_cast<FsEventHandle *>(handle->data));
break;
case HandleType::FS_POLL:
cb(*static_cast<FsPollHandle *>(handle->data));
break;
case HandleType::IDLE:
cb(*static_cast<IdleHandle *>(handle->data));
break;
case HandleType::PIPE:
cb(*static_cast<PipeHandle *>(handle->data));
break;
case HandleType::POLL:
cb(*static_cast<PollHandle *>(handle->data));
break;
case HandleType::PREPARE:
cb(*static_cast<PrepareHandle *>(handle->data));
break;
case HandleType::PROCESS:
cb(*static_cast<ProcessHandle *>(handle->data));
break;
case HandleType::SIGNAL:
cb(*static_cast<SignalHandle *>(handle->data));
break;
case HandleType::TCP:
cb(*static_cast<TCPHandle *>(handle->data));
break;
case HandleType::TIMER:
cb(*static_cast<TimerHandle *>(handle->data));
break;
case HandleType::TTY:
cb(*static_cast<TTYHandle *>(handle->data));
break;
case HandleType::UDP:
cb(*static_cast<UDPHandle *>(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 * @brief Reinitialize any kernel state necessary in the child process after

View File

@ -821,6 +821,24 @@ struct Utilities {
}; };
/**
* @brief Helper type for visitors.
* @tparam Func Types of function objects.
*/
template<class... Func>
struct Overloaded: Func... {
using Func::operator()...;
};
/**
* @brief Deduction guide.
* @tparam Func Types of function objects.
*/
template<class... Func>
Overloaded(Func...) -> Overloaded<Func...>;
} }

View File

@ -36,7 +36,7 @@ void listen(uvw::Loop &loop) {
client->on<uvw::EndEvent>([](const uvw::EndEvent &, uvw::TCPHandle &handle) { client->on<uvw::EndEvent>([](const uvw::EndEvent &, uvw::TCPHandle &handle) {
std::cout << "end" << std::endl; std::cout << "end" << std::endl;
int count = 0; int count = 0;
handle.loop().walk([&count](uvw::BaseHandle &) { ++count; }); handle.loop().walk([&count](auto &) { ++count; });
std::cout << "still alive: " << count << " handles" << std::endl; std::cout << "still alive: " << count << " handles" << std::endl;
handle.close(); handle.close();
}); });

View File

@ -9,7 +9,7 @@ TEST(Loop, DefaultLoop) {
ASSERT_FALSE(def->alive()); ASSERT_FALSE(def->alive());
ASSERT_NO_THROW(def->stop()); ASSERT_NO_THROW(def->stop());
def->walk([](uvw::BaseHandle &) { FAIL(); }); def->walk([](auto &) { FAIL(); });
auto def2 = uvw::Loop::getDefault(); auto def2 = uvw::Loop::getDefault();
ASSERT_EQ(def, def2); ASSERT_EQ(def, def2);
@ -38,7 +38,7 @@ TEST(Loop, Functionalities) {
handle->start(); handle->start();
handle->on<uvw::PrepareEvent>([](const auto &, auto &hndl) { handle->on<uvw::PrepareEvent>([](const auto &, auto &hndl) {
hndl.loop().walk([](uvw::BaseHandle &) { hndl.loop().walk([](auto &) {
static bool trigger = true; static bool trigger = true;
ASSERT_TRUE(trigger); ASSERT_TRUE(trigger);
trigger = false; trigger = false;
@ -51,7 +51,7 @@ TEST(Loop, Functionalities) {
ASSERT_TRUE(loop->timeout().first); ASSERT_TRUE(loop->timeout().first);
ASSERT_NO_THROW(loop->run()); ASSERT_NO_THROW(loop->run());
loop->walk([](uvw::BaseHandle &) { FAIL(); }); loop->walk([](auto &) { FAIL(); });
ASSERT_NO_THROW(loop->run<uvw::Loop::Mode::ONCE>()); ASSERT_NO_THROW(loop->run<uvw::Loop::Mode::ONCE>());
ASSERT_NO_THROW(loop->run<uvw::Loop::Mode::NOWAIT>()); ASSERT_NO_THROW(loop->run<uvw::Loop::Mode::NOWAIT>());

View File

@ -123,16 +123,18 @@ TEST(Timer, Fake) {
loop->run(); loop->run();
} }
TEST(Timer, BaseHandleWalk) { TEST(Timer, BaseHandleWalk) {
auto loop = uvw::Loop::getDefault(); auto loop = uvw::Loop::getDefault();
auto timer = loop->resource<uvw::TimerHandle>(); auto timer = loop->resource<uvw::TimerHandle>();
timer->on<uvw::TimerEvent>([](const auto &event, uvw::TimerHandle &handle) {
auto &loop = handle.loop(); timer->on<uvw::TimerEvent>([](const auto &, uvw::TimerHandle &handle) {
loop.walk([](uvw::BaseHandle& h){ handle.loop().walk(uvw::Overloaded{
h.type(); [](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(); loop->run();
} }