Issue #896 identifies an race condition where multiple subscriptions listen to a common topic and one subscription, from it's callback destroys another subscription (an entity). This revealed an issue where i/o activity can exists on an entity's _handle after the entity has been destroyed. The result is an invalid entity pointer or worse a SEGFAULT. To address this I've added destroy state semantics to the Entity class.
full disclosure:
One quirky behavior I've attempted to resolve is that setting Entity._handle = null, results in the _handle being quickly GC'ed. This causes the SEGFAULT when the handle is still referenced by ShadowNode.Execute(). I believe after calling _handle.release(), the handle must not be GC'ed until after the current Node.execute() completes. I was not sure how to pull this off in C with Nan API so I implemented a hack by using a cache to hold _handle's after their release(). The cache will flush when its size exceeds a const (100 atm). I'm definitely open to revising this hack with the suggestion of others.
entity.js
added _destroy(), isDestroyed()
added cache to hold obsolete _handles to avoid premature GC
entity.d.ts
added isDestroyed()
node.js
added guard statements to execute() to filter out destroyed entities
action/client.js
added _destory() and isDestroyed()
refactored destroy() flow
action/server.js
added _destory() and isDestroyed()
refactored destroy() flows
main.ts
added tests for isDestroyed() to entity subclasses
Issue #896 identifies an race condition where multiple subscriptions listen to a common topic and one subscription, from it's callback destroys another subscription (an entity). This revealed an issue where i/o activity can exists on an entity's _handle after the entity has been destroyed. The result is an invalid entity pointer or worse a SEGFAULT. To address this I've added destroy state semantics to the Entity class.
full disclosure: One quirky behavior I've attempted to resolve is that setting
Entity._handle = null
, results in the _handle being quickly GC'ed. This causes the SEGFAULT when the handle is still referenced by ShadowNode.Execute(). I believe after calling _handle.release(), the handle must not be GC'ed until after the current Node.execute() completes. I was not sure how to pull this off in C with Nan API so I implemented a hack by using a cache to hold_handle
's after theirrelease()
. The cache will flush when its size exceeds a const (100 atm). I'm definitely open to revising this hack with the suggestion of others.entity.js
entity.d.ts
node.js
action/client.js
action/server.js
main.ts
test-destruction.js
Fix #896