use std::any::{Any, TypeId};
use std::cell::{Cell, RefCell};
use std::collections::HashMap;
use std::fmt;
use std::future::Future;
use std::pin::Pin;
use std::rc::Rc;
use std::task::{Context, Poll, Waker};
use rustdv_sim::queue::Queue;
use rustdv_sim::sync::Event;
use rustdv_sim::log::Logger;
use rustdv_sim::{Rng, RustdvPath};
use crate::component::{Component, ComponentNode};
use crate::port::{bind_or_panic, PortName, PortOwner};
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub struct TxnId(pub u64);
impl fmt::Display for TxnId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "#{}", self.0)
}
}
pub struct SeqItem<REQ> {
id: TxnId,
payload: REQ,
}
impl<REQ> SeqItem<REQ> {
pub fn txn_id(&self) -> TxnId {
self.id
}
pub fn payload(&self) -> &REQ {
&self.payload
}
pub fn payload_mut(&mut self) -> &mut REQ {
&mut self.payload
}
pub fn into_payload(self) -> REQ {
self.payload
}
}
#[derive(Debug, Clone)]
pub struct SeqError(pub String);
impl fmt::Display for SeqError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "sequence error: {}", self.0)
}
}
impl std::error::Error for SeqError {}
impl From<&str> for SeqError {
fn from(s: &str) -> SeqError {
SeqError(s.to_string())
}
}
impl From<String> for SeqError {
fn from(s: String) -> SeqError {
SeqError(s)
}
}
impl From<crate::config::ConfigError> for SeqError {
fn from(e: crate::config::ConfigError) -> SeqError {
SeqError(e.to_string())
}
}
struct ItemSlot<REQ> {
id: TxnId,
granted: Event,
ready: Event,
done: Event,
payload: RefCell<Option<REQ>>,
}
struct RespInner<RSP> {
items: RefCell<Vec<(TxnId, RSP)>>,
waiters: RefCell<Vec<Waker>>,
}
pub struct ResponseQueue<RSP> {
inner: Rc<RespInner<RSP>>,
}
impl<RSP> Clone for ResponseQueue<RSP> {
fn clone(&self) -> Self {
ResponseQueue { inner: self.inner.clone() }
}
}
impl<RSP> ResponseQueue<RSP> {
fn new() -> ResponseQueue<RSP> {
ResponseQueue {
inner: Rc::new(RespInner {
items: RefCell::new(Vec::new()),
waiters: RefCell::new(Vec::new()),
}),
}
}
fn push(&self, id: TxnId, rsp: RSP) {
self.inner.items.borrow_mut().push((id, rsp));
for w in self.inner.waiters.borrow_mut().drain(..) {
w.wake();
}
}
pub fn get_response(&self, txn_id: Option<TxnId>) -> GetResponse<RSP> {
GetResponse { inner: self.inner.clone(), txn_id }
}
pub fn try_get_response(&self, txn_id: Option<TxnId>) -> Option<RSP> {
let mut items = self.inner.items.borrow_mut();
let idx = match txn_id {
None => (!items.is_empty()).then_some(0),
Some(id) => items.iter().position(|(i, _)| *i == id),
};
idx.map(|i| items.remove(i).1)
}
}
pub struct GetResponse<RSP> {
inner: Rc<RespInner<RSP>>,
txn_id: Option<TxnId>,
}
impl<RSP> Future for GetResponse<RSP> {
type Output = RSP;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<RSP> {
let mut items = self.inner.items.borrow_mut();
let idx = match self.txn_id {
None => (!items.is_empty()).then_some(0),
Some(id) => items.iter().position(|(i, _)| *i == id),
};
match idx {
Some(i) => Poll::Ready(items.remove(i).1),
None => {
drop(items);
self.inner.waiters.borrow_mut().push(cx.waker().clone());
Poll::Pending
}
}
}
}
struct SeqrInner<REQ: 'static, RSP: 'static> {
queue: Queue<Rc<ItemSlot<REQ>>>,
next_id: Cell<u64>,
responses: ResponseQueue<RSP>,
current: RefCell<Option<Rc<ItemSlot<REQ>>>>,
}
pub trait SeqItemIf<REQ: 'static, RSP: 'static>: 'static {
fn get_next_item(&self) -> Pin<Box<dyn Future<Output = SeqItem<REQ>> + '_>>;
fn try_next_item(&self) -> Option<SeqItem<REQ>>;
fn item_done(&self, rsp: Option<RSP>);
fn put_response(&self, id: TxnId, rsp: RSP);
}
impl<REQ: 'static, RSP: 'static> SeqItemIf<REQ, RSP> for SeqrInner<REQ, RSP> {
fn get_next_item(&self) -> Pin<Box<dyn Future<Output = SeqItem<REQ>> + '_>> {
Box::pin(async move {
assert!(
self.current.borrow().is_none(),
"get_next_item called twice without item_done"
);
let slot = self.queue.get().await;
slot.granted.set();
slot.ready.wait().await;
let payload = slot
.payload
.borrow_mut()
.take()
.expect("item ready but payload missing (rustdv bug)");
let item = SeqItem { id: slot.id, payload };
*self.current.borrow_mut() = Some(slot);
item
})
}
fn try_next_item(&self) -> Option<SeqItem<REQ>> {
assert!(self.current.borrow().is_none(), "try_next_item called without item_done");
let slot = self.queue.try_get()?;
slot.granted.set();
let taken = slot.payload.borrow_mut().take();
let payload = match taken {
Some(p) => p,
None => {
let _ = self.queue.try_put(slot);
return None;
}
};
let item = SeqItem { id: slot.id, payload };
*self.current.borrow_mut() = Some(slot);
Some(item)
}
fn item_done(&self, rsp: Option<RSP>) {
let slot = self
.current
.borrow_mut()
.take()
.expect("item_done without get_next_item");
if let Some(r) = rsp {
self.responses.push(slot.id, r);
}
slot.done.set();
}
fn put_response(&self, id: TxnId, rsp: RSP) {
self.responses.push(id, rsp);
}
}
pub type SeqItemPort<REQ, RSP = REQ> = crate::port::Port<dyn SeqItemIf<REQ, RSP>>;
pub struct SeqItemExport<REQ: 'static, RSP: 'static> {
iface: Rc<dyn SeqItemIf<REQ, RSP>>,
}
impl<REQ: 'static, RSP: 'static> SeqItemExport<REQ, RSP> {
pub fn connect(&self, owner: &dyn PortOwner, name: PortName<dyn SeqItemIf<REQ, RSP>>) {
bind_or_panic(owner, name, self.iface.clone());
}
pub fn connect_port(&self, port: &SeqItemPort<REQ, RSP>) {
port.bind_iface(self.iface.clone());
}
}
pub struct Sequencer<REQ: 'static, RSP: 'static = REQ> {
inner: Rc<SeqrInner<REQ, RSP>>,
}
impl<REQ, RSP> Clone for Sequencer<REQ, RSP> {
fn clone(&self) -> Self {
Sequencer { inner: self.inner.clone() }
}
}
impl<REQ: 'static, RSP: 'static> fmt::Debug for Sequencer<REQ, RSP> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("Sequencer")
}
}
impl<REQ: 'static, RSP: 'static> Default for Sequencer<REQ, RSP> {
fn default() -> Self {
Self::new()
}
}
impl<REQ: 'static, RSP: 'static> Sequencer<REQ, RSP> {
pub fn new() -> Sequencer<REQ, RSP> {
Sequencer {
inner: Rc::new(SeqrInner {
queue: Queue::unbounded(),
next_id: Cell::new(1),
responses: ResponseQueue::new(),
current: RefCell::new(None),
}),
}
}
pub fn handle(&self) -> Sequencer<REQ, RSP> {
self.clone()
}
pub fn seq_item_export(&self) -> SeqItemExport<REQ, RSP> {
SeqItemExport { iface: self.inner.clone() }
}
}
impl<REQ: 'static, RSP: 'static> Component for Sequencer<REQ, RSP> {}
impl<REQ: 'static, RSP: 'static> ComponentNode for Sequencer<REQ, RSP> {
fn node_name(&self) -> &'static str {
"Sequencer"
}
fn children_mut(&mut self) -> Vec<(String, &mut (dyn ComponentNode + 'static))> {
Vec::new()
}
}
pub struct SeqCtx<REQ: 'static, RSP: 'static = REQ> {
inner: Option<Rc<SeqrInner<REQ, RSP>>>,
current: Option<Rc<ItemSlot<REQ>>>,
name: &'static str,
logger: Logger,
seed: u64,
}
impl<REQ: 'static, RSP: 'static> SeqCtx<REQ, RSP> {
fn new(inner: Option<Rc<SeqrInner<REQ, RSP>>>, name: &'static str, seed: u64) -> Self {
SeqCtx {
inner,
current: None,
name,
logger: Logger::at(RustdvPath::root(name)),
seed,
}
}
pub fn name(&self) -> &'static str {
self.name
}
pub fn rng(&self) -> Rng {
Rng::new(self.seed)
}
pub fn info(&self, msg: &str) {
self.logger.info(msg);
}
pub fn warning(&self, msg: &str) {
self.logger.warning(msg);
}
pub fn error(&self, msg: &str) {
self.logger.error(msg);
}
fn seqr(&self) -> Result<&Rc<SeqrInner<REQ, RSP>>, SeqError> {
self.inner.as_ref().ok_or_else(|| {
SeqError(format!(
"{}: start_item in a virtual sequence — it was started without a sequencer",
self.name
))
})
}
pub async fn start_item(&mut self, _item: &mut REQ) -> Result<(), SeqError> {
if self.current.is_some() {
return Err(SeqError("start_item called twice without finish_item".into()));
}
let inner = self.seqr()?.clone();
let id = TxnId(inner.next_id.get());
inner.next_id.set(id.0 + 1);
let slot = Rc::new(ItemSlot {
id,
granted: Event::new(),
ready: Event::new(),
done: Event::new(),
payload: RefCell::new(None),
});
self.current = Some(slot.clone());
inner.queue.put(slot.clone()).await;
slot.granted.wait().await;
Ok(())
}
pub async fn finish_item(&mut self, item: REQ) -> Result<TxnId, SeqError> {
let slot = self
.current
.take()
.ok_or_else(|| SeqError("finish_item without start_item".into()))?;
*slot.payload.borrow_mut() = Some(item);
slot.ready.set();
slot.done.wait().await;
Ok(slot.id)
}
pub fn try_get_response(&mut self, txn_id: Option<TxnId>) -> Option<RSP> {
let responses = self.seqr().ok()?.responses.clone();
responses.try_get_response(txn_id)
}
pub async fn get_response(&mut self, txn_id: Option<TxnId>) -> RSP {
let responses = self.seqr().expect("get_response in a virtual sequence").responses.clone();
responses.get_response(txn_id).await
}
}
pub trait Sequence: Sized + 'static {
type Req: 'static;
type Rsp: 'static;
fn body(
&mut self,
ctx: &mut SeqCtx<Self::Req, Self::Rsp>,
) -> impl Future<Output = Result<(), SeqError>>;
fn seq_name(&self) -> &'static str {
let full = std::any::type_name::<Self>();
full.rsplit("::").next().unwrap_or(full)
}
fn start(
&mut self,
seqr: &Sequencer<Self::Req, Self::Rsp>,
) -> impl Future<Output = Result<(), SeqError>> {
let inner = seqr.inner.clone();
let name = self.seq_name();
async move {
let mut ctx = SeqCtx::new(Some(inner), name, current_seed());
self.body(&mut ctx).await
}
}
fn start_virtual(&mut self) -> impl Future<Output = Result<(), SeqError>> {
let name = self.seq_name();
async move {
let mut ctx = SeqCtx::new(None, name, current_seed());
self.body(&mut ctx).await
}
}
}
thread_local! {
static SEED: Cell<u64> = const { Cell::new(1) };
}
pub fn set_sequence_seed(seed: u64) {
SEED.with(|s| s.set(seed));
}
fn current_seed() -> u64 {
SEED.with(|s| s.get())
}
pub trait DynSequence<REQ: 'static, RSP: 'static> {
fn dyn_body<'a>(
&'a mut self,
ctx: &'a mut SeqCtx<REQ, RSP>,
) -> Pin<Box<dyn Future<Output = Result<(), SeqError>> + 'a>>;
fn dyn_name(&self) -> &'static str;
}
impl<S: Sequence> DynSequence<S::Req, S::Rsp> for S {
fn dyn_body<'a>(
&'a mut self,
ctx: &'a mut SeqCtx<S::Req, S::Rsp>,
) -> Pin<Box<dyn Future<Output = Result<(), SeqError>> + 'a>> {
Box::pin(self.body(ctx))
}
fn dyn_name(&self) -> &'static str {
self.seq_name()
}
}
pub struct RustdvSeq<REQ: 'static, RSP: 'static = REQ> {
inner: Option<Box<dyn DynSequence<REQ, RSP>>>,
}
impl<REQ: 'static, RSP: 'static> Default for RustdvSeq<REQ, RSP> {
fn default() -> Self {
RustdvSeq { inner: None }
}
}
impl<REQ: 'static, RSP: 'static> RustdvSeq<REQ, RSP> {
pub fn new(seq: Box<dyn DynSequence<REQ, RSP>>) -> Self {
RustdvSeq { inner: Some(seq) }
}
pub fn name(&self) -> &'static str {
self.inner.as_ref().map(|s| s.dyn_name()).unwrap_or("<empty>")
}
fn get(&mut self) -> Result<&mut Box<dyn DynSequence<REQ, RSP>>, SeqError> {
self.inner.as_mut().ok_or_else(|| SeqError("an empty sequence slot".into()))
}
pub async fn start(&mut self, seqr: &Sequencer<REQ, RSP>) -> Result<(), SeqError> {
let inner = seqr.inner.clone();
let seq = self.get()?;
let mut ctx = SeqCtx::new(Some(inner), seq.dyn_name(), current_seed());
seq.dyn_body(&mut ctx).await
}
pub async fn start_virtual(&mut self) -> Result<(), SeqError> {
let seq = self.get()?;
let mut ctx = SeqCtx::new(None, seq.dyn_name(), current_seed());
seq.dyn_body(&mut ctx).await
}
}
type SeqOverrides = HashMap<TypeId, (TypeId, fn() -> Box<dyn Any>)>;
thread_local! {
static SEQ_OVERRIDES: RefCell<SeqOverrides> = RefCell::new(HashMap::new());
}
pub fn clear_seq_overrides() {
SEQ_OVERRIDES.with(|o| o.borrow_mut().clear());
}
pub fn set_seq_override<From, To>()
where
From: Sequence,
To: Sequence<Req = From::Req, Rsp = From::Rsp> + Default,
{
fn maker<To: Sequence + Default>() -> Box<dyn Any> {
let boxed: Box<dyn DynSequence<To::Req, To::Rsp>> = Box::new(To::default());
Box::new(boxed)
}
SEQ_OVERRIDES.with(|o| {
o.borrow_mut()
.insert(TypeId::of::<From>(), (TypeId::of::<To>(), maker::<To>));
});
}
pub fn create_seq<S>() -> RustdvSeq<S::Req, S::Rsp>
where
S: Sequence + Default,
{
let over = SEQ_OVERRIDES.with(|o| o.borrow().get(&TypeId::of::<S>()).map(|(_, m)| *m));
match over {
Some(make) => {
let any = make();
let boxed = any
.downcast::<Box<dyn DynSequence<S::Req, S::Rsp>>>()
.expect("sequence override built the wrong request/response types");
RustdvSeq::new(*boxed)
}
None => RustdvSeq::new(Box::new(S::default())),
}
}
#[cfg(test)]
mod tests {
use super::*;
use rustdv_sim::executor;
use rustdv_sim::testing::{assert_pending, block_on};
#[derive(Clone, Debug, PartialEq, Eq, Default)]
struct Cmd {
a: u8,
tag: &'static str,
}
#[derive(Clone, Debug, PartialEq, Eq, Default)]
struct Rsp {
v: u8,
}
fn spawn_one_shot_driver(seqr: &Sequencer<Cmd, Rsp>, answer: u8) {
let port = seqr.seq_item_export();
let inner = seqr.inner.clone();
let _ = port; executor::spawn(async move {
let item = SeqItemIf::get_next_item(&*inner).await;
let v = item.payload().a.wrapping_add(answer);
SeqItemIf::item_done(&*inner, Some(Rsp { v }));
});
}
#[test]
fn start_item_blocks_until_the_driver_asks() {
let seqr: Sequencer<Cmd, Rsp> = Sequencer::new();
let inner = seqr.inner.clone();
assert_pending(async move {
let mut ctx = SeqCtx::new(Some(inner), "T", 1);
let mut cmd = Cmd::default();
ctx.start_item(&mut cmd).await.unwrap();
});
}
#[test]
fn the_driver_sees_what_was_written_after_the_grant() {
block_on(async {
let seqr: Sequencer<Cmd, Rsp> = Sequencer::new();
let inner = seqr.inner.clone();
let seen = Rc::new(RefCell::new(None));
let seen2 = seen.clone();
let d = inner.clone();
executor::spawn(async move {
let item = SeqItemIf::get_next_item(&*d).await;
*seen2.borrow_mut() = Some(item.payload().clone());
SeqItemIf::item_done(&*d, None);
});
let mut ctx = SeqCtx::new(Some(inner), "T", 1);
let mut cmd = Cmd { a: 0, tag: "before" };
ctx.start_item(&mut cmd).await.unwrap();
cmd.a = 42;
cmd.tag = "after the grant";
ctx.finish_item(cmd).await.unwrap();
let got = seen.borrow().clone().expect("the driver got an item");
assert_eq!(got.a, 42, "the late value reached the driver");
assert_eq!(got.tag, "after the grant");
});
}
#[test]
fn finish_item_returns_the_ticket_and_waits_for_item_done() {
block_on(async {
let seqr: Sequencer<Cmd, Rsp> = Sequencer::new();
spawn_one_shot_driver(&seqr, 1);
let mut ctx = SeqCtx::new(Some(seqr.inner.clone()), "T", 1);
let mut cmd = Cmd { a: 10, tag: "x" };
ctx.start_item(&mut cmd).await.unwrap();
let ticket = ctx.finish_item(cmd).await.unwrap();
assert_eq!(ticket, TxnId(1), "tickets start at 1");
});
}
#[test]
fn tickets_are_unique_and_ascending() {
block_on(async {
let seqr: Sequencer<Cmd, Rsp> = Sequencer::new();
let inner = seqr.inner.clone();
let d = inner.clone();
executor::spawn(async move {
for _ in 0..3 {
let _item = SeqItemIf::get_next_item(&*d).await;
SeqItemIf::item_done(&*d, None);
}
});
let mut ctx = SeqCtx::new(Some(inner), "T", 1);
let mut tickets = Vec::new();
for a in 0..3u8 {
let mut cmd = Cmd { a, tag: "" };
ctx.start_item(&mut cmd).await.unwrap();
tickets.push(ctx.finish_item(cmd).await.unwrap());
}
assert_eq!(tickets, vec![TxnId(1), TxnId(2), TxnId(3)]);
});
}
#[test]
#[should_panic(expected = "get_next_item called twice without item_done")]
fn two_get_next_items_without_item_done_is_a_bug() {
block_on(async {
let seqr: Sequencer<Cmd, Rsp> = Sequencer::new();
let inner = seqr.inner.clone();
let s = inner.clone();
executor::spawn(async move {
let mut ctx = SeqCtx::new(Some(s), "T", 1);
let mut cmd = Cmd::default();
ctx.start_item(&mut cmd).await.unwrap();
ctx.finish_item(cmd).await.unwrap();
});
let _a = SeqItemIf::get_next_item(&*inner).await;
let _b = SeqItemIf::get_next_item(&*inner).await;
});
}
#[test]
fn start_item_twice_without_finish_is_an_error() {
block_on(async {
let seqr: Sequencer<Cmd, Rsp> = Sequencer::new();
let inner = seqr.inner.clone();
let d = inner.clone();
executor::spawn(async move {
let _ = SeqItemIf::get_next_item(&*d).await;
});
let mut ctx = SeqCtx::new(Some(inner), "T", 1);
let mut a = Cmd::default();
ctx.start_item(&mut a).await.unwrap();
let mut b = Cmd::default();
let err = ctx.start_item(&mut b).await;
assert!(err.is_err(), "a second start_item without finish_item");
});
}
#[test]
fn try_next_item_is_none_on_an_empty_sequencer() {
block_on(async {
let seqr: Sequencer<Cmd, Rsp> = Sequencer::new();
assert!(SeqItemIf::try_next_item(&*seqr.inner).is_none());
});
}
#[test]
fn try_next_item_takes_a_waiting_item() {
block_on(async {
let seqr: Sequencer<Cmd, Rsp> = Sequencer::new();
let inner = seqr.inner.clone();
let s = inner.clone();
executor::spawn(async move {
let mut ctx = SeqCtx::new(Some(s), "T", 1);
let mut cmd = Cmd { a: 5, tag: "" };
ctx.start_item(&mut cmd).await.unwrap();
ctx.finish_item(cmd).await.unwrap();
});
let mut got = None;
for _ in 0..8 {
executor::current().run_until_idle();
if let Some(item) = SeqItemIf::try_next_item(&*inner) {
got = Some(item.payload().a);
SeqItemIf::item_done(&*inner, None);
break;
}
}
assert_eq!(got, Some(5));
});
}
#[test]
fn item_done_with_a_response_reaches_get_response() {
block_on(async {
let seqr: Sequencer<Cmd, Rsp> = Sequencer::new();
spawn_one_shot_driver(&seqr, 100);
let mut ctx = SeqCtx::new(Some(seqr.inner.clone()), "T", 1);
let mut cmd = Cmd { a: 1, tag: "" };
ctx.start_item(&mut cmd).await.unwrap();
let ticket = ctx.finish_item(cmd).await.unwrap();
let rsp = ctx.get_response(Some(ticket)).await;
assert_eq!(rsp.v, 101);
});
}
#[test]
fn put_response_answers_after_item_done() {
block_on(async {
let seqr: Sequencer<Cmd, Rsp> = Sequencer::new();
let inner = seqr.inner.clone();
let d = inner.clone();
executor::spawn(async move {
let item = SeqItemIf::get_next_item(&*d).await;
let id = item.txn_id();
SeqItemIf::item_done(&*d, None); SeqItemIf::put_response(&*d, id, Rsp { v: 77 });
});
let mut ctx = SeqCtx::new(Some(inner), "T", 1);
let mut cmd = Cmd::default();
ctx.start_item(&mut cmd).await.unwrap();
let ticket = ctx.finish_item(cmd).await.unwrap();
assert_eq!(ctx.get_response(Some(ticket)).await.v, 77);
});
}
#[test]
fn get_response_picks_its_ticket_out_of_order() {
block_on(async {
let seqr: Sequencer<Cmd, Rsp> = Sequencer::new();
let inner = seqr.inner.clone();
inner.responses.push(TxnId(2), Rsp { v: 22 });
inner.responses.push(TxnId(1), Rsp { v: 11 });
let mut ctx = SeqCtx::new(Some(inner), "T", 1);
assert_eq!(ctx.get_response(Some(TxnId(1))).await.v, 11);
assert_eq!(ctx.get_response(Some(TxnId(2))).await.v, 22);
});
}
#[test]
fn get_response_none_takes_the_oldest() {
block_on(async {
let seqr: Sequencer<Cmd, Rsp> = Sequencer::new();
let inner = seqr.inner.clone();
inner.responses.push(TxnId(5), Rsp { v: 50 });
inner.responses.push(TxnId(6), Rsp { v: 60 });
let mut ctx = SeqCtx::new(Some(inner), "T", 1);
assert_eq!(ctx.get_response(None).await.v, 50, "oldest first");
assert_eq!(ctx.get_response(None).await.v, 60);
});
}
#[test]
fn try_get_response_does_not_wait() {
block_on(async {
let seqr: Sequencer<Cmd, Rsp> = Sequencer::new();
let inner = seqr.inner.clone();
let mut ctx = SeqCtx::new(Some(inner.clone()), "T", 1);
assert!(ctx.try_get_response(Some(TxnId(1))).is_none(), "nothing yet");
inner.responses.push(TxnId(1), Rsp { v: 9 });
assert_eq!(ctx.try_get_response(Some(TxnId(1))).unwrap().v, 9);
assert!(ctx.try_get_response(Some(TxnId(1))).is_none(), "and it was taken");
});
}
#[test]
fn a_response_that_never_comes_waits_forever() {
let seqr: Sequencer<Cmd, Rsp> = Sequencer::new();
let inner = seqr.inner.clone();
assert_pending(async move {
let mut ctx = SeqCtx::new(Some(inner), "T", 1);
ctx.get_response(Some(TxnId(1043))).await
});
}
#[test]
fn start_item_in_a_virtual_sequence_is_an_error() {
block_on(async {
let mut ctx: SeqCtx<Cmd, Rsp> = SeqCtx::new(None, "MyVirtualSeq", 1);
let mut cmd = Cmd::default();
match ctx.start_item(&mut cmd).await {
Err(SeqError(msg)) => {
assert!(msg.contains("virtual"), "the error explains: {msg}");
assert!(msg.contains("MyVirtualSeq"), "and names the sequence: {msg}");
}
Ok(()) => panic!("start_item should fail without a sequencer"),
}
});
}
#[test]
fn two_sequences_interleave_one_item_each() {
block_on(async {
let seqr: Sequencer<Cmd, Rsp> = Sequencer::new();
let inner = seqr.inner.clone();
let order = Rc::new(RefCell::new(Vec::new()));
for tag in ["A", "B"] {
let s = inner.clone();
executor::spawn(async move {
let mut ctx = SeqCtx::new(Some(s), tag, 1);
for a in 0..2u8 {
let mut cmd = Cmd { a, tag };
ctx.start_item(&mut cmd).await.unwrap();
ctx.finish_item(cmd).await.unwrap();
}
});
}
let d = inner.clone();
let seen = order.clone();
executor::spawn(async move {
for _ in 0..4 {
let item = SeqItemIf::get_next_item(&*d).await;
seen.borrow_mut().push(item.payload().tag);
SeqItemIf::item_done(&*d, None);
}
});
for _ in 0..64 {
executor::current().run_until_idle();
}
let got = order.borrow().clone();
assert_eq!(got.len(), 4, "all four items were driven");
assert_eq!(got, vec!["A", "B", "A", "B"], "one item each, in turn");
});
}
#[test]
fn a_sequence_name_defaults_to_its_type() {
#[derive(Default)]
struct MyFancySeq;
impl Sequence for MyFancySeq {
type Req = Cmd;
type Rsp = Rsp;
async fn body(&mut self, _ctx: &mut SeqCtx<Cmd, Rsp>) -> Result<(), SeqError> {
Ok(())
}
}
assert_eq!(MyFancySeq.seq_name(), "MyFancySeq");
}
#[test]
fn a_virtual_sequence_runs_its_body() {
#[derive(Default)]
struct VSeq {
ran: bool,
}
impl Sequence for VSeq {
type Req = Cmd;
type Rsp = Rsp;
async fn body(&mut self, _ctx: &mut SeqCtx<Cmd, Rsp>) -> Result<(), SeqError> {
self.ran = true;
Ok(())
}
}
block_on(async {
let mut s = VSeq::default();
s.start_virtual().await.unwrap();
assert!(s.ran);
});
}
}