Waker & RawWaker Send + Sync

Waker is just a wrapper around RawWaker. Why is the first one Send + Sync, but the second is not?

2 Likes

RawWaker’s role compared to Waker is a bit like raw pointers compared to references or box. Raw pointers are deliberately not Send/Sync, because if you write a struct containing raw pointers in a field, it’s quite likely that that struct is not thread-safe, and in order to have safer defaults, Rust is designed such that such a struct should explicitly opt in for being marked thread-safe.

For RawWaker, the analogy is not entirely up there, as you should probably never need to hold onto a RawWaker for long, it’s mostly just an intermediate value for constructing a Waker – on the other hand, this also means that marking it Send and/or Sync isn’t particularly useful either as you shouldn’t really have it e.g. in a field of a struct anyways; at least that would be my guess – if you know of any use-cases that get more complicated due to a lack of a Send/Sync implementation here, feel free to elaborate.

You could think of RawWaker as a fancy version of the tuple type (*const (), &'static RawWakerVTable), with the addition of more backwards compatibility in case new fields would ever need to be added.

Waker is more than “just a wrapper”, because besides wrapping the data, it encapsulates it with a safety contract that you can uphold by manually checking it when constructing one via the Waker::from_raw method, or by using the safe From<Arc<impl Wake + Send + Sync + 'static>> conversion and the Wake trait, which creates a safe RawWaker for you, using Arc to manage the data (and implement cloning and dropping), and the Wake implementation for the business logic (wake and wake_by_ref) in a guaranteed type-safe manner.

4 Likes

Thanks for your detailed post!

You could think of RawWaker as a fancy version of the tuple type (*const (), &'static RawWakerVTable), with the addition of more backwards compatibility in case new fields would ever need to be added.

Maybe that should be stated in the docs if that's the case?

I can't think of a thing that could break when RawWaker was Send + Sync. Ofcourse, it contains a raw pointer, but it's encapsulated well and the second field - vtable &'static RawWakerVTable is Send + Sync. What if for some weird reason I wanted to create a RawWaker instance on background thread and send it to my main (and then use it to create Waker, etc)?

One influence might have been that, in some iterations of the design of Futures (and perhaps in the future) there was a LocalWaker — a version of Waker that did not implement Send or Sync, to be used by executors wishing to provide a waker that is not thread-safe. If this feature currently existed, then you'd be constructing a LocalWaker from a RawWaker where neither is Send + Sync.

2 Likes

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.