-
-
Notifications
You must be signed in to change notification settings - Fork 2.4k
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 Carousel.FilterAsync
not working when called from a non-update thread
#31798
Conversation
…thread I was trying to be smart about things and make use of our `SynchronisationContext` setup, but it turns out to not work in all cases due to the context being missing depending on where you are calling the method from. For now let's prefer the "works everywhere" method of scheduling the final work back to update.
The lock-taking on potentially-the-update-thread earlier in this method still has me slightly skeeved out: osu/osu.Game/Screens/SelectV2/Carousel.cs Lines 236 to 240 in 8c5b19d
but I guess this change in isolation makes sense... |
"slightly skeeved out" is a tame way to put it. I'd put it as "completely wrong" and needs to change as a priority.
|
Valid point which I missed, although I meant more the act of potentially taking a lock on the update thread at all than the mechanics on which object the lock is taken specifically. Like locking on |
It's locking over instant operations, but I'm fine with an alternative approach, just can't find another case we're doing this in a better way.
Is this regarding the lock object? If so I've fixed that. If it's something else you'll need to elaborate or provide a diff. |
As mentioned, it was about |
Hmmmm... This lockless alternative leveraging cpu atomics works in my head: diff --git a/osu.Game/Screens/SelectV2/Carousel.cs b/osu.Game/Screens/SelectV2/Carousel.cs
index 648c2d090a..7c1392ce6e 100644
--- a/osu.Game/Screens/SelectV2/Carousel.cs
+++ b/osu.Game/Screens/SelectV2/Carousel.cs
@@ -227,16 +227,13 @@ private async Task performFilter()
Stopwatch stopwatch = Stopwatch.StartNew();
var cts = new CancellationTokenSource();
- lock (this)
- {
- cancellationSource.Cancel();
- cancellationSource = cts;
- }
+ var previousCancellationSource = Interlocked.Exchange(ref cancellationSource, cts);
+ await previousCancellationSource.CancelAsync().ConfigureAwait(false);
if (DebounceDelay > 0)
{
log($"Filter operation queued, waiting for {DebounceDelay} ms debounce");
- await Task.Delay(DebounceDelay, cts.Token).ConfigureAwait(true);
+ await Task.Delay(DebounceDelay, cts.Token).ConfigureAwait(false);
}
// Copy must be performed on update thread for now (see ConfigureAwait above).
Not sure whether better or worse? (That last change to |
I think either is fine, so go ahead and apply your change.
I missed this one. Should be |
Spotted by @frenzibyte during implementation in the bigger picture.
I was trying to be smart about things and make use of our
SynchronisationContext
setup, but it turns out to not work in all cases due to the context being missing depending on where you are calling the method from.For now let's prefer the "works everywhere" method of scheduling the final work back to update.