Skip to content

src: add AddCodeEventHook API for code events#327

Closed
santigimeno wants to merge 2 commits intonode-v22.x-nsolid-v5.xfrom
santi/jit_code_event_handler
Closed

src: add AddCodeEventHook API for code events#327
santigimeno wants to merge 2 commits intonode-v22.x-nsolid-v5.xfrom
santi/jit_code_event_handler

Conversation

@santigimeno
Copy link
Copy Markdown
Member

@santigimeno santigimeno commented Jun 10, 2025

Introduces a new API to register code event hooks in nsolid. This
includes new handler classes, queueing, and infrastructure to allow
extensible code event observation and callbacks.

Also:

src: a couple of improvements in TSList API
    
Adds a for_each method to TSList that passes the current list size and
item to the callback. Updates erase to return the new size after removal.
These changes improve thread-safe iteration and management of hooks.

Fixes: https://github.com/nodesource/ebpf-roadmap/issues/7

Summary by CodeRabbit

Summary by CodeRabbit

  • New Features

    • Introduced support for registering hooks that listen to JavaScript code events, allowing callbacks to be triggered on code changes.
    • Added APIs for managing code event hooks, including registration, removal, and callback handling.
    • Provided a native addon and JavaScript interface for working with code event hooks and receiving event details.
    • Implemented asynchronous dispatching of code event notifications to registered hooks with detailed event information.
    • Enhanced thread-safe list operations to report size during iteration and return updated size on element removal.
  • Tests

    • Added comprehensive tests for code event hook functionality, including registration, callback invocation, and cleanup.
    • Expanded test coverage for thread-safe list operations, verifying correct iteration and element removal behavior.

@santigimeno santigimeno self-assigned this Jun 10, 2025
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Jun 10, 2025

Walkthrough

This change introduces a new infrastructure for registering and handling code event hooks in the NSolid runtime. It adds new source and header files for a code event handler, updates build configuration, extends core classes to support asynchronous code event dispatching, and provides a public API for registering hooks. Comprehensive tests and a Node.js addon are included to validate and expose the feature.

Changes

File(s) Change Summary
node.gyp Added src/nsolid/nsolid_code_event_handler.cc to build sources.
src/nsolid/nsolid_code_event_handler.cc
src/nsolid/nsolid_code_event_handler.h
Introduced NSolidCodeEventHandler class to process and forward V8 code events.
src/nsolid.h Added CodeEventInfo struct, CodeEventHook class, and template API for registering code event hooks.
src/nsolid.cc Implemented CodeEventHook logic, lifecycle management, and internal hook registration function.
src/nsolid/nsolid_api.cc
src/nsolid/nsolid_api.h
Extended EnvInst and EnvList to manage code event handler setup, hook registration, and event dispatching.
src/nsolid/thread_safe.h Updated TSList API: for_each now provides list size; erase returns new size.
test/addons/nsolid-code-event-hook/binding.cc
test/addons/nsolid-code-event-hook/binding.gyp
test/addons/nsolid-code-event-hook/nsolid-code-event-hook.js
Added Node.js addon and test for registering and handling code event hooks from JavaScript.
test/cctest/test_nsolid_thread_safe.cc Added tests for new TSList behaviors: for_each with size and erase returning new size.

Sequence Diagram(s)

sequenceDiagram
    participant JS as JavaScript
    participant Addon as Native Addon
    participant NSolid as NSolid Runtime
    participant EnvList as EnvList
    participant EnvInst as EnvInst
    participant Handler as NSolidCodeEventHandler

    JS->>Addon: registerCodeEventHook(callback)
    Addon->>NSolid: AddCodeEventHook(cb, data)
    NSolid->>EnvList: AddCodeEventHook(cb, data)
    EnvList->>EnvInst: setup_code_event_handler()
    EnvInst->>Handler: (creates NSolidCodeEventHandler)

    Handler-->>Handler: (V8 triggers code event)
    Handler->>EnvList: on_code_event_q_.enqueue(CodeEventInfo)
    EnvList->>Addon: got_code_event(CodeEventInfo)
    Addon->>JS: callback(functionName, threadId)
Loading

Assessment against linked issues

