Skip to main content

plat/
linux_thread.rs

1// linux_thread -- Linux threads (clone + futex; linked against glibc: pthread_create, at the end of this file), semaphores, the wake-up of the UI thread,
2// system information, the date                       [src/plat/linux/lx_thread.s, plat_linux.s]
3//
4// A thread's stack is an mmap with a PROT_NONE guard page below it; the thread record sits
5// at its top.  The kernel clears the record's tid word and wakes it (a shared futex wake)
6// when the thread ends, which is what join waits for.  Semaphores are process-private
7// futex counters.  The wake-up is an eventfd: the display backends poll it next to their
8// socket, wait_events polls it alone (headless).
9
10use 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;
21// CLONE_VM | FS | FILES | SIGHAND | THREAD | SYSVSEM | PARENT_SETTID | CHILD_CLEARTID
22const CLONE_THREAD_FLAGS: usize = 0x100 | 0x200 | 0x400 | 0x800 | 0x10000 | 0x40000 | 0x100000 | 0x200000;
23const GUARD_SIZE: usize = 4096;
24/// each thread's alternate signal stack (the crash handler runs on it: a stack overflow of the
25/// thread still gets its report), between two guard pages below the thread's stack
26const ALT_SIZE: usize = 65536;
27const THREAD_STACK_DEF: usize = 0x10_0000; // 1 MB
28const THREAD_STACK_MAX: usize = 0x1000_0000; // 256 MB
29
30// ---------------------------------------------------------------- threads
31
32type Start = Box<dyn FnOnce() + Send + 'static>;
33
34/// the thread record at the top of the stack mapping (64 bytes)
35#[repr(C)] #[cfg_attr(jfgui_glibc, allow(dead_code))]
36struct ThreadRec {
37    tid: AtomicU32, // set by clone, cleared by the kernel when the thread ends
38    _pad: u32,
39    base: usize,
40    size: usize,
41    start: *mut Start,
42    _rsv: [usize; 4],
43}
44
45// clone(flags, stack = rec, parent_tid = &rec.tid, child_tid = &rec.tid, tls = 0; rdx, r10, r8 in
46// x86-64's order, rt::os::clone_args): the child starts on its own stack (rsp = rec,
47// 16-aligned) with the record in r9 and calls thread_main(rec)
48#[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    // SAFETY: the alternate signal stack thread_spawn reserved in this thread's mapping (the
75    // kernel gives a new thread none); rec is the record thread_spawn wrote, its closure is
76    // taken over once.
77    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    // SAFETY: ends this thread only (the kernel clears and wakes rec.tid).
84    unsafe { syscall3(SYS_EXIT, 0, 0, 0) };
85    loop {}
86}
87
88/// a thread started by thread_spawn; join it to wait for its end (and free its stack)
89pub struct Thread(*mut ThreadRec);
90
91// SAFETY: the record is only read by join, after the thread ended or through atomics.
92unsafe impl Send for Thread {}
93
94/// run f on a new thread with stack bytes of stack (0 = 1 MB)  [plat_thread_create]
95pub 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    // SAFETY: a fresh anonymous mapping (PROT_READ | PROT_WRITE; MAP_PRIVATE | MAP_ANONYMOUS |
99    // MAP_NORESERVE | MAP_STACK): from the bottom a guard page, the alternate signal stack,
100    // a guard page, the stack.
101    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    /// wait until the thread has ended, then free its stack  [plat_thread_join]
126    pub fn join(self) {
127        // SAFETY: the record stays mapped until the munmap below, which runs after the
128        // kernel cleared the tid (the thread no longer uses its stack).
129        unsafe {
130            let rec = &*self.0;
131            loop {
132                let t = rec.tid.load(Ordering::SeqCst);
133                if t == 0 {
134                    break;
135                }
136                // shared wait: matches the kernel's exit wake
137                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
145/// give the rest of the time slice away  [plat_thread_yield]
146pub fn thread_yield() {
147    rt::os::yield_now()
148}
149
150// ---------------------------------------------------------------- semaphores
151
152struct SemInner {
153    count: AtomicI32, // the futex word
154    waiters: AtomicU32,
155}
156
157/// a counting semaphore
158pub 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    /// add n units, wake sleepers (0: nothing)
166    pub fn post(&self, n: u32) {
167        if n == 0 {
168            return;
169        }
170        // saturating: an i32 overflow would turn the count negative and block waiters for good
171        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            // SAFETY: the futex word lives as long as self.
175            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    /// take one unit: true taken, false timeout (ms -1 = forever, 0 = only try)
182    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            // SAFETY: sleeps only while the count is still 0; ts lives during the call.
211            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
217// ---------------------------------------------------------------- UI wake-up
218
219static WAKE_FD: AtomicI32 = AtomicI32::new(-1);
220static WAKE_FLAG: AtomicU32 = AtomicU32::new(0);
221
222/// the non-blocking wake eventfd (process start)
223pub(crate) fn wake_init() {
224    // SAFETY: eventfd2(0, EFD_CLOEXEC | EFD_NONBLOCK).
225    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
233/// UI thread: reset the eventfd, then the flag (in this order: a wake() between the two is
234/// answered by the E_WAKE delivered now -- its work was queued before it; the other order
235/// could drain the write of a wake() that already set the flag again, and every later wake()
236/// would then find the flag set and stay silent); the E_WAKE to deliver
237pub(crate) fn wake_consume() -> Event {
238    let fd = WAKE_FD.load(Ordering::SeqCst);
239    if fd >= 0 {
240        let mut v = 0u64;
241        // SAFETY: non-blocking read of the 8-byte counter (EAGAIN is fine).
242        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
248/// any thread: make the UI thread's wait return with E_WAKE (coalesced)  [plat_wake]
249pub fn wake() {
250    if WAKE_FLAG.swap(1, Ordering::SeqCst) != 0 {
251        return; // already signalled, not consumed yet
252    }
253    let fd = WAKE_FD.load(Ordering::SeqCst);
254    if fd < 0 {
255        return;
256    }
257    let one = 1u64;
258    loop {
259        // SAFETY: writes the 8-byte counter increment.
260        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
267/// headless wait (no display): until a wake-up (E_WAKE to on_event, returns 1) or the
268/// deadline in time_us() (0 = only poll, u64::MAX = forever; returns 0)  [lx_wait_nodisp]
269pub 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        // one pollfd: fd, POLLIN (a negative fd is ignored: a plain sleep)
280        let mut pfd = [(WAKE_FD.load(Ordering::SeqCst) as u32 as i64) | (1i64 << 32)];
281        // SAFETY: one pollfd.
282        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
294// ---------------------------------------------------------------- system information
295
296/// CPUs this process may run on (sched_getaffinity), >= 1  [plat_cpu_count]
297pub fn cpu_count() -> u32 {
298    let mut mask = [0u64; 16]; // 1024 CPUs
299    // SAFETY: the mask buffer holds 128 bytes.
300    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
310/// physical memory in bytes (sysinfo), 0 unknown  [plat_mem_total]
311pub fn mem_total() -> u64 {
312    let mut si = [0u64; 14]; // struct sysinfo (112 bytes)
313    // SAFETY: the buffer holds a struct sysinfo.
314    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; // mem_unit at 104
318    if unit == 0 { si[4] } else { si[4] * unit as u64 } // totalram at 32
319}
320
321/// the date and time in UTC (no time zone database); the flag is true (UTC)  [plat_date]
322pub fn date() -> (Date, bool) {
323    let mut ts = [0i64; 2];
324    // SAFETY: CLOCK_REALTIME into a timespec.
325    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    // civil from days (H. Hinnant)
330    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// ---------------------------------------------------------------- threads of the glibc build
353//
354// (build_rs.py section "glibc", linux_glibc.rs) Drivers assume every calling thread is a glibc
355// thread: thread-local storage, a thread control block.  So pthread_create makes the thread, on
356// the mapping thread_spawn made: glibc takes its control block and the static TLS from the top of
357// the stack, which TLS_ROOM pays for (about 5 KB with libc alone; libraries opened later take
358// dynamic TLS, which glibc allocates elsewhere), so the stack that is left is the size asked for.
359// The record stays above it; _rsv[0] holds the pthread_t.  pthread_join is what makes the munmap
360// safe; glibc caches no stack of ours.
361
362#[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    // SAFETY: as in the other thread_main: the alternate signal stack thread_spawn reserved, the
369    // closure taken over once.
370    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/// thread_spawn's call: start the thread on rec's mapping -> < 0 when it did not start
380#[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    // SAFETY: attr is a pthread_attr_t for the time of the call; the stack lies between the upper
386    // guard page and the record, thread_spawn wrote the record before this call.
387    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    /// wait until the thread has ended, then free its stack  [plat_thread_join]
402    pub fn join(self) {
403        // SAFETY: the thread was created joinable and only this call joins it; after that nothing
404        // uses its mapping any more.
405        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}