#Any libraries/wrappers that implement Optional atomics?
76 messages · Page 1 of 1 (latest)
what does being Copy or not have to do with it
if your implementation needs it to be Copy then it would be incorrect
If it helps you could use any other value as a niche value, e.g. u64::MAX / 3
dont want to use any other values
Many CPUs support atomic operations on at most 64 bits. The operation you're asking for requires an atomic operation on slightly more than 64 bits of data. So unless you plan to run your program only on CPUs that support 128-bit atomics, you will have to make a compromise of some kind.
If blocking is completely unacceptable for some reason, then one possibility is https://docs.rs/atomicbox/latest/atomicbox/struct.AtomicOptionBox.html, which requires an allocation every time you modify the thing.
Actually no, that doesn't work because it consumes the value into None each time you read it...
Could you explain what your use case is?
the idea here is to denote an unitialized state, that means the reader would see that the atomic hasnt been written to yet and can write an appropriate value as per the case of the uninitialized state
does that make sense?
Are 0 and u64::MAX appropriate values?
No, not really.
sorry! what did you not catch?
Once it's initialized, do you need to modify it later?
yes
Is it fine if two threads both initialize the value and overwrite each other?
no not really, 0 is a value which would have a meaning, unlike None which clearly denotes uninitialized
Like, what's your usage pattern?
basically building a wrapper around a sdk function that returns a stream that returns a number when polled, and i am tracking the number as a static atomic
Is it fine if there's blocking while the value is being initialized?
tbh the whole context is hard to explain
yeah
Relaxed ordering is ok
Is the following scenario fine?
- Thread A makes a request to the API. Gets a response.
- Thread B makes a request to the API. Gets a newer response.
- Thread B writes the newer response.
- Some reader thread observes the value.
- Thread A overwrites the older value with a newer value.
either reader should be able to see that it is the first writer
and from read -> write should be atomic
Let's suppose that the value was already initialized with some very old value already before all this happened.
hmm
Actually... is there only one writer thread in your use case?
the thread that reads also writes
and can be many
rw should be atomic like compare and swap
So... how does each thread know that the value is stale and therefore an API call is needed?
it doesnt need to know about staleness
actually
remove the comparison part
its just swap
continuously swapping as the stream gets polled
depends on the client i think
in short, i just want either thread to know that it is the first writer
Hmmm
How do you want to handle this scenario?
Or should two threads never poll the API "at the same time"?
static CACHE: AtomicU64 = AtomicU64::new(0);
static INITIALIZED: AtomicBool = AtomicBool::new(false);
if INITIALIZED.load(Ordering::Relaxed) {
println!("Using cached value");
return CACHE.load(Ordering::Relaxed);
}
println!("Computing value for the first time");
let result = expensive_computation();
CACHE.store(result, Ordering::Relaxed);
INITIALIZED.store(true, Ordering::Relaxed);
result
something like this
except i dont want this extra atomic static INITIALIZED: AtomicBool = AtomicBool::new(false);
...Because?
So... should expensive_computation() ever be called more than once?
Your use case sounds similar to OnceLock except for the part where you need to later update the value...
This code has the issue where two threads could check INITIALIZED at the same time, then both do expensive_computation, then overwrite each other in CACHE
If one thread is currently retrieving the value to initialize with, should other threads block on waiting for the initialization to finish?
And what should happen if the value is already initialized, but two threads want to poll for an updated value at the same time?
Isn't that just a RwLock then?
Kinda? But I think there should be a way to do this without blocking as much as RwLock
technically its ok to override, but lets just say it was not, what would you do?
Maybe a OnceLock<AtomicU64>, which is kinda weird but I think does what you want.
This type wouldn't solve the "two threads trying to poll for an update at the same time" issue though
whats the performance difference between atomics and oncelock?
After a OnceLock is initialized, it's basically the "two atomics" solution wrapped into a nicer package.
But before it's initialized, OnceLock will internally have a mutex that needs to be locked for initialization.
i see
Idea: Two atomics plus a mutex.
static WRITE_LOCK: Mutex<()> = Mutex::new(());
static CACHE: AtomicU64 = AtomicU64::new(0);
static INITIALIZED: AtomicBool = AtomicBool::new(false);
fn initialize() {
if !INITIALIZED.load(Acquire) {
let _lock = WRITE_LOCK.lock().unwrap();
if !INITIALIZED.load(Acquire) {
let val = expensive_computation();
CACHE.store(val, Release);
INITIALIZED.store(true, Release);
}
}
}
fn update() {
assert!(INITIALIZED.load(Acquire));
let _lock = WRITE_LOCK.lock().unwrap();
let val = expensive_computation();
CACHE.store(val, Release);
}
fn get() -> Option<u64> {
if INITIALIZED.load(Acquire) {
Some(CACHE.load(Acquire));
} else {
None
}
}