use alloc::boxed::Box;
use core::any::{Any, TypeId};
use core::cell::{Cell, RefCell};
use cubecl_environment::sync::reentrant::{ReentrantMutex, ReentrantMutexGuard};
use hashbrown::HashMap;
use std::sync::Arc;
use crate::device::handle::{DeviceHandleSpec, ServerUtilitiesHandle, ServiceCreationError};
use crate::device::{DeviceId, DeviceService};
pub struct ReentrantMutexDeviceHandle {
lock: DeviceStateLock,
device_id: DeviceId,
type_id: TypeId,
type_name: &'static str,
init: fn(DeviceId) -> ReentrantMutexDeviceState,
}
fn init_state<S: DeviceService>(device_id: DeviceId) -> ReentrantMutexDeviceState {
let service = S::init(device_id);
let utilities = service.utilities();
ReentrantMutexDeviceState {
service: Cell::new(Some(Box::new(service))),
utilities,
}
}
impl DeviceHandleSpec for ReentrantMutexDeviceHandle {
const BLOCKING: bool = true;
fn insert<S: DeviceService>(
device_id: DeviceId,
service: S,
) -> Result<Self, ServiceCreationError> {
Self::insert::<S>(device_id, service).map_err(ServiceCreationError::new)
}
fn new<S: DeviceService>(device_id: DeviceId) -> Self {
Self::locate::<S>(device_id)
}
fn device_id(&self) -> DeviceId {
self.device_id
}
fn utilities(&self) -> ServerUtilitiesHandle {
let state = self.lock.lock.lock();
state
.map
.borrow()
.get(&self.type_id)
.expect("Service not yet initialized — call init() before load()")
.utilities
.clone()
}
fn flush_queue(&self) {}
fn submit_blocking<'a, R: Send, T: FnOnce(&mut dyn Any) -> R + Send + 'a>(
&self,
task: T,
) -> Result<R, super::CallError> {
Ok(self.with_lock(task))
}
fn submit<T: FnOnce(&mut dyn Any) + Send + 'static>(&self, task: T) {
self.with_lock(task);
}
fn exclusive<R: Send, T: FnOnce() -> R + Send>(&self, task: T) -> Result<R, super::CallError> {
let guard = self.lock_device();
let result = task();
core::mem::drop(guard);
Ok(result)
}
}
impl Clone for ReentrantMutexDeviceHandle {
fn clone(&self) -> Self {
Self {
lock: self.lock.clone(),
device_id: self.device_id,
type_id: self.type_id,
type_name: self.type_name,
init: self.init,
}
}
}
pub struct DeviceGuard<'a> {
_guard_mutex: Option<ReentrantMutexGuard<'a, DeviceStateMap>>,
}
impl<'a> Drop for DeviceGuard<'a> {
fn drop(&mut self) {
self._guard_mutex = None;
}
}
impl ReentrantMutexDeviceHandle {
pub fn locate<S: DeviceService>(device: DeviceId) -> Self {
DeviceStateLock::locate::<S>(device)
}
pub fn insert<S: DeviceService>(
device: DeviceId,
state_new: S,
) -> Result<Self, alloc::string::String> {
let lock = Self::locate::<S>(device);
let id = TypeId::of::<S>();
let state = lock.lock.lock.lock();
let mut map = state.map.borrow_mut();
if map.contains_key(&id) {
return Err(alloc::format!(
"A server is still registered for device {device:?}"
));
}
let utilities = state_new.utilities();
let any: Box<dyn Any + Send + 'static> = Box::new(state_new);
map.insert(
id,
ReentrantMutexDeviceState {
service: Cell::new(Some(any)),
utilities,
},
);
core::mem::drop(map);
core::mem::drop(state);
Ok(lock)
}
pub fn lock_device(&self) -> DeviceGuard<'_> {
let state = self.lock.lock.lock();
DeviceGuard {
_guard_mutex: Some(state),
}
}
fn with_lock<R>(&self, f: impl FnOnce(&mut dyn Any) -> R) -> R {
let key = self.type_id;
let state = self.lock.lock.lock();
let entry = {
let mut map = state.map.borrow_mut();
map.entry(key)
.or_insert_with(|| (self.init)(self.device_id))
.service
.take()
};
let entry = entry.unwrap_or_else(|| {
panic!(
"State {} is already borrowed by the current thread",
self.type_name,
)
});
let mut restore = Restore {
map: &state.map,
key,
entry: Some(entry),
};
f(&mut **restore.entry.as_mut().expect("taken back only on drop"))
}
}
struct Restore<'a> {
map: &'a RefCell<HashMap<TypeId, ReentrantMutexDeviceState>>,
key: TypeId,
entry: Option<Box<dyn Any + Send>>,
}
impl Drop for Restore<'_> {
fn drop(&mut self) {
let entry = self.entry.take().expect("restored once");
self.map
.borrow()
.get(&self.key)
.expect("Entry still exists")
.service
.replace(Some(entry));
}
}
static GLOBAL: spin::Mutex<DeviceLocator> = spin::Mutex::new(DeviceLocator { state: None });
#[derive(Default)]
struct DeviceLocatorState {
states: HashMap<DeviceId, DeviceStateLock>,
}
struct DeviceLocator {
state: Option<DeviceLocatorState>,
}
#[derive(Clone)]
struct DeviceStateLock {
lock: Arc<ReentrantMutex<DeviceStateMap>>,
}
struct DeviceStateMap {
map: RefCell<HashMap<TypeId, ReentrantMutexDeviceState>>,
}
struct ReentrantMutexDeviceState {
service: Cell<Option<Box<dyn Any + Send + 'static>>>,
utilities: ServerUtilitiesHandle,
}
impl DeviceStateLock {
fn locate<S: DeviceService>(device: DeviceId) -> ReentrantMutexDeviceHandle {
let mut global = GLOBAL.lock();
let locator_state = match &mut global.state {
Some(state) => state,
None => {
global.state = Some(Default::default());
global.state.as_mut().expect("Just created Option::Some")
}
};
let lock = match locator_state.states.get(&device) {
Some(value) => value.clone(),
None => {
let state = DeviceStateMap::new();
let value = DeviceStateLock {
lock: Arc::new(ReentrantMutex::new(state)),
};
locator_state.states.insert(device, value);
locator_state
.states
.get(&device)
.expect("Just inserted the key/value")
.clone()
}
};
ReentrantMutexDeviceHandle {
lock,
device_id: device,
type_id: TypeId::of::<S>(),
type_name: core::any::type_name::<S>(),
init: init_state::<S>,
}
}
}
impl DeviceStateMap {
fn new() -> Self {
Self {
map: RefCell::new(HashMap::new()),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::sync::Arc;
macro_rules! make_service {
($name:ident) => {
struct $name;
impl DeviceService for $name {
fn init(_: DeviceId) -> Self {
$name
}
fn utilities(&self) -> ServerUtilitiesHandle {
Arc::new(())
}
}
};
}
make_service!(Svc1);
make_service!(Svc2);
make_service!(Svc3);
make_service!(Svc4);
make_service!(Svc5);
make_service!(Svc6);
make_service!(Svc7);
make_service!(Svc8);
#[test]
fn test_many_services_reentrant_resize() {
let device = DeviceId {
type_id: 99,
index_id: 99,
};
let h1 = ReentrantMutexDeviceHandle::new::<Svc1>(device);
h1.with_lock(|_| {
let h2 = ReentrantMutexDeviceHandle::new::<Svc2>(device);
h2.with_lock(|_| {
let h3 = ReentrantMutexDeviceHandle::new::<Svc3>(device);
h3.with_lock(|_| {
let h4 = ReentrantMutexDeviceHandle::new::<Svc4>(device);
h4.with_lock(|_| {
let h5 = ReentrantMutexDeviceHandle::new::<Svc5>(device);
h5.with_lock(|_| {
let h6 = ReentrantMutexDeviceHandle::new::<Svc6>(device);
h6.with_lock(|_| {
let h7 = ReentrantMutexDeviceHandle::new::<Svc7>(device);
h7.with_lock(|_| {
let h8 = ReentrantMutexDeviceHandle::new::<Svc8>(device);
h8.with_lock(|_| {});
});
});
});
});
});
});
});
}
}