Closed kring closed 4 months ago
Alright Brian, you've successfully nerd sniped me here.
Your questions reminded me that the whole sleep_for
thing was a cop out. There shouldn't be any spin waiting; it should use a mutex and a condition variable to suspend the thread until there's work to do.
Conceptually this is easy-ish, but it's hard because some details we need to fiddle with are buried in Async++. Basically, to do this, we need access to the std::mutex that Async++ locks when new work is added to the main thread queue. But it's not available through the public interface of fifo_scheduler
. Ok, well, we already have a wrapper for fifo_scheduler
(called QueuedScheduler
). And fifo_scheduler
is actually pretty simple, so let's copy/paste it into QueuedScheduler
. Ok it's implemented with fifo_queue
, which is internal, but we can use it with just a slight hack (#include <async++/../../src/fifo_queue.h>
). Our QueuedScheduler
now becomes the new fifo_queue
rather than delegating to it. Now to build up a signaling mechanism based on a std::condition_variable
that gets notified when something new is added to the main thread task queue. Add a function to dispatch the main thread queue, waiting on the condition variable when there's nothing else to do. And make sure the condition variable is notified when the task we're waiting for completes (just in case the future is not resolved in the main thread).
Yep, that's much better! 🎉
Huh... where did the last 6 hours of my day go?
Well, at least I've addressed all your feedback. I also added support for waiting on SharedFuture
as well and merged in main. This is ready for another look.
Don't merge this yet. The Future resolving from a worker while main thread is waiting
test is consistently deadlocking on Windows (other platforms are either fine or are much less consistent about it). I need to figure out why.
I found and fixed the race condition causing the deadlock. This is ready for another review.
Looks great @kring ! You've addressed all my concerns.
Async code can definitely have nuanced bugs, so appreciate the new tests go to along with this feature.
Merging...
Adds a new method,
waitInMainThread
, toAsyncSystem
.Calling
asyncSystem.waitInMainThread(std::move(future))
is similar to callingfuture.wait()
. Both wait for the future to resolve or reject, and then either return the value or throw an exception.waitInMainThread
also continues to dispatch main thread tasks while waiting, however. This means that it is safe to call from the main thread even if the future hasthenInMainThread
continuations.future.wait()
, on the other hand, is very likely to deadlock in this situation.