@homaiohq/napi-quickfix 0.3.0 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/cpp/acceptor_wrap.cpp +21 -2
- package/cpp/addon.cpp +1 -1
- package/cpp/application_bridge.cpp +12 -0
- package/cpp/application_bridge.h +15 -0
- package/cpp/initiator_wrap.cpp +21 -2
- package/package.json +1 -1
- package/prebuilds/darwin-arm64/node.napi.node +0 -0
- package/prebuilds/darwin-x64/node.napi.node +0 -0
- package/prebuilds/linux-x64/node.napi.glibc.node +0 -0
- package/prebuilds/linux-x64/node.napi.musl.node +0 -0
- package/prebuilds/win32-x64/node.napi.node +0 -0
package/cpp/acceptor_wrap.cpp
CHANGED
|
@@ -185,6 +185,14 @@ void AcceptorWrap::DestroyStoppedEngine() {
|
|
|
185
185
|
SessionOpGate::Unregister(sessionIDs_, gate_.get());
|
|
186
186
|
acceptor_.reset();
|
|
187
187
|
gate_->Unfreeze();
|
|
188
|
+
// The engine is gone, so nothing will call the handlers again: drop the
|
|
189
|
+
// bridge's reference to them. It is what kept this wrap reachable from the
|
|
190
|
+
// user's Engine object (the handlers close over it) and so made the wrap
|
|
191
|
+
// -- and the Engine, its listeners and the parsed dictionaries -- immortal.
|
|
192
|
+
// On the graceful path this runs from the barrier queued behind every
|
|
193
|
+
// callback stop() fired, so none of them is lost; on the force path the
|
|
194
|
+
// TSFN was aborted first, discarding the queue.
|
|
195
|
+
if (bridge_) bridge_->DropHandlers();
|
|
188
196
|
}
|
|
189
197
|
|
|
190
198
|
Napi::Value AcceptorWrap::Start(const Napi::CallbackInfo& info) {
|
|
@@ -248,9 +256,13 @@ Napi::Value AcceptorWrap::Stop(const Napi::CallbackInfo& info) {
|
|
|
248
256
|
return deferred.Promise();
|
|
249
257
|
}
|
|
250
258
|
if (!acceptor_) {
|
|
251
|
-
// Already torn down: just release the TSFN
|
|
259
|
+
// Already torn down: just release the TSFN, drop the handlers (nothing
|
|
260
|
+
// can call them without an engine) and resolve.
|
|
252
261
|
stopped_ = true;
|
|
253
|
-
if (bridge_)
|
|
262
|
+
if (bridge_) {
|
|
263
|
+
bridge_->Release();
|
|
264
|
+
bridge_->DropHandlers();
|
|
265
|
+
}
|
|
254
266
|
deferred.Resolve(env.Undefined());
|
|
255
267
|
return deferred.Promise();
|
|
256
268
|
}
|
|
@@ -323,8 +335,15 @@ Napi::Value AcceptorWrap::Stop(const Napi::CallbackInfo& info) {
|
|
|
323
335
|
self->gate_->Unfreeze();
|
|
324
336
|
if (bridge) {
|
|
325
337
|
if (force) {
|
|
338
|
+
// The abort discarded the queue: no callback can reach JS
|
|
339
|
+
// any more, so the handlers can go now.
|
|
326
340
|
bridge->Abort();
|
|
341
|
+
bridge->DropHandlers();
|
|
327
342
|
} else {
|
|
343
|
+
// Callbacks already queued (a trailing 'logout') are still
|
|
344
|
+
// dispatched after the release, and read the handlers: keep
|
|
345
|
+
// them. The retried stop() drops them in
|
|
346
|
+
// DestroyStoppedEngine().
|
|
328
347
|
bridge->Release();
|
|
329
348
|
}
|
|
330
349
|
}
|
package/cpp/addon.cpp
CHANGED
|
@@ -30,7 +30,7 @@ void RegisterSessionStatic(Napi::Env env, Napi::Object exports);
|
|
|
30
30
|
namespace {
|
|
31
31
|
|
|
32
32
|
// Keep in sync with package.json "version".
|
|
33
|
-
constexpr const char* kAddonVersion = "0.3.
|
|
33
|
+
constexpr const char* kAddonVersion = "0.3.1";
|
|
34
34
|
|
|
35
35
|
Napi::Value Version(const Napi::CallbackInfo& info) {
|
|
36
36
|
return Napi::String::New(info.Env(), kAddonVersion);
|
|
@@ -167,6 +167,18 @@ void ApplicationBridge::WakeAllPending() {
|
|
|
167
167
|
}
|
|
168
168
|
}
|
|
169
169
|
|
|
170
|
+
void ApplicationBridge::DropHandlers() {
|
|
171
|
+
if (!handlers_.IsEmpty()) {
|
|
172
|
+
handlers_.Reset();
|
|
173
|
+
}
|
|
174
|
+
// The dictionaries pin a parsed FIX spec per session (a copy of the
|
|
175
|
+
// session's DataDictionaryProvider, which shares the dictionary's
|
|
176
|
+
// ownership); the sessions that could still want them are gone. Locked
|
|
177
|
+
// for form: the network threads that fill the map are joined by now.
|
|
178
|
+
std::lock_guard<std::mutex> lock(dictionariesMtx_);
|
|
179
|
+
dictionaries_.clear();
|
|
180
|
+
}
|
|
181
|
+
|
|
170
182
|
void ApplicationBridge::Ref(Napi::Env env) {
|
|
171
183
|
if (!released_) {
|
|
172
184
|
tsfn_.Ref(env);
|
package/cpp/application_bridge.h
CHANGED
|
@@ -125,6 +125,13 @@ class ApplicationBridge : public FIX::Application {
|
|
|
125
125
|
void Ref(Napi::Env env);
|
|
126
126
|
void Unref(Napi::Env env);
|
|
127
127
|
|
|
128
|
+
// Drop what the bridge roots on the engine's behalf: the handlers object
|
|
129
|
+
// and the per-session dictionaries (see handlers_ below). JS thread only;
|
|
130
|
+
// call it once the engine is gone, or once no queued callback can reach JS
|
|
131
|
+
// any more (after Abort()). A callback dispatched afterwards passes
|
|
132
|
+
// through as if no handler were installed.
|
|
133
|
+
void DropHandlers();
|
|
134
|
+
|
|
128
135
|
// Queue `fn` to run on the JS thread AFTER every callback already queued
|
|
129
136
|
// (the TSFN queue is FIFO), and keep the event loop alive until it has run.
|
|
130
137
|
// JS thread only; call it before Release()/Abort(). Returns false, without
|
|
@@ -167,6 +174,14 @@ class ApplicationBridge : public FIX::Application {
|
|
|
167
174
|
std::map<FIX::SessionID, std::shared_ptr<const FIX::DataDictionary>>
|
|
168
175
|
dictionaries_;
|
|
169
176
|
|
|
177
|
+
// The user's handlers object, held strongly for the engine's whole life so
|
|
178
|
+
// a started engine stays reachable through them (the wrapper's handlers
|
|
179
|
+
// close over the Engine), like a net.Server does through its listeners.
|
|
180
|
+
// That makes a reference cycle through native memory the GC cannot see:
|
|
181
|
+
// wrap -> bridge -> handlers -> Engine -> wrap. The wraps break it with
|
|
182
|
+
// DropHandlers() as soon as the engine is destroyed (stop() settled, or
|
|
183
|
+
// the force/failed-stop paths that abort the TSFN); the destructor's Reset
|
|
184
|
+
// is only the fallback for a wrap collected without stop().
|
|
170
185
|
Napi::ObjectReference handlers_;
|
|
171
186
|
TSFN tsfn_;
|
|
172
187
|
bool released_ = false;
|
package/cpp/initiator_wrap.cpp
CHANGED
|
@@ -207,6 +207,14 @@ void InitiatorWrap::DestroyStoppedEngine() {
|
|
|
207
207
|
SessionOpGate::Unregister(sessionIDs_, gate_.get());
|
|
208
208
|
initiator_.reset();
|
|
209
209
|
gate_->Unfreeze();
|
|
210
|
+
// The engine is gone, so nothing will call the handlers again: drop the
|
|
211
|
+
// bridge's reference to them. It is what kept this wrap reachable from the
|
|
212
|
+
// user's Engine object (the handlers close over it) and so made the wrap
|
|
213
|
+
// -- and the Engine, its listeners and the parsed dictionaries -- immortal.
|
|
214
|
+
// On the graceful path this runs from the barrier queued behind every
|
|
215
|
+
// callback stop() fired, so none of them is lost; on the force path the
|
|
216
|
+
// TSFN was aborted first, discarding the queue.
|
|
217
|
+
if (bridge_) bridge_->DropHandlers();
|
|
210
218
|
}
|
|
211
219
|
|
|
212
220
|
Napi::Value InitiatorWrap::Start(const Napi::CallbackInfo& info) {
|
|
@@ -272,9 +280,13 @@ Napi::Value InitiatorWrap::Stop(const Napi::CallbackInfo& info) {
|
|
|
272
280
|
return deferred.Promise();
|
|
273
281
|
}
|
|
274
282
|
if (!initiator_) {
|
|
275
|
-
// Already torn down: just release the TSFN
|
|
283
|
+
// Already torn down: just release the TSFN, drop the handlers (nothing
|
|
284
|
+
// can call them without an engine) and resolve.
|
|
276
285
|
stopped_ = true;
|
|
277
|
-
if (bridge_)
|
|
286
|
+
if (bridge_) {
|
|
287
|
+
bridge_->Release();
|
|
288
|
+
bridge_->DropHandlers();
|
|
289
|
+
}
|
|
278
290
|
deferred.Resolve(env.Undefined());
|
|
279
291
|
return deferred.Promise();
|
|
280
292
|
}
|
|
@@ -347,8 +359,15 @@ Napi::Value InitiatorWrap::Stop(const Napi::CallbackInfo& info) {
|
|
|
347
359
|
self->gate_->Unfreeze();
|
|
348
360
|
if (bridge) {
|
|
349
361
|
if (force) {
|
|
362
|
+
// The abort discarded the queue: no callback can reach JS
|
|
363
|
+
// any more, so the handlers can go now.
|
|
350
364
|
bridge->Abort();
|
|
365
|
+
bridge->DropHandlers();
|
|
351
366
|
} else {
|
|
367
|
+
// Callbacks already queued (a trailing 'logout') are still
|
|
368
|
+
// dispatched after the release, and read the handlers: keep
|
|
369
|
+
// them. The retried stop() drops them in
|
|
370
|
+
// DestroyStoppedEngine().
|
|
352
371
|
bridge->Release();
|
|
353
372
|
}
|
|
354
373
|
}
|
package/package.json
CHANGED
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|