summaryrefslogtreecommitdiffstats
path: root/recipes-multimedia/gstreamer/gstreamer1.0/0006-systemclock-Use-futex_time64-syscall-if-available-32.patch
blob: f3e7975c0765198c090ad91881c7a8494b6218cd (plain)
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
From 9ca6b1196e31a4a483fdddad191221616e2c9c3d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= <sebastian@centricular.com>
Date: Mon, 12 Dec 2022 11:34:51 +0200
Subject: [PATCH] systemclock: Use `futex_time64` syscall if available (32-bit
 systems) and use correct `struct timespec` definition

See also https://gitlab.gnome.org/GNOME/glib/-/issues/2634

Fixes https://gitlab.freedesktop.org/gstreamer/gstreamer/-/issues/1648

Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3561>
---
 gst/gstsystemclock.c | 125 ++++++++++++++++++---
 meson.build          |  11 +-
 2 files changed, 121 insertions(+), 15 deletions(-)

diff --git a/gst/gstsystemclock.c b/gst/gstsystemclock.c
index 6d0b6ec47b..7d8efb171e 100644
--- a/gst/gstsystemclock.c
+++ b/gst/gstsystemclock.c
@@ -70,11 +70,15 @@
 #define GST_SYSTEM_CLOCK_WAIT(clock)            g_cond_wait(GST_SYSTEM_CLOCK_GET_COND(clock),GST_SYSTEM_CLOCK_GET_LOCK(clock))
 #define GST_SYSTEM_CLOCK_BROADCAST(clock)       g_cond_broadcast(GST_SYSTEM_CLOCK_GET_COND(clock))
 
-#if defined(HAVE_FUTEX)
+#if defined(HAVE_FUTEX) || defined(HAVE_FUTEX_TIME64)
 #include <unistd.h>
 #include <linux/futex.h>
 #include <sys/syscall.h>
 
+#if !defined(__NR_futex) && !defined(__NR_futex_time64)
+#error "Neither __NR_futex nor __NR_futex_time64 are defined but were found by meson"
+#endif
+
 #ifndef FUTEX_WAIT_BITSET_PRIVATE
 #define FUTEX_WAIT_BITSET_PRIVATE FUTEX_WAIT_BITSET
 #endif
@@ -123,14 +127,35 @@ gst_futex_cond_broadcast (guint * cond_val)
 {
   g_atomic_int_inc (cond_val);
 
+#if defined(__NR_futex_time64)
+  {
+    int res;
+    res = syscall (__NR_futex_time64, cond_val, (gsize) FUTEX_WAKE_PRIVATE,
+        (gsize) INT_MAX, NULL);
+
+    /* 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) {
+#else
+    {
+#endif
+      return;
+    }
+  }
+#endif
+
+#if defined(__NR_futex)
   syscall (__NR_futex, cond_val, (gsize) FUTEX_WAKE_PRIVATE, (gsize) INT_MAX,
       NULL);
+#endif
 }
 
 static gboolean
 gst_futex_cond_wait_until (guint * cond_val, GMutex * mutex, gint64 end_time)
 {
-  struct timespec end;
   guint sampled;
   int res;
   gboolean success;
@@ -138,20 +163,92 @@ gst_futex_cond_wait_until (guint * cond_val, GMutex * mutex, gint64 end_time)
   if (end_time < 0)
     return FALSE;
 
-  end.tv_sec = end_time / 1000000000;
-  end.tv_nsec = end_time % 1000000000;
-
   sampled = *cond_val;
   g_mutex_unlock (mutex);
-  /* we use FUTEX_WAIT_BITSET_PRIVATE rather than FUTEX_WAIT_PRIVATE to be
-   * able to use absolute time */
-  res =
-      syscall (__NR_futex, cond_val, (gsize) FUTEX_WAIT_BITSET_PRIVATE,
-      (gsize) sampled, &end, NULL, FUTEX_BITSET_MATCH_ANY);
-  success = (res < 0 && errno == ETIMEDOUT) ? FALSE : TRUE;
-  g_mutex_lock (mutex);
-
-  return success;
+
+  /* `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.
+   *
+   * 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_t` 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.
+   *
+   * Also some 32-bit systems do not define `__NR_futex` at all and only
+   * define `__NR_futex_time64`.
+   */
+
+#ifdef __NR_futex_time64
+  {
+    struct
+    {
+      gint64 tv_sec;
+      gint64 tv_nsec;
+    } end;
+
+    end.tv_sec = end_time / 1000000000;
+    end.tv_nsec = end_time % 1000000000;
+
+    /* we use FUTEX_WAIT_BITSET_PRIVATE rather than FUTEX_WAIT_PRIVATE to be
+     * able to use absolute time */
+    res =
+        syscall (__NR_futex_time64, cond_val, (gsize) FUTEX_WAIT_BITSET_PRIVATE,
+        (gsize) sampled, &end, NULL, FUTEX_BITSET_MATCH_ANY);
+
+    /* 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) {
+#else
+    {
+#endif
+      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;
+    } end;
+
+    /* Make sure to only ever call this if the end time actually fits into the
+     * target type */
+    g_assert (sizeof (__kernel_long_t) >= 8
+        || end_time / 1000000000 <= G_MAXINT32);
+
+    end.tv_sec = end_time / 1000000000;
+    end.tv_nsec = end_time % 1000000000;
+
+    /* we use FUTEX_WAIT_BITSET_PRIVATE rather than FUTEX_WAIT_PRIVATE to be
+     * able to use absolute time */
+    res =
+        syscall (__NR_futex, cond_val, (gsize) FUTEX_WAIT_BITSET_PRIVATE,
+        (gsize) sampled, &end, NULL, FUTEX_BITSET_MATCH_ANY);
+    success = (res < 0 && errno == ETIMEDOUT) ? FALSE : TRUE;
+    g_mutex_lock (mutex);
+
+    return success;
+  }
+#endif
+
+  /* We can't end up here because of the checks above */
+  g_assert_not_reached ();
 }
 
 #elif defined (G_OS_UNIX)
diff --git a/meson.build b/meson.build
index 0641f7678b..7331c66150 100644
--- a/meson.build
+++ b/meson.build
@@ -294,7 +294,7 @@ if cc.has_header_symbol('pthread.h', 'pthread_cond_timedwait_relative_np')
 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) {
@@ -303,6 +303,15 @@ if cc.links('''#include <linux/futex.h>
                }''', name : 'futex(2) system call')
   cdata.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')
+  cdata.set('HAVE_FUTEX_TIME64', 1)
+endif
 
 # Check for posix timers and monotonic clock
 time_prefix = '#include <time.h>\n'
-- 
2.25.1