Skip to content

[guide-task] Send and Sync are not properly described #18936

Closed
@Kimundi

Description

@Kimundi

At the introduction of spawn:

The spawn function has a very simple type signature: fn spawn(f: proc(): Send). Because it accepts only procs, and procs contain only owned data, spawn can safely move the entire proc and all its associated state into an entirely different task for execution. Like any closure, the function passed to spawn may capture an environment that it carries across tasks.

This doesn't explain what Send means, and instead talks about a simplified concept of owning the state. Something like this would be more correct (with the old proc closures):

The spawn function has a very simple type signature: fn spawn(f: proc():Send). A proc is a closure that captures its environment by value, and the Send bound restricts it to only allow the capture of sendable types, that is data that is safe to transfer across threads. Because all captured state is sendable, the closure is as well, and spawn can safely move the entire proc and all its associated state into an entirely different task for execution.

Then later in the Arc section, Sync is not mentioned at all. A possible introduction could look like this:

To tackle this issue, one can use an Atomically Reference Counted wrapper (Arc) as implemented in the sync library of Rust. With an Arc, the data will no longer be copied for each task. Instead, a single copy of the data exists that can be accessed through an Arc handle (Arc). This handle is send- and clonable, which means you can make as many of them as necessary and then send them to different tasks.

However, for a given type T you are only allowed to construct an Arc<T> if T fulfills the Sync bound. Syncis a build-in trait the expresses that a type is thread safe to access through an shared reference, that is a T should only implement Sync if having two &T in different threads that point to the same memory is safe. For the vast majority of types that are Sync, thread safety is given by simply not allowing any mutation through an shared reference, eg u32 is Share because you can't mutate it through an &u32.

(Remaining examples of read-only Arc usage)

Rust also enable safe mutation of data that is shared between threads, by providing library types that implement Sync and do some kind of runtime checking to safely pass out mutable reference one thread at a time. One example of this is the Mutex<T> type, which can be used to make any type T thread safe to access by using locks. For example you could construct an Arc<Mutex<u32>> to have an integer that can be shared and mutated across threads.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions