-
Bug
-
Resolution: Fixed
-
P3
-
None
-
repo-panama
When closing a shared scope, we perform a thread-local handshake. Each thread is inspected, to look for problematic concurrent accesses to the scope being closed.
When such access is detected, we'd like, in principle, to just make the accessing thread fail with an asynchronous exception. We explored this path in Java 15, and this option was considered too complex to maintain going forward.
For these reasons, we settled on an hybrid approach, where we first update the scope state from ALIVE to CLOSING (in which segments will no longer be accessible). Then the handshake tells us if concurrent access is happening, in which case we go back from CLOSING to ALIVE. If no access is happening, we can move from CLOSING to CLOSED, and call the cleanup actions.
This choice, while simpler to implement, has an important drawback: when races between access and close do occur, we can get exceptions both on the closing side *and* on the accessing side.
A possible improvement would consist in:
1. move the scope from ALIVE to CLOSED - no new thread can access segments associated with the scope
2. do an initial handshake, to collect all threads that are accessing the scope concurrently
3. if no thread is found, finish
4. if some threads T1, T2 ... Tn are found, keep doing handshakes (only against these threads) then go back to (3).
This logic converges quickly, because when we do an handshake against a target thread, we also deoptimize that thread. This means that the thread will have to re-load the liveness state of the resource it's accessing, which will eventually result in an exception being thrown (as the scope is CLOSED). So, it is not possible for a thread to remain in a pending state forever.
This algorithm allows to simplify the ResourceScope::close API.
When such access is detected, we'd like, in principle, to just make the accessing thread fail with an asynchronous exception. We explored this path in Java 15, and this option was considered too complex to maintain going forward.
For these reasons, we settled on an hybrid approach, where we first update the scope state from ALIVE to CLOSING (in which segments will no longer be accessible). Then the handshake tells us if concurrent access is happening, in which case we go back from CLOSING to ALIVE. If no access is happening, we can move from CLOSING to CLOSED, and call the cleanup actions.
This choice, while simpler to implement, has an important drawback: when races between access and close do occur, we can get exceptions both on the closing side *and* on the accessing side.
A possible improvement would consist in:
1. move the scope from ALIVE to CLOSED - no new thread can access segments associated with the scope
2. do an initial handshake, to collect all threads that are accessing the scope concurrently
3. if no thread is found, finish
4. if some threads T1, T2 ... Tn are found, keep doing handshakes (only against these threads) then go back to (3).
This logic converges quickly, because when we do an handshake against a target thread, we also deoptimize that thread. This means that the thread will have to re-load the liveness state of the resource it's accessing, which will eventually result in an exception being thrown (as the scope is CLOSED). So, it is not possible for a thread to remain in a pending state forever.
This algorithm allows to simplify the ResourceScope::close API.