summaryrefslogtreecommitdiffstats
path: root/source/l/glib2/3120.patch
blob: 9764d757538fc63b9169bfb28adf6b3c625954a5 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
From f67e8636dad3038b584887bd1edf4515fbd7ac4a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= <sebastian@centricular.com>
Date: Tue, 13 Dec 2022 15:05:06 +0200
Subject: [PATCH 1/2] glib/gthread-posix: Use `cc.compiles()` instead of
 `cc.links()` for checking for `__NR_futex`

`cc.compiles()` is minimally faster.

We only want to check here whether `__NR_futex` is defined and don't
want to check anything at link-time.
---
 meson.build | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/meson.build b/meson.build
index 2305b3a08e..c763857ea9 100644
--- a/meson.build
+++ b/meson.build
@@ -887,7 +887,7 @@ if host_system == 'qnx'
 endif
 
 # Check for futex(2)
-if cc.links('''#include <linux/futex.h>
+if cc.compiles('''#include <linux/futex.h>
                #include <sys/syscall.h>
                #include <unistd.h>
                int main (int argc, char ** argv) {
-- 
GitLab


From a79c6af23eff5ee978db62e048828c9a992a1261 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= <sebastian@centricular.com>
Date: Mon, 12 Dec 2022 19:58:21 +0200
Subject: [PATCH 2/2] glib/gthread-posix: Conditionally use `futex` and/or
 `futex_time64` syscalls as necessary and use the correct `struct timespec`
 definition

On some systems only `futex_time64` exists (e.g. riscv32) while on
others only `futex` exists (old Linux, 64 bit platforms), so it is
necessary to check for both and try calling both at runtime.

Additionally use the correct `struct timespec` definition. There is not
necessarily any relation between the libc's definition and the kernel's.

Specifically, the libc headers might use 64-bit `time_t` while the kernel
headers use 32-bit `__kernel_old_time_t` on certain systems.

To get around this problem we
  a) check if `futex_time64` is available, which only exists on 32-bit
     platforms and always uses 64-bit `time_t`.
  b) otherwise (or if that returns `ENOSYS`), we call the normal `futex`
     syscall with the `struct timespec` used by the kernel, which uses
     `__kernel_long_t` for both its fields. We use that instead of
     `__kernel_old_time_t` because it is equivalent and available in the
     kernel headers for a longer time.
---
 glib/gbitlock.c       |  15 ++----
 glib/gthread-posix.c  | 120 ++++++++++++++++++++++++++----------------
 glib/gthreadprivate.h |  49 +++++++++++++++++
 meson.build           |   9 ++++
 4 files changed, 137 insertions(+), 56 deletions(-)

diff --git a/glib/gbitlock.c b/glib/gbitlock.c
index 81bfb339f3..9c34de80c8 100644
--- a/glib/gbitlock.c
+++ b/glib/gbitlock.c
@@ -35,6 +35,7 @@
 
 #ifdef G_BIT_LOCK_FORCE_FUTEX_EMULATION
 #undef HAVE_FUTEX
+#undef HAVE_FUTEX_TIME64
 #endif
 
 #ifndef HAVE_FUTEX
@@ -42,7 +43,7 @@ static GMutex g_futex_mutex;
 static GSList *g_futex_address_list = NULL;
 #endif
 
-#ifdef HAVE_FUTEX
+#if defined(HAVE_FUTEX) || defined(HAVE_FUTEX_TIME64)
 /*
  * We have headers for futex(2) on the build machine.  This does not
  * imply that every system that ever runs the resulting glib will have
@@ -51,14 +52,6 @@ static GSList *g_futex_address_list = NULL;
  *
  * If anyone actually gets bit by this, please file a bug. :)
  */
