#reference #thread #safely #send #scope #channel

refcapsule

Safely send references to other threads

1 unstable release

0.1.0-beta1 Nov 7, 2022

#637 in Concurrency

Apache-2.0

16KB
194 lines

refcapsule

Safely send references to other threads in rust

A way to create a scope in which you can send references across a channel

Example:

use std::thread;
use std::time::Duration;
use std::sync::mpsc::channel;
use refcapsule::{Capsule, with_encapsulated};

let (sender, receiver) = channel::<Capsule<u32>>();

// receiver of references

thread::spawn(move || {
    {
        let r = receiver.recv().unwrap();
        thread::sleep(Duration::from_millis(100));
        assert_eq!(*r, 4);
    }
    {
        let r = receiver.recv().unwrap();
        thread::sleep(Duration::from_millis(100));
        assert_eq!(*r, 12);
    }
});

let x: u32 = 4;
let s1 = sender.clone();
with_encapsulated(&x, move |x| s1.send(x).unwrap());

with_encapsulated(&12, move |cap| sender.send(cap).unwrap());

lib.rs:

This module is somewhat similar to scoped threads, but allows passing references to other threads over channels, or similar mechanisms.

It captures zero or more references inside of "capsules" with a specific lifetime, then runs a function in a context where control won't be returned to the caller until all capsules for that lifetime have been dropped.

Since it blocks waiting for the "capsules" to be dropped, the capsules can be safely passed to other threads (for example with a channel), wher they can be derferenced as references again.

Examples

use std::thread;
use std::time::Duration;
use std::sync::mpsc::channel;
use refcapsule::{Capsule, with_encapsulated};

let (sender, receiver) = channel::<Capsule<u32>>();

// receiver of references

thread::spawn(move || {
    {
        let r = receiver.recv().unwrap();
        thread::sleep(Duration::from_millis(100));
        assert_eq!(*r, 4);
    }
    {
        let r = receiver.recv().unwrap();
        thread::sleep(Duration::from_millis(100));
        assert_eq!(*r, 12);
    }
});

let x: u32 = 4;
let s1 = sender.clone();
with_encapsulated(&x, move |x| s1.send(x).unwrap());

with_encapsulated(&12, move |cap| sender.send(cap).unwrap());

Things that shouldn't compile

Mutating the original variable, while it is encapsulated:

use refcapsule::with_encapsulated;

let mut x = 43;
with_encapsulated(&mut x, |y| {
    x = 4;
});

Encapsulating a reference with a lifetime shorter than the encapsulation scope:

use refcapsule::{with_encapsulated, encapsulate::gen};

with_encapsulated(gen(|s| {
    let x = 43;
    s.encapsulate(&x);
}), |x| {});

Mutating the original variable when it is encapsulated using a generator function:

use refcapsule::{with_encapsulated, encapsulate::gen};

let mut x = 43;
with_encapsulated(gen(|s| {
    s.encapsulate_mut(&mut x);
}), |y| {
    x = 4;
});

Save a scope for a longer duration:

use refcapsule::{with_encapsulated, encapsulate::gen};
with_encapsulated(gen(|s| s), |s| ());

No runtime deps