LibC: Implement pthread_cond_* using a futex

This commit is contained in:
Bananymous 2025-08-05 00:22:51 +03:00
parent dfdfb7cdaf
commit eb7922ab88
2 changed files with 23 additions and 22 deletions

View File

@ -10,6 +10,8 @@ __BEGIN_DECLS
#include <bits/types/pthread_attr_t.h> #include <bits/types/pthread_attr_t.h>
#include <bits/types/pthread_t.h> #include <bits/types/pthread_t.h>
#include <stdint.h>
typedef int pthread_once_t; typedef int pthread_once_t;
typedef unsigned pthread_key_t; typedef unsigned pthread_key_t;
@ -36,7 +38,7 @@ typedef struct
struct _pthread_cond_block struct _pthread_cond_block
{ {
struct _pthread_cond_block* next; struct _pthread_cond_block* next;
int signaled; uint32_t futex;
}; };
typedef struct typedef struct
{ {

View File

@ -10,6 +10,7 @@
#include <pthread.h> #include <pthread.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <sys/futex.h>
#include <sys/mman.h> #include <sys/mman.h>
#include <sys/syscall.h> #include <sys/syscall.h>
#include <unistd.h> #include <unistd.h>
@ -1113,7 +1114,12 @@ int pthread_cond_broadcast(pthread_cond_t* cond)
{ {
pthread_spin_lock(&cond->lock); pthread_spin_lock(&cond->lock);
for (auto* block = cond->block_list; block; block = block->next) for (auto* block = cond->block_list; block; block = block->next)
BAN::atomic_store(block->signaled, 1); {
BAN::atomic_store(block->futex, 1);
const int op = FUTEX_WAKE | (cond->attr.shared ? 0 : FUTEX_PRIVATE);
futex(op, &block->futex, 1, nullptr);
}
pthread_spin_unlock(&cond->lock); pthread_spin_unlock(&cond->lock);
return 0; return 0;
} }
@ -1121,8 +1127,13 @@ int pthread_cond_broadcast(pthread_cond_t* cond)
int pthread_cond_signal(pthread_cond_t* cond) int pthread_cond_signal(pthread_cond_t* cond)
{ {
pthread_spin_lock(&cond->lock); pthread_spin_lock(&cond->lock);
if (cond->block_list) if (auto* block = cond->block_list)
BAN::atomic_store(cond->block_list->signaled, 1); {
BAN::atomic_store(block->futex, 1);
const int op = FUTEX_WAKE | (cond->attr.shared ? 0 : FUTEX_PRIVATE);
futex(op, &block->futex, 1, nullptr);
}
pthread_spin_unlock(&cond->lock); pthread_spin_unlock(&cond->lock);
return 0; return 0;
} }
@ -1137,38 +1148,26 @@ int pthread_cond_timedwait(pthread_cond_t* __restrict cond, pthread_mutex_t* __r
{ {
pthread_testcancel(); pthread_testcancel();
constexpr auto has_timed_out =
[](const struct timespec* abstime, clockid_t clock_id) -> bool
{
if (abstime == nullptr)
return false;
struct timespec curtime;
clock_gettime(clock_id, &curtime);
if (curtime.tv_sec < abstime->tv_sec)
return false;
if (curtime.tv_sec > abstime->tv_sec)
return true;
return curtime.tv_nsec >= abstime->tv_nsec;
};
pthread_spin_lock(&cond->lock); pthread_spin_lock(&cond->lock);
_pthread_cond_block block = { _pthread_cond_block block = {
.next = cond->block_list, .next = cond->block_list,
.signaled = 0, .futex = 0,
}; };
cond->block_list = &block; cond->block_list = &block;
pthread_spin_unlock(&cond->lock); pthread_spin_unlock(&cond->lock);
pthread_mutex_unlock(mutex); pthread_mutex_unlock(mutex);
while (BAN::atomic_load(block.signaled) == 0) while (BAN::atomic_load(block.futex) == 0)
{ {
if (has_timed_out(abstime, cond->attr.clock)) const int op = FUTEX_WAIT
| (cond->attr.shared ? 0 : FUTEX_PRIVATE)
| (cond->attr.clock == CLOCK_REALTIME ? FUTEX_REALTIME : 0);
if (futex(op, &block.futex, 0, abstime) == -1 && errno == ETIMEDOUT)
{ {
pthread_mutex_lock(mutex); pthread_mutex_lock(mutex);
return ETIMEDOUT; return ETIMEDOUT;
} }
sched_yield();
} }
pthread_spin_lock(&cond->lock); pthread_spin_lock(&cond->lock);