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: clean up resources on Environment teardown #19377

Closed
wants to merge 21 commits into from

Conversation

addaleax
Copy link
Member

@addaleax addaleax commented Mar 15, 2018

Opening this in reference to #19365, porting commits over from Ayo (that also serve as Worker support preparation):


  • src: add environment cleanup hooks

    This adds pairs of methods to the Environment class and to public APIs
    which can add and remove cleanup handlers.

    Unlike AtExit, this API targets addon developers rather than
    embedders, giving them (and Node’s internals) the ability to register
    per-Environment cleanup work.

    We may want to replace AtExit with this API at some point.

    (This commit is semver-minor.)

  • src: make CleanupHandles() tear down handles/reqs

    Previously, handles would not be closed when the current Environment
    stopped, which is acceptable in a single-Environment-per-process
    situation, but would otherwise create memory and file descriptor
    leaks.

    Also, introduce a generic way to close handles via the
    Environment::CloseHandle() function, which automatically keeps
    track of whether a close callback has been called yet or not.

  • src: unify ReqWrap libuv calling

    This allows easier tracking of whether there are active ReqWraps.

  • src: keep track of open requests

    Workers cannot shut down while requests are open, so keep a counter
    that is increased whenever libuv requests are made and decreased
    whenever their callback is called.

    This also applies to other embedders, who may want to shut down
    an Environment instance early.

  • src: use cleanup hooks to tear down BaseObjects

    Clean up after BaseObject instances when the Environment
    is being shut down. This takes care of closing non-libuv resources
    like zlib instances, which do not require asynchronous shutdown.

  • src: add can_call_into_js flag

    This prevents calls back into JS from the shutdown phase.

  • tools: remove --quiet from run-valgrind.py

    This should no longer be an issue, now that we clean up
    resources when exiting.


New since the PR was opened:

(Note: I’m not sure whether this is necessary and will kick off CI both with and without the commit.)

  • lib: defer pausing stdin to the next tick

    This is done to match the stream implementation, which also
    only actually stops reading in the next tick after the 'pause'
    event is emitted.

  • src: remove NodeCategorySet destructor

    This currently crashes during environment cleanup because
    the object would be torn down while there are enabled categories.

    I’m not sure about the exact semantics here, but since the
    object cannot be garbage collected at this point anyway
    because it’s Persistent handle is strong, removing the
    destructor at least doesn’t make anything worse than it is
    right now (i.e. the destructor would never have been called
    before anyway).

  • src: store fd for libuv streams on Windows

    On Windows, we can't just look up a FD for libuv streams and
    return it in GetFD().
    However, we do sometimes construct streams from their FDs;
    in those cases, it should be okay to store the value on a class field.

  • process: create stdin with manualStart: true

    Otherwise Node.js will try to read data from the handle.
    This causes issues when Node.js is already reading from the
    same handle, but a different associated stream
    (e.g. a possible IPC channel).


Almost all commits have been reviewed in their original form by @Qard (and one by @TimothyGu and @aqrln), and partly have been talked through with them. Many, many thanks for that! 💙

Checklist
  • make -j4 test (UNIX), or vcbuild test (Windows) passes
  • tests and/or benchmarks are included
  • documentation is changed or added
  • commit message follows commit guidelines
  • make tests pass on Windows
  • take care of handles without HandleWraps

/cc @joyeecheung @jasnell @yhwang @gireeshpunathil

@addaleax addaleax added semver-minor PRs that contain new features and should be released in the next minor version. lib / src Issues and PRs related to general changes in the lib or src directory. embedding Issues and PRs related to embedding Node.js in another project. worker Issues and PRs related to Worker support. labels Mar 15, 2018
@nodejs-github-bot nodejs-github-bot added c++ Issues and PRs that require attention from people who are familiar with C++. lib / src Issues and PRs related to general changes in the lib or src directory. labels Mar 15, 2018
@addaleax
Copy link
Member Author

@addaleax
Copy link
Member Author

Sadly, the parallel/test-child-process-std{in,out}-ipc failures seem real and don’t provide any helpful information whatsoever. If anybody from @nodejs/platform-windows could take a look, that would be great, otherwise I’ll try to dig into it on the weekend.

