From e5bb1ac75bc1cf73ef93db0e095bdc4293e12fdf Mon Sep 17 00:00:00 2001 From: Mike Scott Date: Thu, 26 Nov 2020 15:48:25 +0000 Subject: [PATCH] Applied fsync patches --- dlls/ntdll/Makefile.in | 1 + dlls/ntdll/unix/esync.c | 7 +- dlls/ntdll/unix/fsync.c | 1269 ++++++++++++++++++++++++++++++++ dlls/ntdll/unix/fsync.h | 49 ++ dlls/ntdll/unix/loader.c | 2 + dlls/ntdll/unix/server.c | 4 + dlls/ntdll/unix/sync.c | 72 +- dlls/ntdll/unix/unix_private.h | 1 + dlls/ntdll/unix/virtual.c | 1 + include/wine/server_protocol.h | 101 +++ server/Makefile.in | 1 + server/async.c | 2 + server/atom.c | 1 + server/change.c | 1 + server/clipboard.c | 1 + server/completion.c | 1 + server/console.c | 20 + server/debugger.c | 2 + server/device.c | 23 + server/directory.c | 2 + server/esync.c | 4 +- server/event.c | 37 + server/fd.c | 27 + server/file.c | 1 + server/file.h | 1 + server/fsync.c | 527 +++++++++++++ server/fsync.h | 34 + server/handle.c | 1 + server/hook.c | 1 + server/mailslot.c | 4 + server/main.c | 7 + server/mapping.c | 3 + server/mutex.c | 1 + server/named_pipe.c | 5 + server/object.h | 2 + server/process.c | 16 + server/process.h | 1 + server/protocol.def | 54 ++ server/queue.c | 42 ++ server/registry.c | 1 + server/request.c | 1 + server/request.h | 38 + server/semaphore.c | 1 + server/serial.c | 1 + server/signal.c | 1 + server/sock.c | 3 + server/symlink.c | 1 + server/thread.c | 31 + server/thread.h | 2 + server/timer.c | 17 + server/token.c | 1 + server/trace.c | 72 ++ server/winstation.c | 2 + 53 files changed, 2496 insertions(+), 5 deletions(-) create mode 100644 dlls/ntdll/unix/fsync.c create mode 100644 dlls/ntdll/unix/fsync.h create mode 100644 server/fsync.c create mode 100644 server/fsync.h diff --git a/dlls/ntdll/Makefile.in b/dlls/ntdll/Makefile.in index 5dc4ebb07fc..5159b81167f 100644 --- a/dlls/ntdll/Makefile.in +++ b/dlls/ntdll/Makefile.in @@ -48,6 +48,7 @@ C_SRCS = \ unix/env.c \ unix/esync.c \ unix/file.c \ + unix/fsync.c \ unix/loader.c \ unix/process.c \ unix/registry.c \ diff --git a/dlls/ntdll/unix/esync.c b/dlls/ntdll/unix/esync.c index ed801c71991..ac0326b1aba 100644 --- a/dlls/ntdll/unix/esync.c +++ b/dlls/ntdll/unix/esync.c @@ -57,6 +57,7 @@ #include "unix_private.h" #include "esync.h" +#include "fsync.h" WINE_DEFAULT_DEBUG_CHANNEL(esync); @@ -66,7 +67,7 @@ int do_esync(void) static int do_esync_cached = -1; if (do_esync_cached == -1) - do_esync_cached = getenv("WINEESYNC") && atoi(getenv("WINEESYNC")); + do_esync_cached = getenv("WINEESYNC") && atoi(getenv("WINEESYNC")) && !do_fsync(); return do_esync_cached; #else @@ -867,7 +868,7 @@ static NTSTATUS __esync_wait_objects( DWORD count, const HANDLE *handles, BOOLEA return ret; } - if (objs[count - 1] && objs[count - 1]->type == ESYNC_QUEUE) + if (count && objs[count - 1] && objs[count - 1]->type == ESYNC_QUEUE) msgwait = TRUE; if (has_esync && has_server) @@ -896,7 +897,7 @@ static NTSTATUS __esync_wait_objects( DWORD count, const HANDLE *handles, BOOLEA } } - if (wait_any || count == 1) + if (wait_any || count <= 1) { /* Try to check objects now, so we can obviate poll() at least. */ for (i = 0; i < count; i++) diff --git a/dlls/ntdll/unix/fsync.c b/dlls/ntdll/unix/fsync.c new file mode 100644 index 00000000000..5012425b95a --- /dev/null +++ b/dlls/ntdll/unix/fsync.c @@ -0,0 +1,1269 @@ +/* + * futex-based synchronization objects + * + * Copyright (C) 2018 Zebediah Figura + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#if 0 +#pragma makedep unix +#endif + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include +#ifdef HAVE_SYS_MMAN_H +# include +#endif +#ifdef HAVE_SYS_STAT_H +# include +#endif +#ifdef HAVE_SYS_SYSCALL_H +# include +#endif +#include + +#include "ntstatus.h" +#define WIN32_NO_STATUS +#define NONAMELESSUNION +#include "windef.h" +#include "winternl.h" +#include "wine/debug.h" +#include "wine/server.h" + +#include "unix_private.h" +#include "fsync.h" + +WINE_DEFAULT_DEBUG_CHANNEL(fsync); + +#include "pshpack4.h" +struct futex_wait_block +{ + int *addr; +#if __SIZEOF_POINTER__ == 4 + int pad; +#endif + int val; + int bitset; +}; +#include "poppack.h" + +static inline void small_pause(void) +{ +#if defined(__i386__) || defined(__x86_64__) + __asm__ __volatile__( "rep;nop" : : : "memory" ); +#else + __asm__ __volatile__( "" : : : "memory" ); +#endif +} + +static inline int futex_wait_multiple( const struct futex_wait_block *futexes, + int count, const struct timespec *timeout ) +{ + return syscall( __NR_futex, futexes, 31, count, timeout, 0, 0 ); +} + +static inline int futex_wake( int *addr, int val ) +{ + return syscall( __NR_futex, addr, 1, val, NULL, 0, 0 ); +} + +static inline int futex_wait( int *addr, int val, struct timespec *timeout ) +{ + return syscall( __NR_futex, addr, 0, val, timeout, 0, 0 ); +} + +static unsigned int spincount = 100; + +int do_fsync(void) +{ +#ifdef __linux__ + static int do_fsync_cached = -1; + + if (do_fsync_cached == -1) + { + static const struct timespec zero; + futex_wait_multiple( NULL, 0, &zero ); + do_fsync_cached = getenv("WINEFSYNC") && atoi(getenv("WINEFSYNC")) && errno != ENOSYS; + if (getenv("WINEFSYNC_SPINCOUNT")) + spincount = atoi(getenv("WINEFSYNC_SPINCOUNT")); + } + + return do_fsync_cached; +#else + static int once; + if (!once++) + FIXME("futexes not supported on this platform.\n"); + return 0; +#endif +} + +struct fsync +{ + enum fsync_type type; + void *shm; /* pointer to shm section */ +}; + +struct semaphore +{ + int count; + int max; +}; +C_ASSERT(sizeof(struct semaphore) == 8); + +struct event +{ + int signaled; + int unused; +}; +C_ASSERT(sizeof(struct event) == 8); + +struct mutex +{ + int tid; + int count; /* recursion count */ +}; +C_ASSERT(sizeof(struct mutex) == 8); + +static char shm_name[29]; +static int shm_fd; +static void **shm_addrs; +static int shm_addrs_size; /* length of the allocated shm_addrs array */ +static long pagesize; + +static pthread_mutex_t shm_addrs_mutex = PTHREAD_MUTEX_INITIALIZER; + +static void *get_shm( unsigned int idx ) +{ + int entry = (idx * 8) / pagesize; + int offset = (idx * 8) % pagesize; + void *ret; + + pthread_mutex_lock( &shm_addrs_mutex ); + + if (entry >= shm_addrs_size) + { + int new_size = max(shm_addrs_size * 2, entry + 1); + + if (!(shm_addrs = realloc( shm_addrs, new_size * sizeof(shm_addrs[0]) ))) + ERR("Failed to grow shm_addrs array to size %d.\n", shm_addrs_size); + memset( shm_addrs + shm_addrs_size, 0, (new_size - shm_addrs_size) * sizeof(shm_addrs[0]) ); + shm_addrs_size = new_size; + } + + if (!shm_addrs[entry]) + { + void *addr = mmap( NULL, pagesize, PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, entry * pagesize ); + if (addr == (void *)-1) + ERR("Failed to map page %d (offset %#lx).\n", entry, entry * pagesize); + + TRACE("Mapping page %d at %p.\n", entry, addr); + + if (__sync_val_compare_and_swap( &shm_addrs[entry], 0, addr )) + munmap( addr, pagesize ); /* someone beat us to it */ + } + + ret = (void *)((unsigned long)shm_addrs[entry] + offset); + + pthread_mutex_unlock( &shm_addrs_mutex ); + + return ret; +} + +/* We'd like lookup to be fast. To that end, we use a static list indexed by handle. + * This is copied and adapted from the fd cache code. */ + +#define FSYNC_LIST_BLOCK_SIZE (65536 / sizeof(struct fsync)) +#define FSYNC_LIST_ENTRIES 256 + +static struct fsync *fsync_list[FSYNC_LIST_ENTRIES]; +static struct fsync fsync_list_initial_block[FSYNC_LIST_BLOCK_SIZE]; + +static inline UINT_PTR handle_to_index( HANDLE handle, UINT_PTR *entry ) +{ + UINT_PTR idx = (((UINT_PTR)handle) >> 2) - 1; + *entry = idx / FSYNC_LIST_BLOCK_SIZE; + return idx % FSYNC_LIST_BLOCK_SIZE; +} + +static struct fsync *add_to_list( HANDLE handle, enum fsync_type type, void *shm ) +{ + UINT_PTR entry, idx = handle_to_index( handle, &entry ); + + if (entry >= FSYNC_LIST_ENTRIES) + { + FIXME( "too many allocated handles, not caching %p\n", handle ); + return FALSE; + } + + if (!fsync_list[entry]) /* do we need to allocate a new block of entries? */ + { + if (!entry) fsync_list[0] = fsync_list_initial_block; + else + { + void *ptr = anon_mmap_alloc( FSYNC_LIST_BLOCK_SIZE * sizeof(struct fsync), + PROT_READ | PROT_WRITE ); + if (ptr == MAP_FAILED) return FALSE; + fsync_list[entry] = ptr; + } + } + + if (!__sync_val_compare_and_swap((int *)&fsync_list[entry][idx].type, 0, type )) + fsync_list[entry][idx].shm = shm; + + return &fsync_list[entry][idx]; +} + +static struct fsync *get_cached_object( HANDLE handle ) +{ + UINT_PTR entry, idx = handle_to_index( handle, &entry ); + + if (entry >= FSYNC_LIST_ENTRIES || !fsync_list[entry]) return NULL; + if (!fsync_list[entry][idx].type) return NULL; + + return &fsync_list[entry][idx]; +} + +/* Gets an object. This is either a proper fsync object (i.e. an event, + * semaphore, etc. created using create_fsync) or a generic synchronizable + * server-side object which the server will signal (e.g. a process, thread, + * message queue, etc.) */ +static NTSTATUS get_object( HANDLE handle, struct fsync **obj ) +{ + NTSTATUS ret = STATUS_SUCCESS; + unsigned int shm_idx = 0; + enum fsync_type type; + + if ((*obj = get_cached_object( handle ))) return STATUS_SUCCESS; + + if ((INT_PTR)handle < 0) + { + /* We can deal with pseudo-handles, but it's just easier this way */ + return STATUS_NOT_IMPLEMENTED; + } + + /* We need to try grabbing it from the server. */ + SERVER_START_REQ( get_fsync_idx ) + { + req->handle = wine_server_obj_handle( handle ); + if (!(ret = wine_server_call( req ))) + { + shm_idx = reply->shm_idx; + type = reply->type; + } + } + SERVER_END_REQ; + + if (ret) + { + WARN("Failed to retrieve shm index for handle %p, status %#x.\n", handle, ret); + *obj = NULL; + return ret; + } + + TRACE("Got shm index %d for handle %p.\n", shm_idx, handle); + + *obj = add_to_list( handle, type, get_shm( shm_idx ) ); + return ret; +} + +NTSTATUS fsync_close( HANDLE handle ) +{ + UINT_PTR entry, idx = handle_to_index( handle, &entry ); + + TRACE("%p.\n", handle); + + if (entry < FSYNC_LIST_ENTRIES && fsync_list[entry]) + { + if (__atomic_exchange_n( &fsync_list[entry][idx].type, 0, __ATOMIC_SEQ_CST )) + return STATUS_SUCCESS; + } + + return STATUS_INVALID_HANDLE; +} + +static NTSTATUS create_fsync( enum fsync_type type, HANDLE *handle, + ACCESS_MASK access, const OBJECT_ATTRIBUTES *attr, int low, int high ) +{ + NTSTATUS ret; + data_size_t len; + struct object_attributes *objattr; + unsigned int shm_idx; + + if ((ret = alloc_object_attributes( attr, &objattr, &len ))) return ret; + + SERVER_START_REQ( create_fsync ) + { + req->access = access; + req->low = low; + req->high = high; + req->type = type; + wine_server_add_data( req, objattr, len ); + ret = wine_server_call( req ); + if (!ret || ret == STATUS_OBJECT_NAME_EXISTS) + { + *handle = wine_server_ptr_handle( reply->handle ); + shm_idx = reply->shm_idx; + type = reply->type; + } + } + SERVER_END_REQ; + + if (!ret || ret == STATUS_OBJECT_NAME_EXISTS) + { + add_to_list( *handle, type, get_shm( shm_idx )); + TRACE("-> handle %p, shm index %d.\n", *handle, shm_idx); + } + + free( objattr ); + return ret; +} + +static NTSTATUS open_fsync( enum fsync_type type, HANDLE *handle, + ACCESS_MASK access, const OBJECT_ATTRIBUTES *attr ) +{ + NTSTATUS ret; + unsigned int shm_idx; + + SERVER_START_REQ( open_fsync ) + { + req->access = access; + req->attributes = attr->Attributes; + req->rootdir = wine_server_obj_handle( attr->RootDirectory ); + req->type = type; + if (attr->ObjectName) + wine_server_add_data( req, attr->ObjectName->Buffer, attr->ObjectName->Length ); + if (!(ret = wine_server_call( req ))) + { + *handle = wine_server_ptr_handle( reply->handle ); + type = reply->type; + shm_idx = reply->shm_idx; + } + } + SERVER_END_REQ; + + if (!ret) + { + add_to_list( *handle, type, get_shm( shm_idx ) ); + + TRACE("-> handle %p, shm index %u.\n", *handle, shm_idx); + } + return ret; +} + +void fsync_init(void) +{ + struct stat st; + + if (!do_fsync()) + { + /* make sure the server isn't running with WINEFSYNC */ + HANDLE handle; + NTSTATUS ret; + + ret = create_fsync( 0, &handle, 0, NULL, 0, 0 ); + if (ret != STATUS_NOT_IMPLEMENTED) + { + ERR("Server is running with WINEFSYNC but this process is not, please enable WINEFSYNC or restart wineserver.\n"); + exit(1); + } + + return; + } + + if (stat( config_dir, &st ) == -1) + ERR("Cannot stat %s\n", config_dir); + + if (st.st_ino != (unsigned long)st.st_ino) + sprintf( shm_name, "/wine-%lx%08lx-fsync", (unsigned long)((unsigned long long)st.st_ino >> 32), (unsigned long)st.st_ino ); + else + sprintf( shm_name, "/wine-%lx-fsync", (unsigned long)st.st_ino ); + + if ((shm_fd = shm_open( shm_name, O_RDWR, 0644 )) == -1) + { + /* probably the server isn't running with WINEFSYNC, tell the user and bail */ + if (errno == ENOENT) + ERR("Failed to open fsync shared memory file; make sure no stale wineserver instances are running without WINEFSYNC.\n"); + else + ERR("Failed to initialize shared memory: %s\n", strerror( errno )); + exit(1); + } + + pagesize = sysconf( _SC_PAGESIZE ); + + shm_addrs = calloc( 128, sizeof(shm_addrs[0]) ); + shm_addrs_size = 128; +} + +NTSTATUS fsync_create_semaphore( HANDLE *handle, ACCESS_MASK access, + const OBJECT_ATTRIBUTES *attr, LONG initial, LONG max ) +{ + TRACE("name %s, initial %d, max %d.\n", + attr ? debugstr_us(attr->ObjectName) : "", initial, max); + + return create_fsync( FSYNC_SEMAPHORE, handle, access, attr, initial, max ); +} + +NTSTATUS fsync_open_semaphore( HANDLE *handle, ACCESS_MASK access, + const OBJECT_ATTRIBUTES *attr ) +{ + TRACE("name %s.\n", debugstr_us(attr->ObjectName)); + + return open_fsync( FSYNC_SEMAPHORE, handle, access, attr ); +} + +NTSTATUS fsync_release_semaphore( HANDLE handle, ULONG count, ULONG *prev ) +{ + struct fsync *obj; + struct semaphore *semaphore; + ULONG current; + NTSTATUS ret; + + TRACE("%p, %d, %p.\n", handle, count, prev); + + if ((ret = get_object( handle, &obj ))) return ret; + semaphore = obj->shm; + + do + { + current = semaphore->count; + if (count + current > semaphore->max) + return STATUS_SEMAPHORE_LIMIT_EXCEEDED; + } while (__sync_val_compare_and_swap( &semaphore->count, current, count + current ) != current); + + if (prev) *prev = current; + + futex_wake( &semaphore->count, INT_MAX ); + + return STATUS_SUCCESS; +} + +NTSTATUS fsync_query_semaphore( HANDLE handle, void *info, ULONG *ret_len ) +{ + struct fsync *obj; + struct semaphore *semaphore; + SEMAPHORE_BASIC_INFORMATION *out = info; + NTSTATUS ret; + + TRACE("handle %p, info %p, ret_len %p.\n", handle, info, ret_len); + + if ((ret = get_object( handle, &obj ))) return ret; + semaphore = obj->shm; + + out->CurrentCount = semaphore->count; + out->MaximumCount = semaphore->max; + if (ret_len) *ret_len = sizeof(*out); + + return STATUS_SUCCESS; +} + +NTSTATUS fsync_create_event( HANDLE *handle, ACCESS_MASK access, + const OBJECT_ATTRIBUTES *attr, EVENT_TYPE event_type, BOOLEAN initial ) +{ + enum fsync_type type = (event_type == SynchronizationEvent ? FSYNC_AUTO_EVENT : FSYNC_MANUAL_EVENT); + + TRACE("name %s, %s-reset, initial %d.\n", + attr ? debugstr_us(attr->ObjectName) : "", + event_type == NotificationEvent ? "manual" : "auto", initial); + + return create_fsync( type, handle, access, attr, initial, 0xdeadbeef ); +} + +NTSTATUS fsync_open_event( HANDLE *handle, ACCESS_MASK access, + const OBJECT_ATTRIBUTES *attr ) +{ + TRACE("name %s.\n", debugstr_us(attr->ObjectName)); + + return open_fsync( FSYNC_AUTO_EVENT, handle, access, attr ); +} + +NTSTATUS fsync_set_event( HANDLE handle, LONG *prev ) +{ + struct event *event; + struct fsync *obj; + LONG current; + NTSTATUS ret; + + TRACE("%p.\n", handle); + + if ((ret = get_object( handle, &obj ))) return ret; + event = obj->shm; + + if (!(current = __atomic_exchange_n( &event->signaled, 1, __ATOMIC_SEQ_CST ))) + futex_wake( &event->signaled, INT_MAX ); + + if (prev) *prev = current; + + return STATUS_SUCCESS; +} + +NTSTATUS fsync_reset_event( HANDLE handle, LONG *prev ) +{ + struct event *event; + struct fsync *obj; + LONG current; + NTSTATUS ret; + + TRACE("%p.\n", handle); + + if ((ret = get_object( handle, &obj ))) return ret; + event = obj->shm; + + current = __atomic_exchange_n( &event->signaled, 0, __ATOMIC_SEQ_CST ); + + if (prev) *prev = current; + + return STATUS_SUCCESS; +} + +NTSTATUS fsync_pulse_event( HANDLE handle, LONG *prev ) +{ + struct event *event; + struct fsync *obj; + LONG current; + NTSTATUS ret; + + TRACE("%p.\n", handle); + + if ((ret = get_object( handle, &obj ))) return ret; + event = obj->shm; + + /* This isn't really correct; an application could miss the write. + * Unfortunately we can't really do much better. Fortunately this is rarely + * used (and publicly deprecated). */ + if (!(current = __atomic_exchange_n( &event->signaled, 1, __ATOMIC_SEQ_CST ))) + futex_wake( &event->signaled, INT_MAX ); + + /* Try to give other threads a chance to wake up. Hopefully erring on this + * side is the better thing to do... */ + NtYieldExecution(); + + __atomic_store_n( &event->signaled, 0, __ATOMIC_SEQ_CST ); + + if (prev) *prev = current; + + return STATUS_SUCCESS; +} + +NTSTATUS fsync_query_event( HANDLE handle, void *info, ULONG *ret_len ) +{ + struct event *event; + struct fsync *obj; + EVENT_BASIC_INFORMATION *out = info; + NTSTATUS ret; + + TRACE("handle %p, info %p, ret_len %p.\n", handle, info, ret_len); + + if ((ret = get_object( handle, &obj ))) return ret; + event = obj->shm; + + out->EventState = event->signaled; + out->EventType = (obj->type == FSYNC_AUTO_EVENT ? SynchronizationEvent : NotificationEvent); + if (ret_len) *ret_len = sizeof(*out); + + return STATUS_SUCCESS; +} + +NTSTATUS fsync_create_mutex( HANDLE *handle, ACCESS_MASK access, + const OBJECT_ATTRIBUTES *attr, BOOLEAN initial ) +{ + TRACE("name %s, initial %d.\n", + attr ? debugstr_us(attr->ObjectName) : "", initial); + + return create_fsync( FSYNC_MUTEX, handle, access, attr, + initial ? GetCurrentThreadId() : 0, initial ? 1 : 0 ); +} + +NTSTATUS fsync_open_mutex( HANDLE *handle, ACCESS_MASK access, + const OBJECT_ATTRIBUTES *attr ) +{ + TRACE("name %s.\n", debugstr_us(attr->ObjectName)); + + return open_fsync( FSYNC_MUTEX, handle, access, attr ); +} + +NTSTATUS fsync_release_mutex( HANDLE handle, LONG *prev ) +{ + struct mutex *mutex; + struct fsync *obj; + NTSTATUS ret; + + TRACE("%p, %p.\n", handle, prev); + + if ((ret = get_object( handle, &obj ))) return ret; + mutex = obj->shm; + + if (mutex->tid != GetCurrentThreadId()) return STATUS_MUTANT_NOT_OWNED; + + if (prev) *prev = mutex->count; + + if (!--mutex->count) + { + __atomic_store_n( &mutex->tid, 0, __ATOMIC_SEQ_CST ); + futex_wake( &mutex->tid, INT_MAX ); + } + + return STATUS_SUCCESS; +} + +NTSTATUS fsync_query_mutex( HANDLE handle, void *info, ULONG *ret_len ) +{ + struct fsync *obj; + struct mutex *mutex; + MUTANT_BASIC_INFORMATION *out = info; + NTSTATUS ret; + + TRACE("handle %p, info %p, ret_len %p.\n", handle, info, ret_len); + + if ((ret = get_object( handle, &obj ))) return ret; + mutex = obj->shm; + + out->CurrentCount = 1 - mutex->count; + out->OwnedByCaller = (mutex->tid == GetCurrentThreadId()); + out->AbandonedState = (mutex->tid == ~0); + if (ret_len) *ret_len = sizeof(*out); + + return STATUS_SUCCESS; +} + +static LONGLONG update_timeout( ULONGLONG end ) +{ + LARGE_INTEGER now; + LONGLONG timeleft; + + NtQuerySystemTime( &now ); + timeleft = end - now.QuadPart; + if (timeleft < 0) timeleft = 0; + return timeleft; +} + +static NTSTATUS do_single_wait( int *addr, int val, ULONGLONG *end, BOOLEAN alertable ) +{ + int ret; + + if (alertable) + { + int *apc_futex = ntdll_get_thread_data()->fsync_apc_futex; + struct futex_wait_block futexes[2]; + + if (__atomic_load_n( apc_futex, __ATOMIC_SEQ_CST )) + return STATUS_USER_APC; + + futexes[0].addr = addr; + futexes[0].val = val; + futexes[1].addr = apc_futex; + futexes[1].val = 0; +#if __SIZEOF_POINTER__ == 4 + futexes[0].pad = futexes[1].pad = 0; +#endif + futexes[0].bitset = futexes[1].bitset = ~0; + + if (end) + { + LONGLONG timeleft = update_timeout( *end ); + struct timespec tmo_p; + tmo_p.tv_sec = timeleft / (ULONGLONG)TICKSPERSEC; + tmo_p.tv_nsec = (timeleft % TICKSPERSEC) * 100; + ret = futex_wait_multiple( futexes, 2, &tmo_p ); + } + else + ret = futex_wait_multiple( futexes, 2, NULL ); + + if (__atomic_load_n( apc_futex, __ATOMIC_SEQ_CST )) + return STATUS_USER_APC; + } + else + { + if (end) + { + LONGLONG timeleft = update_timeout( *end ); + struct timespec tmo_p; + tmo_p.tv_sec = timeleft / (ULONGLONG)TICKSPERSEC; + tmo_p.tv_nsec = (timeleft % TICKSPERSEC) * 100; + ret = futex_wait( addr, val, &tmo_p ); + } + else + ret = futex_wait( addr, val, NULL ); + } + + if (!ret) + return 0; + else if (ret < 0 && errno == ETIMEDOUT) + return STATUS_TIMEOUT; + else + return STATUS_PENDING; +} + +static NTSTATUS __fsync_wait_objects( DWORD count, const HANDLE *handles, + BOOLEAN wait_any, BOOLEAN alertable, const LARGE_INTEGER *timeout ) +{ + static const LARGE_INTEGER zero = {0}; + + struct futex_wait_block futexes[MAXIMUM_WAIT_OBJECTS + 1]; + struct fsync *objs[MAXIMUM_WAIT_OBJECTS]; + int has_fsync = 0, has_server = 0; + BOOL msgwait = FALSE; + int dummy_futex = 0; + unsigned int spin; + LONGLONG timeleft; + LARGE_INTEGER now; + DWORD waitcount; + ULONGLONG end; + int i, ret; + + /* Grab the APC futex if we don't already have it. */ + if (alertable && !ntdll_get_thread_data()->fsync_apc_futex) + { + unsigned int idx = 0; + SERVER_START_REQ( get_fsync_apc_idx ) + { + if (!(ret = wine_server_call( req ))) + idx = reply->shm_idx; + } + SERVER_END_REQ; + + if (idx) + { + struct event *apc_event = get_shm( idx ); + ntdll_get_thread_data()->fsync_apc_futex = &apc_event->signaled; + } + } + + NtQuerySystemTime( &now ); + if (timeout) + { + if (timeout->QuadPart == TIMEOUT_INFINITE) + timeout = NULL; + else if (timeout->QuadPart > 0) + end = timeout->QuadPart; + else + end = now.QuadPart - timeout->QuadPart; + } + + for (i = 0; i < count; i++) + { + ret = get_object( handles[i], &objs[i] ); + if (ret == STATUS_SUCCESS) + has_fsync = 1; + else if (ret == STATUS_NOT_IMPLEMENTED) + has_server = 1; + else + return ret; + } + + if (count && objs[count - 1] && objs[count - 1]->type == FSYNC_QUEUE) + msgwait = TRUE; + + if (has_fsync && has_server) + FIXME("Can't wait on fsync and server objects at the same time!\n"); + else if (has_server) + return STATUS_NOT_IMPLEMENTED; + + if (TRACE_ON(fsync)) + { + TRACE("Waiting for %s of %d handles:", wait_any ? "any" : "all", count); + for (i = 0; i < count; i++) + TRACE(" %p", handles[i]); + + if (msgwait) + TRACE(" or driver events"); + if (alertable) + TRACE(", alertable"); + + if (!timeout) + TRACE(", timeout = INFINITE.\n"); + else + { + timeleft = update_timeout( end ); + TRACE(", timeout = %ld.%07ld sec.\n", + (long) (timeleft / TICKSPERSEC), (long) (timeleft % TICKSPERSEC)); + } + } + + if (wait_any || count <= 1) + { + while (1) + { + /* Try to grab anything. */ + + if (alertable) + { + /* We must check this first! The server may set an event that + * we're waiting on, but we need to return STATUS_USER_APC. */ + if (__atomic_load_n( ntdll_get_thread_data()->fsync_apc_futex, __ATOMIC_SEQ_CST )) + goto userapc; + } + + for (i = 0; i < count; i++) + { + struct fsync *obj = objs[i]; + + if (obj) + { + if (!obj->type) /* gcc complains if we put this in the switch */ + { + /* Someone probably closed an object while waiting on it. */ + WARN("Handle %p has type 0; was it closed?\n", handles[i]); + return STATUS_INVALID_HANDLE; + } + + switch (obj->type) + { + case FSYNC_SEMAPHORE: + { + struct semaphore *semaphore = obj->shm; + int current; + + /* It would be a little clearer (and less error-prone) + * to use a dedicated interlocked_dec_if_nonzero() + * helper, but nesting loops like that is probably not + * great for performance... */ + for (spin = 0; spin <= spincount || current; ++spin) + { + if ((current = __atomic_load_n( &semaphore->count, __ATOMIC_SEQ_CST )) + && __sync_val_compare_and_swap( &semaphore->count, current, current - 1 ) == current) + { + TRACE("Woken up by handle %p [%d].\n", handles[i], i); + return i; + } + small_pause(); + } + + futexes[i].addr = &semaphore->count; + futexes[i].val = 0; + break; + } + case FSYNC_MUTEX: + { + struct mutex *mutex = obj->shm; + int tid; + + if (mutex->tid == GetCurrentThreadId()) + { + TRACE("Woken up by handle %p [%d].\n", handles[i], i); + mutex->count++; + return i; + } + + for (spin = 0; spin <= spincount; ++spin) + { + if (!(tid = __sync_val_compare_and_swap( &mutex->tid, 0, GetCurrentThreadId() ))) + { + TRACE("Woken up by handle %p [%d].\n", handles[i], i); + mutex->count = 1; + return i; + } + else if (tid == ~0 && (tid = __sync_val_compare_and_swap( &mutex->tid, ~0, GetCurrentThreadId() )) == ~0) + { + TRACE("Woken up by abandoned mutex %p [%d].\n", handles[i], i); + mutex->count = 1; + return STATUS_ABANDONED_WAIT_0 + i; + } + small_pause(); + } + + futexes[i].addr = &mutex->tid; + futexes[i].val = tid; + break; + } + case FSYNC_AUTO_EVENT: + case FSYNC_AUTO_SERVER: + { + struct event *event = obj->shm; + + for (spin = 0; spin <= spincount; ++spin) + { + if (__sync_val_compare_and_swap( &event->signaled, 1, 0 )) + { + TRACE("Woken up by handle %p [%d].\n", handles[i], i); + return i; + } + small_pause(); + } + + futexes[i].addr = &event->signaled; + futexes[i].val = 0; + break; + } + case FSYNC_MANUAL_EVENT: + case FSYNC_MANUAL_SERVER: + case FSYNC_QUEUE: + { + struct event *event = obj->shm; + + for (spin = 0; spin <= spincount; ++spin) + { + if (__atomic_load_n( &event->signaled, __ATOMIC_SEQ_CST )) + { + TRACE("Woken up by handle %p [%d].\n", handles[i], i); + return i; + } + small_pause(); + } + + futexes[i].addr = &event->signaled; + futexes[i].val = 0; + break; + } + default: + ERR("Invalid type %#x for handle %p.\n", obj->type, handles[i]); + assert(0); + } + } + else + { + /* Avoid breaking things entirely. */ + futexes[i].addr = &dummy_futex; + futexes[i].val = dummy_futex; + } + +#if __SIZEOF_POINTER__ == 4 + futexes[i].pad = 0; +#endif + futexes[i].bitset = ~0; + } + + if (alertable) + { + /* We already checked if it was signaled; don't bother doing it again. */ + futexes[i].addr = ntdll_get_thread_data()->fsync_apc_futex; + futexes[i].val = 0; +#if __SIZEOF_POINTER__ == 4 + futexes[i].pad = 0; +#endif + futexes[i].bitset = ~0; + i++; + } + waitcount = i; + + /* Looks like everything is contended, so wait. */ + + if (timeout && !timeout->QuadPart) + { + /* Unlike esync, we already know that we've timed out, so we + * can avoid a syscall. */ + TRACE("Wait timed out.\n"); + return STATUS_TIMEOUT; + } + else if (timeout) + { + LONGLONG timeleft = update_timeout( end ); + struct timespec tmo_p; + tmo_p.tv_sec = timeleft / (ULONGLONG)TICKSPERSEC; + tmo_p.tv_nsec = (timeleft % TICKSPERSEC) * 100; + + ret = futex_wait_multiple( futexes, waitcount, &tmo_p ); + } + else + ret = futex_wait_multiple( futexes, waitcount, NULL ); + + /* FUTEX_WAIT_MULTIPLE can succeed or return -EINTR, -EAGAIN, + * -EFAULT/-EACCES, -ETIMEDOUT. In the first three cases we need to + * try again, bad address is already handled by the fact that we + * tried to read from it, so only break out on a timeout. */ + if (ret == -1 && errno == ETIMEDOUT) + { + TRACE("Wait timed out.\n"); + return STATUS_TIMEOUT; + } + } /* while (1) */ + } + else + { + /* Wait-all is a little trickier to implement correctly. Fortunately, + * it's not as common. + * + * The idea is basically just to wait in sequence on every object in the + * set. Then when we're done, try to grab them all in a tight loop. If + * that fails, release any resources we've grabbed (and yes, we can + * reliably do this—it's just mutexes and semaphores that we have to + * put back, and in both cases we just put back 1), and if any of that + * fails we start over. + * + * What makes this inherently bad is that we might temporarily grab a + * resource incorrectly. Hopefully it'll be quick (and hey, it won't + * block on wineserver) so nobody will notice. Besides, consider: if + * object A becomes signaled but someone grabs it before we can grab it + * and everything else, then they could just as well have grabbed it + * before it became signaled. Similarly if object A was signaled and we + * were blocking on object B, then B becomes available and someone grabs + * A before we can, then they might have grabbed A before B became + * signaled. In either case anyone who tries to wait on A or B will be + * waiting for an instant while we put things back. */ + + NTSTATUS status = STATUS_SUCCESS; + int current; + + while (1) + { + BOOL abandoned; + +tryagain: + abandoned = FALSE; + + /* First step: try to wait on each object in sequence. */ + + for (i = 0; i < count; i++) + { + struct fsync *obj = objs[i]; + + if (obj && obj->type == FSYNC_MUTEX) + { + struct mutex *mutex = obj->shm; + + if (mutex->tid == GetCurrentThreadId()) + continue; + + while ((current = __atomic_load_n( &mutex->tid, __ATOMIC_SEQ_CST ))) + { + status = do_single_wait( &mutex->tid, current, timeout ? &end : NULL, alertable ); + if (status != STATUS_PENDING) + break; + } + } + else if (obj) + { + /* this works for semaphores too */ + struct event *event = obj->shm; + + while (!__atomic_load_n( &event->signaled, __ATOMIC_SEQ_CST )) + { + status = do_single_wait( &event->signaled, 0, timeout ? &end : NULL, alertable ); + if (status != STATUS_PENDING) + break; + } + } + + if (status == STATUS_TIMEOUT) + { + TRACE("Wait timed out.\n"); + return status; + } + else if (status == STATUS_USER_APC) + goto userapc; + } + + /* If we got here and we haven't timed out, that means all of the + * handles were signaled. Check to make sure they still are. */ + for (i = 0; i < count; i++) + { + struct fsync *obj = objs[i]; + + if (obj && obj->type == FSYNC_MUTEX) + { + struct mutex *mutex = obj->shm; + int tid = __atomic_load_n( &mutex->tid, __ATOMIC_SEQ_CST ); + + if (tid && tid != ~0 && tid != GetCurrentThreadId()) + goto tryagain; + } + else if (obj) + { + struct event *event = obj->shm; + + if (!__atomic_load_n( &event->signaled, __ATOMIC_SEQ_CST )) + goto tryagain; + } + } + + /* Yep, still signaled. Now quick, grab everything. */ + for (i = 0; i < count; i++) + { + struct fsync *obj = objs[i]; + switch (obj->type) + { + case FSYNC_MUTEX: + { + struct mutex *mutex = obj->shm; + int tid = __atomic_load_n( &mutex->tid, __ATOMIC_SEQ_CST ); + if (tid == GetCurrentThreadId()) + break; + if (tid && tid != ~0) + goto tooslow; + if (__sync_val_compare_and_swap( &mutex->tid, tid, GetCurrentThreadId() ) != tid) + goto tooslow; + if (tid == ~0) + abandoned = TRUE; + break; + } + case FSYNC_SEMAPHORE: + { + struct semaphore *semaphore = obj->shm; + if (__sync_fetch_and_sub( &semaphore->count, 1 ) <= 0) + goto tooslow; + break; + } + case FSYNC_AUTO_EVENT: + case FSYNC_AUTO_SERVER: + { + struct event *event = obj->shm; + if (!__sync_val_compare_and_swap( &event->signaled, 1, 0 )) + goto tooslow; + break; + } + default: + /* If a manual-reset event changed between there and + * here, it's shouldn't be a problem. */ + break; + } + } + + /* If we got here, we successfully waited on every object. + * Make sure to let ourselves know that we grabbed the mutexes. */ + for (i = 0; i < count; i++) + { + if (objs[i] && objs[i]->type == FSYNC_MUTEX) + { + struct mutex *mutex = objs[i]->shm; + mutex->count++; + } + } + + if (abandoned) + { + TRACE("Wait successful, but some object(s) were abandoned.\n"); + return STATUS_ABANDONED; + } + TRACE("Wait successful.\n"); + return STATUS_SUCCESS; + +tooslow: + for (--i; i >= 0; i--) + { + struct fsync *obj = objs[i]; + switch (obj->type) + { + case FSYNC_MUTEX: + { + struct mutex *mutex = obj->shm; + /* HACK: This won't do the right thing with abandoned + * mutexes, but fixing it is probably more trouble than + * it's worth. */ + __atomic_store_n( &mutex->tid, 0, __ATOMIC_SEQ_CST ); + break; + } + case FSYNC_SEMAPHORE: + { + struct semaphore *semaphore = obj->shm; + __sync_fetch_and_add( &semaphore->count, 1 ); + break; + } + case FSYNC_AUTO_EVENT: + case FSYNC_AUTO_SERVER: + { + struct event *event = obj->shm; + __atomic_store_n( &event->signaled, 1, __ATOMIC_SEQ_CST ); + break; + } + default: + /* doesn't need to be put back */ + break; + } + } + } /* while (1) */ + } /* else (wait-all) */ + + assert(0); /* shouldn't reach here... */ + +userapc: + TRACE("Woken up by user APC.\n"); + + /* We have to make a server call anyway to get the APC to execute, so just + * delegate down to server_wait(). */ + ret = server_wait( NULL, 0, SELECT_INTERRUPTIBLE | SELECT_ALERTABLE, &zero ); + + /* This can happen if we received a system APC, and the APC fd was woken up + * before we got SIGUSR1. poll() doesn't return EINTR in that case. The + * right thing to do seems to be to return STATUS_USER_APC anyway. */ + if (ret == STATUS_TIMEOUT) ret = STATUS_USER_APC; + return ret; +} + +/* Like esync, we need to let the server know when we are doing a message wait, + * and when we are done with one, so that all of the code surrounding hung + * queues works, and we also need this for WaitForInputIdle(). + * + * Unlike esync, we can't wait on the queue fd itself locally. Instead we let + * the server do that for us, the way it normally does. This could actually + * work for esync too, and that might be better. */ +static void server_set_msgwait( int in_msgwait ) +{ + SERVER_START_REQ( fsync_msgwait ) + { + req->in_msgwait = in_msgwait; + wine_server_call( req ); + } + SERVER_END_REQ; +} + +/* This is a very thin wrapper around the proper implementation above. The + * purpose is to make sure the server knows when we are doing a message wait. + * This is separated into a wrapper function since there are at least a dozen + * exit paths from fsync_wait_objects(). */ +NTSTATUS fsync_wait_objects( DWORD count, const HANDLE *handles, BOOLEAN wait_any, + BOOLEAN alertable, const LARGE_INTEGER *timeout ) +{ + BOOL msgwait = FALSE; + struct fsync *obj; + NTSTATUS ret; + + if (count && !get_object( handles[count - 1], &obj ) && obj->type == FSYNC_QUEUE) + { + msgwait = TRUE; + server_set_msgwait( 1 ); + } + + ret = __fsync_wait_objects( count, handles, wait_any, alertable, timeout ); + + if (msgwait) + server_set_msgwait( 0 ); + + return ret; +} + +NTSTATUS fsync_signal_and_wait( HANDLE signal, HANDLE wait, BOOLEAN alertable, + const LARGE_INTEGER *timeout ) +{ + struct fsync *obj; + NTSTATUS ret; + + if ((ret = get_object( signal, &obj ))) return ret; + + switch (obj->type) + { + case FSYNC_SEMAPHORE: + ret = fsync_release_semaphore( signal, 1, NULL ); + break; + case FSYNC_AUTO_EVENT: + case FSYNC_MANUAL_EVENT: + ret = fsync_set_event( signal, NULL ); + break; + case FSYNC_MUTEX: + ret = fsync_release_mutex( signal, NULL ); + break; + default: + return STATUS_OBJECT_TYPE_MISMATCH; + } + if (ret) return ret; + + return fsync_wait_objects( 1, &wait, TRUE, alertable, timeout ); +} diff --git a/dlls/ntdll/unix/fsync.h b/dlls/ntdll/unix/fsync.h new file mode 100644 index 00000000000..b3604548554 --- /dev/null +++ b/dlls/ntdll/unix/fsync.h @@ -0,0 +1,49 @@ +/* + * futex-based synchronization objects + * + * Copyright (C) 2018 Zebediah Figura + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +extern int do_fsync(void) DECLSPEC_HIDDEN; +extern void fsync_init(void) DECLSPEC_HIDDEN; +extern NTSTATUS fsync_close( HANDLE handle ) DECLSPEC_HIDDEN; + +extern NTSTATUS fsync_create_semaphore(HANDLE *handle, ACCESS_MASK access, + const OBJECT_ATTRIBUTES *attr, LONG initial, LONG max) DECLSPEC_HIDDEN; +extern NTSTATUS fsync_release_semaphore( HANDLE handle, ULONG count, ULONG *prev ) DECLSPEC_HIDDEN; +extern NTSTATUS fsync_open_semaphore( HANDLE *handle, ACCESS_MASK access, + const OBJECT_ATTRIBUTES *attr ) DECLSPEC_HIDDEN; +extern NTSTATUS fsync_query_semaphore( HANDLE handle, void *info, ULONG *ret_len ) DECLSPEC_HIDDEN; +extern NTSTATUS fsync_create_event( HANDLE *handle, ACCESS_MASK access, + const OBJECT_ATTRIBUTES *attr, EVENT_TYPE type, BOOLEAN initial ) DECLSPEC_HIDDEN; +extern NTSTATUS fsync_open_event( HANDLE *handle, ACCESS_MASK access, + const OBJECT_ATTRIBUTES *attr ) DECLSPEC_HIDDEN; +extern NTSTATUS fsync_set_event( HANDLE handle, LONG *prev ) DECLSPEC_HIDDEN; +extern NTSTATUS fsync_reset_event( HANDLE handle, LONG *prev ) DECLSPEC_HIDDEN; +extern NTSTATUS fsync_pulse_event( HANDLE handle, LONG *prev ) DECLSPEC_HIDDEN; +extern NTSTATUS fsync_query_event( HANDLE handle, void *info, ULONG *ret_len ) DECLSPEC_HIDDEN; +extern NTSTATUS fsync_create_mutex( HANDLE *handle, ACCESS_MASK access, + const OBJECT_ATTRIBUTES *attr, BOOLEAN initial ) DECLSPEC_HIDDEN; +extern NTSTATUS fsync_open_mutex( HANDLE *handle, ACCESS_MASK access, + const OBJECT_ATTRIBUTES *attr ) DECLSPEC_HIDDEN; +extern NTSTATUS fsync_release_mutex( HANDLE handle, LONG *prev ) DECLSPEC_HIDDEN; +extern NTSTATUS fsync_query_mutex( HANDLE handle, void *info, ULONG *ret_len ) DECLSPEC_HIDDEN; + +extern NTSTATUS fsync_wait_objects( DWORD count, const HANDLE *handles, BOOLEAN wait_any, + BOOLEAN alertable, const LARGE_INTEGER *timeout ) DECLSPEC_HIDDEN; +extern NTSTATUS fsync_signal_and_wait( HANDLE signal, HANDLE wait, + BOOLEAN alertable, const LARGE_INTEGER *timeout ) DECLSPEC_HIDDEN; diff --git a/dlls/ntdll/unix/loader.c b/dlls/ntdll/unix/loader.c index e4c9e5272f9..fb23db29b9b 100644 --- a/dlls/ntdll/unix/loader.c +++ b/dlls/ntdll/unix/loader.c @@ -87,6 +87,7 @@ #include "winternl.h" #include "unix_private.h" #include "esync.h" +#include "fsync.h" #include "wine/list.h" #include "wine/debug.h" @@ -1569,5 +1570,6 @@ static void start_main_thread(void) dbg_init(); startup_info_size = server_init_process(); + fsync_init(); esync_init(); virtual_map_user_shared_data(); init_cpu_info(); diff --git a/dlls/ntdll/unix/server.c b/dlls/ntdll/unix/server.c index d8837f6fde0..bc0d3537a00 100644 --- a/dlls/ntdll/unix/server.c +++ b/dlls/ntdll/unix/server.c @@ -89,6 +89,7 @@ #include "wine/debug.h" #include "unix_private.h" #include "esync.h" +#include "fsync.h" #include "ddk/wdm.h" WINE_DEFAULT_DEBUG_CHANNEL(server); @@ -1645,6 +1646,9 @@ NTSTATUS WINAPI NtClose( HANDLE handle ) NTSTATUS ret; int fd = remove_fd_from_cache( handle ); + if (do_fsync()) + fsync_close( handle ); + if (do_esync()) esync_close( handle ); diff --git a/dlls/ntdll/unix/sync.c b/dlls/ntdll/unix/sync.c index 4f47afdb7b2..fddf8ca082d 100644 --- a/dlls/ntdll/unix/sync.c +++ b/dlls/ntdll/unix/sync.c @@ -73,6 +73,7 @@ #include "wine/debug.h" #include "unix_private.h" #include "esync.h" +#include "fsync.h" WINE_DEFAULT_DEBUG_CHANNEL(sync); @@ -323,6 +324,9 @@ NTSTATUS WINAPI NtCreateSemaphore( HANDLE *handle, ACCESS_MASK access, const OBJ if (max <= 0 || initial < 0 || initial > max) return STATUS_INVALID_PARAMETER; if ((ret = alloc_object_attributes( attr, &objattr, &len ))) return ret; + if (do_fsync()) + return fsync_create_semaphore( handle, access, attr, initial, max ); + if (do_esync()) return esync_create_semaphore( handle, access, attr, initial, max ); @@ -349,6 +353,9 @@ NTSTATUS WINAPI NtOpenSemaphore( HANDLE *handle, ACCESS_MASK access, const OBJEC { NTSTATUS ret; + if (do_fsync()) + return fsync_open_semaphore( handle, access, attr ); + if (do_esync()) return esync_open_semaphore( handle, access, attr ); @@ -388,6 +395,9 @@ NTSTATUS WINAPI NtQuerySemaphore( HANDLE handle, SEMAPHORE_INFORMATION_CLASS cla if (len != sizeof(SEMAPHORE_BASIC_INFORMATION)) return STATUS_INFO_LENGTH_MISMATCH; + if (do_fsync()) + return fsync_query_semaphore( handle, info, ret_len ); + if (do_esync()) return esync_query_semaphore( handle, info, ret_len ); @@ -413,6 +423,9 @@ NTSTATUS WINAPI NtReleaseSemaphore( HANDLE handle, ULONG count, ULONG *previous { NTSTATUS ret; + if (do_fsync()) + return fsync_release_semaphore( handle, count, previous ); + if (do_esync()) return esync_release_semaphore( handle, count, previous ); @@ -440,8 +453,11 @@ NTSTATUS WINAPI NtCreateEvent( HANDLE *handle, ACCESS_MASK access, const OBJECT_ data_size_t len; struct object_attributes *objattr; if (type != NotificationEvent && type != SynchronizationEvent) return STATUS_INVALID_PARAMETER; + if (do_fsync()) + return fsync_create_event( handle, access, attr, type, state ); + if (do_esync()) return esync_create_event( handle, access, attr, type, state ); @@ -470,6 +486,9 @@ NTSTATUS WINAPI NtOpenEvent( HANDLE *handle, ACCESS_MASK access, const OBJECT_AT if ((ret = validate_open_object_attributes( attr ))) return ret; + if (do_fsync()) + return fsync_open_event( handle, access, attr ); + if (do_esync()) return esync_open_event( handle, access, attr ); @@ -495,5 +514,8 @@ NTSTATUS WINAPI NtSetEvent( HANDLE handle, LONG *prev_state ) NTSTATUS ret; + if (do_fsync()) + return fsync_set_event( handle, prev_state ); + if (do_esync()) return esync_set_event( handle ); @@ -517,5 +539,8 @@ NTSTATUS WINAPI NtResetEvent( HANDLE handle, LONG *prev_state ) NTSTATUS ret; + if (do_fsync()) + return fsync_reset_event( handle, prev_state ); + if (do_esync()) return esync_reset_event( handle ); @@ -549,5 +574,8 @@ NTSTATUS WINAPI NtPulseEvent( HANDLE handle, LONG *prev_state ) NTSTATUS ret; + if (do_fsync()) + return fsync_pulse_event( handle, prev_state ); + if (do_esync()) return esync_pulse_event( handle ); @@ -584,6 +612,9 @@ NTSTATUS WINAPI NtQueryEvent( HANDLE handle, EVENT_INFORMATION_CLASS class, if (len != sizeof(EVENT_BASIC_INFORMATION)) return STATUS_INFO_LENGTH_MISMATCH; + if (do_fsync()) + return fsync_query_event( handle, info, ret_len ); + if (do_esync()) return esync_query_event( handle, info, ret_len ); @@ -612,6 +643,9 @@ NTSTATUS WINAPI NtCreateMutant( HANDLE *handle, ACCESS_MASK access, const OBJECT data_size_t len; struct object_attributes *objattr; + if (do_fsync()) + return fsync_create_mutex( handle, access, attr, owned ); + if (do_esync()) return esync_create_mutex( handle, access, attr, owned ); @@ -641,6 +675,9 @@ NTSTATUS WINAPI NtOpenMutant( HANDLE *handle, ACCESS_MASK access, const OBJECT_A if ((ret = validate_open_object_attributes( attr ))) return ret; + if (do_fsync()) + return fsync_open_mutex( handle, access, attr ); + if (do_esync()) return esync_open_mutex( handle, access, attr ); @@ -666,6 +703,9 @@ NTSTATUS WINAPI NtReleaseMutant( HANDLE handle, LONG *prev_count ) { NTSTATUS ret; + if (do_fsync()) + return fsync_release_mutex( handle, prev_count ); + if (do_esync()) return esync_release_mutex( handle, prev_count ); @@ -699,6 +739,9 @@ NTSTATUS WINAPI NtQueryMutant( HANDLE handle, MUTANT_INFORMATION_CLASS class, if (len != sizeof(MUTANT_BASIC_INFORMATION)) return STATUS_INFO_LENGTH_MISMATCH; + if (do_fsync()) + return fsync_query_mutex( handle, info, ret_len ); + if (do_esync()) return esync_query_mutex( handle, info, ret_len ); @@ -1309,6 +1352,13 @@ NTSTATUS WINAPI NtWaitForMultipleObjects( DWORD count, const HANDLE *handles, BO if (!count || count > MAXIMUM_WAIT_OBJECTS) return STATUS_INVALID_PARAMETER_1; + if (do_fsync()) + { + NTSTATUS ret = fsync_wait_objects( count, handles, wait_any, alertable, timeout ); + if (ret != STATUS_NOT_IMPLEMENTED) + return ret; + } + if (do_esync()) { NTSTATUS ret = esync_wait_objects( count, handles, wait_any, alertable, timeout ); @@ -1341,6 +1391,9 @@ NTSTATUS WINAPI NtSignalAndWaitForSingleObject( HANDLE signal, HANDLE wait, select_op_t select_op; UINT flags = SELECT_INTERRUPTIBLE; + if (do_fsync()) + return fsync_signal_and_wait( signal, wait, alertable, timeout ); + if (do_esync()) return esync_signal_and_wait( signal, wait, alertable, timeout ); @@ -1374,7 +1427,24 @@ NTSTATUS WINAPI NtYieldExecution(void) NTSTATUS WINAPI NtDelayExecution( BOOLEAN alertable, const LARGE_INTEGER *timeout ) { /* if alertable, we need to query the server */ - if (alertable) return server_wait( NULL, 0, SELECT_INTERRUPTIBLE | SELECT_ALERTABLE, timeout ); + if (alertable) + { + if (do_fsync()) + { + NTSTATUS ret = fsync_wait_objects( 0, NULL, TRUE, TRUE, timeout ); + if (ret != STATUS_NOT_IMPLEMENTED) + return ret; + } + + if (do_esync()) + { + NTSTATUS ret = esync_wait_objects( 0, NULL, TRUE, TRUE, timeout ); + if (ret != STATUS_NOT_IMPLEMENTED) + return ret; + } + + return server_wait( NULL, 0, SELECT_INTERRUPTIBLE | SELECT_ALERTABLE, timeout ); + } if (!timeout || timeout->QuadPart == TIMEOUT_INFINITE) /* sleep forever */ { diff --git a/dlls/ntdll/unix/unix_private.h b/dlls/ntdll/unix/unix_private.h index 0e2461ded79..5cd12a2bd5d 100644 --- a/dlls/ntdll/unix/unix_private.h +++ b/dlls/ntdll/unix/unix_private.h @@ -58,6 +58,7 @@ struct ntdll_thread_data struct debug_info *debug_info; /* info for debugstr functions */ void *start_stack; /* stack for thread startup */ int esync_apc_fd; /* fd to wait on for user APCs */ + int *fsync_apc_futex; int request_fd; /* fd for sending server requests */ int reply_fd; /* fd for receiving server replies */ int wait_fd[2]; /* fd for sleeping server requests */ diff --git a/dlls/ntdll/unix/virtual.c b/dlls/ntdll/unix/virtual.c index 828cbafb352..360396f9de0 100644 --- a/dlls/ntdll/unix/virtual.c +++ b/dlls/ntdll/unix/virtual.c @@ -2676,8 +2676,9 @@ static void init_teb( TEB *teb, PEB *peb ) InitializeListHead( &teb->ActivationContextStack.FrameListCache ); teb->StaticUnicodeString.Buffer = teb->StaticUnicodeBuffer; teb->StaticUnicodeString.MaximumLength = sizeof(teb->StaticUnicodeBuffer); thread_data->esync_apc_fd = -1; + thread_data->fsync_apc_futex = NULL; thread_data->request_fd = -1; thread_data->reply_fd = -1; thread_data->wait_fd[0] = -1; thread_data->wait_fd[1] = -1; diff --git a/include/wine/server_protocol.h b/include/wine/server_protocol.h index 08b0ddc5f4e..838ae225903 100644 --- a/include/wine/server_protocol.h +++ b/include/wine/server_protocol.h @@ -5434,6 +5434,91 @@ struct get_esync_apc_fd_reply struct reply_header __header; }; +enum fsync_type +{ + FSYNC_SEMAPHORE = 1, + FSYNC_AUTO_EVENT, + FSYNC_MANUAL_EVENT, + FSYNC_MUTEX, + FSYNC_AUTO_SERVER, + FSYNC_MANUAL_SERVER, + FSYNC_QUEUE, +}; + + +struct create_fsync_request +{ + struct request_header __header; + unsigned int access; + int low; + int high; + int type; + /* VARARG(objattr,object_attributes); */ + char __pad_28[4]; +}; +struct create_fsync_reply +{ + struct reply_header __header; + obj_handle_t handle; + int type; + unsigned int shm_idx; + char __pad_20[4]; +}; + + +struct open_fsync_request +{ + struct request_header __header; + unsigned int access; + unsigned int attributes; + obj_handle_t rootdir; + int type; + /* VARARG(name,unicode_str); */ + char __pad_28[4]; +}; +struct open_fsync_reply +{ + struct reply_header __header; + obj_handle_t handle; + int type; + unsigned int shm_idx; + char __pad_20[4]; +}; + + +struct get_fsync_idx_request +{ + struct request_header __header; + obj_handle_t handle; +}; +struct get_fsync_idx_reply +{ + struct reply_header __header; + int type; + unsigned int shm_idx; +}; + +struct fsync_msgwait_request +{ + struct request_header __header; + int in_msgwait; +}; +struct fsync_msgwait_reply +{ + struct reply_header __header; +}; + +struct get_fsync_apc_idx_request +{ + struct request_header __header; + char __pad_12[4]; +}; +struct get_fsync_apc_idx_reply +{ + struct reply_header __header; + unsigned int shm_idx; + char __pad_12[4]; +}; enum request { @@ -5716,9 +5801,15 @@ enum request REQ_get_esync_fd, REQ_esync_msgwait, REQ_get_esync_apc_fd, + REQ_create_fsync, + REQ_open_fsync, + REQ_get_fsync_idx, + REQ_fsync_msgwait, + REQ_get_fsync_apc_idx, REQ_NB_REQUESTS }; + union generic_request { struct request_max_size max_size; @@ -6002,6 +6093,11 @@ union generic_request struct get_esync_fd_request get_esync_fd_request; struct esync_msgwait_request esync_msgwait_request; struct get_esync_apc_fd_request get_esync_apc_fd_request; + struct create_fsync_request create_fsync_request; + struct open_fsync_request open_fsync_request; + struct get_fsync_idx_request get_fsync_idx_request; + struct fsync_msgwait_request fsync_msgwait_request; + struct get_fsync_apc_idx_request get_fsync_apc_idx_request; }; union generic_reply { @@ -6286,6 +6382,11 @@ union generic_reply struct get_esync_fd_reply get_esync_fd_reply; struct esync_msgwait_reply esync_msgwait_reply; struct get_esync_apc_fd_reply get_esync_apc_fd_reply; + struct create_fsync_reply create_fsync_reply; + struct open_fsync_reply open_fsync_reply; + struct get_fsync_idx_reply get_fsync_idx_reply; + struct fsync_msgwait_reply fsync_msgwait_reply; + struct get_fsync_apc_idx_reply get_fsync_apc_idx_reply; }; /* ### protocol_version begin ### */ diff --git a/server/Makefile.in b/server/Makefile.in index 8bd612b4728..6dc5c465e52 100644 --- a/server/Makefile.in +++ b/server/Makefile.in @@ -15,6 +15,7 @@ C_SRCS = \ event.c \ fd.c \ file.c \ + fsync.c \ handle.c \ hook.c \ mach.c \ diff --git a/server/async.c b/server/async.c index bdd68663ab9..4655c41763c 100644 --- a/server/async.c +++ b/server/async.c @@ -71,6 +71,7 @@ static const struct object_ops async_ops = remove_queue, /* remove_queue */ async_signaled, /* signaled */ NULL, /* get_esync_fd */ + NULL, /* get_fsync_idx */ async_satisfied, /* satisfied */ no_signal, /* signal */ no_get_fd, /* get_fd */ @@ -486,6 +487,7 @@ static const struct object_ops iosb_ops = NULL, /* remove_queue */ NULL, /* signaled */ NULL, /* get_esync_fd */ + NULL, /* get_fsync_idx */ NULL, /* satisfied */ no_signal, /* signal */ no_get_fd, /* get_fd */ diff --git a/server/atom.c b/server/atom.c index 73d858fef82..7acef97c30d 100644 --- a/server/atom.c +++ b/server/atom.c @@ -81,6 +81,7 @@ static const struct object_ops atom_table_ops = NULL, /* remove_queue */ NULL, /* signaled */ NULL, /* get_esync_fd */ + NULL, /* get_fsync_idx */ NULL, /* satisfied */ no_signal, /* signal */ no_get_fd, /* get_fd */ diff --git a/server/change.c b/server/change.c index 9c84f01180c..4c11f160f77 100644 --- a/server/change.c +++ b/server/change.c @@ -116,6 +116,7 @@ static const struct object_ops dir_ops = remove_queue, /* remove_queue */ default_fd_signaled, /* signaled */ default_fd_get_esync_fd, /* get_esync_fd */ + default_fd_get_fsync_idx, /* get_fsync_idx */ no_satisfied, /* satisfied */ no_signal, /* signal */ dir_get_fd, /* get_fd */ diff --git a/server/clipboard.c b/server/clipboard.c index 54a5fb683cc..1fa61b67a88 100644 --- a/server/clipboard.c +++ b/server/clipboard.c @@ -78,6 +78,7 @@ static const struct object_ops clipboard_ops = NULL, /* remove_queue */ NULL, /* signaled */ NULL, /* get_esync_fd */ + NULL, /* get_fsync_idx */ NULL, /* satisfied */ no_signal, /* signal */ no_get_fd, /* get_fd */ diff --git a/server/completion.c b/server/completion.c index 176cf1b817e..07c32d1adfa 100644 --- a/server/completion.c +++ b/server/completion.c @@ -65,6 +65,7 @@ static const struct object_ops completion_ops = remove_queue, /* remove_queue */ completion_signaled, /* signaled */ NULL, /* get_esync_fd */ + NULL, /* get_fsync_idx */ no_satisfied, /* satisfied */ no_signal, /* signal */ no_get_fd, /* get_fd */ diff --git a/server/console.c b/server/console.c index 9564b2d2d7d..8796a11eef7 100644 --- a/server/console.c +++ b/server/console.c @@ -43,6 +43,7 @@ #include "winternl.h" #include "wine/condrv.h" #include "esync.h" +#include "fsync.h" struct screen_buffer; @@ -83,6 +84,7 @@ static const struct object_ops console_ops = remove_queue, /* remove_queue */ console_signaled, /* signaled */ NULL, /* get_esync_fd */ + NULL, /* get_fsync_idx */ no_satisfied, /* satisfied */ no_signal, /* signal */ console_get_fd, /* get_fd */ @@ -140,14 +142,16 @@ struct console_server int term_fd; /* UNIX terminal fd */ struct termios termios; /* original termios */ int esync_fd; + unsigned int fsync_idx; }; static void console_server_dump( struct object *obj, int verbose ); static void console_server_destroy( struct object *obj ); static int console_server_signaled( struct object *obj, struct wait_queue_entry *entry ); static int console_server_get_esync_fd( struct object *obj, enum esync_type *type ); +static unsigned int console_server_get_fsync_idx( struct object *obj, enum fsync_type *type ); static struct fd *console_server_get_fd( struct object *obj ); static struct object *console_server_lookup_name( struct object *obj, struct unicode_str *name, unsigned int attr, struct object *root ); static struct object *console_server_open_file( struct object *obj, unsigned int access, unsigned int sharing, unsigned int options ); @@ -160,6 +164,7 @@ static const struct object_ops console_server_ops = remove_queue, /* remove_queue */ console_server_signaled, /* signaled */ console_server_get_esync_fd, /* get_esync_fd */ + console_server_get_fsync_idx, /* get_fsync_idx */ no_satisfied, /* satisfied */ no_signal, /* signal */ console_server_get_fd, /* get_fd */ @@ -229,6 +234,7 @@ static const struct object_ops screen_buffer_ops = NULL, /* remove_queue */ NULL, /* signaled */ NULL, /* get_esync_fd */ + NULL, /* get_fsync_idx */ NULL, /* satisfied */ no_signal, /* signal */ screen_buffer_get_fd, /* get_fd */ @@ -278,6 +284,7 @@ static const struct object_ops console_device_ops = NULL, /* remove_queue */ NULL, /* signaled */ NULL, /* get_esync_fd */ + NULL, /* get_fsync_idx */ no_satisfied, /* satisfied */ no_signal, /* signal */ no_get_fd, /* get_fd */ @@ -309,6 +316,7 @@ static const struct object_ops console_input_ops = NULL, /* remove_queue */ NULL, /* signaled */ NULL, /* get_esync_fd */ + NULL, /* get_fsync_idx */ no_satisfied, /* satisfied */ no_signal, /* signal */ console_input_get_fd, /* get_fd */ @@ -340,6 +348,7 @@ static const struct object_ops console_output_ops = NULL, /* remove_queue */ NULL, /* signaled */ NULL, /* get_esync_fd */ + NULL, /* get_fsync_idx */ no_satisfied, /* satisfied */ no_signal, /* signal */ console_output_get_fd, /* get_fd */ @@ -379,6 +388,7 @@ static const struct object_ops console_connection_ops = NULL, /* remove_queue */ NULL, /* signaled */ NULL, /* get_esync_fd */ + NULL, /* get_fsync_idx */ no_satisfied, /* satisfied */ no_signal, /* signal */ console_connection_get_fd, /* get_fd */ @@ -850,6 +860,13 @@ static int console_server_get_esync_fd( struct object *obj, enum esync_type *typ return server->esync_fd; } +static unsigned int console_server_get_fsync_idx( struct object *obj, enum fsync_type *type ) +{ + struct console_server *server = (struct console_server*)obj; + *type = FSYNC_MANUAL_SERVER; + return server->fsync_idx; +} + static struct fd *console_server_get_fd( struct object* obj ) { struct console_server *server = (struct console_server*)obj; @@ -882,6 +899,9 @@ static struct object *create_console_server( void ) allow_fd_caching(server->fd); server->esync_fd = -1; + if (do_fsync()) + server->fsync_idx = fsync_alloc_shm( 0, 0 ); + if (do_esync()) server->esync_fd = esync_create_fd( 0, 0 ); diff --git a/server/debugger.c b/server/debugger.c index c37f97aa0b6..b7d88cfd2f2 100644 --- a/server/debugger.c +++ b/server/debugger.c @@ -74,6 +74,7 @@ static const struct object_ops debug_event_ops = remove_queue, /* remove_queue */ debug_event_signaled, /* signaled */ NULL, /* get_esync_fd */ + NULL, /* get_fsync_idx */ no_satisfied, /* satisfied */ no_signal, /* signal */ no_get_fd, /* get_fd */ diff --git a/server/device.c b/server/device.c index b8ce131c732..c56151c9b18 100644 --- a/server/device.c +++ b/server/device.c @@ -40,6 +40,7 @@ #include "request.h" #include "process.h" #include "esync.h" +#include "fsync.h" /* IRP object */ @@ -70,6 +71,7 @@ static const struct object_ops irp_call_ops = remove_queue, /* remove_queue */ irp_call_signaled, /* signaled */ NULL, /* get_esync_fd */ + NULL, /* get_fsync_idx */ no_satisfied, /* satisfied */ no_signal, /* signal */ no_get_fd, /* get_fd */ @@ -97,11 +99,13 @@ struct device_manager struct irp_call *current_call; /* call currently executed on client side */ struct wine_rb_tree kernel_objects; /* map of objects that have client side pointer associated */ int esync_fd; /* esync file descriptor */ + unsigned int fsync_idx; }; static void device_manager_dump( struct object *obj, int verbose ); static int device_manager_signaled( struct object *obj, struct wait_queue_entry *entry ); static int device_manager_get_esync_fd( struct object *obj, enum esync_type *type ); +static unsigned int device_manager_get_fsync_idx( struct object *obj, enum fsync_type *type ); static void device_manager_destroy( struct object *obj ); static const struct object_ops device_manager_ops = @@ -113,6 +117,7 @@ static const struct object_ops device_manager_ops = remove_queue, /* remove_queue */ device_manager_signaled, /* signaled */ device_manager_get_esync_fd, /* get_esync_fd */ + device_manager_get_fsync_idx, /* get_fsync_idx */ no_satisfied, /* satisfied */ no_signal, /* signal */ no_get_fd, /* get_fd */ @@ -158,6 +163,7 @@ static const struct object_ops device_ops = NULL, /* remove_queue */ NULL, /* signaled */ NULL, /* get_esync_fd */ + NULL, /* get_fsync_idx */ no_satisfied, /* satisfied */ no_signal, /* signal */ no_get_fd, /* get_fd */ @@ -210,6 +216,7 @@ static const struct object_ops device_file_ops = remove_queue, /* remove_queue */ default_fd_signaled, /* signaled */ NULL, /* get_esync_fd */ + NULL, /* get_fsync_idx */ no_satisfied, /* satisfied */ no_signal, /* signal */ device_file_get_fd, /* get_fd */ @@ -754,6 +761,9 @@ static void delete_file( struct device_file *file ) /* terminate all pending requests */ LIST_FOR_EACH_ENTRY_SAFE( irp, next, &file->requests, struct irp_call, dev_entry ) { + if (do_fsync() && file->device->manager && list_empty( &file->device->manager->requests )) + fsync_clear( &file->device->manager->obj ); + if (do_esync() && file->device->manager && list_empty( &file->device->manager->requests )) esync_clear( file->device->manager->esync_fd ); @@ -799,6 +809,13 @@ static int device_manager_get_esync_fd( struct object *obj, enum esync_type *typ return manager->esync_fd; } +static unsigned int device_manager_get_fsync_idx( struct object *obj, enum fsync_type *type ) +{ + struct device_manager *manager = (struct device_manager *)obj; + *type = FSYNC_MANUAL_SERVER; + return manager->fsync_idx; +} + static void device_manager_destroy( struct object *obj ) { struct device_manager *manager = (struct device_manager *)obj; @@ -849,6 +866,9 @@ static struct device_manager *create_device_manager(void) list_init( &manager->requests ); wine_rb_init( &manager->kernel_objects, compare_kernel_object ); + if (do_fsync()) + manager->fsync_idx = fsync_alloc_shm( 0, 0 ); + if (do_esync()) manager->esync_fd = esync_create_fd( 0, 0 ); } @@ -1017,6 +1037,9 @@ DECL_HANDLER(get_next_device_request) if (irp->file) grab_object( irp ); manager->current_call = irp; + if (do_fsync() && list_empty( &manager->requests )) + fsync_clear( &manager->obj ); + if (do_esync() && list_empty( &manager->requests )) esync_clear( manager->esync_fd ); } diff --git a/server/directory.c b/server/directory.c index faf41585ed8..26e5812073a 100644 --- a/server/directory.c +++ b/server/directory.c @@ -58,6 +58,7 @@ static const struct object_ops object_type_ops = NULL, /* remove_queue */ NULL, /* signaled */ NULL, /* get_esync_fd */ + NULL, /* get_fsync_idx */ NULL, /* satisfied */ no_signal, /* signal */ no_get_fd, /* get_fd */ @@ -96,6 +97,7 @@ static const struct object_ops directory_ops = NULL, /* remove_queue */ NULL, /* signaled */ NULL, /* get_esync_fd */ + NULL, /* get_fsync_idx */ NULL, /* satisfied */ no_signal, /* signal */ no_get_fd, /* get_fd */ diff --git a/server/esync.c b/server/esync.c index d79be851c8f..e975b1e6c2b 100644 --- a/server/esync.c +++ b/server/esync.c @@ -44,6 +44,7 @@ #include "request.h" #include "file.h" #include "esync.h" +#include "fsync.h" int do_esync(void) { @@ -51,7 +52,7 @@ int do_esync(void) static int do_esync_cached = -1; if (do_esync_cached == -1) - do_esync_cached = getenv("WINEESYNC") && atoi(getenv("WINEESYNC")); + do_esync_cached = getenv("WINEESYNC") && atoi(getenv("WINEESYNC")) && !do_fsync(); return do_esync_cached; #else @@ -130,6 +131,7 @@ const struct object_ops esync_ops = NULL, /* remove_queue */ NULL, /* signaled */ esync_get_esync_fd, /* get_esync_fd */ + NULL, /* get_fsync_idx */ NULL, /* satisfied */ no_signal, /* signal */ no_get_fd, /* get_fd */ diff --git a/server/event.c b/server/event.c index b6f989d4d6a..ae7782695f7 100644 --- a/server/event.c +++ b/server/event.c @@ -36,4 +36,5 @@ #include "request.h" #include "security.h" #include "esync.h" +#include "fsync.h" @@ -44,6 +45,7 @@ struct event int manual_reset; /* is it a manual reset event? */ int signaled; /* event has been signaled */ int esync_fd; /* esync file descriptor */ + unsigned int fsync_idx; }; static void event_dump( struct object *obj, int verbose ); @@ -51,5 +53,6 @@ static struct object_type *event_get_type( struct object *obj ); static int event_signaled( struct object *obj, struct wait_queue_entry *entry ); static void event_satisfied( struct object *obj, struct wait_queue_entry *entry ); static int event_get_esync_fd( struct object *obj, enum esync_type *type ); +static unsigned int event_get_fsync_idx( struct object *obj, enum fsync_type *type ); static int event_signal( struct object *obj, unsigned int access); static struct list *event_get_kernel_obj_list( struct object *obj ); @@ -65,6 +68,7 @@ static const struct object_ops event_ops = remove_queue, /* remove_queue */ event_signaled, /* signaled */ event_get_esync_fd, /* get_esync_fd */ + event_get_fsync_idx, /* get_fsync_idx */ event_satisfied, /* satisfied */ event_signal, /* signal */ no_get_fd, /* get_fd */ @@ -101,6 +105,7 @@ static const struct object_ops keyed_event_ops = remove_queue, /* remove_queue */ keyed_event_signaled, /* signaled */ NULL, /* get_esync_fd */ + NULL, /* get_fsync_idx */ no_satisfied, /* satisfied */ no_signal, /* signal */ no_get_fd, /* get_fd */ @@ -133,6 +138,9 @@ struct event *create_event( struct object *root, const struct unicode_str *name, event->manual_reset = manual_reset; event->signaled = initial_state; + if (do_fsync()) + event->fsync_idx = fsync_alloc_shm( initial_state, 0 ); + if (do_esync()) event->esync_fd = esync_create_fd( initial_state, 0 ); } @@ -143,6 +151,10 @@ struct event *create_event( struct object *root, const struct unicode_str *name, struct event *get_event_obj( struct process *process, obj_handle_t handle, unsigned int access ) { struct object *obj; + + if (do_fsync() && (obj = get_handle_obj( process, handle, access, &fsync_ops))) + return (struct event *)obj; /* even though it's not an event */ + if (do_esync() && (obj = get_handle_obj( process, handle, access, &esync_ops))) return (struct event *)obj; /* even though it's not an event */ @@ -155,10 +167,19 @@ void pulse_event( struct event *event ) /* wake up all waiters if manual reset, a single one otherwise */ wake_up( &event->obj, !event->manual_reset ); event->signaled = 0; + + if (do_fsync()) + fsync_clear( &event->obj ); } void set_event( struct event *event ) { + if (do_fsync() && event->obj.ops == &fsync_ops) + { + fsync_set_event( (struct fsync *)event ); + return; + } + if (do_esync() && event->obj.ops == &esync_ops) { esync_set_event( (struct esync *)event ); @@ -172,6 +193,12 @@ void set_event( struct event *event ) void reset_event( struct event *event ) { + if (do_fsync() && event->obj.ops == &fsync_ops) + { + fsync_reset_event( (struct fsync *)event ); + return; + } + if (do_esync() && event->obj.ops == &esync_ops) { esync_reset_event( (struct esync *)event ); @@ -179,6 +206,9 @@ void reset_event( struct event *event ) } event->signaled = 0; + if (do_fsync()) + fsync_clear( &event->obj ); + if (do_esync()) esync_clear( event->esync_fd ); } @@ -212,6 +242,13 @@ static int event_get_esync_fd( struct object *obj, enum esync_type *type ) return event->esync_fd; } +static unsigned int event_get_fsync_idx( struct object *obj, enum fsync_type *type ) +{ + struct event *event = (struct event *)obj; + *type = FSYNC_MANUAL_SERVER; + return event->fsync_idx; +} + static void event_satisfied( struct object *obj, struct wait_queue_entry *entry ) { struct event *event = (struct event *)obj; diff --git a/server/fd.c b/server/fd.c index ea6ece2a111..d592d763635 100644 --- a/server/fd.c +++ b/server/fd.c @@ -103,6 +103,7 @@ #include "process.h" #include "request.h" #include "esync.h" +#include "fsync.h" #include "winternl.h" #include "winioctl.h" @@ -205,6 +206,7 @@ struct fd apc_param_t comp_key; /* completion key to set in completion events */ unsigned int comp_flags; /* completion flags */ int esync_fd; /* esync file descriptor */ + unsigned int fsync_idx; /* fsync shm index */ }; static void fd_dump( struct object *obj, int verbose ); @@ -219,6 +221,7 @@ static const struct object_ops fd_ops = NULL, /* remove_queue */ NULL, /* signaled */ NULL, /* get_esync_fd */ + NULL, /* get_fsync_idx */ NULL, /* satisfied */ no_signal, /* signal */ no_get_fd, /* get_fd */ @@ -261,6 +264,7 @@ static const struct object_ops device_ops = NULL, /* remove_queue */ NULL, /* signaled */ NULL, /* get_esync_fd */ + NULL, /* get_fsync_idx */ NULL, /* satisfied */ no_signal, /* signal */ no_get_fd, /* get_fd */ @@ -302,6 +306,7 @@ static const struct object_ops inode_ops = NULL, /* remove_queue */ NULL, /* signaled */ NULL, /* get_esync_fd */ + NULL, /* get_fsync_idx */ NULL, /* satisfied */ no_signal, /* signal */ no_get_fd, /* get_fd */ @@ -345,6 +350,7 @@ static const struct object_ops file_lock_ops = remove_queue, /* remove_queue */ file_lock_signaled, /* signaled */ NULL, /* get_esync_fd */ + NULL, /* get_fsync_idx */ no_satisfied, /* satisfied */ no_signal, /* signal */ no_get_fd, /* get_fd */ @@ -1714,6 +1720,7 @@ static struct fd *alloc_fd_object(void) fd->completion = NULL; fd->comp_flags = 0; fd->esync_fd = -1; + fd->fsync_idx = 0; init_async_queue( &fd->read_q ); init_async_queue( &fd->write_q ); init_async_queue( &fd->wait_q ); @@ -1723,6 +1730,9 @@ static struct fd *alloc_fd_object(void) if (do_esync()) fd->esync_fd = esync_create_fd( 1, 0 ); + if (do_fsync()) + fd->fsync_idx = fsync_alloc_shm( 1, 0 ); + if ((fd->poll_index = add_poll_user( fd )) == -1) { release_object( fd ); @@ -1756,14 +1766,19 @@ struct fd *alloc_pseudo_fd( const struct fd_ops *fd_user_ops, struct object *use fd->comp_flags = 0; fd->no_fd_status = STATUS_BAD_DEVICE_TYPE; fd->esync_fd = -1; + fd->fsync_idx = 0; init_async_queue( &fd->read_q ); init_async_queue( &fd->write_q ); init_async_queue( &fd->wait_q ); list_init( &fd->inode_entry ); list_init( &fd->locks ); + if (do_fsync()) + fd->fsync_idx = fsync_alloc_shm( 0, 0 ); + if (do_esync()) fd->esync_fd = esync_create_fd( 0, 0 ); + return fd; } @@ -2163,6 +2178,9 @@ void set_fd_signaled( struct fd *fd, int signaled ) fd->signaled = signaled; if (signaled) wake_up( fd->user, 0 ); + if (do_fsync() && !signaled) + fsync_clear( fd->user ); + if (do_esync() && !signaled) esync_clear( fd->esync_fd ); } @@ -2205,5 +2223,14 @@ int default_fd_get_esync_fd( struct object *obj, enum esync_type *type ) return ret; } +unsigned int default_fd_get_fsync_idx( struct object *obj, enum fsync_type *type ) +{ + struct fd *fd = get_obj_fd( obj ); + unsigned int ret = fd->fsync_idx; + *type = FSYNC_MANUAL_SERVER; + release_object( fd ); + return ret; +} + int default_fd_get_poll_events( struct fd *fd ) { diff --git a/server/file.c b/server/file.c index 2c0d24da21d..b12defb844c 100644 --- a/server/file.c +++ b/server/file.c @@ -86,6 +86,7 @@ static const struct object_ops file_ops = remove_queue, /* remove_queue */ default_fd_signaled, /* signaled */ NULL, /* get_esync_fd */ + NULL, /* get_fsync_idx */ no_satisfied, /* satisfied */ no_signal, /* signal */ file_get_fd, /* get_fd */ diff --git a/server/file.h b/server/file.h index 8accc8cc7f1..56fbf1f7a46 100644 --- a/server/file.h +++ b/server/file.h @@ -103,5 +103,6 @@ extern char *dup_fd_name( struct fd *root, const char *name ); extern int default_fd_signaled( struct object *obj, struct wait_queue_entry *entry ); extern int default_fd_get_esync_fd( struct object *obj, enum esync_type *type ); +extern unsigned int default_fd_get_fsync_idx( struct object *obj, enum fsync_type *type ); extern int default_fd_get_poll_events( struct fd *fd ); extern void default_poll_event( struct fd *fd, int event ); diff --git a/server/fsync.c b/server/fsync.c new file mode 100644 index 00000000000..0a88ecdd3f6 --- /dev/null +++ b/server/fsync.c @@ -0,0 +1,527 @@ +/* + * futex-based synchronization objects + * + * Copyright (C) 2018 Zebediah Figura + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "config.h" +#include "wine/port.h" + +#include +#include +#include +#include +#include +#ifdef HAVE_SYS_MMAN_H +# include +#endif +#ifdef HAVE_SYS_STAT_H +# include +#endif +#ifdef HAVE_SYS_SYSCALL_H +# include +#endif +#include + +#include "ntstatus.h" +#define WIN32_NO_STATUS +#include "windef.h" +#include "winternl.h" + +#include "handle.h" +#include "request.h" +#include "fsync.h" + +#include "pshpack4.h" +struct futex_wait_block +{ + int *addr; +#if __SIZEOF_POINTER__ == 4 + int pad; +#endif + int val; +}; +#include "poppack.h" + +static inline int futex_wait_multiple( const struct futex_wait_block *futexes, + int count, const struct timespec *timeout ) +{ + return syscall( __NR_futex, futexes, 31, count, timeout, 0, 0 ); +} + +int do_fsync(void) +{ +#ifdef __linux__ + static int do_fsync_cached = -1; + + if (do_fsync_cached == -1) + { + static const struct timespec zero; + futex_wait_multiple( NULL, 0, &zero ); + do_fsync_cached = getenv("WINEFSYNC") && atoi(getenv("WINEFSYNC")) && errno != ENOSYS; + } + + return do_fsync_cached; +#else + return 0; +#endif +} + +static char shm_name[29]; +static int shm_fd; +static off_t shm_size; +static void **shm_addrs; +static int shm_addrs_size; /* length of the allocated shm_addrs array */ +static long pagesize; + +static int is_fsync_initialized; + +static void shm_cleanup(void) +{ + close( shm_fd ); + if (shm_unlink( shm_name ) == -1) + perror( "shm_unlink" ); +} + +void fsync_init(void) +{ + struct stat st; + + if (fstat( config_dir_fd, &st ) == -1) + fatal_error( "cannot stat config dir\n" ); + + if (st.st_ino != (unsigned long)st.st_ino) + sprintf( shm_name, "/wine-%lx%08lx-fsync", (unsigned long)((unsigned long long)st.st_ino >> 32), (unsigned long)st.st_ino ); + else + sprintf( shm_name, "/wine-%lx-fsync", (unsigned long)st.st_ino ); + + if (!shm_unlink( shm_name )) + fprintf( stderr, "fsync: warning: a previous shm file %s was not properly removed\n", shm_name ); + + shm_fd = shm_open( shm_name, O_RDWR | O_CREAT | O_EXCL, 0644 ); + if (shm_fd == -1) + perror( "shm_open" ); + + pagesize = sysconf( _SC_PAGESIZE ); + + shm_addrs = calloc( 128, sizeof(shm_addrs[0]) ); + shm_addrs_size = 128; + + shm_size = pagesize; + if (ftruncate( shm_fd, shm_size ) == -1) + perror( "ftruncate" ); + + is_fsync_initialized = 1; + + fprintf( stderr, "fsync: up and running.\n" ); + + atexit( shm_cleanup ); +} + +static struct list mutex_list = LIST_INIT(mutex_list); + +struct fsync +{ + struct object obj; + unsigned int shm_idx; + enum fsync_type type; + struct list mutex_entry; +}; + +static void fsync_dump( struct object *obj, int verbose ); +static unsigned int fsync_get_fsync_idx( struct object *obj, enum fsync_type *type ); +static unsigned int fsync_map_access( struct object *obj, unsigned int access ); +static void fsync_destroy( struct object *obj ); + +const struct object_ops fsync_ops = +{ + sizeof(struct fsync), /* size */ + &no_type, /* get_type */ + fsync_dump, /* dump */ + no_add_queue, /* add_queue */ + NULL, /* remove_queue */ + NULL, /* signaled */ + NULL, /* get_esync_fd */ + fsync_get_fsync_idx, /* get_fsync_idx */ + NULL, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ + fsync_map_access, /* map_access */ + default_get_sd, /* get_sd */ + default_set_sd, /* set_sd */ + no_get_full_name, /* get_full_name */ + no_lookup_name, /* lookup_name */ + directory_link_name, /* link_name */ + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ + no_close_handle, /* close_handle */ + fsync_destroy /* destroy */ +}; + +static void fsync_dump( struct object *obj, int verbose ) +{ + struct fsync *fsync = (struct fsync *)obj; + assert( obj->ops == &fsync_ops ); + fprintf( stderr, "fsync idx=%d\n", fsync->shm_idx ); +} + +static unsigned int fsync_get_fsync_idx( struct object *obj, enum fsync_type *type) +{ + struct fsync *fsync = (struct fsync *)obj; + *type = fsync->type; + return fsync->shm_idx; +} + +static unsigned int fsync_map_access( struct object *obj, unsigned int access ) +{ + /* Sync objects have the same flags. */ + if (access & GENERIC_READ) access |= STANDARD_RIGHTS_READ | EVENT_QUERY_STATE; + if (access & GENERIC_WRITE) access |= STANDARD_RIGHTS_WRITE | EVENT_MODIFY_STATE; + if (access & GENERIC_EXECUTE) access |= STANDARD_RIGHTS_EXECUTE | SYNCHRONIZE; + if (access & GENERIC_ALL) access |= STANDARD_RIGHTS_ALL | EVENT_QUERY_STATE | EVENT_MODIFY_STATE; + return access & ~(GENERIC_READ | GENERIC_WRITE | GENERIC_EXECUTE | GENERIC_ALL); +} + +static void fsync_destroy( struct object *obj ) +{ + struct fsync *fsync = (struct fsync *)obj; + if (fsync->type == FSYNC_MUTEX) + list_remove( &fsync->mutex_entry ); +} + +static void *get_shm( unsigned int idx ) +{ + int entry = (idx * 8) / pagesize; + int offset = (idx * 8) % pagesize; + + if (entry >= shm_addrs_size) + { + int new_size = max(shm_addrs_size * 2, entry + 1); + + if (!(shm_addrs = realloc( shm_addrs, new_size * sizeof(shm_addrs[0]) ))) + fprintf( stderr, "fsync: couldn't expand shm_addrs array to size %d\n", entry + 1 ); + + memset( shm_addrs + shm_addrs_size, 0, (new_size - shm_addrs_size) * sizeof(shm_addrs[0]) ); + + shm_addrs_size = new_size; + } + + if (!shm_addrs[entry]) + { + void *addr = mmap( NULL, pagesize, PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, entry * pagesize ); + if (addr == (void *)-1) + { + fprintf( stderr, "fsync: failed to map page %d (offset %#lx): ", entry, entry * pagesize ); + perror( "mmap" ); + } + + if (debug_level) + fprintf( stderr, "fsync: Mapping page %d at %p.\n", entry, addr ); + + if (__sync_val_compare_and_swap( &shm_addrs[entry], 0, addr )) + munmap( addr, pagesize ); /* someone beat us to it */ + } + + return (void *)((unsigned long)shm_addrs[entry] + offset); +} + +/* FIXME: This is rather inefficient... */ +static unsigned int shm_idx_counter = 1; + +unsigned int fsync_alloc_shm( int low, int high ) +{ +#ifdef __linux__ + int shm_idx; + int *shm; + + /* this is arguably a bit of a hack, but we need some way to prevent + * allocating shm for the master socket */ + if (!is_fsync_initialized) + return 0; + + shm_idx = shm_idx_counter++; + + while (shm_idx * 8 >= shm_size) + { + /* Better expand the shm section. */ + shm_size += pagesize; + if (ftruncate( shm_fd, shm_size ) == -1) + { + fprintf( stderr, "fsync: couldn't expand %s to size %jd: ", + shm_name, shm_size ); + perror( "ftruncate" ); + } + } + + shm = get_shm( shm_idx ); + assert(shm); + shm[0] = low; + shm[1] = high; + + return shm_idx; +#else + return 0; +#endif +} + +static int type_matches( enum fsync_type type1, enum fsync_type type2 ) +{ + return (type1 == type2) || + ((type1 == FSYNC_AUTO_EVENT || type1 == FSYNC_MANUAL_EVENT) && + (type2 == FSYNC_AUTO_EVENT || type2 == FSYNC_MANUAL_EVENT)); +} + +struct fsync *create_fsync( struct object *root, const struct unicode_str *name, + unsigned int attr, int low, int high, enum fsync_type type, + const struct security_descriptor *sd ) +{ +#ifdef __linux__ + struct fsync *fsync; + + if ((fsync = create_named_object( root, &fsync_ops, name, attr, sd ))) + { + if (get_error() != STATUS_OBJECT_NAME_EXISTS) + { + /* initialize it if it didn't already exist */ + + /* Initialize the shared memory portion. We want to do this on the + * server side to avoid a potential though unlikely race whereby + * the same object is opened and used between the time it's created + * and the time its shared memory portion is initialized. */ + + fsync->shm_idx = fsync_alloc_shm( low, high ); + fsync->type = type; + if (type == FSYNC_MUTEX) + list_add_tail( &mutex_list, &fsync->mutex_entry ); + } + else + { + /* validate the type */ + if (!type_matches( type, fsync->type )) + { + release_object( &fsync->obj ); + set_error( STATUS_OBJECT_TYPE_MISMATCH ); + return NULL; + } + } + } + + return fsync; +#else + set_error( STATUS_NOT_IMPLEMENTED ); + return NULL; +#endif +} + +static inline int futex_wake( int *addr, int val ) +{ + return syscall( __NR_futex, addr, 1, val, NULL, 0, 0 ); +} + +/* shm layout for events or event-like objects. */ +struct fsync_event +{ + int signaled; + int unused; +}; + +void fsync_wake_futex( unsigned int shm_idx ) +{ + struct fsync_event *event; + + if (debug_level) + fprintf( stderr, "fsync_wake_futex: index %u\n", shm_idx ); + + if (!shm_idx) + return; + + event = get_shm( shm_idx ); + if (!__atomic_exchange_n( &event->signaled, 1, __ATOMIC_SEQ_CST )) + futex_wake( &event->signaled, INT_MAX ); +} + +void fsync_wake_up( struct object *obj ) +{ + enum fsync_type type; + + if (debug_level) + fprintf( stderr, "fsync_wake_up: object %p\n", obj ); + + if (obj->ops->get_fsync_idx) + fsync_wake_futex( obj->ops->get_fsync_idx( obj, &type ) ); +} + +void fsync_clear_futex( unsigned int shm_idx ) +{ + struct fsync_event *event; + + if (debug_level) + fprintf( stderr, "fsync_clear_futex: index %u\n", shm_idx ); + + if (!shm_idx) + return; + + event = get_shm( shm_idx ); + __atomic_store_n( &event->signaled, 0, __ATOMIC_SEQ_CST ); +} + +void fsync_clear( struct object *obj ) +{ + enum fsync_type type; + + if (debug_level) + fprintf( stderr, "fsync_clear: object %p\n", obj ); + + if (obj->ops->get_fsync_idx) + fsync_clear_futex( obj->ops->get_fsync_idx( obj, &type ) ); +} + +void fsync_set_event( struct fsync *fsync ) +{ + struct fsync_event *event = get_shm( fsync->shm_idx ); + assert( fsync->obj.ops == &fsync_ops ); + + if (!__atomic_exchange_n( &event->signaled, 1, __ATOMIC_SEQ_CST )) + futex_wake( &event->signaled, INT_MAX ); +} + +void fsync_reset_event( struct fsync *fsync ) +{ + struct fsync_event *event = get_shm( fsync->shm_idx ); + assert( fsync->obj.ops == &fsync_ops ); + + __atomic_store_n( &event->signaled, 0, __ATOMIC_SEQ_CST ); +} + +struct mutex +{ + int tid; + int count; /* recursion count */ +}; + +void fsync_abandon_mutexes( struct thread *thread ) +{ + struct fsync *fsync; + + LIST_FOR_EACH_ENTRY( fsync, &mutex_list, struct fsync, mutex_entry ) + { + struct mutex *mutex = get_shm( fsync->shm_idx ); + + if (mutex->tid == thread->id) + { + if (debug_level) + fprintf( stderr, "fsync_abandon_mutexes() idx=%d\n", fsync->shm_idx ); + mutex->tid = ~0; + mutex->count = 0; + futex_wake( &mutex->tid, INT_MAX ); + } + } +} + +DECL_HANDLER(create_fsync) +{ + struct fsync *fsync; + struct unicode_str name; + struct object *root; + const struct security_descriptor *sd; + const struct object_attributes *objattr = get_req_object_attributes( &sd, &name, &root ); + + if (!do_fsync()) + { + set_error( STATUS_NOT_IMPLEMENTED ); + return; + } + + if (!objattr) return; + + if ((fsync = create_fsync( root, &name, objattr->attributes, req->low, + req->high, req->type, sd ))) + { + if (get_error() == STATUS_OBJECT_NAME_EXISTS) + reply->handle = alloc_handle( current->process, fsync, req->access, objattr->attributes ); + else + reply->handle = alloc_handle_no_access_check( current->process, fsync, + req->access, objattr->attributes ); + + reply->shm_idx = fsync->shm_idx; + reply->type = fsync->type; + release_object( fsync ); + } + + if (root) release_object( root ); +} + +DECL_HANDLER(open_fsync) +{ + struct unicode_str name = get_req_unicode_str(); + + reply->handle = open_object( current->process, req->rootdir, req->access, + &fsync_ops, &name, req->attributes ); + + if (reply->handle) + { + struct fsync *fsync; + + if (!(fsync = (struct fsync *)get_handle_obj( current->process, reply->handle, + 0, &fsync_ops ))) + return; + + if (!type_matches( req->type, fsync->type )) + { + set_error( STATUS_OBJECT_TYPE_MISMATCH ); + release_object( fsync ); + return; + } + + reply->type = fsync->type; + reply->shm_idx = fsync->shm_idx; + release_object( fsync ); + } +} + +/* Retrieve the index of a shm section which will be signaled by the server. */ +DECL_HANDLER(get_fsync_idx) +{ + struct object *obj; + enum fsync_type type; + + if (!(obj = get_handle_obj( current->process, req->handle, SYNCHRONIZE, NULL ))) + return; + + if (obj->ops->get_fsync_idx) + { + reply->shm_idx = obj->ops->get_fsync_idx( obj, &type ); + reply->type = type; + } + else + { + if (debug_level) + { + fprintf( stderr, "%04x: fsync: can't wait on object: ", current->id ); + obj->ops->dump( obj, 0 ); + } + set_error( STATUS_NOT_IMPLEMENTED ); + } + + release_object( obj ); +} + +DECL_HANDLER(get_fsync_apc_idx) +{ + reply->shm_idx = current->fsync_apc_idx; +} diff --git a/server/fsync.h b/server/fsync.h new file mode 100644 index 00000000000..a91939b7f0a --- /dev/null +++ b/server/fsync.h @@ -0,0 +1,34 @@ +/* + * futex-based synchronization objects + * + * Copyright (C) 2018 Zebediah Figura + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +extern int do_fsync(void); +extern void fsync_init(void); +extern unsigned int fsync_alloc_shm( int low, int high ); +extern void fsync_wake_futex( unsigned int shm_idx ); +extern void fsync_clear_futex( unsigned int shm_idx ); +extern void fsync_wake_up( struct object *obj ); +extern void fsync_clear( struct object *obj ); + +struct fsync; + +extern const struct object_ops fsync_ops; +extern void fsync_set_event( struct fsync *fsync ); +extern void fsync_reset_event( struct fsync *fsync ); +extern void fsync_abandon_mutexes( struct thread *thread ); diff --git a/server/handle.c b/server/handle.c index 4d7e4231a07..60e2b2f7d05 100644 --- a/server/handle.c +++ b/server/handle.c @@ -124,6 +124,7 @@ static const struct object_ops handle_table_ops = NULL, /* remove_queue */ NULL, /* signaled */ NULL, /* get_esync_fd */ + NULL, /* get_fsync_idx */ NULL, /* satisfied */ no_signal, /* signal */ no_get_fd, /* get_fd */ diff --git a/server/hook.c b/server/hook.c index 61b5014c442..379f9c074d5 100644 --- a/server/hook.c +++ b/server/hook.c @@ -82,6 +82,7 @@ static const struct object_ops hook_table_ops = NULL, /* remove_queue */ NULL, /* signaled */ NULL, /* get_esync_fd */ + NULL, /* get_fsync_idx */ NULL, /* satisfied */ no_signal, /* signal */ no_get_fd, /* get_fd */ diff --git a/server/mailslot.c b/server/mailslot.c index 378c0bbb274..f3e465530a3 100644 --- a/server/mailslot.c +++ b/server/mailslot.c @@ -79,6 +79,7 @@ static const struct object_ops mailslot_ops = remove_queue, /* remove_queue */ default_fd_signaled, /* signaled */ NULL, /* get_esync_fd */ + NULL, /* get_fsync_idx */ no_satisfied, /* satisfied */ no_signal, /* signal */ mailslot_get_fd, /* get_fd */ @@ -138,6 +139,7 @@ static const struct object_ops mail_writer_ops = NULL, /* remove_queue */ NULL, /* signaled */ NULL, /* get_esync_fd */ + NULL, /* get_fsync_idx */ NULL, /* satisfied */ no_signal, /* signal */ mail_writer_get_fd, /* get_fd */ @@ -202,6 +204,7 @@ static const struct object_ops mailslot_device_ops = NULL, /* remove_queue */ NULL, /* signaled */ NULL, /* get_esync_fd */ + NULL, /* get_fsync_idx */ no_satisfied, /* satisfied */ no_signal, /* signal */ no_get_fd, /* get_fd */ @@ -233,6 +236,7 @@ static const struct object_ops mailslot_device_file_ops = remove_queue, /* remove_queue */ default_fd_signaled, /* signaled */ NULL, /* get_esync_fd */ + NULL, /* get_fsync_idx */ no_satisfied, /* satisfied */ no_signal, /* signal */ mailslot_device_file_get_fd, /* get_fd */ diff --git a/server/main.c b/server/main.c index 385ae5ee370..9582f5707aa 100644 --- a/server/main.c +++ b/server/main.c @@ -37,7 +37,8 @@ #include "thread.h" #include "request.h" #include "unicode.h" #include "esync.h" +#include "fsync.h" /* command-line options */ int debug_level = 0; @@ -141,9 +142,15 @@ int main( int argc, char *argv[] ) sock_init(); open_master_socket(); + if (do_fsync()) + fsync_init(); + if (do_esync()) esync_init(); + if (!do_fsync() && !do_esync()) + fprintf( stderr, "wineserver: using server-side synchronization.\n" ); + if (debug_level) fprintf( stderr, "wineserver: starting (pid=%ld)\n", (long) getpid() ); set_current_time(); init_scheduler(); diff --git a/server/mapping.c b/server/mapping.c index 37da37d6ab3..1d0e1ec2194 100644 --- a/server/mapping.c +++ b/server/mapping.c @@ -69,6 +69,7 @@ static const struct object_ops ranges_ops = NULL, /* remove_queue */ NULL, /* signaled */ NULL, /* get_esync_fd */ + NULL, /* get_fsync_idx */ NULL, /* satisfied */ no_signal, /* signal */ no_get_fd, /* get_fd */ @@ -106,6 +107,7 @@ static const struct object_ops shared_map_ops = NULL, /* remove_queue */ NULL, /* signaled */ NULL, /* get_esync_fd */ + NULL, /* get_fsync_idx */ NULL, /* satisfied */ no_signal, /* signal */ no_get_fd, /* get_fd */ @@ -165,6 +167,7 @@ static const struct object_ops mapping_ops = NULL, /* remove_queue */ NULL, /* signaled */ NULL, /* get_esync_fd */ + NULL, /* get_fsync_idx */ NULL, /* satisfied */ no_signal, /* signal */ mapping_get_fd, /* get_fd */ diff --git a/server/mutex.c b/server/mutex.c index fc236b3e623..f3e1b006924 100644 --- a/server/mutex.c +++ b/server/mutex.c @@ -62,6 +62,7 @@ static const struct object_ops mutex_ops = remove_queue, /* remove_queue */ mutex_signaled, /* signaled */ NULL, /* get_esync_fd */ + NULL, /* get_fsync_idx */ mutex_satisfied, /* satisfied */ mutex_signal, /* signal */ no_get_fd, /* get_fd */ diff --git a/server/named_pipe.c b/server/named_pipe.c index b438682d214..4e30e789f2b 100644 --- a/server/named_pipe.c +++ b/server/named_pipe.c @@ -119,6 +119,7 @@ static const struct object_ops named_pipe_ops = NULL, /* remove_queue */ NULL, /* signaled */ NULL, /* get_esync_fd */ + NULL, /* get_fsync_idx */ NULL, /* satisfied */ no_signal, /* signal */ no_get_fd, /* get_fd */ @@ -164,6 +165,7 @@ static const struct object_ops pipe_server_ops = remove_queue, /* remove_queue */ default_fd_signaled, /* signaled */ default_fd_get_esync_fd, /* get_esync_fd */ + default_fd_get_fsync_idx, /* get_fsync_idx */ no_satisfied, /* satisfied */ no_signal, /* signal */ pipe_end_get_fd, /* get_fd */ @@ -208,6 +210,7 @@ static const struct object_ops pipe_client_ops = remove_queue, /* remove_queue */ default_fd_signaled, /* signaled */ default_fd_get_esync_fd, /* get_esync_fd */ + default_fd_get_fsync_idx, /* get_fsync_idx */ no_satisfied, /* satisfied */ no_signal, /* signal */ pipe_end_get_fd, /* get_fd */ @@ -256,6 +259,7 @@ static const struct object_ops named_pipe_device_ops = NULL, /* remove_queue */ NULL, /* signaled */ NULL, /* get_esync_fd */ + NULL, /* get_fsync_idx */ no_satisfied, /* satisfied */ no_signal, /* signal */ no_get_fd, /* get_fd */ @@ -288,6 +292,7 @@ static const struct object_ops named_pipe_device_file_ops = remove_queue, /* remove_queue */ default_fd_signaled, /* signaled */ NULL, /* get_esync_fd */ + NULL, /* get_fsync_idx */ no_satisfied, /* satisfied */ no_signal, /* signal */ named_pipe_device_file_get_fd, /* get_fd */ diff --git a/server/object.h b/server/object.h index 10a049c8fb8..12ca073ff8b 100644 --- a/server/object.h +++ b/server/object.h @@ -70,6 +70,8 @@ struct object_ops int (*signaled)(struct object *,struct wait_queue_entry *); /* return the esync fd for this object */ int (*get_esync_fd)(struct object *, enum esync_type *type); + /* return the fsync shm idx for this object */ + unsigned int (*get_fsync_idx)(struct object *, enum fsync_type *type); /* wait satisfied */ void (*satisfied)(struct object *,struct wait_queue_entry *); /* signal an object */ diff --git a/server/process.c b/server/process.c index 10ae5712438..6aa2a2b74c1 100644 --- a/server/process.c +++ b/server/process.c @@ -50,5 +50,6 @@ #include "user.h" #include "security.h" #include "esync.h" +#include "fsync.h" /* process object */ @@ -70,6 +71,7 @@ static void process_poll_event( struct fd *fd, int event ); static struct list *process_get_kernel_obj_list( struct object *obj ); static void process_destroy( struct object *obj ); static int process_get_esync_fd( struct object *obj, enum esync_type *type ); +static unsigned int process_get_fsync_idx( struct object *obj, enum fsync_type *type ); static void terminate_process( struct process *process, struct thread *skip, int exit_code ); static const struct object_ops process_ops = @@ -81,6 +83,7 @@ static const struct object_ops process_ops = remove_queue, /* remove_queue */ process_signaled, /* signaled */ process_get_esync_fd, /* get_esync_fd */ + process_get_fsync_idx, /* get_fsync_idx */ no_satisfied, /* satisfied */ no_signal, /* signal */ no_get_fd, /* get_fd */ @@ -133,6 +136,7 @@ static const struct object_ops startup_info_ops = remove_queue, /* remove_queue */ startup_info_signaled, /* signaled */ NULL, /* get_esync_fd */ + NULL, /* get_fsync_idx */ no_satisfied, /* satisfied */ no_signal, /* signal */ no_get_fd, /* get_fd */ @@ -180,6 +184,7 @@ static const struct object_ops job_ops = remove_queue, /* remove_queue */ job_signaled, /* signaled */ NULL, /* get_esync_fd */ + NULL, /* get_fsync_idx */ no_satisfied, /* satisfied */ no_signal, /* signal */ no_get_fd, /* get_fd */ @@ -548,6 +553,7 @@ struct process *create_process( int fd, struct process *parent, int inherit_all, process->rawinput_mouse = NULL; process->rawinput_kbd = NULL; process->esync_fd = -1; + process->fsync_idx = 0; list_init( &process->kernel_object ); list_init( &process->thread_list ); list_init( &process->locks ); @@ -604,6 +610,9 @@ struct process *create_process( int fd, struct process *parent, int inherit_all, if (!token_assign_label( process->token, security_high_label_sid )) goto error; + if (do_fsync()) + process->fsync_idx = fsync_alloc_shm( 0, 0 ); + if (do_esync()) process->esync_fd = esync_create_fd( 0, 0 ); @@ -687,5 +696,12 @@ static int process_get_esync_fd( struct object *obj, enum esync_type *type ) return process->esync_fd; } +static unsigned int process_get_fsync_idx( struct object *obj, enum fsync_type *type ) +{ + struct process *process = (struct process *)obj; + *type = FSYNC_MANUAL_SERVER; + return process->fsync_idx; +} + static unsigned int process_map_access( struct object *obj, unsigned int access ) { diff --git a/server/process.h b/server/process.h index dd56f88a91e..8bf729e5df5 100644 --- a/server/process.h +++ b/server/process.h @@ -99,6 +99,7 @@ struct process const struct rawinput_device *rawinput_kbd; /* rawinput keyboard device, if any */ struct list kernel_object; /* list of kernel object pointers */ int esync_fd; /* esync file descriptor (signaled on exit) */ + unsigned int fsync_idx; }; #define CPU_FLAG(cpu) (1 << (cpu)) diff --git a/server/protocol.def b/server/protocol.def index 80098c23033..3cf4d5ae8b3 100644 --- a/server/protocol.def +++ b/server/protocol.def @@ -3710,3 +3710,57 @@ enum esync_type /* Retrieve the fd to wait on for user APCs. */ @REQ(get_esync_apc_fd) @END + +enum fsync_type +{ + FSYNC_SEMAPHORE = 1, + FSYNC_AUTO_EVENT, + FSYNC_MANUAL_EVENT, + FSYNC_MUTEX, + FSYNC_AUTO_SERVER, + FSYNC_MANUAL_SERVER, + FSYNC_QUEUE, +}; + +/* Create a new futex-based synchronization object */ +@REQ(create_fsync) + unsigned int access; /* wanted access rights */ + int low; /* initial value of low word */ + int high; /* initial value of high word */ + int type; /* type of fsync object */ + VARARG(objattr,object_attributes); /* object attributes */ +@REPLY + obj_handle_t handle; /* handle to the object */ + int type; /* type of fsync object */ + unsigned int shm_idx; /* this object's index into the shm section */ +@END + +/* Open an fsync object */ +@REQ(open_fsync) + unsigned int access; /* wanted access rights */ + unsigned int attributes; /* object attributes */ + obj_handle_t rootdir; /* root directory */ + int type; /* type of fsync object */ + VARARG(name,unicode_str); /* object name */ +@REPLY + obj_handle_t handle; /* handle to the event */ + int type; /* type of fsync object */ + unsigned int shm_idx; /* this object's index into the shm section */ +@END + +/* Retrieve the shm index for an object. */ +@REQ(get_fsync_idx) + obj_handle_t handle; /* handle to the object */ +@REPLY + int type; + unsigned int shm_idx; +@END + +@REQ(fsync_msgwait) + int in_msgwait; /* are we in a message wait? */ +@END + +@REQ(get_fsync_apc_idx) +@REPLY + unsigned int shm_idx; +@END diff --git a/server/queue.c b/server/queue.c index efaf8a0f7e7..6007e67b03d 100644 --- a/server/queue.c +++ b/server/queue.c @@ -44,6 +44,7 @@ #include "request.h" #include "user.h" #include "esync.h" +#include "fsync.h" #define WM_NCMOUSEFIRST WM_NCMOUSEMOVE #define WM_NCMOUSELAST (WM_NCMOUSEFIRST+(WM_MOUSELAST-WM_MOUSEFIRST)) @@ -144,6 +145,8 @@ struct msg_queue unsigned int ignore_post_msg; /* ignore post messages newer than this unique id */ int esync_fd; /* esync file descriptor (signalled on message) */ int esync_in_msgwait; /* our thread is currently waiting on us */ + unsigned int fsync_idx; + int fsync_in_msgwait; /* our thread is currently waiting on us */ }; struct hotkey @@ -161,6 +164,7 @@ static int msg_queue_add_queue( struct object *obj, struct wait_queue_entry *ent static void msg_queue_remove_queue( struct object *obj, struct wait_queue_entry *entry ); static int msg_queue_signaled( struct object *obj, struct wait_queue_entry *entry ); static int msg_queue_get_esync_fd( struct object *obj, enum esync_type *type ); +static unsigned int msg_queue_get_fsync_idx( struct object *obj, enum fsync_type *type ); static void msg_queue_satisfied( struct object *obj, struct wait_queue_entry *entry ); static void msg_queue_destroy( struct object *obj ); static void msg_queue_poll_event( struct fd *fd, int event ); @@ -177,6 +181,7 @@ static const struct object_ops msg_queue_ops = msg_queue_remove_queue, /* remove_queue */ msg_queue_signaled, /* signaled */ msg_queue_get_esync_fd, /* get_esync_fd */ + msg_queue_get_fsync_idx, /* get_fsync_idx */ msg_queue_satisfied, /* satisfied */ no_signal, /* signal */ no_get_fd, /* get_fd */ @@ -215,6 +220,7 @@ static const struct object_ops thread_input_ops = NULL, /* remove_queue */ NULL, /* signaled */ NULL, /* get_esync_fd */ + NULL, /* get_fsync_idx */ NULL, /* satisfied */ no_signal, /* signal */ no_get_fd, /* get_fd */ @@ -315,12 +321,17 @@ static struct msg_queue *create_msg_queue( struct thread *thread, struct thread_ queue->ignore_post_msg = 0; queue->esync_fd = -1; queue->esync_in_msgwait = 0; + queue->fsync_idx = 0; + queue->fsync_in_msgwait = 0; list_init( &queue->send_result ); list_init( &queue->callback_result ); list_init( &queue->pending_timers ); list_init( &queue->expired_timers ); for (i = 0; i < NB_MSG_KINDS; i++) list_init( &queue->msg_list[i] ); + if (do_fsync()) + queue->fsync_idx = fsync_alloc_shm( 0, 0 ); + if (do_esync()) queue->esync_fd = esync_create_fd( 0, 0 ); @@ -501,6 +512,9 @@ static inline void clear_queue_bits( struct msg_queue *queue, unsigned int bits queue->wake_bits &= ~bits; queue->changed_bits &= ~bits; + if (do_fsync() && !is_signaled( queue )) + fsync_clear( &queue->obj ); + if (do_esync() && !is_signaled( queue )) esync_clear( queue->esync_fd ); } @@ -962,6 +976,9 @@ static int is_queue_hung( struct msg_queue *queue ) return 0; /* thread is waiting on queue -> not hung */ } + if (do_fsync() && queue->fsync_in_msgwait) + return 0; /* thread is waiting on queue in absentia -> not hung */ + if (do_esync() && queue->esync_in_msgwait) return 0; /* thread is waiting on queue in absentia -> not hung */ @@ -1027,6 +1044,13 @@ static int msg_queue_get_esync_fd( struct object *obj, enum esync_type *type ) return queue->esync_fd; } +static unsigned int msg_queue_get_fsync_idx( struct object *obj, enum fsync_type *type ) +{ + struct msg_queue *queue = (struct msg_queue *)obj; + *type = FSYNC_QUEUE; + return queue->fsync_idx; +} + static void msg_queue_satisfied( struct object *obj, struct wait_queue_entry *entry ) { struct msg_queue *queue = (struct msg_queue *)obj; @@ -2405,6 +2429,9 @@ DECL_HANDLER(get_queue_status) reply->changed_bits = queue->changed_bits; queue->changed_bits &= ~req->clear_bits; + if (do_fsync() && !is_signaled( queue )) + fsync_clear( &queue->obj ); + if (do_esync() && !is_signaled( queue )) esync_clear( queue->esync_fd ); } @@ -3406,3 +3433,18 @@ DECL_HANDLER(esync_msgwait) if (queue->fd) set_fd_events( queue->fd, req->in_msgwait ? POLLIN : 0 ); } + +DECL_HANDLER(fsync_msgwait) +{ + struct msg_queue *queue = get_current_queue(); + + if (!queue) return; + queue->fsync_in_msgwait = req->in_msgwait; + + if (current->process->idle_event && !(queue->wake_mask & QS_SMRESULT)) + set_event( current->process->idle_event ); + + /* and start/stop waiting on the driver */ + if (queue->fd) + set_fd_events( queue->fd, req->in_msgwait ? POLLIN : 0 ); +} diff --git a/server/registry.c b/server/registry.c index 8110bbb340c..deed81b470f 100644 --- a/server/registry.c +++ b/server/registry.c @@ -161,6 +161,7 @@ static const struct object_ops key_ops = NULL, /* remove_queue */ NULL, /* signaled */ NULL, /* get_esync_fd */ + NULL, /* get_fsync_idx */ NULL, /* satisfied */ no_signal, /* signal */ no_get_fd, /* get_fd */ diff --git a/server/request.c b/server/request.c index 20b0ec309f3..9fd08139375 100644 --- a/server/request.c +++ b/server/request.c @@ -97,6 +97,7 @@ static const struct object_ops master_socket_ops = NULL, /* remove_queue */ NULL, /* signaled */ NULL, /* get_esync_fd */ + NULL, /* get_fsync_idx */ NULL, /* satisfied */ no_signal, /* signal */ no_get_fd, /* get_fd */ diff --git a/server/request.h b/server/request.h index 49cf56342be..7f74b1bed83 100644 --- a/server/request.h +++ b/server/request.h @@ -398,6 +398,11 @@ DECL_HANDLER(open_esync); DECL_HANDLER(get_esync_fd); DECL_HANDLER(esync_msgwait); DECL_HANDLER(get_esync_apc_fd); +DECL_HANDLER(create_fsync); +DECL_HANDLER(open_fsync); +DECL_HANDLER(get_fsync_idx); +DECL_HANDLER(fsync_msgwait); +DECL_HANDLER(get_fsync_apc_idx); #ifdef WANT_REQUEST_HANDLERS @@ -683,6 +688,11 @@ static const req_handler req_handlers[REQ_NB_REQUESTS] = (req_handler)req_get_esync_fd, (req_handler)req_esync_msgwait, (req_handler)req_get_esync_apc_fd, + (req_handler)req_create_fsync, + (req_handler)req_open_fsync, + (req_handler)req_get_fsync_idx, + (req_handler)req_fsync_msgwait, + (req_handler)req_get_fsync_apc_idx, }; C_ASSERT( sizeof(abstime_t) == 8 ); @@ -2271,6 +2281,34 @@ C_ASSERT( sizeof(struct get_esync_fd_reply) == 16 ); C_ASSERT( FIELD_OFFSET(struct esync_msgwait_request, in_msgwait) == 12 ); C_ASSERT( sizeof(struct esync_msgwait_request) == 16 ); C_ASSERT( sizeof(struct get_esync_apc_fd_request) == 16 ); +C_ASSERT( FIELD_OFFSET(struct create_fsync_request, access) == 12 ); +C_ASSERT( FIELD_OFFSET(struct create_fsync_request, low) == 16 ); +C_ASSERT( FIELD_OFFSET(struct create_fsync_request, high) == 20 ); +C_ASSERT( FIELD_OFFSET(struct create_fsync_request, type) == 24 ); +C_ASSERT( sizeof(struct create_fsync_request) == 32 ); +C_ASSERT( FIELD_OFFSET(struct create_fsync_reply, handle) == 8 ); +C_ASSERT( FIELD_OFFSET(struct create_fsync_reply, type) == 12 ); +C_ASSERT( FIELD_OFFSET(struct create_fsync_reply, shm_idx) == 16 ); +C_ASSERT( sizeof(struct create_fsync_reply) == 24 ); +C_ASSERT( FIELD_OFFSET(struct open_fsync_request, access) == 12 ); +C_ASSERT( FIELD_OFFSET(struct open_fsync_request, attributes) == 16 ); +C_ASSERT( FIELD_OFFSET(struct open_fsync_request, rootdir) == 20 ); +C_ASSERT( FIELD_OFFSET(struct open_fsync_request, type) == 24 ); +C_ASSERT( sizeof(struct open_fsync_request) == 32 ); +C_ASSERT( FIELD_OFFSET(struct open_fsync_reply, handle) == 8 ); +C_ASSERT( FIELD_OFFSET(struct open_fsync_reply, type) == 12 ); +C_ASSERT( FIELD_OFFSET(struct open_fsync_reply, shm_idx) == 16 ); +C_ASSERT( sizeof(struct open_fsync_reply) == 24 ); +C_ASSERT( FIELD_OFFSET(struct get_fsync_idx_request, handle) == 12 ); +C_ASSERT( sizeof(struct get_fsync_idx_request) == 16 ); +C_ASSERT( FIELD_OFFSET(struct get_fsync_idx_reply, type) == 8 ); +C_ASSERT( FIELD_OFFSET(struct get_fsync_idx_reply, shm_idx) == 12 ); +C_ASSERT( sizeof(struct get_fsync_idx_reply) == 16 ); +C_ASSERT( FIELD_OFFSET(struct fsync_msgwait_request, in_msgwait) == 12 ); +C_ASSERT( sizeof(struct fsync_msgwait_request) == 16 ); +C_ASSERT( sizeof(struct get_fsync_apc_idx_request) == 16 ); +C_ASSERT( FIELD_OFFSET(struct get_fsync_apc_idx_reply, shm_idx) == 8 ); +C_ASSERT( sizeof(struct get_fsync_apc_idx_reply) == 16 ); #endif /* WANT_REQUEST_HANDLERS */ diff --git a/server/semaphore.c b/server/semaphore.c index ff94e42576b..005b7ec2ffc 100644 --- a/server/semaphore.c +++ b/server/semaphore.c @@ -59,6 +59,7 @@ static const struct object_ops semaphore_ops = remove_queue, /* remove_queue */ semaphore_signaled, /* signaled */ NULL, /* get_esync_fd */ + NULL, /* get_fsync_idx */ semaphore_satisfied, /* satisfied */ semaphore_signal, /* signal */ no_get_fd, /* get_fd */ diff --git a/server/serial.c b/server/serial.c index a50ace9903f..78d33460892 100644 --- a/server/serial.c +++ b/server/serial.c @@ -93,6 +93,7 @@ static const struct object_ops serial_ops = remove_queue, /* remove_queue */ default_fd_signaled, /* signaled */ NULL, /* get_esync_fd */ + NULL, /* get_fsync_idx */ no_satisfied, /* satisfied */ no_signal, /* signal */ serial_get_fd, /* get_fd */ diff --git a/server/signal.c b/server/signal.c index b6d6dcfc4b6..f5ac61b6975 100644 --- a/server/signal.c +++ b/server/signal.c @@ -68,6 +68,7 @@ static const struct object_ops handler_ops = NULL, /* remove_queue */ NULL, /* signaled */ NULL, /* get_esync_fd */ + NULL, /* get_fsync_idx */ NULL, /* satisfied */ no_signal, /* signal */ no_get_fd, /* get_fd */ diff --git a/server/sock.c b/server/sock.c index 6b771911f26..01c8ebc404b 100644 --- a/server/sock.c +++ b/server/sock.c @@ -189,6 +189,7 @@ static const struct object_ops sock_ops = remove_queue, /* remove_queue */ sock_signaled, /* signaled */ NULL, /* get_esync_fd */ + NULL, /* get_fsync_idx */ no_satisfied, /* satisfied */ no_signal, /* signal */ sock_get_fd, /* get_fd */ @@ -1529,6 +1530,7 @@ static const struct object_ops ifchange_ops = NULL, /* remove_queue */ NULL, /* signaled */ NULL, /* get_esync_fd */ + NULL, /* get_fsync_idx */ no_satisfied, /* satisfied */ no_signal, /* signal */ ifchange_get_fd, /* get_fd */ @@ -1750,6 +1752,7 @@ static const struct object_ops socket_device_ops = NULL, /* remove_queue */ NULL, /* signaled */ NULL, /* get_esync_fd */ + NULL, /* get_fsync_idx */ no_satisfied, /* satisfied */ no_signal, /* signal */ no_get_fd, /* get_fd */ diff --git a/server/symlink.c b/server/symlink.c index 1f5ee0bf72b..2b49d379c2c 100644 --- a/server/symlink.c +++ b/server/symlink.c @@ -61,6 +61,7 @@ static const struct object_ops symlink_ops = NULL, /* remove_queue */ NULL, /* signaled */ NULL, /* get_esync_fd */ + NULL, /* get_fsync_idx */ NULL, /* satisfied */ no_signal, /* signal */ no_get_fd, /* get_fd */ diff --git a/server/thread.c b/server/thread.c index f320f2b26e9..00acd2098b6 100644 --- a/server/thread.c +++ b/server/thread.c @@ -52,6 +52,7 @@ #include "user.h" #include "security.h" #include "esync.h" +#include "fsync.h" #ifdef __i386__ @@ -112,6 +113,7 @@ static const struct object_ops thread_apc_ops = remove_queue, /* remove_queue */ thread_apc_signaled, /* signaled */ NULL, /* get_esync_fd */ + NULL, /* get_fsync_idx */ no_satisfied, /* satisfied */ no_signal, /* signal */ no_get_fd, /* get_fd */ @@ -150,6 +152,7 @@ static const struct object_ops context_ops = remove_queue, /* remove_queue */ context_signaled, /* signaled */ NULL, /* get_esync_fd */ + NULL, /* get_fsync_idx */ no_satisfied, /* satisfied */ no_signal, /* signal */ no_get_fd, /* get_fd */ @@ -173,5 +176,6 @@ static void dump_thread( struct object *obj, int verbose ); static int thread_signaled( struct object *obj, struct wait_queue_entry *entry ); static int thread_get_esync_fd( struct object *obj, enum esync_type *type ); +static unsigned int thread_get_fsync_idx( struct object *obj, enum fsync_type *type ); static unsigned int thread_map_access( struct object *obj, unsigned int access ); static void thread_poll_event( struct fd *fd, int event ); static struct list *thread_get_kernel_obj_list( struct object *obj ); @@ -187,6 +191,7 @@ static const struct object_ops thread_ops = remove_queue, /* remove_queue */ thread_signaled, /* signaled */ thread_get_esync_fd, /* get_esync_fd */ + thread_get_fsync_idx, /* get_fsync_idx */ no_satisfied, /* satisfied */ no_signal, /* signal */ no_get_fd, /* get_fd */ @@ -228,5 +233,6 @@ static inline void init_thread_structure( struct thread *thread ) thread->entry_point = 0; thread->esync_fd = -1; thread->esync_apc_fd = -1; + thread->fsync_idx = 0; thread->system_regs = 0; thread->queue = NULL; @@ -364,6 +370,12 @@ struct thread *create_thread( int fd, struct process *process, const struct secu return NULL; } + if (do_fsync()) + { + thread->fsync_idx = fsync_alloc_shm( 0, 0 ); + thread->fsync_apc_idx = fsync_alloc_shm( 0, 0 ); + } + if (do_esync()) { thread->esync_fd = esync_create_fd( 0, 0 ); @@ -485,5 +497,12 @@ static int thread_get_esync_fd( struct object *obj, enum esync_type *type ) return thread->esync_fd; } +static unsigned int thread_get_fsync_idx( struct object *obj, enum fsync_type *type ) +{ + struct thread *thread = (struct thread *)obj; + *type = FSYNC_MANUAL_SERVER; + return thread->fsync_idx; +} + static unsigned int thread_map_access( struct object *obj, unsigned int access ) { @@ -534,6 +553,7 @@ static struct thread_apc *create_apc( struct object *owner, const apc_call_t *ca apc->result.type = APC_NONE; if (owner) grab_object( owner ); } + return apc; } @@ -1069,6 +1089,9 @@ void wake_up( struct object *obj, int max ) struct list *ptr; int ret; + if (do_fsync()) + fsync_wake_up( obj ); + if (do_esync()) esync_wake_up( obj ); @@ -1159,6 +1182,9 @@ static int queue_apc( struct process *process, struct thread *thread, struct thr { wake_thread( thread ); + if (do_fsync() && queue == &thread->user_apc) + fsync_wake_futex( thread->fsync_apc_idx ); + if (do_esync() && queue == &thread->user_apc) esync_wake_fd( thread->esync_apc_fd ); } @@ -1209,6 +1235,9 @@ static struct thread_apc *thread_dequeue_apc( struct thread *thread, int system list_remove( ptr ); } + if (do_fsync() && list_empty( &thread->system_apc ) && list_empty( &thread->user_apc )) + fsync_clear_futex( thread->fsync_apc_idx ); + if (do_esync() && list_empty( &thread->system_apc ) && list_empty( &thread->user_apc )) esync_clear( thread->esync_apc_fd ); @@ -1328,5 +1357,7 @@ void kill_thread( struct thread *thread, int violent_death ) kill_console_processes( thread, 0 ); abandon_mutexes( thread ); + if (do_fsync()) + fsync_abandon_mutexes( thread ); if (do_esync()) esync_abandon_mutexes( thread ); if (violent_death) diff --git a/server/thread.h b/server/thread.h index 0f6108b684a..53627631343 100644 --- a/server/thread.h +++ b/server/thread.h @@ -56,5 +56,7 @@ struct thread struct list mutex_list; /* list of currently owned mutexes */ int esync_fd; /* esync file descriptor (signalled on exit) */ int esync_apc_fd; /* esync apc fd (signalled when APCs are present) */ + unsigned int fsync_idx; + unsigned int fsync_apc_idx; unsigned int system_regs; /* which system regs have been set */ struct msg_queue *queue; /* message queue */ diff --git a/server/timer.c b/server/timer.c index 11fcdf3bae7..300bb08dd67 100644 --- a/server/timer.c +++ b/server/timer.c @@ -37,4 +37,5 @@ #include "handle.h" #include "request.h" #include "esync.h" +#include "fsync.h" @@ -50,10 +51,12 @@ struct timer client_ptr_t callback; /* callback APC function */ client_ptr_t arg; /* callback argument */ int esync_fd; /* esync file descriptor */ + unsigned int fsync_idx; /* fsync shm index */ }; static void timer_dump( struct object *obj, int verbose ); static int timer_signaled( struct object *obj, struct wait_queue_entry *entry ); static int timer_get_esync_fd( struct object *obj, enum esync_type *type ); +static unsigned int timer_get_fsync_idx( struct object *obj, enum fsync_type *type ); static void timer_satisfied( struct object *obj, struct wait_queue_entry *entry ); static void timer_destroy( struct object *obj ); @@ -69,6 +72,7 @@ static const struct object_ops timer_ops = remove_queue, /* remove_queue */ timer_signaled, /* signaled */ timer_get_esync_fd, /* get_esync_fd */ + timer_get_fsync_idx, /* get_fsync_idx */ timer_satisfied, /* satisfied */ no_signal, /* signal */ no_get_fd, /* get_fd */ @@ -105,6 +109,9 @@ static struct timer *create_timer( struct object *root, const struct unicode_str timer->thread = NULL; timer->esync_fd = -1; + if (do_fsync()) + timer->fsync_idx = fsync_alloc_shm( 0, 0 ); + if (do_esync()) timer->esync_fd = esync_create_fd( 0, 0 ); } @@ -181,6 +188,9 @@ static int set_timer( struct timer *timer, timeout_t expire, unsigned int period period = 0; /* period doesn't make any sense for a manual timer */ timer->signaled = 0; + if (do_fsync()) + fsync_clear( &timer->obj ); + if (do_esync()) esync_clear( timer->esync_fd ); } @@ -224,6 +234,13 @@ static int timer_get_esync_fd( struct object *obj, enum esync_type *type ) return timer->esync_fd; } +static unsigned int timer_get_fsync_idx( struct object *obj, enum fsync_type *type ) +{ + struct timer *timer = (struct timer *)obj; + *type = timer->manual ? FSYNC_MANUAL_SERVER : FSYNC_AUTO_SERVER; + return timer->fsync_idx; +} + static void timer_satisfied( struct object *obj, struct wait_queue_entry *entry ) { struct timer *timer = (struct timer *)obj; diff --git a/server/token.c b/server/token.c index fafa86f292c..5e6d7fffc70 100644 --- a/server/token.c +++ b/server/token.c @@ -148,6 +148,7 @@ static const struct object_ops token_ops = NULL, /* remove_queue */ NULL, /* signaled */ NULL, /* get_esync_fd */ + NULL, /* get_fsync_idx */ NULL, /* satisfied */ no_signal, /* signal */ no_get_fd, /* get_fd */ diff --git a/server/trace.c b/server/trace.c index 7c37f80768a..6945ed9ca6d 100644 --- a/server/trace.c +++ b/server/trace.c @@ -4434,6 +4434,63 @@ static void dump_get_esync_apc_fd_request( const struct get_esync_apc_fd_request { } +static void dump_create_fsync_request( const struct create_fsync_request *req ) +{ + fprintf( stderr, " access=%08x", req->access ); + fprintf( stderr, ", low=%d", req->low ); + fprintf( stderr, ", high=%d", req->high ); + fprintf( stderr, ", type=%d", req->type ); + dump_varargs_object_attributes( ", objattr=", cur_size ); +} + +static void dump_create_fsync_reply( const struct create_fsync_reply *req ) +{ + fprintf( stderr, " handle=%04x", req->handle ); + fprintf( stderr, ", type=%d", req->type ); + fprintf( stderr, ", shm_idx=%08x", req->shm_idx ); +} + +static void dump_open_fsync_request( const struct open_fsync_request *req ) +{ + fprintf( stderr, " access=%08x", req->access ); + fprintf( stderr, ", attributes=%08x", req->attributes ); + fprintf( stderr, ", rootdir=%04x", req->rootdir ); + fprintf( stderr, ", type=%d", req->type ); + dump_varargs_unicode_str( ", name=", cur_size ); +} + +static void dump_open_fsync_reply( const struct open_fsync_reply *req ) +{ + fprintf( stderr, " handle=%04x", req->handle ); + fprintf( stderr, ", type=%d", req->type ); + fprintf( stderr, ", shm_idx=%08x", req->shm_idx ); +} + +static void dump_get_fsync_idx_request( const struct get_fsync_idx_request *req ) +{ + fprintf( stderr, " handle=%04x", req->handle ); +} + +static void dump_get_fsync_idx_reply( const struct get_fsync_idx_reply *req ) +{ + fprintf( stderr, " type=%d", req->type ); + fprintf( stderr, ", shm_idx=%08x", req->shm_idx ); +} + +static void dump_fsync_msgwait_request( const struct fsync_msgwait_request *req ) +{ + fprintf( stderr, " in_msgwait=%d", req->in_msgwait ); +} + +static void dump_get_fsync_apc_idx_request( const struct get_fsync_apc_idx_request *req ) +{ +} + +static void dump_get_fsync_apc_idx_reply( const struct get_fsync_apc_idx_reply *req ) +{ + fprintf( stderr, " shm_idx=%08x", req->shm_idx ); +} + static const dump_func req_dumpers[REQ_NB_REQUESTS] = { (dump_func)dump_new_process_request, (dump_func)dump_exec_process_request, @@ -4714,6 +4771,11 @@ static const dump_func req_dumpers[REQ_NB_REQUESTS] = { (dump_func)dump_get_esync_fd_request, (dump_func)dump_esync_msgwait_request, (dump_func)dump_get_esync_apc_fd_request, + (dump_func)dump_create_fsync_request, + (dump_func)dump_open_fsync_request, + (dump_func)dump_get_fsync_idx_request, + (dump_func)dump_fsync_msgwait_request, + (dump_func)dump_get_fsync_apc_idx_request, }; static const dump_func reply_dumpers[REQ_NB_REQUESTS] = { @@ -4996,6 +5058,11 @@ static const dump_func reply_dumpers[REQ_NB_REQUESTS] = { (dump_func)dump_get_esync_fd_reply, NULL, NULL, + (dump_func)dump_create_fsync_reply, + (dump_func)dump_open_fsync_reply, + (dump_func)dump_get_fsync_idx_reply, + NULL, + (dump_func)dump_get_fsync_apc_idx_reply, }; static const char * const req_names[REQ_NB_REQUESTS] = { @@ -5278,6 +5345,11 @@ static const char * const req_names[REQ_NB_REQUESTS] = { "get_esync_fd", "esync_msgwait", "get_esync_apc_fd", + "create_fsync", + "open_fsync", + "get_fsync_idx", + "fsync_msgwait", + "get_fsync_apc_idx", }; static const struct diff --git a/server/winstation.c b/server/winstation.c index 12060b82c83..9ba7c52d115 100644 --- a/server/winstation.c +++ b/server/winstation.c @@ -65,6 +65,7 @@ static const struct object_ops winstation_ops = NULL, /* remove_queue */ NULL, /* signaled */ NULL, /* get_esync_fd */ + NULL, /* get_fsync_idx */ NULL, /* satisfied */ no_signal, /* signal */ no_get_fd, /* get_fd */ @@ -91,6 +92,7 @@ static const struct object_ops desktop_ops = NULL, /* remove_queue */ NULL, /* signaled */ NULL, /* get_esync_fd */ + NULL, /* get_fsync_idx */ NULL, /* satisfied */ no_signal, /* signal */ no_get_fd, /* get_fd */