mod base;
pub use base::*;
use crate::device::{DeviceId, DeviceService, ServerUtilitiesHandle, ServiceId};
use core::any::Any;
#[cfg(feature = "std")]
#[allow(dead_code)]
mod channel;
#[allow(dead_code)]
mod mutex;
#[cfg(feature = "std")]
#[allow(dead_code)]
mod reentrant;
#[cfg(all(feature = "std", multi_threading))]
type Inner = channel::ChannelDeviceHandle;
#[cfg(all(feature = "std", not(multi_threading)))]
type Inner = reentrant::ReentrantMutexDeviceHandle;
#[cfg(all(not(feature = "std"), not(multi_threading)))]
type Inner = mutex::MutexDeviceHandle;
pub struct DeviceHandle<S: ?Sized, I: DeviceHandleSpec = Inner> {
handle: I,
service: ServiceId,
cast: fn(&mut dyn Any) -> &mut S,
}
impl<S: ?Sized, I: DeviceHandleSpec> Clone for DeviceHandle<S, I> {
fn clone(&self) -> Self {
Self {
handle: self.handle.clone(),
service: self.service,
cast: self.cast,
}
}
}
fn downcast<S: 'static>(state: &mut dyn Any) -> &mut S {
state
.downcast_mut::<S>()
.expect("State type mismatch in the device registry")
}
#[allow(missing_docs)]
impl<S: DeviceService, I: DeviceHandleSpec> DeviceHandle<S, I> {
pub fn insert(device_id: DeviceId, service: S) -> Result<Self, ServiceCreationError> {
Ok(Self {
handle: I::insert::<S>(device_id, service)?,
service: ServiceId::of::<S>(device_id),
cast: downcast::<S>,
})
}
pub fn new(device_id: DeviceId) -> Self {
Self {
handle: I::new::<S>(device_id),
service: ServiceId::of::<S>(device_id),
cast: downcast::<S>,
}
}
}
#[allow(missing_docs)]
impl<S: ?Sized + 'static, I: DeviceHandleSpec> DeviceHandle<S, I> {
pub const fn is_blocking() -> bool {
I::BLOCKING
}
pub fn seen_as<T: ?Sized>(self, cast: fn(&mut dyn Any) -> &mut T) -> DeviceHandle<T, I> {
DeviceHandle {
handle: self.handle,
service: self.service,
cast,
}
}
pub fn device_id(&self) -> DeviceId {
self.handle.device_id()
}
pub fn service_id(&self) -> ServiceId {
self.service
}
pub fn utilities(&self) -> ServerUtilitiesHandle {
self.handle.utilities()
}
pub fn submit_blocking<'a, R: Send, T: FnOnce(&mut S) -> R + Send + 'a>(
&self,
task: T,
) -> Result<R, CallError> {
let cast = self.cast;
self.handle.submit_blocking(move |state| task(cast(state)))
}
pub fn submit<T: FnOnce(&mut S) + Send + 'static>(&self, task: T) {
let cast = self.cast;
self.handle.submit(move |state| task(cast(state)))
}
pub fn flush_queue(&self) {
self.handle.flush_queue();
}
pub fn exclusive<R: Send, T: FnOnce() -> R + Send>(&self, task: T) -> Result<R, CallError> {
self.handle.exclusive(task)
}
pub fn shutdown(device_id: DeviceId) {
I::shutdown(device_id)
}
}
#[cfg(test)]
struct ShutdownGuard {
device_id: DeviceId,
shutdown: fn(DeviceId),
}
#[cfg(test)]
impl Drop for ShutdownGuard {
fn drop(&mut self) {
(self.shutdown)(self.device_id);
}
}
#[cfg(test)]
fn next_test_device_id() -> DeviceId {
use core::sync::atomic::{AtomicU16, Ordering};
static NEXT: AtomicU16 = AtomicU16::new(0);
DeviceId {
type_id: 0,
index_id: NEXT.fetch_add(1, Ordering::Relaxed),
}
}
#[cfg(test)]
pub(crate) struct DeviceFixture<H> {
handle: H,
_guard: ShutdownGuard,
device_id: DeviceId,
}
#[cfg(test)]
impl<H> DeviceFixture<H> {
pub(crate) fn new(build: fn(DeviceId) -> H, shutdown: fn(DeviceId)) -> Self {
let device_id = next_test_device_id();
Self {
handle: build(device_id),
_guard: ShutdownGuard {
device_id,
shutdown,
},
device_id,
}
}
pub(crate) fn device_id(&self) -> DeviceId {
self.device_id
}
}
#[cfg(test)]
impl<H> core::ops::Deref for DeviceFixture<H> {
type Target = H;
fn deref(&self) -> &Self::Target {
&self.handle
}
}
#[cfg(test)]
mod tests_channel {
type DeviceHandle<S> = super::DeviceHandle<S, channel::ChannelDeviceHandle>;
include!("./tests.rs");
include!("./tests_recursive.rs");
}
#[cfg(test)]
mod tests_mutex {
type DeviceHandle<S> = super::DeviceHandle<S, mutex::MutexDeviceHandle>;
include!("./tests.rs");
}
#[cfg(test)]
mod tests_reentrant {
type DeviceHandle<S> = super::DeviceHandle<S, reentrant::ReentrantMutexDeviceHandle>;
include!("./tests.rs");
include!("./tests_recursive.rs");
}