File ntsync7-staging-MR7226.patch of Package wine
diff --git a/configure b/configure
index dcc2e5bb7a24c27a4a6d161bc13fcefb60cb47f2..8dbd80fff2937207f341099c551e68f5d516f1d9 100755
--- a/configure
+++ b/configure
@@ -7741,6 +7741,12 @@ if test "x$ac_cv_header_linux_major_h" = xyes
then :
printf "%s\n" "#define HAVE_LINUX_MAJOR_H 1" >>confdefs.h
+fi
+ac_fn_c_check_header_compile "$LINENO" "linux/ntsync.h" "ac_cv_header_linux_ntsync_h" "$ac_includes_default"
+if test "x$ac_cv_header_linux_ntsync_h" = xyes
+then :
+ printf "%s\n" "#define HAVE_LINUX_NTSYNC_H 1" >>confdefs.h
+
fi
ac_fn_c_check_header_compile "$LINENO" "linux/param.h" "ac_cv_header_linux_param_h" "$ac_includes_default"
if test "x$ac_cv_header_linux_param_h" = xyes
diff --git a/configure.ac b/configure.ac
index e022f49afadbc4bce86837e91b3aa984037a4763..952dfafcb3eb9d0d56318ec2e010da20df3f7a81 100644
--- a/configure.ac
+++ b/configure.ac
@@ -384,6 +384,7 @@ AC_CHECK_HEADERS(\
linux/input.h \
linux/ioctl.h \
linux/major.h \
+ linux/ntsync.h \
linux/param.h \
linux/serial.h \
linux/types.h \
diff --git a/dlls/ntdll/unix/file.c b/dlls/ntdll/unix/file.c
index 8bc69557057ebad408e3ddbd126702c2eb182ecd..755a6e844a4a17457ee1ffc99b38e77b9dc2abb7 100644
--- a/dlls/ntdll/unix/file.c
+++ b/dlls/ntdll/unix/file.c
@@ -6451,7 +6451,7 @@ NTSTATUS WINAPI NtLockFile( HANDLE file, HANDLE event, PIO_APC_ROUTINE apc, void
}
if (handle)
{
- NtWaitForSingleObject( handle, FALSE, NULL );
+ server_wait_for_object( handle, FALSE, NULL );
NtClose( handle );
}
else /* Unix lock conflict, sleep a bit and retry */
diff --git a/dlls/ntdll/unix/process.c b/dlls/ntdll/unix/process.c
index 0c8b4bb9c67b5d5e24e2a71d5d31f23432ef3d4e..821e91d280f844a723a03e15f89bd472b4a9d7e7 100644
--- a/dlls/ntdll/unix/process.c
+++ b/dlls/ntdll/unix/process.c
@@ -920,7 +920,7 @@ NTSTATUS WINAPI NtCreateUserProcess( HANDLE *process_handle_ptr, HANDLE *thread_
/* wait for the new process info to be ready */
- NtWaitForSingleObject( process_info, FALSE, NULL );
+ server_wait_for_object( process_info, FALSE, NULL );
SERVER_START_REQ( get_new_process_info )
{
req->info = wine_server_obj_handle( process_info );
diff --git a/dlls/ntdll/unix/server.c b/dlls/ntdll/unix/server.c
index a6e07215479651eba3d33f6461f57955b5b67ee5..ec8dd50f27bb7a60a7369eb62b4742921fdae5bf 100644
--- a/dlls/ntdll/unix/server.c
+++ b/dlls/ntdll/unix/server.c
@@ -103,7 +103,7 @@ sigset_t server_block_set; /* signals to block during server calls */
static int fd_socket = -1; /* socket to exchange file descriptors with the server */
static int initial_cwd = -1;
static pid_t server_pid;
-static pthread_mutex_t fd_cache_mutex = PTHREAD_MUTEX_INITIALIZER;
+pthread_mutex_t fd_cache_mutex = PTHREAD_MUTEX_INITIALIZER;
/* atomically exchange a 64-bit value */
static inline LONG64 interlocked_xchg64( LONG64 *dest, LONG64 val )
@@ -789,6 +789,21 @@ unsigned int server_wait( const union select_op *select_op, data_size_t size, UI
}
+/* helper function to perform a server-side wait on an internal handle without
+ * using the fast synchronization path */
+unsigned int server_wait_for_object( HANDLE handle, BOOL alertable, const LARGE_INTEGER *timeout )
+{
+ union select_op select_op;
+ UINT flags = SELECT_INTERRUPTIBLE;
+
+ if (alertable) flags |= SELECT_ALERTABLE;
+
+ select_op.wait.op = SELECT_WAIT;
+ select_op.wait.handles[0] = wine_server_obj_handle( handle );
+ return server_wait( &select_op, offsetof( union select_op, wait.handles[1] ), flags, timeout );
+}
+
+
/***********************************************************************
* NtContinue (NTDLL.@)
*/
@@ -865,7 +880,7 @@ unsigned int server_queue_process_apc( HANDLE process, const union apc_call *cal
}
else
{
- NtWaitForSingleObject( handle, FALSE, NULL );
+ server_wait_for_object( handle, FALSE, NULL );
SERVER_START_REQ( get_apc_result )
{
@@ -1079,7 +1094,7 @@ static inline NTSTATUS get_cached_fd( HANDLE handle, int *fd, enum server_fd_typ
/***********************************************************************
* remove_fd_from_cache
*/
-static int remove_fd_from_cache( HANDLE handle )
+int remove_fd_from_cache( HANDLE handle )
{
unsigned int entry, idx = handle_to_index( handle, &entry );
int fd = -1;
@@ -1793,12 +1808,17 @@ NTSTATUS WINAPI NtDuplicateObject( HANDLE source_process, HANDLE source, HANDLE
return result.dup_handle.status;
}
+ /* hold fd_cache_mutex to prevent the fd from being added again between the
+ * call to remove_fd_from_cache and close_handle */
server_enter_uninterrupted_section( &fd_cache_mutex, &sigset );
/* always remove the cached fd; if the server request fails we'll just
* retrieve it again */
if (options & DUPLICATE_CLOSE_SOURCE)
+ {
fd = remove_fd_from_cache( source );
+ close_inproc_sync_obj( source );
+ }
SERVER_START_REQ( dup_handle )
{
@@ -1864,12 +1884,16 @@ NTSTATUS WINAPI NtClose( HANDLE handle )
if (HandleToLong( handle ) >= ~5 && HandleToLong( handle ) <= ~0)
return STATUS_SUCCESS;
+ /* hold fd_cache_mutex to prevent the fd from being added again between the
+ * call to remove_fd_from_cache and close_handle */
server_enter_uninterrupted_section( &fd_cache_mutex, &sigset );
/* always remove the cached fd; if the server request fails we'll just
* retrieve it again */
fd = remove_fd_from_cache( handle );
+ close_inproc_sync_obj( handle );
+
SERVER_START_REQ( close_handle )
{
req->handle = wine_server_obj_handle( handle );
diff --git a/dlls/ntdll/unix/sync.c b/dlls/ntdll/unix/sync.c
index 2b01aaf83b8e8cf65f10d214aa6cad89ae00dae6..f515865661da67f031b26cf35882a0bfd36b1861 100644
--- a/dlls/ntdll/unix/sync.c
+++ b/dlls/ntdll/unix/sync.c
@@ -30,9 +30,11 @@
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
+#include <inttypes.h>
#include <limits.h>
#include <signal.h>
#include <sys/types.h>
+#include <sys/ioctl.h>
#include <sys/mman.h>
#ifdef HAVE_SYS_SYSCALL_H
#include <sys/syscall.h>
@@ -48,6 +50,8 @@
#endif
#include <string.h>
#include <stdarg.h>
+#include <stdbool.h>
+#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
@@ -57,6 +61,9 @@
#ifdef HAVE_KQUEUE
# include <sys/event.h>
#endif
+#ifdef HAVE_LINUX_NTSYNC_H
+# include <linux/ntsync.h>
+#endif
#include "ntstatus.h"
#define WIN32_NO_STATUS
@@ -74,9 +81,11 @@ HANDLE keyed_event = 0;
static const char *debugstr_timeout( const LARGE_INTEGER *timeout )
{
if (!timeout) return "(infinite)";
- return wine_dbgstr_longlong( timeout->QuadPart );
+ return wine_dbg_sprintf( "%lld.%07ld", (long long)(timeout->QuadPart / TICKSPERSEC),
+ (long)(timeout->QuadPart % TICKSPERSEC) );
}
+
/* return a monotonic time counter, in Win32 ticks */
static inline ULONGLONG monotonic_counter(void)
{
@@ -301,6 +310,879 @@ static unsigned int validate_open_object_attributes( const OBJECT_ATTRIBUTES *at
}
+#ifdef NTSYNC_IOC_EVENT_READ
+
+static int get_linux_sync_device(void)
+{
+ static LONG device = -2;
+
+ if (device == -2)
+ {
+ HANDLE handle;
+ int fd, needs_close;
+ NTSTATUS ret;
+
+ SERVER_START_REQ( get_linux_sync_device )
+ {
+ if (!(ret = wine_server_call( req ))) handle = wine_server_ptr_handle( reply->handle );
+ }
+ SERVER_END_REQ;
+
+ if (!ret)
+ {
+ if (!server_get_unix_fd( handle, 0, &fd, &needs_close, NULL, NULL ))
+ {
+ if (InterlockedCompareExchange( &device, fd, -2 ) != -2)
+ {
+ /* someone beat us to it */
+ if (needs_close) close( fd );
+ NtClose( handle );
+ }
+ /* otherwise don't close the device */
+ }
+ else
+ {
+ InterlockedCompareExchange( &device, -1, -2 );
+ NtClose( handle );
+ }
+ }
+ else
+ {
+ InterlockedCompareExchange( &device, -1, -2 );
+ }
+ }
+ return device;
+}
+
+/* It's possible for synchronization primitives to remain alive even after being
+ * closed, because a thread is still waiting on them. It's rare in practice, and
+ * documented as being undefined behaviour by Microsoft, but it works, and some
+ * applications rely on it. This means we need to refcount handles, and defer
+ * deleting them on the server side until the refcount reaches zero. We do this
+ * by having each client process hold a handle to the in-process synchronization
+ * object, as well as a private refcount. When the client refcount reaches zero,
+ * it closes the handle; when all handles are closed, the server deletes the
+ * in-process synchronization object.
+ *
+ * We also need this for signal-and-wait. The signal and wait operations aren't
+ * atomic, but we can't perform the signal and then return STATUS_INVALID_HANDLE
+ * for the wait—we need to either do both operations or neither. That means we
+ * need to grab references to both objects, and prevent them from being
+ * destroyed before we're done with them.
+ *
+ * We want lookup of objects from the cache to be very fast; ideally, it should
+ * be lock-free. We achieve this by using atomic modifications to "refcount",
+ * and guaranteeing that all other fields are valid and correct *as long as*
+ * refcount is nonzero, and we store the entire structure in memory which will
+ * never be freed.
+ *
+ * This means that acquiring the object can't use a simple atomic increment; it
+ * has to use a compare-and-swap loop to ensure that it doesn't try to increment
+ * an object with a zero refcount. That's still leagues better than a real lock,
+ * though, and release can be a single atomic decrement.
+ *
+ * It also means that threads modifying the cache need to take a lock, to
+ * prevent other threads from writing to it concurrently.
+ *
+ * It's possible for an object currently in use (by a waiter) to be closed and
+ * the same handle immediately reallocated to a different object. This should be
+ * a very rare situation, and in that case we simply don't cache the handle.
+ */
+struct inproc_sync_cache_entry
+{
+ LONG refcount;
+ uint8_t type; /* enum inproc_sync_type, stored as uint8_t to save space */
+ bool closed;
+ unsigned int access;
+ /* handle to the underlying in-process sync object, stored as obj_handle_t
+ * to save space */
+ obj_handle_t handle;
+};
+
+
+/* Convenience structure describing an in-process sync object, filled by
+ * get_inproc_sync_obj(). That function will return a pointer to a cache entry;
+ * if the object could not be cached, it will point to the stack_cache member
+ * instead. */
+struct inproc_sync_obj
+{
+ struct inproc_sync_cache_entry *cache;
+ struct inproc_sync_cache_entry stack_cache;
+ int fd, needs_close;
+};
+
+static void release_cached_inproc_sync_obj( struct inproc_sync_cache_entry *cache )
+{
+ /* save the handle and fd now; as soon as the refcount hits 0 we cannot
+ * access the cache anymore */
+ HANDLE handle = wine_server_ptr_handle( cache->handle );
+ LONG refcount = InterlockedDecrement( &cache->refcount );
+
+ assert( refcount >= 0 );
+
+ if (!refcount)
+ {
+ NTSTATUS ret;
+ int fd;
+
+ /* we can't call NtClose here as we may be inside fd_cache_mutex */
+ fd = remove_fd_from_cache( handle );
+ if (fd >= 0) close( fd );
+ SERVER_START_REQ( close_handle )
+ {
+ req->handle = wine_server_obj_handle( handle );
+ ret = wine_server_call( req );
+ }
+ SERVER_END_REQ;
+
+ assert( !ret );
+ }
+}
+
+static void release_inproc_sync_obj( struct inproc_sync_obj *obj )
+{
+ if (obj->needs_close) close( obj->fd );
+ release_cached_inproc_sync_obj( obj->cache );
+}
+
+
+#define INPROC_SYNC_CACHE_BLOCK_SIZE (65536 / sizeof(struct inproc_sync_cache_entry))
+#define INPROC_SYNC_CACHE_ENTRIES 128
+
+static struct inproc_sync_cache_entry *inproc_sync_cache[INPROC_SYNC_CACHE_ENTRIES];
+static struct inproc_sync_cache_entry inproc_sync_cache_initial_block[INPROC_SYNC_CACHE_BLOCK_SIZE];
+
+static inline unsigned int inproc_sync_handle_to_index( HANDLE handle, unsigned int *entry )
+{
+ unsigned int idx = (wine_server_obj_handle(handle) >> 2) - 1;
+ *entry = idx / INPROC_SYNC_CACHE_BLOCK_SIZE;
+ return idx % INPROC_SYNC_CACHE_BLOCK_SIZE;
+}
+
+
+static struct inproc_sync_cache_entry *cache_inproc_sync_obj( HANDLE handle, obj_handle_t inproc_sync,
+ enum inproc_sync_type type, unsigned int access )
+{
+ unsigned int entry, idx = inproc_sync_handle_to_index( handle, &entry );
+ struct inproc_sync_cache_entry *cache;
+ sigset_t sigset;
+ int refcount;
+
+ /* don't cache pseudo-handles; waiting on them is pointless anyway */
+ if ((ULONG)(ULONG_PTR)handle > 0xfffffffa)
+ return FALSE;
+
+ if (entry >= INPROC_SYNC_CACHE_ENTRIES)
+ {
+ FIXME( "too many allocated handles, not caching %p\n", handle );
+ return NULL;
+ }
+
+ if (!inproc_sync_cache[entry]) /* do we need to allocate a new block of entries? */
+ {
+ if (!entry) inproc_sync_cache[0] = inproc_sync_cache_initial_block;
+ else
+ {
+ static const size_t size = INPROC_SYNC_CACHE_BLOCK_SIZE * sizeof(struct inproc_sync_cache_entry);
+ void *ptr = anon_mmap_alloc( size, PROT_READ | PROT_WRITE );
+ if (ptr == MAP_FAILED) return NULL;
+ if (InterlockedCompareExchangePointer( (void **)&inproc_sync_cache[entry], ptr, NULL ))
+ munmap( ptr, size ); /* someone beat us to it */
+ }
+ }
+
+ cache = &inproc_sync_cache[entry][idx];
+
+ /* Hold fd_cache_mutex instead of a separate mutex, to prevent the same
+ * race between this function and NtClose. That is, prevent the object from
+ * being cached again between close_inproc_sync_obj() and close_handle. */
+ server_enter_uninterrupted_section( &fd_cache_mutex, &sigset );
+
+ if (InterlockedCompareExchange( &cache->refcount, 0, 0 ))
+ {
+ /* We lost the race with another thread trying to cache this object, or
+ * the handle is currently being used for another object (i.e. it was
+ * closed and then reused). We have no way of knowing which, and in the
+ * latter case we can't cache this object until the old one is
+ * completely destroyed, so always return failure. */
+ server_leave_uninterrupted_section( &fd_cache_mutex, &sigset );
+ return NULL;
+ }
+
+ cache->handle = inproc_sync;
+ cache->type = type;
+ cache->access = access;
+ cache->closed = false;
+ /* Make sure we set the other members before the refcount; this store needs
+ * release semantics [paired with the load in get_cached_inproc_sync_obj()].
+ * Set the refcount to 2 (one for the handle, one for the caller). */
+ refcount = InterlockedExchange( &cache->refcount, 2 );
+ assert( !refcount );
+
+ server_leave_uninterrupted_section( &fd_cache_mutex, &sigset );
+
+ return cache;
+}
+
+
+/* returns the previous value */
+static inline LONG interlocked_inc_if_nonzero( LONG *dest )
+{
+ LONG val, tmp;
+ for (val = *dest;; val = tmp)
+ {
+ if (!val || (tmp = InterlockedCompareExchange( dest, val + 1, val )) == val)
+ break;
+ }
+ return val;
+}
+
+
+static struct inproc_sync_cache_entry *get_cached_inproc_sync_obj( HANDLE handle )
+{
+ unsigned int entry, idx = inproc_sync_handle_to_index( handle, &entry );
+ struct inproc_sync_cache_entry *cache;
+
+ if (entry >= INPROC_SYNC_CACHE_ENTRIES || !inproc_sync_cache[entry])
+ return NULL;
+
+ cache = &inproc_sync_cache[entry][idx];
+
+ /* this load needs acquire semantics [paired with the store in
+ * cache_inproc_sync_obj()] */
+ if (!interlocked_inc_if_nonzero( &cache->refcount ))
+ return NULL;
+
+ if (cache->closed)
+ {
+ /* The object is still being used, but "handle" has been closed. The
+ * handle value might have been reused for another object in the
+ * meantime, in which case we have to report that valid object, so
+ * force the caller to check the server. */
+ release_cached_inproc_sync_obj( cache );
+ return NULL;
+ }
+
+ return cache;
+}
+
+
+static BOOL inproc_sync_types_match( enum inproc_sync_type a, enum inproc_sync_type b )
+{
+ if (a == b) return TRUE;
+ if (a == INPROC_SYNC_AUTO_EVENT && b == INPROC_SYNC_MANUAL_EVENT) return TRUE;
+ if (b == INPROC_SYNC_AUTO_EVENT && a == INPROC_SYNC_MANUAL_EVENT) return TRUE;
+ return FALSE;
+}
+
+
+static NTSTATUS get_inproc_sync_obj( HANDLE handle, enum inproc_sync_type desired_type,
+ ACCESS_MASK desired_access, struct inproc_sync_obj *obj )
+{
+ struct inproc_sync_cache_entry *cache;
+ obj_handle_t inproc_sync_handle;
+ enum inproc_sync_type type;
+ unsigned int access;
+ NTSTATUS ret;
+
+ /* We don't need the device right now, but if we can't access it, that
+ * means ntsync isn't available. Fail fast in that case. */
+ if (get_linux_sync_device() < 0)
+ return STATUS_NOT_IMPLEMENTED;
+
+ if (!(cache = get_cached_inproc_sync_obj( handle )))
+ {
+ /* try to retrieve it from the server */
+ SERVER_START_REQ( get_linux_sync_obj )
+ {
+ req->handle = wine_server_obj_handle( handle );
+ if (!(ret = wine_server_call( req )))
+ {
+ inproc_sync_handle = reply->handle;
+ access = reply->access;
+ type = reply->type;
+ }
+ }
+ SERVER_END_REQ;
+
+ if (ret) return ret;
+
+ if (!(cache = cache_inproc_sync_obj( handle, inproc_sync_handle, type, access )))
+ {
+ cache = &obj->stack_cache;
+ cache->handle = inproc_sync_handle;
+ cache->type = type;
+ cache->access = access;
+ cache->closed = false;
+ cache->refcount = 1;
+ }
+ }
+
+ obj->cache = cache;
+
+ if ((ret = server_get_unix_fd( wine_server_ptr_handle( cache->handle ),
+ 0, &obj->fd, &obj->needs_close, NULL, NULL )))
+ return ret;
+
+ if (desired_type != INPROC_SYNC_UNKNOWN && !inproc_sync_types_match( cache->type, desired_type ))
+ {
+ release_inproc_sync_obj( obj );
+ return STATUS_OBJECT_TYPE_MISMATCH;
+ }
+
+ if ((cache->access & desired_access) != desired_access)
+ {
+ release_inproc_sync_obj( obj );
+ return STATUS_ACCESS_DENIED;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+
+/* caller must hold fd_cache_mutex */
+void close_inproc_sync_obj( HANDLE handle )
+{
+ struct inproc_sync_cache_entry *cache = get_cached_inproc_sync_obj( handle );
+
+ if (cache)
+ {
+ cache->closed = true;
+ /* once for the reference we just grabbed, and once for the handle */
+ release_cached_inproc_sync_obj( cache );
+ release_cached_inproc_sync_obj( cache );
+ }
+}
+
+
+static NTSTATUS linux_release_semaphore_obj( int obj, ULONG count, ULONG *prev_count )
+{
+ if (ioctl( obj, NTSYNC_IOC_SEM_RELEASE, &count ) < 0)
+ {
+ if (errno == EOVERFLOW)
+ return STATUS_SEMAPHORE_LIMIT_EXCEEDED;
+ return errno_to_status( errno );
+ }
+ if (prev_count) *prev_count = count;
+ return STATUS_SUCCESS;
+}
+
+
+static NTSTATUS inproc_release_semaphore( HANDLE handle, ULONG count, ULONG *prev_count )
+{
+ struct inproc_sync_obj obj;
+ NTSTATUS ret;
+
+ if ((ret = get_inproc_sync_obj( handle, INPROC_SYNC_SEMAPHORE, SEMAPHORE_MODIFY_STATE, &obj )))
+ return ret;
+
+ ret = linux_release_semaphore_obj( obj.fd, count, prev_count );
+
+ release_inproc_sync_obj( &obj );
+ return ret;
+}
+
+
+static NTSTATUS linux_query_semaphore_obj( int obj, SEMAPHORE_BASIC_INFORMATION *info )
+{
+ struct ntsync_sem_args args = {0};
+
+ if (ioctl( obj, NTSYNC_IOC_SEM_READ, &args ) < 0)
+ return errno_to_status( errno );
+ info->CurrentCount = args.count;
+ info->MaximumCount = args.max;
+ return STATUS_SUCCESS;
+}
+
+
+static NTSTATUS inproc_query_semaphore( HANDLE handle, SEMAPHORE_BASIC_INFORMATION *info )
+{
+ struct inproc_sync_obj obj;
+ NTSTATUS ret;
+
+ if ((ret = get_inproc_sync_obj( handle, INPROC_SYNC_SEMAPHORE, SEMAPHORE_QUERY_STATE, &obj )))
+ return ret;
+
+ ret = linux_query_semaphore_obj( obj.fd, info );
+
+ release_inproc_sync_obj( &obj );
+ return ret;
+}
+
+
+static NTSTATUS linux_set_event_obj( int obj, LONG *prev_state )
+{
+ __u32 prev;
+
+ if (ioctl( obj, NTSYNC_IOC_EVENT_SET, &prev ) < 0)
+ return errno_to_status( errno );
+ if (prev_state) *prev_state = prev;
+ return STATUS_SUCCESS;
+}
+
+
+static NTSTATUS inproc_set_event( HANDLE handle, LONG *prev_state )
+{
+ struct inproc_sync_obj obj;
+ NTSTATUS ret;
+
+ if ((ret = get_inproc_sync_obj( handle, INPROC_SYNC_AUTO_EVENT, EVENT_MODIFY_STATE, &obj )))
+ return ret;
+
+ ret = linux_set_event_obj( obj.fd, prev_state );
+
+ release_inproc_sync_obj( &obj );
+ return ret;
+}
+
+
+static NTSTATUS linux_reset_event_obj( int obj, LONG *prev_state )
+{
+ __u32 prev;
+
+ if (ioctl( obj, NTSYNC_IOC_EVENT_RESET, &prev ) < 0)
+ return errno_to_status( errno );
+ if (prev_state) *prev_state = prev;
+ return STATUS_SUCCESS;
+}
+
+
+static NTSTATUS inproc_reset_event( HANDLE handle, LONG *prev_state )
+{
+ struct inproc_sync_obj obj;
+ NTSTATUS ret;
+
+ if ((ret = get_inproc_sync_obj( handle, INPROC_SYNC_AUTO_EVENT, EVENT_MODIFY_STATE, &obj )))
+ return ret;
+
+ ret = linux_reset_event_obj( obj.fd, prev_state );
+
+ release_inproc_sync_obj( &obj );
+ return ret;
+}
+
+
+static NTSTATUS linux_pulse_event_obj( int obj, LONG *prev_state )
+{
+ __u32 prev;
+
+ if (ioctl( obj, NTSYNC_IOC_EVENT_PULSE, &prev ) < 0)
+ return errno_to_status( errno );
+ if (prev_state) *prev_state = prev;
+ return STATUS_SUCCESS;
+}
+
+
+static NTSTATUS inproc_pulse_event( HANDLE handle, LONG *prev_state )
+{
+ struct inproc_sync_obj obj;
+ NTSTATUS ret;
+
+ if ((ret = get_inproc_sync_obj( handle, INPROC_SYNC_AUTO_EVENT, EVENT_MODIFY_STATE, &obj )))
+ return ret;
+
+ ret = linux_pulse_event_obj( obj.fd, prev_state );
+
+ release_inproc_sync_obj( &obj );
+ return ret;
+}
+
+
+static NTSTATUS linux_query_event_obj( int obj, enum inproc_sync_type type, EVENT_BASIC_INFORMATION *info )
+{
+ struct ntsync_event_args args = {0};
+
+ if (ioctl( obj, NTSYNC_IOC_EVENT_READ, &args ) < 0)
+ return errno_to_status( errno );
+ info->EventType = (type == INPROC_SYNC_AUTO_EVENT) ? SynchronizationEvent : NotificationEvent;
+ info->EventState = args.signaled;
+ return STATUS_SUCCESS;
+}
+
+
+static NTSTATUS inproc_query_event( HANDLE handle, EVENT_BASIC_INFORMATION *info )
+{
+ struct inproc_sync_obj obj;
+ NTSTATUS ret;
+
+ if ((ret = get_inproc_sync_obj( handle, INPROC_SYNC_AUTO_EVENT, EVENT_QUERY_STATE, &obj )))
+ return ret;
+
+ ret = linux_query_event_obj( obj.fd, obj.cache->type, info );
+
+ release_inproc_sync_obj( &obj );
+ return ret;
+}
+
+
+static NTSTATUS linux_release_mutex_obj( int obj, LONG *prev_count )
+{
+ struct ntsync_mutex_args args = {0};
+
+ args.owner = GetCurrentThreadId();
+ if (ioctl( obj, NTSYNC_IOC_MUTEX_UNLOCK, &args ) < 0)
+ {
+ if (errno == EOVERFLOW)
+ return STATUS_MUTANT_LIMIT_EXCEEDED;
+ if (errno == EPERM)
+ return STATUS_MUTANT_NOT_OWNED;
+ return errno_to_status( errno );
+ }
+ if (prev_count) *prev_count = 1 - args.count;
+ return STATUS_SUCCESS;
+}
+
+
+static NTSTATUS inproc_release_mutex( HANDLE handle, LONG *prev_count )
+{
+ struct inproc_sync_obj obj;
+ NTSTATUS ret;
+
+ if ((ret = get_inproc_sync_obj( handle, INPROC_SYNC_MUTEX, 0, &obj )))
+ return ret;
+
+ ret = linux_release_mutex_obj( obj.fd, prev_count );
+
+ release_inproc_sync_obj( &obj );
+ return ret;
+}
+
+
+static NTSTATUS linux_query_mutex_obj( int obj, MUTANT_BASIC_INFORMATION *info )
+{
+ struct ntsync_mutex_args args = {0};
+
+ if (ioctl( obj, NTSYNC_IOC_MUTEX_READ, &args ) < 0)
+ {
+ if (errno == EOWNERDEAD)
+ {
+ info->AbandonedState = TRUE;
+ info->OwnedByCaller = FALSE;
+ info->CurrentCount = 1;
+ return STATUS_SUCCESS;
+ }
+ return errno_to_status( errno );
+ }
+ info->AbandonedState = FALSE;
+ info->OwnedByCaller = (args.owner == GetCurrentThreadId());
+ info->CurrentCount = 1 - args.count;
+ return STATUS_SUCCESS;
+}
+
+
+static NTSTATUS inproc_query_mutex( HANDLE handle, MUTANT_BASIC_INFORMATION *info )
+{
+ struct inproc_sync_obj obj;
+ NTSTATUS ret;
+
+ if ((ret = get_inproc_sync_obj( handle, INPROC_SYNC_MUTEX, MUTANT_QUERY_STATE, &obj )))
+ return ret;
+
+ ret = linux_query_mutex_obj( obj.fd, info );
+
+ release_inproc_sync_obj( &obj );
+ return ret;
+}
+
+static void select_queue(void)
+{
+ SERVER_START_REQ( select_inproc_queue )
+ {
+ wine_server_call( req );
+ }
+ SERVER_END_REQ;
+}
+
+static void unselect_queue( BOOL signaled )
+{
+ SERVER_START_REQ( unselect_inproc_queue )
+ {
+ req->signaled = signaled;
+ wine_server_call( req );
+ }
+ SERVER_END_REQ;
+}
+
+static int get_inproc_alert_obj(void)
+{
+ struct ntdll_thread_data *data = ntdll_get_thread_data();
+ struct inproc_sync_obj obj;
+ HANDLE alert_handle;
+ unsigned int ret;
+
+ if (data->linux_alert_obj == -1)
+ {
+ SERVER_START_REQ( get_inproc_alert_event )
+ {
+ if ((ret = wine_server_call( req )))
+ ERR( "failed to get inproc alert event, status %#x\n", ret );
+ alert_handle = wine_server_ptr_handle( reply->handle );
+ }
+ SERVER_END_REQ;
+
+ if ((ret = get_inproc_sync_obj( alert_handle, INPROC_SYNC_UNKNOWN, SYNCHRONIZE, &obj )))
+ ERR( "failed to get inproc alert obj, status %#x\n", ret );
+ data->linux_alert_obj = obj.fd;
+ /* Don't close the server handle (or the fd). */
+ }
+
+ return data->linux_alert_obj;
+}
+
+static NTSTATUS linux_wait_objs( int device, const DWORD count, const int *objs,
+ BOOLEAN wait_any, BOOLEAN alertable, const LARGE_INTEGER *timeout )
+{
+ struct ntsync_wait_args args = {0};
+ unsigned long request;
+ struct timespec now;
+ int ret;
+
+ if (!timeout || timeout->QuadPart == TIMEOUT_INFINITE)
+ {
+ args.timeout = ~(__u64)0;
+ }
+ else if (timeout->QuadPart <= 0)
+ {
+ clock_gettime( CLOCK_MONOTONIC, &now );
+ args.timeout = (now.tv_sec * NSECPERSEC) + now.tv_nsec + (-timeout->QuadPart * 100);
+ }
+ else
+ {
+ args.timeout = (timeout->QuadPart * 100) - (SECS_1601_TO_1970 * NSECPERSEC);
+ args.flags |= NTSYNC_WAIT_REALTIME;
+ }
+
+ args.objs = (uintptr_t)objs;
+ args.count = count;
+ args.owner = GetCurrentThreadId();
+ args.index = ~0u;
+
+ if (alertable)
+ args.alert = get_inproc_alert_obj();
+
+ if (wait_any || count == 1)
+ request = NTSYNC_IOC_WAIT_ANY;
+ else
+ request = NTSYNC_IOC_WAIT_ALL;
+
+ do
+ {
+ ret = ioctl( device, request, &args );
+ } while (ret < 0 && errno == EINTR);
+
+ if (!ret)
+ {
+ if (args.index == count)
+ {
+ static const LARGE_INTEGER timeout;
+
+ ret = server_wait( NULL, 0, SELECT_INTERRUPTIBLE | SELECT_ALERTABLE, &timeout );
+ assert( ret == STATUS_USER_APC );
+ return ret;
+ }
+
+ return wait_any ? args.index : 0;
+ }
+ if (errno == EOWNERDEAD)
+ return STATUS_ABANDONED + (wait_any ? args.index : 0);
+ if (errno == ETIMEDOUT)
+ return STATUS_TIMEOUT;
+ return errno_to_status( errno );
+}
+
+static NTSTATUS inproc_wait( DWORD count, const HANDLE *handles, BOOLEAN wait_any,
+ BOOLEAN alertable, const LARGE_INTEGER *timeout )
+{
+ struct inproc_sync_obj objs[64];
+ int device, fds[64];
+ HANDLE queue = NULL;
+ NTSTATUS ret;
+ DWORD i, j;
+
+ if ((device = get_linux_sync_device()) < 0)
+ return STATUS_NOT_IMPLEMENTED;
+
+ for (i = 0; i < count; ++i)
+ {
+ if ((ret = get_inproc_sync_obj( handles[i], INPROC_SYNC_UNKNOWN, SYNCHRONIZE, &objs[i] )))
+ {
+ for (j = 0; j < i; ++j)
+ release_inproc_sync_obj( &objs[j] );
+ return ret;
+ }
+ if (objs[i].cache->type == INPROC_SYNC_QUEUE)
+ queue = handles[i];
+
+ fds[i] = objs[i].fd;
+ }
+
+ /* It's common to wait on the message queue alone. Some applications wait
+ * on it in fast paths, with a zero timeout. Since we take two server calls
+ * instead of one when going through inproc_wait(), and since we only need
+ * to go through that path if we're waiting on other objects, just delegate
+ * to the server if we're only waiting on the message queue. */
+ if (count == 1 && queue)
+ {
+ release_inproc_sync_obj( &objs[0] );
+ return server_wait_for_object( handles[0], alertable, timeout );
+ }
+
+ if (queue) select_queue();
+
+ ret = linux_wait_objs( device, count, fds, wait_any, alertable, timeout );
+
+ if (queue) unselect_queue( handles[ret] == queue );
+
+ for (i = 0; i < count; ++i)
+ release_inproc_sync_obj( &objs[i] );
+
+ return ret;
+}
+
+static NTSTATUS inproc_signal_and_wait( HANDLE signal, HANDLE wait,
+ BOOLEAN alertable, const LARGE_INTEGER *timeout )
+{
+ struct inproc_sync_obj signal_obj, wait_obj;
+ HANDLE queue = NULL;
+ NTSTATUS ret;
+ int device;
+
+ if ((device = get_linux_sync_device()) < 0)
+ return STATUS_NOT_IMPLEMENTED;
+
+ if ((ret = get_inproc_sync_obj( signal, INPROC_SYNC_UNKNOWN, 0, &signal_obj )))
+ return ret;
+
+ switch (signal_obj.cache->type)
+ {
+ case INPROC_SYNC_SEMAPHORE:
+ if (!(signal_obj.cache->access & SEMAPHORE_MODIFY_STATE))
+ {
+ release_inproc_sync_obj( &signal_obj );
+ return STATUS_ACCESS_DENIED;
+ }
+ break;
+
+ case INPROC_SYNC_AUTO_EVENT:
+ case INPROC_SYNC_MANUAL_EVENT:
+ if (!(signal_obj.cache->access & EVENT_MODIFY_STATE))
+ {
+ release_inproc_sync_obj( &signal_obj );
+ return STATUS_ACCESS_DENIED;
+ }
+ break;
+
+ case INPROC_SYNC_MUTEX:
+ break;
+
+ default:
+ /* can't be signaled */
+ release_inproc_sync_obj( &signal_obj );
+ return STATUS_OBJECT_TYPE_MISMATCH;
+ }
+
+ if ((ret = get_inproc_sync_obj( wait, INPROC_SYNC_UNKNOWN, SYNCHRONIZE, &wait_obj )))
+ {
+ release_inproc_sync_obj( &signal_obj );
+ return ret;
+ }
+
+ if (wait_obj.cache->type == INPROC_SYNC_QUEUE)
+ queue = wait;
+
+ switch (signal_obj.cache->type)
+ {
+ case INPROC_SYNC_SEMAPHORE:
+ ret = linux_release_semaphore_obj( signal_obj.fd, 1, NULL );
+ break;
+
+ case INPROC_SYNC_AUTO_EVENT:
+ case INPROC_SYNC_MANUAL_EVENT:
+ ret = linux_set_event_obj( signal_obj.fd, NULL );
+ break;
+
+ case INPROC_SYNC_MUTEX:
+ ret = linux_release_mutex_obj( signal_obj.fd, NULL );
+ break;
+
+ default:
+ assert( 0 );
+ break;
+ }
+
+ if (!ret)
+ {
+ if (queue) select_queue();
+ ret = linux_wait_objs( device, 1, &wait_obj.fd, TRUE, alertable, timeout );
+ if (queue) unselect_queue( !ret );
+ }
+
+ release_inproc_sync_obj( &signal_obj );
+ release_inproc_sync_obj( &wait_obj );
+ return ret;
+}
+
+#else
+
+void close_inproc_sync_obj( HANDLE handle )
+{
+}
+
+static NTSTATUS inproc_release_semaphore( HANDLE handle, ULONG count, ULONG *prev_count )
+{
+ return STATUS_NOT_IMPLEMENTED;
+}
+
+static NTSTATUS inproc_query_semaphore( HANDLE handle, SEMAPHORE_BASIC_INFORMATION *info )
+{
+ return STATUS_NOT_IMPLEMENTED;
+}
+
+static NTSTATUS inproc_set_event( HANDLE handle, LONG *prev_state )
+{
+ return STATUS_NOT_IMPLEMENTED;
+}
+
+static NTSTATUS inproc_reset_event( HANDLE handle, LONG *prev_state )
+{
+ return STATUS_NOT_IMPLEMENTED;
+}
+
+static NTSTATUS inproc_pulse_event( HANDLE handle, LONG *prev_state )
+{
+ return STATUS_NOT_IMPLEMENTED;
+}
+
+static NTSTATUS inproc_query_event( HANDLE handle, EVENT_BASIC_INFORMATION *info )
+{
+ return STATUS_NOT_IMPLEMENTED;
+}
+
+static NTSTATUS inproc_release_mutex( HANDLE handle, LONG *prev_count )
+{
+ return STATUS_NOT_IMPLEMENTED;
+}
+
+static NTSTATUS inproc_query_mutex( HANDLE handle, MUTANT_BASIC_INFORMATION *info )
+{
+ return STATUS_NOT_IMPLEMENTED;
+}
+
+static NTSTATUS inproc_wait( DWORD count, const HANDLE *handles, BOOLEAN wait_any,
+ BOOLEAN alertable, const LARGE_INTEGER *timeout )
+{
+ return STATUS_NOT_IMPLEMENTED;
+}
+
+static NTSTATUS inproc_signal_and_wait( HANDLE signal, HANDLE wait,
+ BOOLEAN alertable, const LARGE_INTEGER *timeout )
+{
+ return STATUS_NOT_IMPLEMENTED;
+}
+
+#endif
+
+
/******************************************************************************
* NtCreateSemaphore (NTDLL.@)
*/
@@ -311,6 +1193,9 @@ NTSTATUS WINAPI NtCreateSemaphore( HANDLE *handle, ACCESS_MASK access, const OBJ
data_size_t len;
struct object_attributes *objattr;
+ TRACE( "access %#x, name %s, initial %d, max %d\n", (int)access,
+ attr ? debugstr_us(attr->ObjectName) : "(null)", (int)initial, (int)max );
+
*handle = 0;
if (max <= 0 || initial < 0 || initial > max) return STATUS_INVALID_PARAMETER;
if ((ret = alloc_object_attributes( attr, &objattr, &len ))) return ret;
@@ -338,6 +1223,8 @@ NTSTATUS WINAPI NtOpenSemaphore( HANDLE *handle, ACCESS_MASK access, const OBJEC
{
unsigned int ret;
+ TRACE( "access %#x, name %s\n", (int)access, attr ? debugstr_us(attr->ObjectName) : "(null)" );
+
*handle = 0;
if ((ret = validate_open_object_attributes( attr ))) return ret;
@@ -375,6 +1262,12 @@ NTSTATUS WINAPI NtQuerySemaphore( HANDLE handle, SEMAPHORE_INFORMATION_CLASS cla
if (len != sizeof(SEMAPHORE_BASIC_INFORMATION)) return STATUS_INFO_LENGTH_MISMATCH;
+ if ((ret = inproc_query_semaphore( handle, out )) != STATUS_NOT_IMPLEMENTED)
+ {
+ if (!ret && ret_len) *ret_len = sizeof(SEMAPHORE_BASIC_INFORMATION);
+ return ret;
+ }
+
SERVER_START_REQ( query_semaphore )
{
req->handle = wine_server_obj_handle( handle );
@@ -397,6 +1290,11 @@ NTSTATUS WINAPI NtReleaseSemaphore( HANDLE handle, ULONG count, ULONG *previous
{
unsigned int ret;
+ TRACE( "handle %p, count %u, prev_count %p\n", handle, (int)count, previous );
+
+ if ((ret = inproc_release_semaphore( handle, count, previous )) != STATUS_NOT_IMPLEMENTED)
+ return ret;
+
SERVER_START_REQ( release_semaphore )
{
req->handle = wine_server_obj_handle( handle );
@@ -421,6 +1319,9 @@ NTSTATUS WINAPI NtCreateEvent( HANDLE *handle, ACCESS_MASK access, const OBJECT_
data_size_t len;
struct object_attributes *objattr;
+ TRACE( "access %#x, name %s, type %u, state %u\n", (int)access,
+ attr ? debugstr_us(attr->ObjectName) : "(null)", type, state );
+
*handle = 0;
if (type != NotificationEvent && type != SynchronizationEvent) return STATUS_INVALID_PARAMETER;
if ((ret = alloc_object_attributes( attr, &objattr, &len ))) return ret;
@@ -448,6 +1349,8 @@ NTSTATUS WINAPI NtOpenEvent( HANDLE *handle, ACCESS_MASK access, const OBJECT_AT
{
unsigned int ret;
+ TRACE( "access %#x, name %s\n", (int)access, attr ? debugstr_us(attr->ObjectName) : "(null)" );
+
*handle = 0;
if ((ret = validate_open_object_attributes( attr ))) return ret;
@@ -473,6 +1376,11 @@ NTSTATUS WINAPI NtSetEvent( HANDLE handle, LONG *prev_state )
{
unsigned int ret;
+ TRACE( "handle %p, prev_state %p\n", handle, prev_state );
+
+ if ((ret = inproc_set_event( handle, prev_state )) != STATUS_NOT_IMPLEMENTED)
+ return ret;
+
SERVER_START_REQ( event_op )
{
req->handle = wine_server_obj_handle( handle );
@@ -492,6 +1400,11 @@ NTSTATUS WINAPI NtResetEvent( HANDLE handle, LONG *prev_state )
{
unsigned int ret;
+ TRACE( "handle %p, prev_state %p\n", handle, prev_state );
+
+ if ((ret = inproc_reset_event( handle, prev_state )) != STATUS_NOT_IMPLEMENTED)
+ return ret;
+
SERVER_START_REQ( event_op )
{
req->handle = wine_server_obj_handle( handle );
@@ -521,6 +1434,11 @@ NTSTATUS WINAPI NtPulseEvent( HANDLE handle, LONG *prev_state )
{
unsigned int ret;
+ TRACE( "handle %p, prev_state %p\n", handle, prev_state );
+
+ if ((ret = inproc_pulse_event( handle, prev_state )) != STATUS_NOT_IMPLEMENTED)
+ return ret;
+
SERVER_START_REQ( event_op )
{
req->handle = wine_server_obj_handle( handle );
@@ -552,6 +1470,12 @@ NTSTATUS WINAPI NtQueryEvent( HANDLE handle, EVENT_INFORMATION_CLASS class,
if (len != sizeof(EVENT_BASIC_INFORMATION)) return STATUS_INFO_LENGTH_MISMATCH;
+ if ((ret = inproc_query_event( handle, out )) != STATUS_NOT_IMPLEMENTED)
+ {
+ if (!ret && ret_len) *ret_len = sizeof(EVENT_BASIC_INFORMATION);
+ return ret;
+ }
+
SERVER_START_REQ( query_event )
{
req->handle = wine_server_obj_handle( handle );
@@ -577,6 +1501,9 @@ NTSTATUS WINAPI NtCreateMutant( HANDLE *handle, ACCESS_MASK access, const OBJECT
data_size_t len;
struct object_attributes *objattr;
+ TRACE( "access %#x, name %s, owned %u\n", (int)access,
+ attr ? debugstr_us(attr->ObjectName) : "(null)", owned );
+
*handle = 0;
if ((ret = alloc_object_attributes( attr, &objattr, &len ))) return ret;
@@ -602,6 +1529,8 @@ NTSTATUS WINAPI NtOpenMutant( HANDLE *handle, ACCESS_MASK access, const OBJECT_A
{
unsigned int ret;
+ TRACE( "access %#x, name %s\n", (int)access, attr ? debugstr_us(attr->ObjectName) : "(null)" );
+
*handle = 0;
if ((ret = validate_open_object_attributes( attr ))) return ret;
@@ -627,6 +1556,11 @@ NTSTATUS WINAPI NtReleaseMutant( HANDLE handle, LONG *prev_count )
{
unsigned int ret;
+ TRACE( "handle %p, prev_count %p\n", handle, prev_count );
+
+ if ((ret = inproc_release_mutex( handle, prev_count )) != STATUS_NOT_IMPLEMENTED)
+ return ret;
+
SERVER_START_REQ( release_mutex )
{
req->handle = wine_server_obj_handle( handle );
@@ -657,6 +1591,12 @@ NTSTATUS WINAPI NtQueryMutant( HANDLE handle, MUTANT_INFORMATION_CLASS class,
if (len != sizeof(MUTANT_BASIC_INFORMATION)) return STATUS_INFO_LENGTH_MISMATCH;
+ if ((ret = inproc_query_mutex( handle, out )) != STATUS_NOT_IMPLEMENTED)
+ {
+ if (!ret && ret_len) *ret_len = sizeof(MUTANT_BASIC_INFORMATION);
+ return ret;
+ }
+
SERVER_START_REQ( query_mutex )
{
req->handle = wine_server_obj_handle( handle );
@@ -1426,6 +2366,9 @@ NTSTATUS WINAPI NtCreateTimer( HANDLE *handle, ACCESS_MASK access, const OBJECT_
data_size_t len;
struct object_attributes *objattr;
+ TRACE( "access %#x, name %s, type %u\n", (int)access,
+ attr ? debugstr_us(attr->ObjectName) : "(null)", type );
+
*handle = 0;
if (type != NotificationTimer && type != SynchronizationTimer) return STATUS_INVALID_PARAMETER;
if ((ret = alloc_object_attributes( attr, &objattr, &len ))) return ret;
@@ -1453,6 +2396,8 @@ NTSTATUS WINAPI NtOpenTimer( HANDLE *handle, ACCESS_MASK access, const OBJECT_AT
{
unsigned int ret;
+ TRACE( "access %#x, name %s\n", (int)access, attr ? debugstr_us(attr->ObjectName) : "(null)" );
+
*handle = 0;
if ((ret = validate_open_object_attributes( attr ))) return ret;
@@ -1506,6 +2451,8 @@ NTSTATUS WINAPI NtCancelTimer( HANDLE handle, BOOLEAN *state )
{
unsigned int ret;
+ TRACE( "handle %p, state %p\n", handle, state );
+
SERVER_START_REQ( cancel_timer )
{
req->handle = wine_server_obj_handle( handle );
@@ -1574,13 +2521,29 @@ NTSTATUS WINAPI NtWaitForMultipleObjects( DWORD count, const HANDLE *handles, BO
{
union select_op select_op;
UINT i, flags = SELECT_INTERRUPTIBLE;
+ unsigned int ret;
if (!count || count > MAXIMUM_WAIT_OBJECTS) return STATUS_INVALID_PARAMETER_1;
+ if (TRACE_ON(sync))
+ {
+ TRACE( "wait_any %u, alertable %u, handles {%p", wait_any, alertable, handles[0] );
+ for (i = 1; i < count; i++) TRACE( ", %p", handles[i] );
+ TRACE( "}, timeout %s\n", debugstr_timeout(timeout) );
+ }
+
+ if ((ret = inproc_wait( count, handles, wait_any, alertable, timeout )) != STATUS_NOT_IMPLEMENTED)
+ {
+ TRACE( "-> %#x\n", ret );
+ return ret;
+ }
+
if (alertable) flags |= SELECT_ALERTABLE;
select_op.wait.op = wait_any ? SELECT_WAIT : SELECT_WAIT_ALL;
for (i = 0; i < count; i++) select_op.wait.handles[i] = wine_server_obj_handle( handles[i] );
- return server_wait( &select_op, offsetof( union select_op, wait.handles[count] ), flags, timeout );
+ ret = server_wait( &select_op, offsetof( union select_op, wait.handles[count] ), flags, timeout );
+ TRACE( "-> %#x\n", ret );
+ return ret;
}
@@ -1601,9 +2564,15 @@ NTSTATUS WINAPI NtSignalAndWaitForSingleObject( HANDLE signal, HANDLE wait,
{
union select_op select_op;
UINT flags = SELECT_INTERRUPTIBLE;
+ NTSTATUS ret;
+
+ TRACE( "signal %p, wait %p, alertable %u, timeout %s\n", signal, wait, alertable, debugstr_timeout(timeout) );
if (!signal) return STATUS_INVALID_HANDLE;
+ if ((ret = inproc_signal_and_wait( signal, wait, alertable, timeout )) != STATUS_NOT_IMPLEMENTED)
+ return ret;
+
if (alertable) flags |= SELECT_ALERTABLE;
select_op.signal_and_wait.op = SELECT_SIGNAL_AND_WAIT;
select_op.signal_and_wait.wait = wine_server_obj_handle( wait );
@@ -1846,6 +2815,9 @@ NTSTATUS WINAPI NtCreateKeyedEvent( HANDLE *handle, ACCESS_MASK access,
data_size_t len;
struct object_attributes *objattr;
+ TRACE( "access %#x, name %s, flags %#x\n", (int)access,
+ attr ? debugstr_us(attr->ObjectName) : "(null)", (int)flags );
+
*handle = 0;
if ((ret = alloc_object_attributes( attr, &objattr, &len ))) return ret;
@@ -1870,6 +2842,8 @@ NTSTATUS WINAPI NtOpenKeyedEvent( HANDLE *handle, ACCESS_MASK access, const OBJE
{
unsigned int ret;
+ TRACE( "access %#x, name %s\n", (int)access, attr ? debugstr_us(attr->ObjectName) : "(null)" );
+
*handle = 0;
if ((ret = validate_open_object_attributes( attr ))) return ret;
@@ -1896,6 +2870,8 @@ NTSTATUS WINAPI NtWaitForKeyedEvent( HANDLE handle, const void *key,
union select_op select_op;
UINT flags = SELECT_INTERRUPTIBLE;
+ TRACE( "handle %p, key %p, alertable %u, timeout %s\n", handle, key, alertable, debugstr_timeout(timeout) );
+
if (!handle) handle = keyed_event;
if ((ULONG_PTR)key & 1) return STATUS_INVALID_PARAMETER_1;
if (alertable) flags |= SELECT_ALERTABLE;
@@ -1915,6 +2891,8 @@ NTSTATUS WINAPI NtReleaseKeyedEvent( HANDLE handle, const void *key,
union select_op select_op;
UINT flags = SELECT_INTERRUPTIBLE;
+ TRACE( "handle %p, key %p, alertable %u, timeout %s\n", handle, key, alertable, debugstr_timeout(timeout) );
+
if (!handle) handle = keyed_event;
if ((ULONG_PTR)key & 1) return STATUS_INVALID_PARAMETER_1;
if (alertable) flags |= SELECT_ALERTABLE;
@@ -2059,7 +3037,7 @@ NTSTATUS WINAPI NtRemoveIoCompletion( HANDLE handle, ULONG_PTR *key, ULONG_PTR *
}
SERVER_END_REQ;
if (status != STATUS_PENDING) return status;
- if (!timeout || timeout->QuadPart) status = NtWaitForSingleObject( wait_handle, FALSE, timeout );
+ if (!timeout || timeout->QuadPart) status = server_wait_for_object( wait_handle, FALSE, timeout );
else status = STATUS_TIMEOUT;
if (status != WAIT_OBJECT_0) return status;
@@ -2123,7 +3101,7 @@ NTSTATUS WINAPI NtRemoveIoCompletionEx( HANDLE handle, FILE_IO_COMPLETION_INFORM
assert( status == STATUS_USER_APC );
goto done;
}
- if (!timeout || timeout->QuadPart) status = NtWaitForSingleObject( wait_handle, alertable, timeout );
+ if (!timeout || timeout->QuadPart) status = server_wait_for_object( wait_handle, alertable, timeout );
else status = STATUS_TIMEOUT;
if (status != WAIT_OBJECT_0) goto done;
diff --git a/dlls/ntdll/unix/thread.c b/dlls/ntdll/unix/thread.c
index b64a7dd40afbaa64e511fcd7125c25d0dad41780..a66ae7dbe89f7d6cc16492baeff7a676ad675126 100644
--- a/dlls/ntdll/unix/thread.c
+++ b/dlls/ntdll/unix/thread.c
@@ -1801,7 +1801,7 @@ NTSTATUS get_thread_context( HANDLE handle, void *context, BOOL *self, USHORT ma
if (ret == STATUS_PENDING)
{
- NtWaitForSingleObject( context_handle, FALSE, NULL );
+ server_wait_for_object( context_handle, FALSE, NULL );
SERVER_START_REQ( get_thread_context )
{
diff --git a/dlls/ntdll/unix/unix_private.h b/dlls/ntdll/unix/unix_private.h
index b66fa9df6660a9a7e2ef46c2298e0af700f1fe6c..8a3cfc841158894869bc3007b3428f5148e76e49 100644
--- a/dlls/ntdll/unix/unix_private.h
+++ b/dlls/ntdll/unix/unix_private.h
@@ -112,6 +112,7 @@ struct ntdll_thread_data
PRTL_THREAD_START_ROUTINE start; /* thread entry point */
void *param; /* thread entry point parameter */
void *jmp_buf; /* setjmp buffer for exception handling */
+ int linux_alert_obj; /* fd for the linux in-process alert event */
};
C_ASSERT( sizeof(struct ntdll_thread_data) <= sizeof(((TEB *)0)->GdiTebBatch) );
@@ -206,6 +207,8 @@ extern NTSTATUS load_start_exe( WCHAR **image, void **module );
extern ULONG_PTR redirect_arm64ec_rva( void *module, ULONG_PTR rva, const IMAGE_ARM64EC_METADATA *metadata );
extern void start_server( BOOL debug );
+extern pthread_mutex_t fd_cache_mutex;
+
extern unsigned int server_call_unlocked( void *req_ptr );
extern void server_enter_uninterrupted_section( pthread_mutex_t *mutex, sigset_t *sigset );
extern void server_leave_uninterrupted_section( pthread_mutex_t *mutex, sigset_t *sigset );
@@ -213,10 +216,12 @@ extern unsigned int server_select( const union select_op *select_op, data_size_t
timeout_t abs_timeout, struct context_data *context, struct user_apc *user_apc );
extern unsigned int server_wait( const union select_op *select_op, data_size_t size, UINT flags,
const LARGE_INTEGER *timeout );
+extern unsigned int server_wait_for_object( HANDLE handle, BOOL alertable, const LARGE_INTEGER *timeout );
extern unsigned int server_queue_process_apc( HANDLE process, const union apc_call *call,
union apc_result *result );
extern int server_get_unix_fd( HANDLE handle, unsigned int wanted_access, int *unix_fd,
int *needs_close, enum server_fd_type *type, unsigned int *options );
+extern int remove_fd_from_cache( HANDLE handle );
extern void wine_server_send_fd( int fd );
extern void process_exit_wrapper( int status ) DECLSPEC_NORETURN;
extern size_t server_init_process(void);
@@ -379,6 +384,8 @@ extern NTSTATUS wow64_wine_spawnvp( void *args );
extern void dbg_init(void);
+extern void close_inproc_sync_obj( HANDLE handle );
+
extern NTSTATUS call_user_apc_dispatcher( CONTEXT *context_ptr, ULONG_PTR arg1, ULONG_PTR arg2, ULONG_PTR arg3,
PNTAPCFUNC func, NTSTATUS status );
extern NTSTATUS call_user_exception_dispatcher( EXCEPTION_RECORD *rec, CONTEXT *context );
@@ -387,6 +394,7 @@ extern void call_raise_user_exception_dispatcher(void);
#define IMAGE_DLLCHARACTERISTICS_PREFER_NATIVE 0x0010 /* Wine extension */
#define TICKSPERSEC 10000000
+#define NSECPERSEC 1000000000
#define SECS_1601_TO_1970 ((369 * 365 + 89) * (ULONGLONG)86400)
static inline ULONGLONG ticks_from_time_t( time_t time )
@@ -452,7 +460,7 @@ static inline struct async_data server_async( HANDLE handle, struct async_fileio
static inline NTSTATUS wait_async( HANDLE handle, BOOL alertable )
{
- return NtWaitForSingleObject( handle, alertable, NULL );
+ return server_wait_for_object( handle, alertable, NULL );
}
static inline BOOL in_wow64_call(void)
diff --git a/dlls/ntdll/unix/virtual.c b/dlls/ntdll/unix/virtual.c
index 40cea6efe5302ffd0f5dbb9860282dab95655e24..38dec4fc6a8b634b49bbf64319e3d46b5635547b 100644
--- a/dlls/ntdll/unix/virtual.c
+++ b/dlls/ntdll/unix/virtual.c
@@ -3695,6 +3695,7 @@ static TEB *init_teb( void *ptr, BOOL is_wow )
thread_data->reply_fd = -1;
thread_data->wait_fd[0] = -1;
thread_data->wait_fd[1] = -1;
+ thread_data->linux_alert_obj = -1;
list_add_head( &teb_list, &thread_data->entry );
return teb;
}
diff --git a/include/config.h.in b/include/config.h.in
index 6033d8674f3c292ed55be563801d9fe5c9a7f565..7fb12090dfea3eaccc9591a02b6fd37c46fb5be8 100644
--- a/include/config.h.in
+++ b/include/config.h.in
@@ -189,6 +189,9 @@
/* Define to 1 if you have the <linux/major.h> header file. */
#undef HAVE_LINUX_MAJOR_H
+/* Define to 1 if you have the <linux/ntsync.h> header file. */
+#undef HAVE_LINUX_NTSYNC_H
+
/* Define to 1 if you have the <linux/param.h> header file. */
#undef HAVE_LINUX_PARAM_H
diff --git a/include/wine/server_protocol.h b/include/wine/server_protocol.h
index a39ab12397076549e2340acfcb6a4e8ac27752a8..2dc7c25c58b612217f65594e650ead70758863d0 100644
--- a/include/wine/server_protocol.h
+++ b/include/wine/server_protocol.h
@@ -5882,6 +5882,86 @@ struct set_keyboard_repeat_reply
char __pad_12[4];
};
+enum inproc_sync_type
+{
+ INPROC_SYNC_UNKNOWN,
+ INPROC_SYNC_AUTO_EVENT,
+ INPROC_SYNC_MANUAL_EVENT,
+ INPROC_SYNC_SEMAPHORE,
+ INPROC_SYNC_MUTEX,
+ INPROC_SYNC_AUTO_SERVER,
+ INPROC_SYNC_MANUAL_SERVER,
+ INPROC_SYNC_QUEUE,
+};
+
+
+
+struct get_linux_sync_device_request
+{
+ struct request_header __header;
+ char __pad_12[4];
+};
+struct get_linux_sync_device_reply
+{
+ struct reply_header __header;
+ obj_handle_t handle;
+ char __pad_12[4];
+};
+
+
+
+struct get_linux_sync_obj_request
+{
+ struct request_header __header;
+ obj_handle_t handle;
+};
+struct get_linux_sync_obj_reply
+{
+ struct reply_header __header;
+ obj_handle_t handle;
+ int type;
+ unsigned int access;
+ char __pad_20[4];
+};
+
+
+
+struct select_inproc_queue_request
+{
+ struct request_header __header;
+ char __pad_12[4];
+};
+struct select_inproc_queue_reply
+{
+ struct reply_header __header;
+};
+
+
+
+struct unselect_inproc_queue_request
+{
+ struct request_header __header;
+ int signaled;
+};
+struct unselect_inproc_queue_reply
+{
+ struct reply_header __header;
+};
+
+
+
+struct get_inproc_alert_event_request
+{
+ struct request_header __header;
+ char __pad_12[4];
+};
+struct get_inproc_alert_event_reply
+{
+ struct reply_header __header;
+ obj_handle_t handle;
+ char __pad_12[4];
+};
+
enum request
{
@@ -6177,6 +6257,11 @@ enum request
REQ_resume_process,
REQ_get_next_thread,
REQ_set_keyboard_repeat,
+ REQ_get_linux_sync_device,
+ REQ_get_linux_sync_obj,
+ REQ_select_inproc_queue,
+ REQ_unselect_inproc_queue,
+ REQ_get_inproc_alert_event,
REQ_NB_REQUESTS
};
@@ -6476,6 +6561,11 @@ union generic_request
struct resume_process_request resume_process_request;
struct get_next_thread_request get_next_thread_request;
struct set_keyboard_repeat_request set_keyboard_repeat_request;
+ struct get_linux_sync_device_request get_linux_sync_device_request;
+ struct get_linux_sync_obj_request get_linux_sync_obj_request;
+ struct select_inproc_queue_request select_inproc_queue_request;
+ struct unselect_inproc_queue_request unselect_inproc_queue_request;
+ struct get_inproc_alert_event_request get_inproc_alert_event_request;
};
union generic_reply
{
@@ -6773,8 +6863,13 @@ union generic_reply
struct resume_process_reply resume_process_reply;
struct get_next_thread_reply get_next_thread_reply;
struct set_keyboard_repeat_reply set_keyboard_repeat_reply;
+ struct get_linux_sync_device_reply get_linux_sync_device_reply;
+ struct get_linux_sync_obj_reply get_linux_sync_obj_reply;
+ struct select_inproc_queue_reply select_inproc_queue_reply;
+ struct unselect_inproc_queue_reply unselect_inproc_queue_reply;
+ struct get_inproc_alert_event_reply get_inproc_alert_event_reply;
};
-#define SERVER_PROTOCOL_VERSION 855
+#define SERVER_PROTOCOL_VERSION 866
#endif /* __WINE_WINE_SERVER_PROTOCOL_H */
diff --git a/server/Makefile.in b/server/Makefile.in
index 7e571ac2ba64dafdbd94b0d939e27484a364fcf4..91ec73dabc568cc4573e4b6ffb450116095fc11f 100644
--- a/server/Makefile.in
+++ b/server/Makefile.in
@@ -16,6 +16,7 @@ SOURCES = \
file.c \
handle.c \
hook.c \
+ inproc_sync.c \
mach.c \
mailslot.c \
main.c \
diff --git a/server/async.c b/server/async.c
index d2d929c9709f1c99aaced92af5775f692932fe16..e8b50a72f7e31db6177c7c31a193ade4e8649ddd 100644
--- a/server/async.c
+++ b/server/async.c
@@ -90,6 +90,7 @@ static const struct object_ops async_ops =
NULL, /* unlink_name */
no_open_file, /* open_file */
no_kernel_obj_list, /* get_kernel_obj_list */
+ no_get_inproc_sync, /* get_inproc_sync */
no_close_handle, /* close_handle */
async_destroy /* destroy */
};
@@ -710,6 +711,7 @@ static const struct object_ops iosb_ops =
NULL, /* unlink_name */
no_open_file, /* open_file */
no_kernel_obj_list, /* get_kernel_obj_list */
+ no_get_inproc_sync, /* get_inproc_sync */
no_close_handle, /* close_handle */
iosb_destroy /* destroy */
};
diff --git a/server/atom.c b/server/atom.c
index ff0799f5880d83e4a05655c30e69755d554469af..04951e628baa9fc0f2e80db477764f1cd96c1ab7 100644
--- a/server/atom.c
+++ b/server/atom.c
@@ -91,6 +91,7 @@ static const struct object_ops atom_table_ops =
NULL, /* unlink_name */
no_open_file, /* open_file */
no_kernel_obj_list, /* get_kernel_obj_list */
+ no_get_inproc_sync, /* get_inproc_sync */
no_close_handle, /* close_handle */
atom_table_destroy /* destroy */
};
diff --git a/server/change.c b/server/change.c
index f42ce06634073747898afc8f8a13101b8f3d10f1..92d321c8784d8eb902bcf7fc98a29e2ef23fff0c 100644
--- a/server/change.c
+++ b/server/change.c
@@ -124,6 +124,7 @@ static const struct object_ops dir_ops =
NULL, /* unlink_name */
no_open_file, /* open_file */
no_kernel_obj_list, /* get_kernel_obj_list */
+ default_fd_get_inproc_sync, /* get_inproc_sync */
dir_close_handle, /* close_handle */
dir_destroy /* destroy */
};
diff --git a/server/clipboard.c b/server/clipboard.c
index 91f159bc7c9b96add98deaa1068415147003587e..a2258ae869d1f5d10f9f0cf34581019ae87bd969 100644
--- a/server/clipboard.c
+++ b/server/clipboard.c
@@ -88,6 +88,7 @@ static const struct object_ops clipboard_ops =
NULL, /* unlink_name */
no_open_file, /* open_file */
no_kernel_obj_list, /* get_kernel_obj_list */
+ no_get_inproc_sync, /* get_inproc_sync */
no_close_handle, /* close_handle */
clipboard_destroy /* destroy */
};
diff --git a/server/completion.c b/server/completion.c
index 99680ae068063c35e7aba12d1019e1cd49aff023..529ed9d79562b3fae8a93bb517e2c6cb10bc5cbf 100644
--- a/server/completion.c
+++ b/server/completion.c
@@ -77,6 +77,7 @@ struct completion
struct list wait_queue;
unsigned int depth;
int closed;
+ struct inproc_sync *inproc_sync;
};
static void completion_wait_dump( struct object*, int );
@@ -104,6 +105,7 @@ static const struct object_ops completion_wait_ops =
NULL, /* unlink_name */
no_open_file, /* open_file */
no_kernel_obj_list, /* get_kernel_obj_list */
+ no_get_inproc_sync, /* get_inproc_sync */
no_close_handle, /* close_handle */
completion_wait_destroy /* destroy */
};
@@ -156,6 +158,7 @@ static void completion_wait_satisfied( struct object *obj, struct wait_queue_ent
static void completion_dump( struct object*, int );
static int completion_signaled( struct object *obj, struct wait_queue_entry *entry );
static int completion_close_handle( struct object *obj, struct process *process, obj_handle_t handle );
+static struct inproc_sync *completion_get_inproc_sync( struct object *obj );
static void completion_destroy( struct object * );
static const struct object_ops completion_ops =
@@ -178,6 +181,7 @@ static const struct object_ops completion_ops =
default_unlink_name, /* unlink_name */
no_open_file, /* open_file */
no_kernel_obj_list, /* get_kernel_obj_list */
+ completion_get_inproc_sync,/* get_inproc_sync */
completion_close_handle, /* close_handle */
completion_destroy /* destroy */
};
@@ -191,6 +195,7 @@ static void completion_destroy( struct object *obj)
{
free( tmp );
}
+ if (completion->inproc_sync) release_object( completion->inproc_sync );
}
static void completion_dump( struct object *obj, int verbose )
@@ -228,6 +233,7 @@ static int completion_close_handle( struct object *obj, struct process *process,
}
completion->closed = 1;
wake_up( obj, 0 );
+ set_inproc_event( completion->inproc_sync );
return 1;
}
@@ -261,6 +267,16 @@ static struct completion_wait *create_completion_wait( struct thread *thread )
return wait;
}
+static struct inproc_sync *completion_get_inproc_sync( struct object *obj )
+{
+ struct completion *completion = (struct completion *)obj;
+
+ if (!completion->inproc_sync)
+ completion->inproc_sync = create_inproc_event( INPROC_SYNC_MANUAL_SERVER, !list_empty( &completion->queue ) );
+ if (completion->inproc_sync) grab_object( completion->inproc_sync );
+ return completion->inproc_sync;
+}
+
static struct completion *create_completion( struct object *root, const struct unicode_str *name,
unsigned int attr, unsigned int concurrent,
const struct security_descriptor *sd )
@@ -275,6 +291,7 @@ static struct completion *create_completion( struct object *root, const struct u
list_init( &completion->wait_queue );
completion->depth = 0;
completion->closed = 0;
+ completion->inproc_sync = NULL;
}
}
@@ -307,7 +324,11 @@ void add_completion( struct completion *completion, apc_param_t ckey, apc_param_
wake_up( &wait->obj, 1 );
if (list_empty( &completion->queue )) return;
}
- if (!list_empty( &completion->queue )) wake_up( &completion->obj, 0 );
+ if (!list_empty( &completion->queue ))
+ {
+ wake_up( &completion->obj, 0 );
+ set_inproc_event( completion->inproc_sync );
+ }
}
/* create a completion */
@@ -408,6 +429,8 @@ DECL_HANDLER(remove_completion)
reply->information = msg->information;
free( msg );
reply->wait_handle = 0;
+ if (list_empty( &completion->queue ))
+ reset_inproc_event( completion->inproc_sync );
}
release_object( completion );
diff --git a/server/console.c b/server/console.c
index c3e7bf57e924c034b58372d7a2ed09725f3915a3..52448985632efd6607de55735be6df884b4097b9 100644
--- a/server/console.c
+++ b/server/console.c
@@ -61,6 +61,7 @@ struct console
struct fd *fd; /* for bare console, attached input fd */
struct async_queue ioctl_q; /* ioctl queue */
struct async_queue read_q; /* read queue */
+ struct inproc_sync *inproc_sync; /* in-process synchronization object */
};
static void console_dump( struct object *obj, int verbose );
@@ -72,6 +73,7 @@ static struct object *console_lookup_name( struct object *obj, struct unicode_st
static struct object *console_open_file( struct object *obj, unsigned int access,
unsigned int sharing, unsigned int options );
static int console_add_queue( struct object *obj, struct wait_queue_entry *entry );
+static struct inproc_sync *console_get_inproc_sync( struct object *obj );
static const struct object_ops console_ops =
{
@@ -93,6 +95,7 @@ static const struct object_ops console_ops =
NULL, /* unlink_name */
console_open_file, /* open_file */
no_kernel_obj_list, /* get_kernel_obj_list */
+ console_get_inproc_sync, /* get_inproc_sync */
no_close_handle, /* close_handle */
console_destroy /* destroy */
};
@@ -139,6 +142,7 @@ struct console_server
unsigned int once_input : 1; /* flag if input thread has already been requested */
int term_fd; /* UNIX terminal fd */
struct termios termios; /* original termios */
+ struct inproc_sync *inproc_sync; /* in-process synchronization object */
};
static void console_server_dump( struct object *obj, int verbose );
@@ -149,6 +153,7 @@ static struct object *console_server_lookup_name( struct object *obj, struct uni
unsigned int attr, struct object *root );
static struct object *console_server_open_file( struct object *obj, unsigned int access,
unsigned int sharing, unsigned int options );
+static struct inproc_sync *console_server_get_inproc_sync( struct object *obj );
static const struct object_ops console_server_ops =
{
@@ -170,6 +175,7 @@ static const struct object_ops console_server_ops =
NULL, /* unlink_name */
console_server_open_file, /* open_file */
no_kernel_obj_list, /* get_kernel_obj_list */
+ console_server_get_inproc_sync, /* get_inproc_sync */
no_close_handle, /* close_handle */
console_server_destroy /* destroy */
};
@@ -218,6 +224,7 @@ static int screen_buffer_add_queue( struct object *obj, struct wait_queue_entry
static struct fd *screen_buffer_get_fd( struct object *obj );
static struct object *screen_buffer_open_file( struct object *obj, unsigned int access,
unsigned int sharing, unsigned int options );
+static struct inproc_sync *screen_buffer_get_inproc_sync( struct object *obj );
static const struct object_ops screen_buffer_ops =
{
@@ -239,6 +246,7 @@ static const struct object_ops screen_buffer_ops =
NULL, /* unlink_name */
screen_buffer_open_file, /* open_file */
no_kernel_obj_list, /* get_kernel_obj_list */
+ screen_buffer_get_inproc_sync, /* get_inproc_sync */
no_close_handle, /* close_handle */
screen_buffer_destroy /* destroy */
};
@@ -288,6 +296,7 @@ static const struct object_ops console_device_ops =
default_unlink_name, /* unlink_name */
console_device_open_file, /* open_file */
no_kernel_obj_list, /* get_kernel_obj_list */
+ no_get_inproc_sync, /* get_inproc_sync */
no_close_handle, /* close_handle */
no_destroy /* destroy */
};
@@ -303,6 +312,7 @@ static struct object *console_input_open_file( struct object *obj, unsigned int
unsigned int sharing, unsigned int options );
static int console_input_add_queue( struct object *obj, struct wait_queue_entry *entry );
static struct fd *console_input_get_fd( struct object *obj );
+static struct inproc_sync *console_input_get_inproc_sync( struct object *obj );
static void console_input_destroy( struct object *obj );
static const struct object_ops console_input_ops =
@@ -325,6 +335,7 @@ static const struct object_ops console_input_ops =
default_unlink_name, /* unlink_name */
console_input_open_file, /* open_file */
no_kernel_obj_list, /* get_kernel_obj_list */
+ console_input_get_inproc_sync, /* get_inproc_sync */
no_close_handle, /* close_handle */
console_input_destroy /* destroy */
};
@@ -360,6 +371,7 @@ static int console_output_add_queue( struct object *obj, struct wait_queue_entry
static struct fd *console_output_get_fd( struct object *obj );
static struct object *console_output_open_file( struct object *obj, unsigned int access,
unsigned int sharing, unsigned int options );
+static struct inproc_sync *console_output_get_inproc_sync( struct object *obj );
static void console_output_destroy( struct object *obj );
static const struct object_ops console_output_ops =
@@ -382,6 +394,7 @@ static const struct object_ops console_output_ops =
default_unlink_name, /* unlink_name */
console_output_open_file, /* open_file */
no_kernel_obj_list, /* get_kernel_obj_list */
+ console_output_get_inproc_sync, /* get_inproc_sync */
no_close_handle, /* close_handle */
console_output_destroy /* destroy */
};
@@ -440,6 +453,7 @@ static const struct object_ops console_connection_ops =
default_unlink_name, /* unlink_name */
console_connection_open_file, /* open_file */
no_kernel_obj_list, /* get_kernel_obj_list */
+ no_get_inproc_sync, /* get_inproc_sync */
console_connection_close_handle, /* close_handle */
console_connection_destroy /* destroy */
};
@@ -539,6 +553,7 @@ static struct object *create_console(void)
console->server = NULL;
console->fd = NULL;
console->last_id = 0;
+ console->inproc_sync = NULL;
init_async_queue( &console->ioctl_q );
init_async_queue( &console->read_q );
@@ -578,6 +593,7 @@ static int queue_host_ioctl( struct console_server *server, unsigned int code, u
}
list_add_tail( &server->queue, &ioctl->entry );
wake_up( &server->obj, 0 );
+ set_inproc_event( server->inproc_sync );
if (async) set_error( STATUS_PENDING );
return 1;
}
@@ -610,6 +626,7 @@ static void disconnect_console_server( struct console_server *server )
server->console->server = NULL;
server->console = NULL;
wake_up( &server->obj, 0 );
+ set_inproc_event( server->inproc_sync );
}
}
@@ -764,6 +781,8 @@ static void console_destroy( struct object *obj )
free_async_queue( &console->read_q );
if (console->fd)
release_object( console->fd );
+
+ if (console->inproc_sync) release_object( console->inproc_sync );
}
static struct object *create_console_connection( struct console *console )
@@ -817,6 +836,16 @@ static struct object *console_open_file( struct object *obj, unsigned int access
return grab_object( obj );
}
+static struct inproc_sync *console_get_inproc_sync( struct object *obj )
+{
+ struct console *console = (struct console *)obj;
+
+ if (!console->inproc_sync)
+ console->inproc_sync = create_inproc_event( INPROC_SYNC_MANUAL_SERVER, console->signaled );
+ if (console->inproc_sync) grab_object( console->inproc_sync );
+ return console->inproc_sync;
+}
+
static void screen_buffer_dump( struct object *obj, int verbose )
{
struct screen_buffer *screen_buffer = (struct screen_buffer *)obj;
@@ -866,6 +895,17 @@ static struct fd *screen_buffer_get_fd( struct object *obj )
return NULL;
}
+static struct inproc_sync *screen_buffer_get_inproc_sync( struct object *obj )
+{
+ struct screen_buffer *screen_buffer = (struct screen_buffer *)obj;
+ if (!screen_buffer->input)
+ {
+ set_error( STATUS_ACCESS_DENIED );
+ return NULL;
+ }
+ return console_get_inproc_sync( &screen_buffer->input->obj );
+}
+
static void console_server_dump( struct object *obj, int verbose )
{
assert( obj->ops == &console_server_ops );
@@ -878,6 +918,7 @@ static void console_server_destroy( struct object *obj )
assert( obj->ops == &console_server_ops );
disconnect_console_server( server );
if (server->fd) release_object( server->fd );
+ if (server->inproc_sync) release_object( server->inproc_sync );
}
static struct object *console_server_lookup_name( struct object *obj, struct unicode_str *name,
@@ -938,6 +979,17 @@ static struct object *console_server_open_file( struct object *obj, unsigned int
return grab_object( obj );
}
+static struct inproc_sync *console_server_get_inproc_sync( struct object *obj )
+{
+ struct console_server *server = (struct console_server *)obj;
+ int signaled = !server->console || !list_empty( &server->queue );
+
+ if (!server->inproc_sync)
+ server->inproc_sync = create_inproc_event( INPROC_SYNC_MANUAL_SERVER, signaled );
+ if (server->inproc_sync) grab_object( server->inproc_sync );
+ return server->inproc_sync;
+}
+
static struct object *create_console_server( void )
{
struct console_server *server;
@@ -949,6 +1001,7 @@ static struct object *create_console_server( void )
server->term_fd = -1;
list_init( &server->queue );
list_init( &server->read_queue );
+ server->inproc_sync = NULL;
server->fd = alloc_pseudo_fd( &console_server_fd_ops, &server->obj, FILE_SYNCHRONOUS_IO_NONALERT );
if (!server->fd)
{
@@ -1414,6 +1467,16 @@ static struct object *console_input_open_file( struct object *obj, unsigned int
return grab_object( obj );
}
+static struct inproc_sync *console_input_get_inproc_sync( struct object *obj )
+{
+ if (!current->process->console)
+ {
+ set_error( STATUS_ACCESS_DENIED );
+ return NULL;
+ }
+ return console_get_inproc_sync( ¤t->process->console->obj );
+}
+
static void console_input_destroy( struct object *obj )
{
struct console_input *console_input = (struct console_input *)obj;
@@ -1486,6 +1549,16 @@ static struct object *console_output_open_file( struct object *obj, unsigned int
return grab_object( obj );
}
+static struct inproc_sync *console_output_get_inproc_sync( struct object *obj )
+{
+ if (!current->process->console || !current->process->console->active)
+ {
+ set_error( STATUS_ACCESS_DENIED );
+ return NULL;
+ }
+ return console_get_inproc_sync( ¤t->process->console->obj );
+}
+
static void console_output_destroy( struct object *obj )
{
struct console_output *console_output = (struct console_output *)obj;
@@ -1543,11 +1616,16 @@ DECL_HANDLER(get_next_console_request)
if (!server->console->renderer) server->console->renderer = current;
- if (!req->signal) server->console->signaled = 0;
+ if (!req->signal)
+ {
+ server->console->signaled = 0;
+ reset_inproc_event( server->console->inproc_sync );
+ }
else if (!server->console->signaled)
{
server->console->signaled = 1;
wake_up( &server->console->obj, 0 );
+ set_inproc_event( server->console->inproc_sync );
}
if (req->read)
@@ -1569,6 +1647,8 @@ DECL_HANDLER(get_next_console_request)
/* set result of previous ioctl */
ioctl = LIST_ENTRY( list_head( &server->queue ), struct console_host_ioctl, entry );
list_remove( &ioctl->entry );
+ if (list_empty( &server->queue ))
+ reset_inproc_event( server->inproc_sync );
}
if (ioctl)
@@ -1655,5 +1735,8 @@ DECL_HANDLER(get_next_console_request)
set_error( STATUS_PENDING );
}
+ if (list_empty( &server->queue ))
+ reset_inproc_event( server->inproc_sync );
+
release_object( server );
}
diff --git a/server/debugger.c b/server/debugger.c
index 39a740e07e535ebfc85def7ae190d26b8914bced..35449172eaa857d5bb2ebb28cc44e9f6d3901723 100644
--- a/server/debugger.c
+++ b/server/debugger.c
@@ -71,6 +71,7 @@ struct debug_obj
struct object obj; /* object header */
struct list event_queue; /* pending events queue */
unsigned int flags; /* debug flags */
+ struct inproc_sync *inproc_sync; /* in-process synchronization object */
};
@@ -98,12 +99,14 @@ static const struct object_ops debug_event_ops =
NULL, /* unlink_name */
no_open_file, /* open_file */
no_kernel_obj_list, /* get_kernel_obj_list */
+ no_get_inproc_sync, /* get_inproc_sync */
no_close_handle, /* close_handle */
debug_event_destroy /* destroy */
};
static void debug_obj_dump( struct object *obj, int verbose );
static int debug_obj_signaled( struct object *obj, struct wait_queue_entry *entry );
+static struct inproc_sync *debug_obj_get_inproc_sync( struct object *obj );
static void debug_obj_destroy( struct object *obj );
static const struct object_ops debug_obj_ops =
@@ -126,6 +129,7 @@ static const struct object_ops debug_obj_ops =
default_unlink_name, /* unlink_name */
no_open_file, /* open_file */
no_kernel_obj_list, /* get_kernel_obj_list */
+ debug_obj_get_inproc_sync, /* get_inproc_sync */
no_close_handle, /* close_handle */
debug_obj_destroy /* destroy */
};
@@ -253,6 +257,7 @@ static void link_event( struct debug_obj *debug_obj, struct debug_event *event )
/* grab reference since debugger could be killed while trying to wake up */
grab_object( debug_obj );
wake_up( &debug_obj->obj, 0 );
+ set_inproc_event( debug_obj->inproc_sync );
release_object( debug_obj );
}
}
@@ -265,6 +270,7 @@ static void resume_event( struct debug_obj *debug_obj, struct debug_event *event
{
grab_object( debug_obj );
wake_up( &debug_obj->obj, 0 );
+ set_inproc_event( debug_obj->inproc_sync );
release_object( debug_obj );
}
}
@@ -330,6 +336,17 @@ static int debug_obj_signaled( struct object *obj, struct wait_queue_entry *entr
return find_event_to_send( debug_obj ) != NULL;
}
+static struct inproc_sync *debug_obj_get_inproc_sync( struct object *obj )
+{
+ struct debug_obj *debug_obj = (struct debug_obj *)obj;
+ int signaled = find_event_to_send( debug_obj ) != NULL;
+
+ if (!debug_obj->inproc_sync)
+ debug_obj->inproc_sync = create_inproc_event( INPROC_SYNC_MANUAL_SERVER, signaled );
+ if (debug_obj->inproc_sync) grab_object( debug_obj->inproc_sync );
+ return debug_obj->inproc_sync;
+}
+
static void debug_obj_destroy( struct object *obj )
{
struct list *ptr;
@@ -342,6 +359,8 @@ static void debug_obj_destroy( struct object *obj )
/* free all pending events */
while ((ptr = list_head( &debug_obj->event_queue )))
unlink_event( debug_obj, LIST_ENTRY( ptr, struct debug_event, entry ));
+
+ if (debug_obj->inproc_sync) release_object( debug_obj->inproc_sync );
}
struct debug_obj *get_debug_obj( struct process *process, obj_handle_t handle, unsigned int access )
@@ -361,6 +380,7 @@ static struct debug_obj *create_debug_obj( struct object *root, const struct uni
{
debug_obj->flags = flags;
list_init( &debug_obj->event_queue );
+ debug_obj->inproc_sync = NULL;
}
}
return debug_obj;
@@ -569,6 +589,9 @@ DECL_HANDLER(wait_debug_event)
reply->tid = get_thread_id( event->sender );
alloc_event_handles( event, current->process );
set_reply_data( &event->data, min( get_reply_max_size(), sizeof(event->data) ));
+
+ if (!find_event_to_send( debug_obj ))
+ reset_inproc_event( debug_obj->inproc_sync );
}
else
{
diff --git a/server/device.c b/server/device.c
index 1f93cca437da9129c9d937280e5fe69a422804db..ef3ab50d1e654958d912d24548aaf52811d564b2 100644
--- a/server/device.c
+++ b/server/device.c
@@ -78,6 +78,7 @@ static const struct object_ops irp_call_ops =
NULL, /* unlink_name */
no_open_file, /* open_file */
no_kernel_obj_list, /* get_kernel_obj_list */
+ no_get_inproc_sync, /* get_inproc_sync */
no_close_handle, /* close_handle */
irp_call_destroy /* destroy */
};
@@ -92,10 +93,12 @@ struct device_manager
struct list requests; /* list of pending irps across all devices */
struct irp_call *current_call; /* call currently executed on client side */
struct wine_rb_tree kernel_objects; /* map of objects that have client side pointer associated */
+ struct inproc_sync *inproc_sync; /* in-process synchronization object */
};
static void device_manager_dump( struct object *obj, int verbose );
static int device_manager_signaled( struct object *obj, struct wait_queue_entry *entry );
+static struct inproc_sync *device_manager_get_inproc_sync( struct object *obj );
static void device_manager_destroy( struct object *obj );
static const struct object_ops device_manager_ops =
@@ -118,6 +121,7 @@ static const struct object_ops device_manager_ops =
NULL, /* unlink_name */
no_open_file, /* open_file */
no_kernel_obj_list, /* get_kernel_obj_list */
+ device_manager_get_inproc_sync, /* get_inproc_sync */
no_close_handle, /* close_handle */
device_manager_destroy /* destroy */
};
@@ -175,6 +179,7 @@ static const struct object_ops device_ops =
default_unlink_name, /* unlink_name */
device_open_file, /* open_file */
device_get_kernel_obj_list, /* get_kernel_obj_list */
+ no_get_inproc_sync, /* get_inproc_sync */
no_close_handle, /* close_handle */
device_destroy /* destroy */
};
@@ -227,6 +232,7 @@ static const struct object_ops device_file_ops =
NULL, /* unlink_name */
no_open_file, /* open_file */
device_file_get_kernel_obj_list, /* get_kernel_obj_list */
+ default_fd_get_inproc_sync, /* get_inproc_sync */
device_file_close_handle, /* close_handle */
device_file_destroy /* destroy */
};
@@ -418,7 +424,12 @@ static void add_irp_to_queue( struct device_manager *manager, struct irp_call *i
irp->thread = thread ? (struct thread *)grab_object( thread ) : NULL;
if (irp->file) list_add_tail( &irp->file->requests, &irp->dev_entry );
list_add_tail( &manager->requests, &irp->mgr_entry );
- if (list_head( &manager->requests ) == &irp->mgr_entry) wake_up( &manager->obj, 0 ); /* first one */
+ if (list_head( &manager->requests ) == &irp->mgr_entry)
+ {
+ /* first one */
+ wake_up( &manager->obj, 0 );
+ set_inproc_event( manager->inproc_sync );
+ }
}
static struct object *device_open_file( struct object *obj, unsigned int access,
@@ -752,6 +763,9 @@ static void delete_file( struct device_file *file )
set_irp_result( irp, STATUS_FILE_DELETED, NULL, 0, 0 );
}
+ if (list_empty( &file->device->manager->requests ))
+ reset_inproc_event( file->device->manager->inproc_sync );
+
release_object( file );
}
@@ -783,6 +797,16 @@ static int device_manager_signaled( struct object *obj, struct wait_queue_entry
return !list_empty( &manager->requests );
}
+static struct inproc_sync *device_manager_get_inproc_sync( struct object *obj )
+{
+ struct device_manager *manager = (struct device_manager *)obj;
+
+ if (!manager->inproc_sync)
+ manager->inproc_sync = create_inproc_event( INPROC_SYNC_MANUAL_SERVER, !list_empty( &manager->requests ) );
+ if (manager->inproc_sync) grab_object( manager->inproc_sync );
+ return manager->inproc_sync;
+}
+
static void device_manager_destroy( struct object *obj )
{
struct device_manager *manager = (struct device_manager *)obj;
@@ -817,6 +841,8 @@ static void device_manager_destroy( struct object *obj )
assert( !irp->file && !irp->async );
release_object( irp );
}
+
+ if (manager->inproc_sync) release_object( manager->inproc_sync );
}
static struct device_manager *create_device_manager(void)
@@ -826,6 +852,7 @@ static struct device_manager *create_device_manager(void)
if ((manager = alloc_object( &device_manager_ops )))
{
manager->current_call = NULL;
+ manager->inproc_sync = NULL;
list_init( &manager->devices );
list_init( &manager->requests );
wine_rb_init( &manager->kernel_objects, compare_kernel_object );
@@ -1015,6 +1042,10 @@ DECL_HANDLER(get_next_device_request)
}
list_remove( &irp->mgr_entry );
list_init( &irp->mgr_entry );
+
+ if (list_empty( &manager->requests ))
+ reset_inproc_event( manager->inproc_sync );
+
/* we already own the object if it's only on manager queue */
if (irp->file) grab_object( irp );
manager->current_call = irp;
diff --git a/server/directory.c b/server/directory.c
index fd689c561bc97936f2504d4e4fb753c32b354a1b..40cb9b959996c08232a2de8e4da909a977d9c1db 100644
--- a/server/directory.c
+++ b/server/directory.c
@@ -81,6 +81,7 @@ static const struct object_ops object_type_ops =
default_unlink_name, /* unlink_name */
no_open_file, /* open_file */
no_kernel_obj_list, /* get_kernel_obj_list */
+ no_get_inproc_sync, /* get_inproc_sync */
no_close_handle, /* close_handle */
no_destroy /* destroy */
};
@@ -131,6 +132,7 @@ static const struct object_ops directory_ops =
default_unlink_name, /* unlink_name */
no_open_file, /* open_file */
no_kernel_obj_list, /* get_kernel_obj_list */
+ no_get_inproc_sync, /* get_inproc_sync */
no_close_handle, /* close_handle */
directory_destroy /* destroy */
};
diff --git a/server/event.c b/server/event.c
index ad7c09acc992bc730e58a3e3f9a67c6644f3a986..5f25ad61f7b664a12c8f227e3d76ced53f6fa31e 100644
--- a/server/event.c
+++ b/server/event.c
@@ -56,6 +56,7 @@ struct event
struct list kernel_object; /* list of kernel object pointers */
int manual_reset; /* is it a manual reset event? */
int signaled; /* event has been signaled */
+ struct inproc_sync *inproc_sync;/* in-process synchronization object */
};
static void event_dump( struct object *obj, int verbose );
@@ -63,6 +64,8 @@ static int event_signaled( struct object *obj, struct wait_queue_entry *entry );
static void event_satisfied( struct object *obj, struct wait_queue_entry *entry );
static int event_signal( struct object *obj, unsigned int access);
static struct list *event_get_kernel_obj_list( struct object *obj );
+static struct inproc_sync *event_get_inproc_sync( struct object *obj );
+static void event_destroy( struct object *obj );
static const struct object_ops event_ops =
{
@@ -84,8 +87,9 @@ static const struct object_ops event_ops =
default_unlink_name, /* unlink_name */
no_open_file, /* open_file */
event_get_kernel_obj_list, /* get_kernel_obj_list */
+ event_get_inproc_sync, /* get_inproc_sync */
no_close_handle, /* close_handle */
- no_destroy /* destroy */
+ event_destroy /* destroy */
};
@@ -106,10 +110,13 @@ struct type_descr keyed_event_type =
struct keyed_event
{
struct object obj; /* object header */
+ struct inproc_sync *inproc_sync;/* in-process synchronization object */
};
static void keyed_event_dump( struct object *obj, int verbose );
static int keyed_event_signaled( struct object *obj, struct wait_queue_entry *entry );
+static struct inproc_sync *keyed_event_get_inproc_sync( struct object *obj );
+static void keyed_event_destroy( struct object *obj );
static const struct object_ops keyed_event_ops =
{
@@ -131,8 +138,9 @@ static const struct object_ops keyed_event_ops =
default_unlink_name, /* unlink_name */
no_open_file, /* open_file */
no_kernel_obj_list, /* get_kernel_obj_list */
+ keyed_event_get_inproc_sync, /* get_inproc_sync */
no_close_handle, /* close_handle */
- no_destroy /* destroy */
+ keyed_event_destroy /* destroy */
};
@@ -150,6 +158,7 @@ struct event *create_event( struct object *root, const struct unicode_str *name,
list_init( &event->kernel_object );
event->manual_reset = manual_reset;
event->signaled = initial_state;
+ event->inproc_sync = NULL;
}
}
return event;
@@ -173,11 +182,13 @@ void set_event( struct event *event )
event->signaled = 1;
/* wake up all waiters if manual reset, a single one otherwise */
wake_up( &event->obj, !event->manual_reset );
+ set_inproc_event( event->inproc_sync );
}
void reset_event( struct event *event )
{
event->signaled = 0;
+ reset_inproc_event( event->inproc_sync );
}
static void event_dump( struct object *obj, int verbose )
@@ -223,6 +234,30 @@ static struct list *event_get_kernel_obj_list( struct object *obj )
return &event->kernel_object;
}
+static struct inproc_sync *event_get_inproc_sync( struct object *obj )
+{
+ struct event *event = (struct event *)obj;
+
+ /* This state will always be the state that the event was created with.
+ * We could create the inproc_sync at creation time to make this clearer,
+ * but some broken programs create hundreds of thousands of handles which
+ * they never use, and we want to avoid wasting memory or fds in that case. */
+ if (!event->inproc_sync)
+ {
+ enum inproc_sync_type type = event->manual_reset ? INPROC_SYNC_MANUAL_EVENT : INPROC_SYNC_AUTO_EVENT;
+ event->inproc_sync = create_inproc_event( type, event->signaled );
+ }
+ if (event->inproc_sync) grab_object( event->inproc_sync );
+ return event->inproc_sync;
+}
+
+static void event_destroy( struct object *obj )
+{
+ struct event *event = (struct event *)obj;
+
+ if (event->inproc_sync) release_object( event->inproc_sync );
+}
+
struct keyed_event *create_keyed_event( struct object *root, const struct unicode_str *name,
unsigned int attr, const struct security_descriptor *sd )
{
@@ -233,6 +268,7 @@ struct keyed_event *create_keyed_event( struct object *root, const struct unicod
if (get_error() != STATUS_OBJECT_NAME_EXISTS)
{
/* initialize it if it didn't already exist */
+ event->inproc_sync = NULL;
}
}
return event;
@@ -276,6 +312,23 @@ static int keyed_event_signaled( struct object *obj, struct wait_queue_entry *en
return 0;
}
+static struct inproc_sync *keyed_event_get_inproc_sync( struct object *obj )
+{
+ struct keyed_event *event = (struct keyed_event *)obj;
+
+ if (!event->inproc_sync)
+ event->inproc_sync = create_inproc_event( INPROC_SYNC_MANUAL_SERVER, 1 );
+ if (event->inproc_sync) grab_object( event->inproc_sync );
+ return event->inproc_sync;
+}
+
+static void keyed_event_destroy( struct object *obj )
+{
+ struct keyed_event *event = (struct keyed_event *)obj;
+
+ if (event->inproc_sync) release_object( event->inproc_sync );
+}
+
/* create an event */
DECL_HANDLER(create_event)
{
diff --git a/server/fd.c b/server/fd.c
index ce32e7f8397d15e013066a48892cbc7f8847477d..77ac22b9655825f261a282e64a21694ff3365cd9 100644
--- a/server/fd.c
+++ b/server/fd.c
@@ -154,6 +154,7 @@ struct fd
struct completion *completion; /* completion object attached to this fd */
apc_param_t comp_key; /* completion key to set in completion events */
unsigned int comp_flags; /* completion flags */
+ struct inproc_sync *inproc_sync; /* in-process synchronization object */
};
static void fd_dump( struct object *obj, int verbose );
@@ -179,6 +180,7 @@ static const struct object_ops fd_ops =
NULL, /* unlink_name */
no_open_file, /* open_file */
no_kernel_obj_list, /* get_kernel_obj_list */
+ no_get_inproc_sync, /* get_inproc_sync */
no_close_handle, /* close_handle */
fd_destroy /* destroy */
};
@@ -220,6 +222,7 @@ static const struct object_ops device_ops =
NULL, /* unlink_name */
no_open_file, /* open_file */
no_kernel_obj_list, /* get_kernel_obj_list */
+ no_get_inproc_sync, /* get_inproc_sync */
no_close_handle, /* close_handle */
device_destroy /* destroy */
};
@@ -260,6 +263,7 @@ static const struct object_ops inode_ops =
NULL, /* unlink_name */
no_open_file, /* open_file */
no_kernel_obj_list, /* get_kernel_obj_list */
+ no_get_inproc_sync, /* get_inproc_sync */
no_close_handle, /* close_handle */
inode_destroy /* destroy */
};
@@ -302,6 +306,7 @@ static const struct object_ops file_lock_ops =
NULL, /* unlink_name */
no_open_file, /* open_file */
no_kernel_obj_list, /* get_kernel_obj_list */
+ no_get_inproc_sync, /* get_inproc_sync */
no_close_handle, /* close_handle */
no_destroy /* destroy */
};
@@ -1572,6 +1577,7 @@ static void fd_destroy( struct object *obj )
if (fd->unix_fd != -1) close( fd->unix_fd );
free( fd->unix_name );
}
+ if (fd->inproc_sync) release_object( fd->inproc_sync );
}
/* check if the desired access is possible without violating */
@@ -1690,6 +1696,7 @@ static struct fd *alloc_fd_object(void)
fd->poll_index = -1;
fd->completion = NULL;
fd->comp_flags = 0;
+ fd->inproc_sync = NULL;
init_async_queue( &fd->read_q );
init_async_queue( &fd->write_q );
init_async_queue( &fd->wait_q );
@@ -1730,6 +1737,7 @@ struct fd *alloc_pseudo_fd( const struct fd_ops *fd_user_ops, struct object *use
fd->poll_index = -1;
fd->completion = NULL;
fd->comp_flags = 0;
+ fd->inproc_sync = NULL;
fd->no_fd_status = STATUS_BAD_DEVICE_TYPE;
init_async_queue( &fd->read_q );
init_async_queue( &fd->write_q );
@@ -2140,7 +2148,15 @@ void set_fd_signaled( struct fd *fd, int signaled )
{
if (fd->comp_flags & FILE_SKIP_SET_EVENT_ON_HANDLE) return;
fd->signaled = signaled;
- if (signaled) wake_up( fd->user, 0 );
+ if (signaled)
+ {
+ wake_up( fd->user, 0 );
+ set_inproc_event( fd->inproc_sync );
+ }
+ else
+ {
+ reset_inproc_event( fd->inproc_sync );
+ }
}
/* check if events are pending and if yes return which one(s) */
@@ -2166,6 +2182,19 @@ int default_fd_signaled( struct object *obj, struct wait_queue_entry *entry )
return ret;
}
+struct inproc_sync *default_fd_get_inproc_sync( struct object *obj )
+{
+ struct fd *fd = get_obj_fd( obj );
+ struct inproc_sync *ret;
+
+ if (!fd->inproc_sync)
+ fd->inproc_sync = create_inproc_event( INPROC_SYNC_MANUAL_SERVER, fd->signaled );
+ ret = fd->inproc_sync;
+ release_object( fd );
+ if (ret) grab_object( ret );
+ return ret;
+}
+
int default_fd_get_poll_events( struct fd *fd )
{
int events = 0;
diff --git a/server/file.c b/server/file.c
index 8f6566a0077dd77f56c48c98536e731fa1aeada6..bab203b1914363cff7537b589db85c790eed162e 100644
--- a/server/file.c
+++ b/server/file.c
@@ -106,6 +106,7 @@ static const struct object_ops file_ops =
NULL, /* unlink_name */
file_open_file, /* open_file */
file_get_kernel_obj_list, /* get_kernel_obj_list */
+ default_fd_get_inproc_sync, /* get_inproc_sync */
no_close_handle, /* close_handle */
file_destroy /* destroy */
};
diff --git a/server/file.h b/server/file.h
index 7368640a67459b1970865885b59b0614105464f7..baa3358c7c2be6ed807f4169e804a2fa15140c85 100644
--- a/server/file.h
+++ b/server/file.h
@@ -109,6 +109,7 @@ extern char *dup_fd_name( struct fd *root, const char *name ) __WINE_DEALLOC(fre
extern void get_nt_name( struct fd *fd, struct unicode_str *name );
extern int default_fd_signaled( struct object *obj, struct wait_queue_entry *entry );
+extern struct inproc_sync *default_fd_get_inproc_sync( struct object *obj );
extern int default_fd_get_poll_events( struct fd *fd );
extern void default_poll_event( struct fd *fd, int event );
extern void fd_cancel_async( struct fd *fd, struct async *async );
diff --git a/server/handle.c b/server/handle.c
index 8968df73647ae373a14e896e44d201ac9e500bbf..58b0fe290d261418b38edc3ea62f6dfb6d4e4636 100644
--- a/server/handle.c
+++ b/server/handle.c
@@ -138,6 +138,7 @@ static const struct object_ops handle_table_ops =
NULL, /* unlink_name */
no_open_file, /* open_file */
no_kernel_obj_list, /* get_kernel_obj_list */
+ no_get_inproc_sync, /* get_inproc_sync */
no_close_handle, /* close_handle */
handle_table_destroy /* destroy */
};
diff --git a/server/hook.c b/server/hook.c
index ffe7206369e14a10d4775360e85dd58fc541b052..5faa63e8e105eb28bcb5f105fd9a71fe6a848c77 100644
--- a/server/hook.c
+++ b/server/hook.c
@@ -93,6 +93,7 @@ static const struct object_ops hook_table_ops =
NULL, /* unlink_name */
no_open_file, /* open_file */
no_kernel_obj_list, /* get_kernel_obj_list */
+ no_get_inproc_sync, /* get_inproc_sync */
no_close_handle, /* close_handle */
hook_table_destroy /* destroy */
};
diff --git a/server/inproc_sync.c b/server/inproc_sync.c
new file mode 100644
index 0000000000000000000000000000000000000000..57d6bce41163bfe71615d23e15fb90650097e60f
--- /dev/null
+++ b/server/inproc_sync.c
@@ -0,0 +1,418 @@
+/*
+ * In-process synchronization primitives
+ *
+ * Copyright (C) 2021-2022 Elizabeth Figura for CodeWeavers
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include "config.h"
+
+#include <assert.h>
+#include <stdint.h>
+#include <stdio.h>
+
+#include "ntstatus.h"
+#define WIN32_NO_STATUS
+#include "winternl.h"
+
+#include "file.h"
+#include "handle.h"
+#include "request.h"
+#include "thread.h"
+
+#ifdef HAVE_LINUX_NTSYNC_H
+# include <linux/ntsync.h>
+#endif
+
+#ifdef NTSYNC_IOC_EVENT_READ
+
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+struct linux_device
+{
+ struct object obj; /* object header */
+ struct fd *fd; /* fd for unix fd */
+};
+
+static struct object *linux_device_object;
+
+static void linux_device_dump( struct object *obj, int verbose );
+static struct fd *linux_device_get_fd( struct object *obj );
+static void linux_device_destroy( struct object *obj );
+static enum server_fd_type inproc_sync_get_fd_type( struct fd *fd );
+
+static const struct object_ops linux_device_ops =
+{
+ sizeof(struct linux_device), /* size */
+ &no_type, /* type */
+ linux_device_dump, /* dump */
+ no_add_queue, /* add_queue */
+ NULL, /* remove_queue */
+ NULL, /* signaled */
+ NULL, /* satisfied */
+ no_signal, /* signal */
+ linux_device_get_fd, /* get_fd */
+ default_map_access, /* map_access */
+ default_get_sd, /* get_sd */
+ default_set_sd, /* set_sd */
+ no_get_full_name, /* get_full_name */
+ no_lookup_name, /* lookup_name */
+ no_link_name, /* link_name */
+ NULL, /* unlink_name */
+ no_open_file, /* open_file */
+ no_kernel_obj_list, /* get_kernel_obj_list */
+ no_get_inproc_sync, /* get_inproc_sync */
+ no_close_handle, /* close_handle */
+ linux_device_destroy /* destroy */
+};
+
+static const struct fd_ops inproc_sync_fd_ops =
+{
+ default_fd_get_poll_events, /* get_poll_events */
+ default_poll_event, /* poll_event */
+ inproc_sync_get_fd_type, /* get_fd_type */
+ no_fd_read, /* read */
+ no_fd_write, /* write */
+ no_fd_flush, /* flush */
+ no_fd_get_file_info, /* get_file_info */
+ no_fd_get_volume_info, /* get_volume_info */
+ no_fd_ioctl, /* ioctl */
+ default_fd_cancel_async, /* cancel_async */
+ no_fd_queue_async, /* queue_async */
+ default_fd_reselect_async /* reselect_async */
+};
+
+static void linux_device_dump( struct object *obj, int verbose )
+{
+ struct linux_device *device = (struct linux_device *)obj;
+ assert( obj->ops == &linux_device_ops );
+ fprintf( stderr, "In-process synchronization device fd=%p\n", device->fd );
+}
+
+static struct fd *linux_device_get_fd( struct object *obj )
+{
+ struct linux_device *device = (struct linux_device *)obj;
+ return (struct fd *)grab_object( device->fd );
+}
+
+static void linux_device_destroy( struct object *obj )
+{
+ struct linux_device *device = (struct linux_device *)obj;
+ assert( obj->ops == &linux_device_ops );
+ if (device->fd) release_object( device->fd );
+ linux_device_object = NULL;
+}
+
+static enum server_fd_type inproc_sync_get_fd_type( struct fd *fd )
+{
+ return FD_TYPE_FILE;
+}
+
+static struct linux_device *get_linux_device(void)
+{
+ struct linux_device *device;
+ int unix_fd;
+
+ if (linux_device_object)
+ return (struct linux_device *)grab_object( linux_device_object );
+
+ unix_fd = open( "/dev/ntsync", O_CLOEXEC | O_RDONLY );
+ if (unix_fd == -1)
+ {
+ file_set_error();
+ return NULL;
+ }
+
+ if (!(device = alloc_object( &linux_device_ops )))
+ {
+ close( unix_fd );
+ return NULL;
+ }
+
+ if (!(device->fd = create_anonymous_fd( &inproc_sync_fd_ops, unix_fd, &device->obj, 0 )))
+ {
+ release_object( device );
+ return NULL;
+ }
+ allow_fd_caching( device->fd );
+
+ linux_device_object = grab_object( device );
+ make_object_permanent( linux_device_object );
+ return device;
+}
+
+struct inproc_sync
+{
+ struct object obj;
+ enum inproc_sync_type type;
+ struct fd *fd;
+};
+
+static void linux_obj_dump( struct object *obj, int verbose );
+static void linux_obj_destroy( struct object *obj );
+static struct fd *linux_obj_get_fd( struct object *obj );
+
+static const struct object_ops linux_obj_ops =
+{
+ sizeof(struct inproc_sync), /* size */
+ &no_type, /* type */
+ linux_obj_dump, /* dump */
+ no_add_queue, /* add_queue */
+ NULL, /* remove_queue */
+ NULL, /* signaled */
+ NULL, /* satisfied */
+ no_signal, /* signal */
+ linux_obj_get_fd, /* get_fd */
+ default_map_access, /* map_access */
+ default_get_sd, /* get_sd */
+ default_set_sd, /* set_sd */
+ no_get_full_name, /* get_full_name */
+ no_lookup_name, /* lookup_name */
+ no_link_name, /* link_name */
+ NULL, /* unlink_name */
+ no_open_file, /* open_file */
+ no_kernel_obj_list, /* get_kernel_obj_list */
+ no_get_inproc_sync, /* get_inproc_sync */
+ no_close_handle, /* close_handle */
+ linux_obj_destroy /* destroy */
+};
+
+static void linux_obj_dump( struct object *obj, int verbose )
+{
+ struct inproc_sync *inproc_sync = (struct inproc_sync *)obj;
+ assert( obj->ops == &linux_obj_ops );
+ fprintf( stderr, "In-process synchronization object type=%u fd=%p\n", inproc_sync->type, inproc_sync->fd );
+}
+
+static void linux_obj_destroy( struct object *obj )
+{
+ struct inproc_sync *inproc_sync = (struct inproc_sync *)obj;
+ assert( obj->ops == &linux_obj_ops );
+ if (inproc_sync->fd) release_object( inproc_sync->fd );
+}
+
+static struct fd *linux_obj_get_fd( struct object *obj )
+{
+ struct inproc_sync *inproc_sync = (struct inproc_sync *)obj;
+ assert( obj->ops == &linux_obj_ops );
+ return (struct fd *)grab_object( inproc_sync->fd );
+}
+
+static struct inproc_sync *create_inproc_sync( enum inproc_sync_type type, int unix_fd )
+{
+ struct inproc_sync *inproc_sync;
+
+ if (!(inproc_sync = alloc_object( &linux_obj_ops )))
+ {
+ close( unix_fd );
+ return NULL;
+ }
+
+ inproc_sync->type = type;
+
+ if (!(inproc_sync->fd = create_anonymous_fd( &inproc_sync_fd_ops, unix_fd, &inproc_sync->obj, 0 )))
+ {
+ release_object( inproc_sync );
+ return NULL;
+ }
+ allow_fd_caching( inproc_sync->fd );
+
+ return inproc_sync;
+}
+
+struct inproc_sync *create_inproc_event( enum inproc_sync_type type, int signaled )
+{
+ struct ntsync_event_args args;
+ struct linux_device *device;
+ int event;
+
+ if (!(device = get_linux_device())) return NULL;
+
+ args.signaled = signaled;
+ switch (type)
+ {
+ case INPROC_SYNC_AUTO_EVENT:
+ case INPROC_SYNC_AUTO_SERVER:
+ args.manual = 0;
+ break;
+
+ case INPROC_SYNC_MANUAL_EVENT:
+ case INPROC_SYNC_MANUAL_SERVER:
+ case INPROC_SYNC_QUEUE:
+ args.manual = 1;
+ break;
+
+ default:
+ assert(0);
+ break;
+ }
+ if ((event = ioctl( get_unix_fd( device->fd ), NTSYNC_IOC_CREATE_EVENT, &args )) < 0)
+ {
+ file_set_error();
+ release_object( device );
+ return NULL;
+ }
+ release_object( device );
+
+ return create_inproc_sync( type, event );
+}
+
+struct inproc_sync *create_inproc_semaphore( unsigned int count, unsigned int max )
+{
+ struct ntsync_sem_args args;
+ struct linux_device *device;
+ int semaphore;
+
+ if (!(device = get_linux_device())) return NULL;
+
+ args.count = count;
+ args.max = max;
+ if ((semaphore = ioctl( get_unix_fd( device->fd ), NTSYNC_IOC_CREATE_SEM, &args )) < 0)
+ {
+ file_set_error();
+ release_object( device );
+ return NULL;
+ }
+
+ release_object( device );
+
+ return create_inproc_sync( INPROC_SYNC_SEMAPHORE, semaphore );
+}
+
+struct inproc_sync *create_inproc_mutex( thread_id_t owner, unsigned int count )
+{
+ struct ntsync_mutex_args args;
+ struct linux_device *device;
+ int mutex;
+
+ if (!(device = get_linux_device())) return NULL;
+
+ args.owner = owner;
+ args.count = count;
+ if ((mutex = ioctl( get_unix_fd( device->fd ), NTSYNC_IOC_CREATE_MUTEX, &args )) < 0)
+ {
+ file_set_error();
+ release_object( device );
+ return NULL;
+ }
+
+ release_object( device );
+
+ return create_inproc_sync( INPROC_SYNC_MUTEX, mutex );
+}
+
+void set_inproc_event( struct inproc_sync *inproc_sync )
+{
+ __u32 count;
+
+ if (!inproc_sync) return;
+
+ if (debug_level) fprintf( stderr, "set_inproc_event %p\n", inproc_sync->fd );
+
+ ioctl( get_unix_fd( inproc_sync->fd ), NTSYNC_IOC_EVENT_SET, &count );
+}
+
+void reset_inproc_event( struct inproc_sync *inproc_sync )
+{
+ __u32 count;
+
+ if (!inproc_sync) return;
+
+ if (debug_level) fprintf( stderr, "set_inproc_event %p\n", inproc_sync->fd );
+
+ ioctl( get_unix_fd( inproc_sync->fd ), NTSYNC_IOC_EVENT_RESET, &count );
+}
+
+void abandon_inproc_mutex( thread_id_t tid, struct inproc_sync *inproc_sync )
+{
+ ioctl( get_unix_fd( inproc_sync->fd ), NTSYNC_IOC_MUTEX_KILL, &tid );
+}
+
+#else
+
+struct inproc_sync *create_inproc_event( enum inproc_sync_type type, int signaled )
+{
+ set_error( STATUS_NOT_IMPLEMENTED );
+ return NULL;
+}
+
+struct inproc_sync *create_inproc_semaphore( unsigned int count, unsigned int max )
+{
+ set_error( STATUS_NOT_IMPLEMENTED );
+ return NULL;
+}
+
+struct inproc_sync *create_inproc_mutex( thread_id_t owner, unsigned int count )
+{
+ set_error( STATUS_NOT_IMPLEMENTED );
+ return NULL;
+}
+
+void set_inproc_event( struct inproc_sync *inproc_sync )
+{
+}
+
+void reset_inproc_event( struct inproc_sync *obj )
+{
+}
+
+void abandon_inproc_mutex( thread_id_t tid, struct inproc_sync *inproc_sync )
+{
+}
+
+#endif
+
+
+DECL_HANDLER(get_linux_sync_device)
+{
+#ifdef NTSYNC_IOC_EVENT_READ
+ struct linux_device *device;
+
+ if ((device = get_linux_device()))
+ {
+ reply->handle = alloc_handle_no_access_check( current->process, device, 0, 0 );
+ release_object( device );
+ }
+#else
+ set_error( STATUS_NOT_IMPLEMENTED );
+#endif
+}
+
+DECL_HANDLER(get_linux_sync_obj)
+{
+#ifdef NTSYNC_IOC_EVENT_READ
+ struct object *obj;
+
+ if ((obj = get_handle_obj( current->process, req->handle, 0, NULL )))
+ {
+ struct inproc_sync *inproc_sync;
+
+ if ((inproc_sync = obj->ops->get_inproc_sync( obj )))
+ {
+ reply->handle = alloc_handle_no_access_check( current->process, inproc_sync, 0, 0 );
+ reply->type = inproc_sync->type;
+ reply->access = get_handle_access( current->process, req->handle );
+ release_object( inproc_sync );
+ }
+ release_object( obj );
+ }
+#else
+ set_error( STATUS_NOT_IMPLEMENTED );
+#endif
+}
diff --git a/server/mailslot.c b/server/mailslot.c
index c54281c2101fb1b21997d406311df1f0fa2f1bde..929ca3ecb823a297f7cafec74a94dc28625de8e2 100644
--- a/server/mailslot.c
+++ b/server/mailslot.c
@@ -93,6 +93,7 @@ static const struct object_ops mailslot_ops =
default_unlink_name, /* unlink_name */
mailslot_open_file, /* open_file */
no_kernel_obj_list, /* get_kernel_obj_list */
+ default_fd_get_inproc_sync,/* get_inproc_sync */
no_close_handle, /* close_handle */
mailslot_destroy /* destroy */
};
@@ -154,6 +155,7 @@ static const struct object_ops mail_writer_ops =
NULL, /* unlink_name */
no_open_file, /* open_file */
no_kernel_obj_list, /* get_kernel_obj_list */
+ default_fd_get_inproc_sync, /* get_inproc_sync */
no_close_handle, /* close_handle */
mail_writer_destroy /* destroy */
};
@@ -219,6 +221,7 @@ static const struct object_ops mailslot_device_ops =
default_unlink_name, /* unlink_name */
mailslot_device_open_file, /* open_file */
no_kernel_obj_list, /* get_kernel_obj_list */
+ no_get_inproc_sync, /* get_inproc_sync */
no_close_handle, /* close_handle */
mailslot_device_destroy /* destroy */
};
@@ -249,6 +252,7 @@ static const struct object_ops mailslot_device_file_ops =
NULL, /* unlink_name */
no_open_file, /* open_file */
no_kernel_obj_list, /* get_kernel_obj_list */
+ default_fd_get_inproc_sync, /* get_inproc_sync */
no_close_handle, /* close_handle */
mailslot_device_file_destroy /* destroy */
};
diff --git a/server/mapping.c b/server/mapping.c
index 4bdd7c5080ac0fc88b17582d17e17a20d8932382..7c715a874577e1b71b891d3e116944281147b043 100644
--- a/server/mapping.c
+++ b/server/mapping.c
@@ -79,6 +79,7 @@ static const struct object_ops ranges_ops =
NULL, /* unlink_name */
no_open_file, /* open_file */
no_kernel_obj_list, /* get_kernel_obj_list */
+ no_get_inproc_sync, /* get_inproc_sync */
no_close_handle, /* close_handle */
ranges_destroy /* destroy */
};
@@ -115,6 +116,7 @@ static const struct object_ops shared_map_ops =
NULL, /* unlink_name */
no_open_file, /* open_file */
no_kernel_obj_list, /* get_kernel_obj_list */
+ no_get_inproc_sync, /* get_inproc_sync */
no_close_handle, /* close_handle */
shared_map_destroy /* destroy */
};
@@ -188,6 +190,7 @@ static const struct object_ops mapping_ops =
default_unlink_name, /* unlink_name */
no_open_file, /* open_file */
no_kernel_obj_list, /* get_kernel_obj_list */
+ no_get_inproc_sync, /* get_inproc_sync */
no_close_handle, /* close_handle */
mapping_destroy /* destroy */
};
diff --git a/server/mutex.c b/server/mutex.c
index af0efe7213239180b5af4383d5eed7625f366e47..88e489c05de0d5be770bc405808be23ded890706 100644
--- a/server/mutex.c
+++ b/server/mutex.c
@@ -38,6 +38,8 @@
static const WCHAR mutex_name[] = {'M','u','t','a','n','t'};
+static struct list inproc_mutexes = LIST_INIT(inproc_mutexes);
+
struct type_descr mutex_type =
{
{ mutex_name, sizeof(mutex_name) }, /* name */
@@ -57,6 +59,8 @@ struct mutex
unsigned int count; /* recursion count */
int abandoned; /* has it been abandoned? */
struct list entry; /* entry in owner thread mutex list */
+ struct list inproc_mutexes_entry; /* entry in inproc_mutexes list */
+ struct inproc_sync *inproc_sync;/* in-process synchronization object */
};
static void mutex_dump( struct object *obj, int verbose );
@@ -64,6 +68,7 @@ static int mutex_signaled( struct object *obj, struct wait_queue_entry *entry );
static void mutex_satisfied( struct object *obj, struct wait_queue_entry *entry );
static void mutex_destroy( struct object *obj );
static int mutex_signal( struct object *obj, unsigned int access );
+static struct inproc_sync *mutex_get_inproc_sync( struct object *obj );
static const struct object_ops mutex_ops =
{
@@ -85,6 +90,7 @@ static const struct object_ops mutex_ops =
default_unlink_name, /* unlink_name */
no_open_file, /* open_file */
no_kernel_obj_list, /* get_kernel_obj_list */
+ mutex_get_inproc_sync, /* get_inproc_sync */
no_close_handle, /* close_handle */
mutex_destroy /* destroy */
};
@@ -127,6 +133,7 @@ static struct mutex *create_mutex( struct object *root, const struct unicode_str
mutex->owner = NULL;
mutex->abandoned = 0;
if (owned) do_grab( mutex, current );
+ mutex->inproc_sync = NULL;
}
}
return mutex;
@@ -134,16 +141,20 @@ static struct mutex *create_mutex( struct object *root, const struct unicode_str
void abandon_mutexes( struct thread *thread )
{
+ struct mutex *mutex;
struct list *ptr;
while ((ptr = list_head( &thread->mutex_list )) != NULL)
{
- struct mutex *mutex = LIST_ENTRY( ptr, struct mutex, entry );
+ mutex = LIST_ENTRY( ptr, struct mutex, entry );
assert( mutex->owner == thread );
mutex->count = 0;
mutex->abandoned = 1;
do_release( mutex );
}
+
+ LIST_FOR_EACH_ENTRY(mutex, &inproc_mutexes, struct mutex, inproc_mutexes_entry)
+ abandon_inproc_mutex( thread->id, mutex->inproc_sync );
}
static void mutex_dump( struct object *obj, int verbose )
@@ -189,14 +200,38 @@ static int mutex_signal( struct object *obj, unsigned int access )
return 1;
}
+static struct inproc_sync *mutex_get_inproc_sync( struct object *obj )
+{
+ struct mutex *mutex = (struct mutex *)obj;
+
+ /* This state will always be the state that the mutex was created with.
+ * We could create the inproc_sync at creation time to make this clearer,
+ * but some broken programs create hundreds of thousands of handles which
+ * they never use, and we want to avoid wasting memory or fds in that case. */
+ if (!mutex->inproc_sync)
+ {
+ mutex->inproc_sync = create_inproc_mutex( mutex->owner ? mutex->owner->id : 0, mutex->count );
+ if (mutex->inproc_sync) list_add_tail( &inproc_mutexes, &mutex->inproc_mutexes_entry );
+ }
+ if (mutex->inproc_sync) grab_object( mutex->inproc_sync );
+ return mutex->inproc_sync;
+}
+
static void mutex_destroy( struct object *obj )
{
struct mutex *mutex = (struct mutex *)obj;
assert( obj->ops == &mutex_ops );
- if (!mutex->count) return;
- mutex->count = 0;
- do_release( mutex );
+ if (mutex->count)
+ {
+ mutex->count = 0;
+ do_release( mutex );
+ }
+ if (mutex->inproc_sync)
+ {
+ release_object( mutex->inproc_sync );
+ list_remove( &mutex->inproc_mutexes_entry );
+ }
}
/* create a mutex */
diff --git a/server/named_pipe.c b/server/named_pipe.c
index 6e4ae371a1b01c33959119e1b690973e7183b5e3..2ce2756084540981fa9a2bb82b2619daf5b43592 100644
--- a/server/named_pipe.c
+++ b/server/named_pipe.c
@@ -131,6 +131,7 @@ static const struct object_ops named_pipe_ops =
default_unlink_name, /* unlink_name */
named_pipe_open_file, /* open_file */
no_kernel_obj_list, /* get_kernel_obj_list */
+ no_get_inproc_sync, /* get_inproc_sync */
no_close_handle, /* close_handle */
named_pipe_destroy /* destroy */
};
@@ -179,6 +180,7 @@ static const struct object_ops pipe_server_ops =
NULL, /* unlink_name */
pipe_server_open_file, /* open_file */
no_kernel_obj_list, /* get_kernel_obj_list */
+ default_fd_get_inproc_sync, /* get_inproc_sync */
async_close_obj_handle, /* close_handle */
pipe_server_destroy /* destroy */
};
@@ -223,6 +225,7 @@ static const struct object_ops pipe_client_ops =
NULL, /* unlink_name */
no_open_file, /* open_file */
no_kernel_obj_list, /* get_kernel_obj_list */
+ default_fd_get_inproc_sync, /* get_inproc_sync */
async_close_obj_handle, /* close_handle */
pipe_end_destroy /* destroy */
};
@@ -270,6 +273,7 @@ static const struct object_ops named_pipe_device_ops =
default_unlink_name, /* unlink_name */
named_pipe_device_open_file, /* open_file */
no_kernel_obj_list, /* get_kernel_obj_list */
+ no_get_inproc_sync, /* get_inproc_sync */
no_close_handle, /* close_handle */
named_pipe_device_destroy /* destroy */
};
@@ -301,6 +305,7 @@ static const struct object_ops named_pipe_device_file_ops =
NULL, /* unlink_name */
no_open_file, /* open_file */
no_kernel_obj_list, /* get_kernel_obj_list */
+ default_fd_get_inproc_sync, /* get_inproc_sync */
no_close_handle, /* close_handle */
named_pipe_device_file_destroy /* destroy */
};
@@ -351,6 +356,7 @@ static const struct object_ops named_pipe_dir_ops =
NULL, /* unlink_name */
named_pipe_dir_open_file, /* open_file */
no_kernel_obj_list, /* get_kernel_obj_list */
+ default_fd_get_inproc_sync, /* get_inproc_sync */
no_close_handle, /* close_handle */
named_pipe_dir_destroy /* destroy */
};
diff --git a/server/object.c b/server/object.c
index cd368ef724a7769bf43c655f84f97dbe0d287fa2..b07bc59bde1217924d76b9f341476fc4600f20cc 100644
--- a/server/object.c
+++ b/server/object.c
@@ -120,6 +120,7 @@ static const struct object_ops apc_reserve_ops =
default_unlink_name, /* unlink_name */
no_open_file, /* open_file */
no_kernel_obj_list, /* get_kernel_obj_list */
+ no_get_inproc_sync, /* get_inproc_sync */
no_close_handle, /* close_handle */
no_destroy /* destroy */
};
@@ -144,6 +145,7 @@ static const struct object_ops completion_reserve_ops =
default_unlink_name, /* unlink_name */
no_open_file, /* open_file */
no_kernel_obj_list, /* get_kernel_obj_list */
+ no_get_inproc_sync, /* get_inproc_sync */
no_close_handle, /* close_handle */
no_destroy /* destroy */
};
@@ -639,6 +641,12 @@ struct fd *no_get_fd( struct object *obj )
return NULL;
}
+struct inproc_sync *no_get_inproc_sync( struct object *obj )
+{
+ set_error( STATUS_OBJECT_TYPE_MISMATCH );
+ return NULL;
+}
+
unsigned int default_map_access( struct object *obj, unsigned int access )
{
return map_access( access, &obj->ops->type->mapping );
diff --git a/server/object.h b/server/object.h
index 1058f9bfb0a3cc0a81c6f698178bec295276b8bb..d3e1213d15656130a9830b5b1b0f6a054a6639bb 100644
--- a/server/object.h
+++ b/server/object.h
@@ -42,6 +42,7 @@ struct async;
struct async_queue;
struct winstation;
struct object_type;
+struct inproc_sync;
struct unicode_str
@@ -103,6 +104,8 @@ struct object_ops
unsigned int options);
/* return list of kernel objects */
struct list *(*get_kernel_obj_list)(struct object *);
+ /* get a client-waitable in-process synchronization handle to this object */
+ struct inproc_sync *(*get_inproc_sync)(struct object *);
/* close a handle to this object */
int (*close_handle)(struct object *,struct process *,obj_handle_t);
/* destroy on refcount == 0 */
@@ -228,6 +231,17 @@ extern void reset_event( struct event *event );
extern void abandon_mutexes( struct thread *thread );
+/* in-process synchronization functions */
+
+extern struct inproc_sync *create_inproc_event( enum inproc_sync_type type, int signaled );
+extern struct inproc_sync *create_inproc_mutex( thread_id_t owner, unsigned int count );
+extern struct inproc_sync *create_inproc_semaphore( unsigned int count, unsigned int max );
+extern void set_inproc_event( struct inproc_sync *obj );
+extern void reset_inproc_event( struct inproc_sync *obj );
+extern void abandon_inproc_mutex( thread_id_t tid, struct inproc_sync *inproc_sync );
+
+extern struct inproc_sync *no_get_inproc_sync( struct object *obj );
+
/* serial functions */
int get_serial_async_timeout(struct object *obj, int type, int count);
diff --git a/server/process.c b/server/process.c
index e06350f73112f77f79816ce279646e3f5f4ed574..1850d5dcb2b730320f693d28ac4774041ef786da 100644
--- a/server/process.c
+++ b/server/process.c
@@ -94,6 +94,7 @@ static unsigned int process_map_access( struct object *obj, unsigned int access
static struct security_descriptor *process_get_sd( struct object *obj );
static void process_poll_event( struct fd *fd, int event );
static struct list *process_get_kernel_obj_list( struct object *obj );
+static struct inproc_sync *process_get_inproc_sync( struct object *obj );
static void process_destroy( struct object *obj );
static void terminate_process( struct process *process, struct thread *skip, int exit_code );
@@ -117,6 +118,7 @@ static const struct object_ops process_ops =
NULL, /* unlink_name */
no_open_file, /* open_file */
process_get_kernel_obj_list, /* get_kernel_obj_list */
+ process_get_inproc_sync, /* get_inproc_sync */
no_close_handle, /* close_handle */
process_destroy /* destroy */
};
@@ -168,6 +170,7 @@ static const struct object_ops startup_info_ops =
NULL, /* unlink_name */
no_open_file, /* open_file */
no_kernel_obj_list, /* get_kernel_obj_list */
+ no_get_inproc_sync, /* get_inproc_sync */
no_close_handle, /* close_handle */
startup_info_destroy /* destroy */
};
@@ -190,6 +193,7 @@ struct type_descr job_type =
static void job_dump( struct object *obj, int verbose );
static int job_signaled( struct object *obj, struct wait_queue_entry *entry );
+static struct inproc_sync *job_get_inproc_sync( struct object *obj );
static int job_close_handle( struct object *obj, struct process *process, obj_handle_t handle );
static void job_destroy( struct object *obj );
@@ -207,6 +211,7 @@ struct job
struct job *parent;
struct list parent_job_entry; /* list entry for parent job */
struct list child_job_list; /* list of child jobs */
+ struct inproc_sync *inproc_sync; /* in-process synchronization object */
};
static const struct object_ops job_ops =
@@ -229,6 +234,7 @@ static const struct object_ops job_ops =
default_unlink_name, /* unlink_name */
no_open_file, /* open_file */
no_kernel_obj_list, /* get_kernel_obj_list */
+ job_get_inproc_sync, /* get_inproc_sync */
job_close_handle, /* close_handle */
job_destroy /* destroy */
};
@@ -253,6 +259,7 @@ static struct job *create_job_object( struct object *root, const struct unicode_
job->completion_port = NULL;
job->completion_key = 0;
job->parent = NULL;
+ job->inproc_sync = NULL;
}
}
return job;
@@ -409,6 +416,17 @@ static void terminate_job( struct job *job, int exit_code )
job->terminating = 0;
job->signaled = 1;
wake_up( &job->obj, 0 );
+ set_inproc_event( job->inproc_sync );
+}
+
+static struct inproc_sync *job_get_inproc_sync( struct object *obj )
+{
+ struct job *job = (struct job *)obj;
+
+ if (!job->inproc_sync)
+ job->inproc_sync = create_inproc_event( INPROC_SYNC_MANUAL_SERVER, job->signaled );
+ if (job->inproc_sync) grab_object( job->inproc_sync );
+ return job->inproc_sync;
}
static int job_close_handle( struct object *obj, struct process *process, obj_handle_t handle )
@@ -439,6 +457,8 @@ static void job_destroy( struct object *obj )
list_remove( &job->parent_job_entry );
release_object( job->parent );
}
+
+ if (job->inproc_sync) release_object( job->inproc_sync );
}
static void job_dump( struct object *obj, int verbose )
@@ -684,6 +704,7 @@ struct process *create_process( int fd, struct process *parent, unsigned int fla
process->rawinput_device_count = 0;
process->rawinput_mouse = NULL;
process->rawinput_kbd = NULL;
+ process->inproc_sync = NULL;
memset( &process->image_info, 0, sizeof(process->image_info) );
list_init( &process->rawinput_entry );
list_init( &process->kernel_object );
@@ -784,6 +805,8 @@ static void process_destroy( struct object *obj )
free( process->rawinput_devices );
free( process->dir_cache );
free( process->image );
+
+ if (process->inproc_sync) release_object( process->inproc_sync );
}
/* dump a process on stdout for debugging purposes */
@@ -817,6 +840,16 @@ static struct list *process_get_kernel_obj_list( struct object *obj )
return &process->kernel_object;
}
+static struct inproc_sync *process_get_inproc_sync( struct object *obj )
+{
+ struct process *process = (struct process *)obj;
+
+ if (!process->inproc_sync)
+ process->inproc_sync = create_inproc_event( INPROC_SYNC_MANUAL_SERVER, !process->running_threads );
+ if (process->inproc_sync) grab_object( process->inproc_sync );
+ return process->inproc_sync;
+}
+
static struct security_descriptor *process_get_sd( struct object *obj )
{
static struct security_descriptor *process_default_sd;
@@ -981,6 +1014,7 @@ static void process_killed( struct process *process )
release_job_process( process );
start_sigkill_timer( process );
wake_up( &process->obj, 0 );
+ set_inproc_event( process->inproc_sync );
}
/* add a thread to a process running threads list */
diff --git a/server/process.h b/server/process.h
index 96814ab7cf8bd30620e6a3933e6d856273b0ae4e..667e25066723ca8082c09ba843e6b697d9ca664b 100644
--- a/server/process.h
+++ b/server/process.h
@@ -86,6 +86,7 @@ struct process
struct list rawinput_entry; /* entry in the rawinput process list */
struct list kernel_object; /* list of kernel object pointers */
struct pe_image_info image_info; /* main exe image info */
+ struct inproc_sync *inproc_sync; /* in-process synchronization object */
};
/* process functions */
diff --git a/server/protocol.def b/server/protocol.def
index 292303c385fc0efbae54ddd53e13f6a818433dec..5f1848dbc7c79c78c16e16e4133f4c36ab62df03 100644
--- a/server/protocol.def
+++ b/server/protocol.def
@@ -4079,3 +4079,50 @@ struct handle_info
@REPLY
int enable; /* previous state of auto-repeat enable */
@END
+
+enum inproc_sync_type
+{
+ INPROC_SYNC_UNKNOWN,
+ INPROC_SYNC_AUTO_EVENT,
+ INPROC_SYNC_MANUAL_EVENT,
+ INPROC_SYNC_SEMAPHORE,
+ INPROC_SYNC_MUTEX,
+ INPROC_SYNC_AUTO_SERVER,
+ INPROC_SYNC_MANUAL_SERVER,
+ INPROC_SYNC_QUEUE,
+};
+
+
+/* Obtain a handle to the ntsync device object */
+@REQ(get_linux_sync_device)
+@REPLY
+ obj_handle_t handle; /* handle to the device */
+@END
+
+
+/* Get the in-process synchronization object associated with the given handle */
+@REQ(get_linux_sync_obj)
+ obj_handle_t handle; /* handle to the object */
+@REPLY
+ obj_handle_t handle; /* handle to the fast synchronization object */
+ int type; /* object type */
+ unsigned int access; /* handle access rights */
+@END
+
+
+/* Begin a client-side wait on a message queue */
+@REQ(select_inproc_queue)
+@END
+
+
+/* End a client-side wait on a message queue */
+@REQ(unselect_inproc_queue)
+ int signaled; /* was the queue signaled? */
+@END
+
+
+/* Get an event handle to be used for thread alerts with in-process synchronization */
+@REQ(get_inproc_alert_event)
+@REPLY
+ obj_handle_t handle; /* handle to the event */
+@END
diff --git a/server/queue.c b/server/queue.c
index a918540c1284b4b723f532703e99fa6515ca9430..56dda666a3530846edb41e99c5d226019d132faf 100644
--- a/server/queue.c
+++ b/server/queue.c
@@ -133,6 +133,8 @@ struct msg_queue
timeout_t last_get_msg; /* time of last get message call */
int keystate_lock; /* owns an input keystate lock */
const queue_shm_t *shared; /* queue in session shared memory */
+ struct inproc_sync *inproc_sync; /* in-process synchronization object */
+ int in_inproc_wait; /* are we in a client-side wait? */
};
struct hotkey
@@ -150,6 +152,7 @@ static int msg_queue_add_queue( struct object *obj, struct wait_queue_entry *ent
static void msg_queue_remove_queue( struct object *obj, struct wait_queue_entry *entry );
static int msg_queue_signaled( struct object *obj, struct wait_queue_entry *entry );
static void msg_queue_satisfied( struct object *obj, struct wait_queue_entry *entry );
+static struct inproc_sync *msg_queue_get_inproc_sync( struct object *obj );
static void msg_queue_destroy( struct object *obj );
static void msg_queue_poll_event( struct fd *fd, int event );
static void thread_input_dump( struct object *obj, int verbose );
@@ -176,6 +179,7 @@ static const struct object_ops msg_queue_ops =
NULL, /* unlink_name */
no_open_file, /* open_file */
no_kernel_obj_list, /* get_kernel_obj_list */
+ msg_queue_get_inproc_sync, /* get_inproc_sync */
no_close_handle, /* close_handle */
msg_queue_destroy /* destroy */
};
@@ -213,6 +217,7 @@ static const struct object_ops thread_input_ops =
NULL, /* unlink_name */
no_open_file, /* open_file */
no_kernel_obj_list, /* get_kernel_obj_list */
+ no_get_inproc_sync, /* get_inproc_sync */
no_close_handle, /* close_handle */
thread_input_destroy /* destroy */
};
@@ -312,6 +317,8 @@ static struct msg_queue *create_msg_queue( struct thread *thread, struct thread_
queue->hooks = NULL;
queue->last_get_msg = current_time;
queue->keystate_lock = 0;
+ queue->inproc_sync = NULL;
+ queue->in_inproc_wait = 0;
list_init( &queue->send_result );
list_init( &queue->callback_result );
list_init( &queue->pending_timers );
@@ -728,7 +735,11 @@ static inline void set_queue_bits( struct msg_queue *queue, unsigned int bits )
}
SHARED_WRITE_END;
- if (is_signaled( queue )) wake_up( &queue->obj, 0 );
+ if (is_signaled( queue ))
+ {
+ wake_up( &queue->obj, 0 );
+ set_inproc_event( queue->inproc_sync );
+ }
}
/* clear some queue bits */
@@ -748,6 +759,8 @@ static inline void clear_queue_bits( struct msg_queue *queue, unsigned int bits
if (queue->keystate_lock) unlock_input_keystate( queue->input );
queue->keystate_lock = 0;
}
+ if (!is_signaled( queue ))
+ reset_inproc_event( queue->inproc_sync );
}
/* check if message is matched by the filter */
@@ -1228,6 +1241,10 @@ static int is_queue_hung( struct msg_queue *queue )
if (get_wait_queue_thread(entry)->queue == queue)
return 0; /* thread is waiting on queue -> not hung */
}
+
+ if (queue->in_inproc_wait)
+ return 0; /* thread is waiting on queue in absentia -> not hung */
+
return 1;
}
@@ -1293,6 +1310,17 @@ static void msg_queue_satisfied( struct object *obj, struct wait_queue_entry *en
shared->changed_mask = 0;
}
SHARED_WRITE_END;
+ reset_inproc_event( queue->inproc_sync );
+}
+
+static struct inproc_sync *msg_queue_get_inproc_sync( struct object *obj )
+{
+ struct msg_queue *queue = (struct msg_queue *)obj;
+
+ if (!queue->inproc_sync)
+ queue->inproc_sync = create_inproc_event( INPROC_SYNC_QUEUE, is_signaled( queue ) );
+ if (queue->inproc_sync) grab_object( queue->inproc_sync );
+ return queue->inproc_sync;
}
static void msg_queue_destroy( struct object *obj )
@@ -1338,6 +1366,7 @@ static void msg_queue_destroy( struct object *obj )
if (queue->hooks) release_object( queue->hooks );
if (queue->fd) release_object( queue->fd );
if (queue->shared) free_shared_object( queue->shared );
+ if (queue->inproc_sync) release_object( queue->inproc_sync );
}
static void msg_queue_poll_event( struct fd *fd, int event )
@@ -1348,6 +1377,7 @@ static void msg_queue_poll_event( struct fd *fd, int event )
if (event & (POLLERR | POLLHUP)) set_fd_events( fd, -1 );
else set_fd_events( queue->fd, 0 );
wake_up( &queue->obj, 0 );
+ set_inproc_event( queue->inproc_sync );
}
static void thread_input_dump( struct object *obj, int verbose )
@@ -3124,7 +3154,15 @@ DECL_HANDLER(set_queue_mask)
}
SHARED_WRITE_END;
}
- else wake_up( &queue->obj, 0 );
+ else
+ {
+ wake_up( &queue->obj, 0 );
+ set_inproc_event( queue->inproc_sync );
+ }
+ }
+ else
+ {
+ reset_inproc_event( queue->inproc_sync );
}
}
}
@@ -3146,6 +3184,9 @@ DECL_HANDLER(get_queue_status)
shared->changed_bits &= ~req->clear_bits;
}
SHARED_WRITE_END;
+
+ if (!is_signaled( queue ))
+ reset_inproc_event( queue->inproc_sync );
}
else reply->wake_bits = reply->changed_bits = 0;
}
@@ -3343,6 +3384,9 @@ DECL_HANDLER(get_message)
}
SHARED_WRITE_END;
+ if (!is_signaled( queue ))
+ reset_inproc_event( queue->inproc_sync );
+
/* then check for posted messages */
if ((filter & QS_POSTMESSAGE) &&
get_posted_message( queue, get_win, req->get_first, req->get_last, req->flags, reply ))
@@ -3402,6 +3446,7 @@ DECL_HANDLER(get_message)
}
SHARED_WRITE_END;
+ reset_inproc_event( queue->inproc_sync );
set_error( STATUS_PENDING ); /* FIXME */
}
@@ -4195,3 +4240,42 @@ DECL_HANDLER(set_keyboard_repeat)
release_object( desktop );
}
+
+DECL_HANDLER(select_inproc_queue)
+{
+ struct msg_queue *queue = current->queue;
+
+ if (queue->in_inproc_wait)
+ {
+ set_error( STATUS_ACCESS_DENIED );
+ }
+ else
+ {
+ check_thread_queue_idle( current );
+
+ if (queue->fd)
+ set_fd_events( queue->fd, POLLIN );
+
+ queue->in_inproc_wait = 1;
+ }
+}
+
+DECL_HANDLER(unselect_inproc_queue)
+{
+ struct msg_queue *queue = current->queue;
+
+ if (!queue->in_inproc_wait)
+ {
+ set_error( STATUS_ACCESS_DENIED );
+ }
+ else
+ {
+ if (queue->fd)
+ set_fd_events( queue->fd, 0 );
+
+ if (req->signaled)
+ msg_queue_satisfied( &queue->obj, NULL );
+
+ queue->in_inproc_wait = 0;
+ }
+}
diff --git a/server/registry.c b/server/registry.c
index c60c737feffca0180f89026f76887b247d601ba4..8aaf105d70fc2aa1c8af125dec83683236f4c450 100644
--- a/server/registry.c
+++ b/server/registry.c
@@ -192,6 +192,7 @@ static const struct object_ops key_ops =
key_unlink_name, /* unlink_name */
no_open_file, /* open_file */
no_kernel_obj_list, /* get_kernel_obj_list */
+ no_get_inproc_sync, /* get_inproc_sync */
key_close_handle, /* close_handle */
key_destroy /* destroy */
};
diff --git a/server/request.c b/server/request.c
index 2254315b79e58e46eb9128e4b27d12f3598a2d15..24a4f779e68e3e50148f94517b99d4a4b41ae866 100644
--- a/server/request.c
+++ b/server/request.c
@@ -101,6 +101,7 @@ static const struct object_ops master_socket_ops =
NULL, /* unlink_name */
no_open_file, /* open_file */
no_kernel_obj_list, /* get_kernel_obj_list */
+ no_get_inproc_sync, /* get_inproc_sync */
no_close_handle, /* close_handle */
master_socket_destroy /* destroy */
};
diff --git a/server/request_handlers.h b/server/request_handlers.h
index fd39842df564ed7d6c6b7362a1d98d4955646064..417eee55cfbd58e6d83be6a994dda06685e2a564 100644
--- a/server/request_handlers.h
+++ b/server/request_handlers.h
@@ -299,6 +299,11 @@ DECL_HANDLER(suspend_process);
DECL_HANDLER(resume_process);
DECL_HANDLER(get_next_thread);
DECL_HANDLER(set_keyboard_repeat);
+DECL_HANDLER(get_linux_sync_device);
+DECL_HANDLER(get_linux_sync_obj);
+DECL_HANDLER(select_inproc_queue);
+DECL_HANDLER(unselect_inproc_queue);
+DECL_HANDLER(get_inproc_alert_event);
typedef void (*req_handler)( const void *req, void *reply );
static const req_handler req_handlers[REQ_NB_REQUESTS] =
@@ -595,6 +600,11 @@ static const req_handler req_handlers[REQ_NB_REQUESTS] =
(req_handler)req_resume_process,
(req_handler)req_get_next_thread,
(req_handler)req_set_keyboard_repeat,
+ (req_handler)req_get_linux_sync_device,
+ (req_handler)req_get_linux_sync_obj,
+ (req_handler)req_select_inproc_queue,
+ (req_handler)req_unselect_inproc_queue,
+ (req_handler)req_get_inproc_alert_event,
};
C_ASSERT( sizeof(abstime_t) == 8 );
@@ -2270,3 +2280,18 @@ C_ASSERT( offsetof(struct set_keyboard_repeat_request, period) == 20 );
C_ASSERT( sizeof(struct set_keyboard_repeat_request) == 24 );
C_ASSERT( offsetof(struct set_keyboard_repeat_reply, enable) == 8 );
C_ASSERT( sizeof(struct set_keyboard_repeat_reply) == 16 );
+C_ASSERT( sizeof(struct get_linux_sync_device_request) == 16 );
+C_ASSERT( offsetof(struct get_linux_sync_device_reply, handle) == 8 );
+C_ASSERT( sizeof(struct get_linux_sync_device_reply) == 16 );
+C_ASSERT( offsetof(struct get_linux_sync_obj_request, handle) == 12 );
+C_ASSERT( sizeof(struct get_linux_sync_obj_request) == 16 );
+C_ASSERT( offsetof(struct get_linux_sync_obj_reply, handle) == 8 );
+C_ASSERT( offsetof(struct get_linux_sync_obj_reply, type) == 12 );
+C_ASSERT( offsetof(struct get_linux_sync_obj_reply, access) == 16 );
+C_ASSERT( sizeof(struct get_linux_sync_obj_reply) == 24 );
+C_ASSERT( sizeof(struct select_inproc_queue_request) == 16 );
+C_ASSERT( offsetof(struct unselect_inproc_queue_request, signaled) == 12 );
+C_ASSERT( sizeof(struct unselect_inproc_queue_request) == 16 );
+C_ASSERT( sizeof(struct get_inproc_alert_event_request) == 16 );
+C_ASSERT( offsetof(struct get_inproc_alert_event_reply, handle) == 8 );
+C_ASSERT( sizeof(struct get_inproc_alert_event_reply) == 16 );
diff --git a/server/request_trace.h b/server/request_trace.h
index 6e38f2a8379d4337cb148c19b47a14a3af4e163a..dc0db9466c3d9df9a3e96841d32790b778754fb8 100644
--- a/server/request_trace.h
+++ b/server/request_trace.h
@@ -3344,6 +3344,45 @@ static void dump_set_keyboard_repeat_reply( const struct set_keyboard_repeat_rep
fprintf( stderr, " enable=%d", req->enable );
}
+static void dump_get_linux_sync_device_request( const struct get_linux_sync_device_request *req )
+{
+}
+
+static void dump_get_linux_sync_device_reply( const struct get_linux_sync_device_reply *req )
+{
+ fprintf( stderr, " handle=%04x", req->handle );
+}
+
+static void dump_get_linux_sync_obj_request( const struct get_linux_sync_obj_request *req )
+{
+ fprintf( stderr, " handle=%04x", req->handle );
+}
+
+static void dump_get_linux_sync_obj_reply( const struct get_linux_sync_obj_reply *req )
+{
+ fprintf( stderr, " handle=%04x", req->handle );
+ fprintf( stderr, ", type=%d", req->type );
+ fprintf( stderr, ", access=%08x", req->access );
+}
+
+static void dump_select_inproc_queue_request( const struct select_inproc_queue_request *req )
+{
+}
+
+static void dump_unselect_inproc_queue_request( const struct unselect_inproc_queue_request *req )
+{
+ fprintf( stderr, " signaled=%d", req->signaled );
+}
+
+static void dump_get_inproc_alert_event_request( const struct get_inproc_alert_event_request *req )
+{
+}
+
+static void dump_get_inproc_alert_event_reply( const struct get_inproc_alert_event_reply *req )
+{
+ fprintf( stderr, " handle=%04x", req->handle );
+}
+
typedef void (*dump_func)( const void *req );
static const dump_func req_dumpers[REQ_NB_REQUESTS] =
@@ -3640,6 +3679,11 @@ static const dump_func req_dumpers[REQ_NB_REQUESTS] =
(dump_func)dump_resume_process_request,
(dump_func)dump_get_next_thread_request,
(dump_func)dump_set_keyboard_repeat_request,
+ (dump_func)dump_get_linux_sync_device_request,
+ (dump_func)dump_get_linux_sync_obj_request,
+ (dump_func)dump_select_inproc_queue_request,
+ (dump_func)dump_unselect_inproc_queue_request,
+ (dump_func)dump_get_inproc_alert_event_request,
};
static const dump_func reply_dumpers[REQ_NB_REQUESTS] =
@@ -3936,6 +3980,11 @@ static const dump_func reply_dumpers[REQ_NB_REQUESTS] =
NULL,
(dump_func)dump_get_next_thread_reply,
(dump_func)dump_set_keyboard_repeat_reply,
+ (dump_func)dump_get_linux_sync_device_reply,
+ (dump_func)dump_get_linux_sync_obj_reply,
+ NULL,
+ NULL,
+ (dump_func)dump_get_inproc_alert_event_reply,
};
static const char * const req_names[REQ_NB_REQUESTS] =
@@ -4232,6 +4281,11 @@ static const char * const req_names[REQ_NB_REQUESTS] =
"resume_process",
"get_next_thread",
"set_keyboard_repeat",
+ "get_linux_sync_device",
+ "get_linux_sync_obj",
+ "select_inproc_queue",
+ "unselect_inproc_queue",
+ "get_inproc_alert_event",
};
static const struct
diff --git a/server/semaphore.c b/server/semaphore.c
index 53b42a886df5a087f76ca6aad41f642a4e5a1bc1..bff55b1a3086bab35735a45db3be634b217c6194 100644
--- a/server/semaphore.c
+++ b/server/semaphore.c
@@ -55,12 +55,15 @@ struct semaphore
struct object obj; /* object header */
unsigned int count; /* current count */
unsigned int max; /* maximum possible count */
+ struct inproc_sync *inproc_sync; /* fast synchronization object */
};
static void semaphore_dump( struct object *obj, int verbose );
static int semaphore_signaled( struct object *obj, struct wait_queue_entry *entry );
static void semaphore_satisfied( struct object *obj, struct wait_queue_entry *entry );
static int semaphore_signal( struct object *obj, unsigned int access );
+static struct inproc_sync *semaphore_get_inproc_sync( struct object *obj );
+static void semaphore_destroy( struct object *obj );
static const struct object_ops semaphore_ops =
{
@@ -82,8 +85,9 @@ static const struct object_ops semaphore_ops =
default_unlink_name, /* unlink_name */
no_open_file, /* open_file */
no_kernel_obj_list, /* get_kernel_obj_list */
+ semaphore_get_inproc_sync, /* get_inproc_sync */
no_close_handle, /* close_handle */
- no_destroy /* destroy */
+ semaphore_destroy /* destroy */
};
@@ -105,6 +109,7 @@ static struct semaphore *create_semaphore( struct object *root, const struct uni
/* initialize it if it didn't already exist */
sem->count = initial;
sem->max = max;
+ sem->inproc_sync = NULL;
}
}
return sem;
@@ -167,6 +172,27 @@ static int semaphore_signal( struct object *obj, unsigned int access )
return release_semaphore( sem, 1, NULL );
}
+static struct inproc_sync *semaphore_get_inproc_sync( struct object *obj )
+{
+ struct semaphore *semaphore = (struct semaphore *)obj;
+
+ /* This state will always be the state that the semaphore was created with.
+ * We could create the inproc_sync at creation time to make this clearer,
+ * but some broken programs create hundreds of thousands of handles which
+ * they never use, and we want to avoid wasting memory or fds in that case. */
+ if (!semaphore->inproc_sync)
+ semaphore->inproc_sync = create_inproc_semaphore( semaphore->count, semaphore->max );
+ if (semaphore->inproc_sync) grab_object( semaphore->inproc_sync );
+ return semaphore->inproc_sync;
+}
+
+static void semaphore_destroy( struct object *obj )
+{
+ struct semaphore *semaphore = (struct semaphore *)obj;
+
+ if (semaphore->inproc_sync) release_object( semaphore->inproc_sync );
+}
+
/* create a semaphore */
DECL_HANDLER(create_semaphore)
{
diff --git a/server/serial.c b/server/serial.c
index 209f2e9174e29c677b3ce5a7442b6a3625b330d2..93753328d4dc31447b2f961177a7ba1483c81650 100644
--- a/server/serial.c
+++ b/server/serial.c
@@ -103,6 +103,7 @@ static const struct object_ops serial_ops =
NULL, /* unlink_name */
no_open_file, /* open_file */
no_kernel_obj_list, /* get_kernel_obj_list */
+ default_fd_get_inproc_sync, /* get_inproc_sync */
no_close_handle, /* close_handle */
serial_destroy /* destroy */
};
diff --git a/server/signal.c b/server/signal.c
index 19b76d44c16947036fe53b535bb5eb6a793d6a4f..85d16baa98caf4167292af3347f7a9a7f593a777 100644
--- a/server/signal.c
+++ b/server/signal.c
@@ -74,6 +74,7 @@ static const struct object_ops handler_ops =
NULL, /* unlink_name */
no_open_file, /* open_file */
no_kernel_obj_list, /* get_kernel_obj_list */
+ no_get_inproc_sync, /* get_inproc_sync */
no_close_handle, /* close_handle */
handler_destroy /* destroy */
};
diff --git a/server/sock.c b/server/sock.c
index 32d622aaebc626e61641623436b47c3df71c6e08..09366ea7dde9f923d99d9913d4b991016a148c11 100644
--- a/server/sock.c
+++ b/server/sock.c
@@ -483,6 +483,7 @@ static const struct object_ops sock_ops =
NULL, /* unlink_name */
no_open_file, /* open_file */
no_kernel_obj_list, /* get_kernel_obj_list */
+ default_fd_get_inproc_sync, /* get_inproc_sync */
sock_close_handle, /* close_handle */
sock_destroy /* destroy */
};
@@ -3611,6 +3612,7 @@ static const struct object_ops ifchange_ops =
NULL, /* unlink_name */
no_open_file, /* open_file */
no_kernel_obj_list, /* get_kernel_obj_list */
+ no_get_inproc_sync, /* get_inproc_sync */
no_close_handle, /* close_handle */
ifchange_destroy /* destroy */
};
@@ -3832,6 +3834,7 @@ static const struct object_ops socket_device_ops =
default_unlink_name, /* unlink_name */
socket_device_open_file, /* open_file */
no_kernel_obj_list, /* get_kernel_obj_list */
+ no_get_inproc_sync, /* get_inproc_sync */
no_close_handle, /* close_handle */
no_destroy /* destroy */
};
diff --git a/server/symlink.c b/server/symlink.c
index 74b60162c0152ed60cf1bc61dd0737f940a4e941..f0314f2ee9058f5c2fb44cef0f18e05db9b179ba 100644
--- a/server/symlink.c
+++ b/server/symlink.c
@@ -83,6 +83,7 @@ static const struct object_ops symlink_ops =
default_unlink_name, /* unlink_name */
no_open_file, /* open_file */
no_kernel_obj_list, /* get_kernel_obj_list */
+ no_get_inproc_sync, /* get_inproc_sync */
no_close_handle, /* close_handle */
symlink_destroy /* destroy */
};
diff --git a/server/thread.c b/server/thread.c
index 3c7e4541a096233c04b4630e20b89cda73885abf..f8ad398277e00eafc544556b913a3adf659c3df8 100644
--- a/server/thread.c
+++ b/server/thread.c
@@ -111,6 +111,7 @@ static const struct object_ops thread_apc_ops =
NULL, /* unlink_name */
no_open_file, /* open_file */
no_kernel_obj_list, /* get_kernel_obj_list */
+ no_get_inproc_sync, /* get_inproc_sync */
no_close_handle, /* close_handle */
thread_apc_destroy /* destroy */
};
@@ -153,6 +154,7 @@ static const struct object_ops context_ops =
NULL, /* unlink_name */
no_open_file, /* open_file */
no_kernel_obj_list, /* get_kernel_obj_list */
+ no_get_inproc_sync, /* get_inproc_sync */
no_close_handle, /* close_handle */
no_destroy /* destroy */
};
@@ -180,6 +182,7 @@ static int thread_signaled( struct object *obj, struct wait_queue_entry *entry )
static unsigned int thread_map_access( struct object *obj, unsigned int access );
static void thread_poll_event( struct fd *fd, int event );
static struct list *thread_get_kernel_obj_list( struct object *obj );
+static struct inproc_sync *thread_get_inproc_sync( struct object *obj );
static void destroy_thread( struct object *obj );
static const struct object_ops thread_ops =
@@ -202,6 +205,7 @@ static const struct object_ops thread_ops =
NULL, /* unlink_name */
no_open_file, /* open_file */
thread_get_kernel_obj_list, /* get_kernel_obj_list */
+ thread_get_inproc_sync, /* get_inproc_sync */
no_close_handle, /* close_handle */
destroy_thread /* destroy */
};
@@ -294,6 +298,8 @@ static inline void init_thread_structure( struct thread *thread )
thread->token = NULL;
thread->desc = NULL;
thread->desc_len = 0;
+ thread->inproc_sync = NULL;
+ thread->inproc_alert_event = NULL;
thread->creation_time = current_time;
thread->exit_time = 0;
@@ -445,6 +451,16 @@ static struct list *thread_get_kernel_obj_list( struct object *obj )
return &thread->kernel_object;
}
+static struct inproc_sync *thread_get_inproc_sync( struct object *obj )
+{
+ struct thread *thread = (struct thread *)obj;
+
+ if (!thread->inproc_sync)
+ thread->inproc_sync = create_inproc_event( INPROC_SYNC_MANUAL_SERVER, thread->state == TERMINATED );
+ if (thread->inproc_sync) grab_object( thread->inproc_sync );
+ return thread->inproc_sync;
+}
+
/* cleanup everything that is no longer needed by a dead thread */
/* used by destroy_thread and kill_thread */
static void cleanup_thread( struct thread *thread )
@@ -500,6 +516,8 @@ static void destroy_thread( struct object *obj )
release_object( thread->process );
if (thread->id) free_ptid( thread->id );
if (thread->token) release_object( thread->token );
+ if (thread->inproc_sync) release_object( thread->inproc_sync );
+ if (thread->inproc_alert_event) release_object( thread->inproc_alert_event );
}
/* dump a thread on stdout for debugging purposes */
@@ -1221,8 +1239,13 @@ static int queue_apc( struct process *process, struct thread *thread, struct thr
grab_object( apc );
list_add_tail( queue, &apc->entry );
if (!list_prev( queue, &apc->entry )) /* first one */
+ {
wake_thread( thread );
+ if (apc->call.type == APC_USER && thread->inproc_alert_event)
+ set_event( thread->inproc_alert_event );
+ }
+
return 1;
}
@@ -1253,6 +1276,8 @@ void thread_cancel_apc( struct thread *thread, struct object *owner, enum apc_ty
apc->executed = 1;
wake_up( &apc->obj, 0 );
release_object( apc );
+ if (list_empty( &thread->user_apc ) && thread->inproc_alert_event)
+ reset_event( thread->inproc_alert_event );
return;
}
}
@@ -1267,6 +1292,9 @@ static struct thread_apc *thread_dequeue_apc( struct thread *thread, int system
{
apc = LIST_ENTRY( ptr, struct thread_apc, entry );
list_remove( ptr );
+
+ if (list_empty( &thread->user_apc ) && thread->inproc_alert_event)
+ reset_event( thread->inproc_alert_event );
}
return apc;
}
@@ -1364,6 +1392,7 @@ void kill_thread( struct thread *thread, int violent_death )
kill_console_processes( thread, 0 );
abandon_mutexes( thread );
wake_up( &thread->obj, 0 );
+ set_inproc_event( thread->inproc_sync );
if (violent_death) send_thread_signal( thread, SIGQUIT );
cleanup_thread( thread );
remove_process_thread( thread->process, thread );
@@ -2110,3 +2139,12 @@ DECL_HANDLER(get_next_thread)
set_error( STATUS_NO_MORE_ENTRIES );
release_object( process );
}
+
+DECL_HANDLER(get_inproc_alert_event)
+{
+ if (!current->inproc_alert_event)
+ current->inproc_alert_event = create_event( NULL, NULL, 0, 1, !list_empty( ¤t->user_apc ), NULL );
+
+ if (current->inproc_alert_event)
+ reply->handle = alloc_handle( current->process, current->inproc_alert_event, SYNCHRONIZE, 0 );
+}
diff --git a/server/thread.h b/server/thread.h
index 2b256be322f1c538b905639f2996b461c56e845b..4641ad0fe5a2eab41821f019426f56b692f68edd 100644
--- a/server/thread.h
+++ b/server/thread.h
@@ -93,6 +93,8 @@ struct thread
data_size_t desc_len; /* thread description length in bytes */
WCHAR *desc; /* thread description string */
struct completion_wait *completion_wait; /* completion port wait object the thread is associated with */
+ struct inproc_sync *inproc_sync; /* in-process synchronization object */
+ struct event *inproc_alert_event; /* in-process synchronization alert event */
};
extern struct thread *current;
diff --git a/server/timer.c b/server/timer.c
index b0b6ec8153510326fa02d512a3bb47f2c593221a..08aa62292f0aba4ca4f810a8d0ba8b3070e0f7c6 100644
--- a/server/timer.c
+++ b/server/timer.c
@@ -52,20 +52,22 @@ struct type_descr timer_type =
struct timer
{
- struct object obj; /* object header */
- int manual; /* manual reset */
- int signaled; /* current signaled state */
- unsigned int period; /* timer period in ms */
- abstime_t when; /* next expiration */
- struct timeout_user *timeout; /* timeout user */
- struct thread *thread; /* thread that set the APC function */
- client_ptr_t callback; /* callback APC function */
- client_ptr_t arg; /* callback argument */
+ struct object obj; /* object header */
+ int manual; /* manual reset */
+ int signaled; /* current signaled state */
+ unsigned int period; /* timer period in ms */
+ abstime_t when; /* next expiration */
+ struct timeout_user *timeout; /* timeout user */
+ struct thread *thread; /* thread that set the APC function */
+ client_ptr_t callback; /* callback APC function */
+ client_ptr_t arg; /* callback argument */
+ struct inproc_sync *inproc_sync; /* in-process synchronization object */
};
static void timer_dump( struct object *obj, int verbose );
static int timer_signaled( struct object *obj, struct wait_queue_entry *entry );
static void timer_satisfied( struct object *obj, struct wait_queue_entry *entry );
+static struct inproc_sync *timer_get_inproc_sync( struct object *obj );
static void timer_destroy( struct object *obj );
static const struct object_ops timer_ops =
@@ -88,6 +90,7 @@ static const struct object_ops timer_ops =
default_unlink_name, /* unlink_name */
no_open_file, /* open_file */
no_kernel_obj_list, /* get_kernel_obj_list */
+ timer_get_inproc_sync, /* get_inproc_sync */
no_close_handle, /* close_handle */
timer_destroy /* destroy */
};
@@ -110,6 +113,7 @@ static struct timer *create_timer( struct object *root, const struct unicode_str
timer->period = 0;
timer->timeout = NULL;
timer->thread = NULL;
+ timer->inproc_sync = NULL;
}
}
return timer;
@@ -151,6 +155,7 @@ static void timer_callback( void *private )
/* wake up waiters */
timer->signaled = 1;
wake_up( &timer->obj, 0 );
+ set_inproc_event( timer->inproc_sync );
}
/* cancel a running timer */
@@ -181,6 +186,7 @@ static int set_timer( struct timer *timer, timeout_t expire, unsigned int period
{
period = 0; /* period doesn't make any sense for a manual timer */
timer->signaled = 0;
+ reset_inproc_event( timer->inproc_sync );
}
timer->when = (expire <= 0) ? expire - monotonic_time : max( expire, current_time );
timer->period = period;
@@ -215,6 +221,19 @@ static void timer_satisfied( struct object *obj, struct wait_queue_entry *entry
if (!timer->manual) timer->signaled = 0;
}
+static struct inproc_sync *timer_get_inproc_sync( struct object *obj )
+{
+ struct timer *timer = (struct timer *)obj;
+
+ if (!timer->inproc_sync)
+ {
+ enum inproc_sync_type type = timer->manual ? INPROC_SYNC_MANUAL_SERVER : INPROC_SYNC_AUTO_SERVER;
+ timer->inproc_sync = create_inproc_event( type, timer->signaled );
+ }
+ if (timer->inproc_sync) grab_object( timer->inproc_sync );
+ return timer->inproc_sync;
+}
+
static void timer_destroy( struct object *obj )
{
struct timer *timer = (struct timer *)obj;
@@ -222,6 +241,7 @@ static void timer_destroy( struct object *obj )
if (timer->timeout) remove_timeout_user( timer->timeout );
if (timer->thread) release_object( timer->thread );
+ if (timer->inproc_sync) release_object( timer->inproc_sync );
}
/* create a timer */
diff --git a/server/token.c b/server/token.c
index 7e20c670a166baf6bce1dfe1e8753525eaeedb56..844ad2814f47cffb157a8ec3e9e2149022ab98d6 100644
--- a/server/token.c
+++ b/server/token.c
@@ -157,6 +157,7 @@ static const struct object_ops token_ops =
NULL, /* unlink_name */
no_open_file, /* open_file */
no_kernel_obj_list, /* get_kernel_obj_list */
+ no_get_inproc_sync, /* get_inproc_sync */
no_close_handle, /* close_handle */
token_destroy /* destroy */
};
diff --git a/server/window.c b/server/window.c
index f7f9d5e517f98cd464b3a03e2e985935e40059d2..32afdf82c2971016d58746f685f259b975c7d424 100644
--- a/server/window.c
+++ b/server/window.c
@@ -119,6 +119,7 @@ static const struct object_ops window_ops =
NULL, /* unlink_name */
no_open_file, /* open_file */
no_kernel_obj_list, /* get_kernel_obj_list */
+ no_get_inproc_sync, /* get_inproc_sync */
no_close_handle, /* close_handle */
window_destroy /* destroy */
};
diff --git a/server/winstation.c b/server/winstation.c
index b3746090ccf8836e7217350dea9cacf2ff45e59c..4b8a49729717371d5d8644265a99bcca28cb1108 100644
--- a/server/winstation.c
+++ b/server/winstation.c
@@ -88,6 +88,7 @@ static const struct object_ops winstation_ops =
default_unlink_name, /* unlink_name */
no_open_file, /* open_file */
no_kernel_obj_list, /* get_kernel_obj_list */
+ no_get_inproc_sync, /* get_inproc_sync */
winstation_close_handle, /* close_handle */
winstation_destroy /* destroy */
};
@@ -128,6 +129,7 @@ static const struct object_ops desktop_ops =
default_unlink_name, /* unlink_name */
no_open_file, /* open_file */
no_kernel_obj_list, /* get_kernel_obj_list */
+ no_get_inproc_sync, /* get_inproc_sync */
desktop_close_handle, /* close_handle */
desktop_destroy /* destroy */
};