Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
652 views
in Technique[技术] by (71.8m points)

rust - What are examples of types that implement only one of Send and Sync?

Just to get better understanding of the Send and Sync traits, are there examples of types that either:

  • Implement Send and do not implement Sync.
  • Implement Sync and do not implement Send.
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

First of all, it is important to realize that most structs (or enums) are Send:

  • any struct that does not contain any reference can be Send + 'static
  • any struct that contain references with a lower-bound lifetime of '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.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...