A while ago I wrote Diplomatic Bag as a way to deal with !Send
types while inside a multithreaded Tokio runtime, where it's really helpful if everything is Send
. It does this by spawning a globally shared worker thread and all operations on the types are performed on that thread, the user is just given a handle. While writing that I hit an existential question of what I was actually allowed to do with !Send
types, and equivalently Sync
types (I'm happy with T: Sync <=> &T: Send
).
From the 'nomicon:
A type is Send if it is safe to send it to another thread.
Following the letter of that statement, the worker thread can have a map of handles to values and allow those handles to send closures over channels to act on those objects. However, all the values must have the 'static
lifetime as their lifetime can't be lexically related to the handles.
Following more of the spirit of that statement, the handle could be the bytes of the type hidden behind ManuallyDrop
. Now lifetimes work as expected, but the value may be living in another thread's stack frame. The worker thread will see references to it, that will look no different than if it was on the heap. You also have to occasionally pass the bytes over a channel to and from the worker thread but that will just look like the value has moved (Pin
could be interesting but I don't think it matters).
Breaking things further, as long as the operation on a different thread doesn't know anything about the type then nothing it could do could break the !Send
-ness of the type? This is most easily demonstrated in this bit of code which turns a Handle<Result<T, E>>
into a Result<Handle<T>, Handle<E>>
, all on whatever thread the Handle
happens to be on. I think this is safe because Result
doesn't introduce any !Send
-ness but this is stretching things.
At the extreme, a !Send
type could have some methods on it that are safe to call from another thread because they don't touch whatever part of the value makes it !Send
. That makes me wonder if send-ability of values is even the right way to model this.
So, what is unsafe
code allowed to do with !Send
types?
Some previous discussion happened on Reddit here.