diff --git a/default.nix b/default.nix index d513767..7b0e36d 100644 --- a/default.nix +++ b/default.nix @@ -1,4 +1,4 @@ -{ stdenv, lib, pkgs, fetchurl, nix, pkgsCross, +{ stdenv, lib, pkgs, fetchurl, fetchpatch, nix, pkgsCross, installDir ? "$HOME/.local/starcitizen", launcherCache ? "$HOME/.local/share", prefixBaseDir ? "$HOME/.winenix", @@ -26,7 +26,19 @@ assert virtualDesktop == null || assert !(builtins.any (lib.strings.hasSuffix "/") [ installDir launcherCache prefixBaseDir ]); let - wineReccomendedPatches = [ ./patches/joyaxis.patch ./patches/wine_6.13_fastsync2.patch ./patches/device_seek_penalty.patch ]; + wineReccomendedPatches = [ + (fetchpatch { + name = "joyaxis.patch"; + url = "https://raw.githubusercontent.com/snatella/wine-runner-sc/5e48a6fb974da9f1585043a236026c7c57272e67/patches/6.4/joyaxis.patch"; + sha256 = "18f87anf0rkvbkjhgm9j6n2cccp1wl82a12vhzr7a4p7bc025yrz"; + }) + (fetchpatch { + name = "StorageDeviceSeekPenalty.patch"; + url = "https://raw.githubusercontent.com/Frogging-Family/community-patches/f96b11b79d6aada264838d780dfd5a14f85cced0/wine-tkg-git/star-citizen-StorageDeviceSeekPenaltyProperty.mypatch"; + sha256 = "01ilxhl90ib05jwsqy0iajqixdvd571ad803z1qj3lgjk3siff13"; + }) + ]; + winePkg = winePackage.overrideAttrs (attrs: { patches = (if wineSrc == null then attrs.patches else []) ++ winePatches ++ lib.optional applyReccomendedWinePatches wineReccomendedPatches ; src = if wineSrc != null then wineSrc else attrs.src; diff --git a/patches/device_seek_penalty.patch b/patches/device_seek_penalty.patch deleted file mode 100644 index 87088c5..0000000 --- a/patches/device_seek_penalty.patch +++ /dev/null @@ -1,241 +0,0 @@ -From 3314a8690b4884b63d7c818ac59d0bbc638599a6 Mon Sep 17 00:00:00 2001 -From: Mike Scott -Date: Sat, 1 May 2021 22:01:02 +0100 -Subject: [PATCH] fake seek penalty - ---- - dlls/mountmgr.sys/Makefile.in | 1 + - dlls/mountmgr.sys/device.c | 25 ++++ - dlls/mountmgr.sys/unix/device_seek_penalty.c | 145 +++++++++++++++++++ - include/ntddstor.h | 9 +- - 4 files changed, 179 insertions(+), 1 deletion(-) - create mode 100644 dlls/mountmgr.sys/unix/device_seek_penalty.c - -diff --git a/dlls/mountmgr.sys/Makefile.in b/dlls/mountmgr.sys/Makefile.in -index 83204e66504..61b462ddd7b 100644 ---- a/dlls/mountmgr.sys/Makefile.in -+++ b/dlls/mountmgr.sys/Makefile.in -@@ -7,6 +7,7 @@ EXTRALIBS = $(DISKARBITRATION_LIBS) $(SYSTEMCONFIGURATION_LIBS) $(CORESERVICES_L - - C_SRCS = \ - dbus.c \ -+ unix/device_seek_penalty.c \ - device.c \ - diskarb.c \ - mountmgr.c -diff --git a/dlls/mountmgr.sys/device.c b/dlls/mountmgr.sys/device.c -index 04e8fe3c0f5..9396f45970a 100644 ---- a/dlls/mountmgr.sys/device.c -+++ b/dlls/mountmgr.sys/device.c -@@ -1894,6 +1894,31 @@ static NTSTATUS query_property( struct disk_device *device, IRP *irp ) - - break; - } -+ case StorageDeviceSeekPenaltyProperty: -+ { -+ DEVICE_SEEK_PENALTY_DESCRIPTOR *descriptor; -+ int ret; -+ -+ memset( irp->AssociatedIrp.SystemBuffer, 0, irpsp->Parameters.DeviceIoControl.OutputBufferLength ); -+ descriptor = irp->AssociatedIrp.SystemBuffer; -+ descriptor->Version = sizeof(DEVICE_SEEK_PENALTY_DESCRIPTOR); -+ descriptor->Size = sizeof(DEVICE_SEEK_PENALTY_DESCRIPTOR); -+ irp->IoStatus.Information = sizeof(DEVICE_SEEK_PENALTY_DESCRIPTOR); -+ -+ //ret = device_seek_penalty(device->unix_mount); -+ ret = 0; -+ if (ret < 0) { -+ FIXME( "Unsupported property StorageDeviceSeekPenalty\n" ); -+ status = STATUS_NOT_SUPPORTED; -+ } else if (ret == 0) { -+ descriptor->IncursSeekPenalty = FALSE; -+ status = STATUS_SUCCESS; -+ } else if (ret == 1) { -+ descriptor->IncursSeekPenalty = TRUE; -+ status = STATUS_SUCCESS; -+ } -+ break; -+ } - default: - FIXME( "Unsupported property %#x\n", query->PropertyId ); - status = STATUS_NOT_SUPPORTED; -diff --git a/dlls/mountmgr.sys/unix/device_seek_penalty.c b/dlls/mountmgr.sys/unix/device_seek_penalty.c -new file mode 100644 -index 00000000000..da13aaa193d ---- /dev/null -+++ b/dlls/mountmgr.sys/unix/device_seek_penalty.c -@@ -0,0 +1,145 @@ -+/* -+ * System information APIs -+ * -+ * Copyright 1996-1998 Marcus Meissner -+ * -+ * This library is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU Lesser General Public -+ * License as published by the Free Software Foundation; either -+ * version 2.1 of the License, or (at your option) any later version. -+ * -+ * This library is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -+ * Lesser General Public License for more details. -+ * -+ * You should have received a copy of the GNU Lesser General Public -+ * License along with this library; if not, write to the Free Software -+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA -+ */ -+ -+#include "config.h" -+#include "wine/port.h" -+ -+#ifdef linux -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#define NONAMELESSUNION -+#include "ntstatus.h" -+#include "mountmgr.h" -+#define WIN32_NO_STATUS -+#include "windef.h" -+#include "winternl.h" -+#endif -+#include "wine/debug.h" -+ -+WINE_DEFAULT_DEBUG_CHANNEL(mountmgr); -+ -+int device_seek_penalty (char *unix_mount) -+{ -+#ifdef linux -+ FILE *fp; -+ char isrotapathstr[260]; -+ char evpath[260]; -+ char ueventbufstr[260]; -+ char isrotastrbuf[2]; -+ char *fgetsret = NULL; -+ int ret = 0; -+ char *rptr = NULL; -+ char *sptr = NULL; -+ char *devstr_edited = NULL; -+ char mountpath[260]; -+ char *part_dev = NULL; -+ int c = 0; -+ struct stat statbuf; -+ int seekpen; -+ -+ const char *home = getenv( "HOME" ); -+ const char *prefix = getenv( "WINEPREFIX" ); -+ size_t len = (prefix ? strlen(prefix) : strlen(home) + strlen("/.wine")) + sizeof("/dosdevics/com256"); -+ char *path = HeapAlloc( GetProcessHeap(), 0, len ); -+ -+ if (path) -+ { -+ if (prefix) strcpy( path, prefix ); -+ else -+ { -+ strcpy( path, home ); -+ strcat( path, "/.wine" ); -+ } -+ strcat( path, "/dosdevices/" ); -+ } -+ -+ if (!unix_mount) { -+ return -1; -+ } -+ -+ snprintf (mountpath, 260, "%s%s", path, unix_mount); -+ ret = stat (mountpath, &statbuf); -+ if (ret == -1) { -+ return -1; -+ } -+ HeapFree( GetProcessHeap(), 0, path ); -+ sprintf (evpath, "/sys/dev/block/%d:%d/uevent", major(statbuf.st_dev), minor(statbuf.st_dev)); -+ fp = fopen(evpath, "r"); -+ if (!fp) { -+ return -1; -+ } -+ while ((rptr = fgets (ueventbufstr, 260, fp))) { -+ sptr = strstr (rptr, "DEVNAME="); -+ if (sptr) { -+ sptr += strlen ("DEVNAME="); -+ break; -+ } -+ } -+ fclose (fp); -+ devstr_edited = sptr; -+ if (!devstr_edited) { -+ return -1; -+ } -+ /* Find first character after forwardslash '/' */ -+ part_dev = strrchr (devstr_edited, '/'); -+ if (!part_dev || (part_dev == devstr_edited)) part_dev = devstr_edited; -+ else part_dev++; -+ /* Trim off trailing digits and whitespace */ -+ c = strlen (devstr_edited); -+ c--; -+ while ((devstr_edited[c] >= '0' && devstr_edited[c] <= '9') -+ || (devstr_edited[c] == '\n' || devstr_edited[c] == '\r')) -+ devstr_edited[c--] = '\0'; -+ -+ ret = snprintf (isrotapathstr, 260, "/sys/block/%s/queue/rotational", part_dev); -+ if (ret < 1 || ret == 260) { -+ return -1; -+ } -+ -+ fp = fopen(isrotapathstr, "r"); -+ if (!fp) { -+ return -1; -+ } -+ -+ fgetsret = fgets(isrotastrbuf, 2, fp); -+ if (!fgetsret) { -+ return -1; -+ } -+ -+ fclose (fp); -+ -+ if (isrotastrbuf[0] == '1') { -+ seekpen = 1; -+ } else if (isrotastrbuf[0] == '0') { -+ seekpen = 0; -+ } else { -+ return -1; -+ } -+ -+ return seekpen; -+#else -+ return -1; -+#endif -+} -diff --git a/include/ntddstor.h b/include/ntddstor.h -index b8c4bb73b0d..836def413fe 100644 ---- a/include/ntddstor.h -+++ b/include/ntddstor.h -@@ -214,7 +214,8 @@ typedef enum _STORAGE_QUERY_TYPE { - - typedef enum _STORAGE_PROPERTY_ID { - StorageDeviceProperty = 0, -- StorageAdapterProperty -+ StorageAdapterProperty = 1, -+ StorageDeviceSeekPenaltyProperty = 7, - } STORAGE_PROPERTY_ID, *PSTORAGE_PROPERTY_ID; - - typedef struct _STORAGE_PROPERTY_QUERY { -@@ -272,6 +273,12 @@ typedef struct _STORAGE_ADAPTER_DESCRIPTOR { - USHORT BusMinorVersion; - } STORAGE_ADAPTER_DESCRIPTOR, *PSTORAGE_ADAPTER_DESCRIPTOR; - -+typedef struct _DEVICE_SEEK_PENALTY_DESCRIPTOR { -+ ULONG Version; -+ ULONG Size; -+ BOOLEAN IncursSeekPenalty; -+} DEVICE_SEEK_PENALTY_DESCRIPTOR, *PDEVICE_SEEK_PENALTY_DESCRIPTOR; -+ - #ifdef __cplusplus - } - #endif --- -2.31.1 diff --git a/patches/fsync.patch b/patches/fsync.patch deleted file mode 100644 index fa78bbf..0000000 --- a/patches/fsync.patch +++ /dev/null @@ -1,3991 +0,0 @@ -From e5bb1ac75bc1cf73ef93db0e095bdc4293e12fdf Mon Sep 17 00:00:00 2001 -From: Mike Scott -Date: Thu, 26 Nov 2020 15:48:25 +0000 -Subject: [PATCH] Applied fsync patches - ---- - dlls/ntdll/Makefile.in | 1 + - dlls/ntdll/unix/esync.c | 7 +- - dlls/ntdll/unix/fsync.c | 1269 ++++++++++++++++++++++++++++++++ - dlls/ntdll/unix/fsync.h | 49 ++ - dlls/ntdll/unix/loader.c | 2 + - dlls/ntdll/unix/server.c | 4 + - dlls/ntdll/unix/sync.c | 72 +- - dlls/ntdll/unix/unix_private.h | 1 + - dlls/ntdll/unix/virtual.c | 1 + - include/wine/server_protocol.h | 101 +++ - server/Makefile.in | 1 + - server/async.c | 2 + - server/atom.c | 1 + - server/change.c | 1 + - server/clipboard.c | 1 + - server/completion.c | 1 + - server/console.c | 20 + - server/debugger.c | 2 + - server/device.c | 23 + - server/directory.c | 2 + - server/esync.c | 4 +- - server/event.c | 37 + - server/fd.c | 27 + - server/file.c | 1 + - server/file.h | 1 + - server/fsync.c | 527 +++++++++++++ - server/fsync.h | 34 + - server/handle.c | 1 + - server/hook.c | 1 + - server/mailslot.c | 4 + - server/main.c | 7 + - server/mapping.c | 3 + - server/mutex.c | 1 + - server/named_pipe.c | 5 + - server/object.h | 2 + - server/process.c | 16 + - server/process.h | 1 + - server/protocol.def | 54 ++ - server/queue.c | 42 ++ - server/registry.c | 1 + - server/request.c | 1 + - server/request.h | 38 + - server/semaphore.c | 1 + - server/serial.c | 1 + - server/signal.c | 1 + - server/sock.c | 3 + - server/symlink.c | 1 + - server/thread.c | 31 + - server/thread.h | 2 + - server/timer.c | 17 + - server/token.c | 1 + - server/trace.c | 72 ++ - server/winstation.c | 2 + - 53 files changed, 2496 insertions(+), 5 deletions(-) - create mode 100644 dlls/ntdll/unix/fsync.c - create mode 100644 dlls/ntdll/unix/fsync.h - create mode 100644 server/fsync.c - create mode 100644 server/fsync.h - -diff --git a/dlls/ntdll/Makefile.in b/dlls/ntdll/Makefile.in -index 5dc4ebb07fc..5159b81167f 100644 ---- a/dlls/ntdll/Makefile.in -+++ b/dlls/ntdll/Makefile.in -@@ -48,6 +48,7 @@ C_SRCS = \ - unix/env.c \ - unix/esync.c \ - unix/file.c \ -+ unix/fsync.c \ - unix/loader.c \ - unix/process.c \ - unix/registry.c \ -diff --git a/dlls/ntdll/unix/esync.c b/dlls/ntdll/unix/esync.c -index ed801c71991..ac0326b1aba 100644 ---- a/dlls/ntdll/unix/esync.c -+++ b/dlls/ntdll/unix/esync.c -@@ -57,6 +57,7 @@ - - #include "unix_private.h" - #include "esync.h" -+#include "fsync.h" - - WINE_DEFAULT_DEBUG_CHANNEL(esync); - -@@ -66,7 +67,7 @@ int do_esync(void) - static int do_esync_cached = -1; - - if (do_esync_cached == -1) -- do_esync_cached = getenv("WINEESYNC") && atoi(getenv("WINEESYNC")); -+ do_esync_cached = getenv("WINEESYNC") && atoi(getenv("WINEESYNC")) && !do_fsync(); - - return do_esync_cached; - #else -@@ -867,7 +868,7 @@ static NTSTATUS __esync_wait_objects( DWORD count, const HANDLE *handles, BOOLEA - return ret; - } - -- if (objs[count - 1] && objs[count - 1]->type == ESYNC_QUEUE) -+ if (count && objs[count - 1] && objs[count - 1]->type == ESYNC_QUEUE) - msgwait = TRUE; - - if (has_esync && has_server) -@@ -896,7 +897,7 @@ static NTSTATUS __esync_wait_objects( DWORD count, const HANDLE *handles, BOOLEA - } - } - -- if (wait_any || count == 1) -+ if (wait_any || count <= 1) - { - /* Try to check objects now, so we can obviate poll() at least. */ - for (i = 0; i < count; i++) -diff --git a/dlls/ntdll/unix/fsync.c b/dlls/ntdll/unix/fsync.c -new file mode 100644 -index 00000000000..5012425b95a ---- /dev/null -+++ b/dlls/ntdll/unix/fsync.c -@@ -0,0 +1,1269 @@ -+/* -+ * futex-based synchronization objects -+ * -+ * Copyright (C) 2018 Zebediah Figura -+ * -+ * This library is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU Lesser General Public -+ * License as published by the Free Software Foundation; either -+ * version 2.1 of the License, or (at your option) any later version. -+ * -+ * This library is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -+ * Lesser General Public License for more details. -+ * -+ * You should have received a copy of the GNU Lesser General Public -+ * License along with this library; if not, write to the Free Software -+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA -+ */ -+ -+#if 0 -+#pragma makedep unix -+#endif -+ -+#include "config.h" -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#ifdef HAVE_SYS_MMAN_H -+# include -+#endif -+#ifdef HAVE_SYS_STAT_H -+# include -+#endif -+#ifdef HAVE_SYS_SYSCALL_H -+# include -+#endif -+#include -+ -+#include "ntstatus.h" -+#define WIN32_NO_STATUS -+#define NONAMELESSUNION -+#include "windef.h" -+#include "winternl.h" -+#include "wine/debug.h" -+#include "wine/server.h" -+ -+#include "unix_private.h" -+#include "fsync.h" -+ -+WINE_DEFAULT_DEBUG_CHANNEL(fsync); -+ -+#include "pshpack4.h" -+struct futex_wait_block -+{ -+ int *addr; -+#if __SIZEOF_POINTER__ == 4 -+ int pad; -+#endif -+ int val; -+ int bitset; -+}; -+#include "poppack.h" -+ -+static inline void small_pause(void) -+{ -+#if defined(__i386__) || defined(__x86_64__) -+ __asm__ __volatile__( "rep;nop" : : : "memory" ); -+#else -+ __asm__ __volatile__( "" : : : "memory" ); -+#endif -+} -+ -+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 ); -+} -+ -+static inline int futex_wake( int *addr, int val ) -+{ -+ return syscall( __NR_futex, addr, 1, val, NULL, 0, 0 ); -+} -+ -+static inline int futex_wait( int *addr, int val, struct timespec *timeout ) -+{ -+ return syscall( __NR_futex, addr, 0, val, timeout, 0, 0 ); -+} -+ -+static unsigned int spincount = 100; -+ -+int do_fsync(void) -+{ -+#ifdef __linux__ -+ static int do_fsync_cached = -1; -+ -+ if (do_fsync_cached == -1) -+ { -+ static const struct timespec zero; -+ futex_wait_multiple( NULL, 0, &zero ); -+ do_fsync_cached = getenv("WINEFSYNC") && atoi(getenv("WINEFSYNC")) && errno != ENOSYS; -+ if (getenv("WINEFSYNC_SPINCOUNT")) -+ spincount = atoi(getenv("WINEFSYNC_SPINCOUNT")); -+ } -+ -+ return do_fsync_cached; -+#else -+ static int once; -+ if (!once++) -+ FIXME("futexes not supported on this platform.\n"); -+ return 0; -+#endif -+} -+ -+struct fsync -+{ -+ enum fsync_type type; -+ void *shm; /* pointer to shm section */ -+}; -+ -+struct semaphore -+{ -+ int count; -+ int max; -+}; -+C_ASSERT(sizeof(struct semaphore) == 8); -+ -+struct event -+{ -+ int signaled; -+ int unused; -+}; -+C_ASSERT(sizeof(struct event) == 8); -+ -+struct mutex -+{ -+ int tid; -+ int count; /* recursion count */ -+}; -+C_ASSERT(sizeof(struct mutex) == 8); -+ -+static char shm_name[29]; -+static int shm_fd; -+static void **shm_addrs; -+static int shm_addrs_size; /* length of the allocated shm_addrs array */ -+static long pagesize; -+ -+static pthread_mutex_t shm_addrs_mutex = PTHREAD_MUTEX_INITIALIZER; -+ -+static void *get_shm( unsigned int idx ) -+{ -+ int entry = (idx * 8) / pagesize; -+ int offset = (idx * 8) % pagesize; -+ void *ret; -+ -+ pthread_mutex_lock( &shm_addrs_mutex ); -+ -+ if (entry >= shm_addrs_size) -+ { -+ int new_size = max(shm_addrs_size * 2, entry + 1); -+ -+ if (!(shm_addrs = realloc( shm_addrs, new_size * sizeof(shm_addrs[0]) ))) -+ ERR("Failed to grow shm_addrs array to size %d.\n", shm_addrs_size); -+ memset( shm_addrs + shm_addrs_size, 0, (new_size - shm_addrs_size) * sizeof(shm_addrs[0]) ); -+ shm_addrs_size = new_size; -+ } -+ -+ if (!shm_addrs[entry]) -+ { -+ void *addr = mmap( NULL, pagesize, PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, entry * pagesize ); -+ if (addr == (void *)-1) -+ ERR("Failed to map page %d (offset %#lx).\n", entry, entry * pagesize); -+ -+ TRACE("Mapping page %d at %p.\n", entry, addr); -+ -+ if (__sync_val_compare_and_swap( &shm_addrs[entry], 0, addr )) -+ munmap( addr, pagesize ); /* someone beat us to it */ -+ } -+ -+ ret = (void *)((unsigned long)shm_addrs[entry] + offset); -+ -+ pthread_mutex_unlock( &shm_addrs_mutex ); -+ -+ return ret; -+} -+ -+/* We'd like lookup to be fast. To that end, we use a static list indexed by handle. -+ * This is copied and adapted from the fd cache code. */ -+ -+#define FSYNC_LIST_BLOCK_SIZE (65536 / sizeof(struct fsync)) -+#define FSYNC_LIST_ENTRIES 256 -+ -+static struct fsync *fsync_list[FSYNC_LIST_ENTRIES]; -+static struct fsync fsync_list_initial_block[FSYNC_LIST_BLOCK_SIZE]; -+ -+static inline UINT_PTR handle_to_index( HANDLE handle, UINT_PTR *entry ) -+{ -+ UINT_PTR idx = (((UINT_PTR)handle) >> 2) - 1; -+ *entry = idx / FSYNC_LIST_BLOCK_SIZE; -+ return idx % FSYNC_LIST_BLOCK_SIZE; -+} -+ -+static struct fsync *add_to_list( HANDLE handle, enum fsync_type type, void *shm ) -+{ -+ UINT_PTR entry, idx = handle_to_index( handle, &entry ); -+ -+ if (entry >= FSYNC_LIST_ENTRIES) -+ { -+ FIXME( "too many allocated handles, not caching %p\n", handle ); -+ return FALSE; -+ } -+ -+ if (!fsync_list[entry]) /* do we need to allocate a new block of entries? */ -+ { -+ if (!entry) fsync_list[0] = fsync_list_initial_block; -+ else -+ { -+ void *ptr = anon_mmap_alloc( FSYNC_LIST_BLOCK_SIZE * sizeof(struct fsync), -+ PROT_READ | PROT_WRITE ); -+ if (ptr == MAP_FAILED) return FALSE; -+ fsync_list[entry] = ptr; -+ } -+ } -+ -+ if (!__sync_val_compare_and_swap((int *)&fsync_list[entry][idx].type, 0, type )) -+ fsync_list[entry][idx].shm = shm; -+ -+ return &fsync_list[entry][idx]; -+} -+ -+static struct fsync *get_cached_object( HANDLE handle ) -+{ -+ UINT_PTR entry, idx = handle_to_index( handle, &entry ); -+ -+ if (entry >= FSYNC_LIST_ENTRIES || !fsync_list[entry]) return NULL; -+ if (!fsync_list[entry][idx].type) return NULL; -+ -+ return &fsync_list[entry][idx]; -+} -+ -+/* Gets an object. This is either a proper fsync object (i.e. an event, -+ * semaphore, etc. created using create_fsync) or a generic synchronizable -+ * server-side object which the server will signal (e.g. a process, thread, -+ * message queue, etc.) */ -+static NTSTATUS get_object( HANDLE handle, struct fsync **obj ) -+{ -+ NTSTATUS ret = STATUS_SUCCESS; -+ unsigned int shm_idx = 0; -+ enum fsync_type type; -+ -+ if ((*obj = get_cached_object( handle ))) return STATUS_SUCCESS; -+ -+ if ((INT_PTR)handle < 0) -+ { -+ /* We can deal with pseudo-handles, but it's just easier this way */ -+ return STATUS_NOT_IMPLEMENTED; -+ } -+ -+ /* We need to try grabbing it from the server. */ -+ SERVER_START_REQ( get_fsync_idx ) -+ { -+ req->handle = wine_server_obj_handle( handle ); -+ if (!(ret = wine_server_call( req ))) -+ { -+ shm_idx = reply->shm_idx; -+ type = reply->type; -+ } -+ } -+ SERVER_END_REQ; -+ -+ if (ret) -+ { -+ WARN("Failed to retrieve shm index for handle %p, status %#x.\n", handle, ret); -+ *obj = NULL; -+ return ret; -+ } -+ -+ TRACE("Got shm index %d for handle %p.\n", shm_idx, handle); -+ -+ *obj = add_to_list( handle, type, get_shm( shm_idx ) ); -+ return ret; -+} -+ -+NTSTATUS fsync_close( HANDLE handle ) -+{ -+ UINT_PTR entry, idx = handle_to_index( handle, &entry ); -+ -+ TRACE("%p.\n", handle); -+ -+ if (entry < FSYNC_LIST_ENTRIES && fsync_list[entry]) -+ { -+ if (__atomic_exchange_n( &fsync_list[entry][idx].type, 0, __ATOMIC_SEQ_CST )) -+ return STATUS_SUCCESS; -+ } -+ -+ return STATUS_INVALID_HANDLE; -+} -+ -+static NTSTATUS create_fsync( enum fsync_type type, HANDLE *handle, -+ ACCESS_MASK access, const OBJECT_ATTRIBUTES *attr, int low, int high ) -+{ -+ NTSTATUS ret; -+ data_size_t len; -+ struct object_attributes *objattr; -+ unsigned int shm_idx; -+ -+ if ((ret = alloc_object_attributes( attr, &objattr, &len ))) return ret; -+ -+ SERVER_START_REQ( create_fsync ) -+ { -+ req->access = access; -+ req->low = low; -+ req->high = high; -+ req->type = type; -+ wine_server_add_data( req, objattr, len ); -+ ret = wine_server_call( req ); -+ if (!ret || ret == STATUS_OBJECT_NAME_EXISTS) -+ { -+ *handle = wine_server_ptr_handle( reply->handle ); -+ shm_idx = reply->shm_idx; -+ type = reply->type; -+ } -+ } -+ SERVER_END_REQ; -+ -+ if (!ret || ret == STATUS_OBJECT_NAME_EXISTS) -+ { -+ add_to_list( *handle, type, get_shm( shm_idx )); -+ TRACE("-> handle %p, shm index %d.\n", *handle, shm_idx); -+ } -+ -+ free( objattr ); -+ return ret; -+} -+ -+static NTSTATUS open_fsync( enum fsync_type type, HANDLE *handle, -+ ACCESS_MASK access, const OBJECT_ATTRIBUTES *attr ) -+{ -+ NTSTATUS ret; -+ unsigned int shm_idx; -+ -+ SERVER_START_REQ( open_fsync ) -+ { -+ req->access = access; -+ req->attributes = attr->Attributes; -+ req->rootdir = wine_server_obj_handle( attr->RootDirectory ); -+ req->type = type; -+ if (attr->ObjectName) -+ wine_server_add_data( req, attr->ObjectName->Buffer, attr->ObjectName->Length ); -+ if (!(ret = wine_server_call( req ))) -+ { -+ *handle = wine_server_ptr_handle( reply->handle ); -+ type = reply->type; -+ shm_idx = reply->shm_idx; -+ } -+ } -+ SERVER_END_REQ; -+ -+ if (!ret) -+ { -+ add_to_list( *handle, type, get_shm( shm_idx ) ); -+ -+ TRACE("-> handle %p, shm index %u.\n", *handle, shm_idx); -+ } -+ return ret; -+} -+ -+void fsync_init(void) -+{ -+ struct stat st; -+ -+ if (!do_fsync()) -+ { -+ /* make sure the server isn't running with WINEFSYNC */ -+ HANDLE handle; -+ NTSTATUS ret; -+ -+ ret = create_fsync( 0, &handle, 0, NULL, 0, 0 ); -+ if (ret != STATUS_NOT_IMPLEMENTED) -+ { -+ ERR("Server is running with WINEFSYNC but this process is not, please enable WINEFSYNC or restart wineserver.\n"); -+ exit(1); -+ } -+ -+ return; -+ } -+ -+ if (stat( config_dir, &st ) == -1) -+ ERR("Cannot stat %s\n", config_dir); -+ -+ if (st.st_ino != (unsigned long)st.st_ino) -+ sprintf( shm_name, "/wine-%lx%08lx-fsync", (unsigned long)((unsigned long long)st.st_ino >> 32), (unsigned long)st.st_ino ); -+ else -+ sprintf( shm_name, "/wine-%lx-fsync", (unsigned long)st.st_ino ); -+ -+ if ((shm_fd = shm_open( shm_name, O_RDWR, 0644 )) == -1) -+ { -+ /* probably the server isn't running with WINEFSYNC, tell the user and bail */ -+ if (errno == ENOENT) -+ ERR("Failed to open fsync shared memory file; make sure no stale wineserver instances are running without WINEFSYNC.\n"); -+ else -+ ERR("Failed to initialize shared memory: %s\n", strerror( errno )); -+ exit(1); -+ } -+ -+ pagesize = sysconf( _SC_PAGESIZE ); -+ -+ shm_addrs = calloc( 128, sizeof(shm_addrs[0]) ); -+ shm_addrs_size = 128; -+} -+ -+NTSTATUS fsync_create_semaphore( HANDLE *handle, ACCESS_MASK access, -+ const OBJECT_ATTRIBUTES *attr, LONG initial, LONG max ) -+{ -+ TRACE("name %s, initial %d, max %d.\n", -+ attr ? debugstr_us(attr->ObjectName) : "", initial, max); -+ -+ return create_fsync( FSYNC_SEMAPHORE, handle, access, attr, initial, max ); -+} -+ -+NTSTATUS fsync_open_semaphore( HANDLE *handle, ACCESS_MASK access, -+ const OBJECT_ATTRIBUTES *attr ) -+{ -+ TRACE("name %s.\n", debugstr_us(attr->ObjectName)); -+ -+ return open_fsync( FSYNC_SEMAPHORE, handle, access, attr ); -+} -+ -+NTSTATUS fsync_release_semaphore( HANDLE handle, ULONG count, ULONG *prev ) -+{ -+ struct fsync *obj; -+ struct semaphore *semaphore; -+ ULONG current; -+ NTSTATUS ret; -+ -+ TRACE("%p, %d, %p.\n", handle, count, prev); -+ -+ if ((ret = get_object( handle, &obj ))) return ret; -+ semaphore = obj->shm; -+ -+ do -+ { -+ current = semaphore->count; -+ if (count + current > semaphore->max) -+ return STATUS_SEMAPHORE_LIMIT_EXCEEDED; -+ } while (__sync_val_compare_and_swap( &semaphore->count, current, count + current ) != current); -+ -+ if (prev) *prev = current; -+ -+ futex_wake( &semaphore->count, INT_MAX ); -+ -+ return STATUS_SUCCESS; -+} -+ -+NTSTATUS fsync_query_semaphore( HANDLE handle, void *info, ULONG *ret_len ) -+{ -+ struct fsync *obj; -+ struct semaphore *semaphore; -+ SEMAPHORE_BASIC_INFORMATION *out = info; -+ NTSTATUS ret; -+ -+ TRACE("handle %p, info %p, ret_len %p.\n", handle, info, ret_len); -+ -+ if ((ret = get_object( handle, &obj ))) return ret; -+ semaphore = obj->shm; -+ -+ out->CurrentCount = semaphore->count; -+ out->MaximumCount = semaphore->max; -+ if (ret_len) *ret_len = sizeof(*out); -+ -+ return STATUS_SUCCESS; -+} -+ -+NTSTATUS fsync_create_event( HANDLE *handle, ACCESS_MASK access, -+ const OBJECT_ATTRIBUTES *attr, EVENT_TYPE event_type, BOOLEAN initial ) -+{ -+ enum fsync_type type = (event_type == SynchronizationEvent ? FSYNC_AUTO_EVENT : FSYNC_MANUAL_EVENT); -+ -+ TRACE("name %s, %s-reset, initial %d.\n", -+ attr ? debugstr_us(attr->ObjectName) : "", -+ event_type == NotificationEvent ? "manual" : "auto", initial); -+ -+ return create_fsync( type, handle, access, attr, initial, 0xdeadbeef ); -+} -+ -+NTSTATUS fsync_open_event( HANDLE *handle, ACCESS_MASK access, -+ const OBJECT_ATTRIBUTES *attr ) -+{ -+ TRACE("name %s.\n", debugstr_us(attr->ObjectName)); -+ -+ return open_fsync( FSYNC_AUTO_EVENT, handle, access, attr ); -+} -+ -+NTSTATUS fsync_set_event( HANDLE handle, LONG *prev ) -+{ -+ struct event *event; -+ struct fsync *obj; -+ LONG current; -+ NTSTATUS ret; -+ -+ TRACE("%p.\n", handle); -+ -+ if ((ret = get_object( handle, &obj ))) return ret; -+ event = obj->shm; -+ -+ if (!(current = __atomic_exchange_n( &event->signaled, 1, __ATOMIC_SEQ_CST ))) -+ futex_wake( &event->signaled, INT_MAX ); -+ -+ if (prev) *prev = current; -+ -+ return STATUS_SUCCESS; -+} -+ -+NTSTATUS fsync_reset_event( HANDLE handle, LONG *prev ) -+{ -+ struct event *event; -+ struct fsync *obj; -+ LONG current; -+ NTSTATUS ret; -+ -+ TRACE("%p.\n", handle); -+ -+ if ((ret = get_object( handle, &obj ))) return ret; -+ event = obj->shm; -+ -+ current = __atomic_exchange_n( &event->signaled, 0, __ATOMIC_SEQ_CST ); -+ -+ if (prev) *prev = current; -+ -+ return STATUS_SUCCESS; -+} -+ -+NTSTATUS fsync_pulse_event( HANDLE handle, LONG *prev ) -+{ -+ struct event *event; -+ struct fsync *obj; -+ LONG current; -+ NTSTATUS ret; -+ -+ TRACE("%p.\n", handle); -+ -+ if ((ret = get_object( handle, &obj ))) return ret; -+ event = obj->shm; -+ -+ /* This isn't really correct; an application could miss the write. -+ * Unfortunately we can't really do much better. Fortunately this is rarely -+ * used (and publicly deprecated). */ -+ if (!(current = __atomic_exchange_n( &event->signaled, 1, __ATOMIC_SEQ_CST ))) -+ futex_wake( &event->signaled, INT_MAX ); -+ -+ /* Try to give other threads a chance to wake up. Hopefully erring on this -+ * side is the better thing to do... */ -+ NtYieldExecution(); -+ -+ __atomic_store_n( &event->signaled, 0, __ATOMIC_SEQ_CST ); -+ -+ if (prev) *prev = current; -+ -+ return STATUS_SUCCESS; -+} -+ -+NTSTATUS fsync_query_event( HANDLE handle, void *info, ULONG *ret_len ) -+{ -+ struct event *event; -+ struct fsync *obj; -+ EVENT_BASIC_INFORMATION *out = info; -+ NTSTATUS ret; -+ -+ TRACE("handle %p, info %p, ret_len %p.\n", handle, info, ret_len); -+ -+ if ((ret = get_object( handle, &obj ))) return ret; -+ event = obj->shm; -+ -+ out->EventState = event->signaled; -+ out->EventType = (obj->type == FSYNC_AUTO_EVENT ? SynchronizationEvent : NotificationEvent); -+ if (ret_len) *ret_len = sizeof(*out); -+ -+ return STATUS_SUCCESS; -+} -+ -+NTSTATUS fsync_create_mutex( HANDLE *handle, ACCESS_MASK access, -+ const OBJECT_ATTRIBUTES *attr, BOOLEAN initial ) -+{ -+ TRACE("name %s, initial %d.\n", -+ attr ? debugstr_us(attr->ObjectName) : "", initial); -+ -+ return create_fsync( FSYNC_MUTEX, handle, access, attr, -+ initial ? GetCurrentThreadId() : 0, initial ? 1 : 0 ); -+} -+ -+NTSTATUS fsync_open_mutex( HANDLE *handle, ACCESS_MASK access, -+ const OBJECT_ATTRIBUTES *attr ) -+{ -+ TRACE("name %s.\n", debugstr_us(attr->ObjectName)); -+ -+ return open_fsync( FSYNC_MUTEX, handle, access, attr ); -+} -+ -+NTSTATUS fsync_release_mutex( HANDLE handle, LONG *prev ) -+{ -+ struct mutex *mutex; -+ struct fsync *obj; -+ NTSTATUS ret; -+ -+ TRACE("%p, %p.\n", handle, prev); -+ -+ if ((ret = get_object( handle, &obj ))) return ret; -+ mutex = obj->shm; -+ -+ if (mutex->tid != GetCurrentThreadId()) return STATUS_MUTANT_NOT_OWNED; -+ -+ if (prev) *prev = mutex->count; -+ -+ if (!--mutex->count) -+ { -+ __atomic_store_n( &mutex->tid, 0, __ATOMIC_SEQ_CST ); -+ futex_wake( &mutex->tid, INT_MAX ); -+ } -+ -+ return STATUS_SUCCESS; -+} -+ -+NTSTATUS fsync_query_mutex( HANDLE handle, void *info, ULONG *ret_len ) -+{ -+ struct fsync *obj; -+ struct mutex *mutex; -+ MUTANT_BASIC_INFORMATION *out = info; -+ NTSTATUS ret; -+ -+ TRACE("handle %p, info %p, ret_len %p.\n", handle, info, ret_len); -+ -+ if ((ret = get_object( handle, &obj ))) return ret; -+ mutex = obj->shm; -+ -+ out->CurrentCount = 1 - mutex->count; -+ out->OwnedByCaller = (mutex->tid == GetCurrentThreadId()); -+ out->AbandonedState = (mutex->tid == ~0); -+ if (ret_len) *ret_len = sizeof(*out); -+ -+ 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 ) -+{ -+ int ret; -+ -+ 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; -+ -+ 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 ); -+ -+ 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 ); -+ } -+ -+ if (!ret) -+ return 0; -+ else if (ret < 0 && errno == ETIMEDOUT) -+ return STATUS_TIMEOUT; -+ else -+ return STATUS_PENDING; -+} -+ -+static NTSTATUS __fsync_wait_objects( DWORD count, const HANDLE *handles, -+ BOOLEAN wait_any, BOOLEAN alertable, const LARGE_INTEGER *timeout ) -+{ -+ static const LARGE_INTEGER zero = {0}; -+ -+ struct futex_wait_block futexes[MAXIMUM_WAIT_OBJECTS + 1]; -+ struct fsync *objs[MAXIMUM_WAIT_OBJECTS]; -+ int has_fsync = 0, has_server = 0; -+ BOOL msgwait = FALSE; -+ int dummy_futex = 0; -+ unsigned int spin; -+ LONGLONG timeleft; -+ LARGE_INTEGER now; -+ DWORD waitcount; -+ ULONGLONG end; -+ int i, ret; -+ -+ /* Grab the APC futex if we don't already have it. */ -+ if (alertable && !ntdll_get_thread_data()->fsync_apc_futex) -+ { -+ unsigned int idx = 0; -+ SERVER_START_REQ( get_fsync_apc_idx ) -+ { -+ if (!(ret = wine_server_call( req ))) -+ idx = reply->shm_idx; -+ } -+ SERVER_END_REQ; -+ -+ if (idx) -+ { -+ struct event *apc_event = get_shm( idx ); -+ ntdll_get_thread_data()->fsync_apc_futex = &apc_event->signaled; -+ } -+ } -+ -+ NtQuerySystemTime( &now ); -+ if (timeout) -+ { -+ if (timeout->QuadPart == TIMEOUT_INFINITE) -+ timeout = NULL; -+ else if (timeout->QuadPart > 0) -+ end = timeout->QuadPart; -+ else -+ end = now.QuadPart - timeout->QuadPart; -+ } -+ -+ for (i = 0; i < count; i++) -+ { -+ ret = get_object( handles[i], &objs[i] ); -+ if (ret == STATUS_SUCCESS) -+ has_fsync = 1; -+ else if (ret == STATUS_NOT_IMPLEMENTED) -+ has_server = 1; -+ else -+ return ret; -+ } -+ -+ if (count && objs[count - 1] && objs[count - 1]->type == FSYNC_QUEUE) -+ msgwait = TRUE; -+ -+ if (has_fsync && has_server) -+ FIXME("Can't wait on fsync and server objects at the same time!\n"); -+ else if (has_server) -+ return STATUS_NOT_IMPLEMENTED; -+ -+ if (TRACE_ON(fsync)) -+ { -+ TRACE("Waiting for %s of %d handles:", wait_any ? "any" : "all", count); -+ for (i = 0; i < count; i++) -+ TRACE(" %p", handles[i]); -+ -+ if (msgwait) -+ TRACE(" or driver events"); -+ if (alertable) -+ TRACE(", alertable"); -+ -+ if (!timeout) -+ TRACE(", timeout = INFINITE.\n"); -+ else -+ { -+ timeleft = update_timeout( end ); -+ TRACE(", timeout = %ld.%07ld sec.\n", -+ (long) (timeleft / TICKSPERSEC), (long) (timeleft % TICKSPERSEC)); -+ } -+ } -+ -+ if (wait_any || count <= 1) -+ { -+ while (1) -+ { -+ /* Try to grab anything. */ -+ -+ if (alertable) -+ { -+ /* We must check this first! The server may set an event that -+ * we're waiting on, but we need to return STATUS_USER_APC. */ -+ if (__atomic_load_n( ntdll_get_thread_data()->fsync_apc_futex, __ATOMIC_SEQ_CST )) -+ goto userapc; -+ } -+ -+ for (i = 0; i < count; i++) -+ { -+ struct fsync *obj = objs[i]; -+ -+ if (obj) -+ { -+ if (!obj->type) /* gcc complains if we put this in the switch */ -+ { -+ /* Someone probably closed an object while waiting on it. */ -+ WARN("Handle %p has type 0; was it closed?\n", handles[i]); -+ return STATUS_INVALID_HANDLE; -+ } -+ -+ switch (obj->type) -+ { -+ case FSYNC_SEMAPHORE: -+ { -+ 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) -+ { -+ TRACE("Woken up by handle %p [%d].\n", handles[i], i); -+ return i; -+ } -+ small_pause(); -+ } -+ -+ futexes[i].addr = &semaphore->count; -+ futexes[i].val = 0; -+ break; -+ } -+ case FSYNC_MUTEX: -+ { -+ struct mutex *mutex = obj->shm; -+ int tid; -+ -+ if (mutex->tid == GetCurrentThreadId()) -+ { -+ TRACE("Woken up by handle %p [%d].\n", handles[i], i); -+ mutex->count++; -+ return i; -+ } -+ -+ for (spin = 0; spin <= spincount; ++spin) -+ { -+ 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(); -+ } -+ -+ futexes[i].addr = &mutex->tid; -+ futexes[i].val = tid; -+ break; -+ } -+ case FSYNC_AUTO_EVENT: -+ case FSYNC_AUTO_SERVER: -+ { -+ struct event *event = obj->shm; -+ -+ for (spin = 0; spin <= spincount; ++spin) -+ { -+ 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(); -+ } -+ -+ futexes[i].addr = &event->signaled; -+ futexes[i].val = 0; -+ break; -+ } -+ case FSYNC_MANUAL_EVENT: -+ case FSYNC_MANUAL_SERVER: -+ case FSYNC_QUEUE: -+ { -+ struct event *event = obj->shm; -+ -+ for (spin = 0; spin <= spincount; ++spin) -+ { -+ if (__atomic_load_n( &event->signaled, __ATOMIC_SEQ_CST )) -+ { -+ TRACE("Woken up by handle %p [%d].\n", handles[i], i); -+ return i; -+ } -+ small_pause(); -+ } -+ -+ futexes[i].addr = &event->signaled; -+ futexes[i].val = 0; -+ break; -+ } -+ default: -+ ERR("Invalid type %#x for handle %p.\n", obj->type, handles[i]); -+ assert(0); -+ } -+ } -+ else -+ { -+ /* Avoid breaking things entirely. */ -+ futexes[i].addr = &dummy_futex; -+ futexes[i].val = 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++; -+ } -+ waitcount = i; -+ -+ /* Looks like everything is contended, so wait. */ -+ -+ if (timeout && !timeout->QuadPart) -+ { -+ /* Unlike esync, we already know that we've timed out, so we -+ * can avoid a syscall. */ -+ 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 ); -+ -+ /* FUTEX_WAIT_MULTIPLE can succeed or return -EINTR, -EAGAIN, -+ * -EFAULT/-EACCES, -ETIMEDOUT. In the first three cases we need to -+ * try again, bad address is already handled by the fact that we -+ * tried to read from it, so only break out on a timeout. */ -+ if (ret == -1 && errno == ETIMEDOUT) -+ { -+ TRACE("Wait timed out.\n"); -+ return STATUS_TIMEOUT; -+ } -+ } /* while (1) */ -+ } -+ else -+ { -+ /* Wait-all is a little trickier to implement correctly. Fortunately, -+ * it's not as common. -+ * -+ * The idea is basically just to wait in sequence on every object in the -+ * set. Then when we're done, try to grab them all in a tight loop. If -+ * that fails, release any resources we've grabbed (and yes, we can -+ * reliably do this—it's just mutexes and semaphores that we have to -+ * put back, and in both cases we just put back 1), and if any of that -+ * fails we start over. -+ * -+ * What makes this inherently bad is that we might temporarily grab a -+ * resource incorrectly. Hopefully it'll be quick (and hey, it won't -+ * block on wineserver) so nobody will notice. Besides, consider: if -+ * object A becomes signaled but someone grabs it before we can grab it -+ * and everything else, then they could just as well have grabbed it -+ * before it became signaled. Similarly if object A was signaled and we -+ * were blocking on object B, then B becomes available and someone grabs -+ * A before we can, then they might have grabbed A before B became -+ * signaled. In either case anyone who tries to wait on A or B will be -+ * waiting for an instant while we put things back. */ -+ -+ NTSTATUS status = STATUS_SUCCESS; -+ int current; -+ -+ while (1) -+ { -+ BOOL abandoned; -+ -+tryagain: -+ abandoned = FALSE; -+ -+ /* First step: try to wait on each object in sequence. */ -+ -+ for (i = 0; i < count; i++) -+ { -+ struct fsync *obj = objs[i]; -+ -+ if (obj && obj->type == FSYNC_MUTEX) -+ { -+ struct mutex *mutex = obj->shm; -+ -+ if (mutex->tid == GetCurrentThreadId()) -+ continue; -+ -+ while ((current = __atomic_load_n( &mutex->tid, __ATOMIC_SEQ_CST ))) -+ { -+ status = do_single_wait( &mutex->tid, current, timeout ? &end : NULL, alertable ); -+ if (status != STATUS_PENDING) -+ break; -+ } -+ } -+ else if (obj) -+ { -+ /* this works for semaphores too */ -+ struct event *event = obj->shm; -+ -+ while (!__atomic_load_n( &event->signaled, __ATOMIC_SEQ_CST )) -+ { -+ status = do_single_wait( &event->signaled, 0, timeout ? &end : NULL, alertable ); -+ if (status != STATUS_PENDING) -+ break; -+ } -+ } -+ -+ if (status == STATUS_TIMEOUT) -+ { -+ TRACE("Wait timed out.\n"); -+ return status; -+ } -+ else if (status == STATUS_USER_APC) -+ goto userapc; -+ } -+ -+ /* If we got here and we haven't timed out, that means all of the -+ * handles were signaled. Check to make sure they still are. */ -+ for (i = 0; i < count; i++) -+ { -+ struct fsync *obj = objs[i]; -+ -+ if (obj && obj->type == FSYNC_MUTEX) -+ { -+ struct mutex *mutex = obj->shm; -+ int tid = __atomic_load_n( &mutex->tid, __ATOMIC_SEQ_CST ); -+ -+ if (tid && tid != ~0 && tid != GetCurrentThreadId()) -+ goto tryagain; -+ } -+ else if (obj) -+ { -+ struct event *event = obj->shm; -+ -+ if (!__atomic_load_n( &event->signaled, __ATOMIC_SEQ_CST )) -+ goto tryagain; -+ } -+ } -+ -+ /* Yep, still signaled. Now quick, grab everything. */ -+ for (i = 0; i < count; i++) -+ { -+ struct fsync *obj = objs[i]; -+ switch (obj->type) -+ { -+ case FSYNC_MUTEX: -+ { -+ struct mutex *mutex = obj->shm; -+ int tid = __atomic_load_n( &mutex->tid, __ATOMIC_SEQ_CST ); -+ if (tid == GetCurrentThreadId()) -+ break; -+ if (tid && tid != ~0) -+ goto tooslow; -+ if (__sync_val_compare_and_swap( &mutex->tid, tid, GetCurrentThreadId() ) != tid) -+ goto tooslow; -+ if (tid == ~0) -+ abandoned = TRUE; -+ break; -+ } -+ case FSYNC_SEMAPHORE: -+ { -+ struct semaphore *semaphore = obj->shm; -+ if (__sync_fetch_and_sub( &semaphore->count, 1 ) <= 0) -+ goto tooslow; -+ break; -+ } -+ case FSYNC_AUTO_EVENT: -+ case FSYNC_AUTO_SERVER: -+ { -+ struct event *event = obj->shm; -+ if (!__sync_val_compare_and_swap( &event->signaled, 1, 0 )) -+ goto tooslow; -+ break; -+ } -+ default: -+ /* If a manual-reset event changed between there and -+ * here, it's shouldn't be a problem. */ -+ break; -+ } -+ } -+ -+ /* If we got here, we successfully waited on every object. -+ * Make sure to let ourselves know that we grabbed the mutexes. */ -+ for (i = 0; i < count; i++) -+ { -+ if (objs[i] && objs[i]->type == FSYNC_MUTEX) -+ { -+ struct mutex *mutex = objs[i]->shm; -+ mutex->count++; -+ } -+ } -+ -+ if (abandoned) -+ { -+ TRACE("Wait successful, but some object(s) were abandoned.\n"); -+ return STATUS_ABANDONED; -+ } -+ TRACE("Wait successful.\n"); -+ return STATUS_SUCCESS; -+ -+tooslow: -+ for (--i; i >= 0; i--) -+ { -+ struct fsync *obj = objs[i]; -+ switch (obj->type) -+ { -+ case FSYNC_MUTEX: -+ { -+ struct mutex *mutex = obj->shm; -+ /* HACK: This won't do the right thing with abandoned -+ * mutexes, but fixing it is probably more trouble than -+ * it's worth. */ -+ __atomic_store_n( &mutex->tid, 0, __ATOMIC_SEQ_CST ); -+ break; -+ } -+ case FSYNC_SEMAPHORE: -+ { -+ struct semaphore *semaphore = obj->shm; -+ __sync_fetch_and_add( &semaphore->count, 1 ); -+ break; -+ } -+ case FSYNC_AUTO_EVENT: -+ case FSYNC_AUTO_SERVER: -+ { -+ struct event *event = obj->shm; -+ __atomic_store_n( &event->signaled, 1, __ATOMIC_SEQ_CST ); -+ break; -+ } -+ default: -+ /* doesn't need to be put back */ -+ break; -+ } -+ } -+ } /* while (1) */ -+ } /* else (wait-all) */ -+ -+ assert(0); /* shouldn't reach here... */ -+ -+userapc: -+ TRACE("Woken up by user APC.\n"); -+ -+ /* We have to make a server call anyway to get the APC to execute, so just -+ * delegate down to server_wait(). */ -+ ret = server_wait( NULL, 0, SELECT_INTERRUPTIBLE | SELECT_ALERTABLE, &zero ); -+ -+ /* This can happen if we received a system APC, and the APC fd was woken up -+ * before we got SIGUSR1. poll() doesn't return EINTR in that case. The -+ * right thing to do seems to be to return STATUS_USER_APC anyway. */ -+ if (ret == STATUS_TIMEOUT) ret = STATUS_USER_APC; -+ return ret; -+} -+ -+/* Like esync, we need to let the server know when we are doing a message wait, -+ * and when we are done with one, so that all of the code surrounding hung -+ * queues works, and we also need this for WaitForInputIdle(). -+ * -+ * Unlike esync, we can't wait on the queue fd itself locally. Instead we let -+ * the server do that for us, the way it normally does. This could actually -+ * work for esync too, and that might be better. */ -+static void server_set_msgwait( int in_msgwait ) -+{ -+ SERVER_START_REQ( fsync_msgwait ) -+ { -+ req->in_msgwait = in_msgwait; -+ wine_server_call( req ); -+ } -+ SERVER_END_REQ; -+} -+ -+/* This is a very thin wrapper around the proper implementation above. The -+ * purpose is to make sure the server knows when we are doing a message wait. -+ * This is separated into a wrapper function since there are at least a dozen -+ * exit paths from fsync_wait_objects(). */ -+NTSTATUS fsync_wait_objects( DWORD count, const HANDLE *handles, BOOLEAN wait_any, -+ BOOLEAN alertable, const LARGE_INTEGER *timeout ) -+{ -+ BOOL msgwait = FALSE; -+ struct fsync *obj; -+ NTSTATUS ret; -+ -+ if (count && !get_object( handles[count - 1], &obj ) && obj->type == FSYNC_QUEUE) -+ { -+ msgwait = TRUE; -+ server_set_msgwait( 1 ); -+ } -+ -+ ret = __fsync_wait_objects( count, handles, wait_any, alertable, timeout ); -+ -+ if (msgwait) -+ server_set_msgwait( 0 ); -+ -+ return ret; -+} -+ -+NTSTATUS fsync_signal_and_wait( HANDLE signal, HANDLE wait, BOOLEAN alertable, -+ const LARGE_INTEGER *timeout ) -+{ -+ struct fsync *obj; -+ NTSTATUS ret; -+ -+ if ((ret = get_object( signal, &obj ))) return ret; -+ -+ switch (obj->type) -+ { -+ case FSYNC_SEMAPHORE: -+ ret = fsync_release_semaphore( signal, 1, NULL ); -+ break; -+ case FSYNC_AUTO_EVENT: -+ case FSYNC_MANUAL_EVENT: -+ ret = fsync_set_event( signal, NULL ); -+ break; -+ case FSYNC_MUTEX: -+ ret = fsync_release_mutex( signal, NULL ); -+ break; -+ default: -+ return STATUS_OBJECT_TYPE_MISMATCH; -+ } -+ if (ret) return ret; -+ -+ return fsync_wait_objects( 1, &wait, TRUE, alertable, timeout ); -+} -diff --git a/dlls/ntdll/unix/fsync.h b/dlls/ntdll/unix/fsync.h -new file mode 100644 -index 00000000000..b3604548554 ---- /dev/null -+++ b/dlls/ntdll/unix/fsync.h -@@ -0,0 +1,49 @@ -+/* -+ * futex-based synchronization objects -+ * -+ * Copyright (C) 2018 Zebediah Figura -+ * -+ * This library is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU Lesser General Public -+ * License as published by the Free Software Foundation; either -+ * version 2.1 of the License, or (at your option) any later version. -+ * -+ * This library is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -+ * Lesser General Public License for more details. -+ * -+ * You should have received a copy of the GNU Lesser General Public -+ * License along with this library; if not, write to the Free Software -+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA -+ */ -+ -+extern int do_fsync(void) DECLSPEC_HIDDEN; -+extern void fsync_init(void) DECLSPEC_HIDDEN; -+extern NTSTATUS fsync_close( HANDLE handle ) DECLSPEC_HIDDEN; -+ -+extern NTSTATUS fsync_create_semaphore(HANDLE *handle, ACCESS_MASK access, -+ const OBJECT_ATTRIBUTES *attr, LONG initial, LONG max) DECLSPEC_HIDDEN; -+extern NTSTATUS fsync_release_semaphore( HANDLE handle, ULONG count, ULONG *prev ) DECLSPEC_HIDDEN; -+extern NTSTATUS fsync_open_semaphore( HANDLE *handle, ACCESS_MASK access, -+ const OBJECT_ATTRIBUTES *attr ) DECLSPEC_HIDDEN; -+extern NTSTATUS fsync_query_semaphore( HANDLE handle, void *info, ULONG *ret_len ) DECLSPEC_HIDDEN; -+extern NTSTATUS fsync_create_event( HANDLE *handle, ACCESS_MASK access, -+ const OBJECT_ATTRIBUTES *attr, EVENT_TYPE type, BOOLEAN initial ) DECLSPEC_HIDDEN; -+extern NTSTATUS fsync_open_event( HANDLE *handle, ACCESS_MASK access, -+ const OBJECT_ATTRIBUTES *attr ) DECLSPEC_HIDDEN; -+extern NTSTATUS fsync_set_event( HANDLE handle, LONG *prev ) DECLSPEC_HIDDEN; -+extern NTSTATUS fsync_reset_event( HANDLE handle, LONG *prev ) DECLSPEC_HIDDEN; -+extern NTSTATUS fsync_pulse_event( HANDLE handle, LONG *prev ) DECLSPEC_HIDDEN; -+extern NTSTATUS fsync_query_event( HANDLE handle, void *info, ULONG *ret_len ) DECLSPEC_HIDDEN; -+extern NTSTATUS fsync_create_mutex( HANDLE *handle, ACCESS_MASK access, -+ const OBJECT_ATTRIBUTES *attr, BOOLEAN initial ) DECLSPEC_HIDDEN; -+extern NTSTATUS fsync_open_mutex( HANDLE *handle, ACCESS_MASK access, -+ const OBJECT_ATTRIBUTES *attr ) DECLSPEC_HIDDEN; -+extern NTSTATUS fsync_release_mutex( HANDLE handle, LONG *prev ) DECLSPEC_HIDDEN; -+extern NTSTATUS fsync_query_mutex( HANDLE handle, void *info, ULONG *ret_len ) DECLSPEC_HIDDEN; -+ -+extern NTSTATUS fsync_wait_objects( DWORD count, const HANDLE *handles, BOOLEAN wait_any, -+ BOOLEAN alertable, const LARGE_INTEGER *timeout ) DECLSPEC_HIDDEN; -+extern NTSTATUS fsync_signal_and_wait( HANDLE signal, HANDLE wait, -+ BOOLEAN alertable, const LARGE_INTEGER *timeout ) DECLSPEC_HIDDEN; -diff --git a/dlls/ntdll/unix/loader.c b/dlls/ntdll/unix/loader.c -index e4c9e5272f9..fb23db29b9b 100644 ---- a/dlls/ntdll/unix/loader.c -+++ b/dlls/ntdll/unix/loader.c -@@ -87,6 +87,7 @@ - #include "winternl.h" - #include "unix_private.h" - #include "esync.h" -+#include "fsync.h" - #include "wine/list.h" - #include "wine/debug.h" - -@@ -1569,5 +1570,6 @@ static void start_main_thread(void) - dbg_init(); - startup_info_size = server_init_process(); -+ fsync_init(); - esync_init(); - virtual_map_user_shared_data(); - init_cpu_info(); -diff --git a/dlls/ntdll/unix/server.c b/dlls/ntdll/unix/server.c -index d8837f6fde0..bc0d3537a00 100644 ---- a/dlls/ntdll/unix/server.c -+++ b/dlls/ntdll/unix/server.c -@@ -89,6 +89,7 @@ - #include "wine/debug.h" - #include "unix_private.h" - #include "esync.h" -+#include "fsync.h" - #include "ddk/wdm.h" - - WINE_DEFAULT_DEBUG_CHANNEL(server); -@@ -1645,6 +1646,9 @@ NTSTATUS WINAPI NtClose( HANDLE handle ) - NTSTATUS ret; - int fd = remove_fd_from_cache( handle ); - -+ if (do_fsync()) -+ fsync_close( handle ); -+ - if (do_esync()) - esync_close( handle ); - -diff --git a/dlls/ntdll/unix/sync.c b/dlls/ntdll/unix/sync.c -index 4f47afdb7b2..fddf8ca082d 100644 ---- a/dlls/ntdll/unix/sync.c -+++ b/dlls/ntdll/unix/sync.c -@@ -73,6 +73,7 @@ - #include "wine/debug.h" - #include "unix_private.h" - #include "esync.h" -+#include "fsync.h" - - WINE_DEFAULT_DEBUG_CHANNEL(sync); - -@@ -323,6 +324,9 @@ NTSTATUS WINAPI NtCreateSemaphore( HANDLE *handle, ACCESS_MASK access, const OBJ - if (max <= 0 || initial < 0 || initial > max) return STATUS_INVALID_PARAMETER; - if ((ret = alloc_object_attributes( attr, &objattr, &len ))) return ret; - -+ if (do_fsync()) -+ return fsync_create_semaphore( handle, access, attr, initial, max ); -+ - if (do_esync()) - return esync_create_semaphore( handle, access, attr, initial, max ); - -@@ -349,6 +353,9 @@ NTSTATUS WINAPI NtOpenSemaphore( HANDLE *handle, ACCESS_MASK access, const OBJEC - { - NTSTATUS ret; - -+ if (do_fsync()) -+ return fsync_open_semaphore( handle, access, attr ); -+ - if (do_esync()) - return esync_open_semaphore( handle, access, attr ); - -@@ -388,6 +395,9 @@ NTSTATUS WINAPI NtQuerySemaphore( HANDLE handle, SEMAPHORE_INFORMATION_CLASS cla - - if (len != sizeof(SEMAPHORE_BASIC_INFORMATION)) return STATUS_INFO_LENGTH_MISMATCH; - -+ if (do_fsync()) -+ return fsync_query_semaphore( handle, info, ret_len ); -+ - if (do_esync()) - return esync_query_semaphore( handle, info, ret_len ); - -@@ -413,6 +423,9 @@ NTSTATUS WINAPI NtReleaseSemaphore( HANDLE handle, ULONG count, ULONG *previous - { - NTSTATUS ret; - -+ if (do_fsync()) -+ return fsync_release_semaphore( handle, count, previous ); -+ - if (do_esync()) - return esync_release_semaphore( handle, count, previous ); - -@@ -440,8 +453,11 @@ NTSTATUS WINAPI NtCreateEvent( HANDLE *handle, ACCESS_MASK access, const OBJECT_ - data_size_t len; - struct object_attributes *objattr; - - if (type != NotificationEvent && type != SynchronizationEvent) return STATUS_INVALID_PARAMETER; - -+ if (do_fsync()) -+ return fsync_create_event( handle, access, attr, type, state ); -+ - if (do_esync()) - return esync_create_event( handle, access, attr, type, state ); - -@@ -470,6 +486,9 @@ NTSTATUS WINAPI NtOpenEvent( HANDLE *handle, ACCESS_MASK access, const OBJECT_AT - - if ((ret = validate_open_object_attributes( attr ))) return ret; - -+ if (do_fsync()) -+ return fsync_open_event( handle, access, attr ); -+ - if (do_esync()) - return esync_open_event( handle, access, attr ); - -@@ -495,5 +514,8 @@ NTSTATUS WINAPI NtSetEvent( HANDLE handle, LONG *prev_state ) - NTSTATUS ret; - -+ if (do_fsync()) -+ return fsync_set_event( handle, prev_state ); -+ - if (do_esync()) - return esync_set_event( handle ); - -@@ -517,5 +539,8 @@ NTSTATUS WINAPI NtResetEvent( HANDLE handle, LONG *prev_state ) - NTSTATUS ret; - -+ if (do_fsync()) -+ return fsync_reset_event( handle, prev_state ); -+ - if (do_esync()) - return esync_reset_event( handle ); - -@@ -549,5 +574,8 @@ NTSTATUS WINAPI NtPulseEvent( HANDLE handle, LONG *prev_state ) - NTSTATUS ret; - -+ if (do_fsync()) -+ return fsync_pulse_event( handle, prev_state ); -+ - if (do_esync()) - return esync_pulse_event( handle ); - -@@ -584,6 +612,9 @@ NTSTATUS WINAPI NtQueryEvent( HANDLE handle, EVENT_INFORMATION_CLASS class, - - if (len != sizeof(EVENT_BASIC_INFORMATION)) return STATUS_INFO_LENGTH_MISMATCH; - -+ if (do_fsync()) -+ return fsync_query_event( handle, info, ret_len ); -+ - if (do_esync()) - return esync_query_event( handle, info, ret_len ); - -@@ -612,6 +643,9 @@ NTSTATUS WINAPI NtCreateMutant( HANDLE *handle, ACCESS_MASK access, const OBJECT - data_size_t len; - struct object_attributes *objattr; - -+ if (do_fsync()) -+ return fsync_create_mutex( handle, access, attr, owned ); -+ - if (do_esync()) - return esync_create_mutex( handle, access, attr, owned ); - -@@ -641,6 +675,9 @@ NTSTATUS WINAPI NtOpenMutant( HANDLE *handle, ACCESS_MASK access, const OBJECT_A - - if ((ret = validate_open_object_attributes( attr ))) return ret; - -+ if (do_fsync()) -+ return fsync_open_mutex( handle, access, attr ); -+ - if (do_esync()) - return esync_open_mutex( handle, access, attr ); - -@@ -666,6 +703,9 @@ NTSTATUS WINAPI NtReleaseMutant( HANDLE handle, LONG *prev_count ) - { - NTSTATUS ret; - -+ if (do_fsync()) -+ return fsync_release_mutex( handle, prev_count ); -+ - if (do_esync()) - return esync_release_mutex( handle, prev_count ); - -@@ -699,6 +739,9 @@ NTSTATUS WINAPI NtQueryMutant( HANDLE handle, MUTANT_INFORMATION_CLASS class, - - if (len != sizeof(MUTANT_BASIC_INFORMATION)) return STATUS_INFO_LENGTH_MISMATCH; - -+ if (do_fsync()) -+ return fsync_query_mutex( handle, info, ret_len ); -+ - if (do_esync()) - return esync_query_mutex( handle, info, ret_len ); - -@@ -1309,6 +1352,13 @@ NTSTATUS WINAPI NtWaitForMultipleObjects( DWORD count, const HANDLE *handles, BO - - if (!count || count > MAXIMUM_WAIT_OBJECTS) return STATUS_INVALID_PARAMETER_1; - -+ if (do_fsync()) -+ { -+ NTSTATUS ret = fsync_wait_objects( count, handles, wait_any, alertable, timeout ); -+ if (ret != STATUS_NOT_IMPLEMENTED) -+ return ret; -+ } -+ - if (do_esync()) - { - NTSTATUS ret = esync_wait_objects( count, handles, wait_any, alertable, timeout ); -@@ -1341,6 +1391,9 @@ NTSTATUS WINAPI NtSignalAndWaitForSingleObject( HANDLE signal, HANDLE wait, - select_op_t select_op; - UINT flags = SELECT_INTERRUPTIBLE; - -+ if (do_fsync()) -+ return fsync_signal_and_wait( signal, wait, alertable, timeout ); -+ - if (do_esync()) - return esync_signal_and_wait( signal, wait, alertable, timeout ); - -@@ -1374,7 +1427,24 @@ NTSTATUS WINAPI NtYieldExecution(void) - NTSTATUS WINAPI NtDelayExecution( BOOLEAN alertable, const LARGE_INTEGER *timeout ) - { - /* if alertable, we need to query the server */ -- if (alertable) return server_wait( NULL, 0, SELECT_INTERRUPTIBLE | SELECT_ALERTABLE, timeout ); -+ if (alertable) -+ { -+ if (do_fsync()) -+ { -+ NTSTATUS ret = fsync_wait_objects( 0, NULL, TRUE, TRUE, timeout ); -+ if (ret != STATUS_NOT_IMPLEMENTED) -+ return ret; -+ } -+ -+ if (do_esync()) -+ { -+ NTSTATUS ret = esync_wait_objects( 0, NULL, TRUE, TRUE, timeout ); -+ if (ret != STATUS_NOT_IMPLEMENTED) -+ return ret; -+ } -+ -+ return server_wait( NULL, 0, SELECT_INTERRUPTIBLE | SELECT_ALERTABLE, timeout ); -+ } - - if (!timeout || timeout->QuadPart == TIMEOUT_INFINITE) /* sleep forever */ - { -diff --git a/dlls/ntdll/unix/unix_private.h b/dlls/ntdll/unix/unix_private.h -index 0e2461ded79..5cd12a2bd5d 100644 ---- a/dlls/ntdll/unix/unix_private.h -+++ b/dlls/ntdll/unix/unix_private.h -@@ -58,6 +58,7 @@ struct ntdll_thread_data - struct debug_info *debug_info; /* info for debugstr functions */ - void *start_stack; /* stack for thread startup */ - int esync_apc_fd; /* fd to wait on for user APCs */ -+ int *fsync_apc_futex; - int request_fd; /* fd for sending server requests */ - int reply_fd; /* fd for receiving server replies */ - int wait_fd[2]; /* fd for sleeping server requests */ -diff --git a/dlls/ntdll/unix/virtual.c b/dlls/ntdll/unix/virtual.c -index 828cbafb352..360396f9de0 100644 ---- a/dlls/ntdll/unix/virtual.c -+++ b/dlls/ntdll/unix/virtual.c -@@ -2676,8 +2676,9 @@ static void init_teb( TEB *teb, PEB *peb ) - InitializeListHead( &teb->ActivationContextStack.FrameListCache ); - teb->StaticUnicodeString.Buffer = teb->StaticUnicodeBuffer; - teb->StaticUnicodeString.MaximumLength = sizeof(teb->StaticUnicodeBuffer); - thread_data->esync_apc_fd = -1; -+ thread_data->fsync_apc_futex = NULL; - thread_data->request_fd = -1; - thread_data->reply_fd = -1; - thread_data->wait_fd[0] = -1; - thread_data->wait_fd[1] = -1; -diff --git a/include/wine/server_protocol.h b/include/wine/server_protocol.h -index 08b0ddc5f4e..838ae225903 100644 ---- a/include/wine/server_protocol.h -+++ b/include/wine/server_protocol.h -@@ -5434,6 +5434,91 @@ struct get_esync_apc_fd_reply - struct reply_header __header; - }; - -+enum fsync_type -+{ -+ FSYNC_SEMAPHORE = 1, -+ FSYNC_AUTO_EVENT, -+ FSYNC_MANUAL_EVENT, -+ FSYNC_MUTEX, -+ FSYNC_AUTO_SERVER, -+ FSYNC_MANUAL_SERVER, -+ FSYNC_QUEUE, -+}; -+ -+ -+struct create_fsync_request -+{ -+ struct request_header __header; -+ unsigned int access; -+ int low; -+ int high; -+ int type; -+ /* VARARG(objattr,object_attributes); */ -+ char __pad_28[4]; -+}; -+struct create_fsync_reply -+{ -+ struct reply_header __header; -+ obj_handle_t handle; -+ int type; -+ unsigned int shm_idx; -+ char __pad_20[4]; -+}; -+ -+ -+struct open_fsync_request -+{ -+ struct request_header __header; -+ unsigned int access; -+ unsigned int attributes; -+ obj_handle_t rootdir; -+ int type; -+ /* VARARG(name,unicode_str); */ -+ char __pad_28[4]; -+}; -+struct open_fsync_reply -+{ -+ struct reply_header __header; -+ obj_handle_t handle; -+ int type; -+ unsigned int shm_idx; -+ char __pad_20[4]; -+}; -+ -+ -+struct get_fsync_idx_request -+{ -+ struct request_header __header; -+ obj_handle_t handle; -+}; -+struct get_fsync_idx_reply -+{ -+ struct reply_header __header; -+ int type; -+ unsigned int shm_idx; -+}; -+ -+struct fsync_msgwait_request -+{ -+ struct request_header __header; -+ int in_msgwait; -+}; -+struct fsync_msgwait_reply -+{ -+ struct reply_header __header; -+}; -+ -+struct get_fsync_apc_idx_request -+{ -+ struct request_header __header; -+ char __pad_12[4]; -+}; -+struct get_fsync_apc_idx_reply -+{ -+ struct reply_header __header; -+ unsigned int shm_idx; -+ char __pad_12[4]; -+}; - - enum request - { -@@ -5716,9 +5801,15 @@ enum request - REQ_get_esync_fd, - REQ_esync_msgwait, - REQ_get_esync_apc_fd, -+ REQ_create_fsync, -+ REQ_open_fsync, -+ REQ_get_fsync_idx, -+ REQ_fsync_msgwait, -+ REQ_get_fsync_apc_idx, - REQ_NB_REQUESTS - }; - -+ - union generic_request - { - struct request_max_size max_size; -@@ -6002,6 +6093,11 @@ union generic_request - struct get_esync_fd_request get_esync_fd_request; - struct esync_msgwait_request esync_msgwait_request; - struct get_esync_apc_fd_request get_esync_apc_fd_request; -+ struct create_fsync_request create_fsync_request; -+ struct open_fsync_request open_fsync_request; -+ struct get_fsync_idx_request get_fsync_idx_request; -+ struct fsync_msgwait_request fsync_msgwait_request; -+ struct get_fsync_apc_idx_request get_fsync_apc_idx_request; - }; - union generic_reply - { -@@ -6286,6 +6382,11 @@ union generic_reply - struct get_esync_fd_reply get_esync_fd_reply; - struct esync_msgwait_reply esync_msgwait_reply; - struct get_esync_apc_fd_reply get_esync_apc_fd_reply; -+ struct create_fsync_reply create_fsync_reply; -+ struct open_fsync_reply open_fsync_reply; -+ struct get_fsync_idx_reply get_fsync_idx_reply; -+ struct fsync_msgwait_reply fsync_msgwait_reply; -+ struct get_fsync_apc_idx_reply get_fsync_apc_idx_reply; - }; - - /* ### protocol_version begin ### */ -diff --git a/server/Makefile.in b/server/Makefile.in -index 8bd612b4728..6dc5c465e52 100644 ---- a/server/Makefile.in -+++ b/server/Makefile.in -@@ -15,6 +15,7 @@ C_SRCS = \ - event.c \ - fd.c \ - file.c \ -+ fsync.c \ - handle.c \ - hook.c \ - mach.c \ -diff --git a/server/async.c b/server/async.c -index bdd68663ab9..4655c41763c 100644 ---- a/server/async.c -+++ b/server/async.c -@@ -71,6 +71,7 @@ static const struct object_ops async_ops = - remove_queue, /* remove_queue */ - async_signaled, /* signaled */ - NULL, /* get_esync_fd */ -+ NULL, /* get_fsync_idx */ - async_satisfied, /* satisfied */ - no_signal, /* signal */ - no_get_fd, /* get_fd */ -@@ -486,6 +487,7 @@ static const struct object_ops iosb_ops = - NULL, /* remove_queue */ - NULL, /* signaled */ - NULL, /* get_esync_fd */ -+ NULL, /* get_fsync_idx */ - NULL, /* satisfied */ - no_signal, /* signal */ - no_get_fd, /* get_fd */ -diff --git a/server/atom.c b/server/atom.c -index 73d858fef82..7acef97c30d 100644 ---- a/server/atom.c -+++ b/server/atom.c -@@ -81,6 +81,7 @@ static const struct object_ops atom_table_ops = - NULL, /* remove_queue */ - NULL, /* signaled */ - NULL, /* get_esync_fd */ -+ NULL, /* get_fsync_idx */ - NULL, /* satisfied */ - no_signal, /* signal */ - no_get_fd, /* get_fd */ -diff --git a/server/change.c b/server/change.c -index 9c84f01180c..4c11f160f77 100644 ---- a/server/change.c -+++ b/server/change.c -@@ -116,6 +116,7 @@ static const struct object_ops dir_ops = - remove_queue, /* remove_queue */ - default_fd_signaled, /* signaled */ - default_fd_get_esync_fd, /* get_esync_fd */ -+ default_fd_get_fsync_idx, /* get_fsync_idx */ - no_satisfied, /* satisfied */ - no_signal, /* signal */ - dir_get_fd, /* get_fd */ -diff --git a/server/clipboard.c b/server/clipboard.c -index 54a5fb683cc..1fa61b67a88 100644 ---- a/server/clipboard.c -+++ b/server/clipboard.c -@@ -78,6 +78,7 @@ static const struct object_ops clipboard_ops = - NULL, /* remove_queue */ - NULL, /* signaled */ - NULL, /* get_esync_fd */ -+ NULL, /* get_fsync_idx */ - NULL, /* satisfied */ - no_signal, /* signal */ - no_get_fd, /* get_fd */ -diff --git a/server/completion.c b/server/completion.c -index 176cf1b817e..07c32d1adfa 100644 ---- a/server/completion.c -+++ b/server/completion.c -@@ -65,6 +65,7 @@ static const struct object_ops completion_ops = - remove_queue, /* remove_queue */ - completion_signaled, /* signaled */ - NULL, /* get_esync_fd */ -+ NULL, /* get_fsync_idx */ - no_satisfied, /* satisfied */ - no_signal, /* signal */ - no_get_fd, /* get_fd */ -diff --git a/server/console.c b/server/console.c -index 9564b2d2d7d..8796a11eef7 100644 ---- a/server/console.c -+++ b/server/console.c -@@ -43,6 +43,7 @@ - #include "winternl.h" - #include "wine/condrv.h" - #include "esync.h" -+#include "fsync.h" - - struct screen_buffer; - -@@ -83,6 +84,7 @@ static const struct object_ops console_ops = - remove_queue, /* remove_queue */ - console_signaled, /* signaled */ - NULL, /* get_esync_fd */ -+ NULL, /* get_fsync_idx */ - no_satisfied, /* satisfied */ - no_signal, /* signal */ - console_get_fd, /* get_fd */ -@@ -140,14 +142,16 @@ struct console_server - int term_fd; /* UNIX terminal fd */ - struct termios termios; /* original termios */ - int esync_fd; -+ unsigned int fsync_idx; - }; - - static void console_server_dump( struct object *obj, int verbose ); - static void console_server_destroy( struct object *obj ); - static int console_server_signaled( struct object *obj, struct wait_queue_entry *entry ); - static int console_server_get_esync_fd( struct object *obj, enum esync_type *type ); -+static unsigned int console_server_get_fsync_idx( struct object *obj, enum fsync_type *type ); - static struct fd *console_server_get_fd( struct object *obj ); - static struct object *console_server_lookup_name( struct object *obj, struct unicode_str *name, - unsigned int attr, struct object *root ); - static struct object *console_server_open_file( struct object *obj, unsigned int access, - unsigned int sharing, unsigned int options ); -@@ -160,6 +164,7 @@ static const struct object_ops console_server_ops = - remove_queue, /* remove_queue */ - console_server_signaled, /* signaled */ - console_server_get_esync_fd, /* get_esync_fd */ -+ console_server_get_fsync_idx, /* get_fsync_idx */ - no_satisfied, /* satisfied */ - no_signal, /* signal */ - console_server_get_fd, /* get_fd */ -@@ -229,6 +234,7 @@ static const struct object_ops screen_buffer_ops = - NULL, /* remove_queue */ - NULL, /* signaled */ - NULL, /* get_esync_fd */ -+ NULL, /* get_fsync_idx */ - NULL, /* satisfied */ - no_signal, /* signal */ - screen_buffer_get_fd, /* get_fd */ -@@ -278,6 +284,7 @@ static const struct object_ops console_device_ops = - NULL, /* remove_queue */ - NULL, /* signaled */ - NULL, /* get_esync_fd */ -+ NULL, /* get_fsync_idx */ - no_satisfied, /* satisfied */ - no_signal, /* signal */ - no_get_fd, /* get_fd */ -@@ -309,6 +316,7 @@ static const struct object_ops console_input_ops = - NULL, /* remove_queue */ - NULL, /* signaled */ - NULL, /* get_esync_fd */ -+ NULL, /* get_fsync_idx */ - no_satisfied, /* satisfied */ - no_signal, /* signal */ - console_input_get_fd, /* get_fd */ -@@ -340,6 +348,7 @@ static const struct object_ops console_output_ops = - NULL, /* remove_queue */ - NULL, /* signaled */ - NULL, /* get_esync_fd */ -+ NULL, /* get_fsync_idx */ - no_satisfied, /* satisfied */ - no_signal, /* signal */ - console_output_get_fd, /* get_fd */ -@@ -379,6 +388,7 @@ static const struct object_ops console_connection_ops = - NULL, /* remove_queue */ - NULL, /* signaled */ - NULL, /* get_esync_fd */ -+ NULL, /* get_fsync_idx */ - no_satisfied, /* satisfied */ - no_signal, /* signal */ - console_connection_get_fd, /* get_fd */ -@@ -850,6 +860,13 @@ static int console_server_get_esync_fd( struct object *obj, enum esync_type *typ - return server->esync_fd; - } - -+static unsigned int console_server_get_fsync_idx( struct object *obj, enum fsync_type *type ) -+{ -+ struct console_server *server = (struct console_server*)obj; -+ *type = FSYNC_MANUAL_SERVER; -+ return server->fsync_idx; -+} -+ - static struct fd *console_server_get_fd( struct object* obj ) - { - struct console_server *server = (struct console_server*)obj; -@@ -882,6 +899,9 @@ static struct object *create_console_server( void ) - allow_fd_caching(server->fd); - server->esync_fd = -1; - -+ if (do_fsync()) -+ server->fsync_idx = fsync_alloc_shm( 0, 0 ); -+ - if (do_esync()) - server->esync_fd = esync_create_fd( 0, 0 ); - -diff --git a/server/debugger.c b/server/debugger.c -index c37f97aa0b6..b7d88cfd2f2 100644 ---- a/server/debugger.c -+++ b/server/debugger.c -@@ -74,6 +74,7 @@ static const struct object_ops debug_event_ops = - remove_queue, /* remove_queue */ - debug_event_signaled, /* signaled */ - NULL, /* get_esync_fd */ -+ NULL, /* get_fsync_idx */ - no_satisfied, /* satisfied */ - no_signal, /* signal */ - no_get_fd, /* get_fd */ -diff --git a/server/device.c b/server/device.c -index b8ce131c732..c56151c9b18 100644 ---- a/server/device.c -+++ b/server/device.c -@@ -40,6 +40,7 @@ - #include "request.h" - #include "process.h" - #include "esync.h" -+#include "fsync.h" - - /* IRP object */ - -@@ -70,6 +71,7 @@ static const struct object_ops irp_call_ops = - remove_queue, /* remove_queue */ - irp_call_signaled, /* signaled */ - NULL, /* get_esync_fd */ -+ NULL, /* get_fsync_idx */ - no_satisfied, /* satisfied */ - no_signal, /* signal */ - no_get_fd, /* get_fd */ -@@ -97,11 +99,13 @@ struct device_manager - struct irp_call *current_call; /* call currently executed on client side */ - struct wine_rb_tree kernel_objects; /* map of objects that have client side pointer associated */ - int esync_fd; /* esync file descriptor */ -+ unsigned int fsync_idx; - }; - - static void device_manager_dump( struct object *obj, int verbose ); - static int device_manager_signaled( struct object *obj, struct wait_queue_entry *entry ); - static int device_manager_get_esync_fd( struct object *obj, enum esync_type *type ); -+static unsigned int device_manager_get_fsync_idx( struct object *obj, enum fsync_type *type ); - static void device_manager_destroy( struct object *obj ); - - static const struct object_ops device_manager_ops = -@@ -113,6 +117,7 @@ static const struct object_ops device_manager_ops = - remove_queue, /* remove_queue */ - device_manager_signaled, /* signaled */ - device_manager_get_esync_fd, /* get_esync_fd */ -+ device_manager_get_fsync_idx, /* get_fsync_idx */ - no_satisfied, /* satisfied */ - no_signal, /* signal */ - no_get_fd, /* get_fd */ -@@ -158,6 +163,7 @@ static const struct object_ops device_ops = - NULL, /* remove_queue */ - NULL, /* signaled */ - NULL, /* get_esync_fd */ -+ NULL, /* get_fsync_idx */ - no_satisfied, /* satisfied */ - no_signal, /* signal */ - no_get_fd, /* get_fd */ -@@ -210,6 +216,7 @@ static const struct object_ops device_file_ops = - remove_queue, /* remove_queue */ - default_fd_signaled, /* signaled */ - NULL, /* get_esync_fd */ -+ NULL, /* get_fsync_idx */ - no_satisfied, /* satisfied */ - no_signal, /* signal */ - device_file_get_fd, /* get_fd */ -@@ -754,6 +761,9 @@ static void delete_file( struct device_file *file ) - /* terminate all pending requests */ - LIST_FOR_EACH_ENTRY_SAFE( irp, next, &file->requests, struct irp_call, dev_entry ) - { -+ if (do_fsync() && file->device->manager && list_empty( &file->device->manager->requests )) -+ fsync_clear( &file->device->manager->obj ); -+ - if (do_esync() && file->device->manager && list_empty( &file->device->manager->requests )) - esync_clear( file->device->manager->esync_fd ); - -@@ -799,6 +809,13 @@ static int device_manager_get_esync_fd( struct object *obj, enum esync_type *typ - return manager->esync_fd; - } - -+static unsigned int device_manager_get_fsync_idx( struct object *obj, enum fsync_type *type ) -+{ -+ struct device_manager *manager = (struct device_manager *)obj; -+ *type = FSYNC_MANUAL_SERVER; -+ return manager->fsync_idx; -+} -+ - static void device_manager_destroy( struct object *obj ) - { - struct device_manager *manager = (struct device_manager *)obj; -@@ -849,6 +866,9 @@ static struct device_manager *create_device_manager(void) - list_init( &manager->requests ); - wine_rb_init( &manager->kernel_objects, compare_kernel_object ); - -+ if (do_fsync()) -+ manager->fsync_idx = fsync_alloc_shm( 0, 0 ); -+ - if (do_esync()) - manager->esync_fd = esync_create_fd( 0, 0 ); - } -@@ -1017,6 +1037,9 @@ DECL_HANDLER(get_next_device_request) - if (irp->file) grab_object( irp ); - manager->current_call = irp; - -+ if (do_fsync() && list_empty( &manager->requests )) -+ fsync_clear( &manager->obj ); -+ - if (do_esync() && list_empty( &manager->requests )) - esync_clear( manager->esync_fd ); - } -diff --git a/server/directory.c b/server/directory.c -index faf41585ed8..26e5812073a 100644 ---- a/server/directory.c -+++ b/server/directory.c -@@ -58,6 +58,7 @@ static const struct object_ops object_type_ops = - NULL, /* remove_queue */ - NULL, /* signaled */ - NULL, /* get_esync_fd */ -+ NULL, /* get_fsync_idx */ - NULL, /* satisfied */ - no_signal, /* signal */ - no_get_fd, /* get_fd */ -@@ -96,6 +97,7 @@ static const struct object_ops directory_ops = - NULL, /* remove_queue */ - NULL, /* signaled */ - NULL, /* get_esync_fd */ -+ NULL, /* get_fsync_idx */ - NULL, /* satisfied */ - no_signal, /* signal */ - no_get_fd, /* get_fd */ -diff --git a/server/esync.c b/server/esync.c -index d79be851c8f..e975b1e6c2b 100644 ---- a/server/esync.c -+++ b/server/esync.c -@@ -44,6 +44,7 @@ - #include "request.h" - #include "file.h" - #include "esync.h" -+#include "fsync.h" - - int do_esync(void) - { -@@ -51,7 +52,7 @@ int do_esync(void) - static int do_esync_cached = -1; - - if (do_esync_cached == -1) -- do_esync_cached = getenv("WINEESYNC") && atoi(getenv("WINEESYNC")); -+ do_esync_cached = getenv("WINEESYNC") && atoi(getenv("WINEESYNC")) && !do_fsync(); - - return do_esync_cached; - #else -@@ -130,6 +131,7 @@ const struct object_ops esync_ops = - NULL, /* remove_queue */ - NULL, /* signaled */ - esync_get_esync_fd, /* get_esync_fd */ -+ NULL, /* get_fsync_idx */ - NULL, /* satisfied */ - no_signal, /* signal */ - no_get_fd, /* get_fd */ -diff --git a/server/event.c b/server/event.c -index b6f989d4d6a..ae7782695f7 100644 ---- a/server/event.c -+++ b/server/event.c -@@ -36,4 +36,5 @@ - #include "request.h" - #include "security.h" - #include "esync.h" -+#include "fsync.h" - -@@ -44,6 +45,7 @@ struct event - int manual_reset; /* is it a manual reset event? */ - int signaled; /* event has been signaled */ - int esync_fd; /* esync file descriptor */ -+ unsigned int fsync_idx; - }; - - static void event_dump( struct object *obj, int verbose ); -@@ -51,5 +53,6 @@ static struct object_type *event_get_type( struct object *obj ); - static int event_signaled( struct object *obj, struct wait_queue_entry *entry ); - static void event_satisfied( struct object *obj, struct wait_queue_entry *entry ); - static int event_get_esync_fd( struct object *obj, enum esync_type *type ); -+static unsigned int event_get_fsync_idx( struct object *obj, enum fsync_type *type ); - static int event_signal( struct object *obj, unsigned int access); - static struct list *event_get_kernel_obj_list( struct object *obj ); -@@ -65,6 +68,7 @@ static const struct object_ops event_ops = - remove_queue, /* remove_queue */ - event_signaled, /* signaled */ - event_get_esync_fd, /* get_esync_fd */ -+ event_get_fsync_idx, /* get_fsync_idx */ - event_satisfied, /* satisfied */ - event_signal, /* signal */ - no_get_fd, /* get_fd */ -@@ -101,6 +105,7 @@ static const struct object_ops keyed_event_ops = - remove_queue, /* remove_queue */ - keyed_event_signaled, /* signaled */ - NULL, /* get_esync_fd */ -+ NULL, /* get_fsync_idx */ - no_satisfied, /* satisfied */ - no_signal, /* signal */ - no_get_fd, /* get_fd */ -@@ -133,6 +138,9 @@ struct event *create_event( struct object *root, const struct unicode_str *name, - event->manual_reset = manual_reset; - event->signaled = initial_state; - -+ if (do_fsync()) -+ event->fsync_idx = fsync_alloc_shm( initial_state, 0 ); -+ - if (do_esync()) - event->esync_fd = esync_create_fd( initial_state, 0 ); - } -@@ -143,6 +151,10 @@ struct event *create_event( struct object *root, const struct unicode_str *name, - struct event *get_event_obj( struct process *process, obj_handle_t handle, unsigned int access ) - { - struct object *obj; -+ -+ if (do_fsync() && (obj = get_handle_obj( process, handle, access, &fsync_ops))) -+ return (struct event *)obj; /* even though it's not an event */ -+ - if (do_esync() && (obj = get_handle_obj( process, handle, access, &esync_ops))) - return (struct event *)obj; /* even though it's not an event */ - -@@ -155,10 +167,19 @@ void pulse_event( struct event *event ) - /* wake up all waiters if manual reset, a single one otherwise */ - wake_up( &event->obj, !event->manual_reset ); - event->signaled = 0; -+ -+ if (do_fsync()) -+ fsync_clear( &event->obj ); - } - - void set_event( struct event *event ) - { -+ if (do_fsync() && event->obj.ops == &fsync_ops) -+ { -+ fsync_set_event( (struct fsync *)event ); -+ return; -+ } -+ - if (do_esync() && event->obj.ops == &esync_ops) - { - esync_set_event( (struct esync *)event ); -@@ -172,6 +193,12 @@ void set_event( struct event *event ) - - void reset_event( struct event *event ) - { -+ if (do_fsync() && event->obj.ops == &fsync_ops) -+ { -+ fsync_reset_event( (struct fsync *)event ); -+ return; -+ } -+ - if (do_esync() && event->obj.ops == &esync_ops) - { - esync_reset_event( (struct esync *)event ); -@@ -179,6 +206,9 @@ void reset_event( struct event *event ) - } - event->signaled = 0; - -+ if (do_fsync()) -+ fsync_clear( &event->obj ); -+ - if (do_esync()) - esync_clear( event->esync_fd ); - } -@@ -212,6 +242,13 @@ static int event_get_esync_fd( struct object *obj, enum esync_type *type ) - return event->esync_fd; - } - -+static unsigned int event_get_fsync_idx( struct object *obj, enum fsync_type *type ) -+{ -+ struct event *event = (struct event *)obj; -+ *type = FSYNC_MANUAL_SERVER; -+ return event->fsync_idx; -+} -+ - static void event_satisfied( struct object *obj, struct wait_queue_entry *entry ) - { - struct event *event = (struct event *)obj; -diff --git a/server/fd.c b/server/fd.c -index ea6ece2a111..d592d763635 100644 ---- a/server/fd.c -+++ b/server/fd.c -@@ -103,6 +103,7 @@ - #include "process.h" - #include "request.h" - #include "esync.h" -+#include "fsync.h" - - #include "winternl.h" - #include "winioctl.h" -@@ -205,6 +206,7 @@ struct fd - apc_param_t comp_key; /* completion key to set in completion events */ - unsigned int comp_flags; /* completion flags */ - int esync_fd; /* esync file descriptor */ -+ unsigned int fsync_idx; /* fsync shm index */ - }; - - static void fd_dump( struct object *obj, int verbose ); -@@ -219,6 +221,7 @@ static const struct object_ops fd_ops = - NULL, /* remove_queue */ - NULL, /* signaled */ - NULL, /* get_esync_fd */ -+ NULL, /* get_fsync_idx */ - NULL, /* satisfied */ - no_signal, /* signal */ - no_get_fd, /* get_fd */ -@@ -261,6 +264,7 @@ static const struct object_ops device_ops = - NULL, /* remove_queue */ - NULL, /* signaled */ - NULL, /* get_esync_fd */ -+ NULL, /* get_fsync_idx */ - NULL, /* satisfied */ - no_signal, /* signal */ - no_get_fd, /* get_fd */ -@@ -302,6 +306,7 @@ static const struct object_ops inode_ops = - NULL, /* remove_queue */ - NULL, /* signaled */ - NULL, /* get_esync_fd */ -+ NULL, /* get_fsync_idx */ - NULL, /* satisfied */ - no_signal, /* signal */ - no_get_fd, /* get_fd */ -@@ -345,6 +350,7 @@ static const struct object_ops file_lock_ops = - remove_queue, /* remove_queue */ - file_lock_signaled, /* signaled */ - NULL, /* get_esync_fd */ -+ NULL, /* get_fsync_idx */ - no_satisfied, /* satisfied */ - no_signal, /* signal */ - no_get_fd, /* get_fd */ -@@ -1714,6 +1720,7 @@ static struct fd *alloc_fd_object(void) - fd->completion = NULL; - fd->comp_flags = 0; - fd->esync_fd = -1; -+ fd->fsync_idx = 0; - init_async_queue( &fd->read_q ); - init_async_queue( &fd->write_q ); - init_async_queue( &fd->wait_q ); -@@ -1723,6 +1730,9 @@ static struct fd *alloc_fd_object(void) - if (do_esync()) - fd->esync_fd = esync_create_fd( 1, 0 ); - -+ if (do_fsync()) -+ fd->fsync_idx = fsync_alloc_shm( 1, 0 ); -+ - if ((fd->poll_index = add_poll_user( fd )) == -1) - { - release_object( fd ); -@@ -1756,14 +1766,19 @@ struct fd *alloc_pseudo_fd( const struct fd_ops *fd_user_ops, struct object *use - fd->comp_flags = 0; - fd->no_fd_status = STATUS_BAD_DEVICE_TYPE; - fd->esync_fd = -1; -+ fd->fsync_idx = 0; - init_async_queue( &fd->read_q ); - init_async_queue( &fd->write_q ); - init_async_queue( &fd->wait_q ); - list_init( &fd->inode_entry ); - list_init( &fd->locks ); - -+ if (do_fsync()) -+ fd->fsync_idx = fsync_alloc_shm( 0, 0 ); -+ - if (do_esync()) - fd->esync_fd = esync_create_fd( 0, 0 ); -+ - return fd; - } - -@@ -2163,6 +2178,9 @@ void set_fd_signaled( struct fd *fd, int signaled ) - fd->signaled = signaled; - if (signaled) wake_up( fd->user, 0 ); - -+ if (do_fsync() && !signaled) -+ fsync_clear( fd->user ); -+ - if (do_esync() && !signaled) - esync_clear( fd->esync_fd ); - } -@@ -2205,5 +2223,14 @@ int default_fd_get_esync_fd( struct object *obj, enum esync_type *type ) - return ret; - } - -+unsigned int default_fd_get_fsync_idx( struct object *obj, enum fsync_type *type ) -+{ -+ struct fd *fd = get_obj_fd( obj ); -+ unsigned int ret = fd->fsync_idx; -+ *type = FSYNC_MANUAL_SERVER; -+ release_object( fd ); -+ return ret; -+} -+ - int default_fd_get_poll_events( struct fd *fd ) - { -diff --git a/server/file.c b/server/file.c -index 2c0d24da21d..b12defb844c 100644 ---- a/server/file.c -+++ b/server/file.c -@@ -86,6 +86,7 @@ static const struct object_ops file_ops = - remove_queue, /* remove_queue */ - default_fd_signaled, /* signaled */ - NULL, /* get_esync_fd */ -+ NULL, /* get_fsync_idx */ - no_satisfied, /* satisfied */ - no_signal, /* signal */ - file_get_fd, /* get_fd */ -diff --git a/server/file.h b/server/file.h -index 8accc8cc7f1..56fbf1f7a46 100644 ---- a/server/file.h -+++ b/server/file.h -@@ -103,5 +103,6 @@ extern char *dup_fd_name( struct fd *root, const char *name ); - - extern int default_fd_signaled( struct object *obj, struct wait_queue_entry *entry ); - extern int default_fd_get_esync_fd( struct object *obj, enum esync_type *type ); -+extern unsigned int default_fd_get_fsync_idx( struct object *obj, enum fsync_type *type ); - extern int default_fd_get_poll_events( struct fd *fd ); - extern void default_poll_event( struct fd *fd, int event ); -diff --git a/server/fsync.c b/server/fsync.c -new file mode 100644 -index 00000000000..0a88ecdd3f6 ---- /dev/null -+++ b/server/fsync.c -@@ -0,0 +1,527 @@ -+/* -+ * futex-based synchronization objects -+ * -+ * Copyright (C) 2018 Zebediah Figura -+ * -+ * This library is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU Lesser General Public -+ * License as published by the Free Software Foundation; either -+ * version 2.1 of the License, or (at your option) any later version. -+ * -+ * This library is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -+ * Lesser General Public License for more details. -+ * -+ * You should have received a copy of the GNU Lesser General Public -+ * License along with this library; if not, write to the Free Software -+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA -+ */ -+ -+#include "config.h" -+#include "wine/port.h" -+ -+#include -+#include -+#include -+#include -+#include -+#ifdef HAVE_SYS_MMAN_H -+# include -+#endif -+#ifdef HAVE_SYS_STAT_H -+# include -+#endif -+#ifdef HAVE_SYS_SYSCALL_H -+# include -+#endif -+#include -+ -+#include "ntstatus.h" -+#define WIN32_NO_STATUS -+#include "windef.h" -+#include "winternl.h" -+ -+#include "handle.h" -+#include "request.h" -+#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 ); -+} -+ -+int do_fsync(void) -+{ -+#ifdef __linux__ -+ static int do_fsync_cached = -1; -+ -+ if (do_fsync_cached == -1) -+ { -+ static const struct timespec zero; -+ futex_wait_multiple( NULL, 0, &zero ); -+ do_fsync_cached = getenv("WINEFSYNC") && atoi(getenv("WINEFSYNC")) && errno != ENOSYS; -+ } -+ -+ return do_fsync_cached; -+#else -+ return 0; -+#endif -+} -+ -+static char shm_name[29]; -+static int shm_fd; -+static off_t shm_size; -+static void **shm_addrs; -+static int shm_addrs_size; /* length of the allocated shm_addrs array */ -+static long pagesize; -+ -+static int is_fsync_initialized; -+ -+static void shm_cleanup(void) -+{ -+ close( shm_fd ); -+ if (shm_unlink( shm_name ) == -1) -+ perror( "shm_unlink" ); -+} -+ -+void fsync_init(void) -+{ -+ struct stat st; -+ -+ if (fstat( config_dir_fd, &st ) == -1) -+ fatal_error( "cannot stat config dir\n" ); -+ -+ if (st.st_ino != (unsigned long)st.st_ino) -+ sprintf( shm_name, "/wine-%lx%08lx-fsync", (unsigned long)((unsigned long long)st.st_ino >> 32), (unsigned long)st.st_ino ); -+ else -+ sprintf( shm_name, "/wine-%lx-fsync", (unsigned long)st.st_ino ); -+ -+ if (!shm_unlink( shm_name )) -+ fprintf( stderr, "fsync: warning: a previous shm file %s was not properly removed\n", shm_name ); -+ -+ shm_fd = shm_open( shm_name, O_RDWR | O_CREAT | O_EXCL, 0644 ); -+ if (shm_fd == -1) -+ perror( "shm_open" ); -+ -+ pagesize = sysconf( _SC_PAGESIZE ); -+ -+ shm_addrs = calloc( 128, sizeof(shm_addrs[0]) ); -+ shm_addrs_size = 128; -+ -+ shm_size = pagesize; -+ if (ftruncate( shm_fd, shm_size ) == -1) -+ perror( "ftruncate" ); -+ -+ is_fsync_initialized = 1; -+ -+ fprintf( stderr, "fsync: up and running.\n" ); -+ -+ atexit( shm_cleanup ); -+} -+ -+static struct list mutex_list = LIST_INIT(mutex_list); -+ -+struct fsync -+{ -+ struct object obj; -+ unsigned int shm_idx; -+ enum fsync_type type; -+ struct list mutex_entry; -+}; -+ -+static void fsync_dump( struct object *obj, int verbose ); -+static unsigned int fsync_get_fsync_idx( struct object *obj, enum fsync_type *type ); -+static unsigned int fsync_map_access( struct object *obj, unsigned int access ); -+static void fsync_destroy( struct object *obj ); -+ -+const struct object_ops fsync_ops = -+{ -+ sizeof(struct fsync), /* size */ -+ &no_type, /* get_type */ -+ fsync_dump, /* dump */ -+ no_add_queue, /* add_queue */ -+ NULL, /* remove_queue */ -+ NULL, /* signaled */ -+ NULL, /* get_esync_fd */ -+ fsync_get_fsync_idx, /* get_fsync_idx */ -+ NULL, /* satisfied */ -+ no_signal, /* signal */ -+ no_get_fd, /* get_fd */ -+ fsync_map_access, /* map_access */ -+ default_get_sd, /* get_sd */ -+ default_set_sd, /* set_sd */ -+ no_get_full_name, /* get_full_name */ -+ no_lookup_name, /* lookup_name */ -+ directory_link_name, /* link_name */ -+ default_unlink_name, /* unlink_name */ -+ no_open_file, /* open_file */ -+ no_kernel_obj_list, /* get_kernel_obj_list */ -+ no_close_handle, /* close_handle */ -+ fsync_destroy /* destroy */ -+}; -+ -+static void fsync_dump( struct object *obj, int verbose ) -+{ -+ struct fsync *fsync = (struct fsync *)obj; -+ assert( obj->ops == &fsync_ops ); -+ fprintf( stderr, "fsync idx=%d\n", fsync->shm_idx ); -+} -+ -+static unsigned int fsync_get_fsync_idx( struct object *obj, enum fsync_type *type) -+{ -+ struct fsync *fsync = (struct fsync *)obj; -+ *type = fsync->type; -+ return fsync->shm_idx; -+} -+ -+static unsigned int fsync_map_access( struct object *obj, unsigned int access ) -+{ -+ /* Sync objects have the same flags. */ -+ if (access & GENERIC_READ) access |= STANDARD_RIGHTS_READ | EVENT_QUERY_STATE; -+ if (access & GENERIC_WRITE) access |= STANDARD_RIGHTS_WRITE | EVENT_MODIFY_STATE; -+ if (access & GENERIC_EXECUTE) access |= STANDARD_RIGHTS_EXECUTE | SYNCHRONIZE; -+ if (access & GENERIC_ALL) access |= STANDARD_RIGHTS_ALL | EVENT_QUERY_STATE | EVENT_MODIFY_STATE; -+ return access & ~(GENERIC_READ | GENERIC_WRITE | GENERIC_EXECUTE | GENERIC_ALL); -+} -+ -+static void fsync_destroy( struct object *obj ) -+{ -+ struct fsync *fsync = (struct fsync *)obj; -+ if (fsync->type == FSYNC_MUTEX) -+ list_remove( &fsync->mutex_entry ); -+} -+ -+static void *get_shm( unsigned int idx ) -+{ -+ int entry = (idx * 8) / pagesize; -+ int offset = (idx * 8) % pagesize; -+ -+ if (entry >= shm_addrs_size) -+ { -+ int new_size = max(shm_addrs_size * 2, entry + 1); -+ -+ if (!(shm_addrs = realloc( shm_addrs, new_size * sizeof(shm_addrs[0]) ))) -+ fprintf( stderr, "fsync: couldn't expand shm_addrs array to size %d\n", entry + 1 ); -+ -+ memset( shm_addrs + shm_addrs_size, 0, (new_size - shm_addrs_size) * sizeof(shm_addrs[0]) ); -+ -+ shm_addrs_size = new_size; -+ } -+ -+ if (!shm_addrs[entry]) -+ { -+ void *addr = mmap( NULL, pagesize, PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, entry * pagesize ); -+ if (addr == (void *)-1) -+ { -+ fprintf( stderr, "fsync: failed to map page %d (offset %#lx): ", entry, entry * pagesize ); -+ perror( "mmap" ); -+ } -+ -+ if (debug_level) -+ fprintf( stderr, "fsync: Mapping page %d at %p.\n", entry, addr ); -+ -+ if (__sync_val_compare_and_swap( &shm_addrs[entry], 0, addr )) -+ munmap( addr, pagesize ); /* someone beat us to it */ -+ } -+ -+ return (void *)((unsigned long)shm_addrs[entry] + offset); -+} -+ -+/* FIXME: This is rather inefficient... */ -+static unsigned int shm_idx_counter = 1; -+ -+unsigned int fsync_alloc_shm( int low, int high ) -+{ -+#ifdef __linux__ -+ int shm_idx; -+ int *shm; -+ -+ /* this is arguably a bit of a hack, but we need some way to prevent -+ * allocating shm for the master socket */ -+ if (!is_fsync_initialized) -+ return 0; -+ -+ shm_idx = shm_idx_counter++; -+ -+ while (shm_idx * 8 >= shm_size) -+ { -+ /* Better expand the shm section. */ -+ shm_size += pagesize; -+ if (ftruncate( shm_fd, shm_size ) == -1) -+ { -+ fprintf( stderr, "fsync: couldn't expand %s to size %jd: ", -+ shm_name, shm_size ); -+ perror( "ftruncate" ); -+ } -+ } -+ -+ shm = get_shm( shm_idx ); -+ assert(shm); -+ shm[0] = low; -+ shm[1] = high; -+ -+ return shm_idx; -+#else -+ return 0; -+#endif -+} -+ -+static int type_matches( enum fsync_type type1, enum fsync_type type2 ) -+{ -+ return (type1 == type2) || -+ ((type1 == FSYNC_AUTO_EVENT || type1 == FSYNC_MANUAL_EVENT) && -+ (type2 == FSYNC_AUTO_EVENT || type2 == FSYNC_MANUAL_EVENT)); -+} -+ -+struct fsync *create_fsync( struct object *root, const struct unicode_str *name, -+ unsigned int attr, int low, int high, enum fsync_type type, -+ const struct security_descriptor *sd ) -+{ -+#ifdef __linux__ -+ struct fsync *fsync; -+ -+ if ((fsync = create_named_object( root, &fsync_ops, name, attr, sd ))) -+ { -+ if (get_error() != STATUS_OBJECT_NAME_EXISTS) -+ { -+ /* initialize it if it didn't already exist */ -+ -+ /* Initialize the shared memory portion. We want to do this on the -+ * server side to avoid a potential though unlikely race whereby -+ * the same object is opened and used between the time it's created -+ * and the time its shared memory portion is initialized. */ -+ -+ fsync->shm_idx = fsync_alloc_shm( low, high ); -+ fsync->type = type; -+ if (type == FSYNC_MUTEX) -+ list_add_tail( &mutex_list, &fsync->mutex_entry ); -+ } -+ else -+ { -+ /* validate the type */ -+ if (!type_matches( type, fsync->type )) -+ { -+ release_object( &fsync->obj ); -+ set_error( STATUS_OBJECT_TYPE_MISMATCH ); -+ return NULL; -+ } -+ } -+ } -+ -+ return fsync; -+#else -+ set_error( STATUS_NOT_IMPLEMENTED ); -+ return NULL; -+#endif -+} -+ -+static inline int futex_wake( int *addr, int val ) -+{ -+ return syscall( __NR_futex, addr, 1, val, NULL, 0, 0 ); -+} -+ -+/* shm layout for events or event-like objects. */ -+struct fsync_event -+{ -+ int signaled; -+ int unused; -+}; -+ -+void fsync_wake_futex( unsigned int shm_idx ) -+{ -+ struct fsync_event *event; -+ -+ if (debug_level) -+ fprintf( stderr, "fsync_wake_futex: index %u\n", shm_idx ); -+ -+ if (!shm_idx) -+ return; -+ -+ event = get_shm( shm_idx ); -+ if (!__atomic_exchange_n( &event->signaled, 1, __ATOMIC_SEQ_CST )) -+ futex_wake( &event->signaled, INT_MAX ); -+} -+ -+void fsync_wake_up( struct object *obj ) -+{ -+ enum fsync_type type; -+ -+ if (debug_level) -+ fprintf( stderr, "fsync_wake_up: object %p\n", obj ); -+ -+ if (obj->ops->get_fsync_idx) -+ fsync_wake_futex( obj->ops->get_fsync_idx( obj, &type ) ); -+} -+ -+void fsync_clear_futex( unsigned int shm_idx ) -+{ -+ struct fsync_event *event; -+ -+ if (debug_level) -+ fprintf( stderr, "fsync_clear_futex: index %u\n", shm_idx ); -+ -+ if (!shm_idx) -+ return; -+ -+ event = get_shm( shm_idx ); -+ __atomic_store_n( &event->signaled, 0, __ATOMIC_SEQ_CST ); -+} -+ -+void fsync_clear( struct object *obj ) -+{ -+ enum fsync_type type; -+ -+ if (debug_level) -+ fprintf( stderr, "fsync_clear: object %p\n", obj ); -+ -+ if (obj->ops->get_fsync_idx) -+ fsync_clear_futex( obj->ops->get_fsync_idx( obj, &type ) ); -+} -+ -+void fsync_set_event( struct fsync *fsync ) -+{ -+ struct fsync_event *event = get_shm( fsync->shm_idx ); -+ assert( fsync->obj.ops == &fsync_ops ); -+ -+ if (!__atomic_exchange_n( &event->signaled, 1, __ATOMIC_SEQ_CST )) -+ futex_wake( &event->signaled, INT_MAX ); -+} -+ -+void fsync_reset_event( struct fsync *fsync ) -+{ -+ struct fsync_event *event = get_shm( fsync->shm_idx ); -+ assert( fsync->obj.ops == &fsync_ops ); -+ -+ __atomic_store_n( &event->signaled, 0, __ATOMIC_SEQ_CST ); -+} -+ -+struct mutex -+{ -+ int tid; -+ int count; /* recursion count */ -+}; -+ -+void fsync_abandon_mutexes( struct thread *thread ) -+{ -+ struct fsync *fsync; -+ -+ LIST_FOR_EACH_ENTRY( fsync, &mutex_list, struct fsync, mutex_entry ) -+ { -+ struct mutex *mutex = get_shm( fsync->shm_idx ); -+ -+ if (mutex->tid == thread->id) -+ { -+ if (debug_level) -+ fprintf( stderr, "fsync_abandon_mutexes() idx=%d\n", fsync->shm_idx ); -+ mutex->tid = ~0; -+ mutex->count = 0; -+ futex_wake( &mutex->tid, INT_MAX ); -+ } -+ } -+} -+ -+DECL_HANDLER(create_fsync) -+{ -+ struct fsync *fsync; -+ struct unicode_str name; -+ struct object *root; -+ const struct security_descriptor *sd; -+ const struct object_attributes *objattr = get_req_object_attributes( &sd, &name, &root ); -+ -+ if (!do_fsync()) -+ { -+ set_error( STATUS_NOT_IMPLEMENTED ); -+ return; -+ } -+ -+ if (!objattr) return; -+ -+ if ((fsync = create_fsync( root, &name, objattr->attributes, req->low, -+ req->high, req->type, sd ))) -+ { -+ if (get_error() == STATUS_OBJECT_NAME_EXISTS) -+ reply->handle = alloc_handle( current->process, fsync, req->access, objattr->attributes ); -+ else -+ reply->handle = alloc_handle_no_access_check( current->process, fsync, -+ req->access, objattr->attributes ); -+ -+ reply->shm_idx = fsync->shm_idx; -+ reply->type = fsync->type; -+ release_object( fsync ); -+ } -+ -+ if (root) release_object( root ); -+} -+ -+DECL_HANDLER(open_fsync) -+{ -+ struct unicode_str name = get_req_unicode_str(); -+ -+ reply->handle = open_object( current->process, req->rootdir, req->access, -+ &fsync_ops, &name, req->attributes ); -+ -+ if (reply->handle) -+ { -+ struct fsync *fsync; -+ -+ if (!(fsync = (struct fsync *)get_handle_obj( current->process, reply->handle, -+ 0, &fsync_ops ))) -+ return; -+ -+ if (!type_matches( req->type, fsync->type )) -+ { -+ set_error( STATUS_OBJECT_TYPE_MISMATCH ); -+ release_object( fsync ); -+ return; -+ } -+ -+ reply->type = fsync->type; -+ reply->shm_idx = fsync->shm_idx; -+ release_object( fsync ); -+ } -+} -+ -+/* Retrieve the index of a shm section which will be signaled by the server. */ -+DECL_HANDLER(get_fsync_idx) -+{ -+ struct object *obj; -+ enum fsync_type type; -+ -+ if (!(obj = get_handle_obj( current->process, req->handle, SYNCHRONIZE, NULL ))) -+ return; -+ -+ if (obj->ops->get_fsync_idx) -+ { -+ reply->shm_idx = obj->ops->get_fsync_idx( obj, &type ); -+ reply->type = type; -+ } -+ else -+ { -+ if (debug_level) -+ { -+ fprintf( stderr, "%04x: fsync: can't wait on object: ", current->id ); -+ obj->ops->dump( obj, 0 ); -+ } -+ set_error( STATUS_NOT_IMPLEMENTED ); -+ } -+ -+ release_object( obj ); -+} -+ -+DECL_HANDLER(get_fsync_apc_idx) -+{ -+ reply->shm_idx = current->fsync_apc_idx; -+} -diff --git a/server/fsync.h b/server/fsync.h -new file mode 100644 -index 00000000000..a91939b7f0a ---- /dev/null -+++ b/server/fsync.h -@@ -0,0 +1,34 @@ -+/* -+ * futex-based synchronization objects -+ * -+ * Copyright (C) 2018 Zebediah Figura -+ * -+ * This library is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU Lesser General Public -+ * License as published by the Free Software Foundation; either -+ * version 2.1 of the License, or (at your option) any later version. -+ * -+ * This library is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -+ * Lesser General Public License for more details. -+ * -+ * You should have received a copy of the GNU Lesser General Public -+ * License along with this library; if not, write to the Free Software -+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA -+ */ -+ -+extern int do_fsync(void); -+extern void fsync_init(void); -+extern unsigned int fsync_alloc_shm( int low, int high ); -+extern void fsync_wake_futex( unsigned int shm_idx ); -+extern void fsync_clear_futex( unsigned int shm_idx ); -+extern void fsync_wake_up( struct object *obj ); -+extern void fsync_clear( struct object *obj ); -+ -+struct fsync; -+ -+extern const struct object_ops fsync_ops; -+extern void fsync_set_event( struct fsync *fsync ); -+extern void fsync_reset_event( struct fsync *fsync ); -+extern void fsync_abandon_mutexes( struct thread *thread ); -diff --git a/server/handle.c b/server/handle.c -index 4d7e4231a07..60e2b2f7d05 100644 ---- a/server/handle.c -+++ b/server/handle.c -@@ -124,6 +124,7 @@ static const struct object_ops handle_table_ops = - NULL, /* remove_queue */ - NULL, /* signaled */ - NULL, /* get_esync_fd */ -+ NULL, /* get_fsync_idx */ - NULL, /* satisfied */ - no_signal, /* signal */ - no_get_fd, /* get_fd */ -diff --git a/server/hook.c b/server/hook.c -index 61b5014c442..379f9c074d5 100644 ---- a/server/hook.c -+++ b/server/hook.c -@@ -82,6 +82,7 @@ static const struct object_ops hook_table_ops = - NULL, /* remove_queue */ - NULL, /* signaled */ - NULL, /* get_esync_fd */ -+ NULL, /* get_fsync_idx */ - NULL, /* satisfied */ - no_signal, /* signal */ - no_get_fd, /* get_fd */ -diff --git a/server/mailslot.c b/server/mailslot.c -index 378c0bbb274..f3e465530a3 100644 ---- a/server/mailslot.c -+++ b/server/mailslot.c -@@ -79,6 +79,7 @@ static const struct object_ops mailslot_ops = - remove_queue, /* remove_queue */ - default_fd_signaled, /* signaled */ - NULL, /* get_esync_fd */ -+ NULL, /* get_fsync_idx */ - no_satisfied, /* satisfied */ - no_signal, /* signal */ - mailslot_get_fd, /* get_fd */ -@@ -138,6 +139,7 @@ static const struct object_ops mail_writer_ops = - NULL, /* remove_queue */ - NULL, /* signaled */ - NULL, /* get_esync_fd */ -+ NULL, /* get_fsync_idx */ - NULL, /* satisfied */ - no_signal, /* signal */ - mail_writer_get_fd, /* get_fd */ -@@ -202,6 +204,7 @@ static const struct object_ops mailslot_device_ops = - NULL, /* remove_queue */ - NULL, /* signaled */ - NULL, /* get_esync_fd */ -+ NULL, /* get_fsync_idx */ - no_satisfied, /* satisfied */ - no_signal, /* signal */ - no_get_fd, /* get_fd */ -@@ -233,6 +236,7 @@ static const struct object_ops mailslot_device_file_ops = - remove_queue, /* remove_queue */ - default_fd_signaled, /* signaled */ - NULL, /* get_esync_fd */ -+ NULL, /* get_fsync_idx */ - no_satisfied, /* satisfied */ - no_signal, /* signal */ - mailslot_device_file_get_fd, /* get_fd */ -diff --git a/server/main.c b/server/main.c -index 385ae5ee370..9582f5707aa 100644 ---- a/server/main.c -+++ b/server/main.c -@@ -37,7 +37,8 @@ - #include "thread.h" - #include "request.h" - #include "unicode.h" - #include "esync.h" -+#include "fsync.h" - - /* command-line options */ - int debug_level = 0; -@@ -141,9 +142,15 @@ int main( int argc, char *argv[] ) - sock_init(); - open_master_socket(); - -+ if (do_fsync()) -+ fsync_init(); -+ - if (do_esync()) - esync_init(); - -+ if (!do_fsync() && !do_esync()) -+ fprintf( stderr, "wineserver: using server-side synchronization.\n" ); -+ - if (debug_level) fprintf( stderr, "wineserver: starting (pid=%ld)\n", (long) getpid() ); - set_current_time(); - init_scheduler(); -diff --git a/server/mapping.c b/server/mapping.c -index 37da37d6ab3..1d0e1ec2194 100644 ---- a/server/mapping.c -+++ b/server/mapping.c -@@ -69,6 +69,7 @@ static const struct object_ops ranges_ops = - NULL, /* remove_queue */ - NULL, /* signaled */ - NULL, /* get_esync_fd */ -+ NULL, /* get_fsync_idx */ - NULL, /* satisfied */ - no_signal, /* signal */ - no_get_fd, /* get_fd */ -@@ -106,6 +107,7 @@ static const struct object_ops shared_map_ops = - NULL, /* remove_queue */ - NULL, /* signaled */ - NULL, /* get_esync_fd */ -+ NULL, /* get_fsync_idx */ - NULL, /* satisfied */ - no_signal, /* signal */ - no_get_fd, /* get_fd */ -@@ -165,6 +167,7 @@ static const struct object_ops mapping_ops = - NULL, /* remove_queue */ - NULL, /* signaled */ - NULL, /* get_esync_fd */ -+ NULL, /* get_fsync_idx */ - NULL, /* satisfied */ - no_signal, /* signal */ - mapping_get_fd, /* get_fd */ -diff --git a/server/mutex.c b/server/mutex.c -index fc236b3e623..f3e1b006924 100644 ---- a/server/mutex.c -+++ b/server/mutex.c -@@ -62,6 +62,7 @@ static const struct object_ops mutex_ops = - remove_queue, /* remove_queue */ - mutex_signaled, /* signaled */ - NULL, /* get_esync_fd */ -+ NULL, /* get_fsync_idx */ - mutex_satisfied, /* satisfied */ - mutex_signal, /* signal */ - no_get_fd, /* get_fd */ -diff --git a/server/named_pipe.c b/server/named_pipe.c -index b438682d214..4e30e789f2b 100644 ---- a/server/named_pipe.c -+++ b/server/named_pipe.c -@@ -119,6 +119,7 @@ static const struct object_ops named_pipe_ops = - NULL, /* remove_queue */ - NULL, /* signaled */ - NULL, /* get_esync_fd */ -+ NULL, /* get_fsync_idx */ - NULL, /* satisfied */ - no_signal, /* signal */ - no_get_fd, /* get_fd */ -@@ -164,6 +165,7 @@ static const struct object_ops pipe_server_ops = - remove_queue, /* remove_queue */ - default_fd_signaled, /* signaled */ - default_fd_get_esync_fd, /* get_esync_fd */ -+ default_fd_get_fsync_idx, /* get_fsync_idx */ - no_satisfied, /* satisfied */ - no_signal, /* signal */ - pipe_end_get_fd, /* get_fd */ -@@ -208,6 +210,7 @@ static const struct object_ops pipe_client_ops = - remove_queue, /* remove_queue */ - default_fd_signaled, /* signaled */ - default_fd_get_esync_fd, /* get_esync_fd */ -+ default_fd_get_fsync_idx, /* get_fsync_idx */ - no_satisfied, /* satisfied */ - no_signal, /* signal */ - pipe_end_get_fd, /* get_fd */ -@@ -256,6 +259,7 @@ static const struct object_ops named_pipe_device_ops = - NULL, /* remove_queue */ - NULL, /* signaled */ - NULL, /* get_esync_fd */ -+ NULL, /* get_fsync_idx */ - no_satisfied, /* satisfied */ - no_signal, /* signal */ - no_get_fd, /* get_fd */ -@@ -288,6 +292,7 @@ static const struct object_ops named_pipe_device_file_ops = - remove_queue, /* remove_queue */ - default_fd_signaled, /* signaled */ - NULL, /* get_esync_fd */ -+ NULL, /* get_fsync_idx */ - no_satisfied, /* satisfied */ - no_signal, /* signal */ - named_pipe_device_file_get_fd, /* get_fd */ -diff --git a/server/object.h b/server/object.h -index 10a049c8fb8..12ca073ff8b 100644 ---- a/server/object.h -+++ b/server/object.h -@@ -70,6 +70,8 @@ struct object_ops - int (*signaled)(struct object *,struct wait_queue_entry *); - /* return the esync fd for this object */ - int (*get_esync_fd)(struct object *, enum esync_type *type); -+ /* return the fsync shm idx for this object */ -+ unsigned int (*get_fsync_idx)(struct object *, enum fsync_type *type); - /* wait satisfied */ - void (*satisfied)(struct object *,struct wait_queue_entry *); - /* signal an object */ -diff --git a/server/process.c b/server/process.c -index 10ae5712438..6aa2a2b74c1 100644 ---- a/server/process.c -+++ b/server/process.c -@@ -50,5 +50,6 @@ - #include "user.h" - #include "security.h" - #include "esync.h" -+#include "fsync.h" - - /* process object */ -@@ -70,6 +71,7 @@ static void process_poll_event( struct fd *fd, int event ); - static struct list *process_get_kernel_obj_list( struct object *obj ); - static void process_destroy( struct object *obj ); - static int process_get_esync_fd( struct object *obj, enum esync_type *type ); -+static unsigned int process_get_fsync_idx( struct object *obj, enum fsync_type *type ); - static void terminate_process( struct process *process, struct thread *skip, int exit_code ); - - static const struct object_ops process_ops = -@@ -81,6 +83,7 @@ static const struct object_ops process_ops = - remove_queue, /* remove_queue */ - process_signaled, /* signaled */ - process_get_esync_fd, /* get_esync_fd */ -+ process_get_fsync_idx, /* get_fsync_idx */ - no_satisfied, /* satisfied */ - no_signal, /* signal */ - no_get_fd, /* get_fd */ -@@ -133,6 +136,7 @@ static const struct object_ops startup_info_ops = - remove_queue, /* remove_queue */ - startup_info_signaled, /* signaled */ - NULL, /* get_esync_fd */ -+ NULL, /* get_fsync_idx */ - no_satisfied, /* satisfied */ - no_signal, /* signal */ - no_get_fd, /* get_fd */ -@@ -180,6 +184,7 @@ static const struct object_ops job_ops = - remove_queue, /* remove_queue */ - job_signaled, /* signaled */ - NULL, /* get_esync_fd */ -+ NULL, /* get_fsync_idx */ - no_satisfied, /* satisfied */ - no_signal, /* signal */ - no_get_fd, /* get_fd */ -@@ -548,6 +553,7 @@ struct process *create_process( int fd, struct process *parent, int inherit_all, - process->rawinput_mouse = NULL; - process->rawinput_kbd = NULL; - process->esync_fd = -1; -+ process->fsync_idx = 0; - list_init( &process->kernel_object ); - list_init( &process->thread_list ); - list_init( &process->locks ); -@@ -604,6 +610,9 @@ struct process *create_process( int fd, struct process *parent, int inherit_all, - if (!token_assign_label( process->token, security_high_label_sid )) - goto error; - -+ if (do_fsync()) -+ process->fsync_idx = fsync_alloc_shm( 0, 0 ); -+ - if (do_esync()) - process->esync_fd = esync_create_fd( 0, 0 ); - -@@ -687,5 +696,12 @@ static int process_get_esync_fd( struct object *obj, enum esync_type *type ) - return process->esync_fd; - } - -+static unsigned int process_get_fsync_idx( struct object *obj, enum fsync_type *type ) -+{ -+ struct process *process = (struct process *)obj; -+ *type = FSYNC_MANUAL_SERVER; -+ return process->fsync_idx; -+} -+ - static unsigned int process_map_access( struct object *obj, unsigned int access ) - { -diff --git a/server/process.h b/server/process.h -index dd56f88a91e..8bf729e5df5 100644 ---- a/server/process.h -+++ b/server/process.h -@@ -99,6 +99,7 @@ struct process - const struct rawinput_device *rawinput_kbd; /* rawinput keyboard device, if any */ - struct list kernel_object; /* list of kernel object pointers */ - int esync_fd; /* esync file descriptor (signaled on exit) */ -+ unsigned int fsync_idx; - }; - - #define CPU_FLAG(cpu) (1 << (cpu)) -diff --git a/server/protocol.def b/server/protocol.def -index 80098c23033..3cf4d5ae8b3 100644 ---- a/server/protocol.def -+++ b/server/protocol.def -@@ -3710,3 +3710,57 @@ enum esync_type - /* Retrieve the fd to wait on for user APCs. */ - @REQ(get_esync_apc_fd) - @END -+ -+enum fsync_type -+{ -+ FSYNC_SEMAPHORE = 1, -+ FSYNC_AUTO_EVENT, -+ FSYNC_MANUAL_EVENT, -+ FSYNC_MUTEX, -+ FSYNC_AUTO_SERVER, -+ FSYNC_MANUAL_SERVER, -+ FSYNC_QUEUE, -+}; -+ -+/* Create a new futex-based synchronization object */ -+@REQ(create_fsync) -+ unsigned int access; /* wanted access rights */ -+ int low; /* initial value of low word */ -+ int high; /* initial value of high word */ -+ int type; /* type of fsync object */ -+ VARARG(objattr,object_attributes); /* object attributes */ -+@REPLY -+ obj_handle_t handle; /* handle to the object */ -+ int type; /* type of fsync object */ -+ unsigned int shm_idx; /* this object's index into the shm section */ -+@END -+ -+/* Open an fsync object */ -+@REQ(open_fsync) -+ unsigned int access; /* wanted access rights */ -+ unsigned int attributes; /* object attributes */ -+ obj_handle_t rootdir; /* root directory */ -+ int type; /* type of fsync object */ -+ VARARG(name,unicode_str); /* object name */ -+@REPLY -+ obj_handle_t handle; /* handle to the event */ -+ int type; /* type of fsync object */ -+ unsigned int shm_idx; /* this object's index into the shm section */ -+@END -+ -+/* Retrieve the shm index for an object. */ -+@REQ(get_fsync_idx) -+ obj_handle_t handle; /* handle to the object */ -+@REPLY -+ int type; -+ unsigned int shm_idx; -+@END -+ -+@REQ(fsync_msgwait) -+ int in_msgwait; /* are we in a message wait? */ -+@END -+ -+@REQ(get_fsync_apc_idx) -+@REPLY -+ unsigned int shm_idx; -+@END -diff --git a/server/queue.c b/server/queue.c -index efaf8a0f7e7..6007e67b03d 100644 ---- a/server/queue.c -+++ b/server/queue.c -@@ -44,6 +44,7 @@ - #include "request.h" - #include "user.h" - #include "esync.h" -+#include "fsync.h" - - #define WM_NCMOUSEFIRST WM_NCMOUSEMOVE - #define WM_NCMOUSELAST (WM_NCMOUSEFIRST+(WM_MOUSELAST-WM_MOUSEFIRST)) -@@ -144,6 +145,8 @@ struct msg_queue - unsigned int ignore_post_msg; /* ignore post messages newer than this unique id */ - int esync_fd; /* esync file descriptor (signalled on message) */ - int esync_in_msgwait; /* our thread is currently waiting on us */ -+ unsigned int fsync_idx; -+ int fsync_in_msgwait; /* our thread is currently waiting on us */ - }; - - struct hotkey -@@ -161,6 +164,7 @@ static int msg_queue_add_queue( struct object *obj, struct wait_queue_entry *ent - static void msg_queue_remove_queue( struct object *obj, struct wait_queue_entry *entry ); - static int msg_queue_signaled( struct object *obj, struct wait_queue_entry *entry ); - static int msg_queue_get_esync_fd( struct object *obj, enum esync_type *type ); -+static unsigned int msg_queue_get_fsync_idx( struct object *obj, enum fsync_type *type ); - static void msg_queue_satisfied( struct object *obj, struct wait_queue_entry *entry ); - static void msg_queue_destroy( struct object *obj ); - static void msg_queue_poll_event( struct fd *fd, int event ); -@@ -177,6 +181,7 @@ static const struct object_ops msg_queue_ops = - msg_queue_remove_queue, /* remove_queue */ - msg_queue_signaled, /* signaled */ - msg_queue_get_esync_fd, /* get_esync_fd */ -+ msg_queue_get_fsync_idx, /* get_fsync_idx */ - msg_queue_satisfied, /* satisfied */ - no_signal, /* signal */ - no_get_fd, /* get_fd */ -@@ -215,6 +220,7 @@ static const struct object_ops thread_input_ops = - NULL, /* remove_queue */ - NULL, /* signaled */ - NULL, /* get_esync_fd */ -+ NULL, /* get_fsync_idx */ - NULL, /* satisfied */ - no_signal, /* signal */ - no_get_fd, /* get_fd */ -@@ -315,12 +321,17 @@ static struct msg_queue *create_msg_queue( struct thread *thread, struct thread_ - queue->ignore_post_msg = 0; - queue->esync_fd = -1; - queue->esync_in_msgwait = 0; -+ queue->fsync_idx = 0; -+ queue->fsync_in_msgwait = 0; - list_init( &queue->send_result ); - list_init( &queue->callback_result ); - list_init( &queue->pending_timers ); - list_init( &queue->expired_timers ); - for (i = 0; i < NB_MSG_KINDS; i++) list_init( &queue->msg_list[i] ); - -+ if (do_fsync()) -+ queue->fsync_idx = fsync_alloc_shm( 0, 0 ); -+ - if (do_esync()) - queue->esync_fd = esync_create_fd( 0, 0 ); - -@@ -501,6 +512,9 @@ static inline void clear_queue_bits( struct msg_queue *queue, unsigned int bits - queue->wake_bits &= ~bits; - queue->changed_bits &= ~bits; - -+ if (do_fsync() && !is_signaled( queue )) -+ fsync_clear( &queue->obj ); -+ - if (do_esync() && !is_signaled( queue )) - esync_clear( queue->esync_fd ); - } -@@ -962,6 +976,9 @@ static int is_queue_hung( struct msg_queue *queue ) - return 0; /* thread is waiting on queue -> not hung */ - } - -+ if (do_fsync() && queue->fsync_in_msgwait) -+ return 0; /* thread is waiting on queue in absentia -> not hung */ -+ - if (do_esync() && queue->esync_in_msgwait) - return 0; /* thread is waiting on queue in absentia -> not hung */ - -@@ -1027,6 +1044,13 @@ static int msg_queue_get_esync_fd( struct object *obj, enum esync_type *type ) - return queue->esync_fd; - } - -+static unsigned int msg_queue_get_fsync_idx( struct object *obj, enum fsync_type *type ) -+{ -+ struct msg_queue *queue = (struct msg_queue *)obj; -+ *type = FSYNC_QUEUE; -+ return queue->fsync_idx; -+} -+ - static void msg_queue_satisfied( struct object *obj, struct wait_queue_entry *entry ) - { - struct msg_queue *queue = (struct msg_queue *)obj; -@@ -2405,6 +2429,9 @@ DECL_HANDLER(get_queue_status) - reply->changed_bits = queue->changed_bits; - queue->changed_bits &= ~req->clear_bits; - -+ if (do_fsync() && !is_signaled( queue )) -+ fsync_clear( &queue->obj ); -+ - if (do_esync() && !is_signaled( queue )) - esync_clear( queue->esync_fd ); - } -@@ -3406,3 +3433,18 @@ DECL_HANDLER(esync_msgwait) - if (queue->fd) - set_fd_events( queue->fd, req->in_msgwait ? POLLIN : 0 ); - } -+ -+DECL_HANDLER(fsync_msgwait) -+{ -+ struct msg_queue *queue = get_current_queue(); -+ -+ if (!queue) return; -+ queue->fsync_in_msgwait = req->in_msgwait; -+ -+ if (current->process->idle_event && !(queue->wake_mask & QS_SMRESULT)) -+ set_event( current->process->idle_event ); -+ -+ /* and start/stop waiting on the driver */ -+ if (queue->fd) -+ set_fd_events( queue->fd, req->in_msgwait ? POLLIN : 0 ); -+} -diff --git a/server/registry.c b/server/registry.c -index 8110bbb340c..deed81b470f 100644 ---- a/server/registry.c -+++ b/server/registry.c -@@ -161,6 +161,7 @@ static const struct object_ops key_ops = - NULL, /* remove_queue */ - NULL, /* signaled */ - NULL, /* get_esync_fd */ -+ NULL, /* get_fsync_idx */ - NULL, /* satisfied */ - no_signal, /* signal */ - no_get_fd, /* get_fd */ -diff --git a/server/request.c b/server/request.c -index 20b0ec309f3..9fd08139375 100644 ---- a/server/request.c -+++ b/server/request.c -@@ -97,6 +97,7 @@ static const struct object_ops master_socket_ops = - NULL, /* remove_queue */ - NULL, /* signaled */ - NULL, /* get_esync_fd */ -+ NULL, /* get_fsync_idx */ - NULL, /* satisfied */ - no_signal, /* signal */ - no_get_fd, /* get_fd */ -diff --git a/server/request.h b/server/request.h -index 49cf56342be..7f74b1bed83 100644 ---- a/server/request.h -+++ b/server/request.h -@@ -398,6 +398,11 @@ DECL_HANDLER(open_esync); - DECL_HANDLER(get_esync_fd); - DECL_HANDLER(esync_msgwait); - DECL_HANDLER(get_esync_apc_fd); -+DECL_HANDLER(create_fsync); -+DECL_HANDLER(open_fsync); -+DECL_HANDLER(get_fsync_idx); -+DECL_HANDLER(fsync_msgwait); -+DECL_HANDLER(get_fsync_apc_idx); - - #ifdef WANT_REQUEST_HANDLERS - -@@ -683,6 +688,11 @@ static const req_handler req_handlers[REQ_NB_REQUESTS] = - (req_handler)req_get_esync_fd, - (req_handler)req_esync_msgwait, - (req_handler)req_get_esync_apc_fd, -+ (req_handler)req_create_fsync, -+ (req_handler)req_open_fsync, -+ (req_handler)req_get_fsync_idx, -+ (req_handler)req_fsync_msgwait, -+ (req_handler)req_get_fsync_apc_idx, - }; - - C_ASSERT( sizeof(abstime_t) == 8 ); -@@ -2271,6 +2281,34 @@ C_ASSERT( sizeof(struct get_esync_fd_reply) == 16 ); - C_ASSERT( FIELD_OFFSET(struct esync_msgwait_request, in_msgwait) == 12 ); - C_ASSERT( sizeof(struct esync_msgwait_request) == 16 ); - C_ASSERT( sizeof(struct get_esync_apc_fd_request) == 16 ); -+C_ASSERT( FIELD_OFFSET(struct create_fsync_request, access) == 12 ); -+C_ASSERT( FIELD_OFFSET(struct create_fsync_request, low) == 16 ); -+C_ASSERT( FIELD_OFFSET(struct create_fsync_request, high) == 20 ); -+C_ASSERT( FIELD_OFFSET(struct create_fsync_request, type) == 24 ); -+C_ASSERT( sizeof(struct create_fsync_request) == 32 ); -+C_ASSERT( FIELD_OFFSET(struct create_fsync_reply, handle) == 8 ); -+C_ASSERT( FIELD_OFFSET(struct create_fsync_reply, type) == 12 ); -+C_ASSERT( FIELD_OFFSET(struct create_fsync_reply, shm_idx) == 16 ); -+C_ASSERT( sizeof(struct create_fsync_reply) == 24 ); -+C_ASSERT( FIELD_OFFSET(struct open_fsync_request, access) == 12 ); -+C_ASSERT( FIELD_OFFSET(struct open_fsync_request, attributes) == 16 ); -+C_ASSERT( FIELD_OFFSET(struct open_fsync_request, rootdir) == 20 ); -+C_ASSERT( FIELD_OFFSET(struct open_fsync_request, type) == 24 ); -+C_ASSERT( sizeof(struct open_fsync_request) == 32 ); -+C_ASSERT( FIELD_OFFSET(struct open_fsync_reply, handle) == 8 ); -+C_ASSERT( FIELD_OFFSET(struct open_fsync_reply, type) == 12 ); -+C_ASSERT( FIELD_OFFSET(struct open_fsync_reply, shm_idx) == 16 ); -+C_ASSERT( sizeof(struct open_fsync_reply) == 24 ); -+C_ASSERT( FIELD_OFFSET(struct get_fsync_idx_request, handle) == 12 ); -+C_ASSERT( sizeof(struct get_fsync_idx_request) == 16 ); -+C_ASSERT( FIELD_OFFSET(struct get_fsync_idx_reply, type) == 8 ); -+C_ASSERT( FIELD_OFFSET(struct get_fsync_idx_reply, shm_idx) == 12 ); -+C_ASSERT( sizeof(struct get_fsync_idx_reply) == 16 ); -+C_ASSERT( FIELD_OFFSET(struct fsync_msgwait_request, in_msgwait) == 12 ); -+C_ASSERT( sizeof(struct fsync_msgwait_request) == 16 ); -+C_ASSERT( sizeof(struct get_fsync_apc_idx_request) == 16 ); -+C_ASSERT( FIELD_OFFSET(struct get_fsync_apc_idx_reply, shm_idx) == 8 ); -+C_ASSERT( sizeof(struct get_fsync_apc_idx_reply) == 16 ); - - #endif /* WANT_REQUEST_HANDLERS */ - -diff --git a/server/semaphore.c b/server/semaphore.c -index ff94e42576b..005b7ec2ffc 100644 ---- a/server/semaphore.c -+++ b/server/semaphore.c -@@ -59,6 +59,7 @@ static const struct object_ops semaphore_ops = - remove_queue, /* remove_queue */ - semaphore_signaled, /* signaled */ - NULL, /* get_esync_fd */ -+ NULL, /* get_fsync_idx */ - semaphore_satisfied, /* satisfied */ - semaphore_signal, /* signal */ - no_get_fd, /* get_fd */ -diff --git a/server/serial.c b/server/serial.c -index a50ace9903f..78d33460892 100644 ---- a/server/serial.c -+++ b/server/serial.c -@@ -93,6 +93,7 @@ static const struct object_ops serial_ops = - remove_queue, /* remove_queue */ - default_fd_signaled, /* signaled */ - NULL, /* get_esync_fd */ -+ NULL, /* get_fsync_idx */ - no_satisfied, /* satisfied */ - no_signal, /* signal */ - serial_get_fd, /* get_fd */ -diff --git a/server/signal.c b/server/signal.c -index b6d6dcfc4b6..f5ac61b6975 100644 ---- a/server/signal.c -+++ b/server/signal.c -@@ -68,6 +68,7 @@ static const struct object_ops handler_ops = - NULL, /* remove_queue */ - NULL, /* signaled */ - NULL, /* get_esync_fd */ -+ NULL, /* get_fsync_idx */ - NULL, /* satisfied */ - no_signal, /* signal */ - no_get_fd, /* get_fd */ -diff --git a/server/sock.c b/server/sock.c -index 6b771911f26..01c8ebc404b 100644 ---- a/server/sock.c -+++ b/server/sock.c -@@ -189,6 +189,7 @@ static const struct object_ops sock_ops = - remove_queue, /* remove_queue */ - sock_signaled, /* signaled */ - NULL, /* get_esync_fd */ -+ NULL, /* get_fsync_idx */ - no_satisfied, /* satisfied */ - no_signal, /* signal */ - sock_get_fd, /* get_fd */ -@@ -1529,6 +1530,7 @@ static const struct object_ops ifchange_ops = - NULL, /* remove_queue */ - NULL, /* signaled */ - NULL, /* get_esync_fd */ -+ NULL, /* get_fsync_idx */ - no_satisfied, /* satisfied */ - no_signal, /* signal */ - ifchange_get_fd, /* get_fd */ -@@ -1750,6 +1752,7 @@ static const struct object_ops socket_device_ops = - NULL, /* remove_queue */ - NULL, /* signaled */ - NULL, /* get_esync_fd */ -+ NULL, /* get_fsync_idx */ - no_satisfied, /* satisfied */ - no_signal, /* signal */ - no_get_fd, /* get_fd */ -diff --git a/server/symlink.c b/server/symlink.c -index 1f5ee0bf72b..2b49d379c2c 100644 ---- a/server/symlink.c -+++ b/server/symlink.c -@@ -61,6 +61,7 @@ static const struct object_ops symlink_ops = - NULL, /* remove_queue */ - NULL, /* signaled */ - NULL, /* get_esync_fd */ -+ NULL, /* get_fsync_idx */ - NULL, /* satisfied */ - no_signal, /* signal */ - no_get_fd, /* get_fd */ -diff --git a/server/thread.c b/server/thread.c -index f320f2b26e9..00acd2098b6 100644 ---- a/server/thread.c -+++ b/server/thread.c -@@ -52,6 +52,7 @@ - #include "user.h" - #include "security.h" - #include "esync.h" -+#include "fsync.h" - - - #ifdef __i386__ -@@ -112,6 +113,7 @@ static const struct object_ops thread_apc_ops = - remove_queue, /* remove_queue */ - thread_apc_signaled, /* signaled */ - NULL, /* get_esync_fd */ -+ NULL, /* get_fsync_idx */ - no_satisfied, /* satisfied */ - no_signal, /* signal */ - no_get_fd, /* get_fd */ -@@ -150,6 +152,7 @@ static const struct object_ops context_ops = - remove_queue, /* remove_queue */ - context_signaled, /* signaled */ - NULL, /* get_esync_fd */ -+ NULL, /* get_fsync_idx */ - no_satisfied, /* satisfied */ - no_signal, /* signal */ - no_get_fd, /* get_fd */ -@@ -173,5 +176,6 @@ static void dump_thread( struct object *obj, int verbose ); - static int thread_signaled( struct object *obj, struct wait_queue_entry *entry ); - static int thread_get_esync_fd( struct object *obj, enum esync_type *type ); -+static unsigned int thread_get_fsync_idx( struct object *obj, enum fsync_type *type ); - static unsigned int thread_map_access( struct object *obj, unsigned int access ); - static void thread_poll_event( struct fd *fd, int event ); - static struct list *thread_get_kernel_obj_list( struct object *obj ); -@@ -187,6 +191,7 @@ static const struct object_ops thread_ops = - remove_queue, /* remove_queue */ - thread_signaled, /* signaled */ - thread_get_esync_fd, /* get_esync_fd */ -+ thread_get_fsync_idx, /* get_fsync_idx */ - no_satisfied, /* satisfied */ - no_signal, /* signal */ - no_get_fd, /* get_fd */ -@@ -228,5 +233,6 @@ static inline void init_thread_structure( struct thread *thread ) - thread->entry_point = 0; - thread->esync_fd = -1; - thread->esync_apc_fd = -1; -+ thread->fsync_idx = 0; - thread->system_regs = 0; - thread->queue = NULL; -@@ -364,6 +370,12 @@ struct thread *create_thread( int fd, struct process *process, const struct secu - return NULL; - } - -+ if (do_fsync()) -+ { -+ thread->fsync_idx = fsync_alloc_shm( 0, 0 ); -+ thread->fsync_apc_idx = fsync_alloc_shm( 0, 0 ); -+ } -+ - if (do_esync()) - { - thread->esync_fd = esync_create_fd( 0, 0 ); -@@ -485,5 +497,12 @@ static int thread_get_esync_fd( struct object *obj, enum esync_type *type ) - return thread->esync_fd; - } - -+static unsigned int thread_get_fsync_idx( struct object *obj, enum fsync_type *type ) -+{ -+ struct thread *thread = (struct thread *)obj; -+ *type = FSYNC_MANUAL_SERVER; -+ return thread->fsync_idx; -+} -+ - static unsigned int thread_map_access( struct object *obj, unsigned int access ) - { -@@ -534,6 +553,7 @@ static struct thread_apc *create_apc( struct object *owner, const apc_call_t *ca - apc->result.type = APC_NONE; - if (owner) grab_object( owner ); - } -+ - return apc; - } - -@@ -1069,6 +1089,9 @@ void wake_up( struct object *obj, int max ) - struct list *ptr; - int ret; - -+ if (do_fsync()) -+ fsync_wake_up( obj ); -+ - if (do_esync()) - esync_wake_up( obj ); - -@@ -1159,6 +1182,9 @@ static int queue_apc( struct process *process, struct thread *thread, struct thr - { - wake_thread( thread ); - -+ if (do_fsync() && queue == &thread->user_apc) -+ fsync_wake_futex( thread->fsync_apc_idx ); -+ - if (do_esync() && queue == &thread->user_apc) - esync_wake_fd( thread->esync_apc_fd ); - } -@@ -1209,6 +1235,9 @@ static struct thread_apc *thread_dequeue_apc( struct thread *thread, int system - list_remove( ptr ); - } - -+ if (do_fsync() && list_empty( &thread->system_apc ) && list_empty( &thread->user_apc )) -+ fsync_clear_futex( thread->fsync_apc_idx ); -+ - if (do_esync() && list_empty( &thread->system_apc ) && list_empty( &thread->user_apc )) - esync_clear( thread->esync_apc_fd ); - -@@ -1328,5 +1357,7 @@ void kill_thread( struct thread *thread, int violent_death ) - kill_console_processes( thread, 0 ); - abandon_mutexes( thread ); -+ if (do_fsync()) -+ fsync_abandon_mutexes( thread ); - if (do_esync()) - esync_abandon_mutexes( thread ); - if (violent_death) -diff --git a/server/thread.h b/server/thread.h -index 0f6108b684a..53627631343 100644 ---- a/server/thread.h -+++ b/server/thread.h -@@ -56,5 +56,7 @@ struct thread - struct list mutex_list; /* list of currently owned mutexes */ - int esync_fd; /* esync file descriptor (signalled on exit) */ - int esync_apc_fd; /* esync apc fd (signalled when APCs are present) */ -+ unsigned int fsync_idx; -+ unsigned int fsync_apc_idx; - unsigned int system_regs; /* which system regs have been set */ - struct msg_queue *queue; /* message queue */ -diff --git a/server/timer.c b/server/timer.c -index 11fcdf3bae7..300bb08dd67 100644 ---- a/server/timer.c -+++ b/server/timer.c -@@ -37,4 +37,5 @@ - #include "handle.h" - #include "request.h" - #include "esync.h" -+#include "fsync.h" - -@@ -50,10 +51,12 @@ struct timer - client_ptr_t callback; /* callback APC function */ - client_ptr_t arg; /* callback argument */ - int esync_fd; /* esync file descriptor */ -+ unsigned int fsync_idx; /* fsync shm index */ - }; - - static void timer_dump( struct object *obj, int verbose ); - static int timer_signaled( struct object *obj, struct wait_queue_entry *entry ); - static int timer_get_esync_fd( struct object *obj, enum esync_type *type ); -+static unsigned int timer_get_fsync_idx( struct object *obj, enum fsync_type *type ); - static void timer_satisfied( struct object *obj, struct wait_queue_entry *entry ); - static void timer_destroy( struct object *obj ); -@@ -69,6 +72,7 @@ static const struct object_ops timer_ops = - remove_queue, /* remove_queue */ - timer_signaled, /* signaled */ - timer_get_esync_fd, /* get_esync_fd */ -+ timer_get_fsync_idx, /* get_fsync_idx */ - timer_satisfied, /* satisfied */ - no_signal, /* signal */ - no_get_fd, /* get_fd */ -@@ -105,6 +109,9 @@ static struct timer *create_timer( struct object *root, const struct unicode_str - timer->thread = NULL; - timer->esync_fd = -1; - -+ if (do_fsync()) -+ timer->fsync_idx = fsync_alloc_shm( 0, 0 ); -+ - if (do_esync()) - timer->esync_fd = esync_create_fd( 0, 0 ); - } -@@ -181,6 +188,9 @@ static int set_timer( struct timer *timer, timeout_t expire, unsigned int period - period = 0; /* period doesn't make any sense for a manual timer */ - timer->signaled = 0; - -+ if (do_fsync()) -+ fsync_clear( &timer->obj ); -+ - if (do_esync()) - esync_clear( timer->esync_fd ); - } -@@ -224,6 +234,13 @@ static int timer_get_esync_fd( struct object *obj, enum esync_type *type ) - return timer->esync_fd; - } - -+static unsigned int timer_get_fsync_idx( struct object *obj, enum fsync_type *type ) -+{ -+ struct timer *timer = (struct timer *)obj; -+ *type = timer->manual ? FSYNC_MANUAL_SERVER : FSYNC_AUTO_SERVER; -+ return timer->fsync_idx; -+} -+ - static void timer_satisfied( struct object *obj, struct wait_queue_entry *entry ) - { - struct timer *timer = (struct timer *)obj; -diff --git a/server/token.c b/server/token.c -index fafa86f292c..5e6d7fffc70 100644 ---- a/server/token.c -+++ b/server/token.c -@@ -148,6 +148,7 @@ static const struct object_ops token_ops = - NULL, /* remove_queue */ - NULL, /* signaled */ - NULL, /* get_esync_fd */ -+ NULL, /* get_fsync_idx */ - NULL, /* satisfied */ - no_signal, /* signal */ - no_get_fd, /* get_fd */ -diff --git a/server/trace.c b/server/trace.c -index 7c37f80768a..6945ed9ca6d 100644 ---- a/server/trace.c -+++ b/server/trace.c -@@ -4434,6 +4434,63 @@ static void dump_get_esync_apc_fd_request( const struct get_esync_apc_fd_request - { - } - -+static void dump_create_fsync_request( const struct create_fsync_request *req ) -+{ -+ fprintf( stderr, " access=%08x", req->access ); -+ fprintf( stderr, ", low=%d", req->low ); -+ fprintf( stderr, ", high=%d", req->high ); -+ fprintf( stderr, ", type=%d", req->type ); -+ dump_varargs_object_attributes( ", objattr=", cur_size ); -+} -+ -+static void dump_create_fsync_reply( const struct create_fsync_reply *req ) -+{ -+ fprintf( stderr, " handle=%04x", req->handle ); -+ fprintf( stderr, ", type=%d", req->type ); -+ fprintf( stderr, ", shm_idx=%08x", req->shm_idx ); -+} -+ -+static void dump_open_fsync_request( const struct open_fsync_request *req ) -+{ -+ fprintf( stderr, " access=%08x", req->access ); -+ fprintf( stderr, ", attributes=%08x", req->attributes ); -+ fprintf( stderr, ", rootdir=%04x", req->rootdir ); -+ fprintf( stderr, ", type=%d", req->type ); -+ dump_varargs_unicode_str( ", name=", cur_size ); -+} -+ -+static void dump_open_fsync_reply( const struct open_fsync_reply *req ) -+{ -+ fprintf( stderr, " handle=%04x", req->handle ); -+ fprintf( stderr, ", type=%d", req->type ); -+ fprintf( stderr, ", shm_idx=%08x", req->shm_idx ); -+} -+ -+static void dump_get_fsync_idx_request( const struct get_fsync_idx_request *req ) -+{ -+ fprintf( stderr, " handle=%04x", req->handle ); -+} -+ -+static void dump_get_fsync_idx_reply( const struct get_fsync_idx_reply *req ) -+{ -+ fprintf( stderr, " type=%d", req->type ); -+ fprintf( stderr, ", shm_idx=%08x", req->shm_idx ); -+} -+ -+static void dump_fsync_msgwait_request( const struct fsync_msgwait_request *req ) -+{ -+ fprintf( stderr, " in_msgwait=%d", req->in_msgwait ); -+} -+ -+static void dump_get_fsync_apc_idx_request( const struct get_fsync_apc_idx_request *req ) -+{ -+} -+ -+static void dump_get_fsync_apc_idx_reply( const struct get_fsync_apc_idx_reply *req ) -+{ -+ fprintf( stderr, " shm_idx=%08x", req->shm_idx ); -+} -+ - static const dump_func req_dumpers[REQ_NB_REQUESTS] = { - (dump_func)dump_new_process_request, - (dump_func)dump_exec_process_request, -@@ -4714,6 +4771,11 @@ static const dump_func req_dumpers[REQ_NB_REQUESTS] = { - (dump_func)dump_get_esync_fd_request, - (dump_func)dump_esync_msgwait_request, - (dump_func)dump_get_esync_apc_fd_request, -+ (dump_func)dump_create_fsync_request, -+ (dump_func)dump_open_fsync_request, -+ (dump_func)dump_get_fsync_idx_request, -+ (dump_func)dump_fsync_msgwait_request, -+ (dump_func)dump_get_fsync_apc_idx_request, - }; - - static const dump_func reply_dumpers[REQ_NB_REQUESTS] = { -@@ -4996,6 +5058,11 @@ static const dump_func reply_dumpers[REQ_NB_REQUESTS] = { - (dump_func)dump_get_esync_fd_reply, - NULL, - NULL, -+ (dump_func)dump_create_fsync_reply, -+ (dump_func)dump_open_fsync_reply, -+ (dump_func)dump_get_fsync_idx_reply, -+ NULL, -+ (dump_func)dump_get_fsync_apc_idx_reply, - }; - - static const char * const req_names[REQ_NB_REQUESTS] = { -@@ -5278,6 +5345,11 @@ static const char * const req_names[REQ_NB_REQUESTS] = { - "get_esync_fd", - "esync_msgwait", - "get_esync_apc_fd", -+ "create_fsync", -+ "open_fsync", -+ "get_fsync_idx", -+ "fsync_msgwait", -+ "get_fsync_apc_idx", - }; - - static const struct -diff --git a/server/winstation.c b/server/winstation.c -index 12060b82c83..9ba7c52d115 100644 ---- a/server/winstation.c -+++ b/server/winstation.c -@@ -65,6 +65,7 @@ static const struct object_ops winstation_ops = - NULL, /* remove_queue */ - NULL, /* signaled */ - NULL, /* get_esync_fd */ -+ NULL, /* get_fsync_idx */ - NULL, /* satisfied */ - no_signal, /* signal */ - no_get_fd, /* get_fd */ -@@ -91,6 +92,7 @@ static const struct object_ops desktop_ops = - NULL, /* remove_queue */ - NULL, /* signaled */ - NULL, /* get_esync_fd */ -+ NULL, /* get_fsync_idx */ - NULL, /* satisfied */ - no_signal, /* signal */ - no_get_fd, /* get_fd */ - diff --git a/patches/wine_6.13_fastsync2.patch b/patches/wine_6.13_fastsync2.patch deleted file mode 100644 index e0061cb..0000000 --- a/patches/wine_6.13_fastsync2.patch +++ /dev/null @@ -1,4739 +0,0 @@ -diff --git a/configure b/configure -index 74c72003827..fe26bedfce6 100755 ---- a/configure -+++ b/configure -@@ -7500,6 +7500,7 @@ for ac_header in \ - linux/serial.h \ - linux/types.h \ - linux/ucdrom.h \ -+ linux/winesync.h \ - lwp.h \ - mach-o/loader.h \ - mach/mach.h \ -diff --git a/configure.ac b/configure.ac -index dc43d73858f..9006299fee5 100644 ---- a/configure.ac -+++ b/configure.ac -@@ -480,6 +480,7 @@ AC_CHECK_HEADERS(\ - linux/serial.h \ - linux/types.h \ - linux/ucdrom.h \ -+ linux/winesync.h \ - lwp.h \ - mach-o/loader.h \ - mach/mach.h \ -diff --git a/dlls/kernel32/tests/sync.c b/dlls/kernel32/tests/sync.c -index 827f4c53aa0..36ed25b4db3 100644 ---- a/dlls/kernel32/tests/sync.c -+++ b/dlls/kernel32/tests/sync.c -@@ -2778,6 +2778,84 @@ static void test_QueueUserAPC(void) - ok(apc_count == 1, "APC count %u\n", apc_count); - } - -+static int zigzag_state, zigzag_count[2], zigzag_stop; -+ -+static DWORD CALLBACK zigzag_event0(void *arg) -+{ -+ HANDLE *events = arg; -+ -+ while (!zigzag_stop) -+ { -+ WaitForSingleObject(events[0], INFINITE); -+ ResetEvent(events[0]); -+ ok(zigzag_state == 0, "got wrong state %d\n", zigzag_state); -+ zigzag_state++; -+ SetEvent(events[1]); -+ zigzag_count[0]++; -+ } -+ trace("thread 0 got done\n"); -+ return 0; -+} -+ -+static DWORD CALLBACK zigzag_event1(void *arg) -+{ -+ HANDLE *events = arg; -+ -+ while (!zigzag_stop) -+ { -+ WaitForSingleObject(events[1], INFINITE); -+ ResetEvent(events[1]); -+ ok(zigzag_state == 1, "got wrong state %d\n", zigzag_state); -+ zigzag_state--; -+ SetEvent(events[0]); -+ zigzag_count[1]++; -+ } -+ trace("thread 1 got done\n"); -+ return 0; -+} -+ -+static void test_zigzag_event(void) -+{ -+ /* The basic idea is to test SetEvent/Wait back and forth between two -+ * threads. Each thread clears their own event, sets some common data, -+ * signals the other's, then waits on their own. We make sure the common -+ * data is always in the right state. We also print performance data. */ -+ -+ HANDLE threads[2], events[2]; -+ BOOL ret; -+ -+ events[0] = CreateEventA(NULL, FALSE, FALSE, NULL); -+ events[1] = CreateEventA(NULL, FALSE, FALSE, NULL); -+ -+ threads[0] = CreateThread(NULL, 0, zigzag_event0, events, 0, NULL); -+ threads[1] = CreateThread(NULL, 0, zigzag_event1, events, 0, NULL); -+ -+ zigzag_state = 0; -+ zigzag_count[0] = zigzag_count[1] = 0; -+ zigzag_stop = 0; -+ -+ trace("starting zigzag test (events)\n"); -+ SetEvent(events[0]); -+ Sleep(2000); -+ zigzag_stop = 1; -+ ret = WaitForMultipleObjects(2, threads, FALSE, INFINITE); -+ trace("%d\n", ret); -+ ok(ret == 0 || ret == 1, "wait failed: %u\n", ret); -+ -+ ok(zigzag_count[0] == zigzag_count[1] || zigzag_count[0] == zigzag_count[1] + 1, -+ "count did not match: %d != %d\n", zigzag_count[0], zigzag_count[1]); -+ -+ /* signal the other thread to finish, if it didn't already -+ * (in theory they both would at the same time, but there's a slight race on teardown if we get -+ * thread 1 SetEvent -> thread 0 ResetEvent -> thread 0 Wait -> thread 1 exits */ -+ zigzag_state = 1-ret; -+ SetEvent(events[1-ret]); -+ ret = WaitForSingleObject(threads[1-ret], 1000); -+ ok(!ret, "wait failed: %u\n", ret); -+ -+ trace("count: %d\n", zigzag_count[0]); -+} -+ - START_TEST(sync) - { - char **argv; -@@ -2843,5 +2921,6 @@ START_TEST(sync) - test_srwlock_example(); - test_alertable_wait(); - test_apc_deadlock(); -+ test_zigzag_event(); - test_crit_section(); - } -diff --git a/dlls/ntdll/unix/file.c b/dlls/ntdll/unix/file.c -index 21f5d10f3f8..a7b1ff15376 100644 ---- a/dlls/ntdll/unix/file.c -+++ b/dlls/ntdll/unix/file.c -@@ -4690,7 +4690,7 @@ static async_data_t server_async( HANDLE handle, struct async_fileio *user, HAND - - static NTSTATUS wait_async( HANDLE handle, BOOL alertable ) - { -- return NtWaitForSingleObject( handle, alertable, NULL ); -+ return server_wait_for_object( handle, alertable, NULL ); - } - - /* callback for irp async I/O completion */ -@@ -6021,7 +6021,7 @@ NTSTATUS WINAPI NtLockFile( HANDLE file, HANDLE event, PIO_APC_ROUTINE apc, void - } - if (handle) - { -- NtWaitForSingleObject( handle, FALSE, NULL ); -+ server_wait_for_object( handle, FALSE, NULL ); - NtClose( handle ); - } - else /* Unix lock conflict, sleep a bit and retry */ -diff --git a/dlls/ntdll/unix/process.c b/dlls/ntdll/unix/process.c -index c1ff0289cf2..153350be927 100644 ---- a/dlls/ntdll/unix/process.c -+++ b/dlls/ntdll/unix/process.c -@@ -794,7 +794,7 @@ NTSTATUS WINAPI NtCreateUserProcess( HANDLE *process_handle_ptr, HANDLE *thread_ - - /* wait for the new process info to be ready */ - -- NtWaitForSingleObject( process_info, FALSE, NULL ); -+ server_wait_for_object( process_info, FALSE, NULL ); - SERVER_START_REQ( get_new_process_info ) - { - req->info = wine_server_obj_handle( process_info ); -diff --git a/dlls/ntdll/unix/server.c b/dlls/ntdll/unix/server.c -index cec2ef250a3..5754f272b21 100644 ---- a/dlls/ntdll/unix/server.c -+++ b/dlls/ntdll/unix/server.c -@@ -114,7 +114,7 @@ timeout_t server_start_time = 0; /* time of server startup */ - sigset_t server_block_set; /* signals to block during server calls */ - static int fd_socket = -1; /* socket to exchange file descriptors with the server */ - static pid_t server_pid; --static pthread_mutex_t fd_cache_mutex = PTHREAD_MUTEX_INITIALIZER; -+pthread_mutex_t fd_cache_mutex = PTHREAD_MUTEX_INITIALIZER; - - /* atomically exchange a 64-bit value */ - static inline LONG64 interlocked_xchg64( LONG64 *dest, LONG64 val ) -@@ -354,7 +354,7 @@ static int wait_select_reply( void *cookie ) - /*********************************************************************** - * invoke_user_apc - */ --static NTSTATUS invoke_user_apc( CONTEXT *context, const user_apc_t *apc, NTSTATUS status ) -+NTSTATUS invoke_user_apc( CONTEXT *context, const user_apc_t *apc, NTSTATUS status ) - { - return call_user_apc_dispatcher( context, apc->args[0], apc->args[1], apc->args[2], - wine_server_get_ptr( apc->func ), status ); -@@ -685,6 +685,21 @@ unsigned int server_wait( const select_op_t *select_op, data_size_t size, UINT f - } - - -+/* helper function to perform a server-side wait on an internal handle without -+ * using the fast synchronization path */ -+unsigned int server_wait_for_object( HANDLE handle, BOOL alertable, const LARGE_INTEGER *timeout ) -+{ -+ select_op_t select_op; -+ UINT flags = SELECT_INTERRUPTIBLE; -+ -+ if (alertable) flags |= SELECT_ALERTABLE; -+ -+ select_op.wait.op = SELECT_WAIT; -+ select_op.wait.handles[0] = wine_server_obj_handle( handle ); -+ return server_wait( &select_op, offsetof( select_op_t, wait.handles[1] ), flags, timeout ); -+} -+ -+ - /*********************************************************************** - * NtContinue (NTDLL.@) - */ -@@ -749,7 +764,7 @@ unsigned int server_queue_process_apc( HANDLE process, const apc_call_t *call, a - } - else - { -- NtWaitForSingleObject( handle, FALSE, NULL ); -+ server_wait_for_object( handle, FALSE, NULL ); - - SERVER_START_REQ( get_apc_result ) - { -@@ -1684,12 +1699,17 @@ NTSTATUS WINAPI NtDuplicateObject( HANDLE source_process, HANDLE source, HANDLE - return result.dup_handle.status; - } - -+ /* hold fd_cache_mutex to prevent the fd from being added again between the -+ * call to remove_fd_from_cache and close_handle */ - server_enter_uninterrupted_section( &fd_cache_mutex, &sigset ); - - /* always remove the cached fd; if the server request fails we'll just - * retrieve it again */ - if (options & DUPLICATE_CLOSE_SOURCE) -+ { - fd = remove_fd_from_cache( source ); -+ close_fast_sync_obj( source ); -+ } - - SERVER_START_REQ( dup_handle ) - { -@@ -1723,12 +1743,16 @@ NTSTATUS WINAPI NtClose( HANDLE handle ) - NTSTATUS ret; - int fd; - -+ /* hold fd_cache_mutex to prevent the fd from being added again between the -+ * call to remove_fd_from_cache and close_handle */ - server_enter_uninterrupted_section( &fd_cache_mutex, &sigset ); - - /* always remove the cached fd; if the server request fails we'll just - * retrieve it again */ - fd = remove_fd_from_cache( handle ); - -+ close_fast_sync_obj( handle ); -+ - SERVER_START_REQ( close_handle ) - { - req->handle = wine_server_obj_handle( handle ); -diff --git a/dlls/ntdll/unix/signal_arm.c b/dlls/ntdll/unix/signal_arm.c -index f07f97930f1..e226225cd1a 100644 ---- a/dlls/ntdll/unix/signal_arm.c -+++ b/dlls/ntdll/unix/signal_arm.c -@@ -765,6 +765,8 @@ static void usr1_handler( int signal, siginfo_t *siginfo, void *sigcontext ) - save_context( &context, sigcontext ); - wait_suspend( &context ); - restore_context( &context, sigcontext ); -+ -+ // FIXME: fast alertable waits... - } - } - -@@ -840,14 +842,15 @@ void signal_init_process(void) - if (sigaction( SIGABRT, &sig_act, NULL ) == -1) goto error; - sig_act.sa_sigaction = quit_handler; - if (sigaction( SIGQUIT, &sig_act, NULL ) == -1) goto error; -- sig_act.sa_sigaction = usr1_handler; -- if (sigaction( SIGUSR1, &sig_act, NULL ) == -1) goto error; - sig_act.sa_sigaction = trap_handler; - if (sigaction( SIGTRAP, &sig_act, NULL ) == -1) goto error; - sig_act.sa_sigaction = segv_handler; - if (sigaction( SIGSEGV, &sig_act, NULL ) == -1) goto error; - if (sigaction( SIGILL, &sig_act, NULL ) == -1) goto error; - if (sigaction( SIGBUS, &sig_act, NULL ) == -1) goto error; -+ sig_act.sa_sigaction = usr1_handler; -+ sig_act.sa_flags &= ~SA_RESTART; /* needed for fast sync alertable waits */ -+ if (sigaction( SIGUSR1, &sig_act, NULL ) == -1) goto error; - return; - - error: -diff --git a/dlls/ntdll/unix/signal_arm64.c b/dlls/ntdll/unix/signal_arm64.c -index 079064eee4b..206678206dc 100644 ---- a/dlls/ntdll/unix/signal_arm64.c -+++ b/dlls/ntdll/unix/signal_arm64.c -@@ -922,6 +922,8 @@ static void usr1_handler( int signal, siginfo_t *siginfo, void *sigcontext ) - save_context( &context, sigcontext ); - wait_suspend( &context ); - restore_context( &context, sigcontext ); -+ -+ // FIXME: fast alertable waits... - } - } - -@@ -1015,8 +1017,6 @@ void signal_init_process(void) - if (sigaction( SIGABRT, &sig_act, NULL ) == -1) goto error; - sig_act.sa_sigaction = quit_handler; - if (sigaction( SIGQUIT, &sig_act, NULL ) == -1) goto error; -- sig_act.sa_sigaction = usr1_handler; -- if (sigaction( SIGUSR1, &sig_act, NULL ) == -1) goto error; - sig_act.sa_sigaction = usr2_handler; - if (sigaction( SIGUSR2, &sig_act, NULL ) == -1) goto error; - sig_act.sa_sigaction = trap_handler; -@@ -1027,6 +1027,9 @@ void signal_init_process(void) - if (sigaction( SIGILL, &sig_act, NULL ) == -1) goto error; - sig_act.sa_sigaction = bus_handler; - if (sigaction( SIGBUS, &sig_act, NULL ) == -1) goto error; -+ sig_act.sa_sigaction = usr1_handler; -+ sig_act.sa_flags &= ~SA_RESTART; /* needed for fast sync alertable waits */ -+ if (sigaction( SIGUSR1, &sig_act, NULL ) == -1) goto error; - return; - - error: -diff --git a/dlls/ntdll/unix/signal_i386.c b/dlls/ntdll/unix/signal_i386.c -index b5afbcf0aac..0a3b8dc115d 100644 ---- a/dlls/ntdll/unix/signal_i386.c -+++ b/dlls/ntdll/unix/signal_i386.c -@@ -2010,6 +2010,9 @@ static void usr1_handler( int signal, siginfo_t *siginfo, void *sigcontext ) - NtGetContextThread( GetCurrentThread(), &xcontext.c ); - wait_suspend( &xcontext.c ); - NtSetContextThread( GetCurrentThread(), &xcontext.c ); -+ -+ if (ntdll_get_thread_data()->in_fast_alert_wait) -+ siglongjmp( ntdll_get_thread_data()->fast_alert_buf, 1 ); - } - else - { -@@ -2336,14 +2339,15 @@ void signal_init_process(void) - if (sigaction( SIGABRT, &sig_act, NULL ) == -1) goto error; - sig_act.sa_sigaction = quit_handler; - if (sigaction( SIGQUIT, &sig_act, NULL ) == -1) goto error; -- sig_act.sa_sigaction = usr1_handler; -- if (sigaction( SIGUSR1, &sig_act, NULL ) == -1) goto error; - sig_act.sa_sigaction = trap_handler; - if (sigaction( SIGTRAP, &sig_act, NULL ) == -1) goto error; - sig_act.sa_sigaction = segv_handler; - if (sigaction( SIGSEGV, &sig_act, NULL ) == -1) goto error; - if (sigaction( SIGILL, &sig_act, NULL ) == -1) goto error; - if (sigaction( SIGBUS, &sig_act, NULL ) == -1) goto error; -+ sig_act.sa_sigaction = usr1_handler; -+ sig_act.sa_flags &= ~SA_RESTART; /* needed for fast sync alertable waits */ -+ if (sigaction( SIGUSR1, &sig_act, NULL ) == -1) goto error; - return; - - error: -diff --git a/dlls/ntdll/unix/signal_x86_64.c b/dlls/ntdll/unix/signal_x86_64.c -index 148e3641d0e..4d6201e7156 100644 ---- a/dlls/ntdll/unix/signal_x86_64.c -+++ b/dlls/ntdll/unix/signal_x86_64.c -@@ -2478,6 +2478,9 @@ static void usr1_handler( int signal, siginfo_t *siginfo, void *ucontext ) - NtGetContextThread( GetCurrentThread(), &context.c ); - wait_suspend( &context.c ); - NtSetContextThread( GetCurrentThread(), &context.c ); -+ -+ if (ntdll_get_thread_data()->in_fast_alert_wait) -+ siglongjmp( ntdll_get_thread_data()->fast_alert_buf, 1 ); - } - else - { -@@ -2649,14 +2652,15 @@ void signal_init_process(void) - if (sigaction( SIGABRT, &sig_act, NULL ) == -1) goto error; - sig_act.sa_sigaction = quit_handler; - if (sigaction( SIGQUIT, &sig_act, NULL ) == -1) goto error; -- sig_act.sa_sigaction = usr1_handler; -- if (sigaction( SIGUSR1, &sig_act, NULL ) == -1) goto error; - sig_act.sa_sigaction = trap_handler; - if (sigaction( SIGTRAP, &sig_act, NULL ) == -1) goto error; - sig_act.sa_sigaction = segv_handler; - if (sigaction( SIGSEGV, &sig_act, NULL ) == -1) goto error; - if (sigaction( SIGILL, &sig_act, NULL ) == -1) goto error; - if (sigaction( SIGBUS, &sig_act, NULL ) == -1) goto error; -+ sig_act.sa_sigaction = usr1_handler; -+ sig_act.sa_flags &= ~SA_RESTART; /* needed for fast sync alertable waits */ -+ if (sigaction( SIGUSR1, &sig_act, NULL ) == -1) goto error; - return; - - error: -diff --git a/dlls/ntdll/unix/socket.c b/dlls/ntdll/unix/socket.c -index c1cd319ccf9..2e9f5230629 100644 ---- a/dlls/ntdll/unix/socket.c -+++ b/dlls/ntdll/unix/socket.c -@@ -107,7 +107,7 @@ static async_data_t server_async( HANDLE handle, struct async_fileio *user, HAND - - static NTSTATUS wait_async( HANDLE handle, BOOL alertable ) - { -- return NtWaitForSingleObject( handle, alertable, NULL ); -+ return server_wait_for_object( handle, alertable, NULL ); - } - - union unix_sockaddr -diff --git a/dlls/ntdll/unix/sync.c b/dlls/ntdll/unix/sync.c -index 16635ee42fa..31af5cf8040 100644 ---- a/dlls/ntdll/unix/sync.c -+++ b/dlls/ntdll/unix/sync.c -@@ -30,8 +30,15 @@ - - #include - #include -+#include - #include - #include -+#ifdef HAVE_SYS_IOCTL_H -+#include -+#endif -+#ifdef HAVE_SYS_MMAN_H -+#include -+#endif - #ifdef HAVE_SYS_SYSCALL_H - #include - #endif -@@ -61,6 +68,9 @@ - # include - # include - #endif -+#ifdef HAVE_LINUX_WINESYNC_H -+# include -+#endif - - #include "ntstatus.h" - #define WIN32_NO_STATUS -@@ -141,173 +151,1194 @@ static inline int use_futexes(void) - { - static int supported = -1; - -- if (supported == -1) -+ if (supported == -1) -+ { -+ futex_wait( &supported, 10, NULL ); -+ if (errno == ENOSYS) -+ { -+ futex_private = 0; -+ futex_wait( &supported, 10, NULL ); -+ } -+ supported = (errno != ENOSYS); -+ } -+ return supported; -+} -+ -+static int *get_futex(void **ptr) -+{ -+ if (sizeof(void *) == 8) -+ return (int *)((((ULONG_PTR)ptr) + 3) & ~3); -+ else if (!(((ULONG_PTR)ptr) & 3)) -+ return (int *)ptr; -+ else -+ return NULL; -+} -+ -+static void timespec_from_timeout( struct timespec *timespec, const LARGE_INTEGER *timeout ) -+{ -+ LARGE_INTEGER now; -+ timeout_t diff; -+ -+ if (timeout->QuadPart > 0) -+ { -+ NtQuerySystemTime( &now ); -+ diff = timeout->QuadPart - now.QuadPart; -+ } -+ else -+ diff = -timeout->QuadPart; -+ -+ timespec->tv_sec = diff / TICKSPERSEC; -+ timespec->tv_nsec = (diff % TICKSPERSEC) * 100; -+} -+ -+#endif -+ -+ -+static BOOL compare_addr( const void *addr, const void *cmp, SIZE_T size ) -+{ -+ switch (size) -+ { -+ case 1: -+ return (*(const UCHAR *)addr == *(const UCHAR *)cmp); -+ case 2: -+ return (*(const USHORT *)addr == *(const USHORT *)cmp); -+ case 4: -+ return (*(const ULONG *)addr == *(const ULONG *)cmp); -+ case 8: -+ return (*(const ULONG64 *)addr == *(const ULONG64 *)cmp); -+ } -+ -+ return FALSE; -+} -+ -+ -+static const char *debugstr_timeout( const LARGE_INTEGER *timeout ) -+{ -+ if (!timeout) return ""; -+ return wine_dbg_sprintf( "%ld.%07ld", (long)timeout->QuadPart / TICKSPERSEC, -+ (long)timeout->QuadPart % TICKSPERSEC ); -+} -+ -+ -+/* create a struct security_descriptor and contained information in one contiguous piece of memory */ -+NTSTATUS alloc_object_attributes( const OBJECT_ATTRIBUTES *attr, struct object_attributes **ret, -+ data_size_t *ret_len ) -+{ -+ unsigned int len = sizeof(**ret); -+ SID *owner = NULL, *group = NULL; -+ ACL *dacl = NULL, *sacl = NULL; -+ SECURITY_DESCRIPTOR *sd; -+ -+ *ret = NULL; -+ *ret_len = 0; -+ -+ if (!attr) return STATUS_SUCCESS; -+ -+ if (attr->Length != sizeof(*attr)) return STATUS_INVALID_PARAMETER; -+ -+ if ((sd = attr->SecurityDescriptor)) -+ { -+ len += sizeof(struct security_descriptor); -+ if (sd->Revision != SECURITY_DESCRIPTOR_REVISION) return STATUS_UNKNOWN_REVISION; -+ if (sd->Control & SE_SELF_RELATIVE) -+ { -+ SECURITY_DESCRIPTOR_RELATIVE *rel = (SECURITY_DESCRIPTOR_RELATIVE *)sd; -+ if (rel->Owner) owner = (PSID)((BYTE *)rel + rel->Owner); -+ if (rel->Group) group = (PSID)((BYTE *)rel + rel->Group); -+ if ((sd->Control & SE_SACL_PRESENT) && rel->Sacl) sacl = (PSID)((BYTE *)rel + rel->Sacl); -+ if ((sd->Control & SE_DACL_PRESENT) && rel->Dacl) dacl = (PSID)((BYTE *)rel + rel->Dacl); -+ } -+ else -+ { -+ owner = sd->Owner; -+ group = sd->Group; -+ if (sd->Control & SE_SACL_PRESENT) sacl = sd->Sacl; -+ if (sd->Control & SE_DACL_PRESENT) dacl = sd->Dacl; -+ } -+ -+ if (owner) len += offsetof( SID, SubAuthority[owner->SubAuthorityCount] ); -+ if (group) len += offsetof( SID, SubAuthority[group->SubAuthorityCount] ); -+ if (sacl) len += sacl->AclSize; -+ if (dacl) len += dacl->AclSize; -+ -+ /* fix alignment for the Unicode name that follows the structure */ -+ len = (len + sizeof(WCHAR) - 1) & ~(sizeof(WCHAR) - 1); -+ } -+ -+ if (attr->ObjectName) -+ { -+ if (attr->ObjectName->Length & (sizeof(WCHAR) - 1)) return STATUS_OBJECT_NAME_INVALID; -+ len += attr->ObjectName->Length; -+ } -+ else if (attr->RootDirectory) return STATUS_OBJECT_NAME_INVALID; -+ -+ len = (len + 3) & ~3; /* DWORD-align the entire structure */ -+ -+ if (!(*ret = calloc( len, 1 ))) return STATUS_NO_MEMORY; -+ -+ (*ret)->rootdir = wine_server_obj_handle( attr->RootDirectory ); -+ (*ret)->attributes = attr->Attributes; -+ -+ if (attr->SecurityDescriptor) -+ { -+ struct security_descriptor *descr = (struct security_descriptor *)(*ret + 1); -+ unsigned char *ptr = (unsigned char *)(descr + 1); -+ -+ descr->control = sd->Control & ~SE_SELF_RELATIVE; -+ if (owner) descr->owner_len = offsetof( SID, SubAuthority[owner->SubAuthorityCount] ); -+ if (group) descr->group_len = offsetof( SID, SubAuthority[group->SubAuthorityCount] ); -+ if (sacl) descr->sacl_len = sacl->AclSize; -+ if (dacl) descr->dacl_len = dacl->AclSize; -+ -+ memcpy( ptr, owner, descr->owner_len ); -+ ptr += descr->owner_len; -+ memcpy( ptr, group, descr->group_len ); -+ ptr += descr->group_len; -+ memcpy( ptr, sacl, descr->sacl_len ); -+ ptr += descr->sacl_len; -+ memcpy( ptr, dacl, descr->dacl_len ); -+ (*ret)->sd_len = (sizeof(*descr) + descr->owner_len + descr->group_len + descr->sacl_len + -+ descr->dacl_len + sizeof(WCHAR) - 1) & ~(sizeof(WCHAR) - 1); -+ } -+ -+ if (attr->ObjectName) -+ { -+ unsigned char *ptr = (unsigned char *)(*ret + 1) + (*ret)->sd_len; -+ (*ret)->name_len = attr->ObjectName->Length; -+ memcpy( ptr, attr->ObjectName->Buffer, (*ret)->name_len ); -+ } -+ -+ *ret_len = len; -+ return STATUS_SUCCESS; -+} -+ -+ -+static NTSTATUS validate_open_object_attributes( const OBJECT_ATTRIBUTES *attr ) -+{ -+ if (!attr || attr->Length != sizeof(*attr)) return STATUS_INVALID_PARAMETER; -+ -+ if (attr->ObjectName) -+ { -+ if (attr->ObjectName->Length & (sizeof(WCHAR) - 1)) return STATUS_OBJECT_NAME_INVALID; -+ } -+ else if (attr->RootDirectory) return STATUS_OBJECT_NAME_INVALID; -+ -+ return STATUS_SUCCESS; -+} -+ -+ -+#ifdef HAVE_LINUX_WINESYNC_H -+ -+static int get_fast_sync_device(void) -+{ -+ static int fast_sync_fd = -2; -+ -+ if (fast_sync_fd == -2) -+ { -+ HANDLE device; -+ int fd, needs_close; -+ NTSTATUS ret; -+ -+ SERVER_START_REQ( get_fast_sync_device ) -+ { -+ if (!(ret = wine_server_call( req ))) device = wine_server_ptr_handle( reply->handle ); -+ } -+ SERVER_END_REQ; -+ -+ if (!ret) -+ { -+ if (!server_get_unix_fd( device, 0, &fd, &needs_close, NULL, NULL )) -+ { -+ if (InterlockedCompareExchange( &fast_sync_fd, fd, -2 ) != -2) -+ { -+ /* someone beat us to it */ -+ if (needs_close) close( fd ); -+ NtClose( device ); -+ } -+ /* otherwise don't close the device */ -+ } -+ else -+ { -+ InterlockedCompareExchange( &fast_sync_fd, -1, -2 ); -+ NtClose( device ); -+ } -+ } -+ else -+ { -+ InterlockedCompareExchange( &fast_sync_fd, -1, -2 ); -+ } -+ } -+ return fast_sync_fd; -+} -+ -+/* It's possible for synchronization primitives to remain alive even after being -+ * closed, because a thread is still waiting on them. It's rare in practice, and -+ * documented as being undefined behaviour by Microsoft, but it works, and some -+ * applications rely on it. This means we need to refcount handles, and defer -+ * deleting them on the server side until the refcount reaches zero. We do this -+ * by having each client process hold a handle to the fast synchronization -+ * object, as well as a private refcount. When the client refcount reaches zero, -+ * it closes the handle; when all handles are closed, the server deletes the -+ * fast synchronization object. -+ * -+ * We also need this for signal-and-wait. The signal and wait operations aren't -+ * atomic, but we can't perform the signal and then return STATUS_INVALID_HANDLE -+ * for the wait—we need to either do both operations or neither. That means we -+ * need to grab references to both objects, and prevent them from being -+ * destroyed before we're done with them. -+ * -+ * We want lookup of objects from the cache to be very fast; ideally, it should -+ * be lock-free. We achieve this by using atomic modifications to "refcount", -+ * and guaranteeing that all other fields are valid and correct *as long as* -+ * refcount is nonzero, and we store the entire structure in memory which will -+ * never be freed. -+ * -+ * This means that acquiring the object can't use a simple atomic increment; it -+ * has to use a compare-and-swap loop to ensure that it doesn't try to increment -+ * an object with a zero refcount. That's still leagues better than a real lock, -+ * though, and release can be a single atomic decrement. -+ * -+ * It also means that threads modifying the cache need to take a lock, to -+ * prevent other threads from writing to it concurrently. -+ * -+ * It's possible for an object currently in use (by a waiter) to be closed and -+ * the same handle immediately reallocated to a different object. This should be -+ * a very rare situation, and in that case we simply don't cache the handle. -+ */ -+struct fast_sync_cache_entry -+{ -+ LONG refcount; -+ unsigned int obj; -+ enum fast_sync_type type; -+ unsigned int access; -+ BOOL closed; -+ /* handle to the underlying fast sync object, stored as obj_handle_t to save -+ * space */ -+ obj_handle_t handle; -+}; -+ -+ -+static void release_fast_sync_obj( struct fast_sync_cache_entry *cache ) -+{ -+ /* save the handle now; as soon as the refcount hits 0 we cannot access the -+ * cache anymore */ -+ HANDLE handle = wine_server_ptr_handle( cache->handle ); -+ LONG refcount = InterlockedDecrement( &cache->refcount ); -+ -+ assert( refcount >= 0 ); -+ -+ if (!refcount) -+ { -+ NTSTATUS ret; -+ -+ /* we can't call NtClose here as we may be inside fd_cache_mutex */ -+ SERVER_START_REQ( close_handle ) -+ { -+ req->handle = wine_server_obj_handle( handle ); -+ ret = wine_server_call( req ); -+ } -+ SERVER_END_REQ; -+ -+ assert( !ret ); -+ } -+} -+ -+ -+#define FAST_SYNC_CACHE_BLOCK_SIZE (65536 / sizeof(struct fast_sync_cache_entry)) -+#define FAST_SYNC_CACHE_ENTRIES 128 -+ -+static struct fast_sync_cache_entry *fast_sync_cache[FAST_SYNC_CACHE_ENTRIES]; -+static struct fast_sync_cache_entry fast_sync_cache_initial_block[FAST_SYNC_CACHE_BLOCK_SIZE]; -+ -+static inline unsigned int handle_to_index( HANDLE handle, unsigned int *entry ) -+{ -+ unsigned int idx = (wine_server_obj_handle(handle) >> 2) - 1; -+ *entry = idx / FAST_SYNC_CACHE_BLOCK_SIZE; -+ return idx % FAST_SYNC_CACHE_BLOCK_SIZE; -+} -+ -+ -+static struct fast_sync_cache_entry *cache_fast_sync_obj( HANDLE handle, obj_handle_t fast_sync, int obj, -+ enum fast_sync_type type, unsigned int access ) -+{ -+ unsigned int entry, idx = handle_to_index( handle, &entry ); -+ struct fast_sync_cache_entry *cache; -+ sigset_t sigset; -+ int refcount; -+ -+ if (entry >= FAST_SYNC_CACHE_ENTRIES) -+ { -+ FIXME( "too many allocated handles, not caching %p\n", handle ); -+ return NULL; -+ } -+ -+ if (!fast_sync_cache[entry]) /* do we need to allocate a new block of entries? */ -+ { -+ if (!entry) fast_sync_cache[0] = fast_sync_cache_initial_block; -+ else -+ { -+ static const size_t size = FAST_SYNC_CACHE_BLOCK_SIZE * sizeof(struct fast_sync_cache_entry); -+ void *ptr = anon_mmap_alloc( size, PROT_READ | PROT_WRITE ); -+ if (ptr == MAP_FAILED) return NULL; -+ if (InterlockedCompareExchangePointer( (void **)&fast_sync_cache[entry], ptr, NULL )) -+ munmap( ptr, size ); /* someone beat us to it */ -+ } -+ } -+ -+ cache = &fast_sync_cache[entry][idx]; -+ -+ /* Hold fd_cache_mutex instead of a separate mutex, to prevent the same -+ * race between this function and NtClose. That is, prevent the object from -+ * being cached again between close_fast_sync_obj() and close_handle. */ -+ server_enter_uninterrupted_section( &fd_cache_mutex, &sigset ); -+ -+ if (InterlockedCompareExchange( &cache->refcount, 0, 0 )) -+ { -+ /* We lost the race with another thread trying to cache this object, or -+ * the handle is currently being used for another object (i.e. it was -+ * closed and then reused). We have no way of knowing which, and in the -+ * latter case we can't cache this object until the old one is -+ * completely destroyed, so always return failure. */ -+ server_leave_uninterrupted_section( &fd_cache_mutex, &sigset ); -+ return NULL; -+ } -+ -+ cache->handle = fast_sync; -+ cache->obj = obj; -+ cache->type = type; -+ cache->access = access; -+ cache->closed = FALSE; -+ /* make sure we set the other members before the refcount; this store needs -+ * release semantics [paired with the load in get_cached_fast_sync_obj()] -+ * set the refcount to 2 (one for the handle, one for the caller) */ -+ refcount = InterlockedExchange( &cache->refcount, 2 ); -+ assert( !refcount ); -+ -+ server_leave_uninterrupted_section( &fd_cache_mutex, &sigset ); -+ -+ return cache; -+} -+ -+ -+/* returns the previous value */ -+static inline LONG interlocked_inc_if_nonzero( LONG *dest ) -+{ -+ LONG val, tmp; -+ for (val = *dest;; val = tmp) -+ { -+ if (!val || (tmp = InterlockedCompareExchange( dest, val + 1, val )) == val) -+ break; -+ } -+ return val; -+} -+ -+ -+static struct fast_sync_cache_entry *get_cached_fast_sync_obj( HANDLE handle ) -+{ -+ unsigned int entry, idx = handle_to_index( handle, &entry ); -+ struct fast_sync_cache_entry *cache; -+ -+ if (entry >= FAST_SYNC_CACHE_ENTRIES || !fast_sync_cache[entry]) -+ return NULL; -+ -+ cache = &fast_sync_cache[entry][idx]; -+ -+ /* this load needs acquire semantics [paired with the store in -+ * cache_fast_sync_obj()] */ -+ if (!interlocked_inc_if_nonzero( &cache->refcount )) -+ return NULL; -+ -+ if (cache->closed) -+ { -+ /* The object is still being used, but "handle" has been closed. The -+ * handle value might have been reused for another object in the -+ * meantime, in which case we have to report that valid object, so -+ * force the caller to check the server. */ -+ release_fast_sync_obj( cache ); -+ return NULL; -+ } -+ -+ return cache; -+} -+ -+ -+/* returns a pointer to a cache entry; if the object could not be cached, -+ * returns "stack_cache" instead, which should be allocated on stack */ -+static NTSTATUS get_fast_sync_obj( HANDLE handle, enum fast_sync_type desired_type, ACCESS_MASK desired_access, -+ struct fast_sync_cache_entry *stack_cache, -+ struct fast_sync_cache_entry **ret_cache ) -+{ -+ struct fast_sync_cache_entry *cache; -+ obj_handle_t fast_sync_handle; -+ enum fast_sync_type type; -+ unsigned int access; -+ NTSTATUS ret; -+ int obj; -+ -+ /* try to find it in the cache already */ -+ if ((cache = get_cached_fast_sync_obj( handle ))) -+ { -+ *ret_cache = cache; -+ return STATUS_SUCCESS; -+ } -+ -+ /* try to retrieve it from the server */ -+ SERVER_START_REQ( get_fast_sync_obj ) -+ { -+ req->handle = wine_server_obj_handle( handle ); -+ if (!(ret = wine_server_call( req ))) -+ { -+ fast_sync_handle = reply->handle; -+ access = reply->access; -+ type = reply->type; -+ obj = reply->obj; -+ } -+ } -+ SERVER_END_REQ; -+ -+ if (ret) return ret; -+ -+ cache = cache_fast_sync_obj( handle, fast_sync_handle, obj, type, access ); -+ if (!cache) -+ { -+ cache = stack_cache; -+ cache->handle = fast_sync_handle; -+ cache->obj = obj; -+ cache->type = type; -+ cache->access = access; -+ cache->closed = FALSE; -+ cache->refcount = 1; -+ } -+ -+ *ret_cache = cache; -+ -+ if (desired_type && desired_type != cache->type) -+ { -+ release_fast_sync_obj( cache ); -+ return STATUS_OBJECT_TYPE_MISMATCH; -+ } -+ -+ if ((cache->access & desired_access) != desired_access) -+ { -+ release_fast_sync_obj( cache ); -+ return STATUS_ACCESS_DENIED; -+ } -+ -+ return STATUS_SUCCESS; -+} -+ -+ -+/* caller must hold fd_cache_mutex */ -+void close_fast_sync_obj( HANDLE handle ) -+{ -+ struct fast_sync_cache_entry *cache = get_cached_fast_sync_obj( handle ); -+ -+ if (cache) -+ { -+ cache->closed = TRUE; -+ /* once for the reference we just grabbed, and once for the handle */ -+ release_fast_sync_obj( cache ); -+ release_fast_sync_obj( cache ); -+ } -+} -+ -+ -+static NTSTATUS fast_release_semaphore_obj( int device, unsigned int obj, ULONG count, ULONG *prev_count ) -+{ -+ struct winesync_sem_args args = {0}; -+ NTSTATUS ret; -+ -+ args.sem = obj; -+ args.count = count; -+ ret = ioctl( device, WINESYNC_IOC_PUT_SEM, &args ); -+ if (ret < 0) -+ { -+ if (errno == EOVERFLOW) -+ return STATUS_SEMAPHORE_LIMIT_EXCEEDED; -+ else -+ return errno_to_status( errno ); -+ } -+ if (prev_count) *prev_count = args.count; -+ return STATUS_SUCCESS; -+} -+ -+ -+static NTSTATUS fast_release_semaphore( HANDLE handle, ULONG count, ULONG *prev_count ) -+{ -+ struct fast_sync_cache_entry stack_cache, *cache; -+ NTSTATUS ret; -+ int device; -+ -+ if ((device = get_fast_sync_device()) < 0) -+ return STATUS_NOT_IMPLEMENTED; -+ -+ if ((ret = get_fast_sync_obj( handle, FAST_SYNC_SEMAPHORE, -+ SEMAPHORE_MODIFY_STATE, &stack_cache, &cache ))) -+ return ret; -+ -+ ret = fast_release_semaphore_obj( device, cache->obj, count, prev_count ); -+ -+ release_fast_sync_obj( cache ); -+ return ret; -+} -+ -+ -+static NTSTATUS fast_query_semaphore_obj( int device, unsigned int obj, SEMAPHORE_BASIC_INFORMATION *info ) -+{ -+ struct winesync_sem_args args = {0}; -+ NTSTATUS ret; -+ -+ args.sem = obj; -+ ret = ioctl( device, WINESYNC_IOC_READ_SEM, &args ); -+ -+ if (ret < 0) -+ return errno_to_status( errno ); -+ info->CurrentCount = args.count; -+ info->MaximumCount = args.max; -+ return STATUS_SUCCESS; -+} -+ -+ -+static NTSTATUS fast_query_semaphore( HANDLE handle, SEMAPHORE_BASIC_INFORMATION *info ) -+{ -+ struct fast_sync_cache_entry stack_cache, *cache; -+ NTSTATUS ret; -+ int device; -+ -+ if ((device = get_fast_sync_device()) < 0) -+ return STATUS_NOT_IMPLEMENTED; -+ -+ if ((ret = get_fast_sync_obj( handle, FAST_SYNC_SEMAPHORE, -+ SEMAPHORE_QUERY_STATE, &stack_cache, &cache ))) -+ return ret; -+ -+ ret = fast_query_semaphore_obj( device, cache->obj, info ); -+ -+ release_fast_sync_obj( cache ); -+ return ret; -+} -+ -+ -+static NTSTATUS fast_set_event_obj( int device, unsigned int obj, LONG *prev_state ) -+{ -+ struct winesync_sem_args args = {0}; -+ NTSTATUS ret; -+ -+ args.sem = obj; -+ args.count = 1; -+ ret = ioctl( device, WINESYNC_IOC_PUT_SEM, &args ); -+ if (ret < 0) -+ { -+ if (errno == EOVERFLOW) -+ { -+ if (prev_state) *prev_state = 1; -+ return STATUS_SUCCESS; -+ } -+ else -+ return errno_to_status( errno ); -+ } -+ if (prev_state) *prev_state = 0; -+ return STATUS_SUCCESS; -+} -+ -+ -+static NTSTATUS fast_set_event( HANDLE handle, LONG *prev_state ) -+{ -+ struct fast_sync_cache_entry stack_cache, *cache; -+ NTSTATUS ret; -+ int device; -+ -+ if ((device = get_fast_sync_device()) < 0) -+ return STATUS_NOT_IMPLEMENTED; -+ -+ if ((ret = get_fast_sync_obj( handle, FAST_SYNC_EVENT, EVENT_MODIFY_STATE, -+ &stack_cache, &cache ))) -+ return ret; -+ -+ ret = fast_set_event_obj( device, cache->obj, prev_state ); -+ -+ release_fast_sync_obj( cache ); -+ return ret; -+} -+ -+ -+static NTSTATUS fast_reset_event_obj( int device, unsigned int obj, LONG *prev_state ) -+{ -+ NTSTATUS ret; -+ -+ ret = ioctl( device, WINESYNC_IOC_GET_SEM, &obj ); -+ if (ret < 0) -+ { -+ if (errno == EWOULDBLOCK) -+ { -+ if (prev_state) *prev_state = 0; -+ return STATUS_SUCCESS; -+ } -+ else -+ return errno_to_status( errno ); -+ } -+ if (prev_state) *prev_state = 1; -+ return STATUS_SUCCESS; -+} -+ -+ -+static NTSTATUS fast_reset_event( HANDLE handle, LONG *prev_state ) -+{ -+ struct fast_sync_cache_entry stack_cache, *cache; -+ NTSTATUS ret; -+ int device; -+ -+ if ((device = get_fast_sync_device()) < 0) -+ return STATUS_NOT_IMPLEMENTED; -+ -+ if ((ret = get_fast_sync_obj( handle, FAST_SYNC_EVENT, EVENT_MODIFY_STATE, -+ &stack_cache, &cache ))) -+ return ret; -+ -+ ret = fast_reset_event_obj( device, cache->obj, prev_state ); -+ -+ release_fast_sync_obj( cache ); -+ return ret; -+} -+ -+ -+static NTSTATUS fast_pulse_event_obj( int device, unsigned int obj, LONG *prev_state ) -+{ -+ struct winesync_sem_args args = {0}; -+ NTSTATUS ret; -+ -+ args.sem = obj; -+ args.count = 1; -+ ret = ioctl( device, WINESYNC_IOC_PULSE_SEM, &args ); -+ if (ret < 0) -+ { -+ if (errno == EOVERFLOW) -+ { -+ if (prev_state) *prev_state = 1; -+ return STATUS_SUCCESS; -+ } -+ else -+ return errno_to_status( errno ); -+ } -+ if (prev_state) *prev_state = 0; -+ return STATUS_SUCCESS; -+} -+ -+ -+static NTSTATUS fast_pulse_event( HANDLE handle, LONG *prev_state ) -+{ -+ struct fast_sync_cache_entry stack_cache, *cache; -+ NTSTATUS ret; -+ int device; -+ -+ if ((device = get_fast_sync_device()) < 0) -+ return STATUS_NOT_IMPLEMENTED; -+ -+ if ((ret = get_fast_sync_obj( handle, FAST_SYNC_EVENT, EVENT_MODIFY_STATE, -+ &stack_cache, &cache ))) -+ return ret; -+ -+ ret = fast_pulse_event_obj( device, cache->obj, prev_state ); -+ -+ release_fast_sync_obj( cache ); -+ return ret; -+} -+ -+ -+static NTSTATUS fast_query_event_obj( int device, unsigned int obj, EVENT_BASIC_INFORMATION *info ) -+{ -+ struct winesync_sem_args args = {0}; -+ NTSTATUS ret; -+ -+ args.sem = obj; -+ ret = ioctl( device, WINESYNC_IOC_READ_SEM, &args ); -+ -+ if (ret < 0) -+ return errno_to_status( errno ); -+ info->EventType = (args.flags & WINESYNC_SEM_GETONWAIT) ? SynchronizationEvent : NotificationEvent; -+ info->EventState = args.count; -+ return STATUS_SUCCESS; -+} -+ -+ -+static NTSTATUS fast_query_event( HANDLE handle, EVENT_BASIC_INFORMATION *info ) -+{ -+ struct fast_sync_cache_entry stack_cache, *cache; -+ NTSTATUS ret; -+ int device; -+ -+ if ((device = get_fast_sync_device()) < 0) -+ return STATUS_NOT_IMPLEMENTED; -+ -+ if ((ret = get_fast_sync_obj( handle, FAST_SYNC_EVENT, EVENT_QUERY_STATE, -+ &stack_cache, &cache ))) -+ return ret; -+ -+ ret = fast_query_event_obj( device, cache->obj, info ); -+ -+ release_fast_sync_obj( cache ); -+ return ret; -+} -+ -+ -+static NTSTATUS fast_release_mutex_obj( int device, unsigned int obj, LONG *prev_count ) -+{ -+ struct winesync_mutex_args args = {0}; -+ NTSTATUS ret; -+ -+ args.mutex = obj; -+ args.owner = GetCurrentThreadId(); -+ ret = ioctl( device, WINESYNC_IOC_PUT_MUTEX, &args ); -+ -+ if (ret < 0) -+ { -+ if (errno == EOVERFLOW) -+ return STATUS_MUTANT_LIMIT_EXCEEDED; -+ else if (errno == EPERM) -+ return STATUS_MUTANT_NOT_OWNED; -+ else -+ return errno_to_status( errno ); -+ } -+ if (prev_count) *prev_count = 1 - args.count; -+ return STATUS_SUCCESS; -+} -+ -+ -+static NTSTATUS fast_release_mutex( HANDLE handle, LONG *prev_count ) -+{ -+ struct fast_sync_cache_entry stack_cache, *cache; -+ NTSTATUS ret; -+ int device; -+ -+ if ((device = get_fast_sync_device()) < 0) -+ return STATUS_NOT_IMPLEMENTED; -+ -+ if ((ret = get_fast_sync_obj( handle, FAST_SYNC_MUTEX, 0, &stack_cache, &cache ))) -+ return ret; -+ -+ ret = fast_release_mutex_obj( device, cache->obj, prev_count ); -+ -+ release_fast_sync_obj( cache ); -+ return ret; -+} -+ -+ -+static NTSTATUS fast_query_mutex_obj( int device, unsigned int obj, MUTANT_BASIC_INFORMATION *info ) -+{ -+ struct winesync_mutex_args args = {0}; -+ NTSTATUS ret; -+ -+ args.mutex = obj; -+ ret = ioctl( device, WINESYNC_IOC_READ_MUTEX, &args ); -+ -+ if (ret < 0) -+ { -+ if (errno == EOWNERDEAD) -+ { -+ info->AbandonedState = TRUE; -+ info->OwnedByCaller = FALSE; -+ info->CurrentCount = 1; -+ return STATUS_SUCCESS; -+ } -+ else -+ return errno_to_status( errno ); -+ } -+ info->AbandonedState = FALSE; -+ info->OwnedByCaller = (args.owner == GetCurrentThreadId()); -+ info->CurrentCount = 1 - args.count; -+ return STATUS_SUCCESS; -+} -+ -+ -+static NTSTATUS fast_query_mutex( HANDLE handle, MUTANT_BASIC_INFORMATION *info ) -+{ -+ struct fast_sync_cache_entry stack_cache, *cache; -+ NTSTATUS ret; -+ int device; -+ -+ if ((device = get_fast_sync_device()) < 0) -+ return STATUS_NOT_IMPLEMENTED; -+ -+ if ((ret = get_fast_sync_obj( handle, FAST_SYNC_MUTEX, MUTANT_QUERY_STATE, -+ &stack_cache, &cache ))) -+ return ret; -+ -+ ret = fast_query_mutex_obj( device, cache->obj, info ); -+ -+ release_fast_sync_obj( cache ); -+ return ret; -+} -+ -+struct timespec64 -+{ -+ __s64 tv_sec; -+ __s64 tv_nsec; -+}; -+ -+static void timespec64_from_timeout( struct timespec64 *timespec, const LARGE_INTEGER *timeout ) -+{ -+ struct timespec now; -+ timeout_t relative; -+ -+ clock_gettime( CLOCK_MONOTONIC, &now ); -+ -+ if (timeout->QuadPart <= 0) -+ { -+ relative = -timeout->QuadPart; -+ } -+ else -+ { -+ LARGE_INTEGER system_now; -+ -+ /* the system clock is probably REALTIME, so we need to convert to -+ * relative time first */ -+ NtQuerySystemTime( &system_now ); -+ relative = timeout->QuadPart - system_now.QuadPart; -+ } -+ -+ timespec->tv_sec = now.tv_sec + (relative / TICKSPERSEC); -+ timespec->tv_nsec = now.tv_nsec + ((relative % TICKSPERSEC) * 100); -+ if (timespec->tv_nsec >= 1000000000) -+ { -+ timespec->tv_nsec -= 1000000000; -+ ++timespec->tv_sec; -+ } -+} -+ -+static void select_queue( HANDLE queue ) -+{ -+ SERVER_START_REQ( fast_select_queue ) -+ { -+ req->handle = wine_server_obj_handle( queue ); -+ wine_server_call( req ); -+ } -+ SERVER_END_REQ; -+} -+ -+static void unselect_queue( HANDLE queue, BOOL signaled ) -+{ -+ SERVER_START_REQ( fast_unselect_queue ) -+ { -+ req->handle = wine_server_obj_handle( queue ); -+ req->signaled = signaled; -+ wine_server_call( req ); -+ } -+ SERVER_END_REQ; -+} -+ -+static NTSTATUS fast_wait_objs( int device, DWORD count, const unsigned int *objs, BOOLEAN wait_any, -+ BOOLEAN alertable, const LARGE_INTEGER *timeout, user_apc_t *apc ) -+{ -+ volatile struct winesync_wait_args args = {0}; -+ struct timespec64 timespec; -+ uintptr_t timeout_ptr = 0; -+ unsigned long request; -+ NTSTATUS ret; -+ -+ if (timeout && timeout->QuadPart != TIMEOUT_INFINITE) -+ { -+ timeout_ptr = (uintptr_t)×pec; -+ timespec64_from_timeout( ×pec, timeout ); -+ } -+ args.objs = (uintptr_t)objs; -+ args.count = count; -+ args.owner = GetCurrentThreadId(); -+ args.index = ~0u; -+ -+ if (wait_any || count == 1) -+ request = WINESYNC_IOC_WAIT_ANY; -+ else -+ request = WINESYNC_IOC_WAIT_ALL; -+ -+ if (alertable) -+ { -+ struct timespec64 now64; -+ struct timespec now; -+ -+ /* if there is an already signaled object and an APC available, the -+ * object is returned first */ -+ clock_gettime( CLOCK_MONOTONIC, &now ); -+ now64.tv_sec = now.tv_sec; -+ now64.tv_nsec = now.tv_nsec; -+ args.timeout = (uintptr_t)&now64; -+ do -+ { -+ ret = ioctl( device, request, &args ); -+ } while (ret < 0 && errno == EINTR); -+ -+ if (ret < 0 && errno == ETIMEDOUT) -+ { -+ args.timeout = timeout_ptr; -+ -+ /* When a user APC is queued to this thread, the server wakes us -+ * with SIGUSR1, whereupon usr1_handler() will longjmp here, causing -+ * us to poll for a user APC again. It's not enough simply to retry -+ * on EINTR, as we might get SIGUSR1 after checking for user APCs -+ * but before calling ioctl(). */ -+ -+ sigsetjmp( ntdll_get_thread_data()->fast_alert_buf, 1 ); -+ -+ /* If the signal arrives *after* the ioctl, and the wait succeeded, -+ * we don't want to wait again. */ -+ -+ if (args.index != ~0u) -+ { -+ ntdll_get_thread_data()->in_fast_alert_wait = 0; -+ ret = 0; -+ goto out; -+ } -+ -+ ntdll_get_thread_data()->in_fast_alert_wait = 1; -+ -+ SERVER_START_REQ( check_user_apc ) -+ { -+ ret = wine_server_call( req ); -+ } -+ SERVER_END_REQ; -+ -+ if (!ret) -+ { -+ ntdll_get_thread_data()->in_fast_alert_wait = 0; -+ -+ /* Retrieve the user APC. We can't actually dequeue it until -+ * after we reset in_fast_alert_wait, as otherwise we could -+ * have the thread context changed on us and drop the APC data -+ * on the floor. */ -+ ret = server_select( NULL, 0, SELECT_INTERRUPTIBLE | SELECT_ALERTABLE, -+ 0, NULL, NULL, apc ); -+ assert( ret == STATUS_USER_APC ); -+ return ret; -+ } -+ -+ do -+ { -+ ret = ioctl( device, request, &args ); -+ } while (ret < 0 && errno == EINTR); -+ -+ ntdll_get_thread_data()->in_fast_alert_wait = 0; -+ } -+ } -+ else - { -- futex_wait( &supported, 10, NULL ); -- if (errno == ENOSYS) -+ args.timeout = timeout_ptr; -+ do - { -- futex_private = 0; -- futex_wait( &supported, 10, NULL ); -- } -- supported = (errno != ENOSYS); -+ ret = ioctl( device, request, &args ); -+ } while (ret < 0 && errno == EINTR); - } -- return supported; --} - --static int *get_futex(void **ptr) --{ -- if (sizeof(void *) == 8) -- return (int *)((((ULONG_PTR)ptr) + 3) & ~3); -- else if (!(((ULONG_PTR)ptr) & 3)) -- return (int *)ptr; -+out: -+ if (!ret) -+ return wait_any ? args.index : 0; -+ else if (errno == EOWNERDEAD) -+ return STATUS_ABANDONED + (wait_any ? args.index : 0); -+ else if (errno == ETIMEDOUT) -+ return STATUS_TIMEOUT; - else -- return NULL; -+ return errno_to_status( errno ); - } - --static void timespec_from_timeout( struct timespec *timespec, const LARGE_INTEGER *timeout ) -+static NTSTATUS fast_wait( DWORD count, const HANDLE *handles, BOOLEAN wait_any, -+ BOOLEAN alertable, const LARGE_INTEGER *timeout ) - { -- LARGE_INTEGER now; -- timeout_t diff; -+ struct fast_sync_cache_entry stack_cache[64], *cache[64]; -+ unsigned int objs[64]; -+ HANDLE queue = NULL; -+ user_apc_t apc; -+ NTSTATUS ret; -+ DWORD i, j; -+ int device; - -- if (timeout->QuadPart > 0) -+ if ((device = get_fast_sync_device()) < 0) -+ return STATUS_NOT_IMPLEMENTED; -+ -+ for (i = 0; i < count; ++i) - { -- NtQuerySystemTime( &now ); -- diff = timeout->QuadPart - now.QuadPart; -+ if ((ret = get_fast_sync_obj( handles[i], 0, SYNCHRONIZE, &stack_cache[i], &cache[i] ))) -+ { -+ for (j = 0; j < i; ++j) -+ release_fast_sync_obj( cache[j] ); -+ return ret; -+ } -+ if (cache[i]->type == FAST_SYNC_QUEUE) -+ queue = handles[i]; -+ -+ objs[i] = cache[i]->obj; - } -- else -- diff = -timeout->QuadPart; - -- timespec->tv_sec = diff / TICKSPERSEC; -- timespec->tv_nsec = (diff % TICKSPERSEC) * 100; --} -+ /* It's common to wait on the message queue alone. Some applications wait -+ * on it in fast paths, with a zero timeout. Since we take two server calls -+ * instead of one when going through fast_wait_objs(), and since we only -+ * need to go through that path if we're waiting on other objects, just -+ * delegate to the server if we're only waiting on the message queue. */ -+ if (count == 1 && queue) -+ { -+ release_fast_sync_obj( cache[0] ); -+ return server_wait_for_object( handles[0], alertable, timeout ); -+ } - --#endif -+ if (queue) select_queue( queue ); - -+ ret = fast_wait_objs( device, count, objs, wait_any, alertable, timeout, &apc ); - --static BOOL compare_addr( const void *addr, const void *cmp, SIZE_T size ) --{ -- switch (size) -- { -- case 1: -- return (*(const UCHAR *)addr == *(const UCHAR *)cmp); -- case 2: -- return (*(const USHORT *)addr == *(const USHORT *)cmp); -- case 4: -- return (*(const ULONG *)addr == *(const ULONG *)cmp); -- case 8: -- return (*(const ULONG64 *)addr == *(const ULONG64 *)cmp); -- } -+ if (queue) unselect_queue( queue, handles[ret] == queue ); - -- return FALSE; --} -+ for (i = 0; i < count; ++i) -+ release_fast_sync_obj( cache[i] ); - -+ if (ret == STATUS_USER_APC) -+ invoke_user_apc( NULL, &apc, ret ); -+ return ret; -+} - --/* create a struct security_descriptor and contained information in one contiguous piece of memory */ --NTSTATUS alloc_object_attributes( const OBJECT_ATTRIBUTES *attr, struct object_attributes **ret, -- data_size_t *ret_len ) -+static NTSTATUS fast_signal_and_wait( HANDLE signal, HANDLE wait, -+ BOOLEAN alertable, const LARGE_INTEGER *timeout ) - { -- unsigned int len = sizeof(**ret); -- SID *owner = NULL, *group = NULL; -- ACL *dacl = NULL, *sacl = NULL; -- SECURITY_DESCRIPTOR *sd; -- -- *ret = NULL; -- *ret_len = 0; -+ struct fast_sync_cache_entry signal_stack_cache, *signal_cache; -+ struct fast_sync_cache_entry wait_stack_cache, *wait_cache; -+ HANDLE queue = NULL; -+ user_apc_t apc; -+ NTSTATUS ret; -+ int device; - -- if (!attr) return STATUS_SUCCESS; -+ if ((device = get_fast_sync_device()) < 0) -+ return STATUS_NOT_IMPLEMENTED; - -- if (attr->Length != sizeof(*attr)) return STATUS_INVALID_PARAMETER; -+ if ((ret = get_fast_sync_obj( signal, 0, 0, &signal_stack_cache, &signal_cache ))) -+ return ret; - -- if ((sd = attr->SecurityDescriptor)) -+ switch (signal_cache->type) - { -- len += sizeof(struct security_descriptor); -- if (sd->Revision != SECURITY_DESCRIPTOR_REVISION) return STATUS_UNKNOWN_REVISION; -- if (sd->Control & SE_SELF_RELATIVE) -- { -- SECURITY_DESCRIPTOR_RELATIVE *rel = (SECURITY_DESCRIPTOR_RELATIVE *)sd; -- if (rel->Owner) owner = (PSID)((BYTE *)rel + rel->Owner); -- if (rel->Group) group = (PSID)((BYTE *)rel + rel->Group); -- if ((sd->Control & SE_SACL_PRESENT) && rel->Sacl) sacl = (PSID)((BYTE *)rel + rel->Sacl); -- if ((sd->Control & SE_DACL_PRESENT) && rel->Dacl) dacl = (PSID)((BYTE *)rel + rel->Dacl); -- } -- else -- { -- owner = sd->Owner; -- group = sd->Group; -- if (sd->Control & SE_SACL_PRESENT) sacl = sd->Sacl; -- if (sd->Control & SE_DACL_PRESENT) dacl = sd->Dacl; -- } -+ case FAST_SYNC_SEMAPHORE: -+ if (!(signal_cache->access & SEMAPHORE_MODIFY_STATE)) -+ { -+ release_fast_sync_obj( signal_cache ); -+ return STATUS_ACCESS_DENIED; -+ } -+ break; - -- if (owner) len += offsetof( SID, SubAuthority[owner->SubAuthorityCount] ); -- if (group) len += offsetof( SID, SubAuthority[group->SubAuthorityCount] ); -- if (sacl) len += sacl->AclSize; -- if (dacl) len += dacl->AclSize; -+ case FAST_SYNC_EVENT: -+ if (!(signal_cache->access & EVENT_MODIFY_STATE)) -+ { -+ release_fast_sync_obj( signal_cache ); -+ return STATUS_ACCESS_DENIED; -+ } -+ break; - -- /* fix alignment for the Unicode name that follows the structure */ -- len = (len + sizeof(WCHAR) - 1) & ~(sizeof(WCHAR) - 1); -+ case FAST_SYNC_MUTEX: -+ break; -+ -+ default: -+ /* can't be signaled */ -+ release_fast_sync_obj( signal_cache ); -+ return STATUS_OBJECT_TYPE_MISMATCH; - } - -- if (attr->ObjectName) -+ if ((ret = get_fast_sync_obj( wait, 0, SYNCHRONIZE, &wait_stack_cache, &wait_cache ))) - { -- if (attr->ObjectName->Length & (sizeof(WCHAR) - 1)) return STATUS_OBJECT_NAME_INVALID; -- len += attr->ObjectName->Length; -+ release_fast_sync_obj( signal_cache ); -+ return ret; - } -- else if (attr->RootDirectory) return STATUS_OBJECT_NAME_INVALID; -- -- len = (len + 3) & ~3; /* DWORD-align the entire structure */ -- -- if (!(*ret = calloc( len, 1 ))) return STATUS_NO_MEMORY; - -- (*ret)->rootdir = wine_server_obj_handle( attr->RootDirectory ); -- (*ret)->attributes = attr->Attributes; -+ if (wait_cache->type == FAST_SYNC_QUEUE) -+ queue = wait; - -- if (attr->SecurityDescriptor) -+ switch (signal_cache->type) - { -- struct security_descriptor *descr = (struct security_descriptor *)(*ret + 1); -- unsigned char *ptr = (unsigned char *)(descr + 1); -+ case FAST_SYNC_SEMAPHORE: -+ ret = fast_release_semaphore_obj( device, signal_cache->obj, 1, NULL ); -+ break; - -- descr->control = sd->Control & ~SE_SELF_RELATIVE; -- if (owner) descr->owner_len = offsetof( SID, SubAuthority[owner->SubAuthorityCount] ); -- if (group) descr->group_len = offsetof( SID, SubAuthority[group->SubAuthorityCount] ); -- if (sacl) descr->sacl_len = sacl->AclSize; -- if (dacl) descr->dacl_len = dacl->AclSize; -+ case FAST_SYNC_EVENT: -+ ret = fast_set_event_obj( device, signal_cache->obj, NULL ); -+ break; - -- memcpy( ptr, owner, descr->owner_len ); -- ptr += descr->owner_len; -- memcpy( ptr, group, descr->group_len ); -- ptr += descr->group_len; -- memcpy( ptr, sacl, descr->sacl_len ); -- ptr += descr->sacl_len; -- memcpy( ptr, dacl, descr->dacl_len ); -- (*ret)->sd_len = (sizeof(*descr) + descr->owner_len + descr->group_len + descr->sacl_len + -- descr->dacl_len + sizeof(WCHAR) - 1) & ~(sizeof(WCHAR) - 1); -+ case FAST_SYNC_MUTEX: -+ ret = fast_release_mutex_obj( device, signal_cache->obj, NULL ); -+ break; -+ -+ default: -+ assert( 0 ); -+ break; - } - -- if (attr->ObjectName) -+ if (!ret) - { -- unsigned char *ptr = (unsigned char *)(*ret + 1) + (*ret)->sd_len; -- (*ret)->name_len = attr->ObjectName->Length; -- memcpy( ptr, attr->ObjectName->Buffer, (*ret)->name_len ); -+ if (queue) select_queue( queue ); -+ -+ ret = fast_wait_objs( device, 1, &wait_cache->obj, TRUE, alertable, timeout, &apc ); -+ -+ if (queue) unselect_queue( queue, !ret ); - } - -- *ret_len = len; -- return STATUS_SUCCESS; -+ release_fast_sync_obj( signal_cache ); -+ release_fast_sync_obj( wait_cache ); -+ -+ if (ret == STATUS_USER_APC) -+ invoke_user_apc( NULL, &apc, ret ); -+ return ret; - } - -+#else - --static NTSTATUS validate_open_object_attributes( const OBJECT_ATTRIBUTES *attr ) -+void close_fast_sync_obj( HANDLE handle ) - { -- if (!attr || attr->Length != sizeof(*attr)) return STATUS_INVALID_PARAMETER; -+} - -- if (attr->ObjectName) -- { -- if (attr->ObjectName->Length & (sizeof(WCHAR) - 1)) return STATUS_OBJECT_NAME_INVALID; -- } -- else if (attr->RootDirectory) return STATUS_OBJECT_NAME_INVALID; -+static NTSTATUS fast_release_semaphore( HANDLE handle, ULONG count, ULONG *prev_count ) -+{ -+ return STATUS_NOT_IMPLEMENTED; -+} - -- return STATUS_SUCCESS; -+static NTSTATUS fast_query_semaphore( HANDLE handle, SEMAPHORE_BASIC_INFORMATION *info ) -+{ -+ return STATUS_NOT_IMPLEMENTED; -+} -+ -+static NTSTATUS fast_set_event( HANDLE handle, LONG *prev_state ) -+{ -+ return STATUS_NOT_IMPLEMENTED; -+} -+ -+static NTSTATUS fast_reset_event( HANDLE handle, LONG *prev_state ) -+{ -+ return STATUS_NOT_IMPLEMENTED; -+} -+ -+static NTSTATUS fast_pulse_event( HANDLE handle, LONG *prev_state ) -+{ -+ return STATUS_NOT_IMPLEMENTED; -+} -+ -+static NTSTATUS fast_query_event( HANDLE handle, EVENT_BASIC_INFORMATION *info ) -+{ -+ return STATUS_NOT_IMPLEMENTED; -+} -+ -+static NTSTATUS fast_release_mutex( HANDLE handle, LONG *prev_count ) -+{ -+ return STATUS_NOT_IMPLEMENTED; -+} -+ -+static NTSTATUS fast_query_mutex( HANDLE handle, MUTANT_BASIC_INFORMATION *info ) -+{ -+ return STATUS_NOT_IMPLEMENTED; - } - -+static NTSTATUS fast_wait( DWORD count, const HANDLE *handles, BOOLEAN wait_any, -+ BOOLEAN alertable, const LARGE_INTEGER *timeout ) -+{ -+ return STATUS_NOT_IMPLEMENTED; -+} -+ -+static NTSTATUS fast_signal_and_wait( HANDLE signal, HANDLE wait, -+ BOOLEAN alertable, const LARGE_INTEGER *timeout ) -+{ -+ return STATUS_NOT_IMPLEMENTED; -+} -+ -+#endif -+ - - /****************************************************************************** - * NtCreateSemaphore (NTDLL.@) -@@ -319,6 +1350,9 @@ NTSTATUS WINAPI NtCreateSemaphore( HANDLE *handle, ACCESS_MASK access, const OBJ - data_size_t len; - struct object_attributes *objattr; - -+ TRACE( "access %#x, name %s, initial %d, max %d\n", access, -+ attr ? debugstr_us(attr->ObjectName) : "(null)", initial, max ); -+ - if (max <= 0 || initial < 0 || initial > max) return STATUS_INVALID_PARAMETER; - if ((ret = alloc_object_attributes( attr, &objattr, &len ))) return ret; - -@@ -345,6 +1379,8 @@ NTSTATUS WINAPI NtOpenSemaphore( HANDLE *handle, ACCESS_MASK access, const OBJEC - { - NTSTATUS ret; - -+ TRACE( "access %#x, name %s\n", access, attr ? debugstr_us(attr->ObjectName) : "(null)" ); -+ - if ((ret = validate_open_object_attributes( attr ))) return ret; - - SERVER_START_REQ( open_semaphore ) -@@ -381,6 +1417,12 @@ NTSTATUS WINAPI NtQuerySemaphore( HANDLE handle, SEMAPHORE_INFORMATION_CLASS cla - - if (len != sizeof(SEMAPHORE_BASIC_INFORMATION)) return STATUS_INFO_LENGTH_MISMATCH; - -+ if ((ret = fast_query_semaphore( handle, out )) != STATUS_NOT_IMPLEMENTED) -+ { -+ if (!ret && ret_len) *ret_len = sizeof(SEMAPHORE_BASIC_INFORMATION); -+ return ret; -+ } -+ - SERVER_START_REQ( query_semaphore ) - { - req->handle = wine_server_obj_handle( handle ); -@@ -403,6 +1445,11 @@ NTSTATUS WINAPI NtReleaseSemaphore( HANDLE handle, ULONG count, ULONG *previous - { - NTSTATUS ret; - -+ TRACE( "handle %p, count %u, prev_count %p\n", handle, count, previous ); -+ -+ if ((ret = fast_release_semaphore( handle, count, previous )) != STATUS_NOT_IMPLEMENTED) -+ return ret; -+ - SERVER_START_REQ( release_semaphore ) - { - req->handle = wine_server_obj_handle( handle ); -@@ -427,6 +1474,9 @@ NTSTATUS WINAPI NtCreateEvent( HANDLE *handle, ACCESS_MASK access, const OBJECT_ - data_size_t len; - struct object_attributes *objattr; - -+ TRACE( "access %#x, name %s, type %u, state %u\n", access, -+ attr ? debugstr_us(attr->ObjectName) : "(null)", type, state ); -+ - if (type != NotificationEvent && type != SynchronizationEvent) return STATUS_INVALID_PARAMETER; - if ((ret = alloc_object_attributes( attr, &objattr, &len ))) return ret; - -@@ -453,6 +1503,8 @@ NTSTATUS WINAPI NtOpenEvent( HANDLE *handle, ACCESS_MASK access, const OBJECT_AT - { - NTSTATUS ret; - -+ TRACE( "access %#x, name %s\n", access, attr ? debugstr_us(attr->ObjectName) : "(null)" ); -+ - if ((ret = validate_open_object_attributes( attr ))) return ret; - - SERVER_START_REQ( open_event ) -@@ -477,6 +1529,11 @@ NTSTATUS WINAPI NtSetEvent( HANDLE handle, LONG *prev_state ) - { - NTSTATUS ret; - -+ TRACE( "handle %p, prev_state %p\n", handle, prev_state ); -+ -+ if ((ret = fast_set_event( handle, prev_state )) != STATUS_NOT_IMPLEMENTED) -+ return ret; -+ - SERVER_START_REQ( event_op ) - { - req->handle = wine_server_obj_handle( handle ); -@@ -496,6 +1553,11 @@ NTSTATUS WINAPI NtResetEvent( HANDLE handle, LONG *prev_state ) - { - NTSTATUS ret; - -+ TRACE( "handle %p, prev_state %p\n", handle, prev_state ); -+ -+ if ((ret = fast_reset_event( handle, prev_state )) != STATUS_NOT_IMPLEMENTED) -+ return ret; -+ - SERVER_START_REQ( event_op ) - { - req->handle = wine_server_obj_handle( handle ); -@@ -525,6 +1587,11 @@ NTSTATUS WINAPI NtPulseEvent( HANDLE handle, LONG *prev_state ) - { - NTSTATUS ret; - -+ TRACE( "handle %p, prev_state %p\n", handle, prev_state ); -+ -+ if ((ret = fast_pulse_event( handle, prev_state )) != STATUS_NOT_IMPLEMENTED) -+ return ret; -+ - SERVER_START_REQ( event_op ) - { - req->handle = wine_server_obj_handle( handle ); -@@ -557,6 +1624,12 @@ NTSTATUS WINAPI NtQueryEvent( HANDLE handle, EVENT_INFORMATION_CLASS class, - - if (len != sizeof(EVENT_BASIC_INFORMATION)) return STATUS_INFO_LENGTH_MISMATCH; - -+ if ((ret = fast_query_event( handle, out )) != STATUS_NOT_IMPLEMENTED) -+ { -+ if (!ret && ret_len) *ret_len = sizeof(EVENT_BASIC_INFORMATION); -+ return ret; -+ } -+ - SERVER_START_REQ( query_event ) - { - req->handle = wine_server_obj_handle( handle ); -@@ -582,6 +1655,9 @@ NTSTATUS WINAPI NtCreateMutant( HANDLE *handle, ACCESS_MASK access, const OBJECT - data_size_t len; - struct object_attributes *objattr; - -+ TRACE( "access %#x, name %s, owned %u\n", access, -+ attr ? debugstr_us(attr->ObjectName) : "(null)", owned ); -+ - if ((ret = alloc_object_attributes( attr, &objattr, &len ))) return ret; - - SERVER_START_REQ( create_mutex ) -@@ -606,6 +1682,8 @@ NTSTATUS WINAPI NtOpenMutant( HANDLE *handle, ACCESS_MASK access, const OBJECT_A - { - NTSTATUS ret; - -+ TRACE( "access %#x, name %s\n", access, attr ? debugstr_us(attr->ObjectName) : "(null)" ); -+ - if ((ret = validate_open_object_attributes( attr ))) return ret; - - SERVER_START_REQ( open_mutex ) -@@ -630,6 +1708,11 @@ NTSTATUS WINAPI NtReleaseMutant( HANDLE handle, LONG *prev_count ) - { - NTSTATUS ret; - -+ TRACE( "handle %p, prev_count %p\n", handle, prev_count ); -+ -+ if ((ret = fast_release_mutex( handle, prev_count )) != STATUS_NOT_IMPLEMENTED) -+ return ret; -+ - SERVER_START_REQ( release_mutex ) - { - req->handle = wine_server_obj_handle( handle ); -@@ -660,6 +1743,12 @@ NTSTATUS WINAPI NtQueryMutant( HANDLE handle, MUTANT_INFORMATION_CLASS class, - - if (len != sizeof(MUTANT_BASIC_INFORMATION)) return STATUS_INFO_LENGTH_MISMATCH; - -+ if ((ret = fast_query_mutex( handle, out )) != STATUS_NOT_IMPLEMENTED) -+ { -+ if (!ret && ret_len) *ret_len = sizeof(MUTANT_BASIC_INFORMATION); -+ return ret; -+ } -+ - SERVER_START_REQ( query_mutex ) - { - req->handle = wine_server_obj_handle( handle ); -@@ -1277,6 +2366,9 @@ NTSTATUS WINAPI NtCreateTimer( HANDLE *handle, ACCESS_MASK access, const OBJECT_ - data_size_t len; - struct object_attributes *objattr; - -+ TRACE( "access %#x, name %s, type %u\n", access, -+ attr ? debugstr_us(attr->ObjectName) : "(null)", type ); -+ - if (type != NotificationTimer && type != SynchronizationTimer) return STATUS_INVALID_PARAMETER; - - if ((ret = alloc_object_attributes( attr, &objattr, &len ))) return ret; -@@ -1304,6 +2396,8 @@ NTSTATUS WINAPI NtOpenTimer( HANDLE *handle, ACCESS_MASK access, const OBJECT_AT - { - NTSTATUS ret; - -+ TRACE( "access %#x, name %s\n", access, attr ? debugstr_us(attr->ObjectName) : "(null)" ); -+ - if ((ret = validate_open_object_attributes( attr ))) return ret; - - SERVER_START_REQ( open_timer ) -@@ -1356,6 +2450,8 @@ NTSTATUS WINAPI NtCancelTimer( HANDLE handle, BOOLEAN *state ) - { - NTSTATUS ret; - -+ TRACE( "handle %p, state %p\n", handle, state ); -+ - SERVER_START_REQ( cancel_timer ) - { - req->handle = wine_server_obj_handle( handle ); -@@ -1424,13 +2520,29 @@ NTSTATUS WINAPI NtWaitForMultipleObjects( DWORD count, const HANDLE *handles, BO - { - select_op_t select_op; - UINT i, flags = SELECT_INTERRUPTIBLE; -+ NTSTATUS ret; - - if (!count || count > MAXIMUM_WAIT_OBJECTS) return STATUS_INVALID_PARAMETER_1; - -+ if (TRACE_ON(sync)) -+ { -+ TRACE( "wait_any %u, alertable %u, handles {%p", wait_any, alertable, handles[0] ); -+ for (i = 1; i < count; i++) TRACE( ", %p", handles[i] ); -+ TRACE( "}, timeout %s\n", debugstr_timeout(timeout) ); -+ } -+ -+ if ((ret = fast_wait( count, handles, wait_any, alertable, timeout )) != STATUS_NOT_IMPLEMENTED) -+ { -+ TRACE( "-> %#x\n", ret ); -+ return ret; -+ } -+ - if (alertable) flags |= SELECT_ALERTABLE; - select_op.wait.op = wait_any ? SELECT_WAIT : SELECT_WAIT_ALL; - for (i = 0; i < count; i++) select_op.wait.handles[i] = wine_server_obj_handle( handles[i] ); -- return server_wait( &select_op, offsetof( select_op_t, wait.handles[count] ), flags, timeout ); -+ ret = server_wait( &select_op, offsetof( select_op_t, wait.handles[count] ), flags, timeout ); -+ TRACE( "-> %#x\n", ret ); -+ return ret; - } - - -@@ -1451,9 +2563,15 @@ NTSTATUS WINAPI NtSignalAndWaitForSingleObject( HANDLE signal, HANDLE wait, - { - select_op_t select_op; - UINT flags = SELECT_INTERRUPTIBLE; -+ NTSTATUS ret; -+ -+ TRACE( "signal %p, wait %p, alertable %u, timeout %s\n", signal, wait, alertable, debugstr_timeout(timeout) ); - - if (!signal) return STATUS_INVALID_HANDLE; - -+ if ((ret = fast_signal_and_wait( signal, wait, alertable, timeout )) != STATUS_NOT_IMPLEMENTED) -+ return ret; -+ - if (alertable) flags |= SELECT_ALERTABLE; - select_op.signal_and_wait.op = SELECT_SIGNAL_AND_WAIT; - select_op.signal_and_wait.wait = wine_server_obj_handle( wait ); -@@ -1481,6 +2599,8 @@ NTSTATUS WINAPI NtYieldExecution(void) - */ - NTSTATUS WINAPI NtDelayExecution( BOOLEAN alertable, const LARGE_INTEGER *timeout ) - { -+// TRACE( "alertable %u, timeout %s\n", alertable, debugstr_timeout(timeout) ); -+ - /* if alertable, we need to query the server */ - if (alertable) return server_wait( NULL, 0, SELECT_INTERRUPTIBLE | SELECT_ALERTABLE, timeout ); - -@@ -1651,6 +2771,9 @@ NTSTATUS WINAPI NtCreateKeyedEvent( HANDLE *handle, ACCESS_MASK access, - data_size_t len; - struct object_attributes *objattr; - -+ TRACE( "access %#x, name %s, flags %#x\n", access, -+ attr ? debugstr_us(attr->ObjectName) : "(null)", flags ); -+ - if ((ret = alloc_object_attributes( attr, &objattr, &len ))) return ret; - - SERVER_START_REQ( create_keyed_event ) -@@ -1674,6 +2797,8 @@ NTSTATUS WINAPI NtOpenKeyedEvent( HANDLE *handle, ACCESS_MASK access, const OBJE - { - NTSTATUS ret; - -+ TRACE( "access %#x, name %s\n", access, attr ? debugstr_us(attr->ObjectName) : "(null)" ); -+ - if ((ret = validate_open_object_attributes( attr ))) return ret; - - SERVER_START_REQ( open_keyed_event ) -@@ -1699,6 +2824,8 @@ NTSTATUS WINAPI NtWaitForKeyedEvent( HANDLE handle, const void *key, - select_op_t select_op; - UINT flags = SELECT_INTERRUPTIBLE; - -+ TRACE( "handle %p, key %p, alertable %u, timeout %s\n", handle, key, alertable, debugstr_timeout(timeout) ); -+ - if (!handle) handle = keyed_event; - if ((ULONG_PTR)key & 1) return STATUS_INVALID_PARAMETER_1; - if (alertable) flags |= SELECT_ALERTABLE; -@@ -1718,6 +2845,8 @@ NTSTATUS WINAPI NtReleaseKeyedEvent( HANDLE handle, const void *key, - select_op_t select_op; - UINT flags = SELECT_INTERRUPTIBLE; - -+ TRACE( "handle %p, key %p, alertable %u, timeout %s\n", handle, key, alertable, debugstr_timeout(timeout) ); -+ - if (!handle) handle = keyed_event; - if ((ULONG_PTR)key & 1) return STATUS_INVALID_PARAMETER_1; - if (alertable) flags |= SELECT_ALERTABLE; -diff --git a/dlls/ntdll/unix/thread.c b/dlls/ntdll/unix/thread.c -index 54b748e99cb..d7b850d8746 100644 ---- a/dlls/ntdll/unix/thread.c -+++ b/dlls/ntdll/unix/thread.c -@@ -1354,7 +1354,7 @@ NTSTATUS get_thread_context( HANDLE handle, void *context, BOOL *self, USHORT ma - - if (ret == STATUS_PENDING) - { -- NtWaitForSingleObject( context_handle, FALSE, NULL ); -+ server_wait_for_object( context_handle, FALSE, NULL ); - - SERVER_START_REQ( get_thread_context ) - { -diff --git a/dlls/ntdll/unix/unix_private.h b/dlls/ntdll/unix/unix_private.h -index 5ebbc6d702b..6e3bff4ba9e 100644 ---- a/dlls/ntdll/unix/unix_private.h -+++ b/dlls/ntdll/unix/unix_private.h -@@ -22,6 +22,7 @@ - #define __NTDLL_UNIX_PRIVATE_H - - #include -+#include - #include - #include "unixlib.h" - #include "wine/server.h" -@@ -69,6 +70,8 @@ struct ntdll_thread_data - PRTL_THREAD_START_ROUTINE start; /* thread entry point */ - void *param; /* thread entry point parameter */ - void *jmp_buf; /* setjmp buffer for exception handling */ -+ volatile int in_fast_alert_wait; /* are we currently in a fast alertable wait? */ -+ sigjmp_buf fast_alert_buf; /* setjmp buffer to restart a fast alertable wait */ - }; - - C_ASSERT( sizeof(struct ntdll_thread_data) <= sizeof(((TEB *)0)->GdiTebBatch) ); -@@ -168,5 +171,8 @@ extern NTSTATUS load_start_exe( WCHAR **image, void **module ) DECLSPEC_HIDDEN; - extern void start_server( BOOL debug ) DECLSPEC_HIDDEN; - -+extern pthread_mutex_t fd_cache_mutex DECLSPEC_HIDDEN; -+ -+extern NTSTATUS invoke_user_apc( CONTEXT *context, const user_apc_t *apc, NTSTATUS status ) DECLSPEC_HIDDEN; - extern unsigned int server_call_unlocked( void *req_ptr ) DECLSPEC_HIDDEN; - extern void server_enter_uninterrupted_section( pthread_mutex_t *mutex, sigset_t *sigset ) DECLSPEC_HIDDEN; - extern void server_leave_uninterrupted_section( pthread_mutex_t *mutex, sigset_t *sigset ) DECLSPEC_HIDDEN; -@@ -176,6 +182,8 @@ extern unsigned int server_select( const select_op_t *select_op, data_size_t siz - user_apc_t *user_apc ) DECLSPEC_HIDDEN; - extern unsigned int server_wait( const select_op_t *select_op, data_size_t size, UINT flags, - const LARGE_INTEGER *timeout ) DECLSPEC_HIDDEN; -+extern unsigned int server_wait_for_object( HANDLE handle, BOOL alertable, -+ const LARGE_INTEGER *timeout ) DECLSPEC_HIDDEN; - extern unsigned int server_queue_process_apc( HANDLE process, const apc_call_t *call, - apc_result_t *result ) DECLSPEC_HIDDEN; - extern int server_get_unix_fd( HANDLE handle, unsigned int wanted_access, int *unix_fd, -@@ -284,6 +292,8 @@ extern void add_completion( HANDLE handle, ULONG_PTR value, NTSTATUS status, ULO - - extern void dbg_init(void) DECLSPEC_HIDDEN; - -+extern void close_fast_sync_obj( HANDLE handle ) DECLSPEC_HIDDEN; -+ - extern NTSTATUS call_user_apc_dispatcher( CONTEXT *context_ptr, ULONG_PTR arg1, ULONG_PTR arg2, ULONG_PTR arg3, - PNTAPCFUNC func, NTSTATUS status ) DECLSPEC_HIDDEN; - extern NTSTATUS call_user_exception_dispatcher( EXCEPTION_RECORD *rec, CONTEXT *context ) DECLSPEC_HIDDEN; -diff --git a/include/config.h.in b/include/config.h.in -index 68122959035..c5e1d4a921b 100644 ---- a/include/config.h.in -+++ b/include/config.h.in -@@ -396,6 +396,9 @@ - /* Define to 1 if you have the header file. */ - #undef HAVE_LINUX_VIDEODEV2_H - -+/* Define to 1 if you have the header file. */ -+#undef HAVE_LINUX_WINESYNC_H -+ - /* Define to 1 if you have the `lstat' function. */ - #undef HAVE_LSTAT - -diff --git a/include/wine/server_protocol.h b/include/wine/server_protocol.h -index fe1f5394a72..0c877db8fde 100644 ---- a/include/wine/server_protocol.h -+++ b/include/wine/server_protocol.h -@@ -5448,6 +5448,84 @@ struct get_next_thread_reply - }; - - -+enum fast_sync_type -+{ -+ FAST_SYNC_SEMAPHORE = 1, -+ FAST_SYNC_MUTEX, -+ FAST_SYNC_EVENT, -+ FAST_SYNC_SERVER, -+ FAST_SYNC_QUEUE, -+}; -+ -+ -+ -+struct get_fast_sync_device_request -+{ -+ struct request_header __header; -+ char __pad_12[4]; -+}; -+struct get_fast_sync_device_reply -+{ -+ struct reply_header __header; -+ obj_handle_t handle; -+ char __pad_12[4]; -+}; -+ -+ -+ -+struct get_fast_sync_obj_request -+{ -+ struct request_header __header; -+ obj_handle_t handle; -+}; -+struct get_fast_sync_obj_reply -+{ -+ struct reply_header __header; -+ obj_handle_t handle; -+ int obj; -+ int type; -+ unsigned int access; -+}; -+ -+ -+ -+struct fast_select_queue_request -+{ -+ struct request_header __header; -+ obj_handle_t handle; -+}; -+struct fast_select_queue_reply -+{ -+ struct reply_header __header; -+}; -+ -+ -+ -+struct fast_unselect_queue_request -+{ -+ struct request_header __header; -+ obj_handle_t handle; -+ int signaled; -+ char __pad_20[4]; -+}; -+struct fast_unselect_queue_reply -+{ -+ struct reply_header __header; -+}; -+ -+ -+ -+struct check_user_apc_request -+{ -+ struct request_header __header; -+ char __pad_12[4]; -+}; -+struct check_user_apc_reply -+{ -+ struct reply_header __header; -+}; -+ -+ - enum request - { - REQ_new_process, -@@ -5726,6 +5804,11 @@ enum request - REQ_suspend_process, - REQ_resume_process, - REQ_get_next_thread, -+ REQ_get_fast_sync_device, -+ REQ_get_fast_sync_obj, -+ REQ_fast_select_queue, -+ REQ_fast_unselect_queue, -+ REQ_check_user_apc, - REQ_NB_REQUESTS - }; - -@@ -6009,6 +6092,11 @@ union generic_request - struct suspend_process_request suspend_process_request; - struct resume_process_request resume_process_request; - struct get_next_thread_request get_next_thread_request; -+ struct get_fast_sync_device_request get_fast_sync_device_request; -+ struct get_fast_sync_obj_request get_fast_sync_obj_request; -+ struct fast_select_queue_request fast_select_queue_request; -+ struct fast_unselect_queue_request fast_unselect_queue_request; -+ struct check_user_apc_request check_user_apc_request; - }; - union generic_reply - { -@@ -6290,6 +6378,11 @@ union generic_reply - struct suspend_process_reply suspend_process_reply; - struct resume_process_reply resume_process_reply; - struct get_next_thread_reply get_next_thread_reply; -+ struct get_fast_sync_device_reply get_fast_sync_device_reply; -+ struct get_fast_sync_obj_reply get_fast_sync_obj_reply; -+ struct fast_select_queue_reply fast_select_queue_reply; -+ struct fast_unselect_queue_reply fast_unselect_queue_reply; -+ struct check_user_apc_reply check_user_apc_reply; - }; - - /* ### protocol_version begin ### */ -diff --git a/server/Makefile.in b/server/Makefile.in -index 4264e3db108..7486ef5010d 100644 ---- a/server/Makefile.in -+++ b/server/Makefile.in -@@ -12,6 +12,7 @@ C_SRCS = \ - device.c \ - directory.c \ - event.c \ -+ fast_sync.c \ - fd.c \ - file.c \ - handle.c \ -diff --git a/server/async.c b/server/async.c -index 4dedb27f3d8..ad109fbbc05 100644 ---- a/server/async.c -+++ b/server/async.c -@@ -84,6 +84,7 @@ static const struct object_ops async_ops = - NULL, /* unlink_name */ - no_open_file, /* open_file */ - no_kernel_obj_list, /* get_kernel_obj_list */ -+ no_get_fast_sync, /* get_fast_sync */ - no_close_handle, /* close_handle */ - async_destroy /* destroy */ - }; -@@ -506,6 +507,7 @@ static const struct object_ops iosb_ops = - NULL, /* unlink_name */ - no_open_file, /* open_file */ - no_kernel_obj_list, /* get_kernel_obj_list */ -+ no_get_fast_sync, /* get_fast_sync */ - no_close_handle, /* close_handle */ - iosb_destroy /* destroy */ - }; -diff --git a/server/atom.c b/server/atom.c -index 8d0ffbb05f3..e638b064f49 100644 ---- a/server/atom.c -+++ b/server/atom.c -@@ -92,6 +92,7 @@ static const struct object_ops atom_table_ops = - NULL, /* unlink_name */ - no_open_file, /* open_file */ - no_kernel_obj_list, /* get_kernel_obj_list */ -+ no_get_fast_sync, /* get_fast_sync */ - no_close_handle, /* close_handle */ - atom_table_destroy /* destroy */ - }; -diff --git a/server/change.c b/server/change.c -index b02a9cd65bf..0eadae4e96d 100644 ---- a/server/change.c -+++ b/server/change.c -@@ -126,6 +126,7 @@ static const struct object_ops dir_ops = - NULL, /* unlink_name */ - no_open_file, /* open_file */ - no_kernel_obj_list, /* get_kernel_obj_list */ -+ default_fd_get_fast_sync, /* get_fast_sync */ - dir_close_handle, /* close_handle */ - dir_destroy /* destroy */ - }; -diff --git a/server/clipboard.c b/server/clipboard.c -index 1c4875ff726..1d9369deb52 100644 ---- a/server/clipboard.c -+++ b/server/clipboard.c -@@ -89,6 +89,7 @@ static const struct object_ops clipboard_ops = - NULL, /* unlink_name */ - no_open_file, /* open_file */ - no_kernel_obj_list, /* get_kernel_obj_list */ -+ no_get_fast_sync, /* get_fast_sync */ - no_close_handle, /* close_handle */ - clipboard_destroy /* destroy */ - }; -diff --git a/server/completion.c b/server/completion.c -index eb0d256ad09..776095b3900 100644 ---- a/server/completion.c -+++ b/server/completion.c -@@ -62,10 +62,12 @@ struct completion - struct object obj; - struct list queue; - unsigned int depth; -+ struct fast_sync *fast_sync; - }; - - static void completion_dump( struct object*, int ); - static int completion_signaled( struct object *obj, struct wait_queue_entry *entry ); -+static struct fast_sync *completion_get_fast_sync( struct object *obj ); - static void completion_destroy( struct object * ); - - static const struct object_ops completion_ops = -@@ -88,6 +90,7 @@ static const struct object_ops completion_ops = - default_unlink_name, /* unlink_name */ - no_open_file, /* open_file */ - no_kernel_obj_list, /* get_kernel_obj_list */ -+ completion_get_fast_sync, /* get_fast_sync */ - no_close_handle, /* close_handle */ - completion_destroy /* destroy */ - }; -@@ -110,6 +113,7 @@ static void completion_destroy( struct object *obj) - { - free( tmp ); - } -+ if (completion->fast_sync) release_object( completion->fast_sync ); - } - - static void completion_dump( struct object *obj, int verbose ) -@@ -127,6 +131,16 @@ static int completion_signaled( struct object *obj, struct wait_queue_entry *ent - return !list_empty( &completion->queue ); - } - -+static struct fast_sync *completion_get_fast_sync( struct object *obj ) -+{ -+ struct completion *completion = (struct completion *)obj; -+ -+ if (!completion->fast_sync) -+ completion->fast_sync = fast_create_event( FAST_SYNC_SERVER, 1, !list_empty( &completion->queue ) ); -+ if (completion->fast_sync) grab_object( completion->fast_sync ); -+ return completion->fast_sync; -+} -+ - static struct completion *create_completion( struct object *root, const struct unicode_str *name, - unsigned int attr, unsigned int concurrent, - const struct security_descriptor *sd ) -@@ -139,6 +153,7 @@ static struct completion *create_completion( struct object *root, const struct u - { - list_init( &completion->queue ); - completion->depth = 0; -+ completion->fast_sync = NULL; - } - } - -@@ -166,6 +181,7 @@ void add_completion( struct completion *completion, apc_param_t ckey, apc_param_ - list_add_tail( &completion->queue, &msg->queue_entry ); - completion->depth++; - wake_up( &completion->obj, 1 ); -+ fast_set_event( completion->fast_sync ); - } - - /* create a completion */ -@@ -232,6 +248,8 @@ DECL_HANDLER(remove_completion) - reply->status = msg->status; - reply->information = msg->information; - free( msg ); -+ if (list_empty( &completion->queue )) -+ fast_reset_event( completion->fast_sync ); - } - - release_object( completion ); -diff --git a/server/console.c b/server/console.c -index 1e6f6c0f8a3..a6d192394eb 100644 ---- a/server/console.c -+++ b/server/console.c -@@ -62,6 +62,7 @@ struct console - struct fd *fd; /* for bare console, attached input fd */ - struct async_queue ioctl_q; /* ioctl queue */ - struct async_queue read_q; /* read queue */ -+ struct fast_sync *fast_sync; /* fast synchronization object */ - }; - - static void console_dump( struct object *obj, int verbose ); -@@ -72,6 +73,7 @@ static struct object *console_lookup_name( struct object *obj, struct unicode_st - unsigned int attr, struct object *root ); - static struct object *console_open_file( struct object *obj, unsigned int access, - unsigned int sharing, unsigned int options ); -+static struct fast_sync *console_get_fast_sync( struct object *obj ); - - static const struct object_ops console_ops = - { -@@ -93,6 +95,7 @@ static const struct object_ops console_ops = - NULL, /* unlink_name */ - console_open_file, /* open_file */ - no_kernel_obj_list, /* get_kernel_obj_list */ -+ console_get_fast_sync, /* get_fast_sync */ - no_close_handle, /* close_handle */ - console_destroy /* destroy */ - }; -@@ -137,6 +140,7 @@ struct console_server - int busy; /* flag if server processing an ioctl */ - int term_fd; /* UNIX terminal fd */ - struct termios termios; /* original termios */ -+ struct fast_sync *fast_sync; /* fast synchronization object */ - }; - - static void console_server_dump( struct object *obj, int verbose ); -@@ -147,6 +151,7 @@ static struct object *console_server_lookup_name( struct object *obj, struct uni - unsigned int attr, struct object *root ); - static struct object *console_server_open_file( struct object *obj, unsigned int access, - unsigned int sharing, unsigned int options ); -+static struct fast_sync *console_server_get_fast_sync( struct object *obj ); - - static const struct object_ops console_server_ops = - { -@@ -168,6 +173,7 @@ static const struct object_ops console_server_ops = - NULL, /* unlink_name */ - console_server_open_file, /* open_file */ - no_kernel_obj_list, /* get_kernel_obj_list */ -+ console_server_get_fast_sync, /* get_fast_sync */ - no_close_handle, /* close_handle */ - console_server_destroy /* destroy */ - }; -@@ -215,6 +221,7 @@ static int screen_buffer_add_queue( struct object *obj, struct wait_queue_entry - static struct fd *screen_buffer_get_fd( struct object *obj ); - static struct object *screen_buffer_open_file( struct object *obj, unsigned int access, - unsigned int sharing, unsigned int options ); -+static struct fast_sync *screen_buffer_get_fast_sync( struct object *obj ); - - static const struct object_ops screen_buffer_ops = - { -@@ -236,6 +243,7 @@ static const struct object_ops screen_buffer_ops = - NULL, /* unlink_name */ - screen_buffer_open_file, /* open_file */ - no_kernel_obj_list, /* get_kernel_obj_list */ -+ screen_buffer_get_fast_sync, /* get_fast_sync */ - no_close_handle, /* close_handle */ - screen_buffer_destroy /* destroy */ - }; -@@ -284,6 +292,7 @@ static const struct object_ops console_device_ops = - default_unlink_name, /* unlink_name */ - console_device_open_file, /* open_file */ - no_kernel_obj_list, /* get_kernel_obj_list */ -+ no_get_fast_sync, /* get_fast_sync */ - no_close_handle, /* close_handle */ - no_destroy /* destroy */ - }; -@@ -299,6 +308,7 @@ static struct object *console_input_open_file( struct object *obj, unsigned int - unsigned int sharing, unsigned int options ); - static int console_input_add_queue( struct object *obj, struct wait_queue_entry *entry ); - static struct fd *console_input_get_fd( struct object *obj ); -+static struct fast_sync *console_input_get_fast_sync( struct object *obj ); - static void console_input_destroy( struct object *obj ); - - static const struct object_ops console_input_ops = -@@ -321,6 +331,7 @@ static const struct object_ops console_input_ops = - default_unlink_name, /* unlink_name */ - console_input_open_file, /* open_file */ - no_kernel_obj_list, /* get_kernel_obj_list */ -+ console_input_get_fast_sync, /* get_fast_sync */ - no_close_handle, /* close_handle */ - console_input_destroy /* destroy */ - }; -@@ -355,6 +366,7 @@ static int console_output_add_queue( struct object *obj, struct wait_queue_entry - static struct fd *console_output_get_fd( struct object *obj ); - static struct object *console_output_open_file( struct object *obj, unsigned int access, - unsigned int sharing, unsigned int options ); -+static struct fast_sync *console_output_get_fast_sync( struct object *obj ); - static void console_output_destroy( struct object *obj ); - - static const struct object_ops console_output_ops = -@@ -377,6 +389,7 @@ static const struct object_ops console_output_ops = - default_unlink_name, /* unlink_name */ - console_output_open_file, /* open_file */ - no_kernel_obj_list, /* get_kernel_obj_list */ -+ console_output_get_fast_sync, /* get_fast_sync */ - no_close_handle, /* close_handle */ - console_output_destroy /* destroy */ - }; -@@ -434,6 +447,7 @@ static const struct object_ops console_connection_ops = - default_unlink_name, /* unlink_name */ - console_connection_open_file, /* open_file */ - no_kernel_obj_list, /* get_kernel_obj_list */ -+ no_get_fast_sync, /* get_fast_sync */ - console_connection_close_handle, /* close_handle */ - console_connection_destroy /* destroy */ - }; -@@ -516,6 +530,7 @@ static struct object *create_console(void) - console->server = NULL; - console->fd = NULL; - console->last_id = 0; -+ console->fast_sync = NULL; - init_async_queue( &console->ioctl_q ); - init_async_queue( &console->read_q ); - -@@ -555,6 +570,7 @@ static int queue_host_ioctl( struct console_server *server, unsigned int code, u - } - list_add_tail( &server->queue, &ioctl->entry ); - wake_up( &server->obj, 0 ); -+ fast_set_event( server->fast_sync ); - if (async) set_error( STATUS_PENDING ); - return 1; - } -@@ -587,6 +603,7 @@ static void disconnect_console_server( struct console_server *server ) - server->console->server = NULL; - server->console = NULL; - wake_up( &server->obj, 0 ); -+ fast_set_event( server->fast_sync ); - } - } - -@@ -718,6 +735,8 @@ static void console_destroy( struct object *obj ) - free_async_queue( &console->read_q ); - if (console->fd) - release_object( console->fd ); -+ -+ if (console->fast_sync) release_object( console->fast_sync ); - } - - static struct object *create_console_connection( struct console *console ) -@@ -765,6 +784,16 @@ static struct object *console_open_file( struct object *obj, unsigned int access - return grab_object( obj ); - } - -+static struct fast_sync *console_get_fast_sync( struct object *obj ) -+{ -+ struct console *console = (struct console *)obj; -+ -+ if (!console->fast_sync) -+ console->fast_sync = fast_create_event( FAST_SYNC_SERVER, 1, console->signaled ); -+ if (console->fast_sync) grab_object( console->fast_sync ); -+ return console->fast_sync; -+} -+ - static void screen_buffer_dump( struct object *obj, int verbose ) - { - struct screen_buffer *screen_buffer = (struct screen_buffer *)obj; -@@ -814,6 +843,17 @@ static struct fd *screen_buffer_get_fd( struct object *obj ) - return NULL; - } - -+static struct fast_sync *screen_buffer_get_fast_sync( struct object *obj ) -+{ -+ struct screen_buffer *screen_buffer = (struct screen_buffer *)obj; -+ if (!screen_buffer->input) -+ { -+ set_error( STATUS_ACCESS_DENIED ); -+ return NULL; -+ } -+ return console_get_fast_sync( &screen_buffer->input->obj ); -+} -+ - static void console_server_dump( struct object *obj, int verbose ) - { - assert( obj->ops == &console_server_ops ); -@@ -826,6 +866,7 @@ static void console_server_destroy( struct object *obj ) - assert( obj->ops == &console_server_ops ); - disconnect_console_server( server ); - if (server->fd) release_object( server->fd ); -+ if (server->fast_sync) release_object( server->fast_sync ); - } - - static struct object *console_server_lookup_name( struct object *obj, struct unicode_str *name, -@@ -880,6 +921,17 @@ static struct object *console_server_open_file( struct object *obj, unsigned int - return grab_object( obj ); - } - -+static struct fast_sync *console_server_get_fast_sync( struct object *obj ) -+{ -+ struct console_server *server = (struct console_server *)obj; -+ int signaled = !server->console || !list_empty( &server->queue ); -+ -+ if (!server->fast_sync) -+ server->fast_sync = fast_create_event( FAST_SYNC_SERVER, 1, signaled ); -+ if (server->fast_sync) grab_object( server->fast_sync ); -+ return server->fast_sync; -+} -+ - static struct object *create_console_server( void ) - { - struct console_server *server; -@@ -890,6 +942,7 @@ static struct object *create_console_server( void ) - server->term_fd = -1; - list_init( &server->queue ); - list_init( &server->read_queue ); -+ server->fast_sync = NULL; - server->fd = alloc_pseudo_fd( &console_server_fd_ops, &server->obj, FILE_SYNCHRONOUS_IO_NONALERT ); - if (!server->fd) - { -@@ -1339,6 +1392,16 @@ static struct object *console_input_open_file( struct object *obj, unsigned int - return grab_object( obj ); - } - -+static struct fast_sync *console_input_get_fast_sync( struct object *obj ) -+{ -+ if (!current->process->console) -+ { -+ set_error( STATUS_ACCESS_DENIED ); -+ return NULL; -+ } -+ return console_get_fast_sync( ¤t->process->console->obj ); -+} -+ - static void console_input_destroy( struct object *obj ) - { - struct console_input *console_input = (struct console_input *)obj; -@@ -1411,6 +1474,16 @@ static struct object *console_output_open_file( struct object *obj, unsigned int - return grab_object( obj ); - } - -+static struct fast_sync *console_output_get_fast_sync( struct object *obj ) -+{ -+ if (!current->process->console || !current->process->console->active) -+ { -+ set_error( STATUS_ACCESS_DENIED ); -+ return NULL; -+ } -+ return console_get_fast_sync( ¤t->process->console->obj ); -+} -+ - static void console_output_destroy( struct object *obj ) - { - struct console_output *console_output = (struct console_output *)obj; -@@ -1468,11 +1541,16 @@ DECL_HANDLER(get_next_console_request) - - if (!server->console->renderer) server->console->renderer = current; - -- if (!req->signal) server->console->signaled = 0; -+ if (!req->signal) -+ { -+ server->console->signaled = 0; -+ fast_reset_event( server->console->fast_sync ); -+ } - else if (!server->console->signaled) - { - server->console->signaled = 1; - wake_up( &server->console->obj, 0 ); -+ fast_set_event( server->console->fast_sync ); - } - - if (req->read) -@@ -1494,6 +1572,8 @@ DECL_HANDLER(get_next_console_request) - /* set result of previous ioctl */ - ioctl = LIST_ENTRY( list_head( &server->queue ), struct console_host_ioctl, entry ); - list_remove( &ioctl->entry ); -+ if (list_empty( &server->queue )) -+ fast_reset_event( server->fast_sync ); - } - - if (ioctl) -@@ -1593,5 +1673,8 @@ DECL_HANDLER(get_next_console_request) - set_error( STATUS_PENDING ); - } - -+ if (list_empty( &server->queue )) -+ fast_reset_event( server->fast_sync ); -+ - release_object( server ); - } -diff --git a/server/debugger.c b/server/debugger.c -index 9b814469b19..5e44579e0a7 100644 ---- a/server/debugger.c -+++ b/server/debugger.c -@@ -72,6 +72,7 @@ struct debug_obj - struct object obj; /* object header */ - struct list event_queue; /* pending events queue */ - unsigned int flags; /* debug flags */ -+ struct fast_sync *fast_sync; /* fast synchronization object */ - }; - - -@@ -99,12 +100,14 @@ static const struct object_ops debug_event_ops = - NULL, /* unlink_name */ - no_open_file, /* open_file */ - no_kernel_obj_list, /* get_kernel_obj_list */ -+ no_get_fast_sync, /* get_fast_sync */ - no_close_handle, /* close_handle */ - debug_event_destroy /* destroy */ - }; - - static void debug_obj_dump( struct object *obj, int verbose ); - static int debug_obj_signaled( struct object *obj, struct wait_queue_entry *entry ); -+static struct fast_sync *debug_obj_get_fast_sync( struct object *obj ); - static void debug_obj_destroy( struct object *obj ); - - static const struct object_ops debug_obj_ops = -@@ -127,6 +130,7 @@ static const struct object_ops debug_obj_ops = - default_unlink_name, /* unlink_name */ - no_open_file, /* open_file */ - no_kernel_obj_list, /* get_kernel_obj_list */ -+ debug_obj_get_fast_sync, /* get_fast_sync */ - no_close_handle, /* close_handle */ - debug_obj_destroy /* destroy */ - }; -@@ -254,6 +258,7 @@ static void link_event( struct debug_obj *debug_obj, struct debug_event *event ) - /* grab reference since debugger could be killed while trying to wake up */ - grab_object( debug_obj ); - wake_up( &debug_obj->obj, 0 ); -+ fast_set_event( debug_obj->fast_sync ); - release_object( debug_obj ); - } - } -@@ -266,6 +271,7 @@ static void resume_event( struct debug_obj *debug_obj, struct debug_event *event - { - grab_object( debug_obj ); - wake_up( &debug_obj->obj, 0 ); -+ fast_set_event( debug_obj->fast_sync ); - release_object( debug_obj ); - } - } -@@ -331,6 +337,17 @@ static int debug_obj_signaled( struct object *obj, struct wait_queue_entry *entr - return find_event_to_send( debug_obj ) != NULL; - } - -+static struct fast_sync *debug_obj_get_fast_sync( struct object *obj ) -+{ -+ struct debug_obj *debug_obj = (struct debug_obj *)obj; -+ int signaled = find_event_to_send( debug_obj ) != NULL; -+ -+ if (!debug_obj->fast_sync) -+ debug_obj->fast_sync = fast_create_event( FAST_SYNC_SERVER, 1, signaled ); -+ if (debug_obj->fast_sync) grab_object( debug_obj->fast_sync ); -+ return debug_obj->fast_sync; -+} -+ - static void debug_obj_destroy( struct object *obj ) - { - struct list *ptr; -@@ -343,6 +360,8 @@ static void debug_obj_destroy( struct object *obj ) - /* free all pending events */ - while ((ptr = list_head( &debug_obj->event_queue ))) - unlink_event( debug_obj, LIST_ENTRY( ptr, struct debug_event, entry )); -+ -+ if (debug_obj->fast_sync) release_object( debug_obj->fast_sync ); - } - - struct debug_obj *get_debug_obj( struct process *process, obj_handle_t handle, unsigned int access ) -@@ -362,6 +381,7 @@ static struct debug_obj *create_debug_obj( struct object *root, const struct uni - { - debug_obj->flags = flags; - list_init( &debug_obj->event_queue ); -+ debug_obj->fast_sync = NULL; - } - } - return debug_obj; -@@ -570,6 +590,9 @@ DECL_HANDLER(wait_debug_event) - reply->tid = get_thread_id( event->sender ); - alloc_event_handles( event, current->process ); - set_reply_data( &event->data, min( get_reply_max_size(), sizeof(event->data) )); -+ -+ if (!find_event_to_send( debug_obj )) -+ fast_reset_event( debug_obj->fast_sync ); - } - else - { -diff --git a/server/device.c b/server/device.c -index 6400751e339..b3f4329d11b 100644 ---- a/server/device.c -+++ b/server/device.c -@@ -80,6 +80,7 @@ static const struct object_ops irp_call_ops = - NULL, /* unlink_name */ - no_open_file, /* open_file */ - no_kernel_obj_list, /* get_kernel_obj_list */ -+ no_get_fast_sync, /* get_fast_sync */ - no_close_handle, /* close_handle */ - irp_call_destroy /* destroy */ - }; -@@ -94,10 +95,12 @@ struct device_manager - struct list requests; /* list of pending irps across all devices */ - struct irp_call *current_call; /* call currently executed on client side */ - struct wine_rb_tree kernel_objects; /* map of objects that have client side pointer associated */ -+ struct fast_sync *fast_sync; /* fast synchronization object */ - }; - - static void device_manager_dump( struct object *obj, int verbose ); - static int device_manager_signaled( struct object *obj, struct wait_queue_entry *entry ); -+static struct fast_sync *device_manager_get_fast_sync( struct object *obj ); - static void device_manager_destroy( struct object *obj ); - - static const struct object_ops device_manager_ops = -@@ -120,6 +123,7 @@ static const struct object_ops device_manager_ops = - NULL, /* unlink_name */ - no_open_file, /* open_file */ - no_kernel_obj_list, /* get_kernel_obj_list */ -+ device_manager_get_fast_sync, /* get_fast_sync */ - no_close_handle, /* close_handle */ - device_manager_destroy /* destroy */ - }; -@@ -177,6 +181,7 @@ static const struct object_ops device_ops = - default_unlink_name, /* unlink_name */ - device_open_file, /* open_file */ - device_get_kernel_obj_list, /* get_kernel_obj_list */ -+ no_get_fast_sync, /* get_fast_sync */ - no_close_handle, /* close_handle */ - device_destroy /* destroy */ - }; -@@ -229,6 +234,7 @@ static const struct object_ops device_file_ops = - NULL, /* unlink_name */ - no_open_file, /* open_file */ - device_file_get_kernel_obj_list, /* get_kernel_obj_list */ -+ default_fd_get_fast_sync, /* get_fast_sync */ - device_file_close_handle, /* close_handle */ - device_file_destroy /* destroy */ - }; -@@ -439,7 +445,12 @@ static void add_irp_to_queue( struct device_manager *manager, struct irp_call *i - irp->thread = thread ? (struct thread *)grab_object( thread ) : NULL; - if (irp->file) list_add_tail( &irp->file->requests, &irp->dev_entry ); - list_add_tail( &manager->requests, &irp->mgr_entry ); -- if (list_head( &manager->requests ) == &irp->mgr_entry) wake_up( &manager->obj, 0 ); /* first one */ -+ if (list_head( &manager->requests ) == &irp->mgr_entry) -+ { -+ /* first one */ -+ wake_up( &manager->obj, 0 ); -+ fast_set_event( manager->fast_sync ); -+ } - } - - static struct object *device_open_file( struct object *obj, unsigned int access, -@@ -775,6 +786,9 @@ static void delete_file( struct device_file *file ) - set_irp_result( irp, STATUS_FILE_DELETED, NULL, 0, 0 ); - } - -+ if (list_empty( &file->device->manager->requests )) -+ fast_reset_event( file->device->manager->fast_sync ); -+ - release_object( file ); - } - -@@ -806,6 +820,16 @@ static int device_manager_signaled( struct object *obj, struct wait_queue_entry - return !list_empty( &manager->requests ); - } - -+static struct fast_sync *device_manager_get_fast_sync( struct object *obj ) -+{ -+ struct device_manager *manager = (struct device_manager *)obj; -+ -+ if (!manager->fast_sync) -+ manager->fast_sync = fast_create_event( FAST_SYNC_SERVER, 1, !list_empty( &manager->requests ) ); -+ if (manager->fast_sync) grab_object( manager->fast_sync ); -+ return manager->fast_sync; -+} -+ - static void device_manager_destroy( struct object *obj ) - { - struct device_manager *manager = (struct device_manager *)obj; -@@ -840,6 +864,8 @@ static void device_manager_destroy( struct object *obj ) - assert( !irp->file && !irp->async ); - release_object( irp ); - } -+ -+ if (manager->fast_sync) release_object( manager->fast_sync ); - } - - static struct device_manager *create_device_manager(void) -@@ -849,6 +875,7 @@ static struct device_manager *create_device_manager(void) - if ((manager = alloc_object( &device_manager_ops ))) - { - manager->current_call = NULL; -+ manager->fast_sync = NULL; - list_init( &manager->devices ); - list_init( &manager->requests ); - wine_rb_init( &manager->kernel_objects, compare_kernel_object ); -@@ -1014,6 +1041,10 @@ DECL_HANDLER(get_next_device_request) - iosb->in_size = 0; - list_remove( &irp->mgr_entry ); - list_init( &irp->mgr_entry ); -+ -+ if (list_empty( &manager->requests )) -+ fast_reset_event( manager->fast_sync ); -+ - /* we already own the object if it's only on manager queue */ - if (irp->file) grab_object( irp ); - manager->current_call = irp; -diff --git a/server/directory.c b/server/directory.c -index 30d69459984..4a0ec8867f8 100644 ---- a/server/directory.c -+++ b/server/directory.c -@@ -82,6 +82,7 @@ static const struct object_ops object_type_ops = - default_unlink_name, /* unlink_name */ - no_open_file, /* open_file */ - no_kernel_obj_list, /* get_kernel_obj_list */ -+ no_get_fast_sync, /* get_fast_sync */ - no_close_handle, /* close_handle */ - no_destroy /* destroy */ - }; -@@ -132,6 +133,7 @@ static const struct object_ops directory_ops = - default_unlink_name, /* unlink_name */ - no_open_file, /* open_file */ - no_kernel_obj_list, /* get_kernel_obj_list */ -+ no_get_fast_sync, /* get_fast_sync */ - no_close_handle, /* close_handle */ - directory_destroy /* destroy */ - }; -diff --git a/server/event.c b/server/event.c -index ccdd465dd5b..857bc1ab23b 100644 ---- a/server/event.c -+++ b/server/event.c -@@ -56,6 +56,7 @@ struct event - struct list kernel_object; /* list of kernel object pointers */ - int manual_reset; /* is it a manual reset event? */ - int signaled; /* event has been signaled */ -+ struct fast_sync *fast_sync; /* fast synchronization object */ - }; - - static void event_dump( struct object *obj, int verbose ); -@@ -63,6 +64,8 @@ static int event_signaled( struct object *obj, struct wait_queue_entry *entry ); - static void event_satisfied( struct object *obj, struct wait_queue_entry *entry ); - static int event_signal( struct object *obj, unsigned int access); - static struct list *event_get_kernel_obj_list( struct object *obj ); -+static struct fast_sync *event_get_fast_sync( struct object *obj ); -+static void event_destroy( struct object *obj ); - - static const struct object_ops event_ops = - { -@@ -84,8 +87,9 @@ static const struct object_ops event_ops = - default_unlink_name, /* unlink_name */ - no_open_file, /* open_file */ - event_get_kernel_obj_list, /* get_kernel_obj_list */ -+ event_get_fast_sync, /* get_fast_sync */ - no_close_handle, /* close_handle */ -- no_destroy /* destroy */ -+ event_destroy /* destroy */ - }; - - -@@ -106,10 +110,13 @@ struct type_descr keyed_event_type = - struct keyed_event - { - struct object obj; /* object header */ -+ struct fast_sync *fast_sync; /* fast synchronization object */ - }; - - static void keyed_event_dump( struct object *obj, int verbose ); - static int keyed_event_signaled( struct object *obj, struct wait_queue_entry *entry ); -+static struct fast_sync *keyed_event_get_fast_sync( struct object *obj ); -+static void keyed_event_destroy( struct object *obj ); - - static const struct object_ops keyed_event_ops = - { -@@ -131,8 +138,9 @@ static const struct object_ops keyed_event_ops = - default_unlink_name, /* unlink_name */ - no_open_file, /* open_file */ - no_kernel_obj_list, /* get_kernel_obj_list */ -+ keyed_event_get_fast_sync, /* get_fast_sync */ - no_close_handle, /* close_handle */ -- no_destroy /* destroy */ -+ keyed_event_destroy /* destroy */ - }; - - -@@ -150,6 +158,7 @@ struct event *create_event( struct object *root, const struct unicode_str *name, - list_init( &event->kernel_object ); - event->manual_reset = manual_reset; - event->signaled = initial_state; -+ event->fast_sync = NULL; - } - } - return event; -@@ -173,11 +182,13 @@ void set_event( struct event *event ) - event->signaled = 1; - /* wake up all waiters if manual reset, a single one otherwise */ - wake_up( &event->obj, !event->manual_reset ); -+ fast_set_event( event->fast_sync ); - } - - void reset_event( struct event *event ) - { - event->signaled = 0; -+ fast_reset_event( event->fast_sync ); - } - - static void event_dump( struct object *obj, int verbose ) -@@ -223,6 +234,23 @@ static struct list *event_get_kernel_obj_list( struct object *obj ) - return &event->kernel_object; - } - -+static struct fast_sync *event_get_fast_sync( struct object *obj ) -+{ -+ struct event *event = (struct event *)obj; -+ -+ if (!event->fast_sync) -+ event->fast_sync = fast_create_event( FAST_SYNC_EVENT, event->manual_reset, event->signaled ); -+ if (event->fast_sync) grab_object( event->fast_sync ); -+ return event->fast_sync; -+} -+ -+static void event_destroy( struct object *obj ) -+{ -+ struct event *event = (struct event *)obj; -+ -+ if (event->fast_sync) release_object( event->fast_sync ); -+} -+ - struct keyed_event *create_keyed_event( struct object *root, const struct unicode_str *name, - unsigned int attr, const struct security_descriptor *sd ) - { -@@ -233,6 +261,7 @@ struct keyed_event *create_keyed_event( struct object *root, const struct unicod - if (get_error() != STATUS_OBJECT_NAME_EXISTS) - { - /* initialize it if it didn't already exist */ -+ event->fast_sync = NULL; - } - } - return event; -@@ -276,6 +305,23 @@ static int keyed_event_signaled( struct object *obj, struct wait_queue_entry *en - return 0; - } - -+static struct fast_sync *keyed_event_get_fast_sync( struct object *obj ) -+{ -+ struct keyed_event *event = (struct keyed_event *)obj; -+ -+ if (!event->fast_sync) -+ event->fast_sync = fast_create_event( FAST_SYNC_SERVER, 1, 1 ); -+ if (event->fast_sync) grab_object( event->fast_sync ); -+ return event->fast_sync; -+} -+ -+static void keyed_event_destroy( struct object *obj ) -+{ -+ struct keyed_event *event = (struct keyed_event *)obj; -+ -+ if (event->fast_sync) release_object( event->fast_sync ); -+} -+ - /* create an event */ - DECL_HANDLER(create_event) - { -diff --git a/server/fast_sync.c b/server/fast_sync.c -new file mode 100644 -index 00000000000..f0dae236ca3 ---- /dev/null -+++ b/server/fast_sync.c -@@ -0,0 +1,427 @@ -+/* -+ * Fast synchronization primitives -+ * -+ * Copyright (C) 2021 Zebediah Figura for CodeWeavers -+ * -+ * This library is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU Lesser General Public -+ * License as published by the Free Software Foundation; either -+ * version 2.1 of the License, or (at your option) any later version. -+ * -+ * This library is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -+ * Lesser General Public License for more details. -+ * -+ * You should have received a copy of the GNU Lesser General Public -+ * License along with this library; if not, write to the Free Software -+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA -+ */ -+ -+#include "config.h" -+ -+#include -+#include -+#ifdef HAVE_LINUX_WINESYNC_H -+# include -+#endif -+#include -+#ifdef HAVE_SYS_IOCTL_H -+# include -+#endif -+#include -+#include -+ -+#include "ntstatus.h" -+#define WIN32_NO_STATUS -+#include "winternl.h" -+ -+#include "file.h" -+#include "handle.h" -+#include "request.h" -+#include "thread.h" -+ -+#ifdef HAVE_LINUX_WINESYNC_H -+ -+struct fast_sync_device -+{ -+ struct object obj; /* object header */ -+ struct fd *fd; /* fd for unix fd */ -+}; -+ -+static struct fast_sync_device *fast_sync_device_object; -+ -+static void fast_sync_device_dump( struct object *obj, int verbose ); -+static struct fd *fast_sync_device_get_fd( struct object *obj ); -+static void fast_sync_device_destroy( struct object *obj ); -+static enum server_fd_type fast_sync_device_get_fd_type( struct fd *fd ); -+ -+static const struct object_ops fast_sync_device_ops = -+{ -+ sizeof(struct fast_sync_device), /* size */ -+ &no_type, /* type */ -+ fast_sync_device_dump, /* dump */ -+ no_add_queue, /* add_queue */ -+ NULL, /* remove_queue */ -+ NULL, /* signaled */ -+ NULL, /* satisfied */ -+ no_signal, /* signal */ -+ fast_sync_device_get_fd, /* get_fd */ -+ default_map_access, /* map_access */ -+ default_get_sd, /* get_sd */ -+ default_set_sd, /* set_sd */ -+ no_get_full_name, /* get_full_name */ -+ no_lookup_name, /* lookup_name */ -+ no_link_name, /* link_name */ -+ NULL, /* unlink_name */ -+ no_open_file, /* open_file */ -+ no_kernel_obj_list, /* get_kernel_obj_list */ -+ no_get_fast_sync, /* get_fast_sync */ -+ no_close_handle, /* close_handle */ -+ fast_sync_device_destroy /* destroy */ -+}; -+ -+static const struct fd_ops fast_sync_device_fd_ops = -+{ -+ default_fd_get_poll_events, /* get_poll_events */ -+ default_poll_event, /* poll_event */ -+ fast_sync_device_get_fd_type, /* get_fd_type */ -+ no_fd_read, /* read */ -+ no_fd_write, /* write */ -+ no_fd_flush, /* flush */ -+ no_fd_get_file_info, /* get_file_info */ -+ no_fd_get_volume_info, /* get_volume_info */ -+ no_fd_ioctl, /* ioctl */ -+ no_fd_queue_async, /* queue_async */ -+ default_fd_reselect_async /* reselect_async */ -+}; -+ -+static void fast_sync_device_dump( struct object *obj, int verbose ) -+{ -+ struct fast_sync_device *device = (struct fast_sync_device *)obj; -+ assert( obj->ops == &fast_sync_device_ops ); -+ fprintf( stderr, "Fast synchronization device fd=%p\n", device->fd ); -+} -+ -+static struct fd *fast_sync_device_get_fd( struct object *obj ) -+{ -+ struct fast_sync_device *device = (struct fast_sync_device *)obj; -+ return (struct fd *)grab_object( device->fd ); -+} -+ -+static void fast_sync_device_destroy( struct object *obj ) -+{ -+ struct fast_sync_device *device = (struct fast_sync_device *)obj; -+ assert( obj->ops == &fast_sync_device_ops ); -+ if (device->fd) release_object( device->fd ); -+ fast_sync_device_object = NULL; -+} -+ -+static enum server_fd_type fast_sync_device_get_fd_type( struct fd *fd ) -+{ -+ return FD_TYPE_FILE; -+} -+ -+static struct fast_sync_device *get_fast_sync_device(void) -+{ -+ struct fast_sync_device *device; -+ int unix_fd; -+ -+ if (getenv( "WINE_DISABLE_FAST_SYNC" ) && atoi( getenv( "WINE_DISABLE_FAST_SYNC" ) )) -+ { -+ set_error( STATUS_NOT_IMPLEMENTED ); -+ return NULL; -+ } -+ -+ if (fast_sync_device_object) -+ return (struct fast_sync_device *)grab_object( fast_sync_device_object ); -+ -+ unix_fd = open( "/dev/winesync", O_CLOEXEC | O_RDONLY ); -+ if (unix_fd == -1) -+ { -+ file_set_error(); -+ return NULL; -+ } -+ -+ if (!(device = alloc_object( &fast_sync_device_ops ))) -+ { -+ close( unix_fd ); -+ set_error( STATUS_NO_MEMORY ); -+ return NULL; -+ } -+ -+ if (!(device->fd = create_anonymous_fd( &fast_sync_device_fd_ops, unix_fd, &device->obj, 0 ))) -+ { -+ release_object( device ); -+ set_error( STATUS_NO_MEMORY ); -+ return NULL; -+ } -+ -+ fast_sync_device_object = device; -+ return device; -+} -+ -+struct fast_sync -+{ -+ struct object obj; -+ struct fast_sync_device *device; -+ enum fast_sync_type type; -+ unsigned int linux_obj; -+}; -+ -+static void fast_sync_dump( struct object *obj, int verbose ); -+static void fast_sync_destroy( struct object *obj ); -+ -+static const struct object_ops fast_sync_ops = -+{ -+ sizeof(struct fast_sync), /* size */ -+ &no_type, /* type */ -+ fast_sync_dump, /* dump */ -+ no_add_queue, /* add_queue */ -+ NULL, /* remove_queue */ -+ NULL, /* signaled */ -+ NULL, /* satisfied */ -+ no_signal, /* signal */ -+ no_get_fd, /* get_fd */ -+ default_map_access, /* map_access */ -+ default_get_sd, /* get_sd */ -+ default_set_sd, /* set_sd */ -+ no_get_full_name, /* get_full_name */ -+ no_lookup_name, /* lookup_name */ -+ no_link_name, /* link_name */ -+ NULL, /* unlink_name */ -+ no_open_file, /* open_file */ -+ no_kernel_obj_list, /* get_kernel_obj_list */ -+ no_get_fast_sync, /* get_fast_sync */ -+ no_close_handle, /* close_handle */ -+ fast_sync_destroy /* destroy */ -+}; -+ -+static void fast_sync_dump( struct object *obj, int verbose ) -+{ -+ struct fast_sync *fast_sync = (struct fast_sync *)obj; -+ assert( obj->ops == &fast_sync_ops ); -+ fprintf( stderr, "Fast synchronization object type=%u linux_obj=%u\n", -+ fast_sync->type, fast_sync->linux_obj ); -+} -+ -+static void fast_sync_destroy( struct object *obj ) -+{ -+ struct fast_sync *fast_sync = (struct fast_sync *)obj; -+ -+ ioctl( get_unix_fd( fast_sync->device->fd ), WINESYNC_IOC_DELETE, &fast_sync->linux_obj ); -+ release_object( fast_sync->device ); -+} -+ -+struct fast_sync *fast_create_event( enum fast_sync_type type, int manual_reset, int signaled ) -+{ -+ struct winesync_sem_args args = {0}; -+ struct fast_sync_device *device; -+ struct fast_sync *fast_sync; -+ -+ if (!(device = get_fast_sync_device())) return NULL; -+ -+ args.count = signaled; -+ args.max = 1; -+ if (!manual_reset) -+ args.flags |= WINESYNC_SEM_GETONWAIT; -+ if (ioctl( get_unix_fd( device->fd ), WINESYNC_IOC_CREATE_SEM, &args ) < 0) -+ { -+ file_set_error(); -+ release_object( device ); -+ return NULL; -+ } -+ -+ if (!(fast_sync = alloc_object( &fast_sync_ops ))) return NULL; -+ -+ /* transfer our device reference to the fast sync object */ -+ fast_sync->device = device; -+ fast_sync->type = type; -+ fast_sync->linux_obj = args.sem; -+ -+ return fast_sync; -+} -+ -+struct fast_sync *fast_create_semaphore( unsigned int count, unsigned int max ) -+{ -+ struct winesync_sem_args args = {0}; -+ struct fast_sync_device *device; -+ struct fast_sync *fast_sync; -+ -+ if (!(device = get_fast_sync_device())) return NULL; -+ -+ args.count = count; -+ args.max = max; -+ args.flags = WINESYNC_SEM_GETONWAIT; -+ if (ioctl( get_unix_fd( device->fd ), WINESYNC_IOC_CREATE_SEM, &args ) < 0) -+ { -+ file_set_error(); -+ release_object( device ); -+ return NULL; -+ } -+ -+ if (!(fast_sync = alloc_object( &fast_sync_ops ))) return NULL; -+ -+ /* transfer our device reference to the fast sync object */ -+ fast_sync->device = device; -+ fast_sync->type = FAST_SYNC_SEMAPHORE; -+ fast_sync->linux_obj = args.sem; -+ -+ return fast_sync; -+} -+ -+struct fast_sync *fast_create_mutex( thread_id_t owner, unsigned int count ) -+{ -+ struct winesync_mutex_args args = {0}; -+ struct fast_sync_device *device; -+ struct fast_sync *fast_sync; -+ -+ if (!(device = get_fast_sync_device())) return NULL; -+ -+ args.owner = owner; -+ args.count = count; -+ if (ioctl( get_unix_fd( device->fd ), WINESYNC_IOC_CREATE_MUTEX, &args ) < 0) -+ { -+ file_set_error(); -+ release_object( device ); -+ return NULL; -+ } -+ -+ if (!(fast_sync = alloc_object( &fast_sync_ops ))) return NULL; -+ -+ /* transfer our device reference to the fast sync object */ -+ fast_sync->device = device; -+ fast_sync->type = FAST_SYNC_MUTEX; -+ fast_sync->linux_obj = args.mutex; -+ -+ return fast_sync; -+} -+ -+void fast_set_event( struct fast_sync *fast_sync ) -+{ -+ struct winesync_sem_args args = {0}; -+ -+ if (!fast_sync) return; -+ -+ if (debug_level) fprintf( stderr, "fast_set_event %u\n", fast_sync->linux_obj ); -+ -+ args.sem = fast_sync->linux_obj; -+ args.count = 1; -+ ioctl( get_unix_fd( fast_sync->device->fd ), WINESYNC_IOC_PUT_SEM, &args ); -+} -+ -+void fast_reset_event( struct fast_sync *fast_sync ) -+{ -+ if (!fast_sync) return; -+ -+ if (debug_level) fprintf( stderr, "fast_reset_event %u\n", fast_sync->linux_obj ); -+ -+ ioctl( get_unix_fd( fast_sync->device->fd ), WINESYNC_IOC_GET_SEM, &fast_sync->linux_obj ); -+} -+ -+void fast_abandon_mutexes( thread_id_t tid ) -+{ -+ struct fast_sync_device *device; -+ -+ if (!(device = get_fast_sync_device())) -+ { -+ clear_error(); -+ return; -+ } -+ -+ ioctl( get_unix_fd( device->fd ), WINESYNC_IOC_KILL_OWNER, &tid ); -+ release_object( device ); -+} -+ -+int fast_user_apc_needs_signal(void) -+{ -+ return !!fast_sync_device_object; -+} -+ -+#else -+ -+static struct fast_sync_device *get_fast_sync_device(void) -+{ -+ set_error( STATUS_NOT_IMPLEMENTED ); -+ return NULL; -+} -+ -+struct fast_sync *fast_create_event( enum fast_sync_type type, int manual_reset, int signaled ) -+{ -+ set_error( STATUS_NOT_IMPLEMENTED ); -+ return NULL; -+} -+ -+struct fast_sync *fast_create_semaphore( unsigned int count, unsigned int max ) -+{ -+ set_error( STATUS_NOT_IMPLEMENTED ); -+ return NULL; -+} -+ -+struct fast_sync *fast_create_mutex( thread_id_t owner, unsigned int count ) -+{ -+ set_error( STATUS_NOT_IMPLEMENTED ); -+ return NULL; -+} -+ -+void fast_set_event( struct fast_sync *fast_sync ) -+{ -+} -+ -+void fast_reset_event( struct fast_sync *obj ) -+{ -+} -+ -+void fast_abandon_mutexes( thread_id_t tid ) -+{ -+} -+ -+int fast_user_apc_needs_signal(void) -+{ -+ return 0; -+} -+ -+#endif -+ -+DECL_HANDLER(get_fast_sync_device) -+{ -+#ifdef HAVE_LINUX_WINESYNC_H -+ struct fast_sync_device *device; -+ -+ if ((device = get_fast_sync_device())) -+ { -+ reply->handle = alloc_handle( current->process, device, 0, 0 ); -+ release_object( device ); -+ } -+#else -+ set_error( STATUS_NOT_IMPLEMENTED ); -+#endif -+} -+ -+DECL_HANDLER(get_fast_sync_obj) -+{ -+#ifdef HAVE_LINUX_WINESYNC_H -+ struct object *obj; -+ static int once; -+ -+ if (!once++) -+ fprintf( stderr, "wine: using fast synchronization.\n" ); -+ -+ if ((obj = get_handle_obj( current->process, req->handle, 0, NULL ))) -+ { -+ struct fast_sync *fast_sync; -+ -+ if ((fast_sync = obj->ops->get_fast_sync( obj ))) -+ { -+ reply->handle = alloc_handle( current->process, fast_sync, 0, 0 ); -+ reply->obj = fast_sync->linux_obj; -+ reply->type = fast_sync->type; -+ reply->access = get_handle_access( current->process, req->handle ); -+ release_object( fast_sync ); -+ } -+ release_object( obj ); -+ } -+#else -+ set_error( STATUS_NOT_IMPLEMENTED ); -+#endif -+} -diff --git a/server/fd.c b/server/fd.c -index bfa2805d82b..2b7ed1b752e 100644 ---- a/server/fd.c -+++ b/server/fd.c -@@ -199,6 +199,7 @@ struct fd - struct completion *completion; /* completion object attached to this fd */ - apc_param_t comp_key; /* completion key to set in completion events */ - unsigned int comp_flags; /* completion flags */ -+ struct fast_sync *fast_sync; /* fast synchronization object */ - }; - - static void fd_dump( struct object *obj, int verbose ); -@@ -224,6 +225,7 @@ static const struct object_ops fd_ops = - NULL, /* unlink_name */ - no_open_file, /* open_file */ - no_kernel_obj_list, /* get_kernel_obj_list */ -+ no_get_fast_sync, /* get_fast_sync */ - no_close_handle, /* close_handle */ - fd_destroy /* destroy */ - }; -@@ -265,6 +267,7 @@ static const struct object_ops device_ops = - NULL, /* unlink_name */ - no_open_file, /* open_file */ - no_kernel_obj_list, /* get_kernel_obj_list */ -+ no_get_fast_sync, /* get_fast_sync */ - no_close_handle, /* close_handle */ - device_destroy /* destroy */ - }; -@@ -305,6 +308,7 @@ static const struct object_ops inode_ops = - NULL, /* unlink_name */ - no_open_file, /* open_file */ - no_kernel_obj_list, /* get_kernel_obj_list */ -+ no_get_fast_sync, /* get_fast_sync */ - no_close_handle, /* close_handle */ - inode_destroy /* destroy */ - }; -@@ -347,6 +351,7 @@ static const struct object_ops file_lock_ops = - NULL, /* unlink_name */ - no_open_file, /* open_file */ - no_kernel_obj_list, /* get_kernel_obj_list */ -+ no_get_fast_sync, /* get_fast_sync */ - no_close_handle, /* close_handle */ - no_destroy /* destroy */ - }; -@@ -1580,6 +1585,7 @@ static void fd_destroy( struct object *obj ) - if (fd->unix_fd != -1) close( fd->unix_fd ); - free( fd->unix_name ); - } -+ if (fd->fast_sync) release_object( fd->fast_sync ); - } - - /* check if the desired access is possible without violating */ -@@ -1696,6 +1702,7 @@ static struct fd *alloc_fd_object(void) - fd->poll_index = -1; - fd->completion = NULL; - fd->comp_flags = 0; -+ fd->fast_sync = NULL; - init_async_queue( &fd->read_q ); - init_async_queue( &fd->write_q ); - init_async_queue( &fd->wait_q ); -@@ -1734,6 +1741,7 @@ struct fd *alloc_pseudo_fd( const struct fd_ops *fd_user_ops, struct object *use - fd->poll_index = -1; - fd->completion = NULL; - fd->comp_flags = 0; -+ fd->fast_sync = NULL; - fd->no_fd_status = STATUS_BAD_DEVICE_TYPE; - init_async_queue( &fd->read_q ); - init_async_queue( &fd->write_q ); -@@ -2118,7 +2126,15 @@ void set_fd_signaled( struct fd *fd, int signaled ) - { - if (fd->comp_flags & FILE_SKIP_SET_EVENT_ON_HANDLE) return; - fd->signaled = signaled; -- if (signaled) wake_up( fd->user, 0 ); -+ if (signaled) -+ { -+ wake_up( fd->user, 0 ); -+ fast_set_event( fd->fast_sync ); -+ } -+ else -+ { -+ fast_reset_event( fd->fast_sync ); -+ } - } - - /* check if events are pending and if yes return which one(s) */ -@@ -2144,6 +2160,19 @@ int default_fd_signaled( struct object *obj, struct wait_queue_entry *entry ) - return ret; - } - -+struct fast_sync *default_fd_get_fast_sync( struct object *obj ) -+{ -+ struct fd *fd = get_obj_fd( obj ); -+ struct fast_sync *ret; -+ -+ if (!fd->fast_sync) -+ fd->fast_sync = fast_create_event( FAST_SYNC_SERVER, 1, fd->signaled ); -+ ret = fd->fast_sync; -+ release_object( fd ); -+ if (ret) grab_object( ret ); -+ return ret; -+} -+ - int default_fd_get_poll_events( struct fd *fd ) - { - int events = 0; -diff --git a/server/file.c b/server/file.c -index aff4d9e09e1..8c684141fa6 100644 ---- a/server/file.c -+++ b/server/file.c -@@ -109,6 +109,7 @@ static const struct object_ops file_ops = - NULL, /* unlink_name */ - file_open_file, /* open_file */ - file_get_kernel_obj_list, /* get_kernel_obj_list */ -+ default_fd_get_fast_sync, /* get_fast_sync */ - no_close_handle, /* close_handle */ - file_destroy /* destroy */ - }; -diff --git a/server/file.h b/server/file.h -index b8bc7645b19..0989bae1d87 100644 ---- a/server/file.h -+++ b/server/file.h -@@ -104,6 +104,7 @@ extern char *dup_fd_name( struct fd *root, const char *name ); - extern void get_nt_name( struct fd *fd, struct unicode_str *name ); - - extern int default_fd_signaled( struct object *obj, struct wait_queue_entry *entry ); -+extern struct fast_sync *default_fd_get_fast_sync( struct object *obj ); - extern int default_fd_get_poll_events( struct fd *fd ); - extern void default_poll_event( struct fd *fd, int event ); - extern void fd_queue_async( struct fd *fd, struct async *async, int type ); -diff --git a/server/handle.c b/server/handle.c -index d86f0960ccf..6cf388cd1d5 100644 ---- a/server/handle.c -+++ b/server/handle.c -@@ -138,6 +138,7 @@ static const struct object_ops handle_table_ops = - NULL, /* unlink_name */ - no_open_file, /* open_file */ - no_kernel_obj_list, /* get_kernel_obj_list */ -+ no_get_fast_sync, /* get_fast_sync */ - no_close_handle, /* close_handle */ - handle_table_destroy /* destroy */ - }; -diff --git a/server/hook.c b/server/hook.c -index c048908c295..8d1f7895313 100644 ---- a/server/hook.c -+++ b/server/hook.c -@@ -93,6 +93,7 @@ static const struct object_ops hook_table_ops = - NULL, /* unlink_name */ - no_open_file, /* open_file */ - no_kernel_obj_list, /* get_kernel_obj_list */ -+ no_get_fast_sync, /* get_fast_sync */ - no_close_handle, /* close_handle */ - hook_table_destroy /* destroy */ - }; -diff --git a/server/mailslot.c b/server/mailslot.c -index d4b2fd1b562..88aee771e5e 100644 ---- a/server/mailslot.c -+++ b/server/mailslot.c -@@ -90,6 +90,7 @@ static const struct object_ops mailslot_ops = - default_unlink_name, /* unlink_name */ - mailslot_open_file, /* open_file */ - no_kernel_obj_list, /* get_kernel_obj_list */ -+ default_fd_get_fast_sync, /* get_fast_sync */ - no_close_handle, /* close_handle */ - mailslot_destroy /* destroy */ - }; -@@ -148,6 +149,7 @@ static const struct object_ops mail_writer_ops = - NULL, /* unlink_name */ - no_open_file, /* open_file */ - no_kernel_obj_list, /* get_kernel_obj_list */ -+ no_get_fast_sync, /* get_fast_sync */ - no_close_handle, /* close_handle */ - mail_writer_destroy /* destroy */ - }; -@@ -210,6 +212,7 @@ static const struct object_ops mailslot_device_ops = - default_unlink_name, /* unlink_name */ - mailslot_device_open_file, /* open_file */ - no_kernel_obj_list, /* get_kernel_obj_list */ -+ no_get_fast_sync, /* get_fast_sync */ - no_close_handle, /* close_handle */ - mailslot_device_destroy /* destroy */ - }; -@@ -240,6 +243,7 @@ static const struct object_ops mailslot_device_file_ops = - NULL, /* unlink_name */ - no_open_file, /* open_file */ - no_kernel_obj_list, /* get_kernel_obj_list */ -+ default_fd_get_fast_sync, /* get_fast_sync */ - no_close_handle, /* close_handle */ - mailslot_device_file_destroy /* destroy */ - }; -diff --git a/server/mapping.c b/server/mapping.c -index 9cb24a4213f..c03d18254d3 100644 ---- a/server/mapping.c -+++ b/server/mapping.c -@@ -80,6 +80,7 @@ static const struct object_ops ranges_ops = - NULL, /* unlink_name */ - no_open_file, /* open_file */ - no_kernel_obj_list, /* get_kernel_obj_list */ -+ no_get_fast_sync, /* get_fast_sync */ - no_close_handle, /* close_handle */ - ranges_destroy /* destroy */ - }; -@@ -116,6 +117,7 @@ static const struct object_ops shared_map_ops = - NULL, /* unlink_name */ - no_open_file, /* open_file */ - no_kernel_obj_list, /* get_kernel_obj_list */ -+ no_get_fast_sync, /* get_fast_sync */ - no_close_handle, /* close_handle */ - shared_map_destroy /* destroy */ - }; -@@ -189,6 +191,7 @@ static const struct object_ops mapping_ops = - default_unlink_name, /* unlink_name */ - no_open_file, /* open_file */ - no_kernel_obj_list, /* get_kernel_obj_list */ -+ no_get_fast_sync, /* get_fast_sync */ - no_close_handle, /* close_handle */ - mapping_destroy /* destroy */ - }; -diff --git a/server/mutex.c b/server/mutex.c -index 75ff5117d3f..ca5d38da5ea 100644 ---- a/server/mutex.c -+++ b/server/mutex.c -@@ -57,6 +57,7 @@ struct mutex - unsigned int count; /* recursion count */ - int abandoned; /* has it been abandoned? */ - struct list entry; /* entry in owner thread mutex list */ -+ struct fast_sync *fast_sync; /* fast synchronization object */ - }; - - static void mutex_dump( struct object *obj, int verbose ); -@@ -64,6 +65,7 @@ static int mutex_signaled( struct object *obj, struct wait_queue_entry *entry ); - static void mutex_satisfied( struct object *obj, struct wait_queue_entry *entry ); - static void mutex_destroy( struct object *obj ); - static int mutex_signal( struct object *obj, unsigned int access ); -+static struct fast_sync *mutex_get_fast_sync( struct object *obj ); - - static const struct object_ops mutex_ops = - { -@@ -85,6 +87,7 @@ static const struct object_ops mutex_ops = - default_unlink_name, /* unlink_name */ - no_open_file, /* open_file */ - no_kernel_obj_list, /* get_kernel_obj_list */ -+ mutex_get_fast_sync, /* get_fast_sync */ - no_close_handle, /* close_handle */ - mutex_destroy /* destroy */ - }; -@@ -127,6 +130,7 @@ static struct mutex *create_mutex( struct object *root, const struct unicode_str - mutex->owner = NULL; - mutex->abandoned = 0; - if (owned) do_grab( mutex, current ); -+ mutex->fast_sync = NULL; - } - } - return mutex; -@@ -189,14 +193,27 @@ static int mutex_signal( struct object *obj, unsigned int access ) - return 1; - } - -+static struct fast_sync *mutex_get_fast_sync( struct object *obj ) -+{ -+ struct mutex *mutex = (struct mutex *)obj; -+ -+ if (!mutex->fast_sync) -+ mutex->fast_sync = fast_create_mutex( mutex->owner ? mutex->owner->id : 0, mutex->count ); -+ if (mutex->fast_sync) grab_object( mutex->fast_sync ); -+ return mutex->fast_sync; -+} -+ - static void mutex_destroy( struct object *obj ) - { - struct mutex *mutex = (struct mutex *)obj; - assert( obj->ops == &mutex_ops ); - -- if (!mutex->count) return; -- mutex->count = 0; -- do_release( mutex ); -+ if (mutex->count) -+ { -+ mutex->count = 0; -+ do_release( mutex ); -+ } -+ if (mutex->fast_sync) release_object( mutex->fast_sync ); - } - - /* create a mutex */ -diff --git a/server/named_pipe.c b/server/named_pipe.c -index df8c7e3170c..3bc91bb8350 100644 ---- a/server/named_pipe.c -+++ b/server/named_pipe.c -@@ -131,6 +131,7 @@ static const struct object_ops named_pipe_ops = - default_unlink_name, /* unlink_name */ - named_pipe_open_file, /* open_file */ - no_kernel_obj_list, /* get_kernel_obj_list */ -+ no_get_fast_sync, /* get_fast_sync */ - no_close_handle, /* close_handle */ - named_pipe_destroy /* destroy */ - }; -@@ -179,6 +180,7 @@ static const struct object_ops pipe_server_ops = - NULL, /* unlink_name */ - pipe_server_open_file, /* open_file */ - no_kernel_obj_list, /* get_kernel_obj_list */ -+ default_fd_get_fast_sync, /* get_fast_sync */ - no_close_handle, /* close_handle */ - pipe_server_destroy /* destroy */ - }; -@@ -222,6 +224,7 @@ static const struct object_ops pipe_client_ops = - NULL, /* unlink_name */ - no_open_file, /* open_file */ - no_kernel_obj_list, /* get_kernel_obj_list */ -+ default_fd_get_fast_sync, /* get_fast_sync */ - no_close_handle, /* close_handle */ - pipe_end_destroy /* destroy */ - }; -@@ -268,6 +271,7 @@ static const struct object_ops named_pipe_device_ops = - default_unlink_name, /* unlink_name */ - named_pipe_device_open_file, /* open_file */ - no_kernel_obj_list, /* get_kernel_obj_list */ -+ no_get_fast_sync, /* get_fast_sync */ - no_close_handle, /* close_handle */ - named_pipe_device_destroy /* destroy */ - }; -@@ -299,6 +303,7 @@ static const struct object_ops named_pipe_device_file_ops = - NULL, /* unlink_name */ - no_open_file, /* open_file */ - no_kernel_obj_list, /* get_kernel_obj_list */ -+ default_fd_get_fast_sync, /* get_fast_sync */ - no_close_handle, /* close_handle */ - named_pipe_device_file_destroy /* destroy */ - }; -diff --git a/server/object.c b/server/object.c -index b2779fd61fe..e8d6783537c 100644 ---- a/server/object.c -+++ b/server/object.c -@@ -528,6 +528,12 @@ struct fd *no_get_fd( struct object *obj ) - return NULL; - } - -+struct fast_sync *no_get_fast_sync( struct object *obj ) -+{ -+ set_error( STATUS_OBJECT_TYPE_MISMATCH ); -+ return NULL; -+} -+ - unsigned int default_map_access( struct object *obj, unsigned int access ) - { - return map_access( access, &obj->ops->type->mapping ); -diff --git a/server/object.h b/server/object.h -index e9d9a87875e..6d77c9bb0c2 100644 ---- a/server/object.h -+++ b/server/object.h -@@ -45,6 +45,7 @@ struct async; - struct async_queue; - struct winstation; - struct object_type; -+struct fast_sync; - - - struct unicode_str -@@ -106,6 +107,8 @@ struct object_ops - unsigned int options); - /* return list of kernel objects */ - struct list *(*get_kernel_obj_list)(struct object *); -+ /* get a client-waitable fast-synchronization handle to this object */ -+ struct fast_sync *(*get_fast_sync)(struct object *); - /* close a handle to this object */ - int (*close_handle)(struct object *,struct process *,obj_handle_t); - /* destroy on refcount == 0 */ -@@ -222,6 +225,18 @@ extern void reset_event( struct event *event ); - - extern void abandon_mutexes( struct thread *thread ); - -+/* fast-synchronization functions */ -+ -+extern struct fast_sync *fast_create_event( enum fast_sync_type type, int manual_reset, int signaled ); -+extern struct fast_sync *fast_create_semaphore( unsigned int count, unsigned int max ); -+extern struct fast_sync *fast_create_mutex( thread_id_t owner, unsigned int count ); -+extern void fast_set_event( struct fast_sync *obj ); -+extern void fast_reset_event( struct fast_sync *obj ); -+extern void fast_abandon_mutexes( thread_id_t tid ); -+extern int fast_user_apc_needs_signal(void); -+ -+extern struct fast_sync *no_get_fast_sync( struct object *obj ); -+ - /* serial functions */ - - int get_serial_async_timeout(struct object *obj, int type, int count); -diff --git a/server/process.c b/server/process.c -index 5b271b12328..b6ff300eacc 100644 ---- a/server/process.c -+++ b/server/process.c -@@ -80,6 +80,7 @@ static unsigned int process_map_access( struct object *obj, unsigned int access - static struct security_descriptor *process_get_sd( struct object *obj ); - static void process_poll_event( struct fd *fd, int event ); - static struct list *process_get_kernel_obj_list( struct object *obj ); -+static struct fast_sync *process_get_fast_sync( struct object *obj ); - static void process_destroy( struct object *obj ); - static void terminate_process( struct process *process, struct thread *skip, int exit_code ); - -@@ -103,6 +104,7 @@ static const struct object_ops process_ops = - NULL, /* unlink_name */ - no_open_file, /* open_file */ - process_get_kernel_obj_list, /* get_kernel_obj_list */ -+ process_get_fast_sync, /* get_fast_sync */ - no_close_handle, /* close_handle */ - process_destroy /* destroy */ - }; -@@ -154,6 +156,7 @@ static const struct object_ops startup_info_ops = - NULL, /* unlink_name */ - no_open_file, /* open_file */ - no_kernel_obj_list, /* get_kernel_obj_list */ -+ no_get_fast_sync, /* get_fast_sync */ - no_close_handle, /* close_handle */ - startup_info_destroy /* destroy */ - }; -@@ -176,6 +179,7 @@ struct type_descr job_type = - - static void job_dump( struct object *obj, int verbose ); - static int job_signaled( struct object *obj, struct wait_queue_entry *entry ); -+static struct fast_sync *job_get_fast_sync( struct object *obj ); - static int job_close_handle( struct object *obj, struct process *process, obj_handle_t handle ); - static void job_destroy( struct object *obj ); - -@@ -193,6 +197,7 @@ struct job - struct job *parent; - struct list parent_job_entry; /* list entry for parent job */ - struct list child_job_list; /* list of child jobs */ -+ struct fast_sync *fast_sync; /* fast synchronization object */ - }; - - static const struct object_ops job_ops = -@@ -215,6 +220,7 @@ static const struct object_ops job_ops = - default_unlink_name, /* unlink_name */ - no_open_file, /* open_file */ - no_kernel_obj_list, /* get_kernel_obj_list */ -+ job_get_fast_sync, /* get_fast_sync */ - job_close_handle, /* close_handle */ - job_destroy /* destroy */ - }; -@@ -239,6 +245,7 @@ static struct job *create_job_object( struct object *root, const struct unicode_ - job->completion_port = NULL; - job->completion_key = 0; - job->parent = NULL; -+ job->fast_sync = NULL; - } - } - return job; -@@ -377,6 +384,17 @@ static void terminate_job( struct job *job, int exit_code ) - job->terminating = 0; - job->signaled = 1; - wake_up( &job->obj, 0 ); -+ fast_set_event( job->fast_sync ); -+} -+ -+static struct fast_sync *job_get_fast_sync( struct object *obj ) -+{ -+ struct job *job = (struct job *)obj; -+ -+ if (!job->fast_sync) -+ job->fast_sync = fast_create_event( FAST_SYNC_SERVER, 1, job->signaled ); -+ if (job->fast_sync) grab_object( job->fast_sync ); -+ return job->fast_sync; - } - - static int job_close_handle( struct object *obj, struct process *process, obj_handle_t handle ) -@@ -407,6 +425,8 @@ static void job_destroy( struct object *obj ) - list_remove( &job->parent_job_entry ); - release_object( job->parent ); - } -+ -+ if (job->fast_sync) release_object( job->fast_sync ); - } - - static void job_dump( struct object *obj, int verbose ) -@@ -637,6 +657,7 @@ struct process *create_process( int fd, struct process *parent, unsigned int fla - process->trace_data = 0; - process->rawinput_mouse = NULL; - process->rawinput_kbd = NULL; -+ process->fast_sync = NULL; - list_init( &process->kernel_object ); - list_init( &process->thread_list ); - list_init( &process->locks ); -@@ -739,6 +760,8 @@ static void process_destroy( struct object *obj ) - if (process->token) release_object( process->token ); - free( process->dir_cache ); - free( process->image ); -+ -+ if (process->fast_sync) release_object( process->fast_sync ); - } - - /* dump a process on stdout for debugging purposes */ -@@ -770,6 +793,16 @@ static struct list *process_get_kernel_obj_list( struct object *obj ) - return &process->kernel_object; - } - -+static struct fast_sync *process_get_fast_sync( struct object *obj ) -+{ -+ struct process *process = (struct process *)obj; -+ -+ if (!process->fast_sync) -+ process->fast_sync = fast_create_event( FAST_SYNC_SERVER, 1, !process->running_threads ); -+ if (process->fast_sync) grab_object( process->fast_sync ); -+ return process->fast_sync; -+} -+ - static struct security_descriptor *process_get_sd( struct object *obj ) - { - static struct security_descriptor *process_default_sd; -@@ -950,6 +983,7 @@ static void process_killed( struct process *process ) - release_job_process( process ); - start_sigkill_timer( process ); - wake_up( &process->obj, 0 ); -+ fast_set_event( process->fast_sync ); - } - - /* add a thread to a process running threads list */ -diff --git a/server/process.h b/server/process.h -index 0e1a83859d9..7c25f998735 100644 ---- a/server/process.h -+++ b/server/process.h -@@ -87,6 +87,7 @@ struct process - const struct rawinput_device *rawinput_mouse; /* rawinput mouse device, if any */ - const struct rawinput_device *rawinput_kbd; /* rawinput keyboard device, if any */ - struct list kernel_object; /* list of kernel object pointers */ -+ struct fast_sync *fast_sync; /* fast synchronization object */ - }; - - /* process functions */ -diff --git a/server/protocol.def b/server/protocol.def -index 30624a7cbaf..8e49fc478cd 100644 ---- a/server/protocol.def -+++ b/server/protocol.def -@@ -3745,3 +3745,49 @@ struct handle_info - @REPLY - obj_handle_t handle; /* next thread handle */ - @END -+ -+ -+enum fast_sync_type -+{ -+ FAST_SYNC_SEMAPHORE = 1, -+ FAST_SYNC_MUTEX, -+ FAST_SYNC_EVENT, -+ FAST_SYNC_SERVER, -+ FAST_SYNC_QUEUE, -+}; -+ -+ -+/* Obtain a handle to the fast synchronization device object */ -+@REQ(get_fast_sync_device) -+@REPLY -+ obj_handle_t handle; /* handle to the device */ -+@END -+ -+ -+/* Get the fast synchronization object associated with the given handle */ -+@REQ(get_fast_sync_obj) -+ obj_handle_t handle; /* handle to the object */ -+@REPLY -+ obj_handle_t handle; /* handle to the fast synchronization object */ -+ int obj; /* linux object */ -+ int type; /* object type */ -+ unsigned int access; /* handle access rights */ -+@END -+ -+ -+/* Begin a fast wait on a message queue */ -+@REQ(fast_select_queue) -+ obj_handle_t handle; /* handle to the queue */ -+@END -+ -+ -+/* End a fast wait on a message queue */ -+@REQ(fast_unselect_queue) -+ obj_handle_t handle; /* handle to the queue */ -+ int signaled; /* was the queue signaled? */ -+@END -+ -+ -+/* Check if there are any user APCs queued */ -+@REQ(check_user_apc) -+@END -diff --git a/server/queue.c b/server/queue.c -index e4903bcb79f..24770b1dd02 100644 ---- a/server/queue.c -+++ b/server/queue.c -@@ -140,6 +140,8 @@ struct msg_queue - struct thread_input *input; /* thread input descriptor */ - struct hook_table *hooks; /* hook table */ - timeout_t last_get_msg; /* time of last get message call */ -+ struct fast_sync *fast_sync; /* fast synchronization object */ -+ int in_fast_wait; /* are we in a fast wait? */ - }; - - struct hotkey -@@ -157,6 +159,7 @@ static int msg_queue_add_queue( struct object *obj, struct wait_queue_entry *ent - static void msg_queue_remove_queue( struct object *obj, struct wait_queue_entry *entry ); - static int msg_queue_signaled( struct object *obj, struct wait_queue_entry *entry ); - static void msg_queue_satisfied( struct object *obj, struct wait_queue_entry *entry ); -+static struct fast_sync *msg_queue_get_fast_sync( struct object *obj ); - static void msg_queue_destroy( struct object *obj ); - static void msg_queue_poll_event( struct fd *fd, int event ); - static void thread_input_dump( struct object *obj, int verbose ); -@@ -183,6 +186,7 @@ static const struct object_ops msg_queue_ops = - NULL, /* unlink_name */ - no_open_file, /* open_file */ - no_kernel_obj_list, /* get_kernel_obj_list */ -+ msg_queue_get_fast_sync, /* get_fast_sync */ - no_close_handle, /* close_handle */ - msg_queue_destroy /* destroy */ - }; -@@ -220,6 +224,7 @@ static const struct object_ops thread_input_ops = - NULL, /* unlink_name */ - no_open_file, /* open_file */ - no_kernel_obj_list, /* get_kernel_obj_list */ -+ no_get_fast_sync, /* get_fast_sync */ - no_close_handle, /* close_handle */ - thread_input_destroy /* destroy */ - }; -@@ -305,6 +310,8 @@ static struct msg_queue *create_msg_queue( struct thread *thread, struct thread_ - queue->input = (struct thread_input *)grab_object( input ); - queue->hooks = NULL; - queue->last_get_msg = current_time; -+ queue->fast_sync = NULL; -+ queue->in_fast_wait = 0; - list_init( &queue->send_result ); - list_init( &queue->callback_result ); - list_init( &queue->pending_timers ); -@@ -480,7 +487,11 @@ static inline void set_queue_bits( struct msg_queue *queue, unsigned int bits ) - { - queue->wake_bits |= bits; - queue->changed_bits |= bits; -- if (is_signaled( queue )) wake_up( &queue->obj, 0 ); -+ if (is_signaled( queue )) -+ { -+ wake_up( &queue->obj, 0 ); -+ fast_set_event( queue->fast_sync ); -+ } - } - - /* clear some queue bits */ -@@ -488,6 +499,8 @@ static inline void clear_queue_bits( struct msg_queue *queue, unsigned int bits - { - queue->wake_bits &= ~bits; - queue->changed_bits &= ~bits; -+ if (!is_signaled( queue )) -+ fast_reset_event( queue->fast_sync ); - } - - /* check whether msg is a keyboard message */ -@@ -937,6 +950,10 @@ static int is_queue_hung( struct msg_queue *queue ) - if (get_wait_queue_thread(entry)->queue == queue) - return 0; /* thread is waiting on queue -> not hung */ - } -+ -+ if (queue->in_fast_wait) -+ return 0; /* thread is waiting on queue in absentia -> not hung */ -+ - return 1; - } - -@@ -997,6 +1014,17 @@ static void msg_queue_satisfied( struct object *obj, struct wait_queue_entry *en - struct msg_queue *queue = (struct msg_queue *)obj; - queue->wake_mask = 0; - queue->changed_mask = 0; -+ fast_reset_event( queue->fast_sync ); -+} -+ -+static struct fast_sync *msg_queue_get_fast_sync( struct object *obj ) -+{ -+ struct msg_queue *queue = (struct msg_queue *)obj; -+ -+ if (!queue->fast_sync) -+ queue->fast_sync = fast_create_event( FAST_SYNC_QUEUE, 1, is_signaled( queue ) ); -+ if (queue->fast_sync) grab_object( queue->fast_sync ); -+ return queue->fast_sync; - } - - static void msg_queue_destroy( struct object *obj ) -@@ -1035,6 +1063,7 @@ static void msg_queue_destroy( struct object *obj ) - release_object( queue->input ); - if (queue->hooks) release_object( queue->hooks ); - if (queue->fd) release_object( queue->fd ); -+ if (queue->fast_sync) release_object( queue->fast_sync ); - } - - static void msg_queue_poll_event( struct fd *fd, int event ) -@@ -1045,6 +1074,7 @@ static void msg_queue_poll_event( struct fd *fd, int event ) - if (event & (POLLERR | POLLHUP)) set_fd_events( fd, -1 ); - else set_fd_events( queue->fd, 0 ); - wake_up( &queue->obj, 0 ); -+ fast_set_event( queue->fast_sync ); - } - - static void thread_input_dump( struct object *obj, int verbose ) -@@ -2421,8 +2451,20 @@ DECL_HANDLER(set_queue_mask) - if (is_signaled( queue )) - { - /* if skip wait is set, do what would have been done in the subsequent wait */ -- if (req->skip_wait) queue->wake_mask = queue->changed_mask = 0; -- else wake_up( &queue->obj, 0 ); -+ if (req->skip_wait) -+ { -+ queue->wake_mask = queue->changed_mask = 0; -+ fast_reset_event( queue->fast_sync ); -+ } -+ else -+ { -+ wake_up( &queue->obj, 0 ); -+ fast_set_event( queue->fast_sync ); -+ } -+ } -+ else -+ { -+ fast_reset_event( queue->fast_sync ); - } - } - } -@@ -2437,6 +2479,8 @@ DECL_HANDLER(get_queue_status) - reply->wake_bits = queue->wake_bits; - reply->changed_bits = queue->changed_bits; - queue->changed_bits &= ~req->clear_bits; -+ if (!is_signaled( queue )) -+ fast_reset_event( queue->fast_sync ); - } - else reply->wake_bits = reply->changed_bits = 0; - } -@@ -2620,6 +2664,9 @@ DECL_HANDLER(get_message) - if (filter & QS_INPUT) queue->changed_bits &= ~QS_INPUT; - if (filter & QS_PAINT) queue->changed_bits &= ~QS_PAINT; - -+ if (!is_signaled( queue )) -+ fast_reset_event( queue->fast_sync ); -+ - /* then check for posted messages */ - if ((filter & QS_POSTMESSAGE) && - get_posted_message( queue, get_win, req->get_first, req->get_last, req->flags, reply )) -@@ -2673,6 +2720,7 @@ DECL_HANDLER(get_message) - if (get_win == -1 && current->process->idle_event) set_event( current->process->idle_event ); - queue->wake_mask = req->wake_mask; - queue->changed_mask = req->changed_mask; -+ fast_reset_event( queue->fast_sync ); - set_error( STATUS_PENDING ); /* FIXME */ - } - -@@ -3394,3 +3442,61 @@ DECL_HANDLER(get_rawinput_devices) - devices[i++] = e->device; - } - } -+ -+DECL_HANDLER(fast_select_queue) -+{ -+ struct msg_queue *queue; -+ -+ if (!(queue = (struct msg_queue *)get_handle_obj( current->process, req->handle, -+ SYNCHRONIZE, &msg_queue_ops ))) -+ return; -+ -+ /* a thread can only wait on its own queue */ -+ if (current->queue != queue || queue->in_fast_wait) -+ { -+ set_error( STATUS_ACCESS_DENIED ); -+ } -+ else -+ { -+ if (current->process->idle_event && !(queue->wake_mask & QS_SMRESULT)) -+ set_event( current->process->idle_event ); -+ -+ if (queue->fd) -+ set_fd_events( queue->fd, POLLIN ); -+ -+ queue->in_fast_wait = 1; -+ } -+ -+ release_object( queue ); -+} -+ -+DECL_HANDLER(fast_unselect_queue) -+{ -+ struct msg_queue *queue; -+ -+ if (!(queue = (struct msg_queue *)get_handle_obj( current->process, req->handle, -+ SYNCHRONIZE, &msg_queue_ops ))) -+ return; -+ -+ if (current->queue != queue || !queue->in_fast_wait) -+ { -+ set_error( STATUS_ACCESS_DENIED ); -+ } -+ else -+ { -+ if (queue->fd) -+ set_fd_events( queue->fd, 0 ); -+ -+ if (req->signaled) -+ msg_queue_satisfied( &queue->obj, NULL ); -+ -+ queue->in_fast_wait = 0; -+ } -+ -+ release_object( queue ); -+} -+ -+DECL_HANDLER(check_user_apc) -+{ -+ if (list_empty( ¤t->user_apc )) set_error( STATUS_TIMEOUT ); -+} -diff --git a/server/registry.c b/server/registry.c -index 68ec4f9e39a..c9110c56e6e 100644 ---- a/server/registry.c -+++ b/server/registry.c -@@ -188,6 +188,7 @@ static const struct object_ops key_ops = - NULL, /* unlink_name */ - no_open_file, /* open_file */ - no_kernel_obj_list, /* get_kernel_obj_list */ -+ no_get_fast_sync, /* get_fast_sync */ - key_close_handle, /* close_handle */ - key_destroy /* destroy */ - }; -diff --git a/server/request.c b/server/request.c -index 29b63600f15..ae6d9d0b52c 100644 ---- a/server/request.c -+++ b/server/request.c -@@ -109,6 +109,7 @@ static const struct object_ops master_socket_ops = - NULL, /* unlink_name */ - no_open_file, /* open_file */ - no_kernel_obj_list, /* get_kernel_obj_list */ -+ no_get_fast_sync, /* get_fast_sync */ - no_close_handle, /* close_handle */ - master_socket_destroy /* destroy */ - }; -diff --git a/server/request.h b/server/request.h -index a8d74f01f96..1c778fce75f 100644 ---- a/server/request.h -+++ b/server/request.h -@@ -395,6 +395,11 @@ DECL_HANDLER(terminate_job); - DECL_HANDLER(suspend_process); - DECL_HANDLER(resume_process); - DECL_HANDLER(get_next_thread); -+DECL_HANDLER(get_fast_sync_device); -+DECL_HANDLER(get_fast_sync_obj); -+DECL_HANDLER(fast_select_queue); -+DECL_HANDLER(fast_unselect_queue); -+DECL_HANDLER(check_user_apc); - - #ifdef WANT_REQUEST_HANDLERS - -@@ -677,6 +682,11 @@ static const req_handler req_handlers[REQ_NB_REQUESTS] = - (req_handler)req_suspend_process, - (req_handler)req_resume_process, - (req_handler)req_get_next_thread, -+ (req_handler)req_get_fast_sync_device, -+ (req_handler)req_get_fast_sync_obj, -+ (req_handler)req_fast_select_queue, -+ (req_handler)req_fast_unselect_queue, -+ (req_handler)req_check_user_apc, - }; - - C_ASSERT( sizeof(abstime_t) == 8 ); -@@ -2258,6 +2268,22 @@ C_ASSERT( FIELD_OFFSET(struct get_next_thread_request, flags) == 28 ); - C_ASSERT( sizeof(struct get_next_thread_request) == 32 ); - C_ASSERT( FIELD_OFFSET(struct get_next_thread_reply, handle) == 8 ); - C_ASSERT( sizeof(struct get_next_thread_reply) == 16 ); -+C_ASSERT( sizeof(struct get_fast_sync_device_request) == 16 ); -+C_ASSERT( FIELD_OFFSET(struct get_fast_sync_device_reply, handle) == 8 ); -+C_ASSERT( sizeof(struct get_fast_sync_device_reply) == 16 ); -+C_ASSERT( FIELD_OFFSET(struct get_fast_sync_obj_request, handle) == 12 ); -+C_ASSERT( sizeof(struct get_fast_sync_obj_request) == 16 ); -+C_ASSERT( FIELD_OFFSET(struct get_fast_sync_obj_reply, handle) == 8 ); -+C_ASSERT( FIELD_OFFSET(struct get_fast_sync_obj_reply, obj) == 12 ); -+C_ASSERT( FIELD_OFFSET(struct get_fast_sync_obj_reply, type) == 16 ); -+C_ASSERT( FIELD_OFFSET(struct get_fast_sync_obj_reply, access) == 20 ); -+C_ASSERT( sizeof(struct get_fast_sync_obj_reply) == 24 ); -+C_ASSERT( FIELD_OFFSET(struct fast_select_queue_request, handle) == 12 ); -+C_ASSERT( sizeof(struct fast_select_queue_request) == 16 ); -+C_ASSERT( FIELD_OFFSET(struct fast_unselect_queue_request, handle) == 12 ); -+C_ASSERT( FIELD_OFFSET(struct fast_unselect_queue_request, signaled) == 16 ); -+C_ASSERT( sizeof(struct fast_unselect_queue_request) == 24 ); -+C_ASSERT( sizeof(struct check_user_apc_request) == 16 ); - - #endif /* WANT_REQUEST_HANDLERS */ - -diff --git a/server/semaphore.c b/server/semaphore.c -index 543d5883ec8..6b7e737c848 100644 ---- a/server/semaphore.c -+++ b/server/semaphore.c -@@ -55,12 +55,15 @@ struct semaphore - struct object obj; /* object header */ - unsigned int count; /* current count */ - unsigned int max; /* maximum possible count */ -+ struct fast_sync *fast_sync; /* fast synchronization object */ - }; - - static void semaphore_dump( struct object *obj, int verbose ); - static int semaphore_signaled( struct object *obj, struct wait_queue_entry *entry ); - static void semaphore_satisfied( struct object *obj, struct wait_queue_entry *entry ); - static int semaphore_signal( struct object *obj, unsigned int access ); -+static struct fast_sync *semaphore_get_fast_sync( struct object *obj ); -+static void semaphore_destroy( struct object *obj ); - - static const struct object_ops semaphore_ops = - { -@@ -82,8 +85,9 @@ static const struct object_ops semaphore_ops = - default_unlink_name, /* unlink_name */ - no_open_file, /* open_file */ - no_kernel_obj_list, /* get_kernel_obj_list */ -+ semaphore_get_fast_sync, /* get_fast_sync */ - no_close_handle, /* close_handle */ -- no_destroy /* destroy */ -+ semaphore_destroy /* destroy */ - }; - - -@@ -105,6 +109,7 @@ static struct semaphore *create_semaphore( struct object *root, const struct uni - /* initialize it if it didn't already exist */ - sem->count = initial; - sem->max = max; -+ sem->fast_sync = NULL; - } - } - return sem; -@@ -167,6 +172,23 @@ static int semaphore_signal( struct object *obj, unsigned int access ) - return release_semaphore( sem, 1, NULL ); - } - -+static struct fast_sync *semaphore_get_fast_sync( struct object *obj ) -+{ -+ struct semaphore *semaphore = (struct semaphore *)obj; -+ -+ if (!semaphore->fast_sync) -+ semaphore->fast_sync = fast_create_semaphore( semaphore->count, semaphore->max ); -+ if (semaphore->fast_sync) grab_object( semaphore->fast_sync ); -+ return semaphore->fast_sync; -+} -+ -+static void semaphore_destroy( struct object *obj ) -+{ -+ struct semaphore *semaphore = (struct semaphore *)obj; -+ -+ if (semaphore->fast_sync) release_object( semaphore->fast_sync ); -+} -+ - /* create a semaphore */ - DECL_HANDLER(create_semaphore) - { -diff --git a/server/serial.c b/server/serial.c -index d3ea4cbe420..8c68aafe61f 100644 ---- a/server/serial.c -+++ b/server/serial.c -@@ -104,6 +104,7 @@ static const struct object_ops serial_ops = - NULL, /* unlink_name */ - no_open_file, /* open_file */ - no_kernel_obj_list, /* get_kernel_obj_list */ -+ default_fd_get_fast_sync, /* get_fast_sync */ - no_close_handle, /* close_handle */ - serial_destroy /* destroy */ - }; -diff --git a/server/signal.c b/server/signal.c -index 0c22c157f2b..ad555fd51af 100644 ---- a/server/signal.c -+++ b/server/signal.c -@@ -79,6 +79,7 @@ static const struct object_ops handler_ops = - NULL, /* unlink_name */ - no_open_file, /* open_file */ - no_kernel_obj_list, /* get_kernel_obj_list */ -+ no_get_fast_sync, /* get_fast_sync */ - no_close_handle, /* close_handle */ - handler_destroy /* destroy */ - }; -diff --git a/server/sock.c b/server/sock.c -index b4649abbf08..855454f05ee 100644 ---- a/server/sock.c -+++ b/server/sock.c -@@ -214,6 +214,7 @@ static const struct object_ops sock_ops = - NULL, /* unlink_name */ - no_open_file, /* open_file */ - no_kernel_obj_list, /* get_kernel_obj_list */ -+ default_fd_get_fast_sync, /* get_fast_sync */ - sock_close_handle, /* close_handle */ - sock_destroy /* destroy */ - }; -@@ -2072,6 +2073,7 @@ static const struct object_ops ifchange_ops = - NULL, /* unlink_name */ - no_open_file, /* open_file */ - no_kernel_obj_list, /* get_kernel_obj_list */ -+ no_get_fast_sync, /* get_fast_sync */ - no_close_handle, /* close_handle */ - ifchange_destroy /* destroy */ - }; -@@ -2292,6 +2294,7 @@ static const struct object_ops socket_device_ops = - default_unlink_name, /* unlink_name */ - socket_device_open_file, /* open_file */ - no_kernel_obj_list, /* get_kernel_obj_list */ -+ no_get_fast_sync, /* get_fast_sync */ - no_close_handle, /* close_handle */ - no_destroy /* destroy */ - }; -diff --git a/server/symlink.c b/server/symlink.c -index 3879bb685f7..241e13fa369 100644 ---- a/server/symlink.c -+++ b/server/symlink.c -@@ -84,6 +84,7 @@ static const struct object_ops symlink_ops = - default_unlink_name, /* unlink_name */ - no_open_file, /* open_file */ - no_kernel_obj_list, /* get_kernel_obj_list */ -+ no_get_fast_sync, /* get_fast_sync */ - no_close_handle, /* close_handle */ - symlink_destroy /* destroy */ - }; -diff --git a/server/thread.c b/server/thread.c -index 0c7f11c0da1..0cae26c0653 100644 ---- a/server/thread.c -+++ b/server/thread.c -@@ -109,6 +109,7 @@ static const struct object_ops thread_apc_ops = - NULL, /* unlink_name */ - no_open_file, /* open_file */ - no_kernel_obj_list, /* get_kernel_obj_list */ -+ no_get_fast_sync, /* get_fast_sync */ - no_close_handle, /* close_handle */ - thread_apc_destroy /* destroy */ - }; -@@ -146,6 +147,7 @@ static const struct object_ops context_ops = - NULL, /* unlink_name */ - no_open_file, /* open_file */ - no_kernel_obj_list, /* get_kernel_obj_list */ -+ no_get_fast_sync, /* get_fast_sync */ - no_close_handle, /* close_handle */ - no_destroy /* destroy */ - }; -@@ -173,6 +175,7 @@ static int thread_signaled( struct object *obj, struct wait_queue_entry *entry ) - static unsigned int thread_map_access( struct object *obj, unsigned int access ); - static void thread_poll_event( struct fd *fd, int event ); - static struct list *thread_get_kernel_obj_list( struct object *obj ); -+static struct fast_sync *thread_get_fast_sync( struct object *obj ); - static void destroy_thread( struct object *obj ); - - static const struct object_ops thread_ops = -@@ -195,6 +198,7 @@ static const struct object_ops thread_ops = - NULL, /* unlink_name */ - no_open_file, /* open_file */ - thread_get_kernel_obj_list, /* get_kernel_obj_list */ -+ thread_get_fast_sync, /* get_fast_sync */ - no_close_handle, /* close_handle */ - destroy_thread /* destroy */ - }; -@@ -242,6 +246,7 @@ static inline void init_thread_structure( struct thread *thread ) - thread->token = NULL; - thread->desc = NULL; - thread->desc_len = 0; -+ thread->fast_sync = NULL; - - thread->creation_time = current_time; - thread->exit_time = 0; -@@ -391,6 +396,16 @@ static struct list *thread_get_kernel_obj_list( struct object *obj ) - return &thread->kernel_object; - } - -+static struct fast_sync *thread_get_fast_sync( struct object *obj ) -+{ -+ struct thread *thread = (struct thread *)obj; -+ -+ if (!thread->fast_sync) -+ thread->fast_sync = fast_create_event( FAST_SYNC_SERVER, 1, thread->state == TERMINATED ); -+ if (thread->fast_sync) grab_object( thread->fast_sync ); -+ return thread->fast_sync; -+} -+ - /* cleanup everything that is no longer needed by a dead thread */ - /* used by destroy_thread and kill_thread */ - static void cleanup_thread( struct thread *thread ) -@@ -445,6 +460,7 @@ static void destroy_thread( struct object *obj ) - release_object( thread->process ); - if (thread->id) free_ptid( thread->id ); - if (thread->token) release_object( thread->token ); -+ if (thread->fast_sync) release_object( thread->fast_sync ); - } - - /* dump a thread on stdout for debugging purposes */ -@@ -1125,8 +1141,9 @@ static int queue_apc( struct process *process, struct thread *thread, struct thr - { - if (thread->state == TERMINATED) return 0; - if (!(queue = get_apc_queue( thread, apc->call.type ))) return 1; -- /* send signal for system APCs if needed */ -- if (queue == &thread->system_apc && list_empty( queue ) && !is_in_apc_wait( thread )) -+ /* send signal if needed */ -+ if ((queue == &thread->system_apc || fast_user_apc_needs_signal()) -+ && list_empty( queue ) && !is_in_apc_wait( thread )) - { - if (!send_thread_signal( thread, SIGUSR1 )) return 0; - } -@@ -1279,7 +1296,9 @@ void kill_thread( struct thread *thread, int violent_death ) - } - kill_console_processes( thread, 0 ); - abandon_mutexes( thread ); -+ fast_abandon_mutexes( thread->id ); - wake_up( &thread->obj, 0 ); -+ fast_set_event( thread->fast_sync ); - if (violent_death) send_thread_signal( thread, SIGQUIT ); - cleanup_thread( thread ); - remove_process_thread( thread->process, thread ); -diff --git a/server/thread.h b/server/thread.h -index 8dcf966a90a..caabbe5bfd6 100644 ---- a/server/thread.h -+++ b/server/thread.h -@@ -90,6 +90,7 @@ struct thread - struct list kernel_object; /* list of kernel object pointers */ - data_size_t desc_len; /* thread description length in bytes */ - WCHAR *desc; /* thread description string */ -+ struct fast_sync *fast_sync; /* fast synchronization object */ - }; - - extern struct thread *current; -diff --git a/server/timer.c b/server/timer.c -index 5e265d2ddf6..4f9e5e61aa1 100644 ---- a/server/timer.c -+++ b/server/timer.c -@@ -62,11 +62,13 @@ struct timer - struct thread *thread; /* thread that set the APC function */ - client_ptr_t callback; /* callback APC function */ - client_ptr_t arg; /* callback argument */ -+ struct fast_sync *fast_sync; /* fast synchronization object */ - }; - - static void timer_dump( struct object *obj, int verbose ); - static int timer_signaled( struct object *obj, struct wait_queue_entry *entry ); - static void timer_satisfied( struct object *obj, struct wait_queue_entry *entry ); -+static struct fast_sync *timer_get_fast_sync( struct object *obj ); - static void timer_destroy( struct object *obj ); - - static const struct object_ops timer_ops = -@@ -89,6 +91,7 @@ static const struct object_ops timer_ops = - default_unlink_name, /* unlink_name */ - no_open_file, /* open_file */ - no_kernel_obj_list, /* get_kernel_obj_list */ -+ timer_get_fast_sync, /* get_fast_sync */ - no_close_handle, /* close_handle */ - timer_destroy /* destroy */ - }; -@@ -111,6 +114,7 @@ static struct timer *create_timer( struct object *root, const struct unicode_str - timer->period = 0; - timer->timeout = NULL; - timer->thread = NULL; -+ timer->fast_sync = NULL; - } - } - return timer; -@@ -152,6 +156,7 @@ static void timer_callback( void *private ) - /* wake up waiters */ - timer->signaled = 1; - wake_up( &timer->obj, 0 ); -+ fast_set_event( timer->fast_sync ); - } - - /* cancel a running timer */ -@@ -182,6 +187,7 @@ static int set_timer( struct timer *timer, timeout_t expire, unsigned int period - { - period = 0; /* period doesn't make any sense for a manual timer */ - timer->signaled = 0; -+ fast_reset_event( timer->fast_sync ); - } - timer->when = (expire <= 0) ? expire - monotonic_time : max( expire, current_time ); - timer->period = period; -@@ -216,6 +222,16 @@ static void timer_satisfied( struct object *obj, struct wait_queue_entry *entry - if (!timer->manual) timer->signaled = 0; - } - -+static struct fast_sync *timer_get_fast_sync( struct object *obj ) -+{ -+ struct timer *timer = (struct timer *)obj; -+ -+ if (!timer->fast_sync) -+ timer->fast_sync = fast_create_event( FAST_SYNC_SERVER, timer->manual, timer->signaled ); -+ if (timer->fast_sync) grab_object( timer->fast_sync ); -+ return timer->fast_sync; -+} -+ - static void timer_destroy( struct object *obj ) - { - struct timer *timer = (struct timer *)obj; -@@ -223,6 +239,7 @@ static void timer_destroy( struct object *obj ) - - if (timer->timeout) remove_timeout_user( timer->timeout ); - if (timer->thread) release_object( timer->thread ); -+ if (timer->fast_sync) release_object( timer->fast_sync ); - } - - /* create a timer */ -diff --git a/server/token.c b/server/token.c -index ad5d7cda323..7d73e9d7caa 100644 ---- a/server/token.c -+++ b/server/token.c -@@ -173,6 +173,7 @@ static const struct object_ops token_ops = - NULL, /* unlink_name */ - no_open_file, /* open_file */ - no_kernel_obj_list, /* get_kernel_obj_list */ -+ no_get_fast_sync, /* get_fast_sync */ - no_close_handle, /* close_handle */ - token_destroy /* destroy */ - }; -diff --git a/server/trace.c b/server/trace.c -index 1c51bffe1aa..e7b760b4634 100644 ---- a/server/trace.c -+++ b/server/trace.c -@@ -4545,6 +4545,43 @@ static void dump_get_next_thread_reply( const struct get_next_thread_reply *req - fprintf( stderr, " handle=%04x", req->handle ); - } - -+static void dump_get_fast_sync_device_request( const struct get_fast_sync_device_request *req ) -+{ -+} -+ -+static void dump_get_fast_sync_device_reply( const struct get_fast_sync_device_reply *req ) -+{ -+ fprintf( stderr, " handle=%04x", req->handle ); -+} -+ -+static void dump_get_fast_sync_obj_request( const struct get_fast_sync_obj_request *req ) -+{ -+ fprintf( stderr, " handle=%04x", req->handle ); -+} -+ -+static void dump_get_fast_sync_obj_reply( const struct get_fast_sync_obj_reply *req ) -+{ -+ fprintf( stderr, " handle=%04x", req->handle ); -+ fprintf( stderr, ", obj=%d", req->obj ); -+ fprintf( stderr, ", type=%d", req->type ); -+ fprintf( stderr, ", access=%08x", req->access ); -+} -+ -+static void dump_fast_select_queue_request( const struct fast_select_queue_request *req ) -+{ -+ fprintf( stderr, " handle=%04x", req->handle ); -+} -+ -+static void dump_fast_unselect_queue_request( const struct fast_unselect_queue_request *req ) -+{ -+ fprintf( stderr, " handle=%04x", req->handle ); -+ fprintf( stderr, ", signaled=%d", req->signaled ); -+} -+ -+static void dump_check_user_apc_request( const struct check_user_apc_request *req ) -+{ -+} -+ - static const dump_func req_dumpers[REQ_NB_REQUESTS] = { - (dump_func)dump_new_process_request, - (dump_func)dump_get_new_process_info_request, -@@ -4822,6 +4859,11 @@ static const dump_func req_dumpers[REQ_NB_REQUESTS] = { - (dump_func)dump_suspend_process_request, - (dump_func)dump_resume_process_request, - (dump_func)dump_get_next_thread_request, -+ (dump_func)dump_get_fast_sync_device_request, -+ (dump_func)dump_get_fast_sync_obj_request, -+ (dump_func)dump_fast_select_queue_request, -+ (dump_func)dump_fast_unselect_queue_request, -+ (dump_func)dump_check_user_apc_request, - }; - - static const dump_func reply_dumpers[REQ_NB_REQUESTS] = { -@@ -5101,6 +5143,11 @@ static const dump_func reply_dumpers[REQ_NB_REQUESTS] = { - NULL, - NULL, - (dump_func)dump_get_next_thread_reply, -+ (dump_func)dump_get_fast_sync_device_reply, -+ (dump_func)dump_get_fast_sync_obj_reply, -+ NULL, -+ NULL, -+ NULL, - }; - - static const char * const req_names[REQ_NB_REQUESTS] = { -@@ -5380,6 +5427,11 @@ static const char * const req_names[REQ_NB_REQUESTS] = { - "suspend_process", - "resume_process", - "get_next_thread", -+ "get_fast_sync_device", -+ "get_fast_sync_obj", -+ "fast_select_queue", -+ "fast_unselect_queue", -+ "check_user_apc", - }; - - static const struct -diff --git a/server/winstation.c b/server/winstation.c -index 61f9f77c73f..2dee8067dd2 100644 ---- a/server/winstation.c -+++ b/server/winstation.c -@@ -87,6 +87,7 @@ static const struct object_ops winstation_ops = - default_unlink_name, /* unlink_name */ - no_open_file, /* open_file */ - no_kernel_obj_list, /* get_kernel_obj_list */ -+ no_get_fast_sync, /* get_fast_sync */ - winstation_close_handle, /* close_handle */ - winstation_destroy /* destroy */ - }; -@@ -127,6 +128,7 @@ static const struct object_ops desktop_ops = - default_unlink_name, /* unlink_name */ - no_open_file, /* open_file */ - no_kernel_obj_list, /* get_kernel_obj_list */ -+ no_get_fast_sync, /* get_fast_sync */ - desktop_close_handle, /* close_handle */ - desktop_destroy /* destroy */ - }; \ No newline at end of file