Skip to content

Join handles for spawned tasks #1252

@zopsicle

Description

@zopsicle

To implement custom fork/join patterns that join and scope do not allow it would be helpful if spawn returned a join handle. Joining the join handle on a Rayon thread would continue running tasks until the task in question finishes. AFAICT implementing join handles outside of Rayon is impossible without busy looping or sleeping given the current API.

Example suboptimal implementation of join handles:

struct JoinHandle(mpsc::Receiver<()>);

fn spawn_with_join_handle<F>(func: F) -> JoinHandle
    where F: 'static + FnOnce() + Send
{
    let (sender, receiver) = mpsc::channel();
    rayon::spawn(|| { f(); sender.send(); });
    JoinHandle(receiver)
}

impl JoinHandle
{
    fn join(self)
    {
        loop {
            let result = self.0.try_recv();
            if result.is_ok() {
                break
            } else {
                let result = rayon::yield_now();
                if result.is_none() {
                    self.0.recv();
                    break
                }
            }
        }
    }
}

Example use case of implementing the Box2D task API (which is essentially fork–join):

unsafe extern "C" fn b2EnqueueTaskCallback(
    task: b2TaskCallback,
    itemCount: c_int,
    minRange: c_int,
    taskContext: *mut c_void,
    userContext: *mut c_void,
) -> *mut c_void
{
    let join_handle = rayon::spawn_broadcast(|...| task(..., taskContext));
    Box::into_raw(Box::new(join_handle)).cast()
}

unsafe extern "C" fn b2FinishTaskCallback(userTask: *mut c_void, userContext: *mut c_void)
{
    Box::from_raw::<rayon::JoinHandle>(userTask).join();
}

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions