uatomic/x86: Remove redundant memory barriers
[urcu.git] / src / compat_futex.c
1 // SPDX-FileCopyrightText: 2009 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
2 //
3 // SPDX-License-Identifier: LGPL-2.1-or-later
4
5 /*
6 * Userspace RCU library - sys_futex compatibility code
7 */
8
9 #include <stdio.h>
10 #include <pthread.h>
11 #include <signal.h>
12 #include <errno.h>
13 #include <poll.h>
14 #include <stdint.h>
15
16 #include <urcu/arch.h>
17 #include <urcu/assert.h>
18 #include <urcu/futex.h>
19 #include <urcu/system.h>
20
21 /*
22 * Using attribute "weak" for __urcu_compat_futex_lock and
23 * __urcu_compat_futex_cond. Those are globally visible by the entire
24 * program, even though many shared objects may have their own version.
25 * The first version that gets loaded will be used by the entire program
26 * (executable and all shared objects).
27 */
28
29 __attribute__((weak))
30 pthread_mutex_t __urcu_compat_futex_lock = PTHREAD_MUTEX_INITIALIZER;
31 __attribute__((weak))
32 pthread_cond_t __urcu_compat_futex_cond = PTHREAD_COND_INITIALIZER;
33
34 /*
35 * _NOT SIGNAL-SAFE_. pthread_cond is not signal-safe anyway. Though.
36 * For now, timeout, uaddr2 and val3 are unused.
37 * Waiter will relinquish the CPU until woken up.
38 */
39
40 int compat_futex_noasync(int32_t *uaddr, int op, int32_t val,
41 const struct timespec *timeout, int32_t *uaddr2, int32_t val3)
42 {
43 int ret = 0, lockret;
44
45 /*
46 * Check if NULL. Don't let users expect that they are taken into
47 * account.
48 */
49 urcu_posix_assert(!timeout);
50 urcu_posix_assert(!uaddr2);
51 urcu_posix_assert(!val3);
52
53 /*
54 * memory barriers to serialize with the previous uaddr modification.
55 */
56 cmm_smp_mb();
57
58 lockret = pthread_mutex_lock(&__urcu_compat_futex_lock);
59 if (lockret) {
60 errno = lockret;
61 ret = -1;
62 goto end;
63 }
64 switch (op) {
65 case FUTEX_WAIT:
66 /*
67 * Wait until *uaddr is changed to something else than "val".
68 * Comparing *uaddr content against val figures out which
69 * thread has been awakened.
70 */
71 while (CMM_LOAD_SHARED(*uaddr) == val)
72 pthread_cond_wait(&__urcu_compat_futex_cond,
73 &__urcu_compat_futex_lock);
74 break;
75 case FUTEX_WAKE:
76 /*
77 * Each wake is sending a broadcast, thus attempting wakeup of
78 * all awaiting threads, independently of their respective
79 * uaddr.
80 */
81 pthread_cond_broadcast(&__urcu_compat_futex_cond);
82 break;
83 default:
84 errno = EINVAL;
85 ret = -1;
86 }
87 lockret = pthread_mutex_unlock(&__urcu_compat_futex_lock);
88 if (lockret) {
89 errno = lockret;
90 ret = -1;
91 }
92 end:
93 return ret;
94 }
95
96 /*
97 * _ASYNC SIGNAL-SAFE_.
98 * For now, timeout, uaddr2 and val3 are unused.
99 * Waiter will busy-loop trying to read the condition.
100 * It is OK to use compat_futex_async() on a futex address on which
101 * futex() WAKE operations are also performed.
102 */
103
104 int compat_futex_async(int32_t *uaddr, int op, int32_t val,
105 const struct timespec *timeout, int32_t *uaddr2, int32_t val3)
106 {
107 int ret = 0;
108
109 /*
110 * Check if NULL. Don't let users expect that they are taken into
111 * account.
112 */
113 urcu_posix_assert(!timeout);
114 urcu_posix_assert(!uaddr2);
115 urcu_posix_assert(!val3);
116
117 /*
118 * Ensure previous memory operations on uaddr have completed.
119 */
120 cmm_smp_mb();
121
122 switch (op) {
123 case FUTEX_WAIT:
124 while (CMM_LOAD_SHARED(*uaddr) == val) {
125 if (poll(NULL, 0, 10) < 0) {
126 ret = -1;
127 /* Keep poll errno. Caller handles EINTR. */
128 goto end;
129 }
130 }
131 break;
132 case FUTEX_WAKE:
133 break;
134 default:
135 errno = EINVAL;
136 ret = -1;
137 }
138 end:
139 return ret;
140 }
This page took 0.03125 seconds and 4 git commands to generate.