-#include <linux/futex.h>
-#include <sys/syscall.h>
-#include <unistd.h>
-
-#ifndef FUTEX_WAIT_PRIVATE
-#define FUTEX_WAIT_PRIVATE FUTEX_WAIT
-#define FUTEX_WAKE_PRIVATE FUTEX_WAKE
-#endif
 
 /* < private >
  * g_futex_wait:
@@ -81,7 +74,7 @@ static void
 g_futex_wait (const gint *address,
               gint        value)
 {
-  syscall (__NR_futex, address, (gsize) FUTEX_WAIT_PRIVATE, (gsize) value, NULL);
+  g_futex_simple (address, (gsize) FUTEX_WAIT_PRIVATE, (gsize) value, NULL);
 }
 
 /* < private >
@@ -98,7 +91,7 @@ g_futex_wait (const gint *address,
 static void
 g_futex_wake (const gint *address)
 {
-  syscall (__NR_futex, address, (gsize) FUTEX_WAKE_PRIVATE, (gsize) 1, NULL);
+  g_futex_simple (address, (gsize) FUTEX_WAKE_PRIVATE, (gsize) 1, NULL);
 }
 
 #else
diff --git a/glib/gthread-posix.c b/glib/gthread-posix.c
index d96fca5ca2..84f62881d8 100644
--- a/glib/gthread-posix.c
+++ b/glib/gthread-posix.c
@@ -74,7 +74,7 @@
 #include <sys/syscall.h>
 #endif
 
-#if defined(HAVE_FUTEX) && \
+#if (defined(HAVE_FUTEX) || defined(HAVE_FUTEX_TIME64)) && \
     (defined(HAVE_STDATOMIC_H) || defined(__ATOMIC_SEQ_CST))
 #define USE_NATIVE_MUTEX
 #endif
@@ -1397,15 +1397,6 @@ g_system_thread_set_name (const gchar *name)
 /* {{{1 GMutex and GCond futex implementation */
 
 #if defined(USE_NATIVE_MUTEX)
-
-#include <linux/futex.h>
-#include <sys/syscall.h>
-
-#ifndef FUTEX_WAIT_PRIVATE
-#define FUTEX_WAIT_PRIVATE FUTEX_WAIT
-#define FUTEX_WAKE_PRIVATE FUTEX_WAKE
-#endif
-
 /* We should expand the set of operations available in gatomic once we
  * have better C11 support in GCC in common distributions (ie: 4.9).
  *
@@ -1500,8 +1491,8 @@ g_mutex_lock_slowpath (GMutex *mutex)
    */
   while (exchange_acquire (&mutex->i[0], G_MUTEX_STATE_CONTENDED) != G_MUTEX_STATE_EMPTY)
     {
-      syscall (__NR_futex, &mutex->i[0], (gsize) FUTEX_WAIT_PRIVATE,
-               G_MUTEX_STATE_CONTENDED, NULL);
+      g_futex_simple (&mutex->i[0], (gsize) FUTEX_WAIT_PRIVATE,
+                      G_MUTEX_STATE_CONTENDED, NULL);
     }
 }
 
@@ -1519,7 +1510,7 @@ g_mutex_unlock_slowpath (GMutex *mutex,
       g_abort ();
     }
 
-  syscall (__NR_futex, &mutex->i[0], (gsize) FUTEX_WAKE_PRIVATE, (gsize) 1, NULL);
+  g_futex_simple (&mutex->i[0], (gsize) FUTEX_WAKE_PRIVATE, (gsize) 1, NULL);
 }
 
 void
@@ -1587,7 +1578,7 @@ g_cond_wait (GCond  *cond,
   guint sampled = (guint) g_atomic_int_get (&cond->i[0]);
 
   g_mutex_unlock (mutex);
-  syscall (__NR_futex, &cond->i[0], (gsize) FUTEX_WAIT_PRIVATE, (gsize) sampled, NULL);
+  g_futex_simple (&cond->i[0], (gsize) FUTEX_WAIT_PRIVATE, (gsize) sampled, NULL);
   g_mutex_lock (mutex);
 }
 
@@ -1596,7 +1587,7 @@ g_cond_signal (GCond *cond)
 {
   g_atomic_int_inc (&cond->i[0]);
 
-  syscall (__NR_futex, &cond->i[0], (gsize) FUTEX_WAKE_PRIVATE, (gsize) 1, NULL);
+  g_futex_simple (&cond->i[0], (gsize) FUTEX_WAKE_PRIVATE, (gsize) 1, NULL);
 }
 
 void
@@ -1604,7 +1595,7 @@ g_cond_broadcast (GCond *cond)
 {
   g_atomic_int_inc (&cond->i[0]);
 
-  syscall (__NR_futex, &cond->i[0], (gsize) FUTEX_WAKE_PRIVATE, (gsize) INT_MAX, NULL);
+  g_futex_simple (&cond->i[0], (gsize) FUTEX_WAKE_PRIVATE, (gsize) INT_MAX, NULL);
 }
 
 gboolean
