1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-27 23:17:45 +00:00

Kernel: Some futex improvements

This adds support for FUTEX_WAKE_OP, FUTEX_WAIT_BITSET, FUTEX_WAKE_BITSET,
FUTEX_REQUEUE, and FUTEX_CMP_REQUEUE, as well well as global and private
futex and absolute/relative timeouts against the appropriate clock. This
also changes the implementation so that kernel resources are only used when
a thread is blocked on a futex.

Global futexes are implemented as offsets in VMObjects, so that different
processes can share a futex against the same VMObject despite potentially
being mapped at different virtual addresses.
This commit is contained in:
Tom 2020-12-21 23:21:58 -07:00 committed by Andreas Kling
parent 7581b64705
commit 1d621ab172
23 changed files with 928 additions and 63 deletions

View file

@ -60,10 +60,38 @@ int profiling_disable(pid_t pid)
__RETURN_WITH_ERRNO(rc, rc, -1);
}
int futex(int32_t* userspace_address, int futex_op, int32_t value, const struct timespec* timeout)
int futex(uint32_t* userspace_address, int futex_op, uint32_t value, const struct timespec* timeout, uint32_t* userspace_address2, uint32_t value3)
{
Syscall::SC_futex_params params { userspace_address, futex_op, value, timeout };
int rc = syscall(SC_futex, &params);
int rc;
switch (futex_op & FUTEX_CMD_MASK) {
//case FUTEX_CMP_REQUEUE:
// FUTEX_CMP_REQUEUE_PI:
case FUTEX_WAKE_OP: {
// These interpret timeout as a u32 value for val2
Syscall::SC_futex_params params {
.userspace_address = userspace_address,
.futex_op = futex_op,
.val = value,
.val2 = (uint32_t)timeout,
.userspace_address2 = userspace_address2,
.val3 = value3
};
rc = syscall(SC_futex, &params);
break;
}
default: {
Syscall::SC_futex_params params {
.userspace_address = userspace_address,
.futex_op = futex_op,
.val = value,
.timeout = timeout,
.userspace_address2 = userspace_address2,
.val3 = value3
};
rc = syscall(SC_futex, &params);
break;
}
}
__RETURN_WITH_ERRNO(rc, rc, -1);
}

View file

@ -45,10 +45,48 @@ int profiling_disable(pid_t);
#define THREAD_PRIORITY_HIGH 50
#define THREAD_PRIORITY_MAX 99
#define _FUTEX_OP_SHIFT_OP 28
#define _FUTEX_OP_MASK_OP 0xf
#define _FUTEX_OP_SHIFT_CMP 24
#define _FUTEX_OP_MASK_CMP 0xf
#define _FUTEX_OP_SHIFT_OP_ARG 12
#define _FUTEX_OP_MASK_OP_ARG 0xfff
#define _FUTEX_OP_SHIFT_CMP_ARG 0
#define _FUTEX_OP_MASK_CMP_ARG 0xfff
#define FUTEX_OP(op, op_arg, cmp, cmp_arg) \
((((op)&_FUTEX_OP_MASK_OP) << _FUTEX_OP_SHIFT_OP) | (((cmp)&_FUTEX_OP_MASK_CMP) << _FUTEX_OP_SHIFT_CMP) | (((op_arg)&_FUTEX_OP_MASK_OP_ARG) << _FUTEX_OP_SHIFT_OP_ARG) | (((cmp_arg)&_FUTEX_OP_MASK_CMP_ARG) << _FUTEX_OP_SHIFT_CMP_ARG))
#define FUTEX_OP_SET 0
#define FUTEX_OP_ADD 1
#define FUTEX_OP_OR 2
#define FUTEX_OP_ANDN 3
#define FUTEX_OP_XOR 4
#define FUTEX_OP_ARG_SHIFT 8
#define FUTEX_OP_CMP_EQ 0
#define FUTEX_OP_CMP_NE 1
#define FUTEX_OP_CMP_LT 2
#define FUTEX_OP_CMP_LE 3
#define FUTEX_OP_CMP_GT 4
#define FUTEX_OP_CMP_GE 5
#define FUTEX_WAIT 1
#define FUTEX_WAKE 2
int futex(int32_t* userspace_address, int futex_op, int32_t value, const struct timespec* timeout);
#define FUTEX_REQUEUE 3
#define FUTEX_CMP_REQUEUE 4
#define FUTEX_WAKE_OP 5
#define FUTEX_WAIT_BITSET 9
#define FUTEX_WAKE_BITSET 10
#define FUTEX_PRIVATE_FLAG (1 << 7)
#define FUTEX_CLOCK_REALTIME (1 << 8)
#define FUTEX_CMD_MASK ~(FUTEX_PRIVATE_FLAG | FUTEX_CLOCK_REALTIME)
#define FUTEX_BITSET_MATCH_ANY 0xffffffff
int futex(uint32_t* userspace_address, int futex_op, uint32_t value, const struct timespec* timeout, uint32_t* userspace_address2, uint32_t value3);
#define PURGE_ALL_VOLATILE 0x1
#define PURGE_ALL_CLEAN_INODE 0x2

