starry_api/imp/
futex.rs

1use axerrno::{LinuxError, LinuxResult};
2use axtask::{TaskExtRef, current};
3use linux_raw_sys::general::{
4    FUTEX_CMD_MASK, FUTEX_CMP_REQUEUE, FUTEX_REQUEUE, FUTEX_WAIT, FUTEX_WAKE, timespec,
5};
6
7use crate::{
8    ptr::{UserConstPtr, UserPtr, nullable},
9    time::TimeValueLike,
10};
11
12pub fn sys_futex(
13    uaddr: UserConstPtr<u32>,
14    futex_op: u32,
15    value: u32,
16    timeout: UserConstPtr<timespec>,
17    uaddr2: UserPtr<u32>,
18    value3: u32,
19) -> LinuxResult<isize> {
20    info!("futex {:?} {} {}", uaddr.address(), futex_op, value);
21
22    let curr = current();
23    let futex_table = &curr.task_ext().process_data().futex_table;
24
25    let addr = uaddr.address().as_usize();
26    let command = futex_op & (FUTEX_CMD_MASK as u32);
27    match command {
28        FUTEX_WAIT => {
29            if *uaddr.get_as_ref()? != value {
30                return Err(LinuxError::EAGAIN);
31            }
32            let wq = futex_table.get_or_insert(addr);
33
34            if let Some(timeout) = nullable!(timeout.get_as_ref())? {
35                wq.wait_timeout(timeout.to_time_value());
36            } else {
37                wq.wait();
38            }
39
40            Ok(0)
41        }
42        FUTEX_WAKE => {
43            let wq = futex_table.get(addr);
44            let mut count = 0;
45            if let Some(wq) = wq {
46                for _ in 0..value {
47                    if !wq.notify_one(false) {
48                        break;
49                    }
50                    count += 1;
51                }
52            }
53            axtask::yield_now();
54            Ok(count)
55        }
56        FUTEX_REQUEUE | FUTEX_CMP_REQUEUE => {
57            if command == FUTEX_CMP_REQUEUE && *uaddr.get_as_ref()? != value3 {
58                return Err(LinuxError::EAGAIN);
59            }
60            let value2 = timeout.address().as_usize() as u32;
61
62            let wq = futex_table.get(addr);
63            let wq2 = futex_table.get_or_insert(uaddr2.address().as_usize());
64
65            let mut count = 0;
66            if let Some(wq) = wq {
67                for _ in 0..value {
68                    if !wq.notify_one(false) {
69                        break;
70                    }
71                    count += 1;
72                }
73                if count == value as isize {
74                    count += wq.requeue(value2 as usize, &wq2) as isize;
75                }
76            }
77            Ok(count)
78        }
79        _ => Err(LinuxError::ENOSYS),
80    }
81}