Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

src: introduce TimerWrap/TimerWrapHandle utility #34186

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -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',
Expand Down
92 changes: 8 additions & 84 deletions src/inspector_agent.cc
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#include "node_process.h"
#include "node_url.h"
#include "util-inl.h"
#include "timer_wrap.h"
#include "v8-inspector.h"
#include "v8-platform.h"

Expand Down Expand Up @@ -326,86 +327,6 @@ class ChannelImpl final : public v8_inspector::V8Inspector::Channel,
bool retaining_context_;
};

class InspectorTimer {
public:
InspectorTimer(Environment* env,
double interval_s,
V8InspectorClient::TimerCallback callback,
void* data) : env_(env),
callback_(callback),
data_(data) {
uv_timer_init(env->event_loop(), &timer_);
int64_t interval_ms = 1000 * interval_s;
uv_timer_start(&timer_, OnTimer, interval_ms, interval_ms);
timer_.data = this;
}

InspectorTimer(const InspectorTimer&) = delete;

void Stop() {
if (timer_.data == nullptr) return;

timer_.data = nullptr;
uv_timer_stop(&timer_);
env_->CloseHandle(reinterpret_cast<uv_handle_t*>(&timer_), TimerClosedCb);
}

inline Environment* env() const { return env_; }

private:
static void OnTimer(uv_timer_t* uvtimer) {
InspectorTimer* timer = node::ContainerOf(&InspectorTimer::timer_, uvtimer);
timer->callback_(timer->data_);
}

static void TimerClosedCb(uv_handle_t* uvtimer) {
std::unique_ptr<InspectorTimer> timer(
node::ContainerOf(&InspectorTimer::timer_,
reinterpret_cast<uv_timer_t*>(uvtimer)));
// Unique_ptr goes out of scope here and pointer is deleted.
}

~InspectorTimer() = default;

Environment* env_;
uv_timer_t timer_;
V8InspectorClient::TimerCallback callback_;
void* data_;

friend std::unique_ptr<InspectorTimer>::deleter_type;
};

class InspectorTimerHandle {
public:
InspectorTimerHandle(Environment* env, double interval_s,
V8InspectorClient::TimerCallback callback, void* data) {
timer_ = new InspectorTimer(env, interval_s, callback, data);

env->AddCleanupHook(CleanupHook, this);
}

InspectorTimerHandle(const InspectorTimerHandle&) = delete;

~InspectorTimerHandle() {
Stop();
}

private:
void Stop() {
if (timer_ != nullptr) {
timer_->env()->RemoveCleanupHook(CleanupHook, this);
timer_->Stop();
}
timer_ = nullptr;
}

static void CleanupHook(void* data) {
static_cast<InspectorTimerHandle*>(data)->Stop();
}

InspectorTimer* timer_;
};