Objective Addressed Explanation
Implement v8 CodeEvents API in the runtime (#7)

Assessment against linked issues: Out-of-scope changes

No out-of-scope changes detected related to the linked issue.

Suggested reviewers

  • juanarbol

Poem

🐇
In the warren of code, a new hook appears,
Catching events as V8 engineers.
With threads and queues, events now flow,
To JavaScript callbacks, in a seamless show.
Rabbits rejoice, for tracing is neat—
Code hops ahead, on nimble feet!

✨ Finishing Touches
  • 📝 Generate Docstrings

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR introduces a new API for registering and handling code event hooks in nsolid, along with several infrastructure updates to support asynchronous code event callbacks. Key changes include:

  • New TSList APIs to iterate with size parameters and return updated list sizes on erasure.
  • Integration of code event hook registration/handling in both C++ (nsolid APIs, code event handler, and event dispatching) and JavaScript (addon tests).
  • Updates to binding, build files, and API header modifications to support code event hooks.

Reviewed Changes

Copilot reviewed 12 out of 12 changed files in this pull request and generated no comments.

Show a summary per file
File Description
test/cctest/test_nsolid_thread_safe.cc Added tests for new TSList for_each and erase APIs.
test/addons/nsolid-code-event-hook/nsolid-code-event-hook.js Added tests for the new code event hook API in a multi-threaded context.
test/addons/nsolid-code-event-hook/binding.gyp Added binding definition for the new addon.
test/addons/nsolid-code-event-hook/binding.cc Implemented code event callback and hook registration logic in C++.
src/nsolid/thread_safe.h Added for_each overload and modified erase to return updated list size.
src/nsolid/nsolid_code_event_handler.h & .cc Introduced the NSolidCodeEventHandler for handling code events.
src/nsolid/nsolid_api.h & .cc Updated API to include code event handler setup and hook management.
src/nsolid/nsolid.cc Added CodeEventHook implementation and adjustments to hook lifecycle.
node.gyp Updated build configuration to include the new code event handler module.
Comments suppressed due to low confidence (2)

src/nsolid/nsolid.cc:625

  • Calling 'delete this;' in CodeEventHook::Dispose can be error-prone if not handled carefully. Consider clarifying the ownership semantics in documentation or refactoring to use a smart pointer based approach to avoid potential misuse.
delete this;

src/nsolid/nsolid_api.cc:1984

  • The conditional use of 'std::move(info)' when size equals 1 versus passing by value when multiple hooks are present could lead to inconsistent data ownership. Consider using a consistent strategy when dispatching CodeEventInfo to hooks to avoid potential issues with moved-from objects.
code_event_hook_list_.for_each([&info, &envinst_sp](auto& stor, size_t size) {

Copy link
Copy Markdown

@windsurf-bot windsurf-bot Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 file skipped due to size limits:
  • src/nsolid/nsolid_api.cc

💡 To request another review, post a new comment with "/windsurf-review".

Comment thread src/nsolid.cc
Comment on lines +603 to +606
~Impl() {
// Remove the hook from the TSList in EnvList
EnvList::Inst()->RemoveCodeEventHook(hook_);
}
Copy link
Copy Markdown

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 calls EnvList::Inst()->RemoveCodeEventHook(hook_), but there's no check if hook_ is a valid iterator. If Setup() fails or is never called, this could lead to undefined behavior when removing an invalid iterator.

Copy link
Copy Markdown
Member Author

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

Comment thread src/nsolid.cc
Comment on lines +674 to +685
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;
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The add_code_event_hook_ function allocates a CodeEventHook with new but doesn't check if DoSetup() might fail. If DoSetup() fails, the function would still return a partially initialized hook object that could cause issues when used.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

DoSetup cannot fail

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 11

🧹 Nitpick comments (10)
test/addons/nsolid-code-event-hook/nsolid-code-event-hook.js (1)

1-1: Remove redundant 'use strict' directive.

The static analysis tool correctly identifies that the 'use strict' directive is redundant since JavaScript modules are automatically in strict mode.

-'use strict';
🧰 Tools
🪛 Biome (1.9.4)

[error] 1-1: Redundant use strict directive.

The entire contents of JavaScript modules are automatically in strict mode, with no statement needed to initiate it.
Safe fix: Remove the redundant use strict directive.

(lint/suspicious/noRedundantUseStrict)

src/nsolid.cc (2)

625-627: Document the ownership model for Dispose().

The Dispose() method deletes the object itself (delete this), which requires careful documentation about ownership transfer and usage patterns to prevent use-after-free errors.

Consider adding a comment to clarify the ownership model:

 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;
 }

674-683: Consider using exceptions or error codes for allocation failures.

The function returns nullptr on allocation failure, but the caller might not check for this condition.

Consider either:

  1. Using exceptions (if allowed in the codebase)
  2. Adding an error code parameter
  3. At minimum, documenting the nullptr return value in the header file

Current implementation is acceptable if callers are expected to check for nullptr.

src/nsolid/nsolid_api.cc (3)

891-898: Async queue lambda captures info by forwarding-reference – use const ref

CodeEventInfo can potentially be large. The lambda already moves it when
delivering, so capturing by forwarding ref (&&) brings no extra value and
may lead to a double move/invalid use if extended.

-      +[](CodeEventInfo&& info, EnvList* envlist) {
-        envlist->got_code_event(std::move(info));
+      +[](CodeEventInfo info, EnvList* envlist) {
+        envlist->got_code_event(std::move(info));
       },

A plain pass-by-value here is cheaper and clearer.


1033-1056: Minor: potential redundant map copy during last-hook removal

When the last hook is removed we copy env_map_ even if it is already empty.
A quick guard can avoid unnecessary work in the common “no envs” case:

if (size == 0) {
  decltype(env_map_) env_map;
  {
    ns_mutex::scoped_lock lock(map_lock_);
    if (env_map_.empty()) return;
    env_map = env_map_;
  }
  ...
}

1498-1508: DCHECK_EQ may fire if invoked via EventLoop command

setup_code_event_handler() is scheduled with CommandType::InterruptOnly
which is executed inside V8’s interrupt callback. At that point
Isolate::TryGetCurrent() can legitimately return the same isolate but on a
different native thread (depending on how interrupts are delivered). Consider
removing or downgrading the DCHECK_EQ to avoid false positives in RELEASE +
NODE_DEBUG builds.

src/nsolid.h (2)

660-672: Consider making the struct fields const where appropriate.

Since CodeEventInfo is likely used to pass immutable event data to callbacks, consider making appropriate fields const to prevent accidental modification:

 struct CodeEventInfo {
-  uint64_t thread_id;
-  uint64_t timestamp;
-  v8::CodeEventType type;
-  uintptr_t code_start;
-  uintptr_t new_code_start;
-  size_t code_len;
-  std::string fn_name;
-  std::string script_name;
-  int script_line;
-  int script_column;
-  std::string comment;
+  const uint64_t thread_id;
+  const uint64_t timestamp;
+  const v8::CodeEventType type;
+  const uintptr_t code_start;
+  const uintptr_t new_code_start;
+  const size_t code_len;
+  const std::string fn_name;
+  const std::string script_name;
+  const int script_line;
+  const int script_column;
+  const std::string comment;
 };

679-686: Add documentation for the Dispose() method lifecycle.

While the brief describes that Dispose() removes the hook and releases resources, it would be helpful to document whether:

  • The method is idempotent (can be called multiple times safely)
  • The method blocks until any in-progress callbacks complete
  • The object becomes invalid immediately after calling Dispose()
src/nsolid/nsolid_api.h (2)

248-250: Consider adding documentation for the new methods.

These methods are part of the public interface of EnvInst. Consider adding documentation to explain:

  • When these methods should be called
  • Thread safety guarantees
  • Any preconditions or postconditions

653-653: Consider documenting the threading model for code event dispatch.

The method got_code_event takes an rvalue reference, suggesting it's designed for efficient event forwarding. Document:

  • Which thread(s) call this method
  • How it interacts with the async queue
  • Performance considerations
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2492fc8 and 62ac188.

📒 Files selected for processing (12)
  • node.gyp (1 hunks)
  • src/nsolid.cc (2 hunks)
  • src/nsolid.h (7 hunks)
  • src/nsolid/nsolid_api.cc (8 hunks)
  • src/nsolid/nsolid_api.h (11 hunks)
  • src/nsolid/nsolid_code_event_handler.cc (1 hunks)
  • src/nsolid/nsolid_code_event_handler.h (1 hunks)
  • src/nsolid/thread_safe.h (2 hunks)
  • test/addons/nsolid-code-event-hook/binding.cc (1 hunks)
  • test/addons/nsolid-code-event-hook/binding.gyp (1 hunks)
  • test/addons/nsolid-code-event-hook/nsolid-code-event-hook.js (1 hunks)
  • test/cctest/test_nsolid_thread_safe.cc (1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (4)
test/addons/nsolid-code-event-hook/nsolid-code-event-hook.js (1)
test/common/index.js (1)
  • buildType (155-157)
src/nsolid/nsolid_code_event_handler.cc (3)
src/nsolid/nsolid_code_event_handler.h (3)
  • NSolidCodeEventHandler (14-14)
  • NSolidCodeEventHandler (15-15)
  • code_event (17-17)
src/util.h (1)
  • Utf8Value (548-548)
src/nsolid/nsolid_api.cc (2)
  • Inst (914-917)
  • Inst (914-914)
test/addons/nsolid-code-event-hook/binding.cc (3)
src/nsolid.cc (10)
  • CodeEventHook (620-621)
  • CodeEventHook (623-623)
  • cb (610-615)
  • cb (610-612)
  • GetThreadId (56-64)
  • GetThreadId (56-56)
  • GetLocalEnvInst (43-45)
  • GetLocalEnvInst (43-43)
  • GetLocalEnvInst (47-49)
  • GetLocalEnvInst (47-47)
src/nsolid.h (14)
  • CodeEventHook (679-702)
  • CodeEventHook (690-690)
  • CodeEventHook (693-693)
  • RunCommand (523-526)
  • RunCommand (1602-1626)
  • RunCommand (1602-1605)
  • cb (658-658)
  • thread_id (852-852)
  • thread_id (852-852)
  • GetThreadId (404-404)
  • GetLocalEnvInst (385-385)
  • GetLocalEnvInst (391-391)
  • AddCodeEventHook (1842-1860)
  • AddCodeEventHook (1842-1842)
src/nsolid/nsolid_api.cc (8)
  • RunCommand (157-202)
  • RunCommand (157-160)
  • GetCurrent (433-436)
  • GetCurrent (433-433)
  • GetCurrent (439-442)
  • GetCurrent (439-439)
  • AddCodeEventHook (1007-1031)
  • AddCodeEventHook (1007-1010)
src/nsolid/nsolid_api.cc (3)
src/nsolid/nsolid_api.h (23)
  • data (80-80)
  • data (81-81)
  • data (515-518)
  • data (520-523)
  • data (525-528)
  • data (529-532)
  • data (539-542)
  • data (544-546)
  • envinst (340-341)
  • envinst (342-343)
  • envinst (344-345)
  • envinst (346-347)
  • it (547-547)
  • it (620-620)
  • envinst_sp (195-198)
  • envinst_sp (334-335)
  • envinst_sp (662-662)
  • envinst_sp (670-670)
  • envinst_sp (671-671)
  • envinst_sp (672-672)
  • envinst_sp (674-674)
  • envinst_sp (678-678)
  • envinst_sp (679-679)
src/nsolid.h (5)
  • AddCodeEventHook (1842-1860)
  • AddCodeEventHook (1842-1842)
  • RunCommand (523-526)
  • RunCommand (1602-1626)
  • RunCommand (1602-1605)
src/nsolid.cc (2)
  • deleter (463-463)
  • deleter (611-611)
🪛 Biome (1.9.4)
test/addons/nsolid-code-event-hook/nsolid-code-event-hook.js

[error] 1-1: Redundant use strict directive.

The entire contents of JavaScript modules are automatically in strict mode, with no statement needed to initiate it.
Safe fix: Remove the redundant use strict directive.

(lint/suspicious/noRedundantUseStrict)

⏰ Context from checks skipped due to timeout of 90000ms (10)
  • GitHub Check: test-linux (ubuntu-24.04-arm)
  • GitHub Check: test-linux (ubuntu-24.04)
  • GitHub Check: test-macOS
  • GitHub Check: coverage-windows
  • GitHub Check: build-docs
  • GitHub Check: coverage-linux-without-intl
  • GitHub Check: coverage-linux
  • GitHub Check: lint-js-and-md
  • GitHub Check: lint-cpp
  • GitHub Check: build-tarball
🔇 Additional comments (25)
src/nsolid/thread_safe.h (4)

149-155: LGTM! Well-designed enhancement for thread-safe iteration with size awareness.

The new for_each overload that provides the current list size to the callback is a thoughtful addition. Capturing the size once before iteration ensures consistency throughout the callback execution, which is crucial for the code event hook infrastructure.


156-160: Excellent improvement to return the new size after element removal.

Returning the updated list size from the erase method provides valuable feedback for managing hook lifecycles and is more informative than the previous void return type.


189-195: Consistent implementation in the pointer specialization.

The changes are properly mirrored in the pointer template specialization, maintaining API consistency across both variants of TSList.


196-200: Good consistency in the pointer specialization's erase method.

The pointer specialization correctly returns the new size after erasure, maintaining consistency with the main template implementation.

test/addons/nsolid-code-event-hook/binding.gyp (1)

1-9: Clean and correct build configuration for the test addon.

The binding.gyp file follows standard node-gyp conventions with appropriate debug symbols enabled for testing. The configuration correctly references the binding.cc source file and establishes the "binding" target.

node.gyp (1)

517-517: Proper integration of the new code event handler into the build system.

The source file is correctly added to the nsolid_sources list, following the established pattern for NSolid components and maintaining consistency with the directory structure.

src/nsolid/nsolid_code_event_handler.h (1)

1-28: Excellent design for the NSolid code event handler.

This header file demonstrates several best practices:

  • Proper header guards and conditional compilation for internal use only
  • Clean inheritance from v8::CodeEventHandler with appropriate method override
  • Virtual destructor following best practices for polymorphic base classes
  • Well-designed constructor taking necessary context (isolate and thread ID)
  • Clear separation between public interface and private implementation details

The design properly encapsulates the functionality needed for capturing and processing V8 code events within the NSolid framework.

test/addons/nsolid-code-event-hook/nsolid-code-event-hook.js (1)

3-91: Comprehensive and well-structured test for code event hooks.

This test provides excellent coverage of the code event hook functionality:

  • Thorough testing scope: Tests registration, callback invocation, selective unregistration, and complete cleanup
  • Thread-aware testing: Uses worker threads to verify thread-specific hook behavior and thread ID correctness
  • State management: Proper state tracking and reset mechanisms for reliable test execution
  • Asynchronous handling: Appropriate use of setTimeout for asynchronous event testing
  • Robust assertions: Verifies both positive cases (hooks firing) and negative cases (hooks not firing after unregistration)

The test design effectively validates the entire lifecycle of code event hooks and ensures proper isolation between different hook instances.

src/nsolid/nsolid_code_event_handler.cc (2)

1-39: LGTM! Clean implementation of the code event handler.

The implementation correctly:

  • Inherits from v8::CodeEventHandler and overrides the Handle method
  • Extracts all relevant information from the CodeEvent object
  • Creates a well-structured CodeEventInfo with proper move semantics
  • Enqueues the event for asynchronous processing

34-34:

Details

✅ Verification successful

Verify that GetComment() never returns null.

The code directly passes the result of code_event->GetComment() to std::string constructor. If GetComment() can return a null pointer, this would cause undefined behavior.


🏁 Script executed:

#!/bin/bash
# Check the V8 header files or documentation for GetComment() behavior
rg -A 5 "GetComment" --glob "*.h" --glob "*.cc" deps/v8

Length of output: 5140


Confirmed GetComment() always returns a non-null C-string

V8’s own code and tests assume it never yields a null pointer:

  • In deps/v8/src/api/api.cc:

    const char* CodeEvent::GetComment() {
      return reinterpret_cast<i::CodeEvent*>(this)->comment;
    }

    The internal comment field is always initialized to point at a valid string (empty or otherwise).

  • In deps/v8/src/codegen/code-comments.cc:

    const char* CodeCommentsIterator::GetComment() const {
      const char* comment_string = …;
      CHECK_EQ(GetCommentSize(), strlen(comment_string) + 1);
      return comment_string;
    }

    The CHECK_EQ guarantees comment_string is non-null and null-terminated.

  • V8 unit tests (e.g. logging/log-unittest.cc) call std::string(code_event->GetComment()) without guarding against null, and would crash if GetComment() could ever return nullptr.

No defensive null check is needed before constructing std::string from GetComment().

test/cctest/test_nsolid_thread_safe.cc (2)

53-72: Well-structured test for the enhanced for_each method.

The test properly verifies that:

  • The callback receives both the value and the current list size
  • The size remains consistent (3) during iteration
  • Values are visited in the expected order

74-83: Good test coverage for the erase method returning size.

The test correctly verifies that erase returns the updated list size after each removal.

src/nsolid.cc (1)

600-618: Clean implementation of the CodeEventHook internal management.

The Impl class properly encapsulates the hook lifecycle:

  • Adds the hook to EnvList on setup
  • Removes it on destruction
  • Follows RAII principles
src/nsolid/nsolid_api.cc (2)

1120-1123: Dependent on previous fix – keep eye on enable logic

This check is correct only when the race in AddCodeEventHook() is fixed.
Otherwise size() could still be zero here and the handler would never be
enabled for this environment.


844-852: 🛠️ Refactor suggestion

EnvInst::setup_code_event_handler() leaks the old handler on re-enable

If a handler was created previously and Disable() was called, code_event_handler_ still holds the (disabled) instance.
Calling setup_code_event_handler() again will therefore not create a fresh handler nor re-enable the old one.

 void EnvInst::setup_code_event_handler() {
-  if (!code_event_handler_) {
-    code_event_handler_ =
-      std::make_unique<NSolidCodeEventHandler>(isolate_, thread_id_);
-  }
-
-  code_event_handler_->Enable();
+  if (!code_event_handler_)
+    code_event_handler_ =
+        std::make_unique<NSolidCodeEventHandler>(isolate_, thread_id_);
+
+  code_event_handler_->Enable();            // idempotent – safe to call twice
 }

Consider resetting the unique-ptr in disable_code_event_handler() (see next comment) so that a brand-new handler is constructed next time, avoiding surprises caused by a lingering disabled instance.

Likely an incorrect or invalid review comment.

src/nsolid.h (5)

290-290: LGTM!

The function pointer signature is correctly defined to handle code events with the appropriate parameters.


320-320: LGTM!

The proxy function declaration follows the established pattern for other hook proxies in the codebase.


356-358: LGTM!

The internal function declaration correctly returns a CodeEventHook* and takes the necessary parameters for hook registration.


691-692: LGTM!

The friend declaration is necessary to allow the internal function to construct CodeEventHook objects while keeping the constructor private.


1930-1935: LGTM!

The proxy implementation correctly moves the CodeEventInfo to avoid unnecessary copying of the potentially large struct.

src/nsolid/nsolid_api.h (5)

21-21: LGTM!

The include is necessary for the new async queue functionality used for code event dispatching.


427-427: LGTM!

The unique pointer correctly manages the lifetime of the code event handler.


504-507: LGTM!

The struct follows the established pattern for hook storage with proper callback and user data management.


757-758: LGTM!

The async queue and hook list are appropriately placed for managing code event distribution to registered hooks.


544-547:

Details

✅ Verification successful

Verify thread safety of the hook list operations.

The methods operate on TSList which should be thread-safe based on the naming convention. However, ensure that the iterator returned by AddCodeEventHook remains valid across concurrent operations.


🏁 Script executed:

#!/bin/bash
# Check TSList implementation for thread safety guarantees
rg -A 10 "class TSList" src/nsolid/thread_safe.h

Length of output: 50


We need to locate the TSList definition to verify its thread-safety guarantees. Let’s search the entire repository:


🏁 Script executed:

#!/bin/bash
# Search for TSList class definition and usages across the repo
rg -n "class TSList" -n .
rg -n "TSList<" -n .

Length of output: 2633


Continuing verification: locate the full TSList implementation to confirm iterator safety.

Adjust search if needed based on the results.


🏁 Script executed:

#!/bin/bash
# Show TSList implementation around its declaration
rg -n -A20 -B5 "struct TSList<DataType\*>" src/nsolid/thread_safe.h

Length of output: 918


Continuing verification: extract the full TSList<DataType*> specialization (including any erase/remove methods) to confirm that all list operations are mutex-protected.


🏁 Script executed:

#!/bin/bash
# Print lines 170–350 of thread_safe.h to capture the entire TSList<DataType*> block
sed -n '170,350p' src/nsolid/thread_safe.h

Length of output: 1235


Thread safety and iterator validity confirmed

The TSList<DataType*> specialization in src/nsolid/thread_safe.h uses a single nsuv::ns_mutex to guard all push_back, erase, size, and for_each operations.

  • push_back acquires the lock, appends the element, and returns a std::list iterator. According to C++ guarantees for std::list, this iterator remains valid across subsequent insertions.
  • Only calling erase(iterator) (i.e. via RemoveCodeEventHook) will invalidate that specific iterator—this matches the intended removal semantics.

No further changes are required.

Comment thread test/cctest/test_nsolid_thread_safe.cc
Comment thread test/addons/nsolid-code-event-hook/binding.cc
Comment thread test/addons/nsolid-code-event-hook/binding.cc
Comment thread test/addons/nsolid-code-event-hook/binding.cc
Comment thread test/addons/nsolid-code-event-hook/binding.cc
Comment thread src/nsolid/nsolid_api.cc
Comment on lines +1712 to +1714
envlist->on_code_event_q_.reset();
if (envlist->continuous_profiler_) {
envlist->continuous_profiler_.reset();
}

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot Jun 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Queue reset before drain – possible event drop

on_code_event_q_.reset() is called unconditionally during shutdown. If
there are still pending code events they will be destroyed silently, skipping
user hooks and making debugging harder.

Consider:

  1. Draining the queue synchronously (dequeue until empty) before reset.
  2. Or calling reset() after confirming thread_loop_ has stopped
    processing callbacks.
🤖 Prompt for AI Agents
In src/nsolid/nsolid_api.cc around lines 1712 to 1716, the on_code_event_q_
queue is reset unconditionally during shutdown, which can cause pending code
events to be dropped silently. To fix this, modify the shutdown sequence to
first drain the queue synchronously by dequeuing all events until it is empty
before calling reset(), or ensure that thread_loop_ has fully stopped processing
callbacks before resetting the queue. This prevents loss of events and ensures
user hooks are executed properly.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this is relevant once EnvList is gone

Copy link
Copy Markdown

Choose a reason for hiding this comment

The 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.

Comment thread src/nsolid/nsolid_api.cc
Comment thread src/nsolid/nsolid_api.cc
Comment thread src/nsolid/nsolid_api.cc
Comment thread src/nsolid.h
@santigimeno santigimeno force-pushed the santi/jit_code_event_handler branch 2 times, most recently from ba37b75 to 16f39c0 Compare June 10, 2025 11:02
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (1)
test/addons/nsolid-code-event-hook/binding.cc (1)

53-58: ⚠️ Potential issue

Add error handling for V8 operations.

The code uses ToLocalChecked() without error handling, which will crash the process if the operations fail.

-      Local<Value> argv[2] = {
-        String::NewFromUtf8(isolate, info.fn_name.c_str()).ToLocalChecked(),
-        Number::New(isolate, info.thread_id),
-      };
-      // Call the callback in the current context
-      cb->Call(isolate->GetCurrentContext(),
-               Undefined(isolate), 2, argv).ToLocalChecked();
+      v8::TryCatch try_catch(isolate);
+      Local<String> fn_name;
+      if (!String::NewFromUtf8(isolate, info.fn_name.c_str()).ToLocal(&fn_name)) {
+        // Handle string creation failure
+        return;
+      }
+      Local<Value> argv[2] = {
+        fn_name,
+        Number::New(isolate, info.thread_id),
+      };
+      // Call the callback in the current context
+      Local<Value> ret;
+      if (!cb->Call(isolate->GetCurrentContext(),
+                    Undefined(isolate), 2, argv).ToLocal(&ret)) {
+        // Handle exception if needed
+        if (try_catch.HasCaught()) {
+          try_catch.ReThrow();
+        }
+      }
🧹 Nitpick comments (3)
test/addons/nsolid-code-event-hook/nsolid-code-event-hook.js (3)

1-1: Remove redundant 'use strict' directive.

JavaScript modules are automatically in strict mode.

-'use strict';
-
🧰 Tools
🪛 Biome (1.9.4)

[error] 1-1: Redundant use strict directive.

The entire contents of JavaScript modules are automatically in strict mode, with no statement needed to initiate it.
Safe fix: Remove the redundant use strict directive.

(lint/suspicious/noRedundantUseStrict)


9-10: Improve thread check readability.

The condition is checking if the process is a worker thread but not launched with the correct PID argument.

-if (!isMainThread && +process.argv[2] !== process.pid)
+// Skip if this is a worker thread that wasn't launched by the main test
+if (!isMainThread && +process.argv[2] !== process.pid)

46-73: Consider making the test more deterministic.

The test uses multiple fixed 500ms timeouts which could lead to flaky tests on heavily loaded systems. Consider using a more deterministic approach.

Would you like me to suggest an implementation using promises or event-based synchronization instead of fixed timeouts to make the test more reliable?

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 62ac188 and 16f39c0.

📒 Files selected for processing (12)
  • node.gyp (1 hunks)
  • src/nsolid.cc (2 hunks)
  • src/nsolid.h (7 hunks)
  • src/nsolid/nsolid_api.cc (8 hunks)
  • src/nsolid/nsolid_api.h (11 hunks)
  • src/nsolid/nsolid_code_event_handler.cc (1 hunks)
  • src/nsolid/nsolid_code_event_handler.h (1 hunks)
  • src/nsolid/thread_safe.h (2 hunks)
  • test/addons/nsolid-code-event-hook/binding.cc (1 hunks)
  • test/addons/nsolid-code-event-hook/binding.gyp (1 hunks)
  • test/addons/nsolid-code-event-hook/nsolid-code-event-hook.js (1 hunks)
  • test/cctest/test_nsolid_thread_safe.cc (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (10)
  • test/addons/nsolid-code-event-hook/binding.gyp
  • node.gyp
  • src/nsolid/nsolid_code_event_handler.cc
  • src/nsolid/nsolid_code_event_handler.h
  • src/nsolid/thread_safe.h
  • test/cctest/test_nsolid_thread_safe.cc
  • src/nsolid.cc
  • src/nsolid/nsolid_api.cc
  • src/nsolid.h
  • src/nsolid/nsolid_api.h
🧰 Additional context used
🧠 Learnings (1)
test/addons/nsolid-code-event-hook/binding.cc (1)
Learnt from: santigimeno
PR: nodesource/nsolid#327
File: test/addons/nsolid-code-event-hook/binding.cc:64-78
Timestamp: 2025-06-10T09:52:49.401Z
Learning: In the nsolid codebase, test files in test/addons/ consistently use assert() for input validation and runtime checks, not CHECK() macros. This is the established testing convention.
🧬 Code Graph Analysis (2)
test/addons/nsolid-code-event-hook/binding.cc (4)
src/nsolid.cc (12)
  • CodeEventHook (620-621)
  • CodeEventHook (623-623)
  • cb (610-615)
  • cb (610-612)
  • GetThreadId (56-64)
  • GetThreadId (56-56)
  • GetLocalEnvInst (43-45)
  • GetLocalEnvInst (43-43)
  • GetLocalEnvInst (47-49)
  • GetLocalEnvInst (47-47)
  • IsMainThread (91-96)
  • IsMainThread (91-91)
src/nsolid.h (15)
  • CodeEventHook (679-702)
  • CodeEventHook (690-690)
  • CodeEventHook (693-693)
  • RunCommand (523-526)
  • RunCommand (1602-1626)
  • RunCommand (1602-1605)
  • cb (658-658)
  • thread_id (852-852)
  • thread_id (852-852)
  • GetThreadId (404-404)
  • GetLocalEnvInst (385-385)
  • GetLocalEnvInst (391-391)
  • AddCodeEventHook (1842-1860)
  • AddCodeEventHook (1842-1842)
  • IsMainThread (426-426)
src/nsolid/nsolid_api.cc (8)
  • RunCommand (157-202)
  • RunCommand (157-160)
  • GetCurrent (433-436)
  • GetCurrent (433-433)
  • GetCurrent (439-442)
  • GetCurrent (439-439)
  • AddCodeEventHook (1002-1028)
  • AddCodeEventHook (1002-1005)
test/addons/nsolid-code-event-hook/nsolid-code-event-hook.js (1)
  • assert (4-4)
test/addons/nsolid-code-event-hook/nsolid-code-event-hook.js (1)
test/common/index.js (1)
  • buildType (155-157)
🪛 Biome (1.9.4)
test/addons/nsolid-code-event-hook/nsolid-code-event-hook.js

[error] 1-1: Redundant use strict directive.

The entire contents of JavaScript modules are automatically in strict mode, with no statement needed to initiate it.
Safe fix: Remove the redundant use strict directive.

(lint/suspicious/noRedundantUseStrict)

⏰ Context from checks skipped due to timeout of 90000ms (7)
  • GitHub Check: test-tarball-linux
  • GitHub Check: test-linux (ubuntu-24.04)
  • GitHub Check: test-linux (ubuntu-24.04-arm)
  • GitHub Check: test-macOS
  • GitHub Check: coverage-linux-without-intl
  • GitHub Check: coverage-linux
  • GitHub Check: coverage-windows
🔇 Additional comments (1)
test/addons/nsolid-code-event-hook/binding.cc (1)

38-61: ⚠️ Potential issue

Add thread safety for js_callback_per_thread access.

The CodeEventCallback function accesses holder->js_callback_per_thread without holding the mutex, while RegisterJSCodeEventCallback modifies it under the mutex lock. This creates a potential data race.

Since the map access happens in a lambda that runs on the event loop, you need to ensure thread-safe access:

 void CodeEventCallback(std::shared_ptr<node::nsolid::EnvInst> envinst,
                        const node::nsolid::CodeEventInfo& info,
                        HookHolder* holder) {
   assert(0 == node::nsolid::RunCommand(
       envinst,
       node::nsolid::CommandType::EventLoop,
       [holder](node::nsolid::SharedEnvInst envinst,
                const node::nsolid::CodeEventInfo& info) {
+    nsuv::ns_mutex::scoped_lock lock(hooks_mutex_);
     // Call JS callback with function name
     if (!holder->js_callback_per_thread[info.thread_id].IsEmpty()) {
       Isolate* isolate = Isolate::GetCurrent();
       v8::HandleScope scope(isolate);
       Local<Function> cb =
         holder->js_callback_per_thread[info.thread_id].Get(isolate);
+      lock.unlock();  // Release lock before calling JS
       Local<Value> argv[2] = {
         String::NewFromUtf8(isolate, info.fn_name.c_str()).ToLocalChecked(),
         Number::New(isolate, info.thread_id),
       };
       // Call the callback in the current context
       cb->Call(isolate->GetCurrentContext(),
                Undefined(isolate), 2, argv).ToLocalChecked();
     }
   }, info));
 }

Likely an incorrect or invalid review comment.

Comment thread test/addons/nsolid-code-event-hook/binding.cc
Comment thread test/addons/nsolid-code-event-hook/binding.cc
@santigimeno santigimeno force-pushed the santi/jit_code_event_handler branch from 16f39c0 to c9ae3c8 Compare July 2, 2025 08:13
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

♻️ Duplicate comments (1)
src/nsolid/nsolid_api.cc (1)

1709-1709: Potential event loss during shutdown

The queue reset happens unconditionally during shutdown, which could silently drop pending code events as mentioned in previous reviews. While this may be acceptable for shutdown scenarios, consider if there are critical events that should be processed before reset.

🧹 Nitpick comments (4)
test/addons/nsolid-code-event-hook/nsolid-code-event-hook.js (2)

1-1: Remove redundant 'use strict' directive.

The static analysis tool correctly identifies that the 'use strict' directive is redundant in ES modules, as they are automatically in strict mode.

-'use strict';

9-10: Clarify the skip condition logic.

The skip condition logic is confusing and may not work as intended. The condition +process.argv[2] !== process.pid compares the passed main thread PID with the current process PID, but worker threads share the same process ID as the main thread in Node.js.

Consider simplifying this logic or adding a comment to explain the intended behavior:

-if (!isMainThread && +process.argv[2] !== process.pid)
-  skip('Test must first run as the main thread');
+if (!isMainThread && process.argv.length < 5)
+  skip('Test must first run as the main thread');

Or add a comment explaining the current logic:

+// Skip if running in worker thread but without expected main thread PID
 if (!isMainThread && +process.argv[2] !== process.pid)
   skip('Test must first run as the main thread');
src/nsolid/nsolid_api.cc (2)

1002-1028: Verify the race condition fix implementation

The reordering of operations (add hook first, then enable handlers) should address the race condition mentioned in previous reviews. However, there's no error handling when RunCommand fails for individual environments.

Consider adding error handling:

 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.
+    // Log warning about failed handler setup for debugging
   }
 }

1979-1992: Review move semantics optimization

The optimization to move the event when there's only one hook (line 1987) vs. copying for multiple hooks (line 1989) is correct. However, ensure that hooks are prepared to handle both moved and copied CodeEventInfo objects consistently.

The null check for envinst_sp (line 1981-1983) properly handles late-arriving events from destroyed environments.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 16f39c0 and c9ae3c8.

📒 Files selected for processing (12)
  • node.gyp (1 hunks)
  • src/nsolid.cc (2 hunks)
  • src/nsolid.h (7 hunks)
  • src/nsolid/nsolid_api.cc (8 hunks)
  • src/nsolid/nsolid_api.h (11 hunks)
  • src/nsolid/nsolid_code_event_handler.cc (1 hunks)
  • src/nsolid/nsolid_code_event_handler.h (1 hunks)
  • src/nsolid/thread_safe.h (2 hunks)
  • test/addons/nsolid-code-event-hook/binding.cc (1 hunks)
  • test/addons/nsolid-code-event-hook/binding.gyp (1 hunks)
  • test/addons/nsolid-code-event-hook/nsolid-code-event-hook.js (1 hunks)
  • test/cctest/test_nsolid_thread_safe.cc (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (10)
  • test/addons/nsolid-code-event-hook/binding.gyp
  • node.gyp
  • src/nsolid/nsolid_code_event_handler.h
  • src/nsolid/thread_safe.h
  • test/cctest/test_nsolid_thread_safe.cc
  • src/nsolid/nsolid_code_event_handler.cc
  • src/nsolid/nsolid_api.h
  • test/addons/nsolid-code-event-hook/binding.cc
  • src/nsolid.cc
  • src/nsolid.h
🧰 Additional context used
🧠 Learnings (2)
src/nsolid/nsolid_api.cc (3)
Learnt from: santigimeno
PR: nodesource/nsolid#319
File: src/nsolid/async_ts_queue.h:123-135
Timestamp: 2025-06-03T11:35:52.830Z
Learning: The unbounded timer loop issue in AsyncTSQueue has been resolved by implementing proper timer arming logic with the timer_armed_ atomic flag and adding a queue emptiness check (!queue->queue_.empty()) in the async_callback method before restarting the timer. This prevents the timer from firing continuously when the queue is empty.
Learnt from: santigimeno
PR: nodesource/nsolid#319
File: src/nsolid/async_ts_queue.h:123-135
Timestamp: 2025-06-03T11:35:52.830Z
Learning: The unbounded timer loop issue in AsyncTSQueue has been resolved by implementing proper timer arming logic with the timer_armed_ atomic flag and adding a queue emptiness check (!queue->queue_.empty()) in the async_callback method before restarting the timer. This prevents the timer from firing continuously when the queue is empty.
Learnt from: santigimeno
PR: nodesource/nsolid#327
File: test/addons/nsolid-code-event-hook/binding.cc:64-78
Timestamp: 2025-06-10T09:52:49.401Z
Learning: In the nsolid codebase, test files in test/addons/ consistently use assert() for input validation and runtime checks, not CHECK() macros. This is the established testing convention.
test/addons/nsolid-code-event-hook/nsolid-code-event-hook.js (1)
Learnt from: santigimeno
PR: nodesource/nsolid#327
File: test/addons/nsolid-code-event-hook/binding.cc:64-78
Timestamp: 2025-06-10T09:52:49.401Z
Learning: In the nsolid codebase, test files in test/addons/ consistently use assert() for input validation and runtime checks, not CHECK() macros. This is the established testing convention.
🧬 Code Graph Analysis (1)
test/addons/nsolid-code-event-hook/nsolid-code-event-hook.js (1)
test/common/index.js (1)
  • buildType (155-157)
🪛 Biome (1.9.4)
test/addons/nsolid-code-event-hook/nsolid-code-event-hook.js

[error] 1-1: Redundant use strict directive.

The entire contents of JavaScript modules are automatically in strict mode, with no statement needed to initiate it.
Safe fix: Remove the redundant use strict directive.

(lint/suspicious/noRedundantUseStrict)

⏰ Context from checks skipped due to timeout of 90000ms (9)
  • GitHub Check: coverage-linux
  • GitHub Check: test-linux (ubuntu-24.04-arm)
  • GitHub Check: test-linux (ubuntu-24.04)
  • GitHub Check: coverage-windows
  • GitHub Check: coverage-linux-without-intl
  • GitHub Check: test-macOS
  • GitHub Check: build-tarball
  • GitHub Check: lint-js-and-md
  • GitHub Check: build-docs
🔇 Additional comments (7)
test/addons/nsolid-code-event-hook/nsolid-code-event-hook.js (1)

27-74: Excellent test coverage of hook lifecycle.

The test comprehensively validates the hook lifecycle including registration, event triggering, selective unregistration, and cleanup. The logic correctly verifies that hooks receive expected parameters and that unregistered hooks are properly cleaned up.

src/nsolid/nsolid_api.cc (6)

2-2: LGTM - Clean include addition

The include for the code event handler header is properly added.


886-892: LGTM - Proper async queue initialization

The code event queue is correctly initialized with a lambda callback to got_code_event(). The queue setup follows the established pattern used elsewhere in the codebase.


1030-1053: LGTM - Consistent hook removal pattern

The hook removal logic correctly checks if the list becomes empty and disables handlers on all environments. The pattern matches the addition logic and properly handles the last-hook-removed case.


1117-1119: LGTM - Proper hook integration in environment creation

The code correctly sets up the code event handler when adding a new environment if hooks are already registered. This ensures new environments don't miss code events.


1495-1505: LGTM - Clean static wrapper methods

The static helper methods properly ensure the handler setup/disable operations run on the correct isolate thread with appropriate assertions.


844-854: Confirm proper teardown of the code event handler

In EnvInst::disable_code_event_handler() (src/nsolid/nsolid_api.cc:844–854) you currently only do:

code_event_handler_.reset();

without first unregistering the V8 handler. NSolidCodeEventHandler does not override a Disable() method and its destructor is empty, so it’s unclear whether destroying the pointer automatically calls into the base v8::CodeEventHandler to disable itself.

Please verify one of the following:

  • That destroying the NSolidCodeEventHandler (via its destructor and base‐class cleanup) unregisters the V8 code event handler, or
  • If not, explicitly call
    code_event_handler_->Disable();
    before resetting the unique_ptr to avoid leaving V8 in an inconsistent state.

Comment thread test/addons/nsolid-code-event-hook/nsolid-code-event-hook.js
Comment thread test/addons/nsolid-code-event-hook/nsolid-code-event-hook.js
Comment on lines +87 to +88
hookIndex1 = +process.argv[3];
hookIndex2 = +process.argv[4];
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add input validation for command line arguments.

The parsing of command line arguments lacks validation, which could lead to unexpected behavior if arguments are missing or invalid.

 } else {
+  assert(process.argv.length >= 5, 'Missing required arguments');
   hookIndex1 = +process.argv[3];
   hookIndex2 = +process.argv[4];
+  assert(Number.isInteger(hookIndex1) && hookIndex1 >= 0, 'Invalid hookIndex1');
+  assert(Number.isInteger(hookIndex2) && hookIndex2 >= 0, 'Invalid hookIndex2');
 }
🤖 Prompt for AI Agents
In test/addons/nsolid-code-event-hook/nsolid-code-event-hook.js around lines 87
to 88, the code directly converts process.argv[3] and process.argv[4] to numbers
without validating their presence or correctness. Add input validation to check
if these arguments exist and are valid numbers before using them. If invalid or
missing, handle the error gracefully, such as by logging an error message and
exiting the process or providing default values.

@santigimeno santigimeno force-pushed the santi/jit_code_event_handler branch from c9ae3c8 to 41949db Compare July 15, 2025 15:50
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (2)
test/addons/nsolid-code-event-hook/nsolid-code-event-hook.js (2)

82-85: Add error handling for worker thread.

The worker thread creation lacks error handling, which could lead to silent failures in the test.

   const worker = new Worker(__filename, { argv: [process.pid, hookIndex1, hookIndex2] });
+  worker.on('error', (err) => {
+    throw err;
+  });
   worker.on('exit', (code) => {
     assert.strictEqual(code, 0);
   });

47-73: Replace hardcoded timeouts with more reliable synchronization.

The test relies on hardcoded 500ms timeouts which could lead to flaky test behavior in CI environments or under load. Consider using more deterministic synchronization mechanisms like setImmediate() or process.nextTick().

-  // Wait a tick to allow the hook to fire
-  setTimeout(() => {
+  // Use setImmediate for more reliable timing
+  setImmediate(() => {
     assert.ok(state1.called, 'Code event hook was not called');
     assert.ok(state1.customFnNameReceived, 'Custom function name was not received');
     assert.ok(state2.called, 'Code event hook was not called');
     assert.ok(state2.customFnNameReceived, 'Custom function name was not received');

     // Unregister the hooks
     binding.unregisterCodeEventHook(hookIndex1);

     resetState(state1);
     resetState(state2);

     // The hook should not be called after unregistering
     binding.triggerCodeEvent();
-    setTimeout(() => {
+    setImmediate(() => {
       assert.ok(!state1.called, 'Code event hook was called after unregistering');
       assert.ok(state2.called, 'Code event hook was not called after unregistering');
       binding.unregisterCodeEventHook(hookIndex2);
       resetState(state1);
       resetState(state2);
-      setTimeout(() => {
+      setImmediate(() => {
         assert.ok(!state1.called, 'Code event hook was called after unregistering');
         assert.ok(!state2.called, 'Code event hook was called after unregistering');
         binding.unregisterAllHooks();
-      }, 500);
-    }, 500);
-  }, 500);
+      });
+    });
+  });
🧹 Nitpick comments (3)
test/addons/nsolid-code-event-hook/nsolid-code-event-hook.js (1)

1-1: Remove redundant 'use strict' directive.

The 'use strict' directive is redundant in JavaScript modules, as they are automatically in strict mode.

-'use strict';
src/nsolid/nsolid_api.cc (2)

1002-1028: Inefficient handler enablement on existing environments

The current implementation enables code event handlers on all existing environments every time a hook is added, even if handlers are already enabled. This could be inefficient with multiple hooks.

Consider tracking whether this is the first hook (transitioning from 0→1) and only enabling handlers in that case:

 TSList<EnvList::CodeEventHookStor>::iterator EnvList::AddCodeEventHook(
     void* data,
     internal::code_event_hook_proxy_sig proxy,
     internal::deleter_sig deleter) {
+  bool first_hook = (code_event_hook_list_.size() == 0);
   auto it = code_event_hook_list_.push_back(
     { proxy, nsolid::internal::user_data(data, deleter) });

+  if (first_hook) {
     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;
 }

1709-1709: Queue reset during shutdown may drop pending events

The queue is reset unconditionally during shutdown, which could silently drop pending code events and prevent user hooks from being called.

While this might be acceptable during shutdown, consider documenting this behavior or draining the queue first if event delivery is important for cleanup:

 void EnvList::removed_env_cb_(ns_async*, EnvList* envlist) {
+  // Note: Pending code events will be dropped during shutdown
   envlist->on_code_event_q_.reset();
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c9ae3c8 and 41949db.

📒 Files selected for processing (10)
  • node.gyp (1 hunks)
  • src/nsolid.cc (2 hunks)
  • src/nsolid.h (7 hunks)
  • src/nsolid/nsolid_api.cc (8 hunks)
  • src/nsolid/nsolid_api.h (11 hunks)
  • src/nsolid/nsolid_code_event_handler.cc (1 hunks)
  • src/nsolid/nsolid_code_event_handler.h (1 hunks)
  • test/addons/nsolid-code-event-hook/binding.cc (1 hunks)
  • test/addons/nsolid-code-event-hook/binding.gyp (1 hunks)
  • test/addons/nsolid-code-event-hook/nsolid-code-event-hook.js (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • src/nsolid/nsolid_code_event_handler.h
🚧 Files skipped from review as they are similar to previous changes (7)
  • test/addons/nsolid-code-event-hook/binding.gyp
  • node.gyp
  • src/nsolid/nsolid_code_event_handler.cc
  • src/nsolid.cc
  • test/addons/nsolid-code-event-hook/binding.cc
  • src/nsolid.h
  • src/nsolid/nsolid_api.h
🧰 Additional context used
🧠 Learnings (3)
📓 Common learnings
Learnt from: santigimeno
PR: nodesource/nsolid#339
File: test/addons/nsolid-elf-utils/nsolid-elf-utils.js:13-14
Timestamp: 2025-07-08T14:46:47.806Z
Learning: In the nsolid test suite, native addon bindings are expected to be built beforehand; tests do not add fs.existsSync guards to skip when bindings are missing.
Learnt from: santigimeno
PR: nodesource/nsolid#339
File: test/addons/nsolid-elf-utils/binding.cc:13-28
Timestamp: 2025-07-08T14:48:04.827Z
Learning: In nsolid test native addons (e.g., `test/addons/*/binding.cc`), additional JavaScript-facing argument validation is typically omitted because the tests supply well-formed inputs.
src/nsolid/nsolid_api.cc (6)

<retrieved_learning>
Learnt from: santigimeno
PR: #319
File: src/nsolid/async_ts_queue.h:123-135
Timestamp: 2025-06-03T11:35:52.830Z
Learning: The unbounded timer loop issue in AsyncTSQueue has been resolved by implementing proper timer arming logic with the timer_armed_ atomic flag and adding a queue emptiness check (!queue->queue_.empty()) in the async_callback method before restarting the timer. This prevents the timer from firing continuously when the queue is empty.
</retrieved_learning>

<retrieved_learning>
Learnt from: santigimeno
PR: #319
File: src/nsolid/async_ts_queue.h:123-135
Timestamp: 2025-06-03T11:35:52.830Z
Learning: The unbounded timer loop issue in AsyncTSQueue has been resolved by implementing proper timer arming logic with the timer_armed_ atomic flag and adding a queue emptiness check (!queue->queue_.empty()) in the async_callback method before restarting the timer. This prevents the timer from firing continuously when the queue is empty.
</retrieved_learning>

<retrieved_learning>
Learnt from: santigimeno
PR: #339
File: test/addons/nsolid-elf-utils/nsolid-elf-utils.js:13-14
Timestamp: 2025-07-08T14:46:47.806Z
Learning: In the nsolid test suite, native addon bindings are expected to be built beforehand; tests do not add fs.existsSync guards to skip when bindings are missing.
</retrieved_learning>

<retrieved_learning>
Learnt from: santigimeno
PR: #339
File: test/addons/nsolid-elf-utils/binding.cc:13-28
Timestamp: 2025-07-08T14:48:04.827Z
Learning: In nsolid test native addons (e.g., test/addons/*/binding.cc), additional JavaScript-facing argument validation is typically omitted because the tests supply well-formed inputs.
</retrieved_learning>

<retrieved_learning>
Learnt from: santigimeno
PR: #327
File: test/addons/nsolid-code-event-hook/binding.cc:64-78
Timestamp: 2025-06-10T09:52:49.401Z
Learning: In the nsolid codebase, test files in test/addons/ consistently use assert() for input validation and runtime checks, not CHECK() macros. This is the established testing convention.
</retrieved_learning>

<retrieved_learning>
Learnt from: santigimeno
PR: #339
File: src/nsolid/nsolid_elf_utils.cc:18-18
Timestamp: 2025-07-08T16:05:45.341Z
Learning: In the NSolid project, the GetBuildId function in src/nsolid/nsolid_elf_utils.cc is designed to be called only from the NSolid thread, so the static cache doesn't require thread safety mechanisms like mutex protection.
</retrieved_learning>

test/addons/nsolid-code-event-hook/nsolid-code-event-hook.js (4)
Learnt from: santigimeno
PR: nodesource/nsolid#339
File: test/addons/nsolid-elf-utils/binding.cc:13-28
Timestamp: 2025-07-08T14:48:04.827Z
Learning: In nsolid test native addons (e.g., `test/addons/*/binding.cc`), additional JavaScript-facing argument validation is typically omitted because the tests supply well-formed inputs.
Learnt from: santigimeno
PR: nodesource/nsolid#339
File: test/addons/nsolid-elf-utils/nsolid-elf-utils.js:13-14
Timestamp: 2025-07-08T14:46:47.806Z
Learning: In the nsolid test suite, native addon bindings are expected to be built beforehand; tests do not add fs.existsSync guards to skip when bindings are missing.
Learnt from: santigimeno
PR: nodesource/nsolid#327
File: test/addons/nsolid-code-event-hook/binding.cc:64-78
Timestamp: 2025-06-10T09:52:49.401Z
Learning: In the nsolid codebase, test files in test/addons/ consistently use assert() for input validation and runtime checks, not CHECK() macros. This is the established testing convention.
Learnt from: santigimeno
PR: nodesource/nsolid#339
File: test/addons/nsolid-elf-utils/nsolid-elf-utils.js:16-18
Timestamp: 2025-07-08T14:47:34.724Z
Learning: In the nsolid test suite, external tool invocations (e.g., execSync to run `readelf`) are intentionally left uncaught so that any failure causes the test to fail rather than being skipped.
🪛 Biome (1.9.4)
test/addons/nsolid-code-event-hook/nsolid-code-event-hook.js

[error] 1-1: Redundant use strict directive.

The entire contents of JavaScript modules are automatically in strict mode, with no statement needed to initiate it.
Safe fix: Remove the redundant use strict directive.

(lint/suspicious/noRedundantUseStrict)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (9)
  • GitHub Check: test-macOS
  • GitHub Check: coverage-linux-without-intl
  • GitHub Check: coverage-linux
  • GitHub Check: coverage-windows
  • GitHub Check: lint-js-and-md
  • GitHub Check: build-docs
  • GitHub Check: build-tarball
  • GitHub Check: test-linux (ubuntu-24.04)
  • GitHub Check: test-linux (ubuntu-24.04-arm)
🔇 Additional comments (10)
test/addons/nsolid-code-event-hook/nsolid-code-event-hook.js (3)

12-19: LGTM: Hook registration logic is well-structured.

The registerHook function properly validates callback parameters and maintains thread-specific state tracking. The use of bitwise OR for customFnNameReceived is appropriate for accumulating multiple potential matches.


27-42: LGTM: Test state setup and hook registration.

The test properly sets up two independent hook states and registers them correctly. The state structure is clear and supports the test's validation logic.


79-81: LGTM: Hook registration in main thread.

The main thread properly registers two hooks before spawning the worker thread, ensuring the hook indices are available for the worker.

src/nsolid/nsolid_api.cc (7)

2-2: LGTM!

Clean include of the code event handler header.


844-855: LGTM!

The handler setup and disable methods are correctly implemented:

  • setup_code_event_handler() properly checks for existing handler before creating
  • disable_code_event_handler() uses reset() to properly clean up resources
  • Both methods handle the handler lifecycle appropriately

886-892: LGTM!

The async queue initialization is correctly implemented:

  • Creates a typed queue for CodeEventInfo
  • Uses proper lambda callback syntax with move semantics
  • Integrates with the existing EnvList thread loop

1030-1053: LGTM!

The hook removal logic is correctly implemented:

  • Properly removes the hook from the list
  • Checks if it was the last hook (size == 0) before disabling handlers
  • Uses the same pattern as registration for consistency

1117-1119: LGTM!

Correctly integrates code event handler setup into environment creation:

  • Checks if hooks exist before enabling handler
  • Follows the established pattern for conditional setup

1495-1505: LGTM!

The static wrapper methods properly:

  • Verify they're running on the correct isolate/thread
  • Delegate to the appropriate EnvInst instance methods
  • Follow the established command pattern

1979-1992: LGTM!

The event dispatching logic is well-implemented:

  • Properly handles the case where the environment no longer exists
  • Optimizes for single hook case by moving instead of copying
  • Uses the enhanced for_each method that provides size information

Adds a for_each method to TSList that passes the current list size and
item to the callback.
Updates erase to return the new size after removal.
These changes improve thread-safe iteration and management of hooks.
Introduces a new API to register code event hooks in nsolid. This
includes new handler classes, queueing, and infrastructure to allow
extensible code event observation and callbacks.
@santigimeno santigimeno force-pushed the santi/jit_code_event_handler branch from 9bda80b to cae6e4e Compare August 13, 2025 12:19
@jaz239
Copy link
Copy Markdown

jaz239 commented Sep 22, 2025

closing ticket since this must be complete and has been in this pipeline for more than 10 sprints

@jaz239 jaz239 closed this Sep 22, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants