-
Notifications
You must be signed in to change notification settings - Fork 1.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix DefaultExecutor not being able to exit #1876
Conversation
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.
While we are here, fixing this code, I'd also suggest to make the following performance improvement.
See, right now looping for events works like this:
- The next event is taken from the queue and executed.
nextTime
is called to see when the event after this happens (and the queue is analyzed for that). Assume we have an event in the queue, so- We immediately go to 1, where we look at the queue again to remove this event.
It seems inefficient to check queues twice in a row. Instead, I'd suggest to always return 0
from processNextEvent
when even was just executed and return nextTime
only when no event was executed.
@@ -249,9 +258,15 @@ internal abstract class EventLoopImplBase: EventLoopImplPlatform(), Delay { | |||
} | |||
} | |||
|
|||
override fun processNextEvent(): Long { | |||
override fun popNextTask(onlyUnconfined: Boolean): Runnable? { |
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 cannot find a place where this method if called with onlyUnconfined = true
.
If we do this optimization, then the decomposition of Should I keep the decomposition or should I revert it and instead implement the small change to the logic of |
It would be also great to avoid decomposition. It will make the code easier to understand. |
This test could in theory already fail on the second `checkNotEmpty`: after the first `checkNotEmpty` has passed, first, the ticker channel awakens to produce a new element, and then the main thread awakens to check if it's there. However, if the ticker channel is sufficiently slowed down, it may not produce the element in time for the main thread to find it. After introducing the change that allows the worker thread in `DefaultExecutor` to shut down, the initial delay of 2500 ms is sufficient for the worker to shut down (which, by default, happens after 1000 ms of inactivity), and then the aforementioned race condition worsens: additional time is required to launch a new worker thread and it's much easier to miss the deadline. Now, the delays are much shorter, meaning that the worker thread is never inactive long enough to shut down.
Also adds the performance optimization. See the discussion on the PR.
* Fix DefaultExecutor not being able to exit. * Also adds the performance optimization. See the discussion on the PR. * Add a stress test for the DefaultExecutor worker shutting down. * Make `testDelayChannelBackpressure2` not fail: This test could in theory already fail on the second `checkNotEmpty`: after the first `checkNotEmpty` has passed, first, the ticker channel awakens to produce a new element, and then the main thread awakens to check if it's there. However, if the ticker channel is sufficiently slowed down, it may not produce the element in time for the main thread to find it. After introducing the change that allows the worker thread in `DefaultExecutor` to shut down, the initial delay of 2500 ms is sufficient for the worker to shut down (which, by default, happens after 1000 ms of inactivity), and then the aforementioned race condition worsens: additional time is required to launch a new worker thread and it's much easier to miss the deadline. Now, the delays are much shorter, meaning that the worker thread is never inactive long enough to shut down. Fixes #856
* Fix DefaultExecutor not being able to exit. * Also adds the performance optimization. See the discussion on the PR. * Add a stress test for the DefaultExecutor worker shutting down. * Make `testDelayChannelBackpressure2` not fail: This test could in theory already fail on the second `checkNotEmpty`: after the first `checkNotEmpty` has passed, first, the ticker channel awakens to produce a new element, and then the main thread awakens to check if it's there. However, if the ticker channel is sufficiently slowed down, it may not produce the element in time for the main thread to find it. After introducing the change that allows the worker thread in `DefaultExecutor` to shut down, the initial delay of 2500 ms is sufficient for the worker to shut down (which, by default, happens after 1000 ms of inactivity), and then the aforementioned race condition worsens: additional time is required to launch a new worker thread and it's much easier to miss the deadline. Now, the delays are much shorter, meaning that the worker thread is never inactive long enough to shut down. Fixes Kotlin#856
Solves #856.