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}