#![allow(dead_code)]
use std::collections::HashMap;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum ResourceKind {
Gpu,
CpuBuffer,
FileDescriptor,
Socket,
Other,
}
impl ResourceKind {
#[must_use]
pub fn is_gpu(self) -> bool {
matches!(self, ResourceKind::Gpu)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ResourceState {
Active,
Released,
Suspended,
}
impl ResourceState {
#[must_use]
pub fn is_active(self) -> bool {
matches!(self, ResourceState::Active)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct ResourceHandle {
id: u64,
kind: ResourceKind,
}
impl ResourceHandle {
fn new(id: u64, kind: ResourceKind) -> Self {
Self { id, kind }
}
#[must_use]
pub fn is_valid(&self) -> bool {
self.id != 0
}
#[must_use]
pub fn id(&self) -> u64 {
self.id
}
#[must_use]
pub fn kind(&self) -> ResourceKind {
self.kind
}
}
#[derive(Debug)]
struct ResourceEntry {
state: ResourceState,
kind: ResourceKind,
ref_count: u32,
}
#[derive(Debug, Default)]
pub struct ResourceTracker {
entries: HashMap<u64, ResourceEntry>,
next_id: u64,
}
impl ResourceTracker {
#[must_use]
pub fn new() -> Self {
Self {
entries: HashMap::new(),
next_id: 1, }
}
pub fn acquire(&mut self, kind: ResourceKind) -> ResourceHandle {
let id = self.next_id;
self.next_id += 1;
self.entries.insert(
id,
ResourceEntry {
state: ResourceState::Active,
kind,
ref_count: 1,
},
);
ResourceHandle::new(id, kind)
}
pub fn release(&mut self, handle: ResourceHandle) -> bool {
if let Some(entry) = self.entries.get_mut(&handle.id) {
if entry.ref_count > 1 {
entry.ref_count -= 1;
} else {
entry.state = ResourceState::Released;
self.entries.remove(&handle.id);
}
true
} else {
false
}
}
#[must_use]
pub fn active_count(&self) -> usize {
self.entries
.values()
.filter(|e| e.state.is_active())
.count()
}
#[must_use]
pub fn state(&self, handle: ResourceHandle) -> Option<ResourceState> {
self.entries.get(&handle.id).map(|e| e.state)
}
pub fn suspend(&mut self, handle: ResourceHandle) -> bool {
if let Some(entry) = self.entries.get_mut(&handle.id) {
entry.state = ResourceState::Suspended;
true
} else {
false
}
}
pub fn resume(&mut self, handle: ResourceHandle) -> bool {
if let Some(entry) = self.entries.get_mut(&handle.id) {
entry.state = ResourceState::Active;
true
} else {
false
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_resource_kind_is_gpu() {
assert!(ResourceKind::Gpu.is_gpu());
assert!(!ResourceKind::CpuBuffer.is_gpu());
assert!(!ResourceKind::FileDescriptor.is_gpu());
}
#[test]
fn test_resource_state_is_active() {
assert!(ResourceState::Active.is_active());
assert!(!ResourceState::Released.is_active());
assert!(!ResourceState::Suspended.is_active());
}
#[test]
fn test_handle_is_valid() {
let h = ResourceHandle::new(1, ResourceKind::Gpu);
assert!(h.is_valid());
let null = ResourceHandle::new(0, ResourceKind::Other);
assert!(!null.is_valid());
}
#[test]
fn test_tracker_acquire() {
let mut tracker = ResourceTracker::new();
let h = tracker.acquire(ResourceKind::Gpu);
assert!(h.is_valid());
assert_eq!(tracker.active_count(), 1);
}
#[test]
fn test_tracker_release() {
let mut tracker = ResourceTracker::new();
let h = tracker.acquire(ResourceKind::CpuBuffer);
assert!(tracker.release(h));
assert_eq!(tracker.active_count(), 0);
}
#[test]
fn test_tracker_release_unknown_handle() {
let mut tracker = ResourceTracker::new();
let fake = ResourceHandle::new(9999, ResourceKind::Other);
assert!(!tracker.release(fake));
}
#[test]
fn test_tracker_active_count_multiple() {
let mut tracker = ResourceTracker::new();
let _h1 = tracker.acquire(ResourceKind::Gpu);
let _h2 = tracker.acquire(ResourceKind::CpuBuffer);
let h3 = tracker.acquire(ResourceKind::Socket);
assert_eq!(tracker.active_count(), 3);
tracker.release(h3);
assert_eq!(tracker.active_count(), 2);
}
#[test]
fn test_tracker_state_active() {
let mut tracker = ResourceTracker::new();
let h = tracker.acquire(ResourceKind::Gpu);
assert_eq!(tracker.state(h), Some(ResourceState::Active));
}
#[test]
fn test_tracker_state_none_after_release() {
let mut tracker = ResourceTracker::new();
let h = tracker.acquire(ResourceKind::Gpu);
tracker.release(h);
assert_eq!(tracker.state(h), None);
}
#[test]
fn test_tracker_suspend_resume() {
let mut tracker = ResourceTracker::new();
let h = tracker.acquire(ResourceKind::Gpu);
assert!(tracker.suspend(h));
assert_eq!(tracker.state(h), Some(ResourceState::Suspended));
assert!(!tracker.state(h).expect("state should exist").is_active());
assert!(tracker.resume(h));
assert_eq!(tracker.state(h), Some(ResourceState::Active));
}
#[test]
fn test_handle_kind_accessor() {
let mut tracker = ResourceTracker::new();
let h = tracker.acquire(ResourceKind::FileDescriptor);
assert_eq!(h.kind(), ResourceKind::FileDescriptor);
}
#[test]
fn test_tracker_ids_are_unique() {
let mut tracker = ResourceTracker::new();
let h1 = tracker.acquire(ResourceKind::Gpu);
let h2 = tracker.acquire(ResourceKind::Gpu);
assert_ne!(h1.id(), h2.id());
}
}