Skip to content

n-api: test for finalizer calls JS from first round phantom callback #25927

Description

@mlippautz

V8's API does not allow calling directly back into V8 from weak callbacks, see: `

// calls may be called in the first callback. Should additional work be

Instead, a second pass callback should be set using the WeakCallbackInfo API.

Affected are tests using this testing API:

napi_call_function(env, undefined, js_cb, 0, NULL, NULL));

This should fire a DCHECK for allowed allocations as soon as the JS callback executed non-trivial JS.

It also breaks the assumption that V8 can call out to the callback during the GC when not all pointers (of other v8::Persistent handles) have not been updated.

@hashseed

Activity

  1. hashseed commented on Feb 4, 2019

    @hashseed
    Member

    @nodejs/n-api
    @addaleax
    @mcollina

  2. hashseed commented on Feb 4, 2019

    @hashseed
    Member

    I guess in V8 we should add a v8::internal::DisallowJavaScriptExecution scope.

  3. mhdawson commented on Feb 6, 2019

    @mhdawson
    Member

    @mlippautz do you believe its an issue in the test or in the N-API implementation itself?

  4. addaleax commented on Feb 6, 2019

    @addaleax
    Member

    @mhdawson It is, yes

  5. gabrielschulhof commented on Feb 6, 2019

    @gabrielschulhof
    Contributor

    I interpret this to mean that we should honour the engine's constraints and throw an exception from the N-API implementation whenever we're about to call back into V8 knowing that we're coming from a weak callback and knowing that the call would result in JS execution. I think we can capture this with NAPI_PREAMBLE().

  6. gabrielschulhof commented on Feb 6, 2019

    @gabrielschulhof
    Contributor

    @hashseed does this restriction apply also to things like Object::Set() as well? IOW must I avoid all calls that return a Maybe from a weak callback?

  7. addaleax commented on Feb 6, 2019

    @addaleax
    Member

    @gabrielschulhof What we do elsewhere in core (e.g. async hooks) is to delay running callbacks that may invoke JS with a SetImmediate call, btw. GC is nondeterministic anyway, and delaying the callback gives us the freedom to do whatever we want in it.

    I’d first try either that, or use @mlippautz’ suggestion of using SetSecondPassCallback().

  8. mlippautz commented on Feb 7, 2019

    @mlippautz
    Author

    I am not fluent in n-api but it looks like napi_add_finalizer would only accept a native callback in finalize_cb. That seems safe.

    The problem is that the other parameters passed can be used to invoke JS. Prohibit parameters likely limits usability so documentation should probably mention that no V8 API may be called on them and they should only be passed along. (That's how V8 went about this problem.)

    As already mentioned, solutions are posting another task to the platform using node or using SetSecondPassCallback which would post the task from within V8, or potentially execute it synchronously after GC in V8 (e.g. when under memory pressure).

  9. hashseed commented on Feb 7, 2019

    @hashseed
    Member

    @hashseed does this restriction apply also to things like Object::Set() as well? IOW must I avoid all calls that return a Maybe from a weak callback?

    Yes. It's not so much JS execution that's bad, but all access to the heap that assumes consistent heap state. During GC, that may not be the case.

  10. gabrielschulhof commented on Feb 7, 2019

    @gabrielschulhof
    Contributor

    @mlippautz you're right in that there is no indication that it is unsafe to call into V8. It's also true that other engines may not have this restriction. Therefore I think we should try to provide the feature whereby we invoke the weak callback in such a way that it is safe to call into the engine from there.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    node-apiIssues and PRs related to Node-API.

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions