| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent ea63005 commit 1723773
2 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -115,20 +115,71 @@ struct ACHHandle final { | |||
| 115 | 115 | // this. | |
| 116 | 116 | void DeleteACHHandle::operator ()(ACHHandle* handle) const { delete handle; } | |
| 117 | 117 | ||
| 118 | + // TODO(addaleax): Having this extra set of data structures is far from | ||
| 119 | + // ideal, but unfortunately the public synchronous cleanup hook API was | ||
| 120 | + // slightly mis-designed; in particular, RemoveEnvironmentCleanupHook() needs | ||
| 121 | + // to keep working when the Isolate either has no active context (such as | ||
| 122 | + // during GC) or that context is associated with another Node.js Environment. | ||
| 123 | + // We should align this with the asynchronous API, which handles this properly | ||
| 124 | + // through an explicit reference to the cleanup hook instead of requiring | ||
| 125 | + // lookups in internal maps. | ||
| 126 | + struct CleanupHookThunk final { | ||
| 127 | + Isolate* isolate; | ||
| 128 | + Environment* env; | ||
| 129 | + CleanupHook fun; | ||
| 130 | + void* arg; | ||
| 131 | + | ||
| 132 | + bool operator==(const CleanupHookThunk& other) const { | ||
| 133 | + // `env` is intentionally not part of this comparison | ||
| 134 | + return isolate == other.isolate && fun == other.fun && arg == other.arg; | ||
| 135 | + } | ||
| 136 | + }; | ||
| 137 | + struct CleanupHookThunkHash { | ||
| 138 | + size_t operator()(const CleanupHookThunk& thunk) const { | ||
| 139 | + return std::hash<void*>()(thunk.arg); | ||
| 140 | + } | ||
| 141 | + }; | ||
| 142 | + using CleanupHookRegistry = | ||
| 143 | + std::unordered_set<CleanupHookThunk, CleanupHookThunkHash>; | ||
| 144 | + static ExclusiveAccess<CleanupHookRegistry> cleanup_hook_registry; | ||
| 145 | + | ||
| 146 | + static void CleanupHookThunkRun(void* arg) { | ||
| 147 | + const CleanupHookThunk* thunk = static_cast<CleanupHookThunk*>(arg); | ||
| 148 | + thunk->fun(thunk->arg); | ||
| 149 | + RemoveEnvironmentCleanupHook(thunk->isolate, thunk->fun, thunk->arg); | ||
| 150 | + } | ||
| 151 | + | ||
| 118 | 152 | void AddEnvironmentCleanupHook(Isolate* isolate, | |
| 119 | 153 | CleanupHook fun, | |
| 120 | 154 | void* arg) { | |
| 121 | 155 | Environment* env = Environment::GetCurrent(isolate); | |
| 122 | 156 | CHECK_NOT_NULL(env); | |
| 123 | - env->AddCleanupHook(fun, arg); | ||
| 157 | + void* wrapped_arg; | ||
| 158 | + { | ||
| 159 | + ExclusiveAccess<CleanupHookRegistry>::Scoped registry( | ||
| 160 | + &cleanup_hook_registry); | ||
| 161 | + auto result = registry->insert({isolate, env, fun, arg}); | ||
| 162 | + CHECK(result.second); | ||
| 163 | + wrapped_arg = const_cast<CleanupHookThunk*>(&*result.first); | ||
| 164 | + } | ||
| 165 | + env->AddCleanupHook(CleanupHookThunkRun, wrapped_arg); | ||
| 124 | 166 | } | |
| 125 | 167 | ||
| 126 | 168 | void RemoveEnvironmentCleanupHook(Isolate* isolate, | |
| 127 | 169 | CleanupHook fun, | |
| 128 | 170 | void* arg) { | |
| 129 | - Environment* env = Environment::GetCurrent(isolate); | ||
| 130 | - CHECK_NOT_NULL(env); | ||
| 131 | - env->RemoveCleanupHook(fun, arg); | ||
| 171 | + CleanupHookThunk thunk; | ||
| 172 | + void* wrapped_arg; | ||
| 173 | + { | ||
| 174 | + ExclusiveAccess<CleanupHookRegistry>::Scoped registry( | ||
| 175 | + &cleanup_hook_registry); | ||
| 176 | + auto result = registry->find({isolate, nullptr, fun, arg}); | ||
| 177 | + if (result == registry->end()) return; | ||
| 178 | + wrapped_arg = const_cast<CleanupHookThunk*>(&*result); | ||
| 179 | + thunk = *result; | ||
| 180 | + registry->erase(result); | ||
| 181 | + } | ||
| 182 | + thunk.env->RemoveCleanupHook(CleanupHookThunkRun, wrapped_arg); | ||
| 132 | 183 | } | |
| 133 | 184 | ||
| 134 | 185 | static void FinishAsyncCleanupHook(void* arg) { | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -54,8 +54,23 @@ void Initialize(Local<Object> exports, | |||
| 54 | 54 | Isolate* isolate = Isolate::GetCurrent(); | |
| 55 | 55 | node::AddEnvironmentCleanupHook( | |
| 56 | 56 | isolate, Cleanup, const_cast<void*>(static_cast<const void*>("cleanup"))); | |
| 57 | - node::AddEnvironmentCleanupHook(isolate, Dummy, nullptr); | ||
| 58 | - node::RemoveEnvironmentCleanupHook(isolate, Dummy, nullptr); | ||
| 57 | + | ||
| 58 | + // Test that adding and removing a cleanup hook works as expected | ||
| 59 | + { | ||
| 60 | + node::AddEnvironmentCleanupHook(isolate, Dummy, nullptr); | ||
| 61 | + node::RemoveEnvironmentCleanupHook(isolate, Dummy, nullptr); | ||
| 62 | + } | ||
| 63 | + | ||
| 64 | + // Test that adding and removing a cleanup hook also works if there | ||
| 65 | + // is no active context during removal | ||
| 66 | + { | ||
| 67 | + node::AddEnvironmentCleanupHook(isolate, Dummy, nullptr); | ||
| 68 | + { | ||
| 69 | + context->Exit(); | ||
| 70 | + node::RemoveEnvironmentCleanupHook(isolate, Dummy, nullptr); | ||
| 71 | + context->Enter(); | ||
| 72 | + } | ||
| 73 | + } | ||
| 59 | 74 | ||
| 60 | 75 | if (getenv("addExtraItemToEventLoop") != nullptr) { | |
| 61 | 76 | // Add an item to the event loop that we do not clean up in order to make | |
| Back | FazBrowse Home | New Git URL |
0 commit comments