This is my coding along with this wonderful book. In general, I'll try to implement a chapter, update that implementation with the book's implementation, and record my learnings in the README.
Unfortunately, I read this chapter before attempting an implementation. Luckily,
I was still given the opportunity to learn a lot about Send
/Sync
! Most of
the details are summarized in this issue but I'll summarize
here.
I originally thought that Send
was for transferring ownership of a value
to another thread. So I thought this was specifically relating to sending
a T
. However:
- I didn't notice the fact that, if you send
&mut T
, the receiving thread can usestd::mem::swap
(or similar) to get an ownedT
. SoSend
is definitely required for sending&mut T
. - Since
Send
was forT
and I hadn't really thought about&mut T
, I assumed that you neededSync
to send non-T
. That's untrue -Sync
is only required if you need concurrent access to&T
from multiple threads. The reason why&T: Send
impliesSync
is because&T
is copy - if you're allowed to send&T
to one thread then you're allowed to make copies and send to many threads. However, if you're in a situation where you have exclusive access (e.g.Mutex
, spin lock), then you don't needSync
. - You need
Send
even if you only access&T
. If, for example, we remove theDerefMut
implementation on ourSpinLockGuard
, we'd still need to requireT: Send
toimpl Sync for SpinLock
. When we talk aboutSend
and "sending values to another thread" that doesn't mean passing ownership - that means doing anything withT
,&T
, or&mut T
on a thread that the originalT
wasn't initialized on. Some more details in this wonderful StackOverflow answer.
As with Chapter 4, I'd unfortunately already skimmed this chapter before attempting an implementation. Luckily, I did such a bad job at reading that I got plenty of stuff wrong which leaves room for learning!
- Preventing sending the
Receiver
is a much easier way to avoid deadlocks (though I'll need to investigatestd
/crossbeam
to see how this is handled in the wild). - By dropping unsent messages in the
Drop
implementation ofInner
we dodge some racy logic involving an extrawaiting
variable and avoid some kludgyArc::strong_count
ing. - Implementing
Sync
onInner
and then letting autoimplementations bubble up to the public types is better. This is also useful in case you makeInner
public later (as we would if we were avoiding the inner allocation). AtomicBool::get_mut()
is a neat API for when you have&mut self
.