In Brief
- Wrap a value
T with Soul<T>::new(value).
- Pin the
Soul with core::pin::pin! or Box/Arc/Rc::pin.
- Bind
Lich<dyn Trait> to the Soul with soul.bind::<dyn Trait>() (where Trait is a trait implemented by T).
- Use the
Lich in a lifetime-extended context (such as crossing a std::thread::spawn 'static boundary or storing it in a static variable).
- Make sure to drop all
Liches before dropping the Soul.
- On drop, the
Soul will block the thread until all Liches are dropped, potentially creating a deadlock condition (in the name of memory safety).
Since this library makes use of some unsafe code, all tests are run with miri to try to catch any unsoundness.
This library supports #[no_std] (use default-features = false in your 'Cargo.toml').
Examples
#[cfg(all(feature = "shroud", feature = "std"))]
pub mod thread_spawn_bridge {
use core::{num::NonZeroUsize, pin::Pin};
use phylactery::Soul;
use std::thread;
pub fn broadcast<F: Fn(usize) + Send + Sync>(
parallelism: NonZeroUsize,
function: F,
) -> Pin<Box<Soul<F>>> {
let soul = Box::pin(Soul::new(function));
for index in 0..parallelism.get() {
let lich = soul.as_ref().bind::<dyn Fn(usize) + Send + Sync>();
thread::spawn(move || lich(index));
}
soul
}
}
fn main() {
#[cfg(all(feature = "shroud", feature = "std"))]
thread_spawn_bridge::broadcast(
std::thread::available_parallelism().unwrap_or(core::num::NonZeroUsize::MIN),
&|index| println!("{index}"),
);
}
#[cfg(feature = "shroud")]
pub mod scoped_static_logger {
use core::{cell::RefCell, fmt::Display, pin::pin};
use phylactery::{Lich, Soul, shroud};
#[shroud]
pub trait Log {
fn parent(&self) -> Option<&dyn Log>;
fn prefix(&self) -> &str;
fn format(&self) -> &str;
fn arguments(&self) -> &[&dyn Display];
}
pub struct Logger<'a> {
parent: Option<&'a dyn Log>,
prefix: &'a str,
format: &'a str,
arguments: &'a [&'a dyn Display],
}
impl Log for Logger<'_> {
fn parent(&self) -> Option<&dyn Log> {
self.parent
}
fn prefix(&self) -> &str {
self.prefix
}
fn format(&self) -> &str {
self.format
}
fn arguments(&self) -> &[&dyn Display] {
self.arguments
}
}
thread_local! {
static LOGGER: RefCell<Option<Lich<dyn Log>>> = RefCell::default();
}
pub fn scope<T: Display, F: FnOnce(&T)>(prefix: &str, argument: &T, function: F) {
let parent = LOGGER.take();
{
let logger = Logger {
parent: parent.as_deref(),
prefix,
format: "({})",
arguments: &[argument],
};
let soul = pin!(Soul::new(logger));
let lich = soul.as_ref().bind::<dyn Log>();
LOGGER.set(Some(lich));
function(argument);
LOGGER.take().expect("`Lich` has been pushed");
}
LOGGER.set(parent);
}
}
fn main() {
#[cfg(feature = "shroud")]
scoped_static_logger::scope("some-prefix", &37, |value| {
assert_eq!(*value, 37);
});
}
See the examples and tests folder for more detailed examples.
Contribute
- If you find a bug or have a feature request, please open an issues.
phylactery is actively maintained and pull requests are welcome.
- If
phylactery was useful to you, please consider leaving a star!