#smart-pointers #ownership #object #shared #shared-ptr #cell #joint

twinsies

Smart pointer providing 2-way joint ownership of an object

4 stable releases

1.2.1 Mar 27, 2024
1.2.0 Feb 18, 2023
1.1.0 Jan 31, 2023
1.0.0 Jan 28, 2023

#428 in Rust patterns

Download history 8/week @ 2024-01-30 27/week @ 2024-02-06 2/week @ 2024-02-20 15/week @ 2024-02-27 115/week @ 2024-03-26 18/week @ 2024-04-02

133 downloads per month
Used in handoff

MPL-2.0 license

26KB
264 lines

twinsies

Twinsies is a special shared pointer, similar to an Arc, where two specific objects (called Joint) share joint ownership of the underlying object. The key difference compared to an Arc is that the underlying object is dropped when either of the Joint objects go out of scope.

Because a single Joint cannot, by itself, keep the shared object alive, it cannot be dereferenced directly like an Arc. Instead, it must be locked with .lock(). While locked, the object is guaranteed to stay alive as long as the JointLock is alive. If the a Joint is dropped while its partner is locked, the object stays alive, but it dropped immediately as soon as the other Joint is no longer locked.

Twinsies is intended to be used for things like unbuffered channels, join handles, and async Waker- cases where some piece of shared state should only be preserved as long as both halves are still interested in it.

Example

use twinsies::Joint;
use std::cell::Cell;

let (first, second) = Joint::new(Cell::new(0));

assert_eq!(first.lock().unwrap().get(), 0);

first.lock().unwrap().set(10);
assert_eq!(second.lock().unwrap().get(), 10);

drop(second);

// Once `second` is dropped, the shared value is gone
assert!(first.lock().is_none())

Locks preserve liveness

use twinsies::Joint;
use std::cell::Cell;

let (first, second) = Joint::new(Cell::new(0));

let lock = first.lock().unwrap();

lock.set(10);

assert_eq!(second.lock().unwrap().get(), 10);
second.lock().unwrap().set(20);

assert_eq!(lock.get(), 20);

drop(second);

assert_eq!(lock.get(), 20);
lock.set(30);
assert_eq!(lock.get(), 30);

// As soon as the lock is dropped, the shared value is gone, since `second`
// was dropped earlier
drop(lock);
assert!(first.lock().is_none());

No runtime deps

Features