Various futex fixes
This commit is contained in:
committed by
Jeremy Soller
parent
7cf68bc8ee
commit
1fba2ef10a
@@ -235,15 +235,32 @@ impl Pal for Sys {
|
||||
e(unsafe { syscall!(FTRUNCATE, fildes, length) }) as c_int
|
||||
}
|
||||
|
||||
fn futex(
|
||||
addr: *mut c_int,
|
||||
op: c_int,
|
||||
val: c_int,
|
||||
val2: usize,
|
||||
) -> Result<c_long, crate::pthread::Errno> {
|
||||
e_raw(unsafe { syscall!(FUTEX, addr, op, val, val2, 0, 0) })
|
||||
.map(|r| r as c_long)
|
||||
.map_err(|e| crate::pthread::Errno(e as c_int))
|
||||
#[inline]
|
||||
unsafe fn futex_wait(
|
||||
addr: *mut u32,
|
||||
val: u32,
|
||||
deadline: *const timespec,
|
||||
) -> Result<(), crate::pthread::Errno> {
|
||||
e_raw(unsafe {
|
||||
syscall!(
|
||||
FUTEX, addr, // uaddr
|
||||
9, // futex_op: FUTEX_WAIT_BITSET
|
||||
val, // val
|
||||
deadline, // timeout: deadline
|
||||
0, // uaddr2/val2: 0/NULL
|
||||
0xffffffff // val3: FUTEX_BITSET_MATCH_ANY
|
||||
)
|
||||
})
|
||||
.map_err(|e| crate::pthread::Errno(e as c_int))
|
||||
.map(|_| ())
|
||||
}
|
||||
#[inline]
|
||||
unsafe fn futex_wake(addr: *mut u32, num: u32) -> Result<c_int, crate::pthread::Errno> {
|
||||
e_raw(unsafe {
|
||||
syscall!(FUTEX, addr, 1 /* FUTEX_WAKE */, num)
|
||||
})
|
||||
.map_err(|e| crate::pthread::Errno(e as c_int))
|
||||
.map(|n| n as c_int)
|
||||
}
|
||||
|
||||
fn futimens(fd: c_int, times: *const timespec) -> c_int {
|
||||
|
||||
@@ -10,6 +10,7 @@ use crate::{
|
||||
sys_utsname::utsname,
|
||||
time::timespec,
|
||||
},
|
||||
pthread,
|
||||
};
|
||||
|
||||
pub use self::epoll::PalEpoll;
|
||||
@@ -78,12 +79,12 @@ pub trait Pal {
|
||||
|
||||
fn ftruncate(fildes: c_int, length: off_t) -> c_int;
|
||||
|
||||
fn futex(
|
||||
addr: *mut c_int,
|
||||
op: c_int,
|
||||
val: c_int,
|
||||
val2: usize,
|
||||
) -> Result<c_long, crate::pthread::Errno>;
|
||||
unsafe fn futex_wait(
|
||||
addr: *mut u32,
|
||||
val: u32,
|
||||
deadline: *const timespec,
|
||||
) -> Result<(), pthread::Errno>;
|
||||
unsafe fn futex_wake(addr: *mut u32, num: u32) -> Result<c_int, pthread::Errno>;
|
||||
|
||||
fn futimens(fd: c_int, times: *const timespec) -> c_int;
|
||||
|
||||
|
||||
+31
-20
@@ -24,6 +24,7 @@ use crate::{
|
||||
unistd::{F_OK, R_OK, W_OK, X_OK},
|
||||
},
|
||||
io::{self, prelude::*, BufReader},
|
||||
pthread,
|
||||
};
|
||||
|
||||
pub use redox_exec::FdGuard;
|
||||
@@ -300,25 +301,35 @@ impl Pal for Sys {
|
||||
e(syscall::ftruncate(fd as usize, len as usize)) as c_int
|
||||
}
|
||||
|
||||
// FIXME: unsound
|
||||
fn futex(
|
||||
addr: *mut c_int,
|
||||
op: c_int,
|
||||
val: c_int,
|
||||
val2: usize,
|
||||
) -> Result<c_long, crate::pthread::Errno> {
|
||||
match unsafe {
|
||||
syscall::futex(
|
||||
addr as *mut i32,
|
||||
op as usize,
|
||||
val as i32,
|
||||
val2,
|
||||
ptr::null_mut(),
|
||||
)
|
||||
} {
|
||||
Ok(success) => Ok(success as c_long),
|
||||
Err(err) => Err(crate::pthread::Errno(err.errno)),
|
||||
}
|
||||
#[inline]
|
||||
unsafe fn futex_wait(
|
||||
addr: *mut u32,
|
||||
val: u32,
|
||||
deadline: *const timespec,
|
||||
) -> CoreResult<(), pthread::Errno> {
|
||||
syscall::syscall5(
|
||||
syscall::SYS_FUTEX,
|
||||
addr as usize,
|
||||
syscall::FUTEX_WAIT,
|
||||
val as usize,
|
||||
deadline as usize,
|
||||
0,
|
||||
)
|
||||
.map_err(|s| pthread::Errno(s.errno))
|
||||
.map(|_| ())
|
||||
}
|
||||
#[inline]
|
||||
unsafe fn futex_wake(addr: *mut u32, num: u32) -> Result<c_int, pthread::Errno> {
|
||||
syscall::syscall5(
|
||||
syscall::SYS_FUTEX,
|
||||
addr as usize,
|
||||
syscall::FUTEX_WAKE,
|
||||
num as usize,
|
||||
0,
|
||||
0,
|
||||
)
|
||||
.map_err(|s| pthread::Errno(s.errno))
|
||||
.map(|n| n as c_int)
|
||||
}
|
||||
|
||||
// FIXME: unsound
|
||||
@@ -1106,7 +1117,7 @@ impl Pal for Sys {
|
||||
|
||||
fn verify() -> bool {
|
||||
// GETPID on Redox is 20, which is WRITEV on Linux
|
||||
e(unsafe { syscall::syscall5(syscall::number::SYS_GETPID, !0, !0, !0, !0, !0) }) != !0
|
||||
(unsafe { syscall::syscall5(syscall::number::SYS_GETPID, !0, !0, !0, !0, !0) }).is_ok()
|
||||
}
|
||||
|
||||
fn exit_thread() -> ! {
|
||||
|
||||
+5
-9
@@ -74,7 +74,7 @@ impl Cond {
|
||||
unlock: impl FnOnce() -> Result<()>,
|
||||
lock: impl FnOnce() -> Result<()>,
|
||||
lock_with_timeout: impl FnOnce(×pec) -> Result<()>,
|
||||
timeout: Option<×pec>,
|
||||
deadline: Option<×pec>,
|
||||
) -> Result<(), Errno> {
|
||||
// TODO: Error checking for certain types (i.e. robust and errorcheck) of mutexes, e.g. if the
|
||||
// mutex is not locked.
|
||||
@@ -83,14 +83,10 @@ impl Cond {
|
||||
|
||||
unlock();
|
||||
|
||||
match timeout {
|
||||
Some(timeout) => {
|
||||
crate::sync::futex_wait(
|
||||
&self.cur,
|
||||
current,
|
||||
timespec::subtract(*timeout, crate::sync::rttime()).as_ref(),
|
||||
);
|
||||
lock_with_timeout(timeout);
|
||||
match deadline {
|
||||
Some(deadline) => {
|
||||
crate::sync::futex_wait(&self.cur, current, Some(&deadline));
|
||||
lock_with_timeout(deadline);
|
||||
}
|
||||
None => {
|
||||
crate::sync::futex_wait(&self.cur, current, None);
|
||||
|
||||
+35
-18
@@ -16,8 +16,12 @@ pub use self::{
|
||||
};
|
||||
|
||||
use crate::{
|
||||
header::time::timespec,
|
||||
header::{
|
||||
errno::{EAGAIN, ETIMEDOUT},
|
||||
time::timespec,
|
||||
},
|
||||
platform::{types::*, Pal, Sys},
|
||||
pthread::Errno,
|
||||
};
|
||||
use core::{
|
||||
mem::MaybeUninit,
|
||||
@@ -36,7 +40,7 @@ pub enum AttemptStatus {
|
||||
}
|
||||
|
||||
pub trait FutexTy {
|
||||
fn conv(self) -> i32;
|
||||
fn conv(self) -> u32;
|
||||
}
|
||||
pub trait FutexAtomicTy {
|
||||
type Ty: FutexTy;
|
||||
@@ -44,13 +48,13 @@ pub trait FutexAtomicTy {
|
||||
fn ptr(&self) -> *mut Self::Ty;
|
||||
}
|
||||
impl FutexTy for u32 {
|
||||
fn conv(self) -> i32 {
|
||||
self as i32
|
||||
fn conv(self) -> u32 {
|
||||
self
|
||||
}
|
||||
}
|
||||
impl FutexTy for i32 {
|
||||
fn conv(self) -> i32 {
|
||||
self
|
||||
fn conv(self) -> u32 {
|
||||
self as u32
|
||||
}
|
||||
}
|
||||
impl FutexAtomicTy for AtomicU32 {
|
||||
@@ -91,20 +95,26 @@ impl FutexAtomicTy for AtomicI32 {
|
||||
|
||||
pub unsafe fn futex_wake_ptr(ptr: *mut impl FutexTy, n: i32) -> usize {
|
||||
// TODO: unwrap_unchecked?
|
||||
Sys::futex(ptr.cast(), FUTEX_WAKE, n, 0).unwrap() as usize
|
||||
Sys::futex_wake(ptr.cast(), n as u32).unwrap() as usize
|
||||
}
|
||||
pub unsafe fn futex_wait_ptr<T: FutexTy>(
|
||||
ptr: *mut T,
|
||||
value: T,
|
||||
timeout_opt: Option<×pec>,
|
||||
) -> bool {
|
||||
// TODO: unwrap_unchecked?
|
||||
Sys::futex(
|
||||
deadline_opt: Option<×pec>,
|
||||
) -> FutexWaitResult {
|
||||
match Sys::futex_wait(
|
||||
ptr.cast(),
|
||||
FUTEX_WAIT,
|
||||
value.conv(),
|
||||
timeout_opt.map_or(0, |t| t as *const _ as usize),
|
||||
) == Ok(0)
|
||||
deadline_opt.map_or(core::ptr::null(), |t| t as *const _),
|
||||
) {
|
||||
Ok(()) => FutexWaitResult::Waited,
|
||||
Err(Errno(EAGAIN)) => FutexWaitResult::Stale,
|
||||
Err(Errno(ETIMEDOUT)) if deadline_opt.is_some() => FutexWaitResult::TimedOut,
|
||||
Err(other) => {
|
||||
eprintln!("futex failed: {}", other.0);
|
||||
FutexWaitResult::Waited
|
||||
}
|
||||
}
|
||||
}
|
||||
pub fn futex_wake(atomic: &impl FutexAtomicTy, n: i32) -> usize {
|
||||
unsafe { futex_wake_ptr(atomic.ptr(), n) }
|
||||
@@ -112,9 +122,16 @@ pub fn futex_wake(atomic: &impl FutexAtomicTy, n: i32) -> usize {
|
||||
pub fn futex_wait<T: FutexAtomicTy>(
|
||||
atomic: &T,
|
||||
value: T::Ty,
|
||||
timeout_opt: Option<×pec>,
|
||||
) -> bool {
|
||||
unsafe { futex_wait_ptr(atomic.ptr(), value, timeout_opt) }
|
||||
deadline_opt: Option<×pec>,
|
||||
) -> FutexWaitResult {
|
||||
unsafe { futex_wait_ptr(atomic.ptr(), value, deadline_opt) }
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
pub enum FutexWaitResult {
|
||||
Waited, // possibly spurious
|
||||
Stale, // outdated value
|
||||
TimedOut,
|
||||
}
|
||||
|
||||
pub fn rttime() -> timespec {
|
||||
@@ -187,7 +204,7 @@ impl AtomicLock {
|
||||
pub fn wait_if(&self, value: c_int, timeout_opt: Option<×pec>) {
|
||||
self.wait_if_raw(value, timeout_opt);
|
||||
}
|
||||
pub fn wait_if_raw(&self, value: c_int, timeout_opt: Option<×pec>) -> bool {
|
||||
pub fn wait_if_raw(&self, value: c_int, timeout_opt: Option<×pec>) -> FutexWaitResult {
|
||||
futex_wait(&self.atomic, value, timeout_opt)
|
||||
}
|
||||
|
||||
|
||||
@@ -10,6 +10,8 @@ use crate::{
|
||||
|
||||
use crate::platform::{types::*, Pal, Sys};
|
||||
|
||||
use super::FutexWaitResult;
|
||||
|
||||
pub struct RlctMutex {
|
||||
// Actual locking word.
|
||||
inner: AtomicUint,
|
||||
@@ -123,7 +125,11 @@ impl RlctMutex {
|
||||
|
||||
// If the mutex is not robust, simply futex_wait until unblocked.
|
||||
//crate::sync::futex_wait(&self.inner, inner | WAITING_BIT, None);
|
||||
crate::sync::futex_wait(&self.inner, thread, None);
|
||||
if crate::sync::futex_wait(&self.inner, thread, deadline)
|
||||
== FutexWaitResult::TimedOut
|
||||
{
|
||||
return Err(Errno(ETIMEDOUT));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+4
-5
@@ -21,7 +21,7 @@ impl Rwlock {
|
||||
state: AtomicU32::new(0),
|
||||
}
|
||||
}
|
||||
pub fn acquire_write_lock(&self, _timeout: Option<×pec>) {
|
||||
pub fn acquire_write_lock(&self, deadline: Option<×pec>) {
|
||||
let mut waiting_wr = self.state.load(Ordering::Relaxed) & WAITING_WR;
|
||||
|
||||
loop {
|
||||
@@ -45,16 +45,15 @@ impl Rwlock {
|
||||
};
|
||||
waiting_wr = expected & WAITING_WR;
|
||||
|
||||
// TODO: timeout
|
||||
let _ = crate::sync::futex_wait(&self.state, expected, None);
|
||||
let _ = crate::sync::futex_wait(&self.state, expected, deadline);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
pub fn acquire_read_lock(&self, _timeout: Option<×pec>) {
|
||||
pub fn acquire_read_lock(&self, deadline: Option<×pec>) {
|
||||
// TODO: timeout
|
||||
while let Err(old) = self.try_acquire_read_lock() {
|
||||
crate::sync::futex_wait(&self.state, old, None);
|
||||
crate::sync::futex_wait(&self.state, old, deadline);
|
||||
}
|
||||
}
|
||||
pub fn try_acquire_read_lock(&self) -> Result<(), u32> {
|
||||
|
||||
@@ -186,6 +186,7 @@ NAMES=\
|
||||
pthread/rwlock_trylock \
|
||||
pthread/rwlock_randtest \
|
||||
pthread/mutex_recursive \
|
||||
pthread/timeout \
|
||||
grp/getgrouplist \
|
||||
grp/getgrgid_r \
|
||||
grp/getgrnam_r \
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
#include "../test_helpers.h"
|
||||
#include "common.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <pthread.h>
|
||||
#include <time.h>
|
||||
|
||||
struct arg {
|
||||
int status;
|
||||
bool completed;
|
||||
pthread_barrier_t barrier;
|
||||
pthread_mutex_t mutex;
|
||||
};
|
||||
|
||||
void *routine(void *arg_raw) {
|
||||
struct arg *arg = (struct arg *)arg_raw;
|
||||
|
||||
int barrier_status = pthread_barrier_wait(&arg->barrier);
|
||||
|
||||
if (barrier_status != 0 && barrier_status != PTHREAD_BARRIER_SERIAL_THREAD) {
|
||||
arg->status = barrier_status;
|
||||
fputs("failed to wait for barrier\n", stderr);
|
||||
return NULL;
|
||||
}
|
||||
puts("thread waited");
|
||||
|
||||
struct timespec abstime;
|
||||
if ((arg->status = clock_gettime(CLOCK_MONOTONIC, &abstime)) != 0) {
|
||||
fputs("failed to get current time\n", stderr);
|
||||
return NULL;
|
||||
}
|
||||
abstime.tv_sec += 1;
|
||||
|
||||
if ((arg->status = pthread_mutex_timedlock(&arg->mutex, &abstime)) != ETIMEDOUT) {
|
||||
fputs("failed to fail at locking mutex\n", stderr);
|
||||
return NULL;
|
||||
}
|
||||
arg->status = 0;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
int status;
|
||||
|
||||
struct arg arg;
|
||||
arg.completed = false;
|
||||
|
||||
status = pthread_barrier_init(&arg.barrier, NULL, 2);
|
||||
ERROR_IF2(pthread_barrier_init, status, != 0);
|
||||
|
||||
status = pthread_mutex_init(&arg.mutex, NULL);
|
||||
ERROR_IF2(pthread_mutex_init, status, != 0);
|
||||
|
||||
status = pthread_mutex_trylock(&arg.mutex);
|
||||
ERROR_IF2(pthread_mutex_trylock, status, != 0);
|
||||
|
||||
pthread_t thread;
|
||||
status = pthread_create(&thread, NULL, routine, &arg);
|
||||
ERROR_IF2(pthread_create, status, != 0);
|
||||
|
||||
status = pthread_barrier_wait(&arg.barrier);
|
||||
if (status != PTHREAD_BARRIER_SERIAL_THREAD) {
|
||||
ERROR_IF2(pthread_barrier_wait, status, != 0);
|
||||
}
|
||||
puts("main waited");
|
||||
|
||||
status = pthread_join(thread, NULL);
|
||||
ERROR_IF2(pthread_join, status, != 0);
|
||||
|
||||
status = pthread_mutex_unlock(&arg.mutex);
|
||||
ERROR_IF2(pthread_mutex_unlock, status, != 0);
|
||||
|
||||
if (arg.status != 0) {
|
||||
fprintf(stderr, "thread failed: %s\n", strerror(arg.status));
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
status = pthread_mutex_destroy(&arg.mutex);
|
||||
ERROR_IF2(pthread_mutex_destroy, status, != 0);
|
||||
|
||||
status = pthread_barrier_destroy(&arg.barrier);
|
||||
ERROR_IF2(pthread_barrier_destroy, status, != 0);
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
@@ -46,6 +46,15 @@
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
#define ERROR_IF2(func, status, condition) \
|
||||
do { \
|
||||
if (status condition) { \
|
||||
fprintf(stderr, "%s:%s:%d: '%s' failed: %s (%d)\n", \
|
||||
__FILE__, __func__, __LINE__, #func, strerror(status), status); \
|
||||
_exit(EXIT_FAILURE); \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
// Throws errors on API return values not defined by the standards.
|
||||
//
|
||||
// Do not pass functions as the status or condition arguments, they might be
|
||||
|
||||
Reference in New Issue
Block a user