-
-
Notifications
You must be signed in to change notification settings - Fork 2.5k
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
sync: provide API for notifying multiple waiters. #3066
Comments
Awesome! Maybe eventually extend the text in the documentation for tokio::sync as well explaining how to port existing code using CondVars. |
I could go either on this. I think you're right that I can't immediately think of use-cases that require both. On the other hand, if we have two separate public API types for "notify one" and "notify all" behaviors, but they share the underlying internal implementation, and someone does have a use-case for both in the same place, this seems a little inefficient. If a user wants to wake tasks with either a "notify one" or a "notify all" operation, they have to express interest in two separate types, maintaining two separate wait lists... One potential use-case I can think of for both types of notification in one place is something that notifies a single waiter at a time during normal operation but notifies all waiters when it's dropped/shut down/canceled. |
Is that something I can work on? Starting with the counter approach for |
@zaharidichev sure. I believe that based on @hawkw's comments, we should include Assuming the counter strategy works, we should increase the Existing internal usage of Does the counter strategy make sense? |
@carllerche Yes all that makes sense. |
@carllerche Not sure what you mean by "notify_*". I do not think tracking the number of calls to |
@zaharidichev very possible. The |
This PR changes the way `notify_waiters`, is used. Previously in order for the consumer to register interest, in a notification triggered by `notify_waiters`, the `Notified` future had to be polled. This introduced friction when using the api as the future had to be pinned before polled. This change introduces a counter that tracks how many times `notified_waiters` has been called. Upon creation of the future the number of times is loaded. When first polled the future compares this number with the count state of the `Notify` type. This avoids the need for registering the waiter upfront. Fixes: tokio-rs#3066 Signed-off-by: Zahari Dichev <[email protected]>
Based on the discussion, all improvements are forwards compatible. I will remove the 1.0 tag. |
This PR makes `Notify::notify_waiters` public. The method already exists, but it changes the way `notify_waiters`, is used. Previously in order for the consumer to register interest, in a notification triggered by `notify_waiters`, the `Notified` future had to be polled. This introduced friction when using the api as the future had to be pinned before polled. This change introduces a counter that tracks how many times `notified_waiters` has been called. Upon creation of the future the number of times is loaded. When first polled the future compares this number with the count state of the `Notify` type. This avoids the need for registering the waiter upfront. Fixes: #3066
There currently is no async equivalent to the synchronous
CondVar::notify_all
. Roughly,Notify
provides similar functionality, but only has anotify_one()
.Notify
currently has anotify_all()
method, but it is crate private. This API has not been made public due to API questions to work through.First,
Notify
currently uses an approach similar topark
/unpark
. Whennotify_one()
is called and there are no waiters, it stores a pending notification. The next time a task waits onNotify
, the task consumes the pending notification and the wait completes. This behavior may result in spurious wakeups but is designed to be a lightweight mechamism to avoid race condition without requiring locking in many cases.However, the
park
/unpark
behavior does not translate to a "notify many" operation. Implementing a useful "notify many" operation is a bit tricky. Considerstd::sync::CondVar
. In order to wait on aCondVar
, the caller must first acquire a mutex. The mutex, paired withCondVar
is how races are handled. The downside of using aMutex
is that it is fairly coarse. Usually, with regards to mutual exclusion, in "broadcast" style synchronization, the caller wishes to ensure mutual exclusion between producers and consumers and not with other consumers. Additionally, using an "async" mutex for this doesn't make a ton of sense either as the critical section to guard is usually small and synchronous. It usually makes more sense to use astd
Mutex to ensure mutual exclusion, but we need an async-aware notification strategy. Seesync::Watch
as an example of this.The private
notify_waiters()
API handles the race by requiring the consumer to acquire the notification future before checking the state. If the state is as expected and the consumer drops the notification future, otherwise, the consumer waits on the notification future. Unfortunatley, it isn't that simple. In order to register for the notification, the consumer must also poll the notification future. This requires pinning and results in a poor API. I believe that this problem can be solved by storing a counter internally tracking the number of calls tonotify_*
. When the notification future is created, it loads the current count. When the future is awaited, if the current count is greater than the one loaded on creation, the future completes immediately.Additionally,
notify_waiters()
andnotify_one()
are fairly different behaviors. Conflating them in a single notification primitive may not make sense. I am not aware of any use cases that require mixingnotify_one()
andnotify_waiters()
. It may make more sense to provide a separate type for doing broadcast style notifications.Actions
notify_waiters()
API.notify_waiters()
should be included onNotify
, or a separate type should be created.Refs: #2739, #3061
The text was updated successfully, but these errors were encountered: