Open ghost opened 7 years ago
I think the proposed nomenclature is more consistent than that of the current implementation. May I ask why AtomicBox
and AtomicArc
correspond to Option<Box>
and Option<Arc>
, not Box
and Arc
?
Also, I wonder what will be the relation of crossbeam-utils
and atomic-option
crates. Do we want to subsume the functionality of atomic-option
?
Well, atomic Option<Box>
/Option<Arc>
are strictly more powerful than atomic Box
/Arc
, and also more commonly used, I believe (for non-atomic variants this may not be true, though). It's like that argument why we have Ptr<'scope, T>
and don't have non-nullable Ref<'scope, T>
. Moreover, our Atomic<T>
type is a nullable atomic pointer and we don't have a non-nullable variant.
We could have the whole palette of AtomicBox
/AtomicArc
/AtomicOptionBox
/AtomicOptionArc
, but my speculation is that the utility of having non-nullable variants will be low. Just my guess, though.
Yeah, I was thinking of subsuming the functionality of atomic
and atomic-option
crates.
Pinging @Amanieu and @reem to see what they think about this.
I wonder if we can efficiently implement AtomicBox
and AtomicArc
. If they don't fit in a single word, we should resort to blocking implementation for portability, right? Can we ensure that they are fit into a single word?
// Accepts only `T: Sized`, pointers to which always fit into a single word.
struct AtomicBox<T> { inner: AtomicPtr<T> }
// Also accepts `T: !Sized`, pointers to which are fat (two words).
// We would need advanced specialization for this (not yet implemented).
struct AtomicBox<T: ?Sized> { inner: ??? }
I don't think we can easily support fat pointers at all, at least not until full specialization gets into Rust.
AtomicArc
I don't have a strong opinion on supporting arbitrary sized stuff (?Sized and AtomicCell).
Another primitive we might want is something like Pinboard. This is basically just an epoch-protected Box
. The interface would be along the lines of:
impl<T: Send + 'static> EpochBox<T> {
fn new(t: T) -> Self;
fn remove(&self);
fn set(&self, t: T);
}
impl<T: Send + Sync + 'static> EpochBox<T> {
fn get<'scope>(&self, scope: &'scope Scope) -> Option<&'scope T>;
fn take<'scope>(&self, scope: &'scope Scope) -> Option<&'scope T>;
fn swap<'scope>(&self, t: T, scope: &'scope Scope) -> Option<&'scope T>;
}
impl<T: Send + Sync + Clone + 'static> EpochBox<T> {
fn get_clone(&self) -> Option<T>;
fn take_clone(&self) -> Option<T>;
fn swap_clone(&self, t: T) -> Option<T>;
}
~The second group of functions are too easy to misuse (as in holding the pin for too long). This was one of the reasons we preferred the closure api instead of handle.~ edit: nevermind, I just saw that you need to be in a scope to use them.
EpochArc also works well.
To my eyes, https://github.com/crossbeam-rs/crossbeam-utils/issues/2 suggests that we should not allow users to specify Ordering
for atomic types, at least for AtomicBox
and AtomicArc
.
For AtomicCell<T>
, the only implementation I can think of for big T
(of size > 2 words) is just locking: we cannot effectively guarantee atomicity for a big type. @stjepang Is it the implementation you thought of?
@jeehoonkang Yeah, I'm thinking of AtomicCell<T>
as similar to std::atomic
in C++, where it uses atomic instructions for small types and mutexes for bigger types.
You may want to have a look at this crate, which does exactly that.
The disadvantage of using a mutex is that the crate won't work with no_std
any more. This is why I used spinlocks in the atomic
crate.
What do folks think about supporting double-word atomics?
@spacejam It should be exposed as AtomicU128
/ AtomicI128
on platforms that support it.
@Amanieu in particular I'm interested in dw cas that runs on ARM 32 (6k+) for the equivalent of AtomicU64 so that sled can easily support that architecture.
AtomicU64
should already work on some ARM targets (v6 and above). These targets are v6 and above:
arm-unknown-linux-gnueabi
arm-unknown-linux-gnueabihf
armv7-unknown-linux-gnueabihf
@spacejam I'm also waiting for DWCAS to land in Rust. It will enable a whole new class of concurrent algorithms, e.g. LCRQ for starters. It was not possible even in C++, as DWCAS is not supported in its standard library!
@jeehoonkang there is a bunch of code I'm looking forward to refactoring once DWCAS is available! Super excited for it :)
@jeehoonkang DWCAS is supported in C++ through the generic std::atomic<T>
type. Just make T
a 2-word struct.
The standard library offers several primitive atomic types:
Atomic{Usize,Isize,Bool,Ptr}
. Nightly Rust also hasAtomic{U8,I8,U16,I16,U32,I32,U64,I64}
.Those are the primitive types, and then there are crates that expand a little bit. Crate
atomic
has typeAtomic<T>
whereT
can be any type (smallT
s that areCopy
use primitive atomics, and large or non-Copy
T
s simply get wrapped in a mutex).crossbeam
hasAtomicOption<T>
(atomicOption<Box<T>>
) andArcCell<T>
(atomicArc<T>
). There's also crateatomic-option
with a wider set of methods onAtomicOption<T>
.Taking a step back, I'd summarize and rename all those extended atomic types like this:
AtomicBox<T>
- atomicOption<Box<T>>
(equivalentAtomicOption<T>
)AtomicArc<T>
- atomicOption<Arc<T>>
(similar toArcCell<T>
)AtomicCell<T>
- atomicCell<T>
(equivalent toAtomic<T>
)What do you think about including such
AtomicBox<T>
,AtomicArc<T>
, andAtomicCell<T>
in Crossbeam?What I'm attempting to do here is to come up with a well-thought-out set of complementary atomic types that is a natural extension to the primitve atomic types in the standard library. Any thoughts or ideas? Any other nice-to-have atomic types I'm missing?