use std::future::Future;
use std::pin::Pin;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::{Arc, Mutex, Weak};
use std::task::{Context, Poll, Waker};
use rustev_contract::acquisition::AcquisitionRequest;
use rustev_contract::descriptor::BackendDescriptor;
use rustev_contract::dynamic::{DynamicCapability, DynamicOutcome, DynamicRawOutput, TemplateRef};
use rustev_contract::eval_report::EvalReport;
use rustev_contract::output::RawOutput;
use rustev_contract::remote::ServedIdentity;
use rustev_contract::run::{Charge, CostBound, CostModel, RunRecord};
use rustev_contract::snapshot::Snapshot;
use rustev_contract::source::SourceDescriptor;
use rustev_contract::time::DurationMs;
pub type BoxFuture<'a, T> = Pin<Box<dyn Future<Output = T> + Send + 'a>>;
#[derive(Debug, Clone)]
pub struct CallContext {
pub remaining_ms: DurationMs,
pub trace_id: String,
pub principal_handle: Vec<u8>,
}
pub trait ContextSource: Send + Sync {
fn fetch<'a>(
&'a self,
fields: &'a [String],
cx: &'a CallContext,
) -> BoxFuture<'a, Result<Snapshot, String>>;
}
pub struct AttemptCall<'a> {
pub projection: &'a [u8],
pub attempt_id: &'a str,
pub cancel: CancelSignal,
pub cx: &'a CallContext,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum AdapterFailure {
Transient,
Overloaded,
Permanent,
Cancelled,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum CancelAck {
NotRequested,
Stopped,
Unconfirmed,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum RemoteEnd {
Finished,
PossiblyContinuing,
}
#[derive(Debug, Clone, PartialEq)]
pub struct AttemptReport {
pub result: Result<RawOutput, (AdapterFailure, String)>,
pub charge: Charge,
pub cancel: CancelAck,
pub remote: RemoteEnd,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct BatchCapability {
pub compatibility: String,
pub work_units: u64,
}
#[derive(Debug, Clone)]
pub struct BatchMemberCall {
pub projection: Vec<u8>,
pub attempt_id: String,
pub cancel: CancelSignal,
pub cx: CallContext,
}
pub struct BatchAttemptCall {
pub batch_id: String,
pub cancel: CancelSignal,
pub members: Vec<BatchMemberCall>,
}
#[derive(Debug, Clone, PartialEq)]
pub struct BatchMemberReport {
pub attempt_id: String,
pub result: Result<RawOutput, (AdapterFailure, String)>,
}
#[derive(Debug, Clone, PartialEq)]
pub struct BatchAttemptReport {
pub result: Result<Vec<BatchMemberReport>, (AdapterFailure, String)>,
pub charge: Charge,
pub cancel: CancelAck,
pub remote: RemoteEnd,
}
pub trait DecisionBackend: Send + Sync {
fn descriptor(&self) -> &BackendDescriptor;
fn cost_model(&self) -> CostModel;
fn cost_bound(&self, projection: &[u8]) -> CostBound;
fn infer<'a>(&'a self, call: AttemptCall<'a>) -> BoxFuture<'a, AttemptReport>;
fn batch_capability(&self, _projection: &[u8]) -> Option<BatchCapability> {
None
}
fn take_remote_evidence(
&self,
_attempt_id: &str,
) -> Option<rustev_contract::run::RemoteEvidence> {
None
}
fn infer_batch<'a>(&'a self, _call: BatchAttemptCall) -> BoxFuture<'a, BatchAttemptReport> {
Box::pin(async {
BatchAttemptReport {
result: Err((
AdapterFailure::Permanent,
"backend did not implement declared batch semantics".into(),
)),
charge: Charge::Unknown,
cancel: CancelAck::NotRequested,
remote: RemoteEnd::PossiblyContinuing,
}
})
}
}
pub struct DynamicCall<'a> {
pub projection: &'a [u8],
pub template: &'a TemplateRef,
pub attempt_id: &'a str,
pub cancel: CancelSignal,
pub cx: &'a CallContext,
}
#[derive(Debug, Clone, PartialEq)]
pub struct DynamicReport {
pub result: Result<DynamicRawOutput, (AdapterFailure, String)>,
pub served: ServedIdentity,
pub charge: Charge,
pub cancel: CancelAck,
pub remote: RemoteEnd,
}
pub trait DynamicBackend: Send + Sync {
fn backend_id(&self) -> &str;
fn dynamic_capability(&self) -> Option<&DynamicCapability>;
fn cost_bound(&self, projection: &[u8]) -> CostBound;
fn ask<'a>(&'a self, call: DynamicCall<'a>) -> BoxFuture<'a, DynamicReport>;
}
pub trait DynamicEvidenceSink: Send + Sync {
fn deliver<'a>(&'a self, outcome: &'a DynamicOutcome) -> BoxFuture<'a, Result<String, String>>;
}
pub trait Evaluator: Send + Sync {
fn evaluate<'a>(&'a self, cx: &'a CallContext) -> BoxFuture<'a, EvalReport>;
}
pub trait EvidenceSink: Send + Sync {
fn deliver<'a>(&'a self, record: &'a RunRecord) -> BoxFuture<'a, Result<String, String>>;
fn accepts(&self, schema: &str) -> bool {
schema == rustev_contract::schema::RUN
}
fn run_schema(&self) -> &'static str {
rustev_contract::schema::RUN
}
}
#[derive(Clone, Default)]
pub struct CancelSignal(Arc<Inner>);
#[derive(Default)]
struct Inner {
raised: AtomicBool,
state: Mutex<State>,
}
#[derive(Default)]
struct State {
wakers: Vec<Waker>,
children: Vec<Weak<Inner>>,
}
impl std::fmt::Debug for CancelSignal {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_tuple("CancelSignal")
.field(&self.is_raised())
.finish()
}
}
fn lock(m: &Mutex<State>) -> std::sync::MutexGuard<'_, State> {
m.lock().unwrap_or_else(|e| e.into_inner())
}
impl Inner {
fn raise(&self) {
if self.raised.swap(true, Ordering::SeqCst) {
return;
}
let (wakers, children) = {
let mut s = lock(&self.state);
(
std::mem::take(&mut s.wakers),
std::mem::take(&mut s.children),
)
};
for w in wakers {
w.wake();
}
for c in children.iter().filter_map(Weak::upgrade) {
c.raise();
}
}
}
impl CancelSignal {
pub fn new() -> Self {
Self::default()
}
pub fn raise(&self) {
self.0.raise();
}
pub fn is_raised(&self) -> bool {
self.0.raised.load(Ordering::SeqCst)
}
pub fn child(&self) -> CancelSignal {
let child = CancelSignal::new();
{
let mut s = lock(&self.0.state);
if !self.is_raised() {
s.children.retain(|w| w.strong_count() > 0);
s.children.push(Arc::downgrade(&child.0));
return child;
}
}
child.raise();
child
}
pub fn poll_raised(&self, cx: &mut Context<'_>) -> Poll<()> {
if self.is_raised() {
return Poll::Ready(());
}
let mut s = lock(&self.0.state);
if self.is_raised() {
return Poll::Ready(());
}
if !s.wakers.iter().any(|w| w.will_wake(cx.waker())) {
s.wakers.push(cx.waker().clone());
}
Poll::Pending
}
pub fn registered(&self) -> usize {
let s = lock(&self.0.state);
s.wakers.len() + s.children.iter().filter(|w| w.strong_count() > 0).count()
}
pub fn raised(&self) -> impl Future<Output = ()> + Send + '_ {
std::future::poll_fn(move |cx| self.poll_raised(cx))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn raising_a_parent_raises_its_children_and_not_the_reverse() {
let parent = CancelSignal::new();
let a = parent.child();
let b = parent.child();
b.raise();
assert!(b.is_raised() && !a.is_raised() && !parent.is_raised());
parent.raise();
assert!(a.is_raised());
let late = parent.child();
assert!(late.is_raised(), "a child of a raised signal starts raised");
}
}
pub trait AcquisitionSource: Send + Sync {
fn descriptor(&self) -> &SourceDescriptor;
fn cost_bound(&self, request: &AcquisitionRequest) -> CostBound;
fn acquire<'a>(&'a self, call: AcquireCall<'a>) -> BoxFuture<'a, AcquireReport>;
}
pub struct AcquireCall<'a> {
pub operation: &'a str,
pub request: &'a rustev_contract::acquisition::AcquisitionRequest,
pub request_id: &'a rustev_contract::ids::AcquisitionRequestId,
pub attempt_id: &'a str,
pub cancel: CancelSignal,
pub cx: &'a CallContext,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum AcquisitionFailure {
Transient,
Overloaded,
Permanent,
Denied,
}
#[derive(Debug, Clone, PartialEq)]
pub struct AcquireReport {
pub result: Result<Vec<u8>, (AcquisitionFailure, String)>,
pub charge: Charge,
pub cancel: CancelAck,
}