rustubs/proc/sync/
semaphore.rs

1use super::L2_GUARD;
2use crate::arch::x86_64::is_int_enabled;
3use crate::proc::task::{Task, TaskId};
4use crate::Scheduler;
5use alloc::collections::VecDeque;
6use core::sync::atomic::Ordering;
7use core::{cell::SyncUnsafeCell, sync::atomic::AtomicU64};
8
9pub trait Semaphore<T, E>
10where
11	T: ResourceMan<E>,
12	E: Copy + Clone,
13{
14	/// Probeer (try): the consumer end, tries to get resource, blocks on empty
15	fn p(&self) -> Option<E>;
16	/// Verhoog (increment): the producer end, increments the resource and must
17	/// not block
18	fn v(&self, e: E);
19	/// must not block
20	fn is_empty(&self) -> bool;
21	/// must not block
22	fn is_full(&self) -> bool;
23	/// if the semaphore is also to be accessed in the epilogue level, the L2
24	/// lock is already acquired.
25	unsafe fn p_unguarded(&self) -> Option<E> { return None; }
26	#[allow(unused_variables)]
27	unsafe fn v_unguarded(&self, e: E) {}
28}
29
30/// wherever resoure management behind semaphore must provide get and insert
31/// function. They do not need to be atomic. Normaly they only needs to be
32/// wrappers for e.g. enque and deque.
33pub trait ResourceMan<E>
34where E: Copy + Clone
35{
36	fn get_resource(&mut self) -> Option<E>;
37	fn insert_resource(&mut self, e: E) -> bool;
38}
39
40impl<E> ResourceMan<E> for VecDeque<E>
41where E: Copy + Clone
42{
43	fn get_resource(&mut self) -> Option<E> { self.pop_front() }
44	fn insert_resource(&mut self, e: E) -> bool {
45		self.push_back(e);
46		// well, it seems that the vectorDeque has no failing case for
47		// push_back. TODO set a hard capacity limit
48		true
49	}
50}
51
52pub struct SleepSemaphore<T> {
53	pub resource_pool: SyncUnsafeCell<T>,
54	pub sema: AtomicU64,
55	// the wait_room must be synchronized at level 3 (or???)
56	// TODO make a type alias for VecDeque<TaskId>
57	pub wait_room: SyncUnsafeCell<VecDeque<TaskId>>,
58}
59
60impl<T> SleepSemaphore<T> {
61	pub const fn new(t: T) -> Self {
62		Self {
63			resource_pool: SyncUnsafeCell::new(t),
64			sema: AtomicU64::new(0),
65			wait_room: SyncUnsafeCell::new(VecDeque::new()),
66		}
67	}
68
69	unsafe fn wait(&self) {
70		let wq = &mut *self.wait_room.get();
71		Task::curr_wait_in(wq);
72		debug_assert!(is_int_enabled());
73		Scheduler::yield_cpu();
74	}
75
76	unsafe fn wakeup_one(&self) {
77		let wq = &mut *self.wait_room.get();
78		if let Some(t) = wq.pop_front() {
79			t.get_task_ref_mut().wakeup();
80		}
81	}
82
83	pub unsafe fn get_pool_mut(&self) -> &mut T {
84		&mut (*self.resource_pool.get())
85	}
86}
87
88impl<T, E> Semaphore<T, E> for SleepSemaphore<T>
89where
90	T: ResourceMan<E>,
91	E: Copy + Clone,
92{
93	fn p(&self) -> Option<E> {
94		L2_GUARD.lock();
95		unsafe { return self.p_unguarded() };
96	}
97	fn v(&self, e: E) {
98		L2_GUARD.lock();
99		unsafe { self.v_unguarded(e) };
100	}
101	unsafe fn p_unguarded(&self) -> Option<E> {
102		let mut c: u64;
103		loop {
104			c = self.sema.load(Ordering::Relaxed);
105			if c == 0 {
106				unsafe { self.wait() };
107				continue;
108			}
109
110			let r = self.sema.compare_exchange(
111				c,
112				c - 1,
113				Ordering::Acquire,
114				Ordering::Relaxed,
115			);
116			match r {
117				Ok(_) => break,
118				Err(_) => continue,
119			}
120		}
121
122		let thing = (*self.resource_pool.get()).get_resource();
123		return thing;
124	}
125	unsafe fn v_unguarded(&self, e: E) {
126		(*self.resource_pool.get()).insert_resource(e);
127		let _ = self.sema.fetch_add(1, Ordering::SeqCst);
128		self.wakeup_one();
129	}
130	fn is_empty(&self) -> bool { todo!() }
131	fn is_full(&self) -> bool { todo!() }
132}