1use alloc::boxed::Box;
11use core::sync::atomic::{AtomicI32, AtomicU32, Ordering};
12
13use rt::os::*;
14
15use crate::{Date, E_WAKE, Event};
16
17const FUTEX_WAIT: usize = 0;
18const FUTEX_WAKE: usize = 1;
19const FUTEX_PRIVATE: usize = 128;
20const EINTR: isize = 4;
21const CLONE_THREAD_FLAGS: usize = 0x100 | 0x200 | 0x400 | 0x800 | 0x10000 | 0x40000 | 0x100000 | 0x200000;
23const GUARD_SIZE: usize = 4096;
24const ALT_SIZE: usize = 65536;
27const THREAD_STACK_DEF: usize = 0x10_0000; const THREAD_STACK_MAX: usize = 0x1000_0000; type Start = Box<dyn FnOnce() + Send + 'static>;
33
34#[repr(C)] #[cfg_attr(jfgui_glibc, allow(dead_code))]
36struct ThreadRec {
37 tid: AtomicU32, _pad: u32,
39 base: usize,
40 size: usize,
41 start: *mut Start,
42 _rsv: [usize; 4],
43}
44
45#[cfg(not(jfgui_glibc))] core::arch::global_asm!(
49 ".globl jfgui_rs_clone",
50 "jfgui_rs_clone:",
51 " mov r9, rsi",
52 " mov rdx, rsi",
53 " mov r10, rsi",
54 " xor r8d, r8d",
55 " mov eax, {nr}",
56 " syscall",
57 " test rax, rax",
58 " jz 2f",
59 " ret",
60 "2:",
61 " xor ebp, ebp",
62 " mov rdi, r9",
63 " call {main}",
64 " ud2",
65 main = sym thread_main,
66 nr = const SYS_CLONE,
67);
68
69#[cfg(not(jfgui_glibc))] unsafe extern "C" {
70 fn jfgui_rs_clone(flags: usize, rec: *mut u8) -> isize;
71}
72
73#[cfg(not(jfgui_glibc))] extern "C" fn thread_main(rec: *mut ThreadRec) -> ! {
74 let f = unsafe {
78 let ss = [(*rec).base + GUARD_SIZE, 0, ALT_SIZE];
79 syscall3(SYS_SIGALTSTACK, ss.as_ptr() as usize, 0, 0);
80 Box::from_raw((*rec).start)
81 };
82 f();
83 unsafe { syscall3(SYS_EXIT, 0, 0, 0) };
85 loop {}
86}
87
88pub struct Thread(*mut ThreadRec);
90
91unsafe impl Send for Thread {}
93
94pub fn thread_spawn(stack: usize, f: Start) -> Option<Thread> {
96 let size = if stack == 0 { THREAD_STACK_DEF } else { stack.min(THREAD_STACK_MAX) };
97 let size = ((size + 4095) & !4095) + 2 * GUARD_SIZE + ALT_SIZE; #[cfg(jfgui_glibc)] let size = size + TLS_ROOM;
98 unsafe {
102 let base = syscall6(SYS_MMAP, 0, size, 3, 0x02 | 0x20 | 0x4000 | 0x20000, usize::MAX, 0);
103 if base < 0 {
104 return None;
105 }
106 let base = base as usize;
107 if syscall3(SYS_MPROTECT, base, GUARD_SIZE, 0) < 0
108 || syscall3(SYS_MPROTECT, base + GUARD_SIZE + ALT_SIZE, GUARD_SIZE, 0) < 0 {
109 syscall3(SYS_MUNMAP, base, size, 0);
110 return None;
111 }
112 let rec = (base + size - 64) as *mut ThreadRec;
113 let start = Box::into_raw(Box::new(f));
114 rec.write(ThreadRec { tid: AtomicU32::new(0), _pad: 0, base, size, start, _rsv: [0; 4] });
115 if jfgui_rs_clone(CLONE_THREAD_FLAGS, rec as *mut u8) < 0 {
116 drop(Box::from_raw(start));
117 syscall3(SYS_MUNMAP, base, size, 0);
118 return None;
119 }
120 Some(Thread(rec))
121 }
122}
123
124#[cfg(not(jfgui_glibc))] impl Thread {
125 pub fn join(self) {
127 unsafe {
130 let rec = &*self.0;
131 loop {
132 let t = rec.tid.load(Ordering::SeqCst);
133 if t == 0 {
134 break;
135 }
136 syscall6(SYS_FUTEX, &rec.tid as *const AtomicU32 as usize, FUTEX_WAIT, t as usize, 0, 0, 0);
138 }
139 let (base, size) = (rec.base, rec.size);
140 syscall3(SYS_MUNMAP, base, size, 0);
141 }
142 }
143}
144
145pub fn thread_yield() {
147 rt::os::yield_now()
148}
149
150struct SemInner {
153 count: AtomicI32, waiters: AtomicU32,
155}
156
157pub struct Sem(Box<SemInner>);
159
160impl Sem {
161 pub fn new(initial: u32) -> Option<Sem> {
162 Some(Sem(Box::new(SemInner { count: AtomicI32::new(initial as i32), waiters: AtomicU32::new(0) })))
163 }
164
165 pub fn post(&self, n: u32) {
167 if n == 0 {
168 return;
169 }
170 let add = n.min(i32::MAX as u32) as i32;
172 let _ = self.0.count.try_update(Ordering::SeqCst, Ordering::SeqCst, |c| Some(c.saturating_add(add)));
173 if self.0.waiters.load(Ordering::SeqCst) != 0 {
174 unsafe {
176 syscall6(SYS_FUTEX, self.0.count.as_ptr() as usize, FUTEX_WAKE | FUTEX_PRIVATE, n as usize, 0, 0, 0)
177 };
178 }
179 }
180
181 pub fn wait(&self, ms: i32) -> bool {
183 let deadline = if ms > 0 { Some(super::time_us() + ms as u64 * 1000) } else { None };
184 loop {
185 let mut c = self.0.count.load(Ordering::SeqCst);
186 while c > 0 {
187 match self.0.count.compare_exchange(c, c - 1, Ordering::SeqCst, Ordering::SeqCst) {
188 Ok(_) => return true,
189 Err(v) => c = v,
190 }
191 }
192 if ms == 0 {
193 return false;
194 }
195 let mut ts = [0i64; 2];
196 let tsp = match deadline {
197 Some(d) => {
198 let now = super::time_us();
199 if now >= d {
200 return false;
201 }
202 let left = d - now;
203 ts[0] = (left / 1_000_000) as i64;
204 ts[1] = (left % 1_000_000 * 1000) as i64;
205 ts.as_ptr() as usize
206 }
207 None => 0,
208 };
209 self.0.waiters.fetch_add(1, Ordering::SeqCst);
210 unsafe { syscall6(SYS_FUTEX, self.0.count.as_ptr() as usize, FUTEX_WAIT | FUTEX_PRIVATE, 0, tsp, 0, 0) };
212 self.0.waiters.fetch_sub(1, Ordering::SeqCst);
213 }
214 }
215}
216
217static WAKE_FD: AtomicI32 = AtomicI32::new(-1);
220static WAKE_FLAG: AtomicU32 = AtomicU32::new(0);
221
222pub(crate) fn wake_init() {
224 let fd = unsafe { syscall3(SYS_EVENTFD2, 0, 0x80000 | 0x800, 0) };
226 WAKE_FD.store(if fd < 0 { -1 } else { fd as i32 }, Ordering::SeqCst);
227}
228
229pub(crate) fn wake_fd() -> i32 {
230 WAKE_FD.load(Ordering::SeqCst)
231}
232
233pub(crate) fn wake_consume() -> Event {
238 let fd = WAKE_FD.load(Ordering::SeqCst);
239 if fd >= 0 {
240 let mut v = 0u64;
241 unsafe { syscall3(SYS_READ, fd as usize, &mut v as *mut u64 as usize, 8) };
243 }
244 WAKE_FLAG.store(0, Ordering::SeqCst);
245 Event { typ: E_WAKE, ..Default::default() }
246}
247
248pub fn wake() {
250 if WAKE_FLAG.swap(1, Ordering::SeqCst) != 0 {
251 return; }
253 let fd = WAKE_FD.load(Ordering::SeqCst);
254 if fd < 0 {
255 return;
256 }
257 let one = 1u64;
258 loop {
259 let r = unsafe { syscall3(SYS_WRITE, fd as usize, &one as *const u64 as usize, 8) };
261 if r != -EINTR {
262 break;
263 }
264 }
265}
266
267pub fn wait_events(deadline_us: u64, on_event: &mut dyn FnMut(&Event)) -> u32 {
270 loop {
271 let ms: i32 = if deadline_us == u64::MAX {
272 -1
273 } else if deadline_us == 0 {
274 0
275 } else {
276 let now = super::time_us();
277 if now >= deadline_us { 0 } else { ((deadline_us - now + 999) / 1000).min(0x7FFF_FFFF) as i32 }
278 };
279 let mut pfd = [(WAKE_FD.load(Ordering::SeqCst) as u32 as i64) | (1i64 << 32)];
281 let r = unsafe { rt::os::poll(pfd.as_mut_ptr() as usize, 1, ms) };
283 if r == -EINTR {
284 continue;
285 }
286 if r > 0 && (pfd[0] >> 48) & 1 != 0 {
287 on_event(&wake_consume());
288 return 1;
289 }
290 return 0;
291 }
292}
293
294pub fn cpu_count() -> u32 {
298 let mut mask = [0u64; 16]; let r = unsafe { syscall3(SYS_SCHED_GETAFFINITY, 0, 128, mask.as_mut_ptr() as usize) };
301 let mut n = 0;
302 if r > 0 {
303 for m in &mask[..(r as usize).min(128) / 8] {
304 n += m.count_ones();
305 }
306 }
307 n.max(1)
308}
309
310pub fn mem_total() -> u64 {
312 let mut si = [0u64; 14]; if unsafe { syscall3(SYS_SYSINFO, si.as_mut_ptr() as usize, 0, 0) } != 0 {
315 return 0;
316 }
317 let unit = si[13] as u32; if unit == 0 { si[4] } else { si[4] * unit as u64 } }
320
321pub fn date() -> (Date, bool) {
323 let mut ts = [0i64; 2];
324 unsafe { syscall3(SYS_CLOCK_GETTIME, 0, ts.as_mut_ptr() as usize, 0) };
326 let secs = ts[0].max(0) as u64;
327 let days = secs / 86400;
328 let sod = secs % 86400;
329 let z = days + 719468;
331 let era = z / 146097;
332 let doe = z - era * 146097;
333 let yoe = (doe - doe / 1460 + doe / 36524 - doe / 146096) / 365;
334 let doy = doe - (365 * yoe + yoe / 4 - yoe / 100);
335 let mp = (5 * doy + 2) / 153;
336 let day = doy - (153 * mp + 2) / 5 + 1;
337 let month = if mp < 10 { mp + 3 } else { mp - 9 };
338 let year = yoe + era * 400 + (month <= 2) as u64;
339 let d = Date {
340 year: year as u16,
341 month: month as u16,
342 day: day as u16,
343 hour: (sod / 3600) as u16,
344 min: (sod % 3600 / 60) as u16,
345 sec: (sod % 60) as u16,
346 ms: (ts[1] / 1_000_000) as u16,
347 wday: ((days + 4) % 7) as u16,
348 };
349 (d, true)
350}
351
352#[cfg(jfgui_glibc)]
363const TLS_ROOM: usize = 16384;
364
365#[cfg(jfgui_glibc)]
366extern "C" fn thread_main(rec: *mut u8) -> *mut u8 {
367 let rec = rec as *mut ThreadRec;
368 let f = unsafe {
371 let ss = [(*rec).base + GUARD_SIZE, 0, ALT_SIZE];
372 syscall3(SYS_SIGALTSTACK, ss.as_ptr() as usize, 0, 0);
373 Box::from_raw((*rec).start)
374 };
375 f();
376 core::ptr::null_mut()
377}
378
379#[cfg(jfgui_glibc)]
381unsafe fn jfgui_rs_clone(_flags: usize, rec: *mut u8) -> isize {
382 use super::glibc::*;
383 let r = rec as *mut ThreadRec;
384 let mut attr = PthreadAttr([0; 56]);
385 unsafe {
388 let lo = (*r).base + 2 * GUARD_SIZE + ALT_SIZE;
389 if pthread_attr_init(&mut attr) != 0 {
390 return -1;
391 }
392 let e = pthread_attr_setstack(&mut attr, lo as *mut u8, rec as usize - lo);
393 let e = if e != 0 { e } else { pthread_create(&mut (*r)._rsv[0], &attr, thread_main, rec) };
394 pthread_attr_destroy(&mut attr);
395 if e != 0 { -1 } else { 0 }
396 }
397}
398
399#[cfg(jfgui_glibc)]
400impl Thread {
401 pub fn join(self) {
403 unsafe {
406 let rec = &*self.0;
407 super::glibc::pthread_join(rec._rsv[0], core::ptr::null_mut());
408 let (base, size) = (rec.base, rec.size);
409 syscall3(SYS_MUNMAP, base, size, 0);
410 }
411 }
412}