Skip to content

Commit 005cbc9

Browse files
author
Gabriel Schulhof
committed
src: unload addons when environment quits
This is an alternative to nodejs#23319 which attaches the loaded addons to the environment and closes them when the environment is destroyed.
1 parent c991280 commit 005cbc9

File tree

12 files changed

+154
-99
lines changed

12 files changed

+154
-99
lines changed

node.gyp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -421,6 +421,7 @@
421421
'src/node_api.h',
422422
'src/node_api_types.h',
423423
'src/node_binding.h',
424+
'src/node_binding-inl.h',
424425
'src/node_buffer.h',
425426
'src/node_constants.h',
426427
'src/node_context_data.h',

src/env-inl.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -396,6 +396,15 @@ inline uv_loop_t* Environment::event_loop() const {
396396
return isolate_data()->event_loop();
397397
}
398398

399+
inline void Environment::TryLoadAddon(const char* filename,
400+
int flags,
401+
std::function<bool(binding::DLib*)> was_loaded) {
402+
loaded_addons_.emplace_back(filename, flags);
403+
if (!was_loaded(&loaded_addons_.back())) {
404+
loaded_addons_.pop_back();
405+
}
406+
}
407+
399408
inline Environment::AsyncHooks* Environment::async_hooks() {
400409
return &async_hooks_;
401410
}

src/env.cc

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,11 @@ Environment::~Environment() {
267267

268268
TRACE_EVENT_NESTABLE_ASYNC_END0(
269269
TRACING_CATEGORY_NODE1(environment), "Environment", this);
270+
271+
// Dereference all addons that were loaded into this environment.
272+
for (auto& addon : loaded_addons_) {
273+
addon.Close();
274+
}
270275
}
271276