View file

@ -0,0 +1,50 @@
diff a/Userland/Libraries/LibC/serenity.h b/Userland/Libraries/LibC/serenity.h (rejected hunks)
@@ -55,10 +55,47 @@ int profiling_disable(pid_t);
int set_thread_boost(pid_t tid, int amount);
int set_process_boost(pid_t, int amount);
+#define _FUTEX_OP_SHIFT_OP 28
+#define _FUTEX_OP_MASK_OP 0xf
+#define _FUTEX_OP_SHIFT_CMP 24
+#define _FUTEX_OP_MASK_CMP 0xf
+#define _FUTEX_OP_SHIFT_OP_ARG 12
+#define _FUTEX_OP_MASK_OP_ARG 0xfff
+#define _FUTEX_OP_SHIFT_CMP_ARG 0
+#define _FUTEX_OP_MASK_CMP_ARG 0xfff
+
+#define FUTEX_OP(op, op_arg, cmp, cmp_arg) \
+ ((((op)&_FUTEX_OP_MASK_OP) << _FUTEX_OP_SHIFT_OP) | (((cmp)&_FUTEX_OP_MASK_CMP) << _FUTEX_OP_SHIFT_CMP) | (((op_arg)&_FUTEX_OP_MASK_OP_ARG) << _FUTEX_OP_SHIFT_OP_ARG) | (((cmp_arg)&_FUTEX_OP_MASK_CMP_ARG) << _FUTEX_OP_SHIFT_CMP_ARG))
+
+#define FUTEX_OP_SET 0
+#define FUTEX_OP_ADD 1
+#define FUTEX_OP_OR 2
+#define FUTEX_OP_ANDN 3
+#define FUTEX_OP_XOR 4
+#define FUTEX_OP_ARG_SHIFT 8
+
+#define FUTEX_OP_CMP_EQ 0
+#define FUTEX_OP_CMP_NE 1
+#define FUTEX_OP_CMP_LT 2
+#define FUTEX_OP_CMP_LE 3
+#define FUTEX_OP_CMP_GT 4
+#define FUTEX_OP_CMP_GE 5
+
#define FUTEX_WAIT 1
#define FUTEX_WAKE 2
+#define FUTEX_REQUEUE 3
+#define FUTEX_CMP_REQUEUE 4
+#define FUTEX_WAKE_OP 5
+#define FUTEX_WAIT_BITSET 9
+#define FUTEX_WAKE_BITSET 10
+
+#define FUTEX_PRIVATE_FLAG (1 << 7)
+#define FUTEX_CLOCK_REALTIME (1 << 8)
+#define FUTEX_CMD_MASK ~(FUTEX_PRIVATE_FLAG | FUTEX_CLOCK_REALTIME)
+
+#define FUTEX_BITSET_MATCH_ANY 0xffffffff
-int futex(int32_t* userspace_address, int futex_op, int32_t value, const struct timespec* timeout);
+int futex(uint32_t* userspace_address, int futex_op, uint32_t value, const struct timespec* timeout, uint32_t* userspace_address2, uint32_t value3);
#define PURGE_ALL_VOLATILE 0x1
#define PURGE_ALL_CLEAN_INODE 0x2

