From 78237f36ac9ad1a2ac5574377d812dc7a85d06ed Mon Sep 17 00:00:00 2001 From: Philipp Date: Wed, 21 Jul 2021 19:46:25 +0200 Subject: [PATCH] Initial commit --- default.nix | 175 ++ dxvk.nix | 34 + fonts.nix | 39 + patches/fsync.patch | 3991 +++++++++++++++++++++++++++++++++++++++++ patches/joyaxis.patch | 90 + win10.reg | 18 + 6 files changed, 4347 insertions(+) create mode 100644 default.nix create mode 100644 dxvk.nix create mode 100644 fonts.nix create mode 100644 patches/fsync.patch create mode 100644 patches/joyaxis.patch create mode 100644 win10.reg diff --git a/default.nix b/default.nix new file mode 100644 index 0000000..8b6b6d8 --- /dev/null +++ b/default.nix @@ -0,0 +1,175 @@ +{ stdenv, lib, pkgs, fetchurl, nix, pkgsCross, + installDir ? "$HOME/.local/starcitizen", + launcherCache ? "$HOME/.local/share", + prefixBaseDir ? "$HOME/.winenix", + binName ? "starcitizen", + launcherArgs ? [ "--use-gl=osmesa" ], + winePackage ? pkgs.wineWowPackages.unstable, + wineGlobalEnv ? [ "DXVK_STATE_CACHE=0" ], + winePatches ? [ ./patches/joyaxis.patch ], + virtualDesktop ? null, + registryFiles ? [ ] +}: + +#check wether string is two positive integers separated by x +assert virtualDesktop == null || +(lib.strings.toInt(lib.strings.concatStrings(lib.strings.splitString "x" virtualDesktop))) > 0; +#check whether paths are not terminated with a / +assert !(builtins.any (lib.strings.hasSuffix "/") [ installDir launcherCache prefixBaseDir ]); + +let + winePkg = winePackage.overrideAttrs (attrs: { + patches = attrs.patches ++ winePatches; +}); + + dxvk = (with import ~/repositories/nixpkgs/default.nix {}; pkgsCross.mingwW64.callPackage ./dxvk.nix { }); + fonts = pkgs.callPackage ./fonts.nix {}; + wine64 = "${winePkg}/bin/wine64"; + wineMulti = "${winePkg}/bin/wine"; + regEdit = "${wine64} regedit"; + reg = "${wine64} reg"; + rsiInstaller = fetchurl { + url = "https://install.robertsspaceindustries.com/star-citizen/RSI-Setup-1.4.11.exe"; + sha256 = "1afc4b36dd1e22d75df8da2ecb925833f9b10327c991be20e138503cde627022"; + }; + win10Reg = pkgs.writeTextFile { + name = "win10.reg"; + text = ./win10.reg; + }; + script = pkgs.writeShellScriptBin binName '' + #sane bash + set -eo pipefail + + function installPrefix () { + if [ ! -d "$WINEPREFIX" ]; then + ${winePkg}/bin/wineboot -i + #set win 10 + ${regEdit} ${win10Reg} + + #install dxvk + for library in ${dxvk}/bin/*.dll ; do + cp $library "$WINEPREFIX/drive_c/windows/system32" + local libraryFile=$(basename $library) + ${reg} add 'HKEY_CURRENT_USER\Software\Wine\DllOverrides' /v ''${libraryFile/\.dll/} /d native /f + done + + #install fonts + cp ${fonts}/share/fonts/*.TTF "$WINEPREFIX/drive_c/windows/Fonts" + ${regEdit} ${fonts}/share/regs/fonts.reg + + + #symlink persistent game and launcher binaries into prefix. + gameBinariesPers="${installDir}/Roberts Space Industries" + gameBinariesLinkDest="$WINEPREFIX/drive_c/Program Files/" + + mkdir -p "$gameBinariesPers" "$gameBinariesLinkDest" + ln -s "$gameBinariesPers" "$gameBinariesLinkDest" + + #symlink launcher cache + launcherCachePers="${launcherCache}/rsilauncher" + mkdir -p "$launcherCachePers" "$WINEPREFIX/drive_c/users/$USER/AppData/Roaming/" + ln -s "$launcherCachePers" "$WINEPREFIX/drive_c/users/$USER/AppData/Roaming/" + + #apply custom user registry files + ${lib.concatMapStrings (x: "${regEdit} " + x + "\n") registryFiles} + + fi + } + + function installLauncher () { + installPrefix + + #install the launcher + echo "Install with default parameters, launcherArgs won't be used when you launch the launcher from the setup" + ${winePkg}/bin/wine ${rsiInstaller} + } + + function runGame () { + installPrefix + + #install the launcher conditionally here and not in the launcher to allow force reinstalls. + if [ ! -f "${installDir}/Roberts Space Industries/RSI Launcher/RSI Launcher.exe" ]; then + installLauncher + fi + + ${wineMulti} \ + ${if virtualDesktop != null then "explorer /desktop=${binName},${virtualDesktop}" else ""} \ + "$WINEPREFIX/drive_c/Program Files/Roberts Space Industries/RSI Launcher/RSI Launcher.exe" \ + ${lib.concatStringsSep " " launcherArgs} + } + + function printPrefix () { + echo "$WINEPREFIX" + } + + function clean () { + find "${prefixBaseDir}" -maxdepth 1 -type d -name ????????????????????????????????-"${binName}" -and -not -wholename "$WINEPREFIX" -delete + } + + function removePrefix () { + #Good thing the GPL contains a no warranty clause + rm -rf "$WINEPREFIX" + } + + function help () { + echo "Gid gud!" + } + + function wine () { + installPrefix + ${winePkg}/bin/"$@" + } + + + #base setup + if [ ! -d "${installDir}" ]; then + mkdir -p "${installDir}/Roberts Space Industries" + fi + + #create base dir for nix installs + if [ ! -d ${prefixBaseDir} ]; then + mkdir -p "${prefixBaseDir}" + fi + + #core idea: Generate UUID from storage path of this script, ensure + #new prefix for any changes. Needs fast prefix installs and state outside. + uuid=$(basename $(${pkgs.nix}/bin/nix path-info "$BASH_SOURCE")) + export WINEPREFIX="${prefixBaseDir}/$uuid" + + #export all global env vars + ${lib.concatMapStrings (x: "export " + x + "\n") wineGlobalEnv } + + #parse input + case "$@" in + --install-prefix) + installPrefix + ;; + --install-launcher) + installLauncher + ;; + --clean) + clean + ;; + --removePrefix) + cleanPrefix + ;; + --run-game|"") + runGame + ;; + --print-prefix) + printPrefix + ;; + --help) + help + ;; + --wine*) + shift + wine "$@" + ;; + *) + help + exit 1 + esac + ''; + +in script diff --git a/dxvk.nix b/dxvk.nix new file mode 100644 index 0000000..077d384 --- /dev/null +++ b/dxvk.nix @@ -0,0 +1,34 @@ +{ stdenv, lib, fetchFromGitHub, glslang, buildPackages, writeScriptBin, pkgs, pkgsCross }: + +let + fix = writeScriptBin "x86_64-w64-mingw32-windres" '' + #!${stdenv.shell} + exec ${pkgsCross.mingwW64.buildPackages.binutils.bintools}/bin/x86_64-w64-mingw32-windres --preprocessor=x86_64-w64-mingw32-gcc --preprocessor-arg=-E --preprocessor-arg=-xc --preprocessor-arg=-DRC_INVOKED $@ + ''; + +in stdenv.mkDerivation rec { + pname = "dxvk"; + version = "1.9"; + + src = fetchFromGitHub { + owner = "doitsujin"; + repo = "dxvk"; + rev = "v${version}"; + sha256 = "01db23ncbrrq0cqnp25fg5plp88v5i5ri0i38m0wida8mw3mmjsa"; + }; + + CFLAGS="-fstack-protector"; + CPPFLAGS="-fstack-protector"; + mesonFlags = [ "--cross-file build-win64.txt" "--buildtype release" ]; + depsBuildBuild = [ + fix + buildPackages.gcc + buildPackages.meson + buildPackages.ninja + glslang + ]; + depsBuildTarget = [ + pkgs.windows.pthreads + ]; +} + diff --git a/fonts.nix b/fonts.nix new file mode 100644 index 0000000..806d337 --- /dev/null +++ b/fonts.nix @@ -0,0 +1,39 @@ +{ stdenv, lib, fetchurl, cabextract, pkgs }: + +let + regeditfile = pkgs.writeTextFile { + name = "fonts.reg"; + text = + '' + REGEDIT4 + + [HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Fonts] + "Arial Bold"="Arialbd.TTF" + "Arial Bold Italic"="Arialbi.TTF" + "Arial Italic"="Ariali.TTF" + "Arial"="Arial.TTF" + "Arial Black"="AriBlk.TTF" + ''; + }; + in stdenv.mkDerivation rec { + name = "ariblk-font"; + src = fetchurl { + url = "https://mirrors.kernel.org/gentoo/distfiles/arial32.exe"; + sha256 = "85297a4d146e9c87ac6f74822734bdee5f4b2a722d7eaa584b7f2cbf76f478f6"; + }; + srcblk = fetchurl { + url = "https://mirrors.kernel.org/gentoo/distfiles/arialb32.exe"; + sha256 = "a425f0ffb6a1a5ede5b979ed6177f4f4f4fdef6ae7c302a7b7720ef332fec0a8"; + }; + + nativeBuildInputs = [ cabextract ]; + unpackPhase = '' + cabextract ${src} + cabextract ${srcblk} + ''; + installPhase = '' + mkdir -p $out/share/fonts $out/share/regs + cp *.TTF $out/share/fonts + cp ${regeditfile} $out/share/regs/fonts.reg + ''; +} diff --git a/patches/fsync.patch b/patches/fsync.patch new file mode 100644 index 0000000..fa78bbf --- /dev/null +++ b/patches/fsync.patch @@ -0,0 +1,3991 @@ +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/joyaxis.patch b/patches/joyaxis.patch new file mode 100644 index 0000000..94b5898 --- /dev/null +++ b/patches/joyaxis.patch @@ -0,0 +1,90 @@ +diff --git a/dlls/joy.cpl/joy.h b/dlls/joy.cpl/joy.h +index ec7af4f787..672e8995e1 100644 +--- a/dlls/joy.cpl/joy.h ++++ b/dlls/joy.cpl/joy.h +@@ -47,8 +47,8 @@ struct Joystick { + struct Effect *effects; + }; + +-#define TEST_MAX_BUTTONS 32 +-#define TEST_MAX_AXES 4 ++#define TEST_MAX_BUTTONS 64 ++#define TEST_MAX_AXES 8 + + struct Graphics { + HWND hwnd; +diff --git a/dlls/joy.cpl/main.c b/dlls/joy.cpl/main.c +index 4ad9cf848c..35dba75978 100644 +--- a/dlls/joy.cpl/main.c ++++ b/dlls/joy.cpl/main.c +@@ -414,6 +414,16 @@ static DWORD WINAPI input_thread(void *param) + axes_pos[1][1] = state.lRy; + axes_pos[2][0] = state.lZ; + axes_pos[2][1] = state.lRz; ++ axes_pos[3][0] = 0; ++ axes_pos[3][1] = 0; ++ axes_pos[4][0] = 0; ++ axes_pos[4][1] = 0; ++ axes_pos[5][0] = 0; ++ axes_pos[5][1] = 0; ++ axes_pos[6][0] = 0; ++ axes_pos[6][1] = 0; ++ axes_pos[7][0] = 0; ++ axes_pos[7][1] = 0; + + /* Set pov values */ + for (j = 0; j < ARRAY_SIZE(pov_val); j++) +diff --git a/dlls/winebus.sys/bus_sdl.c b/dlls/winebus.sys/bus_sdl.c +index 0560e4bb12..13d3377b03 100644 +--- a/dlls/winebus.sys/bus_sdl.c ++++ b/dlls/winebus.sys/bus_sdl.c +@@ -402,10 +402,10 @@ static BOOL build_report_descriptor(struct platform_private *ext) + report_size = 0; + + axis_count = pSDL_JoystickNumAxes(ext->sdl_joystick); +- if (axis_count > 6) ++ if (axis_count > 16) + { +- FIXME("Clamping joystick to 6 axis\n"); +- axis_count = 6; ++ FIXME("Clamping joystick to 16 axis\n"); ++ axis_count = 16; + } + + ext->axis_start = report_size; +@@ -421,9 +421,9 @@ static BOOL build_report_descriptor(struct platform_private *ext) + ext->ball_start = report_size; + if (ball_count) + { +- if ((ball_count*2) + axis_count > 9) ++ if ((ball_count*2) + axis_count > 19) + { +- FIXME("Capping ball + axis at 9\n"); ++ FIXME("Capping ball + axis at 19\n"); + ball_count = (9-axis_count)/2; + } + descript_size += sizeof(REPORT_AXIS_HEADER); +diff --git a/dlls/winejoystick.drv/joystick_linux.c b/dlls/winejoystick.drv/joystick_linux.c +index 8d1a7b1a25..e579d99aa7 100644 +--- a/dlls/winejoystick.drv/joystick_linux.c ++++ b/dlls/winejoystick.drv/joystick_linux.c +@@ -260,9 +260,9 @@ LRESULT driver_joyGetDevCaps(DWORD_PTR dwDevID, LPJOYCAPSW lpCaps, DWORD dwSize) + lpCaps->wUmax = 0xFFFF; + lpCaps->wVmin = 0; + lpCaps->wVmax = 0xFFFF; +- lpCaps->wMaxAxes = 6; /* same as MS Joystick Driver */ ++ lpCaps->wMaxAxes = 16; /* same as MS Joystick Driver */ + lpCaps->wNumAxes = 0; /* nr of axes in use */ +- lpCaps->wMaxButtons = 32; /* same as MS Joystick Driver */ ++ lpCaps->wMaxButtons = 64; /* same as MS Joystick Driver */ + lpCaps->szRegKey[0] = 0; + lpCaps->szOEMVxD[0] = 0; + lpCaps->wCaps = 0; +@@ -326,6 +326,7 @@ LRESULT driver_joyGetPosEx(DWORD_PTR dwDevID, LPJOYINFOEX lpInfo) + switch (jstck->axesMap[ev.number]) { + case 0: /* X */ + case 8: /* Wheel */ ++ case 40: /* Mouse-like */ + jstck->x = ev.value; + break; + case 1: /* Y */ diff --git a/win10.reg b/win10.reg new file mode 100644 index 0000000..85d4ea5 --- /dev/null +++ b/win10.reg @@ -0,0 +1,18 @@ +REGEDIT4 + +[HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\ProductOptions] +"ProductType"="WinNT" + +[HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion] +"CSDVersion"="" +"CurrentBuildNumber"="10241" +"CurrentVersion"="10.0" +"ProductName"="Microsoft Windows 10" +"CurrentBuild"="17763" +"CurrentBuildNumber"="17763" +"CurrentMajorVersionNumber"=dword:0000000a +"CurrentMinorVersionNumber"=dword:00000000 + +[HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Windows] +"CSDVersion"="dword:00000000" +