Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There were both a race condition and logic error with the implementation, which could lead to spurious signals, crashes, and other undefined behaviour.
Race Condition
Thread 1:
...
suspend
: acquires lock for actorA
and poolSuspended
suspend
: releases the locksThread 2:
....
resume
: acquires lock for actorA
and poolSuspended
, so the actor is queuedresume
: releases the locksA
is popped from the work queue and resumes executionBoth thread 1 and 2 now have unguarded concurrent ownership of actor
A
, which constitutes a race condition.Logic Error
Note: this is only one example of what could happen.
Thread 1:
...
suspend
: acquires lock for actorA
and poolSuspended
A
and and poolThread 2:
...
resume
: acquires lock for actorA
and poolSuspend
, so the actor is queuedA
resumes executionjield
is called, and the state of actorA
changes toJielding
workerThread
: fetches the state of actorA
(Jielding
)Thread 1:
workerThread
: fetches the state of actorA
(Jielding
)A
for execution and changes the state toQueued
Thread 2:
state
variable still containsJielding
, so the actor is queued againThe same actor is now in the work queue two times!
The Solution
In both cases, the problem is that another thread can assume ownership of an actor while the worker thread responsible for the actor hasn't finished suspending it, due to the actor state being eagerly set to
Suspended
bysuspend
.suspend
now sets the state to the newSuspending
state first. When handling theSuspending
state, if new signals were received in the meantime (sendSig
cannot resume a suspending actor), the actor is queued again right away. Otherwise, it's suspended.