Skip to content

Commit 462ba69

Browse files
committed
Speed up unnamed POSIX semaphores
When sem_wait() used its futexes it would always use process shared mode which can be problematic on platforms like Windows, where that causes it to use the slow futex polyfill. Now when sem_init() is called in private mode that'll be passed along so we can use a faster WaitOnAddress() call
1 parent b5fcb59 commit 462ba69

File tree

5 files changed

+76
-30
lines changed

5 files changed

+76
-30
lines changed

libc/thread/sem_destroy.c

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
╚─────────────────────────────────────────────────────────────────────────────*/
1919
#include "libc/assert.h"
2020
#include "libc/intrin/atomic.h"
21+
#include "libc/intrin/strace.h"
2122
#include "libc/limits.h"
2223
#include "libc/sysv/errfuns.h"
2324
#include "libc/thread/semaphore.h"
@@ -40,14 +41,20 @@
4041
* @raise EBUSY if `sem` has waiters
4142
*/
4243
int sem_destroy(sem_t *sem) {
43-
int waiters;
44+
int rc, waiters;
4445
npassert(sem->sem_magic != SEM_MAGIC_NAMED);
45-
if (sem->sem_magic != SEM_MAGIC_UNNAMED)
46-
return einval();
47-
waiters = atomic_load_explicit(&sem->sem_waiters, memory_order_relaxed);
48-
unassert(waiters >= 0);
49-
if (waiters)
50-
return ebusy();
51-
atomic_store_explicit(&sem->sem_value, INT_MIN, memory_order_relaxed);
52-
return 0;
46+
if (sem->sem_magic != SEM_MAGIC_UNNAMED) {
47+
rc = einval();
48+
} else {
49+
waiters = atomic_load_explicit(&sem->sem_waiters, memory_order_relaxed);
50+
unassert(waiters >= 0);
51+
if (waiters) {
52+
rc = ebusy();
53+
} else {
54+
atomic_store_explicit(&sem->sem_value, INT_MIN, memory_order_relaxed);
55+
rc = 0;
56+
}
57+
}
58+
STRACE("sem_destroy(%p) → %d% m", sem, rc);
59+
return rc;
5360
}

libc/thread/sem_init.c

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include "libc/calls/calls.h"
2020
#include "libc/dce.h"
2121
#include "libc/intrin/atomic.h"
22+
#include "libc/intrin/strace.h"
2223
#include "libc/limits.h"
2324
#include "libc/sysv/errfuns.h"
2425
#include "libc/thread/semaphore.h"
@@ -37,12 +38,17 @@
3738
* @raise EINVAL if `value` exceeds `SEM_VALUE_MAX`
3839
*/
3940
int sem_init(sem_t *sem, int pshared, unsigned value) {
40-
if (value > SEM_VALUE_MAX)
41-
return einval();
42-
sem->sem_magic = SEM_MAGIC_UNNAMED;
43-
atomic_store_explicit(&sem->sem_value, value, memory_order_relaxed);
44-
sem->sem_pshared = !!pshared;
45-
sem->sem_pid = getpid();
46-
sem->sem_waiters = 0;
47-
return 0;
41+
int rc;
42+
if (value > SEM_VALUE_MAX) {
43+
rc = einval();
44+
} else {
45+
sem->sem_magic = SEM_MAGIC_UNNAMED;
46+
atomic_store_explicit(&sem->sem_value, value, memory_order_relaxed);
47+
sem->sem_pshared = !!pshared;
48+
sem->sem_pid = getpid();
49+
sem->sem_waiters = 0;
50+
rc = 0;
51+
}
52+
STRACE("sem_init(%p, %hhhd, %u) → %d% m", sem, pshared, value, rc);
53+
return rc;
4854
}

libc/thread/sem_post.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ int sem_post(sem_t *sem) {
4646
old = atomic_fetch_add_explicit(&sem->sem_value, 1, memory_order_acq_rel);
4747
unassert(old > INT_MIN);
4848
if (old >= 0) {
49-
wakeups = nsync_futex_wake_(&sem->sem_value, 1, true);
49+
wakeups = nsync_futex_wake_(&sem->sem_value, 1, sem->sem_pshared);
5050
npassert(wakeups >= 0);
5151
rc = 0;
5252
} else {

libc/thread/sem_timedwait.c

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ static void sem_timedwait_cleanup(void *arg) {
5959
* @cancelationpoint
6060
*/
6161
int sem_timedwait(sem_t *sem, const struct timespec *abstime) {
62-
int i, v, rc, e = errno;
62+
int v, rc, e = errno;
6363

6464
#if 0
6565
if (IsXnuSilicon() && sem->sem_magic == SEM_MAGIC_KERNEL) {
@@ -103,16 +103,13 @@ int sem_timedwait(sem_t *sem, const struct timespec *abstime) {
103103
}
104104
#endif
105105

106-
for (i = 0; i < 7; ++i) {
107-
rc = sem_trywait(sem);
108-
if (!rc) {
109-
return rc;
110-
} else if (errno == EAGAIN) {
111-
errno = e;
112-
sem_delay(i);
113-
} else {
114-
return rc;
115-
}
106+
rc = sem_trywait(sem);
107+
if (!rc) {
108+
return rc;
109+
} else if (errno == EAGAIN) {
110+
errno = e;
111+
} else {
112+
return rc;
116113
}
117114

118115
BEGIN_CANCELATION_POINT;
@@ -122,7 +119,8 @@ int sem_timedwait(sem_t *sem, const struct timespec *abstime) {
122119

123120
do {
124121
if (!(v = atomic_load_explicit(&sem->sem_value, memory_order_relaxed))) {
125-
rc = nsync_futex_wait_(&sem->sem_value, v, true, CLOCK_REALTIME, abstime);
122+
rc = nsync_futex_wait_(&sem->sem_value, v, sem->sem_pshared,
123+
CLOCK_REALTIME, abstime);
126124
if (rc == -EINTR || rc == -ECANCELED) {
127125
errno = -rc;
128126
rc = -1;

test/posix/unnamed_semaphore_test.c

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
#include <pthread.h>
2+
#include <semaphore.h>
3+
4+
#define THREADS 10
5+
#define ITERATIONS 100000
6+
7+
int g_count;
8+
sem_t g_sem;
9+
10+
void *worker(void *arg) {
11+
for (int i = 0; i < ITERATIONS; ++i) {
12+
if (sem_wait(&g_sem))
13+
exit(6);
14+
++g_count;
15+
if (sem_post(&g_sem))
16+
exit(7);
17+
}
18+
return 0;
19+
}
20+
21+
int main(int argc, char *argv[]) {
22+
pthread_t th[THREADS];
23+
if (sem_init(&g_sem, 0, 1))
24+
return 1;
25+
for (int i = 0; i < THREADS; ++i)
26+
if (pthread_create(&th[i], 0, worker, 0))
27+
return 2;
28+
for (int i = 0; i < THREADS; ++i)
29+
if (pthread_join(th[i], 0))
30+
return 3;
31+
if (g_count != THREADS * ITERATIONS)
32+
return 4;
33+
if (sem_destroy(&g_sem))
34+
return 5;
35+
}

0 commit comments

Comments
 (0)