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: unload native addons when the environment is destroyed #24861

Closed
Closed
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
18 changes: 18 additions & 0 deletions doc/api/addons.md
Expand Up @@ -234,6 +234,23 @@ NODE_MODULE_INIT(/* exports, module, context */) {
}
```

#### Worker support

In order to support [`Worker`][] threads, addons need to clean up any resources
they may have allocated when such a thread exists. This can be achieved through
the usage of the `AddEnvironmentCleanupHook()` function:

```c++
void AddEnvironmentCleanupHook(v8::Isolate* isolate,
void (*fun)(void* arg),
void* arg);
```

This function adds a hook that will run before a given Node.js instance shuts
down. If necessary, such hooks can be removed using
`RemoveEnvironmentCleanupHook()` before they are run, which has the same
signature.

### Building

Once the source code has been written, it must be compiled into the binary
Expand Down Expand Up @@ -1349,6 +1366,7 @@ Test in JavaScript by running:
require('./build/Release/addon');
```

[`Worker`]: worker_threads.html#worker_threads_class_worker
[Electron]: https://electronjs.org/
[Embedder's Guide]: https://github.com/v8/v8/wiki/Embedder's%20Guide
[Linking to Node.js' own dependencies]: #addons_linking_to_node_js_own_dependencies
Expand Down
1 change: 1 addition & 0 deletions node.gyp
Expand Up @@ -421,6 +421,7 @@
'src/node_api.h',
'src/node_api_types.h',
'src/node_binding.h',
'src/node_binding-inl.h',
'src/node_buffer.h',
'src/node_constants.h',
'src/node_context_data.h',
Expand Down
10 changes: 10 additions & 0 deletions src/env-inl.h
Expand Up @@ -396,6 +396,16 @@ inline uv_loop_t* Environment::event_loop() const {
return isolate_data()->event_loop();
}

inline void Environment::TryLoadAddon(
const char* filename,
int flags,
std::function<bool(binding::DLib*)> was_loaded) {
loaded_addons_.emplace_back(filename, flags);
if (!was_loaded(&loaded_addons_.back())) {
loaded_addons_.pop_back();
}
}

inline Environment::AsyncHooks* Environment::async_hooks() {
return &async_hooks_;
}
Expand Down
5 changes: 5 additions & 0 deletions src/env.cc
Expand Up @@ -267,6 +267,11 @@ Environment::~Environment() {

TRACE_EVENT_NESTABLE_ASYNC_END0(
TRACING_CATEGORY_NODE1(environment), "Environment", this);

// Dereference all addons that were loaded into this environment.
for (auto& addon : loaded_addons_) {
gabrielschulhof marked this conversation as resolved.
Show resolved Hide resolved
addon.Close();
}
}

void Environment::Start(const std::vector<std::string>& args,
Expand Down
10 changes: 8 additions & 2 deletions src/env.h
Expand Up @@ -30,18 +30,20 @@
#endif
#include "handle_wrap.h"
#include "node.h"
#include "node_binding-inl.h"
#include "node_http2_state.h"
#include "node_options.h"
#include "req_wrap.h"
#include "util.h"
#include "uv.h"
#include "v8.h"

#include <list>
#include <stdint.h>
#include <vector>
#include <functional>
#include <list>
#include <unordered_map>
#include <unordered_set>
#include <vector>

struct nghttp2_rcbuf;

Expand Down Expand Up @@ -636,6 +638,9 @@ class Environment {
inline v8::Isolate* isolate() const;
inline uv_loop_t* event_loop() const;
inline uint32_t watched_providers() const;
inline void TryLoadAddon(const char* filename,
int flags,
std::function<bool(binding::DLib*)> was_loaded);

static inline Environment* from_timer_handle(uv_timer_t* handle);
inline uv_timer_t* timer_handle();
Expand Down Expand Up @@ -921,6 +926,7 @@ class Environment {
inline void ThrowError(v8::Local<v8::Value> (*fun)(v8::Local<v8::String>),
const char* errmsg);

std::list<binding::DLib> loaded_addons_;
v8::Isolate* const isolate_;
IsolateData* const isolate_data_;
uv_timer_t timer_handle_;
Expand Down
2 changes: 1 addition & 1 deletion src/node.cc
Expand Up @@ -19,7 +19,7 @@
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.

#include "node_binding.h"
#include "node_binding-inl.h"
#include "node_buffer.h"
#include "node_constants.h"
#include "node_context_data.h"
Expand Down
2 changes: 1 addition & 1 deletion src/node_api.cc
Expand Up @@ -3,7 +3,7 @@
#define NAPI_EXPERIMENTAL
#include "js_native_api_v8.h"
#include "node_api.h"
#include "node_binding.h"
#include "node_binding-inl.h"
#include "node_errors.h"
#include "node_internals.h"

Expand Down
59 changes: 59 additions & 0 deletions src/node_binding-inl.h
@@ -0,0 +1,59 @@
#ifndef SRC_NODE_BINDING_INL_H_
#define SRC_NODE_BINDING_INL_H_

gabrielschulhof marked this conversation as resolved.
Show resolved Hide resolved
#include "node_binding.h"

namespace node {

namespace binding {

inline DLib::DLib(const char* filename, int flags)
: filename_(filename), flags_(flags), handle_(nullptr) {}

#ifdef __POSIX__
inline bool DLib::Open() {
handle_ = dlopen(filename_.c_str(), flags_);
if (handle_ != nullptr) return true;
errmsg_ = dlerror();
return false;
}

inline void DLib::Close() {
if (handle_ == nullptr) return;
dlclose(handle_);
handle_ = nullptr;
}

inline void* DLib::GetSymbolAddress(const char* name) {
return dlsym(handle_, name);
}
#else // !__POSIX__
inline bool DLib::Open() {
int ret = uv_dlopen(filename_.c_str(), &lib_);
if (ret == 0) {
handle_ = static_cast<void*>(lib_.handle);
return true;
}
errmsg_ = uv_dlerror(&lib_);
uv_dlclose(&lib_);
return false;
}

inline void DLib::Close() {
if (handle_ == nullptr) return;
uv_dlclose(&lib_);
handle_ = nullptr;
}

inline void* DLib::GetSymbolAddress(const char* name) {
void* address;
if (0 == uv_dlsym(&lib_, name, &address)) return address;
return nullptr;
}
#endif // !__POSIX__

} // end of namespace binding

} // end of namespace node

#endif // SRC_NODE_BINDING_INL_H_