hermit/syscalls/
condvar.rs

1// The implementation is inspired by Andrew D. Birrell's paper
2// "Implementing Condition Variables with Semaphores"
3
4use alloc::boxed::Box;
5use core::sync::atomic::{AtomicIsize, Ordering};
6use core::{mem, ptr};
7
8use crate::synch::semaphore::Semaphore;
9
10struct CondQueue {
11	counter: AtomicIsize,
12	sem1: Semaphore,
13	sem2: Semaphore,
14}
15
16impl CondQueue {
17	pub fn new() -> Self {
18		CondQueue {
19			counter: AtomicIsize::new(0),
20			sem1: Semaphore::new(0),
21			sem2: Semaphore::new(0),
22		}
23	}
24}
25
26#[hermit_macro::system]
27#[unsafe(no_mangle)]
28pub unsafe extern "C" fn sys_destroy_queue(ptr: usize) -> i32 {
29	unsafe {
30		let id = ptr::with_exposed_provenance_mut::<usize>(ptr);
31		if id.is_null() {
32			debug!("sys_wait: invalid address to condition variable");
33			return -1;
34		}
35
36		if *id != 0 {
37			let cond = Box::from_raw(ptr::with_exposed_provenance_mut::<CondQueue>(*id));
38			mem::drop(cond);
39		}
40
41		0
42	}
43}
44
45#[hermit_macro::system]
46#[unsafe(no_mangle)]
47pub unsafe extern "C" fn sys_notify(ptr: usize, count: i32) -> i32 {
48	unsafe {
49		let id = ptr::with_exposed_provenance::<usize>(ptr);
50
51		if id.is_null() {
52			// invalid argument
53			debug!("sys_notify: invalid address to condition variable");
54			return -1;
55		}
56
57		if *id == 0 {
58			debug!("sys_notify: invalid reference to condition variable");
59			return -1;
60		}
61
62		let cond = &mut *(ptr::with_exposed_provenance_mut::<CondQueue>(*id));
63
64		if count < 0 {
65			// Wake up all task that has been waiting for this condition variable
66			while cond.counter.load(Ordering::SeqCst) > 0 {
67				cond.counter.fetch_sub(1, Ordering::SeqCst);
68				cond.sem1.release();
69				cond.sem2.acquire(None);
70			}
71		} else {
72			for _ in 0..count {
73				cond.counter.fetch_sub(1, Ordering::SeqCst);
74				cond.sem1.release();
75				cond.sem2.acquire(None);
76			}
77		}
78
79		0
80	}
81}
82
83#[hermit_macro::system]
84#[unsafe(no_mangle)]
85pub unsafe extern "C" fn sys_init_queue(ptr: usize) -> i32 {
86	unsafe {
87		let id = ptr::with_exposed_provenance_mut::<usize>(ptr);
88		if id.is_null() {
89			debug!("sys_init_queue: invalid address to condition variable");
90			return -1;
91		}
92
93		if *id == 0 {
94			debug!("Create condition variable queue");
95			let queue = Box::new(CondQueue::new());
96			*id = Box::into_raw(queue) as usize;
97		}
98
99		0
100	}
101}
102
103#[hermit_macro::system]
104#[unsafe(no_mangle)]
105pub unsafe extern "C" fn sys_add_queue(ptr: usize, timeout_ns: i64) -> i32 {
106	unsafe {
107		let id = ptr::with_exposed_provenance_mut::<usize>(ptr);
108		if id.is_null() {
109			debug!("sys_add_queue: invalid address to condition variable");
110			return -1;
111		}
112
113		if *id == 0 {
114			debug!("Create condition variable queue");
115			let queue = Box::new(CondQueue::new());
116			*id = Box::into_raw(queue) as usize;
117		}
118
119		if timeout_ns <= 0 {
120			let cond = &mut *(ptr::with_exposed_provenance_mut::<CondQueue>(*id));
121			cond.counter.fetch_add(1, Ordering::SeqCst);
122
123			0
124		} else {
125			error!("Conditional variables with timeout is currently not supported");
126
127			-1
128		}
129	}
130}
131
132#[hermit_macro::system]
133#[unsafe(no_mangle)]
134pub unsafe extern "C" fn sys_wait(ptr: usize) -> i32 {
135	unsafe {
136		let id = ptr::with_exposed_provenance_mut::<usize>(ptr);
137		if id.is_null() {
138			debug!("sys_wait: invalid address to condition variable");
139			return -1;
140		}
141
142		if *id == 0 {
143			error!("sys_wait: Unable to determine condition variable");
144			return -1;
145		}
146
147		let cond = &mut *(ptr::with_exposed_provenance_mut::<CondQueue>(*id));
148		cond.sem1.acquire(None);
149		cond.sem2.release();
150
151		0
152	}
153}