-
Notifications
You must be signed in to change notification settings - Fork 14
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
A crate for atomic primitives: crossbeam-atomic #28
Conversation
I like this proposal very much! Thanks for writing it up. I have two comments:
|
@Amanieu Since you asked for more details on the proposed atomic primitives, I expanded the RFC. My thoughts:
What do you think? |
fn get_and(&self, val: i8) -> i8; | ||
fn get_or(&self, val: i8) -> i8; | ||
fn get_xor(&self, val: i8) -> i8; | ||
} |
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 don't think it's worth the trouble of adding these operations. You should just use AtomicI8
or Atomic<i8>
if you want atomic integer operations. These don't belong on a cell type.
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.
But AtomicI8
and Atomic<i8>
still require memory orderings on all operations. Those aren't particularly good solutions for casual users of atomics.
We need some kind of primitive that has SeqCst-by-default atomic integer operations. That is one of the pain points from the motivation sections.
Does this RFC now provide sufficient motivation for creating |
LGTM |
text/2018-02-20-crossbeam-atomic.md
Outdated
unsafe impl<T: Pointer> Sync for HazardCell<T> {} | ||
|
||
// A `Pointer` is just a smart pointer represented as one word. | ||
unsafe trait Pointer {} |
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.
This trait should have into_raw
and from_raw
methods.
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.
To be honest, I'm not a huge fan of creating more crates. Although in this case it does look like this group of primitives needs a separate crate due to the cyclic dependency on epoch
it would create, I think we should be wary of proliferating subcrates endlessly, as there already are quite a lot of them.
That said, I can't think of any better option right now, so let's do this. I hope that if a better way to organise things comes up, we can do a breaking release which moves things around, kind of like tokio.
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 thought a little bit more and it seems to me now that these types don't belong into a single crate. The reason is that they work on very different levels of abstraction.
The Atomic
type is very low-level and it would even make sense to use it in epoch
. But we can't, because..
The EpochCell
(and maybe other types proposed here, depending on their implementation) depend on epoch
, so we have a cycle.
I think the reason for this is that one type is very low-level, while the other has an entire garbage collector below it.
What I would propose as an alternative solution is to organise them differently. First, let's put everything low-level (such as Atomic
) that depends only on std
in crossbeam-utils
.
Then, let's put types that use a particular garbage collection mechanism into crates that provide it - for example EpochCell
into epoch
and HazardCell
into hazard
when we do eventually make that crate.
This way, there is no need to make new crates and all dependencies are neatly satisfied. This goes back to the discussion we had (I think on the skiplist thread) about whether crossbeam
subcrates should be organised by abstraction or by implementation. The current proposal groups by abstraction, but doing it by implementation as described above instead would avoid unnecessary problems. Then, we can simply reexport these types together under mod atomic
in the main crate, hence preserving a neat interface. Thoughts?
Another, small problem is that naming of Atomic
and Pointer
conflicts with names already present in epoch
. I would rename Pointer
here to something like Boxed
and epoch::Atomic
to something like epoch::Guarded
.
The motivation for creating Instead, the purpose of
It's interesting to draw parallels with tokio now that it is going through the reform. Here are a few quotes I like from the article you've linked:
In Crossbeam we're following a similar strategy. Library authors can pick individual crates they need, e.g. if you're writing a concurrent data structure you might want to depend only on
Our subcrates are of varying levels of stability, too. E.g.
This is an important benefit of having separate crates. Iterating experimental crates (like
Suppose both All that said, I'd be onboard with consolidating crates and rethinking the whole organization once the dust settles down. I hope we can release
Sure, but maybe that's not an issue if we don't use
This is a good observation. While I find it tempting to separate things into high-level and low-level categories, I actually prefer separating by what they do, or what they are used for. In that sense,
This makes a lot of sense, but... Now I kind of regret naming these primitives that way because
In any case, given These two primitives are a lot like
Yes, the main crate should have an
There was a recent long discussion on a similar problem here. The question was whether The same applies in our situation. If you need to disambiguate these structs, using |
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'm also for creating a new crate. I agree with @stjepang that the number of crates is not a big deal.
But the number of repositories may be. It incurs costs to manage multiple repo, e.g. synching multiple repos at a time, finding the repo, ... I'm thinking of proposing to merge all crates into crossbeam-rs/crossbeam
repository, but before that I'd like to ask you opinion here.
@jeehoonkang Yeah, I'm having doubts about our split into multiple repositories, too. One thing to consider here is that tokio, rayon, and futures group their subcrates into a single repo. Probably for a good reason. |
I think naming types by their implementations is fine - what it is, really, is a way for people to remember a set of properties under a single name. Imagine that instead of calling a type LinkedList we called it SlowSearchButQuickInsertAndSplitList instead. Even though a user shouldn't have to care about implementation details, it doesn't hurt to have a high-level idea of why a given type has the properties it does. In any case, the discussion of naming is tangential to this - division into crates and naming are mostly independent, and as you noted module prefixes are an effective way of disambiguating type names. My reasoning for why I think it would be better to implement these types within existing crates is not due to naming, but rather a hope that we can keep similar code together.
I agree that they are very similar in purpose, but not in implementation. In fact, in that aspect they differ substantially and use varying mechanisms. My hope is that we could group them in this way, e.g. by the underlying GC scheme, so that all code in such a crate can be reasoned about on the same basis. Moreover, this would avoid dependency problems like the one which arose here (although in this case it's not much of a worry, i think similar problems will arise with by-interface grouping). We could still easy have an interface which expresses the usage clearly by grouping the reexports into a single module, effectively like the Java I recall that in the case of structures, we decided to have one
As hopefully explained, I'm not opposed to new crates when they are clearly required, e.g. for new GC schemes, but rather to whether the |
Good point. I'd like to pull back on my suggestion on the eager vs lazy naming idea. :) I'm not yet sure what to call our types, to be honest. Maybe it becomes clearer once we implement them. In the end it probably depends on how closely the implementation details are tied to the data structure's properties as seen from the outside. Choosing a very specific name is a commitment:
Yes, that is true.
It seems this is actually the root of our disagreement. You're convinced that all these primitives are very different in implementation and that their implementations are tied to underlying GCs. My train of thought goes like this:
In the end, I can see why you're skeptical. I would be willing to hold off creating a new crate and prototype a set of these primitives so we can make a decision later. |
Another crate you might be interested in: https://github.com/Amanieu/seqlock |
From your description, |
@Vtec234 I'm growing more and more sympathetic to your concerns about the complicated dependency graph that is emerging. Putting this RFC on hold. |
@stjepang It's still a perfectly fine RFC, I think it'd be good to go if you just changed the proposal from "adding a new crate" to "adding these new types to existing crates", with the APIs here seen as provisional. The full APIs and implementations could then be fleshed out in PRs. What do you think? |
Since there wasn't sufficient motivation for creating a new repository (at least not at this moment), let's close this RFC. There's already a PR for adding I'm also implementing |
Create a new crate
crossbeam-atomic
, which will host a collection of atomic utilites that complement those instd::sync::atomic
.No code is proposed as a starting point for the crate - this time we'll simply start with an empty crate.
Rendered