File fsync_futex_waitv.patch of Package wine
From 04904bbab87d5662e40743f26534a74ee7d2c943 Mon Sep 17 00:00:00 2001
From: Tk-Glitch <ti3nou@gmail.com>
Date: Wed, 16 Mar 2022 11:50:28 +0100
Subject: Import futex_waitv support from Proton Experimental
https://github.com/ValveSoftware/wine/tree/205874085341305cac84ae61a0141d2fa5b892dd
From ntdll/fsync: Support futex_waitv() API to ntdll: Include linux/futex.h in fsync.c
diff --git a/configure b/configure
index 7ea8b1c0ceb..03533857085 100755
--- a/configure
+++ b/configure
@@ -7982,6 +7982,12 @@ if test "x$ac_cv_header_linux_filter_h" = xyes
then :
printf "%s\n" "#define HAVE_LINUX_FILTER_H 1" >>confdefs.h
+fi
+ac_fn_c_check_header_compile "$LINENO" "linux/futex.h" "ac_cv_header_linux_futex_h" "$ac_includes_default"
+if test "x$ac_cv_header_linux_futex_h" = xyes
+then :
+ printf "%s\n" "#define HAVE_LINUX_FUTEX_H 1" >>confdefs.h
+
fi
ac_fn_c_check_header_compile "$LINENO" "linux/hdreg.h" "ac_cv_header_linux_hdreg_h" "$ac_includes_default"
if test "x$ac_cv_header_linux_hdreg_h" = xyes
diff --git a/configure.ac b/configure.ac
index 815e40fa08d..0082bd7e4a7 100644
--- a/configure.ac
+++ b/configure.ac
@@ -441,6 +441,7 @@ AC_CHECK_HEADERS(\
link.h \
linux/cdrom.h \
linux/filter.h \
+ linux/futex.h \
linux/hdreg.h \
linux/hidraw.h \
linux/input.h \
diff --git a/dlls/ntdll/unix/esync.c b/dlls/ntdll/unix/esync.c
index 3b97bfd62ad..0c6c5b8553b 100644
--- a/dlls/ntdll/unix/esync.c
+++ b/dlls/ntdll/unix/esync.c
@@ -953,6 +953,8 @@ static NTSTATUS __esync_wait_objects( DWORD count, const HANDLE *handles, BOOLEA
if (event->signaled)
{
+ if (ac_odyssey && alertable)
+ usleep( 0 );
if ((size = read( obj->fd, &value, sizeof(value) )) == sizeof(value))
{
TRACE("Woken up by handle %p [%d].\n", handles[i], i);
@@ -968,6 +970,12 @@ static NTSTATUS __esync_wait_objects( DWORD count, const HANDLE *handles, BOOLEA
if (event->signaled)
{
+ if (ac_odyssey && alertable)
+ {
+ usleep( 0 );
+ if (!event->signaled)
+ break;
+ }
TRACE("Woken up by handle %p [%d].\n", handles[i], i);
return i;
}
@@ -996,6 +1004,9 @@ static NTSTATUS __esync_wait_objects( DWORD count, const HANDLE *handles, BOOLEA
while (1)
{
+ if (ac_odyssey && alertable)
+ usleep( 0 );
+
ret = do_poll( fds, pollcount, timeout ? &end : NULL );
if (ret > 0)
{
diff --git a/dlls/ntdll/unix/file.c b/dlls/ntdll/unix/file.c
index d0f8be6c309..f9f6e252b6c 100644
--- a/dlls/ntdll/unix/file.c
+++ b/dlls/ntdll/unix/file.c
@@ -5363,6 +5363,230 @@ static NTSTATUS set_pending_write( HANDLE device )
return status;
}
+static pthread_mutex_t async_file_read_mutex = PTHREAD_MUTEX_INITIALIZER;
+static pthread_cond_t async_file_read_cond = PTHREAD_COND_INITIALIZER;
+
+struct async_file_read_job
+{
+ HANDLE handle;
+ int unix_handle;
+ int needs_close;
+ HANDLE event;
+ IO_STATUS_BLOCK *io;
+ void *buffer;
+ ULONG length;
+ LARGE_INTEGER offset;
+ DWORD thread_id;
+ LONG cancelled;
+ struct list queue_entry;
+ struct async_file_read_job *next;
+};
+
+
+static struct list async_file_read_queue = LIST_INIT( async_file_read_queue );
+static struct async_file_read_job *async_file_read_running, *async_file_read_free;
+
+static void async_file_complete_io( struct async_file_read_job *job, NTSTATUS status, ULONG total )
+{
+ job->io->Status = status;
+ job->io->Information = total;
+
+ if (job->event) NtSetEvent( job->event, NULL );
+}
+
+static void *async_file_read_thread(void *dummy)
+{
+ struct async_file_read_job *job, *ptr;
+ ULONG buffer_length = 0;
+ void *buffer = NULL;
+ struct list *entry;
+ NTSTATUS status;
+ ULONG total;
+ int result;
+
+ pthread_mutex_lock( &async_file_read_mutex );
+ while (1)
+ {
+ while (!(entry = list_head( &async_file_read_queue )))
+ {
+ pthread_cond_wait( &async_file_read_cond, &async_file_read_mutex );
+ continue;
+ }
+
+ job = LIST_ENTRY( entry, struct async_file_read_job, queue_entry );
+ list_remove( entry );
+
+ total = 0;
+
+ if ( job->cancelled )
+ {
+ pthread_mutex_unlock( &async_file_read_mutex );
+ status = STATUS_CANCELLED;
+ goto done;
+ }
+
+ job->next = async_file_read_running;
+ async_file_read_running = job;
+ pthread_mutex_unlock( &async_file_read_mutex );
+
+ if (!buffer_length)
+ {
+ buffer = malloc(job->length);
+ buffer_length = job->length;
+ }
+ else if (buffer_length < job->length)
+ {
+ buffer = realloc(buffer, job->length);
+ buffer_length = job->length;
+ }
+
+ while ((result = pread( job->unix_handle, buffer, job->length, job->offset.QuadPart )) == -1)
+ {
+ if (errno != EINTR)
+ {
+ status = errno_to_status( errno );
+ goto done;
+ }
+ if (job->cancelled)
+ break;
+ }
+
+ total = result;
+ status = (total || !job->length) ? STATUS_SUCCESS : STATUS_END_OF_FILE;
+done:
+ if (job->needs_close) close( job->unix_handle );
+
+ if (!InterlockedCompareExchange(&job->cancelled, 1, 0))
+ {
+ if (status == STATUS_SUCCESS)
+ memcpy( job->buffer, buffer, total );
+
+ async_file_complete_io( job, status, total );
+ }
+
+ pthread_mutex_lock( &async_file_read_mutex );
+
+ if (status != STATUS_CANCELLED)
+ {
+ ptr = async_file_read_running;
+ if (job == ptr)
+ {
+ async_file_read_running = job->next;
+ }
+ else
+ {
+ while (ptr && ptr->next != job)
+ ptr = ptr->next;
+
+ assert( ptr );
+ ptr->next = job->next;
+ }
+ }
+
+ job->next = async_file_read_free;
+ async_file_read_free = job;
+ }
+
+ return NULL;
+}
+
+static pthread_once_t async_file_read_once = PTHREAD_ONCE_INIT;
+
+static void async_file_read_init(void)
+{
+ pthread_t async_file_read_thread_id;
+ pthread_attr_t pthread_attr;
+
+ ERR("HACK: AC Odyssey async read workaround.\n");
+
+ pthread_attr_init( &pthread_attr );
+ pthread_attr_setscope( &pthread_attr, PTHREAD_SCOPE_SYSTEM );
+ pthread_attr_setdetachstate( &pthread_attr, PTHREAD_CREATE_DETACHED );
+
+ pthread_create( &async_file_read_thread_id, &pthread_attr, (void * (*)(void *))async_file_read_thread, NULL);
+ pthread_attr_destroy( &pthread_attr );
+}
+
+static NTSTATUS queue_async_file_read( HANDLE handle, int unix_handle, int needs_close, HANDLE event,
+ IO_STATUS_BLOCK *io, void *buffer, ULONG length, LARGE_INTEGER *offset )
+{
+ struct async_file_read_job *job;
+
+ pthread_once( &async_file_read_once, async_file_read_init );
+
+ NtResetEvent( event, NULL );
+
+ pthread_mutex_lock( &async_file_read_mutex );
+
+ if (async_file_read_free)
+ {
+ job = async_file_read_free;
+ async_file_read_free = async_file_read_free->next;
+ }
+ else
+ {
+ if (!(job = malloc( sizeof(*job) )))
+ {
+ pthread_mutex_unlock( &async_file_read_mutex );
+ return STATUS_NO_MEMORY;
+ }
+ }
+
+ job->handle = handle;
+ job->unix_handle = unix_handle;
+ job->needs_close = needs_close;
+ job->event = event;
+ job->io = io;
+ job->buffer = buffer;
+ job->length = length;
+ job->offset = *offset;
+ job->thread_id = GetCurrentThreadId();
+ job->cancelled = 0;
+
+ list_add_tail( &async_file_read_queue, &job->queue_entry );
+
+ pthread_cond_signal( &async_file_read_cond );
+ pthread_mutex_unlock( &async_file_read_mutex );
+
+ return STATUS_PENDING;
+}
+
+static NTSTATUS cancel_async_file_read( HANDLE handle, IO_STATUS_BLOCK *io )
+{
+ DWORD thread_id = GetCurrentThreadId();
+ struct async_file_read_job *job;
+ unsigned int count = 0;
+
+ TRACE( "handle %p, io %p.\n", handle, io );
+
+ pthread_mutex_lock( &async_file_read_mutex );
+ job = async_file_read_running;
+ while (job)
+ {
+ if (((io && job->io == io)
+ || (!io && job->handle == handle && job->thread_id == thread_id))
+ && !InterlockedCompareExchange(&job->cancelled, 1, 0))
+ {
+ async_file_complete_io( job, STATUS_CANCELLED, 0 );
+ ++count;
+ }
+ job = job->next;
+ }
+
+ LIST_FOR_EACH_ENTRY( job, &async_file_read_queue, struct async_file_read_job, queue_entry )
+ {
+ if (((io && job->io == io)
+ || (!io && job->handle == handle && job->thread_id == thread_id))
+ && !InterlockedCompareExchange(&job->cancelled, 1, 0))
+ {
+ async_file_complete_io( job, STATUS_CANCELLED, 0 );
+ ++count;
+ }
+ }
+
+ pthread_mutex_unlock( &async_file_read_mutex );
+ return count ? STATUS_SUCCESS : STATUS_NOT_FOUND;
+}
/******************************************************************************
* NtReadFile (NTDLL.@)
@@ -5404,6 +5628,13 @@ NTSTATUS WINAPI NtReadFile( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc, vo
goto done;
}
+ if (ac_odyssey && async_read && length && event && !apc)
+ {
+ status = queue_async_file_read( handle, unix_handle, needs_close, event, io, buffer, length, offset );
+ needs_close = 0;
+ goto err;
+ }
+
if (offset && offset->QuadPart != FILE_USE_FILE_POINTER_POSITION)
{
/* async I/O doesn't make sense on regular files */
@@ -6823,6 +7054,9 @@ NTSTATUS WINAPI NtCancelIoFile( HANDLE handle, IO_STATUS_BLOCK *io_status )
TRACE( "%p %p\n", handle, io_status );
+ if (ac_odyssey && !cancel_async_file_read( handle, NULL ))
+ return (io_status->Status = STATUS_SUCCESS);
+
SERVER_START_REQ( cancel_async )
{
req->handle = wine_server_obj_handle( handle );
@@ -6848,6 +7082,9 @@ NTSTATUS WINAPI NtCancelIoFileEx( HANDLE handle, IO_STATUS_BLOCK *io, IO_STATUS_
TRACE( "%p %p %p\n", handle, io, io_status );
+ if (ac_odyssey && !cancel_async_file_read( handle, io ))
+ return (io_status->Status = STATUS_SUCCESS);
+
SERVER_START_REQ( cancel_async )
{
req->handle = wine_server_obj_handle( handle );
diff --git a/dlls/ntdll/unix/fsync.c b/dlls/ntdll/unix/fsync.c
index f5ae0a821b0..284e2fcce01 100644
--- a/dlls/ntdll/unix/fsync.c
+++ b/dlls/ntdll/unix/fsync.c
@@ -27,6 +27,9 @@
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
+#ifdef HAVE_LINUX_FUTEX_H
+# include <linux/futex.h>
+#endif
#include <limits.h>
#include <stdarg.h>
#include <stdio.h>
@@ -39,6 +42,7 @@
# include <sys/syscall.h>
#endif
#include <unistd.h>
+#include <stdint.h>
#include "ntstatus.h"
#define WIN32_NO_STATUS
@@ -54,43 +58,82 @@
WINE_DEFAULT_DEBUG_CHANNEL(fsync);
#include "pshpack4.h"
-struct futex_wait_block
-{
- int *addr;
-#if __SIZEOF_POINTER__ == 4
- int pad;
+#include "poppack.h"
+
+/* futex_waitv interface */
+
+#ifndef __NR_futex_waitv
+# define __NR_futex_waitv 449
#endif
- int val;
- int bitset;
+
+#ifndef FUTEX_32
+# define FUTEX_32 2
+struct futex_waitv {
+ uint64_t val;
+ uint64_t uaddr;
+ uint32_t flags;
+ uint32_t __reserved;
};
-#include "poppack.h"
+#endif
+
+#define u64_to_ptr(x) (void *)(uintptr_t)(x)
-static inline void small_pause(void)
+struct timespec64
{
-#if defined(__i386__) || defined(__x86_64__)
- __asm__ __volatile__( "rep;nop" : : : "memory" );
-#else
- __asm__ __volatile__( "" : : : "memory" );
-#endif
+ long long tv_sec;
+ long long tv_nsec;
+};
+
+static LONGLONG update_timeout( ULONGLONG end )
+{
+ LARGE_INTEGER now;
+ LONGLONG timeleft;
+
+ NtQuerySystemTime( &now );
+ timeleft = end - now.QuadPart;
+ if (timeleft < 0) timeleft = 0;
+ return timeleft;
}
-static inline int futex_wait_multiple( const struct futex_wait_block *futexes,
- int count, const struct timespec *timeout )
+static inline void futex_vector_set( struct futex_waitv *waitv, int *addr, int val )
{
- return syscall( __NR_futex, futexes, 31, count, timeout, 0, 0 );
+ waitv->uaddr = (uintptr_t) addr;
+ waitv->val = val;
+ waitv->flags = FUTEX_32;
+ waitv->__reserved = 0;
}
-static inline int futex_wake( int *addr, int val )
+static void simulate_sched_quantum(void)
{
- return syscall( __NR_futex, addr, 1, val, NULL, 0, 0 );
+ if (!fsync_simulate_sched_quantum) return;
+ /* futex wait is often very quick to resume a waiting thread when woken.
+ * That reveals synchonization bugs in some games which happen to work on
+ * Windows due to the waiting threads having some minimal delay to wake up. */
+ usleep(0);
}
-static inline int futex_wait( int *addr, int val, struct timespec *timeout )
+static inline int futex_wait_multiple( const struct futex_waitv *futexes,
+ int count, const ULONGLONG *end )
{
- return syscall( __NR_futex, addr, 0, val, timeout, 0, 0 );
+ if (end)
+ {
+ struct timespec64 timeout;
+ ULONGLONG tmp = *end - SECS_1601_TO_1970 * TICKSPERSEC;
+ timeout.tv_sec = tmp / (ULONGLONG)TICKSPERSEC;
+ timeout.tv_nsec = (tmp % TICKSPERSEC) * 100;
+
+ return syscall( __NR_futex_waitv, futexes, count, 0, &timeout, CLOCK_REALTIME );
+ }
+ else
+ {
+ return syscall( __NR_futex_waitv, futexes, count, 0, NULL, 0 );
+ }
}
-static unsigned int spincount = 100;
+static inline int futex_wake( int *addr, int val )
+{
+ return syscall( __NR_futex, addr, 1, val, NULL, 0, 0 );
+}
int do_fsync(void)
{
@@ -99,11 +142,16 @@ int do_fsync(void)
if (do_fsync_cached == -1)
{
- static const struct timespec zero;
- futex_wait_multiple( NULL, 0, &zero );
+ FILE *f;
+ if ((f = fopen( "/sys/kernel/futex2/wait", "r" )))
+ {
+ fclose(f);
+ do_fsync_cached = 0;
+ return do_fsync_cached;
+ }
+
+ syscall( __NR_futex_waitv, NULL, 0, 0, NULL, 0 );
do_fsync_cached = getenv("WINEFSYNC") && atoi(getenv("WINEFSYNC")) && errno != ENOSYS;
- if (getenv("WINEFSYNC_SPINCOUNT"))
- spincount = atoi(getenv("WINEFSYNC_SPINCOUNT"));
}
return do_fsync_cached;
@@ -646,64 +694,30 @@ NTSTATUS fsync_query_mutex( HANDLE handle, void *info, ULONG *ret_len )
return STATUS_SUCCESS;
}
-static LONGLONG update_timeout( ULONGLONG end )
-{
- LARGE_INTEGER now;
- LONGLONG timeleft;
-
- NtQuerySystemTime( &now );
- timeleft = end - now.QuadPart;
- if (timeleft < 0) timeleft = 0;
- return timeleft;
-}
-
static NTSTATUS do_single_wait( int *addr, int val, ULONGLONG *end, BOOLEAN alertable )
{
+ struct futex_waitv futexes[2];
int ret;
+ futex_vector_set( &futexes[0], addr, val );
+
if (alertable)
{
int *apc_futex = ntdll_get_thread_data()->fsync_apc_futex;
- struct futex_wait_block futexes[2];
if (__atomic_load_n( apc_futex, __ATOMIC_SEQ_CST ))
return STATUS_USER_APC;
- futexes[0].addr = addr;
- futexes[0].val = val;
- futexes[1].addr = apc_futex;
- futexes[1].val = 0;
-#if __SIZEOF_POINTER__ == 4
- futexes[0].pad = futexes[1].pad = 0;
-#endif
- futexes[0].bitset = futexes[1].bitset = ~0;
+ futex_vector_set( &futexes[1], apc_futex, 0 );
- if (end)
- {
- LONGLONG timeleft = update_timeout( *end );
- struct timespec tmo_p;
- tmo_p.tv_sec = timeleft / (ULONGLONG)TICKSPERSEC;
- tmo_p.tv_nsec = (timeleft % TICKSPERSEC) * 100;
- ret = futex_wait_multiple( futexes, 2, &tmo_p );
- }
- else
- ret = futex_wait_multiple( futexes, 2, NULL );
+ ret = futex_wait_multiple( futexes, 2, end );
if (__atomic_load_n( apc_futex, __ATOMIC_SEQ_CST ))
return STATUS_USER_APC;
}
else
{
- if (end)
- {
- LONGLONG timeleft = update_timeout( *end );
- struct timespec tmo_p;
- tmo_p.tv_sec = timeleft / (ULONGLONG)TICKSPERSEC;
- tmo_p.tv_nsec = (timeleft % TICKSPERSEC) * 100;
- ret = futex_wait( addr, val, &tmo_p );
- }
- else
- ret = futex_wait( addr, val, NULL );
+ ret = futex_wait_multiple( futexes, 1, end );
}
if (!ret)
@@ -719,12 +733,11 @@ static NTSTATUS __fsync_wait_objects( DWORD count, const HANDLE *handles,
{
static const LARGE_INTEGER zero = {0};
- struct futex_wait_block futexes[MAXIMUM_WAIT_OBJECTS + 1];
+ struct futex_waitv futexes[MAXIMUM_WAIT_OBJECTS + 1];
struct fsync *objs[MAXIMUM_WAIT_OBJECTS];
+ BOOL msgwait = FALSE, waited = FALSE;
int has_fsync = 0, has_server = 0;
- BOOL msgwait = FALSE;
int dummy_futex = 0;
- unsigned int spin;
LONGLONG timeleft;
LARGE_INTEGER now;
DWORD waitcount;
@@ -834,23 +847,15 @@ static NTSTATUS __fsync_wait_objects( DWORD count, const HANDLE *handles,
struct semaphore *semaphore = obj->shm;
int current;
- /* It would be a little clearer (and less error-prone)
- * to use a dedicated interlocked_dec_if_nonzero()
- * helper, but nesting loops like that is probably not
- * great for performance... */
- for (spin = 0; spin <= spincount || current; ++spin)
+ if ((current = __atomic_load_n( &semaphore->count, __ATOMIC_SEQ_CST ))
+ && __sync_val_compare_and_swap( &semaphore->count, current, current - 1 ) == current)
{
- if ((current = __atomic_load_n( &semaphore->count, __ATOMIC_SEQ_CST ))
- && __sync_val_compare_and_swap( &semaphore->count, current, current - 1 ) == current)
- {
- TRACE("Woken up by handle %p [%d].\n", handles[i], i);
- return i;
- }
- small_pause();
+ TRACE("Woken up by handle %p [%d].\n", handles[i], i);
+ if (waited) simulate_sched_quantum();
+ return i;
}
- futexes[i].addr = &semaphore->count;
- futexes[i].val = 0;
+ futex_vector_set( &futexes[i], &semaphore->count, 0 );
break;
}
case FSYNC_MUTEX:
@@ -862,28 +867,25 @@ static NTSTATUS __fsync_wait_objects( DWORD count, const HANDLE *handles,
{
TRACE("Woken up by handle %p [%d].\n", handles[i], i);
mutex->count++;
+ if (waited) simulate_sched_quantum();
return i;
}
- for (spin = 0; spin <= spincount; ++spin)
+ if (!(tid = __sync_val_compare_and_swap( &mutex->tid, 0, GetCurrentThreadId() )))
{
- if (!(tid = __sync_val_compare_and_swap( &mutex->tid, 0, GetCurrentThreadId() )))
- {
- TRACE("Woken up by handle %p [%d].\n", handles[i], i);
- mutex->count = 1;
- return i;
- }
- else if (tid == ~0 && (tid = __sync_val_compare_and_swap( &mutex->tid, ~0, GetCurrentThreadId() )) == ~0)
- {
- TRACE("Woken up by abandoned mutex %p [%d].\n", handles[i], i);
- mutex->count = 1;
- return STATUS_ABANDONED_WAIT_0 + i;
- }
- small_pause();
+ TRACE("Woken up by handle %p [%d].\n", handles[i], i);
+ mutex->count = 1;
+ if (waited) simulate_sched_quantum();
+ return i;
+ }
+ else if (tid == ~0 && (tid = __sync_val_compare_and_swap( &mutex->tid, ~0, GetCurrentThreadId() )) == ~0)
+ {
+ TRACE("Woken up by abandoned mutex %p [%d].\n", handles[i], i);
+ mutex->count = 1;
+ return STATUS_ABANDONED_WAIT_0 + i;
}
- futexes[i].addr = &mutex->tid;
- futexes[i].val = tid;
+ futex_vector_set( &futexes[i], &mutex->tid, tid );
break;
}
case FSYNC_AUTO_EVENT:
@@ -891,18 +893,17 @@ static NTSTATUS __fsync_wait_objects( DWORD count, const HANDLE *handles,
{
struct event *event = obj->shm;
- for (spin = 0; spin <= spincount; ++spin)
+ if (__sync_val_compare_and_swap( &event->signaled, 1, 0 ))
{
- if (__sync_val_compare_and_swap( &event->signaled, 1, 0 ))
- {
- TRACE("Woken up by handle %p [%d].\n", handles[i], i);
- return i;
- }
- small_pause();
+ if (ac_odyssey && alertable)
+ usleep( 0 );
+
+ TRACE("Woken up by handle %p [%d].\n", handles[i], i);
+ if (waited) simulate_sched_quantum();
+ return i;
}
- futexes[i].addr = &event->signaled;
- futexes[i].val = 0;
+ futex_vector_set( &futexes[i], &event->signaled, 0 );
break;
}
case FSYNC_MANUAL_EVENT:
@@ -911,18 +912,17 @@ static NTSTATUS __fsync_wait_objects( DWORD count, const HANDLE *handles,
{
struct event *event = obj->shm;
- for (spin = 0; spin <= spincount; ++spin)
+ if (__atomic_load_n( &event->signaled, __ATOMIC_SEQ_CST ))
{
- if (__atomic_load_n( &event->signaled, __ATOMIC_SEQ_CST ))
- {
- TRACE("Woken up by handle %p [%d].\n", handles[i], i);
- return i;
- }
- small_pause();
+ if (ac_odyssey && alertable)
+ usleep( 0 );
+
+ TRACE("Woken up by handle %p [%d].\n", handles[i], i);
+ if (waited) simulate_sched_quantum();
+ return i;
}
- futexes[i].addr = &event->signaled;
- futexes[i].val = 0;
+ futex_vector_set( &futexes[i], &event->signaled, 0 );
break;
}
default:
@@ -933,31 +933,22 @@ static NTSTATUS __fsync_wait_objects( DWORD count, const HANDLE *handles,
else
{
/* Avoid breaking things entirely. */
- futexes[i].addr = &dummy_futex;
- futexes[i].val = dummy_futex;
+ futex_vector_set( &futexes[i], &dummy_futex, dummy_futex );
}
-
-#if __SIZEOF_POINTER__ == 4
- futexes[i].pad = 0;
-#endif
- futexes[i].bitset = ~0;
}
if (alertable)
{
/* We already checked if it was signaled; don't bother doing it again. */
- futexes[i].addr = ntdll_get_thread_data()->fsync_apc_futex;
- futexes[i].val = 0;
-#if __SIZEOF_POINTER__ == 4
- futexes[i].pad = 0;
-#endif
- futexes[i].bitset = ~0;
- i++;
+ futex_vector_set( &futexes[i++], ntdll_get_thread_data()->fsync_apc_futex, 0 );
}
waitcount = i;
/* Looks like everything is contended, so wait. */
+ if (ac_odyssey && alertable)
+ usleep( 0 );
+
if (timeout && !timeout->QuadPart)
{
/* Unlike esync, we already know that we've timed out, so we
@@ -965,17 +956,8 @@ static NTSTATUS __fsync_wait_objects( DWORD count, const HANDLE *handles,
TRACE("Wait timed out.\n");
return STATUS_TIMEOUT;
}
- else if (timeout)
- {
- LONGLONG timeleft = update_timeout( end );
- struct timespec tmo_p;
- tmo_p.tv_sec = timeleft / (ULONGLONG)TICKSPERSEC;
- tmo_p.tv_nsec = (timeleft % TICKSPERSEC) * 100;
- ret = futex_wait_multiple( futexes, waitcount, &tmo_p );
- }
- else
- ret = futex_wait_multiple( futexes, waitcount, NULL );
+ ret = futex_wait_multiple( futexes, waitcount, timeout ? &end : NULL );
/* FUTEX_WAIT_MULTIPLE can succeed or return -EINTR, -EAGAIN,
* -EFAULT/-EACCES, -ETIMEDOUT. In the first three cases we need to
@@ -986,6 +968,7 @@ static NTSTATUS __fsync_wait_objects( DWORD count, const HANDLE *handles,
TRACE("Wait timed out.\n");
return STATUS_TIMEOUT;
}
+ else waited = TRUE;
} /* while (1) */
}
else
@@ -1090,6 +1073,7 @@ tryagain:
for (i = 0; i < count; i++)
{
struct fsync *obj = objs[i];
+ if (!obj) continue;
switch (obj->type)
{
case FSYNC_MUTEX:
@@ -1109,7 +1093,10 @@ tryagain:
case FSYNC_SEMAPHORE:
{
struct semaphore *semaphore = obj->shm;
- if (__sync_fetch_and_sub( &semaphore->count, 1 ) <= 0)
+ int current;
+
+ if (!(current = __atomic_load_n( &semaphore->count, __ATOMIC_SEQ_CST ))
+ || __sync_val_compare_and_swap( &semaphore->count, current, current - 1 ) != current)
goto tooslow;
break;
}
@@ -1151,6 +1138,7 @@ tooslow:
for (--i; i >= 0; i--)
{
struct fsync *obj = objs[i];
+ if (!obj) continue;
switch (obj->type)
{
case FSYNC_MUTEX:
diff --git a/dlls/ntdll/unix/loader.c b/dlls/ntdll/unix/loader.c
index 0360bc64b00..f30d900a07b 100644
--- a/dlls/ntdll/unix/loader.c
+++ b/dlls/ntdll/unix/loader.c
@@ -2028,6 +2028,34 @@ const unixlib_entry_t __wine_unix_call_wow64_funcs[] =
};
#endif /* _WIN64 */
+
+BOOL ac_odyssey;
+BOOL fsync_simulate_sched_quantum;
+
+static void hacks_init(void)
+{
+ static const char upc_exe[] = "Ubisoft Game Launcher\\upc.exe";
+ static const char ac_odyssey_exe[] = "ACOdyssey.exe";
+ const char *env_str;
+
+ if (main_argc > 1 && strstr(main_argv[1], ac_odyssey_exe))
+ {
+ ERR("HACK: AC Odyssey sync tweak on.\n");
+ ac_odyssey = TRUE;
+ return;
+ }
+ env_str = getenv("WINE_FSYNC_SIMULATE_SCHED_QUANTUM");
+ if (env_str)
+ fsync_simulate_sched_quantum = !!atoi(env_str);
+ else if (main_argc > 1)
+ fsync_simulate_sched_quantum = !!strstr(main_argv[1], upc_exe);
+ if (fsync_simulate_sched_quantum)
+ ERR("HACK: Simulating sched quantum in fsync.\n");
+
+ env_str = getenv("SteamGameId");
+ if (env_str && !strcmp(env_str, "50130"))
+ setenv("WINESTEAMNOEXEC", "1", 0);
+}
/***********************************************************************
* start_main_thread
@@ -2041,6 +2041,7 @@ static void start_main_thread(void)
signal_alloc_thread( teb );
dbg_init();
startup_info_size = server_init_process();
+ hacks_init();
fsync_init();
esync_init();
virtual_map_user_shared_data();
diff --git a/dlls/ntdll/unix/unix_private.h b/dlls/ntdll/unix/unix_private.h
index dbd3e645976..683c2a4f1c2 100644
--- a/dlls/ntdll/unix/unix_private.h
+++ b/dlls/ntdll/unix/unix_private.h
@@ -146,6 +146,9 @@ #ifdef __i386__
extern struct ldt_copy __wine_ldt_copy;
#endif
+extern BOOL ac_odyssey;
+extern BOOL fsync_simulate_sched_quantum;
+
extern void init_environment(void);
extern void init_startup_info(void);
extern void *create_startup_info( const UNICODE_STRING *nt_image, const RTL_USER_PROCESS_PARAMETERS *params,
diff --git a/include/config.h.in b/include/config.h.in
index 8f9b9737b24..4670e4bc881 100644
--- a/include/config.h.in
+++ b/include/config.h.in
@@ -188,6 +188,9 @@
/* Define to 1 if you have the <linux/filter.h> header file. */
#undef HAVE_LINUX_FILTER_H
+/* Define to 1 if you have the <linux/futex.h> header file. */
+#undef HAVE_LINUX_FUTEX_H
+
/* Define if Linux-style gethostbyname_r and gethostbyaddr_r are available */
#undef HAVE_LINUX_GETHOSTBYNAME_R_6
diff --git a/server/fsync.c b/server/fsync.c
index af1c68f2a90..2b8c5e4bc15 100644
--- a/server/fsync.c
+++ b/server/fsync.c
@@ -44,21 +44,11 @@
#include "fsync.h"
#include "pshpack4.h"
-struct futex_wait_block
-{
- int *addr;
-#if __SIZEOF_POINTER__ == 4
- int pad;
-#endif
- int val;
-};
#include "poppack.h"
-static inline int futex_wait_multiple( const struct futex_wait_block *futexes,
- int count, const struct timespec *timeout )
-{
- return syscall( __NR_futex, futexes, 31, count, timeout, 0, 0 );
-}
+#ifndef __NR_futex_waitv
+#define __NR_futex_waitv 449
+#endif
int do_fsync(void)
{
@@ -67,8 +57,16 @@ int do_fsync(void)
if (do_fsync_cached == -1)
{
- static const struct timespec zero;
- futex_wait_multiple( NULL, 0, &zero );
+ FILE *f;
+ if ((f = fopen( "/sys/kernel/futex2/wait", "r" )))
+ {
+ fclose(f);
+ do_fsync_cached = 0;
+ fprintf( stderr, "fsync: old futex2 patches detected, disabling.\n" );
+ return do_fsync_cached;
+ }
+
+ syscall( __NR_futex_waitv, 0, 0, 0, 0, 0);
do_fsync_cached = getenv("WINEFSYNC") && atoi(getenv("WINEFSYNC")) && errno != ENOSYS;
}