class SameThreadInspectorSession : public InspectorSession {
public:
SameThreadInspectorSession(
Expand Down Expand Up @@ -602,9 +523,12 @@ class NodeInspectorClient : public V8InspectorClient {
void startRepeatingTimer(double interval_s,
TimerCallback callback,
void* data) override {
timers_.emplace(std::piecewise_construct, std::make_tuple(data),
std::make_tuple(env_, interval_s, callback,
data));
auto result =
timers_.emplace(std::piecewise_construct, std::make_tuple(data),
std::make_tuple(env_, callback, data));
CHECK(result.second);
uint64_t interval = 1000 * interval_s;
result.first->second.Update(interval, interval);
}

void cancelTimer(void* data) override {
Expand Down Expand Up @@ -724,7 +648,7 @@ class NodeInspectorClient : public V8InspectorClient {
bool running_nested_loop_ = false;
std::unique_ptr<V8Inspector> client_;
// Note: ~ChannelImpl may access timers_ so timers_ has to come first.
std::unordered_map<void*, InspectorTimerHandle> timers_;
std::unordered_map<void*, TimerWrapHandle> timers_;
std::unordered_map<int, std::unique_ptr<ChannelImpl>> channels_;
int next_session_id_ = 1;
bool waiting_for_resume_ = false;
Expand Down
9 changes: 3 additions & 6 deletions src/quic/node_quic_session-inl.h
Original file line number Diff line number Diff line change
Expand Up @@ -375,8 +375,7 @@ QuicCID QuicSession::dcid() const {
// timer to actually monitor. Here we take the calculated timeout
// and extend out the libuv timer.
void QuicSession::UpdateRetransmitTimer(uint64_t timeout) {
DCHECK_NOT_NULL(retransmit_);
retransmit_->Update(timeout);
retransmit_.Update(timeout, timeout);
}

void QuicSession::CheckAllocatedSize(size_t previous_size) const {
Expand Down Expand Up @@ -512,13 +511,11 @@ void QuicSession::set_remote_transport_params() {
}

void QuicSession::StopIdleTimer() {
CHECK_NOT_NULL(idle_);
idle_->Stop();
idle_.Stop();
}

void QuicSession::StopRetransmitTimer() {
CHECK_NOT_NULL(retransmit_);
retransmit_->Stop();
retransmit_.Stop();
}

// Called by the OnVersionNegotiation callback when a version
Expand Down
7 changes: 3 additions & 4 deletions src/quic/node_quic_session.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1431,8 +1431,8 @@ QuicSession::QuicSession(
socket_(socket),
alpn_(alpn),
hostname_(hostname),
idle_(new Timer(socket->env(), [this]() { OnIdleTimeout(); })),
retransmit_(new Timer(socket->env(), [this]() { MaybeTimeout(); })),
idle_(socket->env(), [this](void* data) { OnIdleTimeout(); }),
retransmit_(socket->env(), [this](void* data) { MaybeTimeout(); }),
dcid_(dcid),
state_(env()->isolate()),
quic_state_(socket->quic_state()) {
Expand Down Expand Up @@ -2461,14 +2461,13 @@ void QuicSession::UpdateConnectionID(
// will be silently closed. It is important to update this as activity
// occurs to keep the idle timer from firing.
void QuicSession::UpdateIdleTimer() {
CHECK_NOT_NULL(idle_);
uint64_t now = uv_hrtime();
uint64_t expiry = ngtcp2_conn_get_idle_expiry(connection());
// nano to millis
uint64_t timeout = expiry > now ? (expiry - now) / 1000000ULL : 1;
if (timeout == 0) timeout = 1;
Debug(this, "Updating idle timeout to %" PRIu64, timeout);
idle_->Update(timeout);
idle_.Update(timeout, timeout);
}


Expand Down
5 changes: 3 additions & 2 deletions src/quic/node_quic_session.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#include "node_quic_util.h"
#include "node_sockaddr.h"
#include "stream_base.h"
#include "timer_wrap.h"
#include "v8.h"
#include "uv.h"

Expand Down Expand Up @@ -1471,8 +1472,8 @@ class QuicSession : public AsyncWrap,
QuicSessionListener* listener_ = nullptr;
JSQuicSessionListener default_listener_;

TimerPointer idle_;
TimerPointer retransmit_;
TimerWrapHandle idle_;
TimerWrapHandle retransmit_;

QuicCID scid_;
QuicCID dcid_;
Expand Down
43 changes: 0 additions & 43 deletions src/quic/node_quic_util-inl.h
Original file line number Diff line number Diff line change
Expand Up @@ -73,49 +73,6 @@ size_t GetMaxPktLen(const SocketAddress& addr) {
NGTCP2_MAX_PKTLEN_IPV4;
}

Timer::Timer(Environment* env, std::function<void()> fn)
: env_(env),
fn_(fn) {
uv_timer_init(env_->event_loop(), &timer_);
timer_.data = this;
}

void Timer::Stop() {
if (stopped_)
return;
stopped_ = true;

if (timer_.data == this) {
uv_timer_stop(&timer_);
timer_.data = nullptr;
}
}

// If the timer is not currently active, interval must be either 0 or greater.
// If the timer is already active, interval is ignored.
void Timer::Update(uint64_t interval) {
if (stopped_)
return;
uv_timer_start(&timer_, OnTimeout, interval, interval);
uv_unref(reinterpret_cast<uv_handle_t*>(&timer_));
}

void Timer::Free(Timer* timer) {
timer->env_->CloseHandle(
reinterpret_cast<uv_handle_t*>(&timer->timer_),
[&](uv_handle_t* timer) {
Timer* t = ContainerOf(
&Timer::timer_,
reinterpret_cast<uv_timer_t*>(timer));
delete t;
});
}

void Timer::OnTimeout(uv_timer_t* timer) {
Timer* t = ContainerOf(&Timer::timer_, timer);
t->fn_();
}

QuicError::QuicError(
int32_t family_,
uint64_t code_) :
Expand Down
32 changes: 0 additions & 32 deletions src/quic/node_quic_util.h
Original file line number Diff line number Diff line change
Expand Up @@ -338,38 +338,6 @@ class QuicCID : public MemoryRetainer {
const ngtcp2_cid* ptr_;
};

// Simple timer wrapper that is used to implement the internals
// for idle and retransmission timeouts. Call Update to start or
// reset the timer; Stop to halt the timer.
class Timer final : public MemoryRetainer {
public:
inline explicit Timer(Environment* env, std::function<void()> fn);

// Stops the timer with the side effect of the timer no longer being usable.
// It will be cleaned up and the Timer object will be destroyed.
inline void Stop();

// If the timer is not currently active, interval must be either 0 or greater.
// If the timer is already active, interval is ignored.
inline void Update(uint64_t interval);

static inline void Free(Timer* timer);

SET_NO_MEMORY_INFO()
SET_MEMORY_INFO_NAME(Timer)
SET_SELF_SIZE(Timer)

private:
static inline void OnTimeout(uv_timer_t* timer);

bool stopped_ = false;
Environment* env_;
std::function<void()> fn_;
uv_timer_t timer_;
};

using TimerPointer = DeleteFnPtr<Timer, Timer::Free>;

// A Stateless Reset Token is a mechanism by which a QUIC
// endpoint can discreetly signal to a peer that it has
// lost all state associated with a connection. This
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);
jasnell marked this conversation as resolved.
Show resolved Hide resolved
}

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
Loading