@addaleax addaleax force-pushed the cleanup branch 2 times, most recently from 3a4d878 to c207e6e Compare March 16, 2018 00:09

while (!cleanup_hooks_.empty()) {
// Copy into a vector, since we can't sort an unordered_set in-place.
std::vector<CleanupHookCallback> callbacks(
Copy link
Member

@joyeecheung joyeecheung Mar 16, 2018

Choose a reason for hiding this comment

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

nit: maybe just swap the two vectors?

Copy link
Member Author

Choose a reason for hiding this comment

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

The other one is not a vector :)

Copy link
Member

@joyeecheung joyeecheung left a comment

Choose a reason for hiding this comment

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

Very nice work! Left some comments but I think they are ignorable, will take a deeper look over the weekend.

src/env.h Outdated
@@ -854,6 +872,32 @@ class Environment {
void RunAndClearNativeImmediates();
static void CheckImmediate(uv_check_t* handle);

struct CleanupHookCallback {
void (*fun_)(void*);
Copy link
Member

Choose a reason for hiding this comment

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

nit: I think we usually go with fn_ or cb_ for these kinds of members?

src/node_file.cc Outdated
// Convert a C++ lambda function to a raw, C-style function.
// This is helpful, because ReqWrap::Dispatch() does not recognize lambda
// functions, and thus does not wrap them properly.
typedef void(*uv_fs_callback_t)(uv_fs_t*);
Copy link
Member

@joyeecheung joyeecheung Mar 16, 2018

Choose a reason for hiding this comment

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

I believe this only works on non-capturing lambdas? Also simply initializing a uv_fs_callback_t with the lambda inline when they are going to be passed probably works as well?

wrap->Dispatch(..., uv_fs_callback_t{[](uv_fs_t*) {
  // lambda code here
}})

Copy link
Member Author

Choose a reason for hiding this comment

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

I believe this only works on non-capturing lambdas?

We wouldn’t be able to pass others to libuv anyway. But yes, I think it only works with them.

Also simply initializing a uv_fs_callback_t with the lambda inline when they are going to be passed probably works as well?

Heh. Yes. 😄

@@ -527,6 +554,14 @@ void Environment::SetUnrefImmediate(native_immediate_callback cb,
CreateImmediate(cb, data, obj, false);
}

inline bool Environment::can_call_into_js() const {
return can_call_into_js_;
Copy link
Member

Choose a reason for hiding this comment

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

I think technically, we cannot call into JS whenever there is a pending exception? Is is possible for someone to bump into a pending exception while this returns true?

Copy link
Member Author

Choose a reason for hiding this comment

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

Is is possible for someone to bump into a pending exception while this returns true?

I would expect that, yes. But the code should be laid out to handle such a situation anyway, right?

The function isn’t meant to give an exhausting result that tells code whether it’s okay to call into JS or not… it’s more of an “allowed to” than “able to”. Maybe we should rename it to may_call_into_js()? What do you think?

Copy link
Member

Choose a reason for hiding this comment

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

@addaleax In my understanding may_call_into_js() is probably not too different from can_call_into_js(), maybe add a comment there making it clear that this returning true does not guarantee that one can call into the JS without being careful about pending exceptions? Or, since we only set it to false...maybe cannot_call_into_js()?

Copy link
Member

Choose a reason for hiding this comment

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

Or, if we don't want to be too board about the meaning of this...maybe just something like is_in_tear_down()?

Copy link
Member Author

Choose a reason for hiding this comment

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

maybe add a comment there making it clear that this returning true does not guarantee that one can call into the JS without being careful about pending exceptions?

Sure, I’m adding a comment. :)

Or, if we don't want to be too board about the meaning of this...maybe just something like is_in_tear_down()?

I think I’d prefer to name it according to how its consumers use it. I don’t know why we would, but I wouldn’t consider it impossible that at some point we may want to use this flag for other things too.

@gireeshpunathil
Copy link
Member

@addaleax - thanks for this amazing work!

src: add environment cleanup hooks:

  • I assume Environment::RunCleanup() is the teardown API, should we have some docs around that?

src: make CleanupHandles() tear down handles/reqs

  • I guess this covers all the handles that were created through the JS code. How about the handles created by the embedders directly through uv_*_init means, not through the Node's opaque *Wrap means?

src: add can_call_into_js flag

  • Once the tear down is initiated, how is this (calling into JS land) possible? is this specific to worker case? Should we have this here?

One of the test case I have in my mind is (basically derived from my goal through #19365 ) is to be able to do something like this:

int main() {
  while (1) { // or some condition
    void *params = read(config);
    node::Start(params);
 }
}
static void acb(uv_async_t* handle) {
// tear down on demand, on some condition
  Environment::RunCleanup();
}

And validate that at every loop back edges, the run time state is same as the baseline - in terms of memory, threads, fds, ... I will come up with something and test this code.

@addaleax addaleax added the wip Issues and PRs that are still a work in progress. label Mar 16, 2018
@addaleax
Copy link
Member Author

I assume Environment::RunCleanup() is the teardown API, should we have some docs around that?

Hm – @joyeecheung I think we should call that function as part of FreeEnvironment() now … does that sound right to you?

I guess this covers all the handles that were created through the JS code. How about the handles created by the embedders directly through uv_*_init means, not through the Node's opaque *Wrap means?

Hm, yes, there might be few ones that would be left and should be included here.

I think it’s only the uv_async_init + uv_timer_init calls from cares_wrap.cc that are relevant here, though.

Once the tear down is initiated, how is this (calling into JS land) possible?

It’s totally possible, we just don’t want to allow it because a) it’s inconsistent with the current behaviour of dropping everything when exiting and b) it could schedule arbitrary amounts of work by creating new handles and requests.

(We could make an exception of some sort for stdio, though. This PR might be a good starting point for the “flush on exit” problem we’ve been having for years…)

is this specific to worker case? Should we have this here?

It’s not specific to the worker case, no.

And validate that at every loop back edges, the run time state is same as the baseline - in terms of memory, threads, fds, ... I will come up with something and test this code.

Fwiw, for memory that is more or less covered by testing with valgrind, which now passes for most tests. (Yay! 🎉)

Copy link
Member

@bnoordhuis bnoordhuis left a comment

Choose a reason for hiding this comment

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

Left some suggestions but by and large LGTM.

current Node.js environment exits.

A function can safely be specified multiple times with different
`arg` values. In that case, it will be called multiple times as well.
Copy link
Member

Choose a reason for hiding this comment

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

Probably worth specifying in which order the hooks run.

[](uv_handle_t* handle) {
delete reinterpret_cast<uv_timer_t*>(handle);
});
env()->CloseHandle(timer_handle_, [](uv_timer_t* handle){ delete handle; });
Copy link
Member

Choose a reason for hiding this comment

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

Space before {.

req_wrap->Dispatched();
int err = req_wrap->Dispatch(uv_getnameinfo,
AfterGetNameInfo,
(struct sockaddr*)&addr,
Copy link
Member

Choose a reason for hiding this comment

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

Since you're here: reinterpret_cast

src/env.h Outdated

// We keep track of the insertion order for these objects, so that we can
// call the callbacks in reverse order when we are cleaning up.
int64_t insertion_order_counter_;
Copy link
Member

Choose a reason for hiding this comment

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

uint64_t? Same on line 899.

@@ -1076,6 +1076,8 @@ class ContextifyScript : public BaseObject {
const bool break_on_sigint,
const FunctionCallbackInfo<Value>& args,
TryCatch* try_catch) {
if (!env->can_call_into_js())
return false;
Copy link
Member

Choose a reason for hiding this comment

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

All the return false exits in this function are from pending exceptions except this one. Probably harmless because:

  • no callers use the return value, and
  • all callers return to JS land afterwards

@@ -5124,6 +5127,7 @@ void PBKDF2(const FunctionCallbackInfo<Value>& args) {
if (args[5]->IsFunction()) {
obj->Set(env->context(), env->ondone_string(), args[5]).FromJust();

env->IncreaseWaitingRequestCounter();
Copy link
Member

Choose a reason for hiding this comment

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

Maybe do this from the constructor and destructor? I assume it's harmless in the sync case?

Copy link
Member Author

Choose a reason for hiding this comment

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

Right now it’s always done directly before the request is started and when the callback is called. I think that makes things easier to keep track off.


/* Below is dark template magic designed to invoke libuv functions that
* initialize uv_req_t instances in a unified fashion, to allow easier
* tracking of active/inactive requests. */
Copy link
Member

Choose a reason for hiding this comment

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

C++ comments, please.

// Detect `int uv_foo(uv_loop_t* loop, uv_req_t* request, ...);`.
template <typename ReqT, typename... Args>
struct CallLibuvFunction<ReqT, int(*)(uv_loop_t*, ReqT*, Args...)> {
typedef int(*T)(uv_loop_t*, ReqT*, Args...);
Copy link
Member

Choose a reason for hiding this comment

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

Might be easier to read as a using T = ... declaration (and F is arguably a better name for it.)

if (err >= 0)
env()->IncreaseWaitingRequestCounter();
return err;
}
Copy link
Member

Choose a reason for hiding this comment

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

Just thinking out aloud but maybe there's a way to make it more flexible / comprehensible / less prone to obscure compile errors? Quick sketch:

template <typename T>
class LibuvCallback {
 public:
  using InitF = std::function<int(*)(T*, void (*)(T*, int))>;
  using DoneF = std::function<void(*)(std::unique_ptr<LibuvCallback>, int)>;

  T* req() { return &req_; }

 private:
  template <typename> friend int MakeLibuvCallback(Environment*, InitF, DoneF);
  std::unique_ptr<LibuvCallback> self_;
  Environment* const env_;
  DoneF done_;
  T req_;

  LibuvCallback(Environment* env, DoneF done) : env_(env), done_(done), req_() {}
  
  static void Callback(T* req, int status) {
    LibuvCallback* that = ContainerOf(&LibuvCallback::req_, req);
    that->env_->DecreaseWaitingRequestCounter();
    that->done_(std::move(that->self_), status);
  }
};

template <typename T>
int MakeLibuvCallback(
    Environment* env, LibuvCallback<T>::InitF init, LibuvCallback<T>::DoneF done) {
  using C = LibuvCallback<T>;
  std::unique_ptr<C> that(new C(env, done));
  if (int err = init(&that->req_, C::Callback)) return err;
  env->IncreaseWaitingRequestCounter();
  that->self_ = std::move(that);
  return 0;
}

And used like this:

{
  Environment* env = /* ... */;
  const char* path = /* ... */;
  auto init_cb = [=] (uv_fs_t* req, uv_fs_cb cb) {
    return uv_fs_stat(env->event_loop(), req, path, cb);
  };
  auto done_cb = [=] (std::unique_ptr<LibuvCallback<uv_fs_t>> that, int status) {
    // do something with that->req()
    // auto-deleted when |that| goes out of scope
  };
  int err = MakeLibuvCallback(env, init_cb, done_cb);
  // ...
}

The std::unique_ptr is to make it possible to extend the lifetime of the request beyond the done callback. It shouldn't do more heap allocations than currently, assuming the compiler is smart enough to unbox the init callback.

Copy link
Member Author

Choose a reason for hiding this comment

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

I like the approach, and I’ll think about whether we can implement it here, but one of the reasons for the current complexity is that libuv requests don’t always match a (uv_foo_t* request, int status) signature. Otherwise this would be a lot simpler. :)

doc/api/n-api.md Outdated
@@ -890,6 +890,53 @@ If still valid, this API returns the `napi_value` representing the
JavaScript Object associated with the `napi_ref`. Otherise, result
will be NULL.

### Cleanup on exit of the current Node.js instance

While a Node.js typically releases all its resources when exiting, embedders
Copy link
Member

Choose a reason for hiding this comment

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

s/While a Node.js/While Node.js

doc/api/n-api.md Outdated
void* arg);
```

Un-registers `fun` as a function to be run with the `arg` parameter once the
Copy link
Member

Choose a reason for hiding this comment

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

s/Un-registers/Unregisters

doc/api/n-api.md Outdated
need to be exact matches.

The function must have originally been registered
with `napi_add_env_cleanup_hook`.
Copy link
Member

Choose a reason for hiding this comment

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

What happens if it's not?

Copy link
Member Author

Choose a reason for hiding this comment

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

Nothing, and we can’t really do anything about that, but it doesn’t make sense to call this function otherwise.

Copy link
Member

Choose a reason for hiding this comment

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

As long as it's a non-op in that case, ++

Registers `fun` as a function to be run with the `arg` parameter once the
current Node.js environment exits.

A function can safely be specified multiple times with different
Copy link
Member

@yhwang yhwang Mar 16, 2018

Choose a reason for hiding this comment

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

one question: if I call napi_add_env_cleanup_hook multiple times with the same function and arg, how can I just remove one of them when calling napi_remove_env_cleanup_hook?

Edit: I saw unorder_set::emplace() and erase(). In my case, it only add one callback into the cleanup hook.

Copy link
Member Author

Choose a reason for hiding this comment

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

@yhwang You can’t do it, right now it would crash with that – I’ve clarified this in the documentation.

The reason is that you basically never want that. If you pass the same arg + fun combination twice in a meaningful way, that means that you rely on global state; in that case, your code will probably not work anyway in the scenarios that this is meant to support.

(Another reason is the suspicion – not benchmarked – that a std::unordered_set is more performant than a std::unordered_multiset.)

Copy link
Member

Choose a reason for hiding this comment

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

If you pass the same arg + fun combination twice in a meaningful way, that means that you rely on global state

hmm, the arg could be a refcount object and decrease its counter in each call of the callback function :-p

I saw you said with different arg values now. Good catch!

Copy link
Member Author

@addaleax addaleax Mar 16, 2018

Choose a reason for hiding this comment

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

If you pass the same arg + fun combination twice in a meaningful way, that means that you rely on global state

hmm, the arg could be a refcount object and decrease its counter in each call of the callback function :-p

Yeah, you could still get that kind of behaviour by creating pointers with an additional layer of indirection if you really want to and using those as unique keys. But like I said, probably not something one really wants to do :)

@joyeecheung
Copy link
Member

Hm – @joyeecheung I think we should call that function as part of FreeEnvironment() now … does that sound right to you?

SGTM

@addaleax addaleax force-pushed the cleanup branch 3 times, most recently from d70f0e9 to 01f9041 Compare March 16, 2018 22:12
@addaleax
Copy link
Member Author

Updated with most feedback applied, still haven’t gotten around to looking into the Windows failures

CI: https://ci.nodejs.org/job/node-test-commit/16957/

@@ -39,6 +39,12 @@ inline BaseObject::BaseObject(Environment* env, v8::Local<v8::Object> handle)
// nullptr in case it's accessed by the user before construction is complete.
if (handle->InternalFieldCount() > 0)
handle->SetAlignedPointerInInternalField(0, nullptr);
env_->AddCleanupHook(DeleteMe, static_cast<void*>(this));
Copy link
Member

@yhwang yhwang Mar 16, 2018

Choose a reason for hiding this comment

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

will DeleteMe() somehow conflict with delete data.GetParameter(); in BaseObject::WeakCallback()? should DeleteMe() call ClearWeak() ?
I didn't read through all of the code please forgive me if I ask stupid question. Right now seems two mechanisms for clean up, one is v8::PersistentBase< T >::SetWeak() which relies on V8 GC and the env cleanup hook you add now. could them be merged into one?

Copy link
Member Author

Choose a reason for hiding this comment

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

will DeleteMe() somehow conflict with delete data.GetParameter(); in BaseObject::WeakCallback()?

I don’t think so… when one of these mechanisms is called, the other gets disabled in the destructor. That should work, right?

Should DeleteMe() call ClearWeak() ?

The delete self; call destroys the persistent handle, so I don’t think calling ClearWeak() on that handle before it would make a difference?

I didn't read through all of the code please forgive me if I ask stupid question.

No no, please keep these questions (which are not stupid) coming :) It’s good to talk this through.

could them be merged into one?

Hm – how would we do this? There isn’t much code being added here, so I’m not sure how to merge those…

Also, there’s a third way to cleanup a BaseObject: To delete it explicitly somewhere else, like we do e.g. for HandleWraps. I agree that lifetime management in Node core is currently not the easiest thing to keep track off, though.

@jasnell
Copy link
Member

jasnell commented Mar 17, 2018

Looking quickly at the windows failure... it's in a specific child process test. While I'm waiting for the windows build to actually complete, I'm making a somewhat completely random guess that there might be a missing DecreaseWaitingRequestCounter? Perhaps?

@addaleax
Copy link
Member Author

@jasnell Yeah, that’s a possibility, but I’m not sure how that would fail in a platform-dependent manner.

It might be an instance of a libuv request not finishing, like libuv/libuv#1729 (if that’s a real bug) or a similar issue. But even that’s a long shot without doing any investigation. :)

MylesBorins pushed a commit that referenced this pull request Aug 7, 2018
Refs: #19377
PR-URL: #20585
Reviewed-By: Ben Noordhuis <[email protected]>
Reviewed-By: James M Snell <[email protected]>
gabrielschulhof pushed a commit to gabrielschulhof/node that referenced this pull request Aug 21, 2018
This adds pairs of methods to the `Environment` class and to public APIs
which can add and remove cleanup handlers.

Unlike `AtExit`, this API targets addon developers rather than
embedders, giving them (and Node\u2019s internals) the ability to register
per-`Environment` cleanup work.

We may want to replace `AtExit` with this API at some point.

Many thanks for Stephen Belanger for reviewing the original version of
this commit in the Ayo.js project.

Refs: ayojs/ayo#82
PR-URL: nodejs#19377
Reviewed-By: Ben Noordhuis <[email protected]>
Reviewed-By: James M Snell <[email protected]>
MylesBorins pushed a commit that referenced this pull request Sep 6, 2018
This adds pairs of methods to the `Environment` class and to public APIs
which can add and remove cleanup handlers.

Unlike `AtExit`, this API targets addon developers rather than
embedders, giving them (and Node\u2019s internals) the ability to register
per-`Environment` cleanup work.

We may want to replace `AtExit` with this API at some point.

Many thanks for Stephen Belanger for reviewing the original version of
this commit in the Ayo.js project.

Refs: ayojs/ayo#82
Backport-PR-URL: #22435
PR-URL: #19377
Reviewed-By: Ben Noordhuis <[email protected]>
Reviewed-By: James M Snell <[email protected]>
@MylesBorins MylesBorins mentioned this pull request Sep 6, 2018
MylesBorins pushed a commit to MylesBorins/node that referenced this pull request Nov 5, 2018
Refs: nodejs#19377
PR-URL: nodejs#20585
Reviewed-By: Ben Noordhuis <[email protected]>
Reviewed-By: James M Snell <[email protected]>
MylesBorins pushed a commit that referenced this pull request Nov 11, 2018
Backport-PR-URL: #24103
Refs: #19377
PR-URL: #20585
Reviewed-By: Ben Noordhuis <[email protected]>
Reviewed-By: James M Snell <[email protected]>
gabrielschulhof pushed a commit to gabrielschulhof/node that referenced this pull request Mar 15, 2019
This adds pairs of methods to the `Environment` class and to public APIs
which can add and remove cleanup handlers.

Unlike `AtExit`, this API targets addon developers rather than
embedders, giving them (and Node’s internals) the ability to register
per-`Environment` cleanup work.

We may want to replace `AtExit` with this API at some point.

Many thanks for Stephen Belanger for reviewing the original version of
this commit in the Ayo.js project.

Refs: ayojs/ayo#82
PR-URL: nodejs#19377
Reviewed-By: Ben Noordhuis <[email protected]>
Reviewed-By: James M Snell <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
author ready PRs that have at least one approval, no pending requests for changes, and a CI started. c++ Issues and PRs that require attention from people who are familiar with C++. embedding Issues and PRs related to embedding Node.js in another project. lib / src Issues and PRs related to general changes in the lib or src directory. semver-minor PRs that contain new features and should be released in the next minor version. worker Issues and PRs related to Worker support.
Projects
None yet
Development

Successfully merging this pull request may close these issues.