Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Expose a public unsafe API to spawn threads with unrestricted lifetime bounds #2546

Closed
oliver-giersch opened this issue Sep 20, 2018 · 4 comments

Comments

@oliver-giersch
Copy link

Currently, the only way to spawn a thread is with 'static lifetime bounds on the closure and its return type. This makes sense for regular threads with an indeterminate lifetime. However, the std::thread module could also expose an explicitly unsafe way for spawning threads, with function signatures like the following:

//in std::thread

impl Builder {
    pub unsafe fn spawn_unchecked<F, T>(self, f: F) -> io::Result<JoinHandle<T>>
    where
        F: FnOnce() -> T,
        F: Send + 'a,
        T: Send + 'a {...}
}

and (for the free-standing version)

//in std::thread

pub unsafe fn spawn_unchecked<F, T>(self, f: F) -> io::Result<JoinHandle<T>>
where
       F: FnOnce() -> T,
       F: Send + 'a,
       T: Send + 'a {...}

Since the implementation of of Builder::spawn relies on unsafe code itself and the std::sys::thread types/functions don't require 'static lifetime bounds (haven't checked for all), this should be comparatively easy to implement by refactoring the spawn implementation into spawn_unchecked and calling that function with 'static lifetime bounds for the safe API.

The benefits would mainly be:

  • users could use the unsafe API to spawn "scoped" threads without resorting to external crates if they so desire (adding the responsibility to ensure manual joining of course)
  • safe and general implementations for actual scope threads could be simplified and made (slightly) more efficient

Currently, scoped thread implementations have to use a lot of tricks with their return types and lifetime transmuting to get to work. Results have to be separately allocated and internally set instead of using the std::thread::JoinHandle type, which would be much more fitting if not for the lifetime bounds on the spawn functions.
To summarize, scoped threads could be implemented more safely and with fewer allocations.

@Diggsey
Copy link
Contributor

Diggsey commented Sep 20, 2018

I'm not sure I understand the motivation for this: why wouldn't callers just unsafely cast F to a type with a static bound and then call the existing spawn function?

@mark-i-m
Copy link
Member

I think something like crossbeam's scoped threads is a better solution.

@oliver-giersch
Copy link
Author

I'm not sure I understand the motivation for this: why wouldn't callers just unsafely cast F to a type with a static bound and then call the existing spawn function?

What you can do is put a FnOnce() -> T + 'a where T: 'static closure in a box, cast it to a trait object and transmute the lifetime bound on the closure away. To my knowledge it is not possible, however, to do anything about the lifetime bound for the return type T.

I think something like crossbeam's scoped threads is a better solution.

Scoped thread implementations like crossbeam's are exactly what could be made simpler (and possibly slightly safer and more efficient), if there was an API for spawning lifetime unrestricted threads.
Currently what you have to do is (starting with a FnOnce() -> T + 'a where T: 'a):

  • wrap it in another closure FnOnce() -> () + 'a, which writes the result of the original closure to some pointer that you pass into the closure (can be done safely with Arc<Mutex<Option<T>>
  • box the wrapping closure
  • cast to an FnBox trait object
  • transmute the 'a lifetime bound to 'static
  • use it to spawn the thread

Since you can't use the JoinHandle to retrieve the result (it returns () upon join()), you have to implement further logic to retrieve the result from the pointer that was written to by the thread.
All of this could basically condensed into a single step with only a single line of unsafe code, if there was a public API in std::thread to unsafely spawn lifetime unrestricted threads.

@Centril
Copy link
Contributor

Centril commented Oct 14, 2018

Closing in favor of rust-lang/rust#55043.

@Centril Centril closed this as completed Oct 14, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants