mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2024-11-11 11:56:16 +01:00
Merge branch 'futex64' into 'main'
glib/gthread-posix: Conditionally use `futex` and/or `futex_time64` syscalls... See merge request GNOME/glib!3120
This commit is contained in:
commit
d0f456a504
@ -35,6 +35,7 @@
|
|||||||
|
|
||||||
#ifdef G_BIT_LOCK_FORCE_FUTEX_EMULATION
|
#ifdef G_BIT_LOCK_FORCE_FUTEX_EMULATION
|
||||||
#undef HAVE_FUTEX
|
#undef HAVE_FUTEX
|
||||||
|
#undef HAVE_FUTEX_TIME64
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef HAVE_FUTEX
|
#ifndef HAVE_FUTEX
|
||||||
@ -42,7 +43,7 @@ static GMutex g_futex_mutex;
|
|||||||
static GSList *g_futex_address_list = NULL;
|
static GSList *g_futex_address_list = NULL;
|
||||||
#endif
|
#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
|
* 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
|
* 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. :)
|
* 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 >
|
/* < private >
|
||||||
* g_futex_wait:
|
* g_futex_wait:
|
||||||
@ -81,7 +74,7 @@ static void
|
|||||||
g_futex_wait (const gint *address,
|
g_futex_wait (const gint *address,
|
||||||
gint value)
|
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 >
|
/* < private >
|
||||||
@ -98,7 +91,7 @@ g_futex_wait (const gint *address,
|
|||||||
static void
|
static void
|
||||||
g_futex_wake (const gint *address)
|
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
|
#else
|
||||||
|
@ -74,7 +74,7 @@
|
|||||||
#include <sys/syscall.h>
|
#include <sys/syscall.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(HAVE_FUTEX) && \
|
#if (defined(HAVE_FUTEX) || defined(HAVE_FUTEX_TIME64)) && \
|
||||||
(defined(HAVE_STDATOMIC_H) || defined(__ATOMIC_SEQ_CST))
|
(defined(HAVE_STDATOMIC_H) || defined(__ATOMIC_SEQ_CST))
|
||||||
#define USE_NATIVE_MUTEX
|
#define USE_NATIVE_MUTEX
|
||||||
#endif
|
#endif
|
||||||
@ -1394,15 +1394,6 @@ g_system_thread_set_name (const gchar *name)
|
|||||||
/* {{{1 GMutex and GCond futex implementation */
|
/* {{{1 GMutex and GCond futex implementation */
|
||||||
|
|
||||||
#if defined(USE_NATIVE_MUTEX)
|
#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
|
/* We should expand the set of operations available in gatomic once we
|
||||||
* have better C11 support in GCC in common distributions (ie: 4.9).
|
* have better C11 support in GCC in common distributions (ie: 4.9).
|
||||||
*
|
*
|
||||||
@ -1497,7 +1488,7 @@ g_mutex_lock_slowpath (GMutex *mutex)
|
|||||||
*/
|
*/
|
||||||
while (exchange_acquire (&mutex->i[0], G_MUTEX_STATE_CONTENDED) != G_MUTEX_STATE_EMPTY)
|
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_futex_simple (&mutex->i[0], (gsize) FUTEX_WAIT_PRIVATE,
|
||||||
G_MUTEX_STATE_CONTENDED, NULL);
|
G_MUTEX_STATE_CONTENDED, NULL);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1516,7 +1507,7 @@ g_mutex_unlock_slowpath (GMutex *mutex,
|
|||||||
g_abort ();
|
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
|
void
|
||||||
@ -1584,7 +1575,7 @@ g_cond_wait (GCond *cond,
|
|||||||
guint sampled = (guint) g_atomic_int_get (&cond->i[0]);
|
guint sampled = (guint) g_atomic_int_get (&cond->i[0]);
|
||||||
|
|
||||||
g_mutex_unlock (mutex);
|
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);
|
g_mutex_lock (mutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1593,7 +1584,7 @@ g_cond_signal (GCond *cond)
|
|||||||
{
|
{
|
||||||
g_atomic_int_inc (&cond->i[0]);
|
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
|
void
|
||||||
@ -1601,7 +1592,7 @@ g_cond_broadcast (GCond *cond)
|
|||||||
{
|
{
|
||||||
g_atomic_int_inc (&cond->i[0]);
|
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
|
gboolean
|
||||||
@ -1611,12 +1602,6 @@ g_cond_wait_until (GCond *cond,
|
|||||||
{
|
{
|
||||||
struct timespec now;
|
struct timespec now;
|
||||||
struct timespec span;
|
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;
|
guint sampled;
|
||||||
int res;
|
int res;
|
||||||
@ -1637,38 +1622,83 @@ g_cond_wait_until (GCond *cond,
|
|||||||
if (span.tv_sec < 0)
|
if (span.tv_sec < 0)
|
||||||
return FALSE;
|
return FALSE;
|
||||||
|
|
||||||
/* On x32 (ILP32 ABI on x86_64) and potentially sparc64, the raw futex()
|
/* `struct timespec` as defined by the libc headers does not necessarily
|
||||||
* syscall takes a 32-bit timespan argument *regardless* of whether userspace
|
* have any relation to the one used by the kernel for the `futex` syscall.
|
||||||
* is using 32-bit or 64-bit `struct timespec`. This means that we can’t
|
|
||||||
* unconditionally pass a `struct timespec` pointer into the syscall.
|
|
||||||
*
|
*
|
||||||
* Assume that any such platform is new enough to define the
|
* Specifically, the libc headers might use 64-bit `time_t` while the kernel
|
||||||
* `__NR_futex_time64` workaround syscall (which accepts 64-bit timespecs,
|
* headers use 32-bit `__kernel_old_time_t` on certain systems.
|
||||||
* introduced in kernel 5.1), and use that as a proxy for whether to pass in
|
|
||||||
* `long[2]` or `struct timespec`.
|
|
||||||
*
|
*
|
||||||
* As per https://lwn.net/Articles/776427/, the `time64` syscalls only exist
|
* To get around this problem we
|
||||||
* on 32-bit platforms, so in this case `sizeof(long)` should always be
|
* a) check if `futex_time64` is available, which only exists on 32-bit
|
||||||
* 32 bits.
|
* 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
|
* Also some 32-bit systems do not define `__NR_futex` at all and only
|
||||||
* and hence very unlikely to overflow, even if using 32-bit longs.
|
* 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];
|
sampled = cond->i[0];
|
||||||
g_mutex_unlock (mutex);
|
g_mutex_unlock (mutex);
|
||||||
|
|
||||||
|
#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);
|
res = syscall (__NR_futex, &cond->i[0], (gsize) FUTEX_WAIT_PRIVATE, (gsize) sampled, &span_arg);
|
||||||
success = (res < 0 && errno == ETIMEDOUT) ? FALSE : TRUE;
|
success = (res < 0 && errno == ETIMEDOUT) ? FALSE : TRUE;
|
||||||
g_mutex_lock (mutex);
|
g_mutex_lock (mutex);
|
||||||
|
|
||||||
return success;
|
return success;
|
||||||
}
|
}
|
||||||
|
#endif /* defined(__NR_futex) */
|
||||||
|
|
||||||
|
/* We can't end up here because of the checks above */
|
||||||
|
g_assert_not_reached ();
|
||||||
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -40,6 +40,55 @@ struct _GRealThread
|
|||||||
|
|
||||||
/* system thread implementation (gthread-posix.c, gthread-win32.c) */
|
/* 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 */
|
/* Platform-specific scheduler settings for a thread */
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
|
11
meson.build
11
meson.build
@ -887,7 +887,7 @@ if host_system == 'qnx'
|
|||||||
endif
|
endif
|
||||||
|
|
||||||
# Check for futex(2)
|
# Check for futex(2)
|
||||||
if cc.links('''#include <linux/futex.h>
|
if cc.compiles('''#include <linux/futex.h>
|
||||||
#include <sys/syscall.h>
|
#include <sys/syscall.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
int main (int argc, char ** argv) {
|
int main (int argc, char ** argv) {
|
||||||
@ -896,6 +896,15 @@ if cc.links('''#include <linux/futex.h>
|
|||||||
}''', name : 'futex(2) system call')
|
}''', name : 'futex(2) system call')
|
||||||
glib_conf.set('HAVE_FUTEX', 1)
|
glib_conf.set('HAVE_FUTEX', 1)
|
||||||
endif
|
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)
|
# Check for eventfd(2)
|
||||||
if cc.links('''#include <sys/eventfd.h>
|
if cc.links('''#include <sys/eventfd.h>
|
||||||
|
Loading…
Reference in New Issue
Block a user