Struct topo::CallId [−][src]
pub struct CallId { /* fields omitted */ }
Expand description
Identifies the scope of a nested function call in a way that can be deterministically reproduced across multiple executions.
The CallId::current
for a function call is the combined product of:
- a callsite: the
std::panic::Location
where the function was called - a parent: the
CallId::current
which was active when calling the function - a slot: a value indicating the call’s “index” within the parent call
When a nested call returns or unwinds, it reverts CallId::current
to
the parent CallId
.
Example
use topo::{call, root, CallId};
let returns_two_ids = || {
let first = call(|| CallId::current());
let second = call(|| CallId::current());
assert_ne!(first, second, "these are always distinct calls");
(first, second)
};
// running the closure as a nested call(...) gives different siblings
assert_ne!(call(returns_two_ids), call(returns_two_ids));
// a call to root(...) gives each of these closure calls an identical parent CallId
assert_eq!(root(returns_two_ids), root(returns_two_ids));
Creation
Every CallId
is created by calling one of:
- a function marked
nested
- a function passed to
call
- a function and slot passed to
call_in_slot
Slots
Slots are used to differentiate between repeated calls at the same callsite
and define the “index” of a child call within its parent. By default (and in
call
) the slot is populated by the number of times the current
callsite has been called in this parent. Users can provide their own slot
with call_in_slot
or using #[topo::nested(slot = "...")]
:
See call_in_slot
and nested
for examples.
Roots
The topmost parent or “root” of a callgraph can be defined in two ways:
- a
call
orcall_in_slot
invocation with no parent implicitly creates its own root - an explicit call to
root
creates a new subgraph regardless of the current parent
See root
for examples.
CallId
and multiple threads
The illicit
environment used for tracking the current CallId
is
thread-local, but values used to track slots are
interned in a global cache. This means that two different threads calling
an identical chain of nested functions can observe identical CallId
s:
use std::{
sync::mpsc::{channel, Sender},
thread,
};
let (send_ids, recv_ids) = channel();
let spawn_worker = |sender: Sender<(CallId, CallId)>| {
thread::spawn(move || sender.send(root(returns_two_ids)).unwrap())
};
let first_thread = spawn_worker(send_ids.clone());
let second_thread = spawn_worker(send_ids);
first_thread.join().unwrap();
second_thread.join().unwrap();
// the two worker threads "did the same work"
assert_eq!(recv_ids.recv()?, recv_ids.recv()?);
Implementations
Trait Implementations
Auto Trait Implementations
impl RefUnwindSafe for CallId
impl UnwindSafe for CallId
Blanket Implementations
Mutably borrows from an owned value. Read more
impl<T> Downcast for T where
T: Any,
impl<T> Downcast for T where
T: Any,
Convert Box<dyn Trait>
(where Trait: Downcast
) to Box<dyn Any>
. Box<dyn Any>
can
then be further downcast
into Box<ConcreteType>
where ConcreteType
implements Trait
. Read more
pub fn into_any_rc(self: Rc<T>) -> Rc<dyn Any + 'static>
pub fn into_any_rc(self: Rc<T>) -> Rc<dyn Any + 'static>
Convert Rc<Trait>
(where Trait: Downcast
) to Rc<Any>
. Rc<Any>
can then be
further downcast
into Rc<ConcreteType>
where ConcreteType
implements Trait
. Read more
Convert &Trait
(where Trait: Downcast
) to &Any
. This is needed since Rust cannot
generate &Any
’s vtable from &Trait
’s. Read more
pub fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
pub fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
Convert &mut Trait
(where Trait: Downcast
) to &Any
. This is needed since Rust cannot
generate &mut Any
’s vtable from &mut Trait
’s. Read more