diff --git a/node.gyp b/node.gyp index 47ce95de75591a..23f9e2ec7ffe5c 100644 --- a/node.gyp +++ b/node.gyp @@ -638,6 +638,7 @@ 'src/string_decoder.cc', 'src/tcp_wrap.cc', 'src/timers.cc', + 'src/timer_wrap.cc', 'src/tracing/agent.cc', 'src/tracing/node_trace_buffer.cc', 'src/tracing/node_trace_writer.cc', @@ -741,6 +742,7 @@ 'src/tracing/trace_event.h', 'src/tracing/trace_event_common.h', 'src/tracing/traced_value.h', + 'src/timer_wrap.h', 'src/tty_wrap.h', 'src/udp_wrap.h', 'src/util.h', diff --git a/src/timer_wrap.cc b/src/timer_wrap.cc new file mode 100644 index 00000000000000..618c50c82e2cbd --- /dev/null +++ b/src/timer_wrap.cc @@ -0,0 +1,94 @@ +#include "env-inl.h" +#include "memory_tracker-inl.h" +#include "timer_wrap.h" +#include "uv.h" + +namespace node { + +TimerWrap::TimerWrap(Environment* env, TimerCb fn, void* user_data) + : env_(env), + fn_(fn), + user_data_(user_data) { + uv_timer_init(env->event_loop(), &timer_); + timer_.data = this; +} + +void TimerWrap::Stop(bool close) { + if (timer_.data == nullptr) return; + uv_timer_stop(&timer_); + if (LIKELY(close)) { + timer_.data = nullptr; + env_->CloseHandle(reinterpret_cast(&timer_), TimerClosedCb); + } +} + +void TimerWrap::TimerClosedCb(uv_handle_t* handle) { + std::unique_ptr ptr( + ContainerOf(&TimerWrap::timer_, + reinterpret_cast(handle))); +} + +void TimerWrap::Update(uint64_t interval, uint64_t repeat) { + if (timer_.data == nullptr) return; + uv_timer_start(&timer_, OnTimeout, interval, repeat); +} + +void TimerWrap::Ref() { + if (timer_.data == nullptr) return; + uv_ref(reinterpret_cast(&timer_)); +} + +void TimerWrap::Unref() { + if (timer_.data == nullptr) return; + uv_unref(reinterpret_cast(&timer_)); +} + +void TimerWrap::OnTimeout(uv_timer_t* timer) { + TimerWrap* t = ContainerOf(&TimerWrap::timer_, timer); + t->fn_(t->user_data_); +} + +TimerWrapHandle::TimerWrapHandle( + Environment* env, + TimerWrap::TimerCb fn, + void* user_data) { + timer_ = new TimerWrap(env, fn, user_data); + env->AddCleanupHook(CleanupHook, this); +} + +void TimerWrapHandle::Stop(bool close) { + if (UNLIKELY(!close)) + return timer_->Stop(close); + + if (timer_ != nullptr) { + timer_->env()->RemoveCleanupHook(CleanupHook, this); + timer_->Stop(); + } + timer_ = nullptr; +} + +void TimerWrapHandle::Ref() { + if (timer_ != nullptr) + timer_->Ref(); +} + +void TimerWrapHandle::Unref() { + if (timer_ != nullptr) + timer_->Unref(); +} + +void TimerWrapHandle::Update(uint64_t interval, uint64_t repeat) { + if (timer_ != nullptr) + timer_->Update(interval, repeat); +} + +void TimerWrapHandle::CleanupHook(void* data) { + static_cast(data)->Stop(); +} + +void TimerWrapHandle::MemoryInfo(node::MemoryTracker* tracker) const { + if (timer_ != nullptr) + tracker->TrackField("timer", *timer_); +} + +} // namespace node diff --git a/src/timer_wrap.h b/src/timer_wrap.h new file mode 100644 index 00000000000000..c75e17b776beef --- /dev/null +++ b/src/timer_wrap.h @@ -0,0 +1,84 @@ +#ifndef SRC_TIMER_WRAP_H_ +#define SRC_TIMER_WRAP_H_ + +#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS + +#include "memory_tracker.h" +#include "env.h" +#include "uv.h" + +#include + +namespace node { + +// Utility class that makes working with libuv timers a bit easier. +class TimerWrap final : public MemoryRetainer { + public: + using TimerCb = std::function; + + TimerWrap(Environment* env, TimerCb fn, void* user_data); + TimerWrap(const TimerWrap&) = delete; + + inline Environment* env() const { return env_; } + + // Completely stops the timer, making it no longer usable. + void Stop(bool close = true); + + // Starts / Restarts the Timer + void Update(uint64_t interval, uint64_t repeat = 0); + + void Ref(); + + void Unref(); + + SET_NO_MEMORY_INFO(); + SET_MEMORY_INFO_NAME(TimerWrap) + SET_SELF_SIZE(TimerWrap) + + private: + static void TimerClosedCb(uv_handle_t* handle); + static void OnTimeout(uv_timer_t* timer); + ~TimerWrap() = default; + + Environment* env_; + TimerCb fn_; + uv_timer_t timer_; + void* user_data_ = nullptr; + + friend std::unique_ptr::deleter_type; +}; + +class TimerWrapHandle : public MemoryRetainer { + public: + TimerWrapHandle( + Environment* env, + TimerWrap::TimerCb fn, + void* user_data = nullptr); + + TimerWrapHandle(const TimerWrapHandle&) = delete; + + ~TimerWrapHandle() { Stop(); } + + void Update(uint64_t interval, uint64_t repeat = 0); + + void Ref(); + + void Unref(); + + void Stop(bool close = true); + + void MemoryInfo(node::MemoryTracker* tracker) const override; + + SET_MEMORY_INFO_NAME(TimerWrapHandle) + SET_SELF_SIZE(TimerWrapHandle) + + private: + static void CleanupHook(void* data); + TimerWrap* timer_; +}; + +} // namespace node + +#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS + +#endif // SRC_TIMER_WRAP_H_