From 9ca6b1196e31a4a483fdddad191221616e2c9c3d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= 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: --- subprojects/gstreamer/gst/gstsystemclock.c | 123 ++++++++++++++++++--- subprojects/gstreamer/meson.build | 11 +- 2 files changed, 120 insertions(+), 14 deletions(-) diff --git a/subprojects/gstreamer/gst/gstsystemclock.c b/subprojects/gstreamer/gst/gstsystemclock.c index 6d0b6ec47b..7d8efb171e 100644 --- a/subprojects/gstreamer/gst/gstsystemclock.c +++ b/subprojects/gstreamer/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 #include #include +#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/subprojects/gstreamer/meson.build b/subprojects/gstreamer/meson.build index 0641f7678b..7331c66150 100644 --- a/subprojects/gstreamer/meson.build +++ b/subprojects/gstreamer/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 +if cc.compiles('''#include #include #include int main (int argc, char ** argv) { @@ -303,6 +303,15 @@ if cc.links('''#include }''', name : 'futex(2) system call') cdata.set('HAVE_FUTEX', 1) endif +if cc.compiles('''#include + #include + #include + 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 \n'