-
Notifications
You must be signed in to change notification settings - Fork 5
src: add AddCodeEventHook API for code events #327
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
base: node-v22.x-nsolid-v5.x
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -597,6 +597,42 @@ int Snapshot::get_snapshot_(SharedEnvInst envinst, | |
GetHeapSnapshot(envinst, redacted, data, proxy, deleter); | ||
} | ||
|
||
class CodeEventHook::Impl { | ||
public: | ||
Impl() = default; | ||
~Impl() { | ||
// Remove the hook from the TSList in EnvList | ||
EnvList::Inst()->RemoveCodeEventHook(hook_); | ||
} | ||
|
||
private: | ||
friend class CodeEventHook; | ||
void Setup(internal::code_event_hook_proxy_sig cb, | ||
void(*deleter)(void*), | ||
void* data) { | ||
// Add hook to the TSList in EnvList | ||
hook_ = EnvList::Inst()->AddCodeEventHook(data, cb, deleter); | ||
} | ||
|
||
TSList<EnvList::CodeEventHookStor>::iterator hook_; | ||
}; | ||
|
||
CodeEventHook::CodeEventHook(): impl_(std::make_unique<Impl>()) { | ||
} | ||
|
||
CodeEventHook::~CodeEventHook() = default; | ||
|
||
void CodeEventHook::Dispose() { | ||
// This method transfers ownership to the callee and deletes the object. | ||
// The caller must not use this object after calling Dispose(). | ||
delete this; | ||
} | ||
|
||
void CodeEventHook::DoSetup(internal::code_event_hook_proxy_sig cb, | ||
internal::deleter_sig deleter, | ||
void* data) { | ||
impl_->Setup(cb, deleter, data); | ||
} | ||
|
||
namespace internal { | ||
|
||
|
@@ -637,6 +673,17 @@ void thread_removed_hook_(void* data, | |
EnvList::Inst()->EnvironmentDeletionHook(data, proxy, deleter); | ||
} | ||
|
||
CodeEventHook* add_code_event_hook_(void* data, | ||
code_event_hook_proxy_sig proxy, | ||
deleter_sig deleter) { | ||
CodeEventHook* hook = new (std::nothrow) CodeEventHook(); | ||
if (hook == nullptr) { | ||
return nullptr; | ||
} | ||
hook->DoSetup(proxy, deleter, data); | ||
return hook; | ||
} | ||
Comment on lines
+676
to
+685
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. DoSetup cannot fail |
||
|
||
|
||
int queue_callback_(void* data, queue_callback_proxy_sig proxy) { | ||
return EnvList::Inst()->QueueCallback(proxy, data); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,5 @@ | ||
#include "nsolid_api.h" | ||
#include "nsolid/nsolid_code_event_handler.h" | ||
#include "nsolid/nsolid_heap_snapshot.h" | ||
#include "nsolid_bindings.h" | ||
#include "node_buffer.h" | ||
|
@@ -840,6 +841,19 @@ void EnvInst::StoreSourceCode(int script_id, | |
} | ||
|
||
|
||
void EnvInst::setup_code_event_handler() { | ||
if (!code_event_handler_) { | ||
code_event_handler_ = | ||
std::make_unique<NSolidCodeEventHandler>(isolate_, thread_id_); | ||
code_event_handler_->Enable(); | ||
} | ||
} | ||
|
||
void EnvInst::disable_code_event_handler() { | ||
code_event_handler_.reset(); | ||
} | ||
|
||
|
||
EnvList::EnvList(): info_(nlohmann::json()) { | ||
int er; | ||
// Create event loop and new thread to run EnvList commands. | ||
|
@@ -869,6 +883,13 @@ EnvList::EnvList(): info_(nlohmann::json()) { | |
er = thread_.create(env_list_routine_, this); | ||
CHECK_EQ(er, 0); | ||
continuous_profiler_ = std::make_shared<ContinuousProfiler>(&thread_loop_); | ||
on_code_event_q_ = | ||
AsyncTSQueue<CodeEventInfo>::create( | ||
&thread_loop_, | ||
+[](CodeEventInfo&& info, EnvList* envlist) { | ||
envlist->got_code_event(std::move(info)); | ||
}, | ||
this); | ||
} | ||
|
||
|
||
|
@@ -978,6 +999,58 @@ void EnvList::OnUnblockedLoopHook( | |
{ 0, proxy, nsolid::internal::user_data(data, deleter) }); | ||
} | ||
|
||
TSList<EnvList::CodeEventHookStor>::iterator EnvList::AddCodeEventHook( | ||
void* data, | ||
internal::code_event_hook_proxy_sig proxy, | ||
internal::deleter_sig deleter) { | ||
|
||
auto it = code_event_hook_list_.push_back( | ||
{ proxy, nsolid::internal::user_data(data, deleter) }); | ||
|
||
decltype(env_map_) env_map; | ||
{ | ||
// Copy the envinst map so we don't need to keep it locked the entire time. | ||
ns_mutex::scoped_lock lock(map_lock_); | ||
env_map = env_map_; | ||
} | ||
|
||
for (auto& entry : env_map) { | ||
SharedEnvInst envinst = entry.second; | ||
int er = RunCommand(envinst, | ||
CommandType::InterruptOnly, | ||
setup_code_event_handler); | ||
if (er) { | ||
// Nothing to do here, really. | ||
} | ||
} | ||
|
||
return it; | ||
} | ||
|
||
void EnvList::RemoveCodeEventHook( | ||
TSList<EnvList::CodeEventHookStor>::iterator it) { | ||
size_t size = code_event_hook_list_.erase(it); | ||
if (size == 0) { | ||
// No hooks left, disable the code event handler. | ||
decltype(env_map_) env_map; | ||
{ | ||
// Copy the envinst map so we don't need to keep it locked the entire | ||
// time. | ||
ns_mutex::scoped_lock lock(map_lock_); | ||
env_map = env_map_; | ||
} | ||
|
||
for (auto& entry : env_map) { | ||
SharedEnvInst envinst = entry.second; | ||
int er = RunCommand(envinst, | ||
CommandType::InterruptOnly, | ||
disable_code_event_handler); | ||
if (er) { | ||
// Nothing to do here, really. | ||
} | ||
} | ||
} | ||
} | ||
|
||
int EnvList::QueueCallback(q_cb_sig cb, uint64_t timeout, void* data) { | ||
QCbTimeoutStor* stor = new (std::nothrow) QCbTimeoutStor({ | ||
|
@@ -1041,6 +1114,10 @@ NODE_PERFORMANCE_MILESTONES(V) | |
env->isolate()->AddGCEpilogueCallback(EnvInst::v8_gc_epilogue_cb_, | ||
envinst_sp.get()); | ||
|
||
if (code_event_hook_list_.size() > 0) { | ||
envinst_sp->setup_code_event_handler(); | ||
} | ||
|
||
// Run EnvironmentCreationHook callbacks. | ||
env_creation_list_.for_each([envinst_sp](auto& stor) { | ||
stor.cb(envinst_sp, stor.data.get()); | ||
|
@@ -1415,6 +1492,18 @@ void EnvList::datapoint_cb_(std::queue<MetricsStream::Datapoint>&& q) { | |
}); | ||
} | ||
|
||
void EnvList::setup_code_event_handler(SharedEnvInst envinst_sp) { | ||
DCHECK_EQ(envinst_sp->isolate(), v8::Isolate::TryGetCurrent()); | ||
|
||
envinst_sp->setup_code_event_handler(); | ||
} | ||
|
||
void EnvList::disable_code_event_handler(SharedEnvInst envinst_sp) { | ||
DCHECK_EQ(envinst_sp->isolate(), v8::Isolate::TryGetCurrent()); | ||
|
||
envinst_sp->disable_code_event_handler(); | ||
} | ||
|
||
|
||
void EnvInst::send_datapoint(MetricsStream::Type type, | ||
double value) { | ||
|
@@ -1617,9 +1706,11 @@ void EnvList::log_written_cb_(ns_async*, EnvList* envlist) { | |
// registered users will need to receive their last set of metrics and a | ||
// notification that it'll be their last. | ||
void EnvList::removed_env_cb_(ns_async*, EnvList* envlist) { | ||
envlist->on_code_event_q_.reset(); | ||
if (envlist->continuous_profiler_) { | ||
envlist->continuous_profiler_.reset(); | ||
} | ||
|
||
Comment on lines
+1709
to
+1713
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Queue reset before drain – possible event drop
Consider:
🤖 Prompt for AI Agents
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think this is relevant once EnvList is gone There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @santigimeno, understood! If EnvList is planned to be removed in future refactoring, then improving the queue draining behavior in the current implementation wouldn't provide much value. Thanks for the clarification on the architectural direction. |
||
envlist->removed_env_msg_.close(); | ||
envlist->process_callbacks_msg_.close(); | ||
envlist->log_written_msg_.close(); | ||
|
@@ -1885,6 +1976,20 @@ void EnvList::fill_trace_id_q() { | |
} | ||
} | ||
|
||
void EnvList::got_code_event(CodeEventInfo&& info) { | ||
auto envinst_sp = EnvInst::GetInst(info.thread_id); | ||
if (envinst_sp == nullptr) { | ||
return; | ||
} | ||
|
||
code_event_hook_list_.for_each([&info, &envinst_sp](auto& stor, size_t size) { | ||
if (size == 1) { | ||
stor.cb(envinst_sp, std::move(info), stor.data.get()); | ||
} else { | ||
stor.cb(envinst_sp, info, stor.data.get()); | ||
} | ||
}); | ||
} | ||
|
||
void EnvInst::custom_command_(SharedEnvInst envinst_sp, | ||
const std::string req_id) { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The destructor of
CodeEventHook::Impl
unconditionally callsEnvList::Inst()->RemoveCodeEventHook(hook_)
, but there's no check ifhook_
is a valid iterator. IfSetup()
fails or is never called, this could lead to undefined behavior when removing an invalid iterator.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This cannot ever happen afaict