-
Notifications
You must be signed in to change notification settings - Fork 560
Dynamically bypass selector polling if no I/O events are present #4377
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
Merged
djspiewak
merged 15 commits into
typelevel:series/3.6.x
from
djspiewak:bug/conditionally-poll
Jul 7, 2025
+233
−56
Merged
Changes from all commits
Commits
Show all changes
15 commits
Select commit
Hold shift + click to select a range
db913fd
Initial attempt at enriching the interrupt state machine to avoid pol…
djspiewak 2eee23a
Simplified loops a bit
djspiewak b09a2b7
Fixed conditional and interrupt test
djspiewak 158dac4
Restored Scala 3 support
djspiewak d807be0
Added test for new mixed-mode polling handling
djspiewak 86ed31b
Update core/jvm/src/main/scala/cats/effect/unsafe/ParkedSignal.scala
djspiewak 0c2cfa1
Update core/jvm/src/main/scala/cats/effect/unsafe/ParkedSignal.scala
djspiewak 07400e0
Update core/jvm/src/main/scala/cats/effect/unsafe/WorkerThread.scala
djspiewak 3c2b5fe
Update core/jvm/src/main/scala/cats/effect/unsafe/WorkerThread.scala
djspiewak 9f15058
Use `createDefaultPollingSystem`
armanbilge 1c13c66
Create more chaos with the external queue
djspiewak f5ea181
Corrected blocked thread detection to handle interrupting state
djspiewak 5149437
Run `MutexSpec` sequentially on windows
djspiewak 9231cf0
SJS doesn't support getProperty
djspiewak c33f415
Merge branch 'series/3.6.x' into bug/conditionally-poll
djspiewak File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
28 changes: 28 additions & 0 deletions
28
core/jvm/src/main/scala/cats/effect/unsafe/ParkedSignal.scala
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,28 @@ | ||
| /* | ||
| * Copyright 2020-2025 Typelevel | ||
| * | ||
| * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| * you may not use this file except in compliance with the License. | ||
| * You may obtain a copy of the License at | ||
| * | ||
| * http://www.apache.org/licenses/LICENSE-2.0 | ||
| * | ||
| * Unless required by applicable law or agreed to in writing, software | ||
| * distributed under the License is distributed on an "AS IS" BASIS, | ||
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| * See the License for the specific language governing permissions and | ||
| * limitations under the License. | ||
| */ | ||
|
|
||
| package cats.effect.unsafe | ||
|
|
||
| private sealed abstract class ParkedSignal extends Product with Serializable | ||
|
|
||
| private object ParkedSignal { | ||
| case object Unparked extends ParkedSignal | ||
|
|
||
| case object ParkedPolling extends ParkedSignal | ||
| case object ParkedSimple extends ParkedSignal | ||
|
|
||
| case object Interrupting extends ParkedSignal | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
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 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 semantic here is different from the old implementation:
getAndSetinvolves a loop, but there is no longer a loop here.In the (admittedly unlikely) scenario that a worker thread transitions from
ParkedPollingtoParkedSimpleor vice versa while evaluating this condition, we would prematurely give up on attempting to wake up this thread. We might consider looping if thecompareAndSetfails.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 think that might not be worth it. So going from
ParkedPollingtoParkedSimplewould require that between theget()above and the CAS, the other thread would need to wake itself up (or be awakened by another notify), run through its whole state machine, pick up a fiber, execute that fiber through the whole machinery, run out of work, go through the whole "check all the things for work" (which, includes doing the thing that we're trying to notify it to do here!) and then finally go back and park again, all in the space of just these few lines.Aside from the fact that this race condition is almost inconceivable, it effectively just results in a loss of performance since we give up on the thread and go wake another one. The work item we're trying to notify on would actually get picked up (before the target thread resuspends), and we'll ultimately wake the same number of threads.
This compared to having an actual CAS loop which would require more complexity here and I think an extra conditional jump, despite the fact that it's effectively never going to be hit and wouldn't matter even if it did. So I think I'd rather just eat the CAS failure.
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.
Sure ... I am just worried about relying on the existence of another thread. For example, if there is exactly once worker thread in the runtime, could we end up in a deadlock?
I have thought through it a few times and I have not come up with a scenario yet that could deadlock, but I don't feel confident.
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.
If there's only one worker then that worker is either awake already (in which case we can't have entered this conditional) or it's parked and we're notifying it (in which case all the normal rules apply).
Uh oh!
There was an error while loading. Please reload this page.
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.
Right, so if all the normal rules are applying ...
What happens if we fail the CAS due to the race condition, we don't loop, and then we don't go to the next thread because there is no next thread? We've given up on our one-and-only thread.
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.
You words precisely capture my feelings about this: