Skip to content

Commit

Permalink
src: add TimerWrap utility
Browse files Browse the repository at this point in the history
Consolidate uv_timer_t boilerplate code into a shared utility.
There are several places throughout the code where we use uv_timer_t
internally (inspector, perf, quic), with some code duplication.
This eliminates the duplicated code, ensures that cleanup occurs
correctly, and simplifies use of the timers.

Signed-off-by: James M Snell <[email protected]>

PR-URL: #34186
Reviewed-By: Anna Henningsen <[email protected]>
Reviewed-By: Matteo Collina <[email protected]>
  • Loading branch information
jasnell authored and targos committed Apr 30, 2021
1 parent ae9bd89 commit a3a4d2c
Show file tree
Hide file tree
Showing 3 changed files with 180 additions and 0 deletions.
2 changes: 2 additions & 0 deletions node.gyp
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down Expand Up @@ -742,6 +743,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',
Expand Down
94 changes: 94 additions & 0 deletions src/timer_wrap.cc
Original file line number Diff line number Diff line change
@@ -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<uv_handle_t*>(&timer_), TimerClosedCb);
}
}

void TimerWrap::TimerClosedCb(uv_handle_t* handle) {
std::unique_ptr<TimerWrap> ptr(
ContainerOf(&TimerWrap::timer_,
reinterpret_cast<uv_timer_t*>(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<uv_handle_t*>(&timer_));
}

void TimerWrap::Unref() {
if (timer_.data == nullptr) return;
uv_unref(reinterpret_cast<uv_handle_t*>(&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<TimerWrapHandle*>(data)->Stop();
}

void TimerWrapHandle::MemoryInfo(node::MemoryTracker* tracker) const {
if (timer_ != nullptr)
tracker->TrackField("timer", *timer_);
}

} // namespace node
84 changes: 84 additions & 0 deletions src/timer_wrap.h
Original file line number Diff line number Diff line change
@@ -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 <functional>

namespace node {

// Utility class that makes working with libuv timers a bit easier.
class TimerWrap final : public MemoryRetainer {
public:
using TimerCb = std::function<void(void*)>;

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<TimerWrap>::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_

0 comments on commit a3a4d2c

Please sign in to comment.