Just to get better understanding of the Send
and Sync
traits, are there examples of types that either:
- Implement
Send
and do not implementSync
. - Implement
Sync
and do not implementSend
.
Just to get better understanding of the Send
and Sync
traits, are there examples of types that either:
Send
and do not implement Sync
.Sync
and do not implement Send
.
First of all, it is important to realize that most structs (or enums) are
Send
:Send + 'static
'a
can beSend + 'a
As a result, you would generally expect any
Sync
struct
to beSend
too, becauseSend
is such an easy bar to reach (compared to the much harder bar of beingSync
which requires safe concurrent modification from multiple threads).However, nothing prevents the creator of a type to specifically mark it as not
Send
. For example, let's resuscitate conditions!The idea of conditions, in Lisp, is that you setup a handler for a given condition (say:
FileNotFound
) and then when deep in the stack this condition is met then your handler is called.How would you implement this in Rust?
Well, to preserve threads independence, you would use thread-local storage for the condition handlers (see
std::thread_local!
). Each condition would be a stack of condition handlers, with either only the top one invoked or an iterative process starting from the top one but reaching down until one succeeds.But then, how would you set them?
Personally, I'd use RAII! I would bind the condition handler in the thread-local stack and register it in the frame (for example, using an intrusive doubly-linked list as the stack).
This way, when I am done, the condition handler automatically un-registers itself.
Of course, the system has to account for users doing unexpected things (like storing the condition handlers in the heap and not dropping them in the order they were created), and this is why we use a doubly-linked list, so that the handler can un-register itself from the middle of the stack if necessary.
So we have a:
and the "real" handler is passed by the user as
T
.Would this handler be
Sync
?Possibly, depends how you create it but there is no reason you could not create a handler so that a reference to it could not be shared between multiple threads.
Note: those threads could not access its
prev
/next
data members, which are private, and need not beSync
.Would this handler be
Send
?Unless specific care is taken, no.
The
prev
andnext
fields are not protected against concurrent accesses, and even worse if the handler were to be dropped while another thread had obtained a reference to it (for example, another handler trying to un-register itself) then this now dangling reference would cause Undefined Behavior.Note: the latter issue means that just switching
Option<*mut Handler<T>>
forAtomicPtr<ConditionHandler<T>>
is not sufficient; see Common Pitfalls in Writing Lock-Free Algorithms for more details.And there you have it: a
ConditionHandler<T>
isSync
ifT
isSync
but will never beSend
(as is).For completeness, many types implement
Send
but notSync
(mostSend
types, actually):Option
orVec
for example.Cell
andRefCell
implementSend
but notSync
because they can be safely sent between threads but not shared between them.