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>
This commit is contained in:
Sebastian Dröge 2022-12-12 11:34:51 +02:00 committed by GStreamer Marge Bot
parent 6687e7ed17
commit 9ca6b1196e
2 changed files with 120 additions and 14 deletions

View file

@ -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)

View file

@ -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'