Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

gstreamer1.0: use futex_time64 syscall if available (32-bit systems) #1725 #1737

Merged
merged 1 commit into from
Jan 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,211 @@
From 9ca6b1196e31a4a483fdddad191221616e2c9c3d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= <[email protected]>
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

1 change: 1 addition & 0 deletions recipes-multimedia/gstreamer/gstreamer1.0_1.20.3.imx.bb
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ SRC_URI = "https://gstreamer.freedesktop.org/src/gstreamer/gstreamer-${PV}.tar.x
file://0003-tests-use-a-dictionaries-for-environment.patch;striplevel=3 \
file://0004-tests-add-helper-script-to-run-the-installed_tests.patch;striplevel=3 \
file://0005-tests-remove-gstbin-test_watch_for_state_change-test.patch \
file://0006-systemclock-Use-futex_time64-syscall-if-available-32.patch \
"
SRC_URI[sha256sum] = "de094a404a3ad8f4977829ea87edf695a4da0b5c8f613ebe54ab414bac89f031"

Expand Down
Loading