View file

@ -78,7 +78,7 @@ struct utimbuf {
typedef int pthread_t;
typedef int pthread_key_t;
typedef int32_t pthread_once_t;
typedef uint32_t pthread_once_t;
typedef struct __pthread_mutex_t {
uint32_t lock;
@ -93,7 +93,7 @@ typedef struct __pthread_mutexattr_t {
} pthread_mutexattr_t;
typedef struct __pthread_cond_t {
int32_t value;
uint32_t value;
uint32_t previous;
int clockid; // clockid_t
} pthread_cond_t;

View file

@ -495,12 +495,25 @@ int pthread_cond_destroy(pthread_cond_t*)
return 0;
}
static int futex_wait(uint32_t& futex_addr, uint32_t value, const struct timespec* abstime)
{
int saved_errno = errno;
// NOTE: FUTEX_WAIT takes a relative timeout, so use FUTEX_WAIT_BITSET instead!
int rc = futex(&futex_addr, FUTEX_WAIT_BITSET, value, abstime, nullptr, FUTEX_BITSET_MATCH_ANY);
if (rc < 0 && errno == EAGAIN) {
// If we didn't wait, that's not an error
errno = saved_errno;
rc = 0;
}
return rc;
}
static int cond_wait(pthread_cond_t* cond, pthread_mutex_t* mutex, const struct timespec* abstime)
{
i32 value = cond->value;
u32 value = cond->value;
cond->previous = value;
pthread_mutex_unlock(mutex);
int rc = futex(&cond->value, FUTEX_WAIT, value, abstime);
int rc = futex_wait(cond->value, value, abstime);
pthread_mutex_lock(mutex);
return rc;
}
@ -538,7 +551,7 @@ int pthread_cond_signal(pthread_cond_t* cond)
{
u32 value = cond->previous + 1;
cond->value = value;
int rc = futex(&cond->value, FUTEX_WAKE, 1, nullptr);
int rc = futex(&cond->value, FUTEX_WAKE, 1, nullptr, nullptr, 0);
ASSERT(rc == 0);
return 0;
}
@ -547,7 +560,7 @@ int pthread_cond_broadcast(pthread_cond_t* cond)
{
u32 value = cond->previous + 1;
cond->value = value;
int rc = futex(&cond->value, FUTEX_WAKE, INT32_MAX, nullptr);
int rc = futex(&cond->value, FUTEX_WAKE, INT32_MAX, nullptr, nullptr, 0);
ASSERT(rc == 0);
return 0;
}

View file

@ -65,7 +65,7 @@ int pthread_once(pthread_once_t* self, void (*callback)(void))
// anyone.
break;
case State::PERFORMING_WITH_WAITERS:
futex(self, FUTEX_WAKE, INT_MAX, nullptr);
futex(self, FUTEX_WAKE, INT_MAX, nullptr, nullptr, 0);
break;
}
@ -95,7 +95,7 @@ int pthread_once(pthread_once_t* self, void (*callback)(void))
[[fallthrough]];
case State::PERFORMING_WITH_WAITERS:
// Let's wait for it.
futex(self, FUTEX_WAIT, state2, nullptr);
futex(self, FUTEX_WAIT, state2, nullptr, nullptr, 0);
// We have been woken up, but that might have been due to a signal
// or something, so we have to reevaluate. We need acquire ordering
// here for the same reason as above. Hopefully we'll just see