Send means that a type is safe to move from one thread to another. If the same type also implements Copy , this also means that it is safe to copy from one thread to another. Sync means that a type is safe to reference from multiple threads at the same time.
Rust captures this through the Send and Sync traits. A type is Send if it is safe to send it to another thread. A type is Sync if it is safe to share between threads (T is Sync if and only if &T is Send).
A trait in Rust is a group of methods that are defined for a particular type. Traits are an abstract definition of shared behavior amongst different types. So, in a way, traits are to Rust what interfaces are to Java or abstract classes are to C++. A trait method is able to access other methods within that trait.
If you need mutability, put a Cell or RefCell inside the Rc ; see an example of mutability inside an Rc . Rc uses non-atomic reference counting. This means that overhead is very low, but an Rc cannot be sent between threads, and consequently Rc does not implement Send .
First of all, it is important to realize that most structs (or enums) are Send
:
Send + 'static
'a
can be Send + 'a
As a result, you would generally expect any Sync
struct
to be Send
too, because Send
is such an easy bar to reach (compared to the much harder bar of being Sync
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:
struct ConditionHandler<T> {
handler: T,
prev: Option<*mut ConditionHandler<T>>,
next: Option<*mut ConditionHandler<T>>,
}
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 be Sync
.
Would this handler be Send
?
Unless specific care is taken, no.
The prev
and next
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>>
for AtomicPtr<ConditionHandler<T>>
is not sufficient; see Common Pitfalls in Writing Lock-Free Algorithms for more details.
And there you have it: a ConditionHandler<T>
is Sync
if T
is Sync
but will never be Send
(as is).
For completeness, many types implement Send
but not Sync
(most Send
types, actually): Option
or Vec
for example.
Cell
and RefCell
implement Send
but not Sync
because they can be safely sent between threads but not shared between them.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With