@@ -1614,12 +1605,6 @@ g_cond_wait_until (GCond  *cond,
 {
   struct timespec now;
   struct timespec span;
-#ifdef __NR_futex_time64
-  long span_arg[2];
-  G_STATIC_ASSERT (sizeof (span_arg[0]) == 4);
-#else
-  struct timespec span_arg;
-#endif
 
   guint sampled;
   int res;
@@ -1640,37 +1625,82 @@ g_cond_wait_until (GCond  *cond,
   if (span.tv_sec < 0)
     return FALSE;
 
-  /* On x32 (ILP32 ABI on x86_64) and potentially sparc64, the raw futex()
-   * syscall takes a 32-bit timespan argument *regardless* of whether userspace
-   * is using 32-bit or 64-bit `struct timespec`. This means that we can’t
-   * unconditionally pass a `struct timespec` pointer into the syscall.
+  /* `struct timespec` as defined by the libc headers does not necessarily
+   * have any relation to the one used by the kernel for the `futex` syscall.
    *
-   * Assume that any such platform is new enough to define the
-   * `__NR_futex_time64` workaround syscall (which accepts 64-bit timespecs,
-   * introduced in kernel 5.1), and use that as a proxy for whether to pass in
-   * `long[2]` or `struct timespec`.
+   * Specifically, the libc headers might use 64-bit `time_t` while the kernel
+   * headers use 32-bit `__kernel_old_time_t` on certain systems.
    *
-   * As per https://lwn.net/Articles/776427/, the `time64` syscalls only exist
-   * on 32-bit platforms, so in this case `sizeof(long)` should always be
-   * 32 bits.
+   * To get around this problem we
+   *   a) check if `futex_time64` is available, which only exists on 32-bit
+   *      platforms and always uses 64-bit `time_t`.
+   *   b) otherwise (or if that returns `ENOSYS`), we call the normal `futex`
+   *      syscall with the `struct timespec` used by the kernel, which uses
+   *      `__kernel_long_t` for both its fields. We use that instead of
+   *      `__kernel_old_time_t` because it is equivalent and available in the
+   *      kernel headers for a longer time.
    *
-   * Don’t bother actually calling `__NR_futex_time64` as the `span` is relative
-   * and hence very unlikely to overflow, even if using 32-bit longs.
+   * Also some 32-bit systems do not define `__NR_futex` at all and only
+   * define `__NR_futex_time64`.
    */
-#ifdef __NR_futex_time64
-  span_arg[0] = span.tv_sec;
-  span_arg[1] = span.tv_nsec;
-#else
-  span_arg = span;
-#endif
 
   sampled = cond->i[0];
   g_mutex_unlock (mutex);
-  res = syscall (__NR_futex, &cond->i[0], (gsize) FUTEX_WAIT_PRIVATE, (gsize) sampled, &span_arg);
-  success = (res < 0 && errno == ETIMEDOUT) ? FALSE : TRUE;
-  g_mutex_lock (mutex);
 
-  return success;
+#ifdef __NR_futex_time64
+  {
+    struct
+    {
+      gint64 tv_sec;
+      gint64 tv_nsec;
+    } span_arg;
+
+    span_arg.tv_sec = span.tv_sec;
+    span_arg.tv_nsec = span.tv_nsec;
+
+    res = syscall (__NR_futex_time64, &cond->i[0], (gsize) FUTEX_WAIT_PRIVATE, (gsize) sampled, &span_arg);
+
+    /* If the syscall does not exist (`ENOSYS`), we retry again below with the
+     * normal `futex` syscall. This can happen if newer kernel headers are
+     * used than the kernel that is actually running.
+     */
+#  ifdef __NR_futex
+    if (res >= 0 || errno != ENOSYS)
+#  endif /* defined(__NR_futex) */
+      {
+        success = (res < 0 && errno == ETIMEDOUT) ? FALSE : TRUE;
+        g_mutex_lock (mutex);
+
+        return success;
+      }
+  }
+#endif
+
+#ifdef __NR_futex
+  {
+    struct
+    {
+      __kernel_long_t tv_sec;
+      __kernel_long_t tv_nsec;
+    } span_arg;
+
+    /* Make sure to only ever call this if the end time actually fits into the target type */
+    if (G_UNLIKELY (sizeof (__kernel_long_t) < 8 && span.tv_sec > G_MAXINT32))
+      g_error ("%s: Can’t wait for more than %us", G_STRFUNC, G_MAXINT32);
+
+    span_arg.tv_sec = span.tv_sec;
+    span_arg.tv_nsec = span.tv_nsec;
+
+    res = syscall (__NR_futex, &cond->i[0], (gsize) FUTEX_WAIT_PRIVATE, (gsize) sampled, &span_arg);
+    success = (res < 0 && errno == ETIMEDOUT) ? FALSE : TRUE;
+    g_mutex_lock (mutex);
+
+    return success;
+  }
+#endif /* defined(__NR_futex) */
+
+  /* We can't end up here because of the checks above */
+  g_assert_not_reached ();
 }
 
 #endif
diff --git a/glib/gthreadprivate.h b/glib/gthreadprivate.h
index 2ae705d4f5..6eaf422753 100644
--- a/glib/gthreadprivate.h
+++ b/glib/gthreadprivate.h
@@ -40,6 +40,55 @@ struct  _GRealThread
 
 /* system thread implementation (gthread-posix.c, gthread-win32.c) */
 
+#if defined(HAVE_FUTEX) || defined(HAVE_FUTEX_TIME64)
+#include <linux/futex.h>
+#include <sys/syscall.h>
+#include <unistd.h>
+
+#ifndef FUTEX_WAIT_PRIVATE
+#define FUTEX_WAIT_PRIVATE FUTEX_WAIT
+#define FUTEX_WAKE_PRIVATE FUTEX_WAKE
+#endif
+
+/* Wrapper macro to call `futex_time64` and/or `futex` with simple
+ * parameters and without returning the return value.
+ *
+ * If the `futex_time64` syscall does not exist (`ENOSYS`), we retry again
+ * with the normal `futex` syscall. This can happen if newer kernel headers
+ * are used than the kernel that is actually running.
+ *
+ * This must not be called with a timeout parameter as that differs
+ * in size between the two syscall variants!
+ */
+#if defined(__NR_futex) && defined(__NR_futex_time64)
+#define g_futex_simple(uaddr, futex_op, ...)                                     \
+  G_STMT_START                                                                   \
+  {                                                                              \
+    int res = syscall (__NR_futex_time64, uaddr, (gsize) futex_op, __VA_ARGS__); \
+    if (res < 0 && errno == ENOSYS)                                              \
+      syscall (__NR_futex, uaddr, (gsize) futex_op, __VA_ARGS__);                \
+  }                                                                              \
+  G_STMT_END
+#elif defined(__NR_futex_time64)
+#define g_futex_simple(uaddr, futex_op, ...)                           \
+  G_STMT_START                                                         \
+  {                                                                    \
+    syscall (__NR_futex_time64, uaddr, (gsize) futex_op, __VA_ARGS__); \
+  }                                                                    \
+  G_STMT_END
+#elif defined(__NR_futex)
+#define g_futex_simple(uaddr, futex_op, ...)                    \
+  G_STMT_START                                                  \
+  {                                                             \
+    syscall (__NR_futex, uaddr, (gsize) futex_op, __VA_ARGS__); \
+  }                                                             \
+  G_STMT_END
+#else /* !defined(__NR_futex) && !defined(__NR_futex_time64) */
+#error "Neither __NR_futex nor __NR_futex_time64 are defined but were found by meson"
+#endif /* defined(__NR_futex) && defined(__NR_futex_time64) */
+
+#endif
+
 /* Platform-specific scheduler settings for a thread */
 typedef struct
 {
diff --git a/meson.build b/meson.build
index c763857ea9..75a3db504e 100644
--- a/meson.build
+++ b/meson.build
@@ -896,6 +896,15 @@ if cc.compiles('''#include <linux/futex.h>
                }''', name : 'futex(2) system call')
   glib_conf.set('HAVE_FUTEX', 1)
 endif
+if cc.compiles('''#include <linux/futex.h>
+               #include <sys/syscall.h>
+               #include <unistd.h>
+               int main (int argc, char ** argv) {
+                 syscall (__NR_futex_time64, NULL, FUTEX_WAKE, FUTEX_WAIT);
+                 return 0;
+               }''', name : 'futex(2) system call')
+  glib_conf.set('HAVE_FUTEX_TIME64', 1)
+endif
 
 # Check for eventfd(2)
 if cc.links('''#include <sys/eventfd.h>
-- 
GitLab