Skip to content

Conversation

@wayneparrott
Copy link
Collaborator

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

test-destruction.js

  • added entity.destory() test cases

Fix #896

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

test-destruction.js
- added entity.destory() test cases
entity.js
- added static gcHandles()

node.js
- revised execute() to use for-of loops for each entity type. This
simplifies the looping of each group of entities through the use of
loop interruption (e.g., use continue statement) to detect a
destroyed entity before processing. Additionally it the for-of loop
reduces the number of isDestroyed() calls Lastly, added a call to
Entity.gcHandles() prior to exiting the method.
@wayneparrott
Copy link
Collaborator Author

@minggangw my latest commit 9af468f to address your suggestion may have overwritten f3840e2

@minggangw
Copy link
Member

@minggangw my latest commit 9af468f to address your suggestion may have overwritten f3840e2

Sorry for missing this comment, the change looks good to me, thanks!

Copy link
Member

@minggangw minggangw left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM! Thanks for fixing the crashing issue

@minggangw
Copy link
Member

Hi @wayneparrott I have approved this PR, please go ahead to merge it after resolving the conflict, thanks!

@wayneparrott wayneparrott merged commit 6cef17a into RobotWebTools:develop Mar 7, 2023
minggangw pushed a commit that referenced this pull request Apr 3, 2023
* improve entity destroy() resiliency #896

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

test-destruction.js
- added entity.destory() test cases

* improve destroyed entity handling to avoid segfault

entity.js
- added static gcHandles()

node.js
- revised execute() to use for-of loops for each entity type. This
simplifies the looping of each group of entities through the use of
loop interruption (e.g., use continue statement) to detect a
destroyed entity before processing. Additionally it the for-of loop
reduces the number of isDestroyed() calls Lastly, added a call to
Entity.gcHandles() prior to exiting the method.
@wayneparrott wayneparrott deleted the destroyed_entity_896 branch April 10, 2023 23:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Crash when destroying subscription

2 participants