-
Notifications
You must be signed in to change notification settings - Fork 341
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
Add utility type WakerSet to the sync module #390
Conversation
@nbdd0121 It would be interesting to see how to consolidate efforts of this PR and PR #370. They're not necessarily in conflict - in fact, they're probably complementary. This PR factors out the |
The Registry type is pretty much similar to my WakerListLock type, but with better API design. Most improvements in #370 still applies, such as using linked list, and various techniques used to shorten hot path and de-bloat the generated code. I can surely rebase my PR on top on this. Some of my concerns:
I am quite surprised about the performance improvement by this PR. I think mostly it's due changing the type to AtomicBool. I can dig further on the underlying reason. |
The reason is that empty flag doesn't tell us much. If the list of registered items is not empty, a In other words, a
Unfortunately, I don't know how to do this more elegantly without
It's not used anywhere anymore, but I have no reservations about keeping it if it is useful. |
But considering that the item will only be turned into The main reason that I think empty flag is better is that when Registry is switched to use linked list rather than slab, emptiness checking is essentially free and requires no special book-keeping to manage. |
You're right. I am being naive originally thinking some sort of Acq/Rel would be sufficient. I ended up spending a few hours reading through relevant standards and research papers, and realized that this cannot be done without protecting the read in some sort of lock. The fundamental reason is that there are no easy way to turn thread-local sequenced-before relation to inter-thread happens before other than using sequential consistency. I think the current Mutex's approach of having 1 bit indicating if anything has been blocked is much easier to reason about, as everything is protected by locks. |
I don't think it's a Map. It's more like a set (or a list, if backed by a linked list rather than a slab). I am not convinced by replacing cancel with remove and notify_one. If the waker is never woken up and cancelled, we don't need to wake up anyone. In general to prefer the old names to the current one. Or maybe we can call the methods park/unpark. |
That's true, but I did find some performance benefits under heavy contention where we avoid locking the entry list when there's nothing really to notify. Although, I think checking if the first entry is turned into |
You're right, that was an oversight. Okay, so we need the following methods:
Do these method names sound okay? I renamed the type to |
For the method name I tend to stick with |
@nbdd0121 API-wise the difference between a "map" and a "list" is that with a list you provide the key, but with a map the key is provided for you: // Map -- key is generated for you.
fn insert(&mut self, val) -> key;
// List -- you provide the key.
// Operations like `push` are just shorthands on top of this.
fn insert(&mut self, index, val); In this case the key is generated, which means it's more similar to a |
Not really. A map can never generate a key, because the key is always supplied by the user. // Map: User must supply the key
fn insert(&mut self, k: K, v: V) You cannot insert anything to a map without providing a key. On the other hand, set (or vector and list) can return an iterator upon insertion: // Set: An iterator is provided to the user.
std::pair<iterator,bool> insert( value_type&& value ); The fact that we use a |
What if we just call it |
Can we do another round of review and merge if this looks good? @nbdd0121 I still want us to have your optimizations on top of this PR :) I'm also particularly interested in having a doubly linked list because it allows us to have stronger fairness guarantees than what In fact, it'd be great if we just removed the What do you think? Sorry for overriding your already submitted PR, especially given how much effort you've put into it :( |
flag |= NOTIFY_ALL; | ||
} | ||
|
||
// Use `SeqCst` ordering to synchronize with `WakerSet::lock_to_notify()`. |
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 probably mean WakerSet::notify_{one, all}
here
This leads to a broader issue. Currently it's very difficult to use |
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.
API-wise this looks good to me. But my knowledge of Atomics is limited, so I can't comment on any of that. It seems @nbdd0121's feedback should probably be addressed before this can be merged.
Still approving this since I don't have any feedback left at this point.
|
||
/// Set when the entry list is locked. | ||
#[allow(clippy::identity_op)] | ||
const LOCKED: usize = 1 << 0; |
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.
Alternatively the clippy lint could be removed by writing
const LOCKED: usize = 0b001;
const NOTIFY_ONE: usize = 0b010;
const NOTIFY_ALL: usize = 0b100;
I switched to @nbdd0121 I agree the previous approach with Here are latest benchmarks. PR #370 :
This PR:
The difference in the highly contended cases is noticeable, at the expense of a smaller speed bump in uncontended cases, even with so many |
This type is shared among
Mutex
andchannel
for now, but would also be useful for implementingRwLock
andBarrier
- it'd make the code cleaner and more efficient at the same time!We can integrate
Registry
intoMutex
andchannel
in a follow-up PR. It might even be useful for implementingCondvar
.Comparing benchmarks from PR #370...
master
branch:PR #370:
This PR: