use alloc::vec::Vec;
use ruda_core::kernel::{BufferIOAttr, KernelIo};
use super::{Binding, KernelArguments, RudaCount, ServerError, WriteScope};
use crate::runtime::id::KernelId;
#[derive(Debug)]
pub struct KernelExecution {
kernel: KernelId,
buffers: Vec<Binding>,
tensor_maps: Vec<Binding>,
declared_io: KernelIo,
count: Option<Binding>,
reads: Vec<Binding>,
writes: Vec<Binding>,
write_scope: Option<WriteScope>,
armed: bool,
}
impl KernelExecution {
pub fn new(kernel: KernelId, args: &KernelArguments, count: &RudaCount) -> Self {
Self::from_declared_bindings(
kernel,
args.buffers.clone(),
args.tensor_maps.iter().map(|map| map.binding.clone()).collect(),
args.declared_io.clone(),
match count { RudaCount::Dynamic(binding) => Some(binding.clone()), _ => None },
)
}
pub fn from_bindings(
kernel: KernelId,
buffers: Vec<Binding>,
tensor_maps: Vec<Binding>,
count: Option<Binding>,
) -> Self {
Self::from_declared_bindings(kernel, buffers, tensor_maps, KernelIo::default(), count)
}
pub fn from_declared_bindings(
kernel: KernelId,
buffers: Vec<Binding>,
tensor_maps: Vec<Binding>,
declared_io: KernelIo,
count: Option<Binding>,
) -> Self {
let mut execution = Self {
kernel,
buffers,
tensor_maps,
declared_io,
count,
reads: Vec::new(),
writes: Vec::new(),
write_scope: None,
armed: true,
};
execution.set_io(None);
execution
}
pub fn set_io(&mut self, io: Option<&KernelIo>) {
assert!(self.write_scope.is_none(), "Cannot change I/O after entering a write scope");
self.reads.clear();
self.writes.clear();
for (bindings, accesses, declared) in [
(&self.buffers, io.map(|io| &io.buffers), &self.declared_io.buffers),
(&self.tensor_maps, io.map(|io| &io.tensor_maps), &self.declared_io.tensor_maps),
] {
for (index, binding) in bindings.iter().enumerate() {
let access = accesses.and_then(|accesses| accesses.get(index))
.or_else(|| declared.get(index))
.copied().unwrap_or(BufferIOAttr::ReadWrite);
if access.reads() { self.reads.push(binding.clone()); }
if access.writes() { self.writes.push(binding.clone()); }
}
}
self.reads.extend(self.count.iter().cloned());
}
pub fn reads(&self) -> &[Binding] { &self.reads }
pub fn writes(&self) -> &[Binding] {
self.write_scope.as_ref().map_or(self.writes.as_slice(), WriteScope::bindings)
}
pub fn check(&mut self) -> Result<(), ServerError> {
assert!(self.write_scope.is_none(), "Inputs must be checked before claiming outputs");
if let Some(error) = Binding::propagate_failure(self.kernel.clone(), self.reads.iter(), &self.writes) {
self.armed = false;
return Err(error);
}
if let Err(error) = Binding::check_all(self.reads.iter()) {
return Err(self.fail(error));
}
Ok(())
}
pub fn enter(&mut self) {
if self.armed && self.write_scope.is_none() {
self.write_scope = Some(WriteScope::new(core::mem::take(&mut self.writes)));
}
}
pub fn begin_submission(&mut self) -> Result<Option<WriteScope>, ServerError> {
if !self.armed { return Ok(None); }
self.check()?;
self.enter();
self.armed = false;
Ok(self.write_scope.take())
}
pub fn fail(&mut self, error: ServerError) -> ServerError {
if self.armed {
self.enter();
self.armed = false;
if let Some(scope) = self.write_scope.take() {
return scope.fail(error);
}
}
error
}
pub fn complete(mut self) {
if self.armed {
self.enter();
if let Some(scope) = self.write_scope.take() { scope.complete(); }
}
self.armed = false;
}
pub fn skip(&mut self) { self.armed = false; }
pub fn cancel(mut self) { self.armed = false; }
pub fn finish(mut self, result: Result<(), ServerError>) -> Result<(), ServerError> {
match result {
Ok(()) => { self.complete(); Ok(()) }
Err(error) => Err(self.fail(error)),
}
}
}
impl Drop for KernelExecution {
fn drop(&mut self) {
if self.armed && self.write_scope.is_none() {
Binding::fail_all(self.writes.iter(), ServerError::TornDown);
}
}
}