3992 lines
144 KiB
Diff
3992 lines
144 KiB
Diff
From e5bb1ac75bc1cf73ef93db0e095bdc4293e12fdf Mon Sep 17 00:00:00 2001
|
|
From: Mike Scott <example@example.com>
|
|
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 <assert.h>
|
|
+#include <errno.h>
|
|
+#include <fcntl.h>
|
|
+#include <limits.h>
|
|
+#include <stdarg.h>
|
|
+#include <stdio.h>
|
|
+#include <stdlib.h>
|
|
+#ifdef HAVE_SYS_MMAN_H
|
|
+# include <sys/mman.h>
|
|
+#endif
|
|
+#ifdef HAVE_SYS_STAT_H
|
|
+# include <sys/stat.h>
|
|
+#endif
|
|
+#ifdef HAVE_SYS_SYSCALL_H
|
|
+# include <sys/syscall.h>
|
|
+#endif
|
|
+#include <unistd.h>
|
|
+
|
|
+#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) : "<no name>", 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) : "<no name>",
|
|
+ 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) : "<no name>", 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 <errno.h>
|
|
+#include <fcntl.h>
|
|
+#include <limits.h>
|
|
+#include <stdio.h>
|
|
+#include <stdarg.h>
|
|
+#ifdef HAVE_SYS_MMAN_H
|
|
+# include <sys/mman.h>
|
|
+#endif
|
|
+#ifdef HAVE_SYS_STAT_H
|
|
+# include <sys/stat.h>
|
|
+#endif
|
|
+#ifdef HAVE_SYS_SYSCALL_H
|
|
+# include <sys/syscall.h>
|
|
+#endif
|
|
+#include <unistd.h>
|
|
+
|
|
+#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 */
|
|
|