272277
void Environment::Start(const std::vector<std::string>& args,

src/env.h

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,18 +30,20 @@
3030
#endif
3131
#include "handle_wrap.h"
3232
#include "node.h"
33+
#include "node_binding-inl.h"
3334
#include "node_http2_state.h"
3435
#include "node_options.h"
3536
#include "req_wrap.h"
3637
#include "util.h"
3738
#include "uv.h"
3839
#include "v8.h"
3940

40-
#include <list>
4141
#include <stdint.h>
42-
#include <vector>
42+
#include <functional>
43+
#include <list>
4344
#include <unordered_map>
4445
#include <unordered_set>
46+
#include <vector>
4547

4648
struct nghttp2_rcbuf;
4749

@@ -636,6 +638,9 @@ class Environment {
636638
inline v8::Isolate* isolate() const;
637639
inline uv_loop_t* event_loop() const;
638640
inline uint32_t watched_providers() const;
641+
inline void TryLoadAddon(const char* filename,
642+
int flags,
643+
std::function<bool(binding::DLib*)> was_loaded);
639644

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

929+
std::list<binding::DLib> loaded_addons_;
924930
v8::Isolate* const isolate_;
925931
IsolateData* const isolate_data_;
926932
uv_timer_t timer_handle_;

src/node.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
2020
// USE OR OTHER DEALINGS IN THE SOFTWARE.
2121

22-
#include "node_binding.h"
22+
#include "node_binding-inl.h"
2323
#include "node_buffer.h"
2424
#include "node_constants.h"
2525
#include "node_context_data.h"

src/node_api.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
#define NAPI_EXPERIMENTAL
44
#include "js_native_api_v8.h"
55
#include "node_api.h"
6-
#include "node_binding.h"
6+
#include "node_binding-inl.h"
77
#include "node_errors.h"
88
#include "node_internals.h"
99

src/node_binding-inl.h

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
#ifndef SRC_NODE_BINDING_INL_H_
2+
#define SRC_NODE_BINDING_INL_H_
3+
4+
#include "node_binding.h"
5+
6+
namespace node {
7+
8+
namespace binding {
9+
10+
inline DLib::DLib(const char* filename, int flags):
11+
filename_(filename), flags_(flags), handle_(nullptr) {}
12+
13+
#ifdef __POSIX__
14+
inline bool DLib::Open() {
15+
handle_ = dlopen(filename_.c_str(), flags_);
16+
if (handle_ != nullptr) return true;
17+
errmsg_ = dlerror();
18+
return false;
19+
}
20+
21+
inline void DLib::Close() {
22+
if (handle_ == nullptr) return;
23+
dlclose(handle_);
24+
handle_ = nullptr;
25+
}
26+
27+
inline void* DLib::GetSymbolAddress(const char* name) {
28+
return dlsym(handle_, name);
29+
}
30+
#else // !__POSIX__
31+
inline bool DLib::Open() {
32+
int ret = uv_dlopen(filename_.c_str(), &lib_);
33+
if (ret == 0) {
34+
handle_ = static_cast<void*>(lib_.handle);
35+
return true;
36+
}
37+
errmsg_ = uv_dlerror(&lib_);
38+
uv_dlclose(&lib_);
39+
return false;
40+
}
41+
42+
inline void DLib::Close() {
43+
if (handle_ == nullptr) return;
44+
uv_dlclose(&lib_);
45+
handle_ = nullptr;
46+
}
47+
48+
inline void* DLib::GetSymbolAddress(const char* name) {
49+
void* address;
50+
if (0 == uv_dlsym(&lib_, name, &address)) return address;
51+
return nullptr;
52+
}
53+
#endif // !__POSIX__
54+
55+
} // end of namespace binding
56+
57+
} // end of namespace node
58+
59+
#endif // SRC_NODE_BINDING_INL_H_

src/node_binding.cc

Lines changed: 23 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,7 @@
1-
#include "node_binding.h"
1+
#include "node_binding-inl.h"
22
#include "node_internals.h"
33
#include "node_native_module.h"
44

5-
#if defined(__POSIX__)
6-
#include <dlfcn.h>
7-
#endif
8-
95
#if HAVE_OPENSSL
106
#define NODE_BUILTIN_OPENSSL_MODULES(V) V(crypto) V(tls_wrap)
117
#else
@@ -126,74 +122,6 @@ extern "C" void node_module_register(void* m) {
126122

127123
namespace binding {
128124

129-
class DLib {
130-
public:
131-
#ifdef __POSIX__
132-
static const int kDefaultFlags = RTLD_LAZY;
133-
#else
134-
static const int kDefaultFlags = 0;
135-
#endif
136-
137-
inline DLib(const char* filename, int flags)
138-
: filename_(filename), flags_(flags), handle_(nullptr) {}
139-
140-
inline bool Open();
141-
inline void Close();
142-
inline void* GetSymbolAddress(const char* name);
143-
144-
const std::string filename_;
145-
const int flags_;
146-
std::string errmsg_;
147-
void* handle_;
148-
#ifndef __POSIX__
149-
uv_lib_t lib_;
150-
#endif
151-
private:
152-
DISALLOW_COPY_AND_ASSIGN(DLib);
153-
};
154-
155-
#ifdef __POSIX__
156-
bool DLib::Open() {
157-
handle_ = dlopen(filename_.c_str(), flags_);
158-
if (handle_ != nullptr) return true;
159-
errmsg_ = dlerror();
160-
return false;
161-
}
162-
163-
void DLib::Close() {
164-
if (handle_ == nullptr) return;
165-
dlclose(handle_);
166-
handle_ = nullptr;
167-
}
168-
169-
void* DLib::GetSymbolAddress(const char* name) {
170-
return dlsym(handle_, name);
171-
}
172-
#else // !__POSIX__
173-
bool DLib::Open() {
174-
int ret = uv_dlopen(filename_.c_str(), &lib_);
175-
if (ret == 0) {
176-
handle_ = static_cast<void*>(lib_.handle);
177-
return true;
178-
}
179-
errmsg_ = uv_dlerror(&lib_);
180-
uv_dlclose(&lib_);
181-
return false;
182-
}
183-
184-
void DLib::Close() {
185-
if (handle_ == nullptr) return;
186-
uv_dlclose(&lib_);
187-
handle_ = nullptr;
188-
}
189-
190-
void* DLib::GetSymbolAddress(const char* name) {
191-
void* address;
192-
if (0 == uv_dlsym(&lib_, name, &address)) return address;
193-
return nullptr;
194-
}
195-
#endif // !__POSIX__
196-
197125
using InitializerCallback = void (*)(Local<Object> exports,
198126
Local<Value> module,
199127
Local<Context> context);
@@ -247,8 +175,8 @@ void DLOpen(const FunctionCallbackInfo<Value>& args) {
247175
}
248176

249177
node::Utf8Value filename(env->isolate(), args[1]); // Cast
250-
DLib dlib(*filename, flags);
251-
bool is_opened = dlib.Open();
178+
env->TryLoadAddon(*filename, flags, [&](DLib* dlib) {
179+
const bool is_opened = dlib->Open();
252180

253181
// Objects containing v14 or later modules will have registered themselves
254182
// on the pending list. Activate all of them now. At present, only one
@@ -258,37 +186,38 @@ void DLOpen(const FunctionCallbackInfo<Value>& args) {
258186
uv_key_set(&thread_local_modpending, nullptr);
259187

260188
if (!is_opened) {
261-
Local<String> errmsg = OneByteString(env->isolate(), dlib.errmsg_.c_str());
262-
dlib.Close();
189+
Local<String> errmsg = OneByteString(env->isolate(), dlib->errmsg_.c_str());
190+
dlib->Close();
263191
#ifdef _WIN32
264192
// Windows needs to add the filename into the error message
265193
errmsg = String::Concat(
266194
env->isolate(), errmsg, args[1]->ToString(context).ToLocalChecked());
267195
#endif // _WIN32
268196
env->isolate()->ThrowException(Exception::Error(errmsg));
269-
return;
197+
return false;
270198
}
271199

272200
if (mp == nullptr) {
273-
if (auto callback = GetInitializerCallback(&dlib)) {
201+
if (auto callback = GetInitializerCallback(dlib)) {
274202
callback(exports, module, context);
275-
} else if (auto napi_callback = GetNapiInitializerCallback(&dlib)) {
203+
} else if (auto napi_callback = GetNapiInitializerCallback(dlib)) {
276204
napi_module_register_by_symbol(exports, module, context, napi_callback);
277205
} else {
278-
dlib.Close();
206+
dlib->Close();
279207
env->ThrowError("Module did not self-register.");
208+
return false;
280209
}
281-
return;
210+
return true;
282211
}
283212

284213
// -1 is used for N-API modules
285214
if ((mp->nm_version != -1) && (mp->nm_version != NODE_MODULE_VERSION)) {
286215
// Even if the module did self-register, it may have done so with the wrong
287216
// version. We must only give up after having checked to see if it has an
288217
// appropriate initializer callback.
289-
if (auto callback = GetInitializerCallback(&dlib)) {
218+
if (auto callback = GetInitializerCallback(dlib)) {
290219
callback(exports, module, context);
291-
return;
220+
return true;
292221
}
293222
char errmsg[1024];
294223
snprintf(errmsg,
@@ -305,17 +234,17 @@ void DLOpen(const FunctionCallbackInfo<Value>& args) {
305234

306235
// NOTE: `mp` is allocated inside of the shared library's memory, calling
307236
// `dlclose` will deallocate it
308-
dlib.Close();
237+
dlib->Close();
309238
env->ThrowError(errmsg);
310-
return;
239+
return false;
311240
}
312241
if (mp->nm_flags & NM_F_BUILTIN) {
313-
dlib.Close();
242+
dlib->Close();
314243
env->ThrowError("Built-in module self-registered.");
315-
return;
244+
return false;
316245
}
317246

318-
mp->nm_dso_handle = dlib.handle_;
247+
mp->nm_dso_handle = dlib->handle_;
319248
mp->nm_link = modlist_addon;
320249
modlist_addon = mp;
321250

@@ -324,11 +253,14 @@ void DLOpen(const FunctionCallbackInfo<Value>& args) {
324253
} else if (mp->nm_register_func != nullptr) {
325254
mp->nm_register_func(exports, module, mp->nm_priv);
326255
} else {
327-
dlib.Close();
256+
dlib->Close();
328257
env->ThrowError("Module has no declared entry point.");
329-
return;
258+
return false;
330259
}
331260

261+
return true;
262+
});
263+
332264
// Tell coverity that 'handle' should not be freed when we return.
333265
// coverity[leaked_storage]
334266
}

src/node_binding.h

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,14 @@
33

44
#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
55

6+
#if defined(__POSIX__)
7+
#include <dlfcn.h>
8+
#endif
9+
610
#include "node.h"
11+
#define NAPI_EXPERIMENTAL
712
#include "node_api.h"
13+
#include "util.h"
814
#include "uv.h"
915
#include "v8.h"
1016

@@ -46,6 +52,32 @@ extern bool node_is_initialized;
4652

4753
namespace binding {
4854

55+
class DLib {
56+
public:
57+
#ifdef __POSIX__
58+
static const int kDefaultFlags = RTLD_LAZY;
59+
#else
60+
static const int kDefaultFlags = 0;
61+
#endif
62+
63+
DLib(const char* filename, int flags);
64+
65+
bool Open();
66+
void Close();
67+
void* GetSymbolAddress(const char* name);
68+
69+
const std::string filename_;
70+
const int flags_;
71+
std::string errmsg_;
72+
void* handle_;
73+
#ifndef __POSIX__
74+
uv_lib_t lib_;
75+
#endif
76+
77+
private:
78+
DISALLOW_COPY_AND_ASSIGN(DLib);
79+
};
80+
4981
// Call _register<module_name> functions for all of
5082
// the built-in modules. Because built-in modules don't
5183
// use the __attribute__((constructor)). Need to
@@ -60,5 +92,7 @@ void DLOpen(const v8::FunctionCallbackInfo<v8::Value>& args);
6092

6193
} // namespace node
6294

95+
#include "node_binding-inl.h"
96+
6397
#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
6498
#endif // SRC_NODE_BINDING_H_

0 commit comments

Comments
 (0)