m-ou-se / rust-atomics-and-locks

Code examples, data structures, and links from my book, Rust Atomics and Locks.
Other
1.33k stars 120 forks source link

[Chapter 5] Saving one byte of memory when controlling channel state - Missing state reset? #51

Closed brunojppb closed 7 months ago

brunojppb commented 7 months ago

The content that the question is about

Chapter 5 - Using a Single Atomic for the Channel State

The question

Hi Mara! 👋

I bought your physical book a couple weeks ago and I'm learning a ton! Really appreciate the contents there! ❤️

While reading Chapter 5 - Using a Single Atomic for the Channel State, I came across this great example where we can save one byte by using an atomic state variable to control when the channel is ready to send/receive messages:

But while trying this example, I was left wondering whether the example is missing a store call to the state control variable so messages to the channel can be sent again?

CleanShot 2024-03-10 at 23 37 28@2x

Here is what I came up with, but I'm not quite sure whether this is right:

pub fn receive(&self) -> T {
        if self.state.compare_exchange(
            READY, READING, Ordering::Acquire, Ordering::Relaxed
        ).is_err() {
            panic!("No message available!");
        }

-       unsafe { (*self.message.get()).assume_init_read() }
+       unsafe {
+           let v = (*self.message.get()).assume_init_read();
+           self.state.store(EMPTY, Ordering::Release);
+           v
+       }
    }

If you have the time, could you have a look at this and point me to the right direction?

Thanks again for this great book and also for making it available online for free! ❤️

m-ou-se commented 7 months ago

Those channels are all "one shot channels", meaning that they can only be used once (which is exactly what you need in many situations). If you want a channel that can be re-used, then you'd indeed need to reset the state to EMPTY at that point. :)

Note that if you do that, you'd also need to change the first Relaxed to Acquire in fn send. (And at that point you'll notice that both fn send and fn receive will start with an acquire-compare-exchange and end with a release-store, just like a mutex lock+unlock pair. At that point you have basically implemented a mutex with one additional bit of information: whether something is stored in it or not. Basically a Mutex<Option<T>>, except you get to check the bit of the option without having to lock the mutex, to allow for 'lock if empty' and 'lock if nonempty' to be done as one atomic instruction.)

brunojppb commented 7 months ago

ho! that makes a ton of sense. I misinterpreted the meaning of "one shot channels". I thought it could be reused, but one message at a time.

Appreciate the feedback, Mara! 🙌