-
Notifications
You must be signed in to change notification settings - Fork 158
Make DeterministicRunner#close always block until the closing is done #1255
New issue
Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? # to your account
Conversation
0e1d01d
to
054bb25
Compare
054bb25
to
5a2f4b7
Compare
closeRequested = true; | ||
if (inRunUntilAllBlocked) { | ||
if ( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The main important change in this PR is here, everything else are refactorings, cleanups, and comments.
if (inRunUntilAllBlocked) { | ||
if ( | ||
// Do not close immediately if runUntilAllBlocked executes. | ||
// closeRequested will tell a control thread to call close() at the end of the event loop. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This comment should remain above the conditional I think since it refers to setting of closeRequested
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, it's for inRunUntilAllBlocked
check specifically. Another check in this if is closeStarted
, not closeRequested
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am referring to the moved comment that is talking about closeRequested
. It made more since when it was above closeRequested
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not really, because this comment makes sense only on inRunUntilAllBlocked == true
condition. Before it was the only condition, so it made sense there, but not anymore.
I rephrased the comment to make it more clear.
// closeRequested will tell a control thread to call close() at the end of the event loop. | ||
inRunUntilAllBlocked | ||
// some other thread or caller is already in the process of closing | ||
|| closeStarted) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This will only be true if another thread gets to line 360 where this is unlocked right? Otherwise, isn't that what the lock is for? Doesn't the lock prevent multiple threads calling this method?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This will only be true if another thread gets to line 360 where this is unlocked right?
Yes, another thread started a shutdown, set the flag and released the lock, another thread can proceed to here and observe that another shutdown is already in progress.
lock.unlock(); | ||
// We will not perform the closure in this call and should just wait on the future when | ||
// another thread responsible for it will close. | ||
closeFuture.join(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Any concerns of a hang here? Specifically, if closeFuture.complete
is not reached but closeStarted = true
is, this code will hang.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No. If the thread started shutdown, but didn't reach the end - it's a bug and something unexpected happened. But to make this code LOOK safer, I will wrap it in try-finally.
// closeRequested will tell a control thread to call close() at the end of the event loop. | ||
inRunUntilAllBlocked | ||
// some other thread or caller is already in the process of closing | ||
|| closeStarted) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Probably not much of an improvement except for readability, but you could get rid of closeStarted
, make closeFuture
nullable, only create the future at 331, and change this to || closeFuture != null
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nah, the problem is that we can be here if inRunUntilAllBlocked is true, closeStarted is false. In this case, we don't have a future to wait on. We also can't create one if we use closeFuture != null
as closeStarted
.
I think it's also easier for future maintenance of this class to have a final CompletableFuture
representing closure from the start that can be referenced at any moment at all even without taking a lock.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Concur on the final (though could use AtomicReference
I guess). I just know that they have similar use. But defniitely a subjective thing and adds no real value. Ignore.
@@ -343,6 +372,9 @@ public void close() { | |||
throw new Error("Unexpected failure stopping coroutine", e); | |||
} | |||
} | |||
|
|||
closeFuture.complete(null); | |||
closed = true; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Javadoc says this is to be used under lock, but this is section of code is not locked (can make synchronized or use AtomicBoolean
if it's important to be unlocked while waiting on threadFutures
)
Also, some languages have compile-time guards you can use with annotations that, when you say a mutex/lock applies to a class member, it statically analyzes that only that class member is used under that lock. Does Java have anything like that?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch! Yeah. I think, instead of making it Atomic, I will better just get rid of a separate var altogether and will just use the future instead.
4adfb85
to
781287a
Compare
781287a
to
5f9f192
Compare
What was changed
DeterministicRunner#close
now returns always after the closing is fully completed.Why?
Previous behavior may be causing rejected executions from the cache because the threads were not actually released yet even when the executions are gone from the cache.
How was this tested
Unit test executes two
#close
in parallel and checking that they return only when the workflow thread is finished which fails on a current master.