Skip to content

fix: Don't send JavaScript objects between isolates #4

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

Merged
merged 3 commits into from
Jun 5, 2025

Conversation

timfish
Copy link
Collaborator

@timfish timfish commented Jun 3, 2025

Once you create v8 objects in an isolate, you cannot return them from another isolate. This PR changes it so we pass a vector of intermediate JsStackFrame structs and construct the v8 JavaScript objects on the correct isolate.

The CI test's that were failing are now passing so this definitely fixes the intermittent issues we were seeing.

@timfish timfish force-pushed the timfish/fix/dont-mix-isolates branch from e19c500 to a7fcea4 Compare June 3, 2025 16:54
@timfish timfish marked this pull request as ready for review June 3, 2025 17:14
@timfish timfish requested a review from AbhiPrasad June 3, 2025 17:14
@timfish
Copy link
Collaborator Author

timfish commented Jun 3, 2025

@codecov-ai-reviewer review

This comment was marked as outdated.

Copy link

codecov-ai bot commented Jun 3, 2025

PR Description

This pull request refactors the stack trace capture mechanism in the native module to improve performance and maintainability. It replaces the direct use of V8 objects within the stack trace capture logic with a C++ data structure, JsStackFrame, to represent stack frames. This allows for better separation of concerns and reduces the overhead of creating and manipulating V8 objects during the interrupt handler.

Click to see more

Key Technical Changes

The key technical changes include:

  1. Introduction of JsStackFrame and JsStackTrace: These C++ structures replace the direct use of V8 objects for representing stack frames and stack traces, respectively.
  2. Refactoring of ExecutionInterrupted: This function now populates a JsStackTrace instead of directly creating a V8 array. This reduces the amount of V8 API calls within the interrupt handler.
  3. Change of Promise Type: The promise used for asynchronous stack trace capture now resolves to a JsStackTrace instead of a Local<Array>.
  4. Deferred V8 Object Creation: The conversion from JsStackFrame to V8 objects is now performed outside the interrupt handler in the CaptureStackTraces function, minimizing the time spent in the interrupt.
  5. Yarn Lockfile Handling: The test/yarn.lock file is now ignored, as it's a generated file specific to the test environment.

Architecture Decisions

The main architectural decision is to decouple the stack trace representation from the V8 API. This improves the module's resilience to V8 API changes and makes the code easier to test and maintain. The use of std::future and std::async allows for parallel stack trace capture from different isolates, improving overall performance. Scoping the lock guard more narrowly in CaptureStackTraces minimizes contention and improves concurrency.

Dependencies and Interactions

This change primarily affects the internal implementation of stack trace capture. It does not introduce any new external dependencies. It interacts with the V8 API for stack trace retrieval and object creation, but the interaction is now more localized. The RegisterThread and GetThreadsLastSeen functions are not directly affected but rely on the same thread management mechanism.

Risk Considerations

The primary risk is potential performance regressions due to the overhead of copying data between the V8 heap and the C++ data structures. However, this is mitigated by performing the V8 object creation outside the interrupt handler. Thorough testing is required to ensure that the changes do not introduce any memory leaks or race conditions. The UTF-8 conversion from V8 strings to C++ strings could potentially fail, requiring careful error handling.

Notable Implementation Details

A notable implementation detail is the use of v8::String::Utf8Value to convert V8 strings to C++ strings. The code defaults to '?' if the conversion fails. The lock guard in CaptureStackTraces is scoped to only protect access to the threads map, allowing the asynchronous stack trace captures to proceed in parallel. The test/yarn.lock file is added to .gitignore because it's specific to the test environment and contains local file references.

Copy link
Member

@AbhiPrasad AbhiPrasad left a comment

Choose a reason for hiding this comment

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

last time I wrote c++ I was grinding a uni assignment so hopefully these apply 🙏


// Type alias for a vector of JsStackFrame
using JsStackTrace = std::vector<JsStackFrame>;

// Function to be called when an isolate's execution is interrupted
static void ExecutionInterrupted(Isolate *isolate, void *data) {
Copy link
Member

Choose a reason for hiding this comment

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

Can this throw in any way? If it does, it'll create a deadlock because promise->set_value(frames); can never be called.

Only thing that looks sus is the string conversions with things like utf8_fn.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

For the string conversions we check for a nullptr and return a default

        v8::String::Utf8Value utf8_fn(isolate, fn_name);
        function_name = *utf8_fn ? *utf8_fn : "?";

f.function_name.c_str(),
NewStringType::kNormal)
.ToLocalChecked())
.Check();
Copy link
Member

Choose a reason for hiding this comment

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

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I've seen this used like this everywhere so can only assume it's ok to use 🤷‍♂️
https://github.com/search?q=%2FToLocalChecked%5C(%5C)%2F%20%20%2FCheck%5C(%5C)%2F&type=code

@timfish timfish requested a review from AbhiPrasad June 5, 2025 14:05
Copy link
Member

@AbhiPrasad AbhiPrasad left a comment

Choose a reason for hiding this comment

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

Let's merge this in for now, we can get further feedback later.

@timfish timfish merged commit bb9d1bf into main Jun 5, 2025
61 checks passed
@timfish timfish deleted the timfish/fix/dont-mix-isolates branch June 5, 2025 16:08
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.

2 participants