You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
//! `Barriers` allow tests to granularly observe and control the execution of//! source code by injecting observability and control hooks in to source code.//! Barriers allow construction of complex tests which otherwise may rely on//! timing conditions (which are difficult to write, flaky, and hard to//! maintain) or monitoring and control of the network layer.//!//! Barriers are designed for Turmoil simulation tests. Integrations with//! turmoil allow for test code to step the simulation until a barrier is//! triggered.//!//! # Architecture//! ┌──────────────┐ ┌─────────────────────┐//! │ Source Code │ │ Test Code │//! │ │ │ │//! │ ┌─────────┐ │ ┌──────────────────┐ │ │//! │ │ Trigger ┼─┼─────►│ Barrier Repo │◄───┼── Barrier::build() │//! │ └─────────┘ │ │ (Thread Local) │ │ │//! │ ┌─────────┐ │ ┌───┼ ├────┼─► Barrier::await │//! │ │ Resumer │◄┼──┘ └──────────────────┘ │ │//! │ └─────────┘ │ │ │//! │ │ │ │//! └──────────────┘ └─────────────────────┘//!//! A barrier consists of two halves, a `Trigger` which defines the condition//! a barrier is waiting for and a `Resumer` which controls when the src code//! in a barrier is released. Interesting points of source code may be annotated//! with Triggers; these triggers will no-op if test code is not interested and//! are conditionally compiled out of non-test code. When test code creates a//! barrier, the condition and resumer is registered in the barrier repo. Most//! barriers are 'observe-only' and do not control execution (typically test//! code is simply driving simulation forward until a Barrier is triggered).//! However, test code may cause a future hitting a barrier to suspend until//! the test code resumes it.//!//! Triggers are type safe Rust structs. Source code may define triggers as any//! type desired. Barrier conditions are defined as match statements against a//! trigger. Reactions are built as an enum of well defined actions; arbitrary//! reaction code is not allowed to curtail insane usage.//!//! Note: Each trigger event wakes at most one barrier and processes in order//! of registration. Avoid registering multiple barriers for the same triggers//! to avoid confusion.
And some code as well!
use std::{any::Any, cell::RefCell, marker::PhantomData, ops::Deref};use uuid::Uuid;use tokio::sync::{
mpsc::{self,UnboundedReceiver,UnboundedSender},
oneshot,};thread_local!{staticBARRIERS:BarrierRepo = BarrierRepo::new();}pubstructBarrierRepo{barriers:RefCell<Vec<BarrierState>>,}implBarrierRepo{pubfnnew() -> Self{Self{barriers:RefCell::new(vec![]),}}pubfninsert(&self,barrier:BarrierState){self.barriers.borrow_mut().push(barrier);}pubfndrop(&self,id:Uuid){self.barriers.borrow_mut().retain(|t| t.id != id);}fnbarrier<T:Any + Send>(&self,t:&T) -> Option<(Reaction, mpsc::UnboundedSender<Waker>)>{let guard = self.barriers.borrow();for barrier in guard.iter(){if(barrier.condition)(t){returnSome((barrier.reaction.clone(), barrier.to_test.clone()));}}None}}pubasyncfntrigger<T:Any + Send>(t:T){letSome((reaction, to_test)) = BARRIERS.with(|barriers| barriers.barrier(&t))else{return;};let(tx, rx) = oneshot::channel();let waker = match reaction {Reaction::Noop => {
tx.send(()).expect("Receiver is owned");None}Reaction::Suspend => Some(tx),};let _ = to_test.send((Box::new(t), waker));let _ = rx.await;}pubstructBarrierState{/// For dropping, we match equality of barriers based on this randomly/// generated id.id:Uuid,condition:Box<Condition>,reaction:Reaction,to_test:UnboundedSender<Waker>,}pubtypeCondition = dynFn(&dynAny) -> bool;pubstructBarrier<T>{id:Uuid,from_src:UnboundedReceiver<Waker>,_t:PhantomData<T>,}impl<T:Any + Send>Barrier<T>{/// Create a new barrier that matches the given `condition`pubfnnew(condition:implFn(&T) -> bool + 'static) -> Self{Self::build(Reaction::Noop, condition)}pubfnbuild(reaction:Reaction,condition:implFn(&T) -> bool + 'static) -> Self{let condition = Box::new(move |t:&dynAny| match t.downcast_ref::<T>(){Some(t) => condition(t),None => false,});let(tx, rx) = mpsc::unbounded_channel();let id = Uuid::new_v4();let state = BarrierState{
id,
condition,
reaction,to_test: tx,};BARRIERS.with(|barriers| barriers.insert(state));Self{
id,from_src: rx,_t:PhantomData,}}pubasyncfnwait(&mutself) -> Option<Triggered<T>>{let(data, release) = self.from_src.recv().await?;let data = *data.downcast::<T>().unwrap();Some(Triggered{ data, release })}}impl<T>DropforBarrier<T>{fndrop(&mutself){BARRIERS.with(|barriers| barriers.drop(self.id));}}pubstructTriggered<T>{data:T,release:Option<oneshot::Sender<()>>,}impl<T>DerefforTriggered<T>{typeTarget = T;fnderef(&self) -> &Self::Target{&self.data}}impl<T>DropforTriggered<T>{fndrop(&mutself){ifletSome(release) = self.release.take(){let _ = release.send(());}}}#[derive(Debug,Clone)]pubenumReaction{Noop,Suspend,}pubtypeWaker = (Box<dynAny + Send>,Option<oneshot::Sender<()>>);
The text was updated successfully, but these errors were encountered:
And some code as well!
The text was updated successfully, but these errors were encountered: