File ntsync-staging.patch of Package wine

This is the wip/ntsync branch from https://gitlab.winehq.org/rbernon/wine/, + a make_requests run to make it compile.
Slightly touched to work with staging.

diff --git a/configure.ac b/configure.ac
index 4436fa6..ca8a26d 100644
--- a/configure.ac
+++ b/configure.ac
@@ -682,6 +682,7 @@ AC_CHECK_HEADERS(\
 	linux/input.h \
 	linux/ioctl.h \
 	linux/major.h \
+	linux/ntsync.h \
 	linux/param.h \
 	linux/seccomp.h \
 	linux/serial.h \
diff --git a/dlls/ntdll/unix/server.c b/dlls/ntdll/unix/server.c
index 1aa0d5c..95ade27 100644
--- a/dlls/ntdll/unix/server.c
+++ b/dlls/ntdll/unix/server.c
@@ -124,7 +124,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 )
@@ -838,6 +838,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.@)
  */
@@ -981,7 +996,7 @@ void wine_server_send_fd( int fd )
  *
  * Receive a file descriptor passed from the server.
  */
-static int receive_fd( obj_handle_t *handle )
+int wine_server_receive_fd( obj_handle_t *handle )
 {
     struct iovec vec;
     struct msghdr msghdr;
@@ -1176,7 +1191,7 @@ int server_get_unix_fd( HANDLE handle, unsigned int wanted_access, int *unix_fd,
                 if (type) *type = reply->type;
                 if (options) *options = reply->options;
                 access = reply->access;
-                if ((fd = receive_fd( &fd_handle )) != -1)
+                if ((fd = wine_server_receive_fd( &fd_handle )) != -1)
                 {
                     assert( wine_server_ptr_handle(fd_handle) == handle );
                     *needs_close = (!reply->cacheable ||
@@ -1613,6 +1628,7 @@ size_t server_init_process(void)
 {
     const char *arch = getenv( "WINEARCH" );
     const char *env_socket = getenv( "WINESERVERSOCKET" );
+    struct ntdll_thread_data *data = ntdll_get_thread_data();
     obj_handle_t version;
     unsigned int i;
     int ret, reply_pipe;
@@ -1652,7 +1668,7 @@ size_t server_init_process(void)
     pthread_sigmask( SIG_BLOCK, &server_block_set, NULL );
 
     /* receive the first thread request fd on the main socket */
-    ntdll_get_thread_data()->request_fd = receive_fd( &version );
+    data->request_fd = wine_server_receive_fd( &version );
 
 #ifdef SO_PASSCRED
     /* now that we hopefully received the server_pid, disable SO_PASSCRED */
@@ -1687,16 +1703,29 @@ size_t server_init_process(void)
         req->unix_pid    = getpid();
         req->unix_tid    = get_unix_tid();
         req->reply_fd    = reply_pipe;
-        req->wait_fd     = ntdll_get_thread_data()->wait_fd[1];
+        req->wait_fd     = data->wait_fd[1];
         req->debug_level = (TRACE_ON(server) != 0);
         wine_server_set_reply( req, supported_machines, sizeof(supported_machines) );
-        ret = wine_server_call( req );
-        pid               = reply->pid;
-        tid               = reply->tid;
-        peb->SessionId    = reply->session_id;
-        info_size         = reply->info_size;
-        server_start_time = reply->server_start;
-        supported_machines_count = wine_server_reply_size( reply ) / sizeof(*supported_machines);
+        if (!(ret = wine_server_call( req )))
+        {
+            obj_handle_t handle;
+            pid               = reply->pid;
+            tid               = reply->tid;
+            peb->SessionId    = reply->session_id;
+            info_size         = reply->info_size;
+            server_start_time = reply->server_start;
+            supported_machines_count = wine_server_reply_size( reply ) / sizeof(*supported_machines);
+            if (reply->inproc_device)
+            {
+                inproc_device_fd = wine_server_receive_fd( &handle );
+                assert( handle == reply->inproc_device );
+            }
+            if (reply->alert_handle)
+            {
+                data->alert_fd = wine_server_receive_fd( &handle );
+                assert( handle == reply->alert_handle );
+            }
+        }
     }
     SERVER_END_REQ;
     close( reply_pipe );
@@ -1787,25 +1816,42 @@ void server_init_process_done(void)
  *
  * Send an init thread request.
  */
-void server_init_thread( void *entry_point, BOOL *suspend )
+void server_init_thread( struct ntdll_thread_data *data, BOOL *suspend )
 {
+    sigset_t sigset;
     void *teb;
     int reply_pipe = init_thread_pipe();
 
     /* always send the native TEB */
     if (!(teb = NtCurrentTeb64())) teb = NtCurrentTeb();
 
+    /* We need to use fd_cache_mutex here to protect against races with
+     * other threads trying to receive fds for the fd cache,
+     * and we need to use an uninterrupted section to prevent reentrancy. */
+    server_enter_uninterrupted_section( &fd_cache_mutex, &sigset );
+
     SERVER_START_REQ( init_thread )
     {
         req->unix_tid  = get_unix_tid();
         req->teb       = wine_server_client_ptr( teb );
-        req->entry     = wine_server_client_ptr( entry_point );
+        req->entry     = wine_server_client_ptr( data->start );
         req->reply_fd  = reply_pipe;
         req->wait_fd   = ntdll_get_thread_data()->wait_fd[1];
-        wine_server_call( req );
+        if (!wine_server_call( req ))
+        {
+            obj_handle_t handle;
+            if (reply->alert_handle)
+            {
+                data->alert_fd = wine_server_receive_fd( &handle );
+                assert( handle == reply->alert_handle );
+            }
+        }
         *suspend = reply->suspend;
     }
     SERVER_END_REQ;
+
+    server_leave_uninterrupted_section( &fd_cache_mutex, &sigset );
+
     close( reply_pipe );
 }
 
@@ -1868,12 +1914,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( source );
+    }
 
     SERVER_START_REQ( dup_handle )
     {
@@ -1939,11 +1990,14 @@ 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( handle );
 
     SERVER_START_REQ( close_handle )
     {
diff --git a/dlls/ntdll/unix/sync.c b/dlls/ntdll/unix/sync.c
index 117915b..3b2f4ed 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,7 @@
 #endif
 #include <string.h>
 #include <stdarg.h>
+#include <stdint.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <time.h>
@@ -57,6 +60,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
@@ -70,13 +76,16 @@
 WINE_DEFAULT_DEBUG_CHANNEL(sync);
 
 HANDLE keyed_event = 0;
+int inproc_device_fd = -1;
 
 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)
 {
@@ -300,6 +309,663 @@ static unsigned int validate_open_object_attributes( const OBJECT_ATTRIBUTES *at
     return STATUS_SUCCESS;
 }
 
+#ifdef NTSYNC_IOC_EVENT_READ
+
+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 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 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 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 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 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 = args.manual ? NotificationEvent : SynchronizationEvent;
+    info->EventState = args.signaled;
+    return STATUS_SUCCESS;
+}
+
+static NTSTATUS linux_release_mutex_obj( int obj, LONG *prev_count )
+{
+    struct ntsync_mutex_args 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 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 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 = ntdll_get_thread_data()->alert_fd;
+
+    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 );
+}
+
+#else /* NTSYNC_IOC_EVENT_READ */
+
+static NTSTATUS linux_release_semaphore_obj( int obj, ULONG count, ULONG *prev_count )
+{
+    return STATUS_NOT_IMPLEMENTED;
+}
+
+static NTSTATUS linux_query_semaphore_obj( int obj, SEMAPHORE_BASIC_INFORMATION *info )
+{
+    return STATUS_NOT_IMPLEMENTED;
+}
+
+static NTSTATUS linux_set_event_obj( int obj, LONG *prev_state )
+{
+    return STATUS_NOT_IMPLEMENTED;
+}
+
+static NTSTATUS linux_reset_event_obj( int obj, LONG *prev_state )
+{
+    return STATUS_NOT_IMPLEMENTED;
+}
+
+static NTSTATUS linux_pulse_event_obj( int obj, LONG *prev_state )
+{
+    return STATUS_NOT_IMPLEMENTED;
+}
+
+static NTSTATUS linux_query_event_obj( int obj, enum inproc_sync_type type, EVENT_BASIC_INFORMATION *info )
+{
+    return STATUS_NOT_IMPLEMENTED;
+}
+
+static NTSTATUS linux_release_mutex_obj( int obj, LONG *prev_count )
+{
+    return STATUS_NOT_IMPLEMENTED;
+}
+
+static NTSTATUS linux_query_mutex_obj( int obj, MUTANT_BASIC_INFORMATION *info )
+{
+    return STATUS_NOT_IMPLEMENTED;
+}
+
+static NTSTATUS linux_wait_objs( int device, const DWORD count, const int *objs,
+                                 BOOLEAN wait_any, BOOLEAN alertable, const LARGE_INTEGER *timeout )
+{
+    return STATUS_NOT_IMPLEMENTED;
+}
+
+#endif /* NTSYNC_IOC_EVENT_READ */
+
+/* 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
+{
+    LONG refcount;
+    int fd;
+    unsigned int access;
+    unsigned int type : 2;
+    unsigned int internal : 1;
+    unsigned int queue : 1;
+    unsigned int closed : 1;
+};
+
+#define INPROC_SYNC_CACHE_BLOCK_SIZE  (65536 / sizeof(struct inproc_sync))
+#define INPROC_SYNC_CACHE_ENTRIES     128
+
+static struct inproc_sync *inproc_sync_cache[INPROC_SYNC_CACHE_ENTRIES];
+static struct inproc_sync 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_inproc_sync( HANDLE handle, struct inproc_sync *sync )
+{
+    unsigned int entry, idx = inproc_sync_handle_to_index( handle, &entry );
+    struct inproc_sync *cache;
+    int refcount;
+
+    /* don't cache pseudo-handles; waiting on them is pointless anyway */
+    if ((ULONG)(ULONG_PTR)handle > 0xfffffffa) return sync;
+
+    if (entry >= INPROC_SYNC_CACHE_ENTRIES)
+    {
+        FIXME( "too many allocated handles, not caching %p\n", handle );
+        return sync;
+    }
+
+    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);
+            void *ptr = anon_mmap_alloc( size, PROT_READ | PROT_WRITE );
+            if (ptr == MAP_FAILED) return sync;
+            if (InterlockedCompareExchangePointer( (void **)&inproc_sync_cache[entry], ptr, NULL ))
+                munmap( ptr, size ); /* someone beat us to it */
+        }
+    }
+
+    cache = &inproc_sync_cache[entry][idx];
+
+    if (InterlockedCompareExchange( &cache->refcount, 0, 0 ))
+    {
+        /* The handle is currently being used for another object (i.e. it was
+         * closed and then reused, but some thread is waiting on the old handle
+         * or otherwise simultaneously using the old object). We can't cache
+         * this object until the old one is completely destroyed. */
+        return sync;
+    }
+
+    cache->fd = sync->fd;
+    cache->access = sync->access;
+    cache->type = sync->type;
+    cache->queue = sync->queue;
+    cache->internal = sync->internal;
+    cache->closed = sync->closed;
+    /* Make sure we set the other members before the refcount; this store needs
+     * release semantics [paired with the load in get_cached_inproc_sync()].
+     * Set the refcount to 2 (one for the handle, one for the caller). */
+    refcount = InterlockedExchange( &cache->refcount, 2 );
+    assert( !refcount );
+
+    assert( sync->refcount == 1 );
+    memset( sync, 0, sizeof(*sync) );
+
+    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 void release_inproc_sync( struct inproc_sync *sync )
+{
+    /* save the fd now; as soon as the refcount hits 0 we cannot
+     * access the cache anymore */
+    int fd = sync->fd;
+    LONG refcount = InterlockedDecrement( &sync->refcount );
+
+    assert( refcount >= 0 );
+    if (!refcount) close( fd );
+}
+
+static struct inproc_sync *get_cached_inproc_sync( HANDLE handle )
+{
+    unsigned int entry, idx = inproc_sync_handle_to_index( handle, &entry );
+    struct inproc_sync *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()] */
+    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_inproc_sync( cache );
+        return NULL;
+    }
+
+    return cache;
+}
+
+/* returns a pointer to a cache entry; if the object could not be cached,
+ * returns "cache" instead, which should be allocated on stack */
+static NTSTATUS get_inproc_sync( HANDLE handle, enum inproc_sync_type desired_type, ACCESS_MASK desired_access,
+                                 struct inproc_sync *cache, struct inproc_sync **out )
+{
+    struct inproc_sync *sync;
+    sigset_t sigset;
+    NTSTATUS ret;
+
+    /* try to find it in the cache already */
+    if ((sync = get_cached_inproc_sync( handle ))) goto done;
+
+    /* We need to use fd_cache_mutex here to protect against races with
+     * other threads trying to receive fds for the fd cache,
+     * and we need to use an uninterrupted section to prevent reentrancy.
+     * We also need fd_cache_mutex to protect against the same race with
+     * NtClose, that is, to prevent the object from being cached again between
+     * close_inproc_sync() and close_handle. */
+    server_enter_uninterrupted_section( &fd_cache_mutex, &sigset );
+
+    if ((sync = get_cached_inproc_sync( handle )))
+    {
+        server_leave_uninterrupted_section( &fd_cache_mutex, &sigset );
+        goto done;
+    }
+
+    /* try to retrieve it from the server */
+    SERVER_START_REQ( get_inproc_sync_fd )
+    {
+        req->handle = wine_server_obj_handle( handle );
+        if (!(ret = wine_server_call( req )))
+        {
+            obj_handle_t fd_handle;
+            sync = cache;
+            sync->refcount = 1;
+            sync->fd = wine_server_receive_fd( &fd_handle );
+            assert( wine_server_ptr_handle(fd_handle) == handle );
+            sync->access = reply->access;
+            sync->type = reply->type;
+            sync->internal = reply->internal;
+            sync->closed = 0;
+        }
+    }
+    SERVER_END_REQ;
+
+    if (!ret) sync = cache_inproc_sync( handle, sync );
+    server_leave_uninterrupted_section( &fd_cache_mutex, &sigset );
+
+    if (ret) return ret;
+done:
+    if (desired_type != INPROC_SYNC_UNKNOWN && desired_type != sync->type)
+    {
+        release_inproc_sync( sync );
+        return STATUS_OBJECT_TYPE_MISMATCH;
+    }
+    if ((sync->access & desired_access) != desired_access)
+    {
+        release_inproc_sync( sync );
+        return STATUS_ACCESS_DENIED;
+    }
+
+    *out = sync;
+    return STATUS_SUCCESS;
+}
+
+extern unsigned int check_signal_access( struct inproc_sync *sync )
+{
+    if (sync->internal) return STATUS_OBJECT_TYPE_MISMATCH;
+
+    switch (sync->type)
+    {
+    case INPROC_SYNC_EVENT:
+        if (!(sync->access & EVENT_MODIFY_STATE)) return STATUS_ACCESS_DENIED;
+        return STATUS_SUCCESS;
+    case INPROC_SYNC_MUTEX:
+        if (!(sync->access & SYNCHRONIZE)) return STATUS_ACCESS_DENIED;
+        return STATUS_SUCCESS;
+    case INPROC_SYNC_SEMAPHORE:
+        if (!(sync->access & SEMAPHORE_MODIFY_STATE)) return STATUS_ACCESS_DENIED;
+        return STATUS_SUCCESS;
+    }
+
+    assert( 0 );
+    return STATUS_OBJECT_TYPE_MISMATCH;
+}
+
+static void select_queue(void)
+{
+    SERVER_START_REQ( select_inproc_queue )
+    {
+        req->select = 1;
+        wine_server_call( req );
+    }
+    SERVER_END_REQ;
+}
+
+static void unselect_queue( BOOL signaled )
+{
+    SERVER_START_REQ( select_inproc_queue )
+    {
+        req->signaled = signaled;
+        wine_server_call( req );
+    }
+    SERVER_END_REQ;
+}
+
+/* caller must hold fd_cache_mutex */
+void close_inproc_sync( HANDLE handle )
+{
+    struct inproc_sync *cache;
+
+    if (inproc_device_fd < 0) return;
+    if ((cache = get_cached_inproc_sync( handle )))
+    {
+        cache->closed = 1;
+        /* once for the reference we just grabbed, and once for the handle */
+        release_inproc_sync( cache );
+        release_inproc_sync( cache );
+    }
+}
+
+static NTSTATUS inproc_release_semaphore( HANDLE handle, ULONG count, ULONG *prev_count )
+{
+    struct inproc_sync cache, *sync;
+    NTSTATUS ret;
+
+    if (inproc_device_fd < 0) return STATUS_NOT_IMPLEMENTED;
+    if ((ret = get_inproc_sync( handle, INPROC_SYNC_SEMAPHORE, SEMAPHORE_MODIFY_STATE, &cache, &sync ))) return ret;
+    ret = linux_release_semaphore_obj( sync->fd, count, prev_count );
+    release_inproc_sync( sync );
+    return ret;
+}
+
+static NTSTATUS inproc_query_semaphore( HANDLE handle, SEMAPHORE_BASIC_INFORMATION *info )
+{
+    struct inproc_sync cache, *sync;
+    NTSTATUS ret;
+
+    if (inproc_device_fd < 0) return STATUS_NOT_IMPLEMENTED;
+    if ((ret = get_inproc_sync( handle, INPROC_SYNC_SEMAPHORE, SEMAPHORE_QUERY_STATE, &cache, &sync ))) return ret;
+    ret = linux_query_semaphore_obj( sync->fd, info );
+    release_inproc_sync( sync );
+    return ret;
+}
+
+static NTSTATUS inproc_set_event( HANDLE handle, LONG *prev_state )
+{
+    struct inproc_sync cache, *sync;
+    NTSTATUS ret;
+
+    if (inproc_device_fd < 0) return STATUS_NOT_IMPLEMENTED;
+    if ((ret = get_inproc_sync( handle, INPROC_SYNC_EVENT, EVENT_MODIFY_STATE, &cache, &sync ))) return ret;
+    ret = linux_set_event_obj( sync->fd, prev_state );
+    release_inproc_sync( sync );
+    return ret;
+}
+
+static NTSTATUS inproc_reset_event( HANDLE handle, LONG *prev_state )
+{
+    struct inproc_sync cache, *sync;
+    NTSTATUS ret;
+
+    if (inproc_device_fd < 0) return STATUS_NOT_IMPLEMENTED;
+    if ((ret = get_inproc_sync( handle, INPROC_SYNC_EVENT, EVENT_MODIFY_STATE, &cache, &sync ))) return ret;
+    ret = linux_reset_event_obj( sync->fd, prev_state );
+    release_inproc_sync( sync );
+    return ret;
+}
+
+static NTSTATUS inproc_pulse_event( HANDLE handle, LONG *prev_state )
+{
+    struct inproc_sync cache, *sync;
+    NTSTATUS ret;
+
+    if (inproc_device_fd < 0) return STATUS_NOT_IMPLEMENTED;
+    if ((ret = get_inproc_sync( handle, INPROC_SYNC_EVENT, EVENT_MODIFY_STATE, &cache, &sync ))) return ret;
+    ret = linux_pulse_event_obj( sync->fd, prev_state );
+    release_inproc_sync( sync );
+    return ret;
+}
+
+static NTSTATUS inproc_query_event( HANDLE handle, EVENT_BASIC_INFORMATION *info )
+{
+    struct inproc_sync cache, *sync;
+    NTSTATUS ret;
+
+    if (inproc_device_fd < 0) return STATUS_NOT_IMPLEMENTED;
+    if ((ret = get_inproc_sync( handle, INPROC_SYNC_EVENT, EVENT_QUERY_STATE, &cache, &sync ))) return ret;
+    ret = linux_query_event_obj( sync->fd, sync->type, info );
+    release_inproc_sync( sync );
+    return ret;
+}
+
+static NTSTATUS inproc_release_mutex( HANDLE handle, LONG *prev_count )
+{
+    struct inproc_sync cache, *sync;
+    NTSTATUS ret;
+
+    if (inproc_device_fd < 0) return STATUS_NOT_IMPLEMENTED;
+    if ((ret = get_inproc_sync( handle, INPROC_SYNC_MUTEX, 0, &cache, &sync ))) return ret;
+    ret = linux_release_mutex_obj( sync->fd, prev_count );
+    release_inproc_sync( sync );
+    return ret;
+}
+
+static NTSTATUS inproc_query_mutex( HANDLE handle, MUTANT_BASIC_INFORMATION *info )
+{
+    struct inproc_sync cache, *sync;
+    NTSTATUS ret;
+
+    if (inproc_device_fd < 0) return STATUS_NOT_IMPLEMENTED;
+    if ((ret = get_inproc_sync( handle, INPROC_SYNC_MUTEX, MUTANT_QUERY_STATE, &cache, &sync ))) return ret;
+    ret = linux_query_mutex_obj( sync->fd, info );
+    release_inproc_sync( sync );
+    return ret;
+}
+
+static NTSTATUS inproc_wait( DWORD count, const HANDLE *handles, BOOLEAN wait_any,
+                             BOOLEAN alertable, const LARGE_INTEGER *timeout )
+{
+    HANDLE server_queue = UlongToHandle( NtUserGetThreadInfo()->server_queue );
+    struct inproc_sync *syncs[64], cache[ARRAY_SIZE(syncs)];
+    int objs[ARRAY_SIZE(syncs)];
+    UINT queue = -1;
+    NTSTATUS ret;
+
+    if (inproc_device_fd < 0) return STATUS_NOT_IMPLEMENTED;
+
+    assert( count <= ARRAY_SIZE(syncs) );
+    for (int i = 0; i < count; ++i)
+    {
+        if ((ret = get_inproc_sync( handles[i], INPROC_SYNC_UNKNOWN, SYNCHRONIZE, cache + i, &syncs[i] )))
+        {
+            while (i--) release_inproc_sync( syncs[i] );
+            return ret;
+        }
+        if (handles[i] == server_queue) queue = i;
+        objs[i] = syncs[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 != -1)
+    {
+        release_inproc_sync( syncs[0] );
+        return server_wait_for_object( handles[0], alertable, timeout );
+    }
+
+    if (queue != -1) select_queue();
+    ret = linux_wait_objs( inproc_device_fd, count, objs, wait_any, alertable, timeout );
+    if (queue != -1) unselect_queue( ret == queue );
+
+    while (count--) release_inproc_sync( syncs[count] );
+    return ret;
+}
+
+static NTSTATUS inproc_signal_and_wait( HANDLE signal, HANDLE wait,
+                                        BOOLEAN alertable, const LARGE_INTEGER *timeout )
+{
+    HANDLE server_queue = UlongToHandle( NtUserGetThreadInfo()->server_queue );
+    struct inproc_sync cache[2], *signal_sync, *wait_sync;
+    NTSTATUS ret;
+
+    if (inproc_device_fd < 0) return STATUS_NOT_IMPLEMENTED;
+
+    if ((ret = get_inproc_sync( signal, INPROC_SYNC_UNKNOWN, 0, cache + 0, &signal_sync ))) return ret;
+    if ((ret = check_signal_access( signal_sync )))
+    {
+        release_inproc_sync( signal_sync );
+        return ret;
+    }
+
+    if ((ret = get_inproc_sync( wait, INPROC_SYNC_UNKNOWN, SYNCHRONIZE, cache + 1, &wait_sync )))
+    {
+        release_inproc_sync( signal_sync );
+        return ret;
+    }
+
+    switch (signal_sync->type)
+    {
+    case INPROC_SYNC_EVENT:     ret = linux_set_event_obj( signal_sync->fd, NULL ); break;
+    case INPROC_SYNC_MUTEX:     ret = linux_release_mutex_obj( signal_sync->fd, NULL ); break;
+    case INPROC_SYNC_SEMAPHORE: ret = linux_release_semaphore_obj( signal_sync->fd, 1, NULL ); break;
+    default: assert( 0 ); break;
+    }
+
+    if (!ret)
+    {
+        if (wait == server_queue) select_queue();
+        ret = linux_wait_objs( inproc_device_fd, 1, &wait_sync->fd, TRUE, alertable, timeout );
+        if (wait == server_queue) unselect_queue( !ret );
+    }
+
+    release_inproc_sync( signal_sync );
+    release_inproc_sync( wait_sync );
+    return ret;
+}
+
 
 /******************************************************************************
  *              NtCreateSemaphore (NTDLL.@)
@@ -311,6 +977,9 @@ NTSTATUS WINAPI NtCreateSemaphore( HANDLE *handle, ACCESS_MASK access, const OBJ
     data_size_t len;
     struct object_attributes *objattr;
 
+    TRACE( "access %#x, name %s, initial %d, max %d\n", access,
+           attr ? debugstr_us(attr->ObjectName) : "(null)", initial, max );
+
     *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 +1007,8 @@ NTSTATUS WINAPI NtOpenSemaphore( HANDLE *handle, ACCESS_MASK access, const OBJEC
 {
     unsigned int ret;
 
+    TRACE( "access %#x, name %s\n", access, attr ? debugstr_us(attr->ObjectName) : "(null)" );
+
     *handle = 0;
     if ((ret = validate_open_object_attributes( attr ))) return ret;
 
@@ -375,6 +1046,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 +1074,11 @@ NTSTATUS WINAPI NtReleaseSemaphore( HANDLE handle, ULONG count, ULONG *previous
 {
     unsigned int ret;
 
+    TRACE( "handle %p, count %u, prev_count %p\n", handle, 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 +1103,9 @@ NTSTATUS WINAPI NtCreateEvent( HANDLE *handle, ACCESS_MASK access, const OBJECT_
     data_size_t len;
     struct object_attributes *objattr;
 
+    TRACE( "access %#x, name %s, type %u, state %u\n", access,
+           attr ? debugstr_us(attr->ObjectName) : "(null)", type, state );
+
     *handle = 0;
     if (type != NotificationEvent && type != SynchronizationEvent) return STATUS_INVALID_PARAMETER;
     if ((ret = alloc_object_attributes( attr, &objattr, &len ))) return ret;
@@ -448,6 +1133,8 @@ NTSTATUS WINAPI NtOpenEvent( HANDLE *handle, ACCESS_MASK access, const OBJECT_AT
 {
     unsigned int ret;
 
+    TRACE( "access %#x, name %s\n", access, attr ? debugstr_us(attr->ObjectName) : "(null)" );
+
     *handle = 0;
     if ((ret = validate_open_object_attributes( attr ))) return ret;
 
@@ -473,6 +1160,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 +1184,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 +1218,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 +1254,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 +1285,9 @@ NTSTATUS WINAPI NtCreateMutant( HANDLE *handle, ACCESS_MASK access, const OBJECT
     data_size_t len;
     struct object_attributes *objattr;
 
+    TRACE( "access %#x, name %s, owned %u\n", access,
+           attr ? debugstr_us(attr->ObjectName) : "(null)", owned );
+
     *handle = 0;
     if ((ret = alloc_object_attributes( attr, &objattr, &len ))) return ret;
 
@@ -602,6 +1313,8 @@ NTSTATUS WINAPI NtOpenMutant( HANDLE *handle, ACCESS_MASK access, const OBJECT_A
 {
     unsigned int ret;
 
+    TRACE( "access %#x, name %s\n", access, attr ? debugstr_us(attr->ObjectName) : "(null)" );
+
     *handle = 0;
     if ((ret = validate_open_object_attributes( attr ))) return ret;
 
@@ -627,6 +1340,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 +1375,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 +2150,9 @@ NTSTATUS WINAPI NtCreateTimer( HANDLE *handle, ACCESS_MASK access, const OBJECT_
     data_size_t len;
     struct object_attributes *objattr;
 
+    TRACE( "access %#x, name %s, type %u\n", access,
+           attr ? debugstr_us(attr->ObjectName) : "(null)", type );
+
     *handle = 0;
     if (type != NotificationTimer && type != SynchronizationTimer) return STATUS_INVALID_PARAMETER;
     if ((ret = alloc_object_attributes( attr, &objattr, &len ))) return ret;
@@ -1453,6 +2180,8 @@ NTSTATUS WINAPI NtOpenTimer( HANDLE *handle, ACCESS_MASK access, const OBJECT_AT
 {
     unsigned int ret;
 
+    TRACE( "access %#x, name %s\n", access, attr ? debugstr_us(attr->ObjectName) : "(null)" );
+
     *handle = 0;
     if ((ret = validate_open_object_attributes( attr ))) return ret;
 
@@ -1506,6 +2235,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 +2305,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 +2348,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 +2599,9 @@ NTSTATUS WINAPI NtCreateKeyedEvent( HANDLE *handle, ACCESS_MASK access,
     data_size_t len;
     struct object_attributes *objattr;
 
+    TRACE( "access %#x, name %s, flags %#x\n", access,
+           attr ? debugstr_us(attr->ObjectName) : "(null)", flags );
+
     *handle = 0;
     if ((ret = alloc_object_attributes( attr, &objattr, &len ))) return ret;
 
@@ -1870,6 +2626,8 @@ NTSTATUS WINAPI NtOpenKeyedEvent( HANDLE *handle, ACCESS_MASK access, const OBJE
 {
     unsigned int ret;
 
+    TRACE( "access %#x, name %s\n", access, attr ? debugstr_us(attr->ObjectName) : "(null)" );
+
     *handle = 0;
     if ((ret = validate_open_object_attributes( attr ))) return ret;
 
@@ -1896,6 +2654,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 +2675,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 +2821,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 +2885,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 7f61a37..dc32f62 100644
--- a/dlls/ntdll/unix/thread.c
+++ b/dlls/ntdll/unix/thread.c
@@ -1104,6 +1104,7 @@ static void contexts_from_server( CONTEXT *context, struct context_data server_c
  */
 static DECLSPEC_NORETURN void pthread_exit_wrapper( int status )
 {
+    close( ntdll_get_thread_data()->alert_fd );
     close( ntdll_get_thread_data()->wait_fd[0] );
     close( ntdll_get_thread_data()->wait_fd[1] );
     close( ntdll_get_thread_data()->reply_fd );
@@ -1126,7 +1127,7 @@ static void start_thread( TEB *teb )
     thread_data->syscall_trace = TRACE_ON(syscall);
     thread_data->pthread_id = pthread_self();
     pthread_setspecific( teb_key, teb );
-    server_init_thread( thread_data->start, &suspend );
+    server_init_thread( thread_data, &suspend );
     signal_start_thread( thread_data->start, thread_data->param, suspend, teb );
 }
 
diff --git a/dlls/ntdll/unix/unix_private.h b/dlls/ntdll/unix/unix_private.h
index 11d8221..a07c7ba 100644
--- a/dlls/ntdll/unix/unix_private.h
+++ b/dlls/ntdll/unix/unix_private.h
@@ -108,6 +108,7 @@ struct ntdll_thread_data
     int                       request_fd;    /* fd for sending server requests */
     int                       reply_fd;      /* fd for receiving server replies */
     int                       wait_fd[2];    /* fd for sleeping server requests */
+    int                       alert_fd;      /* fd for user apc alerts */
     BOOL                      allow_writes;  /* ThreadAllowWrites flags */
     pthread_t                 pthread_id;    /* pthread thread id */
     void                     *kernel_stack;  /* stack for thread startup and kernel syscalls */
@@ -198,8 +199,10 @@ extern unsigned int supported_machines_count;
 extern USHORT supported_machines[8];
 extern BOOL process_exiting;
 extern HANDLE keyed_event;
+extern int inproc_device_fd;
 extern timeout_t server_start_time;
 extern sigset_t server_block_set;
+extern pthread_mutex_t fd_cache_mutex;
 extern struct _KUSER_SHARED_DATA *user_shared_data;
 #ifdef __i386__
 extern struct ldt_copy __wine_ldt_copy;
@@ -229,15 +232,17 @@ 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 void wine_server_send_fd( int fd );
+extern int wine_server_receive_fd( obj_handle_t *handle );
 extern void process_exit_wrapper( int status ) DECLSPEC_NORETURN;
 extern size_t server_init_process(void);
 extern void server_init_process_done(void);
-extern void server_init_thread( void *entry_point, BOOL *suspend );
+extern void server_init_thread( struct ntdll_thread_data *data, BOOL *suspend );
 extern int server_pipe( int fd[2] );
 
 extern void fpux_to_fpu( I386_FLOATING_SAVE_AREA *fpu, const XSAVE_FORMAT *fpux );
@@ -389,6 +394,8 @@ extern NTSTATUS wow64_wine_spawnvp( void *args );
 
 extern void dbg_init(void);
 
+extern void close_inproc_sync( HANDLE handle );
+
 extern NTSTATUS call_user_apc_dispatcher( CONTEXT *context_ptr, unsigned int flags, ULONG_PTR arg1, ULONG_PTR arg2,
                                           ULONG_PTR arg3, PNTAPCFUNC func, NTSTATUS status );
 extern NTSTATUS call_user_exception_dispatcher( EXCEPTION_RECORD *rec, CONTEXT *context );
@@ -397,6 +404,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 )
@@ -468,7 +476,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 d796783..b452b5c 100644
--- a/dlls/ntdll/unix/virtual.c
+++ b/dlls/ntdll/unix/virtual.c
@@ -4036,6 +4036,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->alert_fd   = -1;
     list_add_head( &teb_list, &thread_data->entry );
     return teb;
 }
diff --git a/dlls/win32u/message.c b/dlls/win32u/message.c
index 51a173a..e703f13 100644
--- a/dlls/win32u/message.c
+++ b/dlls/win32u/message.c
@@ -3098,10 +3098,10 @@ static void process_sent_messages(void)
  */
 static HANDLE get_server_queue_handle(void)
 {
-    struct user_thread_info *thread_info = get_user_thread_info();
+    struct ntuser_thread_info *thread_info = NtUserGetThreadInfo();
     HANDLE ret;
 
-    if (!(ret = thread_info->server_queue))
+    if (!(ret = UlongToHandle( thread_info->server_queue )))
     {
         SERVER_START_REQ( get_msg_queue_handle )
         {
@@ -3109,7 +3109,7 @@ static HANDLE get_server_queue_handle(void)
             ret = wine_server_ptr_handle( reply->handle );
         }
         SERVER_END_REQ;
-        thread_info->server_queue = ret;
+        thread_info->server_queue = HandleToUlong( ret );
         if (!ret) ERR( "Cannot get server thread queue\n" );
     }
     return ret;
diff --git a/dlls/win32u/ntuser_private.h b/dlls/win32u/ntuser_private.h
index 17995bd..88732a9 100644
--- a/dlls/win32u/ntuser_private.h
+++ b/dlls/win32u/ntuser_private.h
@@ -101,7 +101,6 @@ static inline BOOL is_broadcast( HWND hwnd )
 struct user_thread_info
 {
     struct ntuser_thread_info     client_info;            /* Data shared with client */
-    HANDLE                        server_queue;           /* Handle to server-side queue */
     DWORD                         last_getmsg_time;       /* Get/PeekMessage last request time */
     LONGLONG                      last_driver_time;       /* Get/PeekMessage driver event time */
     WORD                          hook_call_depth;        /* Number of recursively called hook procs */
diff --git a/dlls/win32u/sysparams.c b/dlls/win32u/sysparams.c
index a242bc4..791d706 100644
--- a/dlls/win32u/sysparams.c
+++ b/dlls/win32u/sysparams.c
@@ -7047,6 +7047,7 @@ BOOL is_exiting_thread( DWORD tid )
 static void thread_detach(void)
 {
     struct user_thread_info *thread_info = get_user_thread_info();
+    HANDLE server_queue = UlongToHandle( thread_info->client_info.server_queue );
 
     destroy_thread_windows();
     user_driver->pThreadDetach();
@@ -7054,7 +7055,11 @@ static void thread_detach(void)
     free( thread_info->rawinput );
 
     cleanup_imm_thread();
-    NtClose( thread_info->server_queue );
+    if (server_queue)
+    {
+        NtClose( server_queue );
+        thread_info->client_info.server_queue = 0;
+    }
     free( thread_info->session_data );
 
     exiting_thread_id = 0;
diff --git a/include/config.h.in b/include/config.h.in
index 376d283..cf05cc6 100644
--- a/include/config.h.in
+++ b/include/config.h.in
@@ -198,6 +198,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/ntuser.h b/include/ntuser.h
index 029a8be..587c343 100644
--- a/include/ntuser.h
+++ b/include/ntuser.h
@@ -134,6 +134,7 @@ struct ntuser_thread_info
     UINT           default_imc;       /* default input context */
     UINT64         client_imm;        /* client IMM thread info */
     UINT64         wmchar_data;       /* client data for WM_CHAR mappings */
+    UINT           server_queue;      /* handle of the server-side queue */
 };
 
 static inline struct ntuser_thread_info *NtUserGetThreadInfo(void)
diff --git a/include/wine/server_protocol.h b/include/wine/server_protocol.h
index b8dad20..172ec5c 100644
--- a/include/wine/server_protocol.h
+++ b/include/wine/server_protocol.h
@@ -1148,6 +1148,8 @@ struct init_first_thread_reply
     thread_id_t  tid;
     timeout_t    server_start;
     unsigned int session_id;
+    obj_handle_t inproc_device;
+    obj_handle_t alert_handle;
     data_size_t  info_size;
     /* VARARG(machines,ushorts); */
 };
@@ -1167,7 +1169,7 @@ struct init_thread_reply
 {
     struct reply_header __header;
     int          suspend;
-    char __pad_12[4];
+    obj_handle_t alert_handle;
 };
 
 
@@ -5980,6 +5982,44 @@ struct set_keyboard_repeat_reply
 };
 
 
+enum inproc_sync_type
+{
+    INPROC_SYNC_UNKNOWN = 0,
+    INPROC_SYNC_EVENT = 1,
+    INPROC_SYNC_MUTEX = 2,
+    INPROC_SYNC_SEMAPHORE = 3,
+};
+
+
+struct get_inproc_sync_fd_request
+{
+    struct request_header __header;
+    obj_handle_t handle;
+};
+struct get_inproc_sync_fd_reply
+{
+    struct reply_header __header;
+    unsigned char type;
+    unsigned char internal;
+    char __pad_10[2];
+    unsigned int access;
+};
+
+
+
+struct select_inproc_queue_request
+{
+    struct request_header __header;
+    int          select;
+    int          signaled;
+    char __pad_20[4];
+};
+struct select_inproc_queue_reply
+{
+    struct reply_header __header;
+};
+
+
 enum request
 {
     REQ_new_process,
@@ -6280,6 +6320,8 @@ enum request
     REQ_get_next_process,
     REQ_get_next_thread,
     REQ_set_keyboard_repeat,
+    REQ_get_inproc_sync_fd,
+    REQ_select_inproc_queue,
     REQ_NB_REQUESTS
 };
 
@@ -6585,6 +6627,8 @@ union generic_request
     struct get_next_process_request get_next_process_request;
     struct get_next_thread_request get_next_thread_request;
     struct set_keyboard_repeat_request set_keyboard_repeat_request;
+    struct get_inproc_sync_fd_request get_inproc_sync_fd_request;
+    struct select_inproc_queue_request select_inproc_queue_request;
 };
 union generic_reply
 {
@@ -6888,8 +6932,10 @@ union generic_reply
     struct get_next_process_reply get_next_process_reply;
     struct get_next_thread_reply get_next_thread_reply;
     struct set_keyboard_repeat_reply set_keyboard_repeat_reply;
+    struct get_inproc_sync_fd_reply get_inproc_sync_fd_reply;
+    struct select_inproc_queue_reply select_inproc_queue_reply;
 };
 
-#define SERVER_PROTOCOL_VERSION 891
+#define SERVER_PROTOCOL_VERSION 892
 
 #endif /* __WINE_WINE_SERVER_PROTOCOL_H */
diff --git a/server/Makefile.in b/server/Makefile.in
index 6e4e68d..57250fd 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 4068f74..bea0f6c 100644
--- a/server/async.c
+++ b/server/async.c
@@ -79,7 +79,7 @@ static const struct object_ops async_ops =
     remove_queue,              /* remove_queue */
     async_signaled,            /* signaled */
     async_satisfied,           /* satisfied */
-    no_signal,                 /* signal */
+    NULL,                      /* signal */
     no_get_fd,                 /* get_fd */
     default_get_sync,          /* get_sync */
     default_map_access,        /* map_access */
@@ -697,13 +697,13 @@ static const struct object_ops iosb_ops =
     sizeof(struct iosb),      /* size */
     &no_type,                 /* type */
     iosb_dump,                /* dump */
-    no_add_queue,             /* add_queue */
+    NULL,                     /* add_queue */
     NULL,                     /* remove_queue */
     NULL,                     /* signaled */
     NULL,                     /* satisfied */
-    no_signal,                /* signal */
+    NULL,                     /* signal */
     no_get_fd,                /* get_fd */
-    default_get_sync,         /* get_sync */
+    no_get_sync,              /* get_sync */
     default_map_access,       /* map_access */
     default_get_sd,           /* get_sd */
     default_set_sd,           /* set_sd */
diff --git a/server/atom.c b/server/atom.c
index 0bba828..9124a18 100644
--- a/server/atom.c
+++ b/server/atom.c
@@ -75,13 +75,13 @@ static const struct object_ops atom_table_ops =
     sizeof(struct atom_table),    /* size */
     &no_type,                     /* type */
     atom_table_dump,              /* dump */
-    no_add_queue,                 /* add_queue */
+    NULL,                         /* add_queue */
     NULL,                         /* remove_queue */
     NULL,                         /* signaled */
     NULL,                         /* satisfied */
-    no_signal,                    /* signal */
+    NULL,                         /* signal */
     no_get_fd,                    /* get_fd */
-    default_get_sync,             /* get_sync */
+    no_get_sync,                  /* get_sync */
     default_map_access,           /* map_access */
     default_get_sd,               /* get_sd */
     default_set_sd,               /* set_sd */
diff --git a/server/change.c b/server/change.c
index be3251a..469c507 100644
--- a/server/change.c
+++ b/server/change.c
@@ -113,7 +113,7 @@ static const struct object_ops dir_ops =
     NULL,                     /* remove_queue */
     NULL,                     /* signaled */
     NULL,                     /* satisfied */
-    no_signal,                /* signal */
+    NULL,                     /* signal */
     dir_get_fd,               /* get_fd */
     default_fd_get_sync,      /* get_sync */
     default_map_access,       /* map_access */
diff --git a/server/clipboard.c b/server/clipboard.c
index 59a5035..9395b32 100644
--- a/server/clipboard.c
+++ b/server/clipboard.c
@@ -73,13 +73,13 @@ static const struct object_ops clipboard_ops =
     sizeof(struct clipboard),     /* size */
     &no_type,                     /* type */
     clipboard_dump,               /* dump */
-    no_add_queue,                 /* add_queue */
+    NULL,                         /* add_queue */
     NULL,                         /* remove_queue */
     NULL,                         /* signaled */
     NULL,                         /* satisfied */
-    no_signal,                    /* signal */
+    NULL,                         /* signal */
     no_get_fd,                    /* get_fd */
-    default_get_sync,             /* get_sync */
+    no_get_sync,                  /* get_sync */
     default_map_access,           /* map_access */
     default_get_sd,               /* get_sd */
     default_set_sd,               /* set_sd */
diff --git a/server/completion.c b/server/completion.c
index a2028a8..3180222 100644
--- a/server/completion.c
+++ b/server/completion.c
@@ -73,7 +73,7 @@ struct completion_wait
 struct completion
 {
     struct object       obj;
-    struct event_sync  *sync;
+    struct object      *sync;
     struct list         queue;
     struct list         wait_queue;
     unsigned int        depth;
@@ -93,7 +93,7 @@ static const struct object_ops completion_wait_ops =
     remove_queue,                   /* remove_queue */
     completion_wait_signaled,       /* signaled */
     completion_wait_satisfied,      /* satisfied */
-    no_signal,                      /* signal */
+    NULL,                           /* signal */
     no_get_fd,                      /* get_fd */
     default_get_sync,               /* get_sync */
     default_map_access,             /* map_access */
@@ -168,7 +168,7 @@ static const struct object_ops completion_ops =
     NULL,                      /* remove_queue */
     NULL,                      /* signaled */
     NULL,                      /* satisfied */
-    no_signal,                 /* signal */
+    NULL,                      /* signal */
     no_get_fd,                 /* get_fd */
     completion_get_sync,       /* get_sync */
     default_map_access,        /* map_access */
diff --git a/server/console.c b/server/console.c
index f0cb668..2af3a23 100644
--- a/server/console.c
+++ b/server/console.c
@@ -53,7 +53,7 @@ struct history_line
 struct console
 {
     struct object                obj;           /* object header */
-    struct event_sync           *sync;          /* sync object for wait/signal */
+    struct object               *sync;          /* sync object for wait/signal */
     struct thread               *renderer;      /* console renderer thread */
     struct screen_buffer        *active;        /* active screen buffer */
     struct console_server       *server;        /* console server object */
@@ -84,7 +84,7 @@ static const struct object_ops console_ops =
     NULL,                             /* remove_queue */
     NULL,                             /* signaled */
     NULL,                             /* satisfied */
-    no_signal,                        /* signal */
+    NULL,                             /* signal */
     console_get_fd,                   /* get_fd */
     console_get_sync,                 /* get_sync */
     default_map_access,               /* map_access */
@@ -134,7 +134,7 @@ struct console_host_ioctl
 struct console_server
 {
     struct object         obj;            /* object header */
-    struct event_sync    *sync;           /* sync object for wait/signal */
+    struct object        *sync;           /* sync object for wait/signal */
     struct fd            *fd;             /* pseudo-fd for ioctls */
     struct console       *console;        /* attached console */
     struct list           queue;          /* ioctl queue */
@@ -163,7 +163,7 @@ static const struct object_ops console_server_ops =
     NULL,                             /* remove_queue */
     NULL,                             /* signaled */
     NULL,                             /* satisfied */
-    no_signal,                        /* signal */
+    NULL,                             /* signal */
     console_server_get_fd,            /* get_fd */
     console_server_get_sync,          /* get_sync */
     default_map_access,               /* map_access */
@@ -210,7 +210,7 @@ struct font_info
 struct screen_buffer
 {
     struct object         obj;           /* object header */
-    struct event_sync    *sync;          /* sync object for wait/signal */
+    struct object        *sync;          /* sync object for wait/signal */
     struct list           entry;         /* entry in list of all screen buffers */
     struct console       *input;         /* associated console input */
     unsigned int          id;            /* buffer id */
@@ -234,7 +234,7 @@ static const struct object_ops screen_buffer_ops =
     NULL,                             /* remove_queue */
     NULL,                             /* signaled */
     NULL,                             /* satisfied */
-    no_signal,                        /* signal */
+    NULL,                             /* signal */
     screen_buffer_get_fd,             /* get_fd */
     screen_buffer_get_sync,           /* get_sync */
     default_map_access,               /* map_access */
@@ -280,13 +280,13 @@ static const struct object_ops console_device_ops =
     sizeof(struct object),            /* size */
     &device_type,                     /* type */
     console_device_dump,              /* dump */
-    no_add_queue,                     /* add_queue */
+    NULL,                             /* add_queue */
     NULL,                             /* remove_queue */
     NULL,                             /* signaled */
-    no_satisfied,                     /* satisfied */
-    no_signal,                        /* signal */
+    NULL,                             /* satisfied */
+    NULL,                             /* signal */
     no_get_fd,                        /* get_fd */
-    default_get_sync,                 /* get_sync */
+    no_get_sync,                      /* get_sync */
     default_map_access,               /* map_access */
     default_get_sd,                   /* get_sd */
     default_set_sd,                   /* set_sd */
@@ -303,7 +303,7 @@ static const struct object_ops console_device_ops =
 struct console_input
 {
     struct object         obj;         /* object header */
-    struct event_sync    *sync;        /* sync object for wait/signal */
+    struct object        *sync;        /* sync object for wait/signal */
     struct fd            *fd;          /* pseudo-fd */
     struct list           entry;       /* entry in console->inputs */
     struct console       *console;     /* associated console at creation time */
@@ -325,7 +325,7 @@ static const struct object_ops console_input_ops =
     NULL,                             /* remove_queue */
     NULL,                             /* signaled */
     NULL,                             /* satisfied */
-    no_signal,                        /* signal */
+    NULL,                             /* signal */
     console_input_get_fd,             /* get_fd */
     console_input_get_sync,           /* get_sync */
     default_map_access,               /* map_access */
@@ -364,7 +364,7 @@ static const struct fd_ops console_input_fd_ops =
 struct console_output
 {
     struct object         obj;         /* object header */
-    struct event_sync    *sync;        /* sync object for wait/signal */
+    struct object        *sync;        /* sync object for wait/signal */
     struct fd            *fd;          /* pseudo-fd */
     struct list           entry;       /* entry in console->outputs */
     struct console       *console;     /* associated console at creation time */
@@ -386,7 +386,7 @@ static const struct object_ops console_output_ops =
     NULL,                             /* remove_queue */
     NULL,                             /* signaled */
     NULL,                             /* satisfied */
-    no_signal,                        /* signal */
+    NULL,                             /* signal */
     console_output_get_fd,            /* get_fd */
     console_output_get_sync,          /* get_sync */
     default_map_access,               /* map_access */
@@ -441,13 +441,13 @@ static const struct object_ops console_connection_ops =
     sizeof(struct console_connection),/* size */
     &device_type,                     /* type */
     console_connection_dump,          /* dump */
-    no_add_queue,                     /* add_queue */
+    NULL,                             /* add_queue */
     NULL,                             /* remove_queue */
     NULL,                             /* signaled */
-    no_satisfied,                     /* satisfied */
-    no_signal,                        /* signal */
+    NULL,                             /* satisfied */
+    NULL,                             /* signal */
     console_connection_get_fd,        /* get_fd */
-    default_get_sync,                 /* get_sync */
+    no_get_sync,                      /* get_sync */
     default_map_access,               /* map_access */
     default_get_sd,                   /* get_sd */
     default_set_sd,                   /* set_sd */
@@ -646,7 +646,7 @@ static struct object *create_screen_buffer( struct console *console )
     }
 
     if (!(screen_buffer = alloc_object( &screen_buffer_ops ))) return NULL;
-    screen_buffer->sync  = (struct event_sync *)grab_object( console->sync );
+    screen_buffer->sync  = grab_object( console->sync );
     screen_buffer->id    = ++console->last_id;
     screen_buffer->input = console;
     init_async_queue( &screen_buffer->ioctl_q );
@@ -1350,7 +1350,7 @@ static struct object *console_device_lookup_name( struct object *obj, struct uni
 
         name->len = 0;
         if (!(console_input = alloc_object( &console_input_ops ))) return NULL;
-        console_input->sync = (struct event_sync *)grab_object( current->process->console->sync );
+        console_input->sync = grab_object( current->process->console->sync );
         console_input->fd = alloc_pseudo_fd( &console_input_fd_ops, &console_input->obj,
                                              FILE_SYNCHRONOUS_IO_NONALERT );
         if (!console_input->fd)
@@ -1375,7 +1375,7 @@ static struct object *console_device_lookup_name( struct object *obj, struct uni
 
         name->len = 0;
         if (!(console_output = alloc_object( &console_output_ops ))) return NULL;
-        console_output->sync = (struct event_sync *)grab_object( current->process->console->sync );
+        console_output->sync = grab_object( current->process->console->sync );
         console_output->fd = alloc_pseudo_fd( &console_output_fd_ops, &console_output->obj,
                                              FILE_SYNCHRONOUS_IO_NONALERT );
         if (!console_output->fd)
diff --git a/server/debugger.c b/server/debugger.c
index 2956ee1..7c48355 100644
--- a/server/debugger.c
+++ b/server/debugger.c
@@ -43,7 +43,7 @@ enum debug_event_state { EVENT_QUEUED, EVENT_SENT, EVENT_DELAYED, EVENT_CONTINUE
 struct debug_event
 {
     struct object          obj;       /* object header */
-    struct event_sync     *sync;      /* sync object for wait/signal */
+    struct object         *sync;      /* sync object for wait/signal */
     struct list            entry;     /* entry in event queue */
     struct thread         *sender;    /* thread which sent this event */
     struct file           *file;      /* file object for events that need one */
@@ -70,7 +70,7 @@ struct type_descr debug_obj_type =
 struct debug_obj
 {
     struct object        obj;         /* object header */
-    struct event_sync   *sync;       /* sync object for wait/signal */
+    struct object       *sync;       /* sync object for wait/signal */
     struct list          event_queue; /* pending events queue */
     unsigned int         flags;       /* debug flags */
 };
@@ -89,7 +89,7 @@ static const struct object_ops debug_event_ops =
     NULL,                          /* remove_queue */
     NULL,                          /* signaled */
     NULL,                          /* satisfied */
-    no_signal,                     /* signal */
+    NULL,                          /* signal */
     no_get_fd,                     /* get_fd */
     debug_event_get_sync,          /* get_sync */
     default_map_access,            /* map_access */
@@ -118,7 +118,7 @@ static const struct object_ops debug_obj_ops =
     NULL,                          /* remove_queue */
     NULL,                          /* signaled */
     NULL,                          /* satisfied */
-    no_signal,                     /* signal */
+    NULL,                          /* signal */
     no_get_fd,                     /* get_fd */
     debug_obj_get_sync,            /* get_sync */
     default_map_access,            /* map_access */
diff --git a/server/device.c b/server/device.c
index f4b3f19..ddd66c1 100644
--- a/server/device.c
+++ b/server/device.c
@@ -63,13 +63,13 @@ static const struct object_ops irp_call_ops =
     sizeof(struct irp_call),          /* size */
     &no_type,                         /* type */
     irp_call_dump,                    /* dump */
-    no_add_queue,                     /* add_queue */
+    NULL,                             /* add_queue */
     NULL,                             /* remove_queue */
     NULL,                             /* signaled */
     NULL,                             /* satisfied */
-    no_signal,                        /* signal */
+    NULL,                             /* signal */
     no_get_fd,                        /* get_fd */
-    default_get_sync,                 /* get_sync */
+    no_get_sync,                      /* get_sync */
     default_map_access,               /* map_access */
     default_get_sd,                   /* get_sd */
     default_set_sd,                   /* set_sd */
@@ -89,7 +89,7 @@ static const struct object_ops irp_call_ops =
 struct device_manager
 {
     struct object          obj;            /* object header */
-    struct event_sync     *sync;           /* sync object for wait/signal */
+    struct object         *sync;           /* sync object for wait/signal */
     struct list            devices;        /* list of devices */
     struct list            requests;       /* list of pending irps across all devices */
     struct irp_call       *current_call;   /* call currently executed on client side */
@@ -109,7 +109,7 @@ static const struct object_ops device_manager_ops =
     NULL,                             /* remove_queue */
     NULL,                             /* signaled */
     NULL,                             /* satisfied */
-    no_signal,                        /* signal */
+    NULL,                             /* signal */
     no_get_fd,                        /* get_fd */
     device_manager_get_sync,          /* get_sync */
     default_map_access,               /* map_access */
@@ -163,13 +163,13 @@ static const struct object_ops device_ops =
     sizeof(struct device),            /* size */
     &device_type,                     /* type */
     device_dump,                      /* dump */
-    no_add_queue,                     /* add_queue */
+    NULL,                             /* add_queue */
     NULL,                             /* remove_queue */
     NULL,                             /* signaled */
-    no_satisfied,                     /* satisfied */
-    no_signal,                        /* signal */
+    NULL,                             /* satisfied */
+    NULL,                             /* signal */
     no_get_fd,                        /* get_fd */
-    default_get_sync,                 /* get_sync */
+    no_get_sync,                      /* get_sync */
     default_map_access,               /* map_access */
     default_get_sd,                   /* get_sd */
     default_set_sd,                   /* set_sd */
@@ -220,7 +220,7 @@ static const struct object_ops device_file_ops =
     NULL,                             /* remove_queue */
     NULL,                             /* signaled */
     NULL,                             /* satisfied */
-    no_signal,                        /* signal */
+    NULL,                             /* signal */
     device_file_get_fd,               /* get_fd */
     default_fd_get_sync,              /* get_sync */
     default_map_access,               /* map_access */
diff --git a/server/directory.c b/server/directory.c
index c56c216..9a0c014 100644
--- a/server/directory.c
+++ b/server/directory.c
@@ -66,13 +66,13 @@ static const struct object_ops object_type_ops =
     sizeof(struct object_type),   /* size */
     &objtype_type,                /* type */
     object_type_dump,             /* dump */
-    no_add_queue,                 /* add_queue */
+    NULL,                         /* add_queue */
     NULL,                         /* remove_queue */
     NULL,                         /* signaled */
     NULL,                         /* satisfied */
-    no_signal,                    /* signal */
+    NULL,                         /* signal */
     no_get_fd,                    /* get_fd */
-    default_get_sync,             /* get_sync */
+    no_get_sync,                  /* get_sync */
     default_map_access,           /* map_access */
     default_get_sd,               /* get_sd */
     default_set_sd,               /* set_sd */
@@ -117,13 +117,13 @@ static const struct object_ops directory_ops =
     sizeof(struct directory),     /* size */
     &directory_type,              /* type */
     directory_dump,               /* dump */
-    no_add_queue,                 /* add_queue */
+    NULL,                         /* add_queue */
     NULL,                         /* remove_queue */
     NULL,                         /* signaled */
     NULL,                         /* satisfied */
-    no_signal,                    /* signal */
+    NULL,                         /* signal */
     no_get_fd,                    /* get_fd */
-    default_get_sync,             /* get_sync */
+    no_get_sync,                  /* get_sync */
     default_map_access,           /* map_access */
     default_get_sd,               /* get_sd */
     default_set_sd,               /* set_sd */
diff --git a/server/event.c b/server/event.c
index c69554f..82e3ebb 100644
--- a/server/event.c
+++ b/server/event.c
@@ -48,6 +48,7 @@ struct type_descr event_type =
         STANDARD_RIGHTS_EXECUTE | SYNCHRONIZE,
         EVENT_ALL_ACCESS
     },
+    EVENT_MODIFY_STATE,                   /* signal access */
 };
 
 struct event_sync
@@ -60,7 +61,7 @@ struct event_sync
 static void event_sync_dump( struct object *obj, int verbose );
 static int event_sync_signaled( struct object *obj, struct wait_queue_entry *entry );
 static void event_sync_satisfied( struct object *obj, struct wait_queue_entry *entry );
-static int event_sync_signal( struct object *obj, unsigned int access );
+static int event_sync_signal( struct object *obj, int signal );
 
 static const struct object_ops event_sync_ops =
 {
@@ -87,15 +88,17 @@ static const struct object_ops event_sync_ops =
     no_destroy                 /* destroy */
 };
 
-struct event_sync *create_event_sync( int manual, int signaled )
+struct object *create_event_sync( int manual, int signaled )
 {
     struct event_sync *event;
 
+    if (get_inproc_device_fd() >= 0) return create_inproc_event_sync( manual, signaled );
+
     if (!(event = alloc_object( &event_sync_ops ))) return NULL;
     event->manual   = manual;
     event->signaled = signaled;
 
-    return event;
+    return &event->obj;
 }
 
 static void event_sync_dump( struct object *obj, int verbose )
@@ -113,44 +116,33 @@ static int event_sync_signaled( struct object *obj, struct wait_queue_entry *ent
     return event->signaled;
 }
 
-void signal_sync( struct event_sync *event )
-{
-    event->signaled = 1;
-    /* wake up all waiters if manual reset, a single one otherwise */
-    wake_up( &event->obj, !event->manual );
-}
-
-void reset_sync( struct event_sync *event )
-{
-    event->signaled = 0;
-}
-
 static void event_sync_satisfied( struct object *obj, struct wait_queue_entry *entry )
 {
     struct event_sync *event = (struct event_sync *)obj;
     assert( obj->ops == &event_sync_ops );
     /* Reset if it's an auto-reset event */
-    if (!event->manual) reset_sync( event );
+    if (!event->manual) event->signaled = 0;
 }
 
-static int event_sync_signal( struct object *obj, unsigned int access )
+static int event_sync_signal( struct object *obj, int signal )
 {
     struct event_sync *event = (struct event_sync *)obj;
     assert( obj->ops == &event_sync_ops );
-    signal_sync( event );
+
+    /* wake up all waiters if manual reset, a single one otherwise */
+    if ((event->signaled = !!signal)) wake_up( &event->obj, !event->manual );
     return 1;
 }
 
 struct event
 {
     struct object      obj;             /* object header */
-    struct event_sync *sync;            /* event sync object */
+    struct object     *sync;            /* event sync object */
     struct list        kernel_object;   /* list of kernel object pointers */
 };
 
 static void event_dump( struct object *obj, int verbose );
 static struct object *event_get_sync( struct object *obj );
-static int event_signal( struct object *obj, unsigned int access);
 static struct list *event_get_kernel_obj_list( struct object *obj );
 static void event_destroy( struct object *obj );
 
@@ -163,7 +155,7 @@ static const struct object_ops event_ops =
     NULL,                      /* remove_queue */
     NULL,                      /* signaled */
     NULL,                      /* satisfied */
-    event_signal,              /* signal */
+    NULL,                      /* signal */
     no_get_fd,                 /* get_fd */
     event_get_sync,            /* get_sync */
     default_map_access,        /* map_access */
@@ -201,6 +193,7 @@ struct keyed_event
 
 static void keyed_event_dump( struct object *obj, int verbose );
 static int keyed_event_signaled( struct object *obj, struct wait_queue_entry *entry );
+static void keyed_event_satisfied( struct object *obj, struct wait_queue_entry *entry );
 
 static const struct object_ops keyed_event_ops =
 {
@@ -210,8 +203,8 @@ static const struct object_ops keyed_event_ops =
     add_queue,                   /* add_queue */
     remove_queue,                /* remove_queue */
     keyed_event_signaled,        /* signaled */
-    no_satisfied,                /* satisfied */
-    no_signal,                   /* signal */
+    keyed_event_satisfied,       /* satisfied */
+    NULL,                        /* signal */
     no_get_fd,                   /* get_fd */
     default_get_sync,            /* get_sync */
     default_map_access,          /* map_access */
@@ -271,7 +264,7 @@ static void event_dump( struct object *obj, int verbose )
 {
     struct event *event = (struct event *)obj;
     assert( obj->ops == &event_ops );
-    event->sync->obj.ops->dump( &event->sync->obj, verbose );
+    event->sync->ops->dump( event->sync, verbose );
 }
 
 static struct object *event_get_sync( struct object *obj )
@@ -281,20 +274,6 @@ static struct object *event_get_sync( struct object *obj )
     return grab_object( event->sync );
 }
 
-static int event_signal( struct object *obj, unsigned int access )
-{
-    struct event *event = (struct event *)obj;
-    assert( obj->ops == &event_ops );
-
-    if (!(access & EVENT_MODIFY_STATE))
-    {
-        set_error( STATUS_ACCESS_DENIED );
-        return 0;
-    }
-    set_event( event );
-    return 1;
-}
-
 static struct list *event_get_kernel_obj_list( struct object *obj )
 {
     struct event *event = (struct event *)obj;
@@ -362,6 +341,11 @@ static int keyed_event_signaled( struct object *obj, struct wait_queue_entry *en
     return 0;
 }
 
+static void keyed_event_satisfied( struct object *obj, struct wait_queue_entry *entry )
+{
+    assert( obj->ops == &keyed_event_ops );
+}
+
 /* create an event */
 DECL_HANDLER(create_event)
 {
@@ -399,11 +383,14 @@ DECL_HANDLER(open_event)
 /* do an event operation */
 DECL_HANDLER(event_op)
 {
+    struct event_sync *sync;
     struct event *event;
 
     if (!(event = get_event_obj( current->process, req->handle, EVENT_MODIFY_STATE ))) return;
+    sync = (struct event_sync *)event->sync;
+    assert( event->sync->ops == &event_sync_ops );
 
-    reply->state = event->sync->signaled;
+    reply->state = sync->signaled;
     switch(req->op)
     {
     case PULSE_EVENT:
@@ -426,12 +413,15 @@ DECL_HANDLER(event_op)
 /* return details about the event */
 DECL_HANDLER(query_event)
 {
+    struct event_sync *sync;
     struct event *event;
 
     if (!(event = get_event_obj( current->process, req->handle, EVENT_QUERY_STATE ))) return;
+    sync = (struct event_sync *)event->sync;
+    assert( event->sync->ops == &event_sync_ops );
 
-    reply->manual_reset = event->sync->manual;
-    reply->state = event->sync->signaled;
+    reply->manual_reset = sync->manual;
+    reply->state = sync->signaled;
 
     release_object( event );
 }
diff --git a/server/fd.c b/server/fd.c
index 63fd09e..42e74e6 100644
--- a/server/fd.c
+++ b/server/fd.c
@@ -134,7 +134,7 @@ struct fd
 {
     struct object        obj;         /* object header */
     const struct fd_ops *fd_ops;      /* file descriptor operations */
-    struct event_sync   *sync;        /* sync object for wait/signal */
+    struct object       *sync;        /* sync object for wait/signal */
     struct inode        *inode;       /* inode that this fd belongs to */
     struct list          inode_entry; /* entry in inode fd list */
     struct closed_fd    *closed;      /* structure to store the unix fd at destroy time */
@@ -174,7 +174,7 @@ static const struct object_ops fd_ops =
     NULL,                     /* remove_queue */
     NULL,                     /* signaled */
     NULL,                     /* satisfied */
-    no_signal,                /* signal */
+    NULL,                     /* signal */
     no_get_fd,                /* get_fd */
     fd_get_sync,              /* get_sync */
     default_map_access,       /* map_access */
@@ -212,13 +212,13 @@ static const struct object_ops device_ops =
     sizeof(struct device),    /* size */
     &no_type,                 /* type */
     device_dump,              /* dump */
-    no_add_queue,             /* add_queue */
+    NULL,                     /* add_queue */
     NULL,                     /* remove_queue */
     NULL,                     /* signaled */
     NULL,                     /* satisfied */
-    no_signal,                /* signal */
+    NULL,                     /* signal */
     no_get_fd,                /* get_fd */
-    default_get_sync,         /* get_sync */
+    no_get_sync,              /* get_sync */
     default_map_access,       /* map_access */
     default_get_sd,           /* get_sd */
     default_set_sd,           /* set_sd */
@@ -253,13 +253,13 @@ static const struct object_ops inode_ops =
     sizeof(struct inode),     /* size */
     &no_type,                 /* type */
     inode_dump,               /* dump */
-    no_add_queue,             /* add_queue */
+    NULL,                     /* add_queue */
     NULL,                     /* remove_queue */
     NULL,                     /* signaled */
     NULL,                     /* satisfied */
-    no_signal,                /* signal */
+    NULL,                     /* signal */
     no_get_fd,                /* get_fd */
-    default_get_sync,         /* get_sync */
+    no_get_sync,              /* get_sync */
     default_map_access,       /* map_access */
     default_get_sd,           /* get_sd */
     default_set_sd,           /* set_sd */
@@ -278,7 +278,7 @@ static const struct object_ops inode_ops =
 struct file_lock
 {
     struct object       obj;         /* object header */
-    struct event_sync  *sync;        /* sync object for wait/signal */
+    struct object      *sync;        /* sync object for wait/signal */
     struct fd          *fd;          /* fd owning this lock */
     struct list         fd_entry;    /* entry in list of locks on a given fd */
     struct list         inode_entry; /* entry in inode list of locks */
@@ -302,7 +302,7 @@ static const struct object_ops file_lock_ops =
     NULL,                       /* remove_queue */
     NULL,                       /* signaled */
     NULL,                       /* satisfied */
-    no_signal,                  /* signal */
+    NULL,                       /* signal */
     no_get_fd,                  /* get_fd */
     file_lock_get_sync,         /* get_sync */
     default_map_access,         /* map_access */
diff --git a/server/file.c b/server/file.c
index 654b082..f9c9a0a 100644
--- a/server/file.c
+++ b/server/file.c
@@ -124,7 +124,7 @@ static const struct object_ops file_ops =
     NULL,                         /* remove_queue */
     NULL,                         /* signaled */
     NULL,                         /* satisfied */
-    no_signal,                    /* signal */
+    NULL,                         /* signal */
     file_get_fd,                  /* get_fd */
     default_fd_get_sync,          /* get_sync */
     default_map_access,           /* map_access */
diff --git a/server/handle.c b/server/handle.c
index ddcf03a..860913c 100644
--- a/server/handle.c
+++ b/server/handle.c
@@ -123,13 +123,13 @@ static const struct object_ops handle_table_ops =
     sizeof(struct handle_table),     /* size */
     &no_type,                        /* type */
     handle_table_dump,               /* dump */
-    no_add_queue,                    /* add_queue */
+    NULL,                            /* add_queue */
     NULL,                            /* remove_queue */
     NULL,                            /* signaled */
     NULL,                            /* satisfied */
-    no_signal,                       /* signal */
+    NULL,                            /* signal */
     no_get_fd,                       /* get_fd */
-    default_get_sync,                /* get_sync */
+    no_get_sync,                     /* get_sync */
     default_map_access,              /* map_access */
     default_get_sd,                  /* get_sd */
     default_set_sd,                  /* set_sd */
diff --git a/server/hook.c b/server/hook.c
index e53ba45..9cba360 100644
--- a/server/hook.c
+++ b/server/hook.c
@@ -77,13 +77,13 @@ static const struct object_ops hook_table_ops =
     sizeof(struct hook_table),    /* size */
     &no_type,                     /* type */
     hook_table_dump,              /* dump */
-    no_add_queue,                 /* add_queue */
+    NULL,                         /* add_queue */
     NULL,                         /* remove_queue */
     NULL,                         /* signaled */
     NULL,                         /* satisfied */
-    no_signal,                    /* signal */
+    NULL,                         /* signal */
     no_get_fd,                    /* get_fd */
-    default_get_sync,             /* get_sync */
+    no_get_sync,                  /* get_sync */
     default_map_access,           /* map_access */
     default_get_sd,               /* get_sd */
     default_set_sd,               /* set_sd */
diff --git a/server/inproc_sync.c b/server/inproc_sync.c
new file mode 100644
index 0000000..66d3fcc
--- /dev/null
+++ b/server/inproc_sync.c
@@ -0,0 +1,251 @@
+/*
+ * 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"
+#include "user.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>
+
+int get_inproc_device_fd(void)
+{
+    static int fd = -2;
+    if (fd == -2) fd = open( "/dev/ntsync", O_CLOEXEC | O_RDONLY );
+    return fd;
+}
+
+struct inproc_sync
+{
+    struct object          obj;  /* object header */
+    enum inproc_sync_type  type;
+    int                    fd;
+    struct list            entry;
+};
+
+static struct list inproc_mutexes = LIST_INIT( inproc_mutexes );
+
+static void inproc_sync_dump( struct object *obj, int verbose );
+static int inproc_sync_signal( struct object *obj, int signal );
+static void inproc_sync_destroy( struct object *obj );
+
+static const struct object_ops inproc_sync_ops =
+{
+    sizeof(struct inproc_sync), /* size */
+    &no_type,                   /* type */
+    inproc_sync_dump,           /* dump */
+    NULL,                       /* add_queue */
+    NULL,                       /* remove_queue */
+    NULL,                       /* signaled */
+    NULL,                       /* satisfied */
+    inproc_sync_signal,         /* signal */
+    no_get_fd,                  /* get_fd */
+    default_get_sync,           /* get_sync */
+    default_map_access,         /* map_access */
+    default_get_sd,             /* get_sd */
+    default_set_sd,             /* set_sd */
+    default_get_full_name,      /* get_full_name */
+    no_lookup_name,             /* lookup_name */
+    directory_link_name,        /* link_name */
+    default_unlink_name,        /* unlink_name */
+    no_open_file,               /* open_file */
+    no_kernel_obj_list,         /* get_kernel_obj_list */
+    no_close_handle,            /* close_handle */
+    inproc_sync_destroy,        /* destroy */
+};
+
+struct object *create_inproc_event_sync( int manual, int signaled )
+{
+    struct ntsync_event_args args = {.signaled = signaled, .manual = manual};
+    struct inproc_sync *event;
+
+    if (!(event = alloc_object( &inproc_sync_ops ))) return NULL;
+    event->type = INPROC_SYNC_EVENT;
+    event->fd = ioctl( get_inproc_device_fd(), NTSYNC_IOC_CREATE_EVENT, &args );
+    list_init( &event->entry );
+
+    return &event->obj;
+}
+
+struct object *create_inproc_semaphore_sync( unsigned int initial, unsigned int max )
+{
+    struct ntsync_sem_args args = {.count = initial, .max = max};
+    struct inproc_sync *sem;
+
+    if (!(sem = alloc_object( &inproc_sync_ops ))) return NULL;
+    sem->type = INPROC_SYNC_SEMAPHORE;
+    sem->fd = ioctl( get_inproc_device_fd(), NTSYNC_IOC_CREATE_SEM, &args );
+    list_init( &sem->entry );
+    return &sem->obj;
+}
+
+struct object *create_inproc_mutex_sync( thread_id_t owner, unsigned int count )
+{
+    struct ntsync_mutex_args args = {.owner = owner, .count = count};
+    struct inproc_sync *mutex;
+
+    if (!(mutex = alloc_object( &inproc_sync_ops ))) return NULL;
+    mutex->type = INPROC_SYNC_MUTEX;
+    mutex->fd = ioctl( get_inproc_device_fd(), NTSYNC_IOC_CREATE_MUTEX, &args );
+    list_add_tail( &inproc_mutexes, &mutex->entry );
+    return &mutex->obj;
+}
+
+static void inproc_sync_dump( struct object *obj, int verbose )
+{
+    struct inproc_sync *sync = (struct inproc_sync *)obj;
+    assert( obj->ops == &inproc_sync_ops );
+    fprintf( stderr, "Inproc sync type=%d, fd=%d\n", sync->type, sync->fd );
+}
+
+static int inproc_sync_signal( struct object *obj, int signal )
+{
+    struct inproc_sync *sync = (struct inproc_sync *)obj;
+    __u32 count;
+
+    assert( obj->ops == &inproc_sync_ops );
+    assert( sync->type == INPROC_SYNC_EVENT );
+    assert( signal == 0 || signal == 1 );
+
+    if (signal)
+    {
+        if (debug_level) fprintf( stderr, "set_inproc_event %d\n", sync->fd );
+        ioctl( sync->fd, NTSYNC_IOC_EVENT_SET, &count );
+    }
+    else
+    {
+        if (debug_level) fprintf( stderr, "reset_inproc_event %d\n", sync->fd );
+        ioctl( sync->fd, NTSYNC_IOC_EVENT_RESET, &count );
+    }
+
+    return 1;
+}
+
+static void inproc_sync_destroy( struct object *obj )
+{
+    struct inproc_sync *sync = (struct inproc_sync *)obj;
+    assert( obj->ops == &inproc_sync_ops );
+    list_remove( &sync->entry );
+    close( sync->fd );
+}
+
+void abandon_inproc_mutexes( thread_id_t tid )
+{
+    struct inproc_sync *mutex;
+
+    LIST_FOR_EACH_ENTRY( mutex, &inproc_mutexes, struct inproc_sync, entry )
+        ioctl( mutex->fd, NTSYNC_IOC_MUTEX_KILL, &tid );
+}
+
+int get_inproc_sync_fd( struct object *obj, unsigned char *type )
+{
+    struct object *sync;
+    int fd = -1;
+
+    if (obj != (struct object *)current->queue) sync = get_obj_sync( obj );
+    else sync = thread_queue_inproc_sync( current );
+    if (!sync) return -1;
+
+    if (sync->ops == &inproc_sync_ops)
+    {
+        struct inproc_sync *inproc = (struct inproc_sync *)sync;
+        *type = inproc->type;
+        fd = inproc->fd;
+    }
+
+    release_object( sync );
+    return fd;
+}
+
+#else /* NTSYNC_IOC_EVENT_READ */
+
+int get_inproc_device_fd(void)
+{
+    return -1;
+}
+
+struct object *create_inproc_event_sync( int manual, int signaled )
+{
+    return NULL;
+}
+
+struct object *create_inproc_semaphore_sync( unsigned int initial, unsigned int max )
+{
+    return NULL;
+}
+
+struct object *create_inproc_mutex_sync( thread_id_t owner, unsigned int count )
+{
+    return NULL;
+}
+
+void abandon_inproc_mutexes( thread_id_t tid )
+{
+}
+
+int get_inproc_sync_fd( struct object *obj, unsigned char *type )
+{
+    return -1;
+}
+
+#endif /* NTSYNC_IOC_EVENT_READ */
+
+DECL_HANDLER(get_inproc_sync_fd)
+{
+    struct object *obj;
+    int fd;
+
+    if (!(obj = get_handle_obj( current->process, req->handle, 0, NULL ))) return;
+
+    reply->access = get_handle_access( current->process, req->handle );
+    reply->internal = check_signal_access( obj, reply->access ) == STATUS_OBJECT_TYPE_MISMATCH;
+
+    if ((fd = get_inproc_sync_fd( obj, &reply->type )) < 0) set_error( STATUS_NOT_IMPLEMENTED );
+    else send_client_fd( current->process, fd, req->handle );
+
+    release_object( obj );
+}
+
+DECL_HANDLER(select_inproc_queue)
+{
+    if (!thread_queue_select( current, req->select )) return;
+    if (req->select) check_thread_queue_idle( current );
+    if (req->signaled) thread_queue_satisfied( current );
+}
diff --git a/server/mailslot.c b/server/mailslot.c
index 430566d..8cc7c43 100644
--- a/server/mailslot.c
+++ b/server/mailslot.c
@@ -83,7 +83,7 @@ static const struct object_ops mailslot_ops =
     NULL,                      /* remove_queue */
     NULL,                      /* signaled */
     NULL,                      /* satisfied */
-    no_signal,                 /* signal */
+    NULL,                      /* signal */
     mailslot_get_fd,           /* get_fd */
     default_fd_get_sync,       /* get_sync */
     mailslot_map_access,       /* map_access */
@@ -141,13 +141,13 @@ static const struct object_ops mail_writer_ops =
     sizeof(struct mail_writer), /* size */
     &file_type,                 /* type */
     mail_writer_dump,           /* dump */
-    no_add_queue,               /* add_queue */
+    NULL,                       /* add_queue */
     NULL,                       /* remove_queue */
     NULL,                       /* signaled */
     NULL,                       /* satisfied */
-    no_signal,                  /* signal */
+    NULL,                       /* signal */
     mail_writer_get_fd,         /* get_fd */
-    default_get_sync,           /* get_sync */
+    no_get_sync,                /* get_sync */
     mail_writer_map_access,     /* map_access */
     default_get_sd,             /* get_sd */
     default_set_sd,             /* set_sd */
@@ -207,13 +207,13 @@ static const struct object_ops mailslot_device_ops =
     sizeof(struct mailslot_device), /* size */
     &device_type,                   /* type */
     mailslot_device_dump,           /* dump */
-    no_add_queue,                   /* add_queue */
+    NULL,                           /* add_queue */
     NULL,                           /* remove_queue */
     NULL,                           /* signaled */
-    no_satisfied,                   /* satisfied */
-    no_signal,                      /* signal */
+    NULL,                           /* satisfied */
+    NULL,                           /* signal */
     no_get_fd,                      /* get_fd */
-    default_get_sync,               /* get_sync */
+    no_get_sync,                    /* get_sync */
     default_map_access,             /* map_access */
     default_get_sd,                 /* get_sd */
     default_set_sd,                 /* set_sd */
@@ -242,7 +242,7 @@ static const struct object_ops mailslot_device_file_ops =
     NULL,                                   /* remove_queue */
     NULL,                                   /* signaled */
     NULL,                                   /* satisfied */
-    no_signal,                              /* signal */
+    NULL,                                   /* signal */
     mailslot_device_file_get_fd,            /* get_fd */
     default_fd_get_sync,                    /* get_sync */
     default_map_access,                     /* map_access */
diff --git a/server/mapping.c b/server/mapping.c
index c3f57b6..1aa00c6 100644
--- a/server/mapping.c
+++ b/server/mapping.c
@@ -64,13 +64,13 @@ static const struct object_ops ranges_ops =
     sizeof(struct ranges),     /* size */
     &no_type,                  /* type */
     ranges_dump,               /* dump */
-    no_add_queue,              /* add_queue */
+    NULL,                      /* add_queue */
     NULL,                      /* remove_queue */
     NULL,                      /* signaled */
     NULL,                      /* satisfied */
-    no_signal,                 /* signal */
+    NULL,                      /* signal */
     no_get_fd,                 /* get_fd */
-    default_get_sync,          /* get_sync */
+    no_get_sync,               /* get_sync */
     default_map_access,        /* map_access */
     default_get_sd,            /* get_sd */
     default_set_sd,            /* set_sd */
@@ -101,13 +101,13 @@ static const struct object_ops shared_map_ops =
     sizeof(struct shared_map), /* size */
     &no_type,                  /* type */
     shared_map_dump,           /* dump */
-    no_add_queue,              /* add_queue */
+    NULL,                      /* add_queue */
     NULL,                      /* remove_queue */
     NULL,                      /* signaled */
     NULL,                      /* satisfied */
-    no_signal,                 /* signal */
+    NULL,                      /* signal */
     no_get_fd,                 /* get_fd */
-    default_get_sync,          /* get_sync */
+    no_get_sync,               /* get_sync */
     default_map_access,        /* map_access */
     default_get_sd,            /* get_sd */
     default_set_sd,            /* set_sd */
@@ -175,13 +175,13 @@ static const struct object_ops mapping_ops =
     sizeof(struct mapping),      /* size */
     &mapping_type,               /* type */
     mapping_dump,                /* dump */
-    no_add_queue,                /* add_queue */
+    NULL,                        /* add_queue */
     NULL,                        /* remove_queue */
     NULL,                        /* signaled */
     NULL,                        /* satisfied */
-    no_signal,                   /* signal */
+    NULL,                        /* signal */
     mapping_get_fd,              /* get_fd */
-    default_get_sync,            /* get_sync */
+    no_get_sync,                 /* get_sync */
     default_map_access,          /* map_access */
     default_get_sd,              /* get_sd */
     default_set_sd,              /* set_sd */
diff --git a/server/mutex.c b/server/mutex.c
index e370d30..0638dd6 100644
--- a/server/mutex.c
+++ b/server/mutex.c
@@ -48,6 +48,7 @@ struct type_descr mutex_type =
         STANDARD_RIGHTS_EXECUTE | SYNCHRONIZE,
         MUTANT_ALL_ACCESS
     },
+    SYNCHRONIZE,                          /* signal access */
 };
 
 struct mutex_sync
@@ -62,6 +63,7 @@ struct mutex_sync
 static void mutex_sync_dump( struct object *obj, int verbose );
 static int mutex_sync_signaled( struct object *obj, struct wait_queue_entry *entry );
 static void mutex_sync_satisfied( struct object *obj, struct wait_queue_entry *entry );
+static int mutex_sync_signal( struct object *obj, int signal );
 static void mutex_sync_destroy( struct object *obj );
 
 static const struct object_ops mutex_sync_ops =
@@ -73,7 +75,7 @@ static const struct object_ops mutex_sync_ops =
     remove_queue,              /* remove_queue */
     mutex_sync_signaled,       /* signaled */
     mutex_sync_satisfied,      /* satisfied */
-    no_signal,                 /* signal */
+    mutex_sync_signal,         /* signal */
     no_get_fd,                 /* get_fd */
     default_get_sync,          /* get_sync */
     default_map_access,        /* map_access */
@@ -153,28 +155,36 @@ static void mutex_sync_satisfied( struct object *obj, struct wait_queue_entry *e
     mutex->abandoned = 0;
 }
 
-static struct mutex_sync *create_mutex_sync( int owned )
+static int mutex_sync_signal( struct object *obj, int signal )
+{
+    struct mutex_sync *mutex = (struct mutex_sync *)obj;
+    assert( obj->ops == &mutex_sync_ops );
+    return do_release( mutex, current, 1 );
+}
+
+static struct object *create_mutex_sync( int owned )
 {
     struct mutex_sync *mutex;
 
+    if (get_inproc_device_fd() >= 0) return create_inproc_mutex_sync( owned ? current->id : 0, owned ? 1 : 0 );
+
     if (!(mutex = alloc_object( &mutex_sync_ops ))) return NULL;
     mutex->count = 0;
     mutex->owner = NULL;
     mutex->abandoned = 0;
     if (owned) do_grab( mutex, current );
 
-    return mutex;
+    return &mutex->obj;
 }
 
 struct mutex
 {
     struct object       obj;             /* object header */
-    struct mutex_sync  *sync;            /* mutex sync object */
+    struct object      *sync;            /* mutex sync object */
 };
 
 static void mutex_dump( struct object *obj, int verbose );
 static struct object *mutex_get_sync( struct object *obj );
-static int mutex_signal( struct object *obj, unsigned int access );
 static void mutex_destroy( struct object *obj );
 
 static const struct object_ops mutex_ops =
@@ -186,7 +196,7 @@ static const struct object_ops mutex_ops =
     NULL,                      /* remove_queue */
     NULL,                      /* signaled */
     NULL,                      /* satisfied */
-    mutex_signal,              /* signal */
+    NULL,                      /* signal */
     no_get_fd,                 /* get_fd */
     mutex_get_sync,            /* get_sync */
     default_map_access,        /* map_access */
@@ -235,13 +245,15 @@ void abandon_mutexes( struct thread *thread )
         mutex->abandoned = 1;
         do_release( mutex, thread, mutex->count );
     }
+
+    abandon_inproc_mutexes( thread->id );
 }
 
 static void mutex_dump( struct object *obj, int verbose )
 {
     struct mutex *mutex = (struct mutex *)obj;
     assert( obj->ops == &mutex_ops );
-    mutex->sync->obj.ops->dump( &mutex->sync->obj, verbose );
+    mutex->sync->ops->dump( mutex->sync, verbose );
 }
 
 static struct object *mutex_get_sync( struct object *obj )
@@ -251,19 +263,6 @@ static struct object *mutex_get_sync( struct object *obj )
     return grab_object( mutex->sync );
 }
 
-static int mutex_signal( struct object *obj, unsigned int access )
-{
-    struct mutex *mutex = (struct mutex *)obj;
-    assert( obj->ops == &mutex_ops );
-
-    if (!(access & SYNCHRONIZE))
-    {
-        set_error( STATUS_ACCESS_DENIED );
-        return 0;
-    }
-    return do_release( mutex->sync, current, 1 );
-}
-
 static void mutex_destroy( struct object *obj )
 {
     struct mutex *mutex = (struct mutex *)obj;
@@ -312,8 +311,11 @@ DECL_HANDLER(release_mutex)
     if ((mutex = (struct mutex *)get_handle_obj( current->process, req->handle,
                                                  0, &mutex_ops )))
     {
-        reply->prev_count = mutex->sync->count;
-        do_release( mutex->sync, current, 1 );
+        struct mutex_sync *sync = (struct mutex_sync *)mutex->sync;
+        assert( mutex->sync->ops == &mutex_sync_ops );
+
+        reply->prev_count = sync->count;
+        do_release( sync, current, 1 );
         release_object( mutex );
     }
 }
@@ -326,9 +328,12 @@ DECL_HANDLER(query_mutex)
     if ((mutex = (struct mutex *)get_handle_obj( current->process, req->handle,
                                                  MUTANT_QUERY_STATE, &mutex_ops )))
     {
-        reply->count = mutex->sync->count;
-        reply->owned = (mutex->sync->owner == current);
-        reply->abandoned = mutex->sync->abandoned;
+        struct mutex_sync *sync = (struct mutex_sync *)mutex->sync;
+        assert( mutex->sync->ops == &mutex_sync_ops );
+
+        reply->count = sync->count;
+        reply->owned = (sync->owner == current);
+        reply->abandoned = sync->abandoned;
 
         release_object( mutex );
     }
diff --git a/server/named_pipe.c b/server/named_pipe.c
index e67b25b..1a9bac3 100644
--- a/server/named_pipe.c
+++ b/server/named_pipe.c
@@ -116,13 +116,13 @@ static const struct object_ops named_pipe_ops =
     sizeof(struct named_pipe),    /* size */
     &no_type,                     /* type */
     named_pipe_dump,              /* dump */
-    no_add_queue,                 /* add_queue */
+    NULL,                         /* add_queue */
     NULL,                         /* remove_queue */
     NULL,                         /* signaled */
     NULL,                         /* satisfied */
-    no_signal,                    /* signal */
+    NULL,                         /* signal */
     no_get_fd,                    /* get_fd */
-    default_get_sync,             /* get_sync */
+    no_get_sync,                  /* get_sync */
     named_pipe_map_access,        /* map_access */
     default_get_sd,               /* get_sd */
     default_set_sd,               /* set_sd */
@@ -169,7 +169,7 @@ static const struct object_ops pipe_server_ops =
     NULL,                         /* remove_queue */
     NULL,                         /* signaled */
     NULL,                         /* satisfied */
-    no_signal,                    /* signal */
+    NULL,                         /* signal */
     pipe_end_get_fd,              /* get_fd */
     default_fd_get_sync,          /* get_sync */
     default_map_access,           /* map_access */
@@ -214,7 +214,7 @@ static const struct object_ops pipe_client_ops =
     NULL,                         /* remove_queue */
     NULL,                         /* signaled */
     NULL,                         /* satisfied */
-    no_signal,                    /* signal */
+    NULL,                         /* signal */
     pipe_end_get_fd,              /* get_fd */
     default_fd_get_sync,          /* get_sync */
     default_map_access,           /* map_access */
@@ -259,13 +259,13 @@ static const struct object_ops named_pipe_device_ops =
     sizeof(struct named_pipe_device), /* size */
     &device_type,                     /* type */
     named_pipe_device_dump,           /* dump */
-    no_add_queue,                     /* add_queue */
+    NULL,                             /* add_queue */
     NULL,                             /* remove_queue */
     NULL,                             /* signaled */
-    no_satisfied,                     /* satisfied */
-    no_signal,                        /* signal */
+    NULL,                             /* satisfied */
+    NULL,                             /* signal */
     no_get_fd,                        /* get_fd */
-    default_get_sync,                 /* get_sync */
+    no_get_sync,                      /* get_sync */
     default_map_access,               /* map_access */
     default_get_sd,                   /* get_sd */
     default_set_sd,                   /* set_sd */
@@ -295,7 +295,7 @@ static const struct object_ops named_pipe_device_file_ops =
     NULL,                                    /* remove_queue */
     NULL,                                    /* signaled */
     NULL,                                    /* satisfied */
-    no_signal,                               /* signal */
+    NULL,                                    /* signal */
     named_pipe_device_file_get_fd,           /* get_fd */
     default_fd_get_sync,                     /* get_sync */
     default_map_access,                      /* map_access */
@@ -346,7 +346,7 @@ static const struct object_ops named_pipe_dir_ops =
     NULL,                                    /* remove_queue */
     NULL,                                    /* signaled */
     NULL,                                    /* satisfied */
-    no_signal,                               /* signal */
+    NULL,                                    /* signal */
     named_pipe_dir_get_fd,                   /* get_fd */
     default_fd_get_sync,                     /* get_sync */
     default_map_access,                      /* map_access */
diff --git a/server/object.c b/server/object.c
index e8f0dba..2994a05 100644
--- a/server/object.c
+++ b/server/object.c
@@ -106,13 +106,13 @@ static const struct object_ops apc_reserve_ops =
     sizeof(struct reserve),     /* size */
     &apc_reserve_type,          /* type */
     dump_reserve,               /* dump */
-    no_add_queue,               /* add_queue */
+    NULL,                       /* add_queue */
     NULL,                       /* remove_queue */
     NULL,                       /* signaled */
-    no_satisfied,               /* satisfied */
-    no_signal,                  /* signal */
+    NULL,                       /* satisfied */
+    NULL,                       /* signal */
     no_get_fd,                  /* get_fd */
-    default_get_sync,           /* get_sync */
+    no_get_sync,                /* get_sync */
     default_map_access,         /* map_access */
     default_get_sd,             /* get_sd */
     default_set_sd,             /* set_sd */
@@ -131,13 +131,13 @@ static const struct object_ops completion_reserve_ops =
     sizeof(struct reserve),    /* size */
     &completion_reserve_type,  /* type */
     dump_reserve,              /* dump */
-    no_add_queue,              /* add_queue */
+    NULL,                      /* add_queue */
     NULL,                      /* remove_queue */
     NULL,                      /* signaled */
-    no_satisfied,              /* satisfied */
-    no_signal,                 /* signal */
+    NULL,                      /* satisfied */
+    NULL,                      /* signal */
     no_get_fd,                 /* get_fd */
-    default_get_sync,          /* get_sync */
+    no_get_sync,               /* get_sync */
     default_map_access,        /* map_access */
     default_get_sd,            /* get_sd */
     default_set_sd,            /* set_sd */
@@ -620,22 +620,6 @@ struct namespace *create_namespace( unsigned int hash_size )
 
 /* functions for unimplemented/default object operations */
 
-int no_add_queue( struct object *obj, struct wait_queue_entry *entry )
-{
-    set_error( STATUS_OBJECT_TYPE_MISMATCH );
-    return 0;
-}
-
-void no_satisfied( struct object *obj, struct wait_queue_entry *entry )
-{
-}
-
-int no_signal( struct object *obj, unsigned int access )
-{
-    set_error( STATUS_OBJECT_TYPE_MISMATCH );
-    return 0;
-}
-
 struct fd *no_get_fd( struct object *obj )
 {
     set_error( STATUS_OBJECT_TYPE_MISMATCH );
@@ -647,6 +631,11 @@ struct object *default_get_sync( struct object *obj )
     return grab_object( obj );
 }
 
+struct object *no_get_sync( struct object *obj )
+{
+    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 3e51903..9eecb57 100644
--- a/server/object.h
+++ b/server/object.h
@@ -56,6 +56,7 @@ struct type_descr
     struct unicode_str name;          /* type name */
     unsigned int       valid_access;  /* mask for valid access bits */
     struct generic_map mapping;       /* generic access mapping */
+    unsigned int       signal_access; /* mask for valid signal access */
     unsigned int       index;         /* index in global array of types */
     unsigned int       obj_count;     /* count of objects of this type */
     unsigned int       handle_count;  /* count of handles of this type */
@@ -80,8 +81,8 @@ struct object_ops
     int  (*signaled)(struct object *,struct wait_queue_entry *);
     /* wait satisfied */
     void (*satisfied)(struct object *,struct wait_queue_entry *);
-    /* signal an object */
-    int  (*signal)(struct object *, unsigned int);
+    /* signal/reset an object */
+    int  (*signal)(struct object *,int);
     /* return an fd object that can be used to read/write from the object */
     struct fd *(*get_fd)(struct object *);
     /* return a sync that can be used to wait/signal the object */
@@ -168,11 +169,9 @@ extern void release_object( void *obj );
 extern struct object *find_object( const struct namespace *namespace, const struct unicode_str *name,
                                    unsigned int attributes );
 extern struct object *find_object_index( const struct namespace *namespace, unsigned int index );
-extern int no_add_queue( struct object *obj, struct wait_queue_entry *entry );
-extern void no_satisfied( struct object *obj, struct wait_queue_entry *entry );
-extern int no_signal( struct object *obj, unsigned int access );
 extern struct fd *no_get_fd( struct object *obj );
 extern struct object *default_get_sync( struct object *obj );
+extern struct object *no_get_sync( struct object *obj );
 static inline struct object *get_obj_sync( struct object *obj ) { return obj->ops->get_sync( obj ); }
 extern unsigned int default_map_access( struct object *obj, unsigned int access );
 extern struct security_descriptor *default_get_sd( struct object *obj );
@@ -221,13 +220,12 @@ static inline void *mem_append( void *ptr, const void *src, data_size_t len )
 
 /* event functions */
 
-struct event_sync;
 struct event;
 struct keyed_event;
 
-extern struct event_sync *create_event_sync( int manual, int signaled );
-extern void signal_sync( struct event_sync *sync );
-extern void reset_sync( struct event_sync *sync );
+extern struct object *create_event_sync( int manual, int signaled );
+extern void signal_sync( struct object *sync );
+extern void reset_sync( struct object *sync );
 
 extern struct event *create_event( struct object *root, const struct unicode_str *name,
                                    unsigned int attr, int manual_reset, int initial_state,
@@ -243,6 +241,15 @@ extern void reset_event( struct event *event );
 
 extern void abandon_mutexes( struct thread *thread );
 
+/* in-process synchronization functions */
+
+extern int get_inproc_device_fd(void);
+extern struct object *create_inproc_event_sync( int manual, int signaled );
+extern struct object *create_inproc_semaphore_sync( unsigned int initial, unsigned int max );
+extern struct object *create_inproc_mutex_sync( thread_id_t owner, unsigned int count );
+extern void abandon_inproc_mutexes( thread_id_t owner );
+extern int get_inproc_sync_fd( struct object *obj, unsigned char *type );
+
 /* 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 8e0f918..28f7b6f 100644
--- a/server/process.c
+++ b/server/process.c
@@ -107,7 +107,7 @@ static const struct object_ops process_ops =
     NULL,                        /* remove_queue */
     NULL,                        /* signaled */
     NULL,                        /* satisfied */
-    no_signal,                   /* signal */
+    NULL,                        /* signal */
     no_get_fd,                   /* get_fd */
     process_get_sync,            /* get_sync */
     process_map_access,          /* map_access */
@@ -140,7 +140,7 @@ static const struct fd_ops process_fd_ops =
 struct startup_info
 {
     struct object               obj;            /* object header */
-    struct event_sync          *sync;           /* sync object for wait/signal */
+    struct object              *sync;           /* sync object for wait/signal */
     struct process             *process;        /* created process */
     data_size_t                 info_size;      /* size of startup info */
     data_size_t                 data_size;      /* size of whole startup data */
@@ -160,7 +160,7 @@ static const struct object_ops startup_info_ops =
     NULL,                          /* remove_queue */
     NULL,                          /* signaled */
     NULL,                          /* satisfied */
-    no_signal,                     /* signal */
+    NULL,                          /* signal */
     no_get_fd,                     /* get_fd */
     startup_info_get_sync,         /* get_sync */
     default_map_access,            /* map_access */
@@ -200,7 +200,7 @@ static void job_destroy( struct object *obj );
 struct job
 {
     struct object        obj;               /* object header */
-    struct event_sync   *sync;              /* sync object for wait/signal */
+    struct object       *sync;              /* sync object for wait/signal */
     struct list          process_list;      /* list of processes */
     int                  num_processes;     /* count of running processes */
     int                  total_processes;   /* count of processes which have been assigned */
@@ -222,7 +222,7 @@ static const struct object_ops job_ops =
     NULL,                          /* remove_queue */
     NULL,                          /* signaled */
     NULL,                          /* satisfied */
-    no_signal,                     /* signal */
+    NULL,                          /* signal */
     no_get_fd,                     /* get_fd */
     job_get_sync,                  /* get_sync */
     default_map_access,            /* map_access */
diff --git a/server/process.h b/server/process.h
index 251af45..a229ad9 100644
--- a/server/process.h
+++ b/server/process.h
@@ -36,7 +36,7 @@ enum startup_state { STARTUP_IN_PROGRESS, STARTUP_DONE, STARTUP_ABORTED };
 struct process
 {
     struct object        obj;             /* object header */
-    struct event_sync   *sync;            /* sync object for wait/signal */
+    struct object       *sync;            /* sync object for wait/signal */
     struct list          entry;           /* entry in system-wide process list */
     process_id_t         parent_id;       /* parent process id (at the time of creation) */
     struct list          thread_list;     /* thread list */
diff --git a/server/protocol.def b/server/protocol.def
index 82aed0d..56e2c5e 100644
--- a/server/protocol.def
+++ b/server/protocol.def
@@ -1126,6 +1126,8 @@ struct obj_locator
     thread_id_t  tid;          /* thread id of the new thread */
     timeout_t    server_start; /* server start time */
     unsigned int session_id;   /* process session id */
+    obj_handle_t inproc_device;/* inproc device fd in flight with this handle */
+    obj_handle_t alert_handle; /* alert fd is in flight with this handle */
     data_size_t  info_size;    /* total size of startup info */
     VARARG(machines,ushorts);  /* array of supported machines */
 @END
@@ -1140,6 +1142,7 @@ struct obj_locator
     client_ptr_t entry;        /* entry point (in thread address space) */
 @REPLY
     int          suspend;      /* is thread suspended? */
+    obj_handle_t alert_handle; /* alert fd is in flight with this handle */
 @END
 
 
@@ -4141,3 +4144,28 @@ struct handle_info
 @REPLY
     int enable;                /* previous state of auto-repeat enable */
 @END
+
+
+enum inproc_sync_type
+{
+    INPROC_SYNC_UNKNOWN = 0,
+    INPROC_SYNC_EVENT = 1,
+    INPROC_SYNC_MUTEX = 2,
+    INPROC_SYNC_SEMAPHORE = 3,
+};
+
+/* Get the in-process synchronization fd associated with the waitable handle */
+@REQ(get_inproc_sync_fd)
+    obj_handle_t handle;        /* handle to the object */
+@REPLY
+    unsigned char type;         /* inproc sync type */
+    unsigned char internal;     /* sync is an internal event sync */
+    unsigned int access;        /* handle access rights */
+@END
+
+
+/* Begin/end a client-side wait on a message queue */
+@REQ(select_inproc_queue)
+    int          select;          /* 1: begin waiting, 0: end waiting */
+    int          signaled;        /* was the queue signaled? */
+@END
diff --git a/server/queue.c b/server/queue.c
index 10d5983..af7bae3 100644
--- a/server/queue.c
+++ b/server/queue.c
@@ -116,6 +116,7 @@ struct msg_queue
 {
     struct object          obj;             /* object header */
     struct fd             *fd;              /* optional file descriptor to poll */
+    struct object         *inproc_sync;     /* inproc sync for client-side waits */
     int                    signaled;        /* queue is signaled from fd POLLIN or masks */
     int                    paint_count;     /* pending paint messages count */
     int                    hotkey_count;    /* pending hotkey messages count */
@@ -169,7 +170,7 @@ static const struct object_ops msg_queue_ops =
     msg_queue_remove_queue,    /* remove_queue */
     msg_queue_signaled,        /* signaled */
     msg_queue_satisfied,       /* satisfied */
-    no_signal,                 /* signal */
+    NULL,                      /* signal */
     no_get_fd,                 /* get_fd */
     default_get_sync,          /* get_sync */
     default_map_access,        /* map_access */
@@ -203,13 +204,13 @@ static const struct object_ops thread_input_ops =
     sizeof(struct thread_input),  /* size */
     &no_type,                     /* type */
     thread_input_dump,            /* dump */
-    no_add_queue,                 /* add_queue */
+    NULL,                         /* add_queue */
     NULL,                         /* remove_queue */
     NULL,                         /* signaled */
     NULL,                         /* satisfied */
-    no_signal,                    /* signal */
+    NULL,                         /* signal */
     no_get_fd,                    /* get_fd */
-    default_get_sync,             /* get_sync */
+    no_get_sync,                  /* get_sync */
     default_map_access,           /* map_access */
     default_get_sd,               /* get_sd */
     default_set_sd,               /* set_sd */
@@ -308,6 +309,7 @@ static struct msg_queue *create_msg_queue( struct thread *thread, struct thread_
     if ((queue = alloc_object( &msg_queue_ops )))
     {
         queue->fd              = NULL;
+        queue->inproc_sync     = NULL;
         queue->signaled        = 0;
         queue->paint_count     = 0;
         queue->hotkey_count    = 0;
@@ -328,11 +330,8 @@ static struct msg_queue *create_msg_queue( struct thread *thread, struct thread_
         list_init( &queue->expired_timers );
         for (i = 0; i < NB_MSG_KINDS; i++) list_init( &queue->msg_list[i] );
 
-        if (!(queue->shared = alloc_shared_object()))
-        {
-            release_object( queue );
-            return NULL;
-        }
+        if (get_inproc_device_fd() >= 0 && !(queue->inproc_sync = create_inproc_event_sync( 1, 0 ))) goto error;
+        if (!(queue->shared = alloc_shared_object())) goto error;
 
         SHARED_WRITE_BEGIN( queue->shared, queue_shm_t )
         {
@@ -354,6 +353,10 @@ static struct msg_queue *create_msg_queue( struct thread *thread, struct thread_
     }
     if (new_input) release_object( new_input );
     return queue;
+
+error:
+    release_object( queue );
+    return NULL;
 }
 
 /* free the message queue of a thread at thread exit */
@@ -718,11 +721,13 @@ static void signal_queue_sync( struct msg_queue *queue )
     if (queue->signaled) return;
     queue->signaled = 1;
     wake_up( &queue->obj, 0 );
+    if (queue->inproc_sync) signal_sync( queue->inproc_sync );
 }
 
 static void reset_queue_sync( struct msg_queue *queue )
 {
     queue->signaled = 0;
+    if (queue->inproc_sync) reset_sync( queue->inproc_sync );
 }
 
 /* check the queue status */
@@ -1365,6 +1370,16 @@ static void msg_queue_satisfied( struct object *obj, struct wait_queue_entry *en
     reset_queue_sync( queue );
 }
 
+int thread_queue_select( struct thread *thread, int select )
+{
+    return msg_queue_select( thread->queue, select );
+}
+
+void thread_queue_satisfied( struct thread *thread )
+{
+    msg_queue_satisfied( &thread->queue->obj, NULL );
+}
+
 static void msg_queue_destroy( struct object *obj )
 {
     struct msg_queue *queue = (struct msg_queue *)obj;
@@ -1408,6 +1423,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 )
@@ -1497,6 +1513,12 @@ int init_thread_queue( struct thread *thread )
     return (create_msg_queue( thread, NULL ) != NULL);
 }
 
+struct object *thread_queue_inproc_sync( struct thread *thread )
+{
+    if (!thread->queue) return NULL;
+    return grab_object( thread->queue->inproc_sync );
+}
+
 /* attach two thread input data structures */
 int attach_thread_input( struct thread *thread_from, struct thread *thread_to )
 {
diff --git a/server/registry.c b/server/registry.c
index b59bd63..b11af54 100644
--- a/server/registry.c
+++ b/server/registry.c
@@ -177,13 +177,13 @@ static const struct object_ops key_ops =
     sizeof(struct key),      /* size */
     &key_type,               /* type */
     key_dump,                /* dump */
-    no_add_queue,            /* add_queue */
+    NULL,                    /* add_queue */
     NULL,                    /* remove_queue */
     NULL,                    /* signaled */
     NULL,                    /* satisfied */
-    no_signal,               /* signal */
+    NULL,                    /* signal */
     no_get_fd,               /* get_fd */
-    default_get_sync,        /* get_sync */
+    no_get_sync,             /* get_sync */
     key_map_access,          /* map_access */
     key_get_sd,              /* get_sd */
     default_set_sd,          /* set_sd */
diff --git a/server/request.c b/server/request.c
index 835ea30..92f5b42 100644
--- a/server/request.c
+++ b/server/request.c
@@ -86,13 +86,13 @@ static const struct object_ops master_socket_ops =
     sizeof(struct master_socket),  /* size */
     &no_type,                      /* type */
     master_socket_dump,            /* dump */
-    no_add_queue,                  /* add_queue */
+    NULL,                          /* add_queue */
     NULL,                          /* remove_queue */
     NULL,                          /* signaled */
     NULL,                          /* satisfied */
-    no_signal,                     /* signal */
+    NULL,                          /* signal */
     no_get_fd,                     /* get_fd */
-    default_get_sync,              /* get_sync */
+    no_get_sync,                   /* get_sync */
     default_map_access,            /* map_access */
     default_get_sd,                /* get_sd */
     default_set_sd,                /* set_sd */
diff --git a/server/request_handlers.h b/server/request_handlers.h
index d042a45..2069d5b 100644
--- a/server/request_handlers.h
+++ b/server/request_handlers.h
@@ -305,6 +305,8 @@ DECL_HANDLER(resume_process);
 DECL_HANDLER(get_next_process);
 DECL_HANDLER(get_next_thread);
 DECL_HANDLER(set_keyboard_repeat);
+DECL_HANDLER(get_inproc_sync_fd);
+DECL_HANDLER(select_inproc_queue);
 
 typedef void (*req_handler)( const void *req, void *reply );
 static const req_handler req_handlers[REQ_NB_REQUESTS] =
@@ -607,6 +609,8 @@ static const req_handler req_handlers[REQ_NB_REQUESTS] =
     (req_handler)req_get_next_process,
     (req_handler)req_get_next_thread,
     (req_handler)req_set_keyboard_repeat,
+    (req_handler)req_get_inproc_sync_fd,
+    (req_handler)req_select_inproc_queue,
 };
 
 C_ASSERT( sizeof(abstime_t) == 8 );
@@ -709,8 +713,10 @@ C_ASSERT( offsetof(struct init_first_thread_reply, pid) == 8 );
 C_ASSERT( offsetof(struct init_first_thread_reply, tid) == 12 );
 C_ASSERT( offsetof(struct init_first_thread_reply, server_start) == 16 );
 C_ASSERT( offsetof(struct init_first_thread_reply, session_id) == 24 );
-C_ASSERT( offsetof(struct init_first_thread_reply, info_size) == 28 );
-C_ASSERT( sizeof(struct init_first_thread_reply) == 32 );
+C_ASSERT( offsetof(struct init_first_thread_reply, inproc_device) == 28 );
+C_ASSERT( offsetof(struct init_first_thread_reply, alert_handle) == 32 );
+C_ASSERT( offsetof(struct init_first_thread_reply, info_size) == 36 );
+C_ASSERT( sizeof(struct init_first_thread_reply) == 40 );
 C_ASSERT( offsetof(struct init_thread_request, unix_tid) == 12 );
 C_ASSERT( offsetof(struct init_thread_request, reply_fd) == 16 );
 C_ASSERT( offsetof(struct init_thread_request, wait_fd) == 20 );
@@ -718,6 +724,7 @@ C_ASSERT( offsetof(struct init_thread_request, teb) == 24 );
 C_ASSERT( offsetof(struct init_thread_request, entry) == 32 );
 C_ASSERT( sizeof(struct init_thread_request) == 40 );
 C_ASSERT( offsetof(struct init_thread_reply, suspend) == 8 );
+C_ASSERT( offsetof(struct init_thread_reply, alert_handle) == 12 );
 C_ASSERT( sizeof(struct init_thread_reply) == 16 );
 C_ASSERT( offsetof(struct terminate_process_request, handle) == 12 );
 C_ASSERT( offsetof(struct terminate_process_request, exit_code) == 16 );
@@ -2301,3 +2308,12 @@ 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( offsetof(struct get_inproc_sync_fd_request, handle) == 12 );
+C_ASSERT( sizeof(struct get_inproc_sync_fd_request) == 16 );
+C_ASSERT( offsetof(struct get_inproc_sync_fd_reply, type) == 8 );
+C_ASSERT( offsetof(struct get_inproc_sync_fd_reply, internal) == 9 );
+C_ASSERT( offsetof(struct get_inproc_sync_fd_reply, access) == 12 );
+C_ASSERT( sizeof(struct get_inproc_sync_fd_reply) == 16 );
+C_ASSERT( offsetof(struct select_inproc_queue_request, select) == 12 );
+C_ASSERT( offsetof(struct select_inproc_queue_request, signaled) == 16 );
+C_ASSERT( sizeof(struct select_inproc_queue_request) == 24 );
diff --git a/server/request_trace.h b/server/request_trace.h
index 079acda..a8eeca9 100644
--- a/server/request_trace.h
+++ b/server/request_trace.h
@@ -148,6 +148,8 @@ static void dump_init_first_thread_reply( const struct init_first_thread_reply *
     fprintf( stderr, ", tid=%04x", req->tid );
     dump_timeout( ", server_start=", &req->server_start );
     fprintf( stderr, ", session_id=%08x", req->session_id );
+    fprintf( stderr, ", inproc_device=%04x", req->inproc_device );
+    fprintf( stderr, ", alert_handle=%04x", req->alert_handle );
     fprintf( stderr, ", info_size=%u", req->info_size );
     dump_varargs_ushorts( ", machines=", cur_size );
 }
@@ -164,6 +166,7 @@ static void dump_init_thread_request( const struct init_thread_request *req )
 static void dump_init_thread_reply( const struct init_thread_reply *req )
 {
     fprintf( stderr, " suspend=%d", req->suspend );
+    fprintf( stderr, ", alert_handle=%04x", req->alert_handle );
 }
 
 static void dump_terminate_process_request( const struct terminate_process_request *req )
@@ -3402,6 +3405,24 @@ static void dump_set_keyboard_repeat_reply( const struct set_keyboard_repeat_rep
     fprintf( stderr, " enable=%d", req->enable );
 }
 
+static void dump_get_inproc_sync_fd_request( const struct get_inproc_sync_fd_request *req )
+{
+    fprintf( stderr, " handle=%04x", req->handle );
+}
+
+static void dump_get_inproc_sync_fd_reply( const struct get_inproc_sync_fd_reply *req )
+{
+    fprintf( stderr, " type=%02x", req->type );
+    fprintf( stderr, ", internal=%02x", req->internal );
+    fprintf( stderr, ", access=%08x", req->access );
+}
+
+static void dump_select_inproc_queue_request( const struct select_inproc_queue_request *req )
+{
+    fprintf( stderr, " select=%d", req->select );
+    fprintf( stderr, ", signaled=%d", req->signaled );
+}
+
 typedef void (*dump_func)( const void *req );
 
 static const dump_func req_dumpers[REQ_NB_REQUESTS] =
@@ -3704,6 +3725,8 @@ static const dump_func req_dumpers[REQ_NB_REQUESTS] =
     (dump_func)dump_get_next_process_request,
     (dump_func)dump_get_next_thread_request,
     (dump_func)dump_set_keyboard_repeat_request,
+    (dump_func)dump_get_inproc_sync_fd_request,
+    (dump_func)dump_select_inproc_queue_request,
 };
 
 static const dump_func reply_dumpers[REQ_NB_REQUESTS] =
@@ -4006,6 +4029,8 @@ static const dump_func reply_dumpers[REQ_NB_REQUESTS] =
     (dump_func)dump_get_next_process_reply,
     (dump_func)dump_get_next_thread_reply,
     (dump_func)dump_set_keyboard_repeat_reply,
+    (dump_func)dump_get_inproc_sync_fd_reply,
+    NULL,
 };
 
 static const char * const req_names[REQ_NB_REQUESTS] =
@@ -4308,6 +4333,8 @@ static const char * const req_names[REQ_NB_REQUESTS] =
     "get_next_process",
     "get_next_thread",
     "set_keyboard_repeat",
+    "get_inproc_sync_fd",
+    "select_inproc_queue",
 };
 
 static const struct
diff --git a/server/semaphore.c b/server/semaphore.c
index 4b31bfe..8a350c6 100644
--- a/server/semaphore.c
+++ b/server/semaphore.c
@@ -48,6 +48,7 @@ struct type_descr semaphore_type =
         STANDARD_RIGHTS_EXECUTE | SYNCHRONIZE,
         SEMAPHORE_ALL_ACCESS
     },
+    SEMAPHORE_MODIFY_STATE,                       /* signal access */
 };
 
 struct semaphore_sync
@@ -60,6 +61,7 @@ struct semaphore_sync
 static void semaphore_sync_dump( struct object *obj, int verbose );
 static int semaphore_sync_signaled( struct object *obj, struct wait_queue_entry *entry );
 static void semaphore_sync_satisfied( struct object *obj, struct wait_queue_entry *entry );
+static int semaphore_sync_signal( struct object *obj, int signal );
 
 static const struct object_ops semaphore_sync_ops =
 {
@@ -70,7 +72,7 @@ static const struct object_ops semaphore_sync_ops =
     remove_queue,                  /* remove_queue */
     semaphore_sync_signaled,       /* signaled */
     semaphore_sync_satisfied,      /* satisfied */
-    no_signal,                     /* signal */
+    semaphore_sync_signal,         /* signal */
     no_get_fd,                     /* get_fd */
     default_get_sync,              /* get_sync */
     default_map_access,            /* map_access */
@@ -130,25 +132,33 @@ static void semaphore_sync_satisfied( struct object *obj, struct wait_queue_entr
     sem->count--;
 }
 
-static struct semaphore_sync *create_semaphore_sync( unsigned int initial, unsigned int max )
+static int semaphore_sync_signal( struct object *obj, int signal )
+{
+    struct semaphore_sync *sem = (struct semaphore_sync *)obj;
+    assert( obj->ops == &semaphore_sync_ops );
+    return release_semaphore( sem, 1, NULL );
+}
+
+static struct object *create_semaphore_sync( unsigned int initial, unsigned int max )
 {
     struct semaphore_sync *sem;
 
+    if (get_inproc_device_fd() >= 0) return create_inproc_semaphore_sync( initial, max );
+
     if (!(sem = alloc_object( &semaphore_sync_ops ))) return NULL;
     sem->count = initial;
     sem->max   = max;
-    return sem;
+    return &sem->obj;
 }
 
 struct semaphore
 {
     struct object          obj;    /* object header */
-    struct semaphore_sync *sync;   /* semaphore sync object */
+    struct object         *sync;   /* semaphore sync object */
 };
 
 static void semaphore_dump( struct object *obj, int verbose );
 static struct object *semaphore_get_sync( struct object *obj );
-static int semaphore_signal( struct object *obj, unsigned int access );
 static void semaphore_destroy( struct object *obj );
 
 static const struct object_ops semaphore_ops =
@@ -160,7 +170,7 @@ static const struct object_ops semaphore_ops =
     NULL,                          /* remove_queue */
     NULL,                          /* signaled */
     NULL,                          /* satisfied */
-    semaphore_signal,              /* signal */
+    NULL,                          /* signal */
     no_get_fd,                     /* get_fd */
     semaphore_get_sync,            /* get_sync */
     default_map_access,            /* map_access */
@@ -208,7 +218,7 @@ static void semaphore_dump( struct object *obj, int verbose )
 {
     struct semaphore *sem = (struct semaphore *)obj;
     assert( obj->ops == &semaphore_ops );
-    sem->sync->obj.ops->dump( &sem->sync->obj, verbose );
+    sem->sync->ops->dump( sem->sync, verbose );
 }
 
 static struct object *semaphore_get_sync( struct object *obj )
@@ -218,19 +228,6 @@ static struct object *semaphore_get_sync( struct object *obj )
     return grab_object( sem->sync );
 }
 
-static int semaphore_signal( struct object *obj, unsigned int access )
-{
-    struct semaphore *sem = (struct semaphore *)obj;
-    assert( obj->ops == &semaphore_ops );
-
-    if (!(access & SEMAPHORE_MODIFY_STATE))
-    {
-        set_error( STATUS_ACCESS_DENIED );
-        return 0;
-    }
-    return release_semaphore( sem->sync, 1, NULL );
-}
-
 static void semaphore_destroy( struct object *obj )
 {
     struct semaphore *sem = (struct semaphore *)obj;
@@ -279,7 +276,10 @@ DECL_HANDLER(release_semaphore)
     if ((sem = (struct semaphore *)get_handle_obj( current->process, req->handle,
                                                    SEMAPHORE_MODIFY_STATE, &semaphore_ops )))
     {
-        release_semaphore( sem->sync, req->count, &reply->prev_count );
+        struct semaphore_sync *sync = (struct semaphore_sync *)sem->sync;
+        assert( sem->sync->ops == &semaphore_sync_ops );
+
+        release_semaphore( sync, req->count, &reply->prev_count );
         release_object( sem );
     }
 }
@@ -292,8 +292,11 @@ DECL_HANDLER(query_semaphore)
     if ((sem = (struct semaphore *)get_handle_obj( current->process, req->handle,
                                                    SEMAPHORE_QUERY_STATE, &semaphore_ops )))
     {
-        reply->current = sem->sync->count;
-        reply->max = sem->sync->max;
+        struct semaphore_sync *sync = (struct semaphore_sync *)sem->sync;
+        assert( sem->sync->ops == &semaphore_sync_ops );
+
+        reply->current = sync->count;
+        reply->max = sync->max;
         release_object( sem );
     }
 }
diff --git a/server/serial.c b/server/serial.c
index 66cb4aa..4015008 100644
--- a/server/serial.c
+++ b/server/serial.c
@@ -92,7 +92,7 @@ static const struct object_ops serial_ops =
     NULL,                         /* remove_queue */
     NULL,                         /* signaled */
     NULL,                         /* satisfied */
-    no_signal,                    /* signal */
+    NULL,                         /* signal */
     serial_get_fd,                /* get_fd */
     default_fd_get_sync,          /* get_sync */
     default_map_access,           /* map_access */
diff --git a/server/signal.c b/server/signal.c
index 078951a..1e72a46 100644
--- a/server/signal.c
+++ b/server/signal.c
@@ -59,13 +59,13 @@ static const struct object_ops handler_ops =
     sizeof(struct handler),   /* size */
     &no_type,                 /* type */
     handler_dump,             /* dump */
-    no_add_queue,             /* add_queue */
+    NULL,                     /* add_queue */
     NULL,                     /* remove_queue */
     NULL,                     /* signaled */
     NULL,                     /* satisfied */
-    no_signal,                /* signal */
+    NULL,                     /* signal */
     no_get_fd,                /* get_fd */
-    default_get_sync,         /* get_sync */
+    no_get_sync,              /* get_sync */
     default_map_access,       /* map_access */
     default_get_sd,           /* get_sd */
     default_set_sd,           /* set_sd */
diff --git a/server/sock.c b/server/sock.c
index 1911605..7045181 100644
--- a/server/sock.c
+++ b/server/sock.c
@@ -492,7 +492,7 @@ static const struct object_ops sock_ops =
     NULL,                         /* remove_queue */
     NULL,                         /* signaled */
     NULL,                         /* satisfied */
-    no_signal,                    /* signal */
+    NULL,                         /* signal */
     sock_get_fd,                  /* get_fd */
     default_fd_get_sync,          /* get_sync */
     default_map_access,           /* map_access */
@@ -3891,13 +3891,13 @@ static const struct object_ops ifchange_ops =
     sizeof(struct ifchange), /* size */
     &no_type,                /* type */
     ifchange_dump,           /* dump */
-    no_add_queue,            /* add_queue */
+    NULL,                    /* add_queue */
     NULL,                    /* remove_queue */
     NULL,                    /* signaled */
-    no_satisfied,            /* satisfied */
-    no_signal,               /* signal */
+    NULL,                    /* satisfied */
+    NULL,                    /* signal */
     ifchange_get_fd,         /* get_fd */
-    default_get_sync,        /* get_sync */
+    no_get_sync,             /* get_sync */
     default_map_access,      /* map_access */
     default_get_sd,          /* get_sd */
     default_set_sd,          /* set_sd */
@@ -4113,13 +4113,13 @@ static const struct object_ops socket_device_ops =
     sizeof(struct object),      /* size */
     &device_type,               /* type */
     socket_device_dump,         /* dump */
-    no_add_queue,               /* add_queue */
+    NULL,                       /* add_queue */
     NULL,                       /* remove_queue */
     NULL,                       /* signaled */
-    no_satisfied,               /* satisfied */
-    no_signal,                  /* signal */
+    NULL,                       /* satisfied */
+    NULL,                       /* signal */
     no_get_fd,                  /* get_fd */
-    default_get_sync,           /* get_sync */
+    no_get_sync,                /* get_sync */
     default_map_access,         /* map_access */
     default_get_sd,             /* get_sd */
     default_set_sd,             /* set_sd */
diff --git a/server/symlink.c b/server/symlink.c
index 238dcfa..fd991f2 100644
--- a/server/symlink.c
+++ b/server/symlink.c
@@ -68,13 +68,13 @@ static const struct object_ops symlink_ops =
     sizeof(struct symlink),       /* size */
     &symlink_type,                /* type */
     symlink_dump,                 /* dump */
-    no_add_queue,                 /* add_queue */
+    NULL,                         /* add_queue */
     NULL,                         /* remove_queue */
     NULL,                         /* signaled */
     NULL,                         /* satisfied */
-    no_signal,                    /* signal */
+    NULL,                         /* signal */
     no_get_fd,                    /* get_fd */
-    default_get_sync,             /* get_sync */
+    no_get_sync,                  /* get_sync */
     default_map_access,           /* map_access */
     default_get_sd,               /* get_sd */
     default_set_sd,               /* set_sd */
diff --git a/server/thread.c b/server/thread.c
index b0d2f0a..39acaef 100644
--- a/server/thread.c
+++ b/server/thread.c
@@ -85,7 +85,7 @@ struct thread_wait
 struct thread_apc
 {
     struct object       obj;      /* object header */
-    struct event_sync  *sync;     /* sync object for wait/signal */
+    struct object      *sync;     /* sync object for wait/signal */
     struct list         entry;    /* queue linked list */
     struct thread      *caller;   /* thread that queued this apc */
     struct object      *owner;    /* object that queued this apc */
@@ -109,7 +109,7 @@ static const struct object_ops thread_apc_ops =
     NULL,                       /* remove_queue */
     NULL,                       /* signaled */
     NULL,                       /* satisfied */
-    no_signal,                  /* signal */
+    NULL,                       /* signal */
     no_get_fd,                  /* get_fd */
     thread_apc_get_sync,        /* get_sync */
     default_map_access,         /* map_access */
@@ -131,7 +131,7 @@ static const struct object_ops thread_apc_ops =
 struct context
 {
     struct object           obj;        /* object header */
-    struct event_sync      *sync;       /* sync object for wait/signal */
+    struct object          *sync;       /* sync object for wait/signal */
     unsigned int            status;     /* status of the context */
     struct context_data     regs[2];    /* context data */
 };
@@ -154,7 +154,7 @@ static const struct object_ops context_ops =
     NULL,                       /* remove_queue */
     NULL,                       /* signaled */
     NULL,                       /* satisfied */
-    no_signal,                  /* signal */
+    NULL,                       /* signal */
     no_get_fd,                  /* get_fd */
     context_get_sync,           /* get_sync */
     default_map_access,         /* map_access */
@@ -204,7 +204,7 @@ static const struct object_ops thread_ops =
     NULL,                       /* remove_queue */
     NULL,                       /* signaled */
     NULL,                       /* satisfied */
-    no_signal,                  /* signal */
+    NULL,                       /* signal */
     no_get_fd,                  /* get_fd */
     thread_get_sync,            /* get_sync */
     thread_map_access,          /* map_access */
@@ -397,6 +397,7 @@ static inline void init_thread_structure( struct thread *thread )
     int i;
 
     thread->sync            = NULL;
+    thread->alert_sync      = NULL;
     thread->unix_pid        = -1;  /* not known yet */
     thread->unix_tid        = -1;  /* not known yet */
     thread->context         = NULL;
@@ -561,6 +562,7 @@ struct thread *create_thread( int fd, struct process *process, const struct secu
     }
     if (!(thread->request_fd = create_anonymous_fd( &thread_fd_ops, fd, &thread->obj, 0 ))) goto error;
     if (!(thread->sync = create_event_sync( 1, 0 ))) goto error;
+    if (get_inproc_device_fd() >= 0 && !(thread->alert_sync = create_inproc_event_sync( 1, 0 ))) goto error;
 
     if (process->desktop)
     {
@@ -656,6 +658,7 @@ static void destroy_thread( struct object *obj )
     if (thread->exit_poll) remove_timeout_user( thread->exit_poll );
     if (thread->id) free_ptid( thread->id );
     if (thread->token) release_object( thread->token );
+    if (thread->alert_sync) release_object( thread->alert_sync );
     if (thread->sync) release_object( thread->sync );
 }
 
@@ -1016,8 +1019,15 @@ static void object_sync_remove_queue( struct object *obj, struct wait_queue_entr
 
 static int object_sync_add_queue( struct object *obj, struct wait_queue_entry *entry )
 {
-    struct object *sync = get_obj_sync( obj );
-    int ret = sync->ops->add_queue( sync, entry );
+    struct object *sync;
+    int ret;
+
+    if (!(sync = get_obj_sync( obj )))
+    {
+        set_error( STATUS_OBJECT_TYPE_MISMATCH );
+        return 0;
+    }
+    ret = sync->ops->add_queue( sync, entry );
     release_object( sync );
     return ret;
 }
@@ -1030,6 +1040,26 @@ static int object_sync_signaled( struct object *obj, struct wait_queue_entry *en
     return ret;
 }
 
+void signal_sync( struct object *obj )
+{
+    assert( obj->ops->signal );
+    obj->ops->signal( obj, 1 );
+}
+
+void reset_sync( struct object *obj )
+{
+    assert( obj->ops->signal );
+    obj->ops->signal( obj, 0 );
+}
+
+static int object_sync_signal( struct object *obj )
+{
+    struct object *sync = get_obj_sync( obj );
+    int ret = sync->ops->signal( sync, -1 );
+    release_object( sync );
+    return ret;
+}
+
 /* finish waiting */
 static unsigned int end_wait( struct thread *thread, unsigned int status )
 {
@@ -1259,6 +1289,14 @@ static void thread_timeout( void *ptr )
     wake_thread( thread );
 }
 
+/* check if an event flag, a semaphore or a mutex can be signaled */
+unsigned int check_signal_access( struct object *obj, unsigned int access )
+{
+    if (!obj->ops->type->signal_access) return STATUS_OBJECT_TYPE_MISMATCH;
+    if (!(access & obj->ops->type->signal_access)) return STATUS_ACCESS_DENIED;
+    return STATUS_SUCCESS;
+}
+
 /* try signaling an event flag, a semaphore or a mutex */
 static int signal_object( obj_handle_t handle )
 {
@@ -1268,7 +1306,9 @@ static int signal_object( obj_handle_t handle )
     obj = get_handle_obj( current->process, handle, 0, NULL );
     if (obj)
     {
-        ret = obj->ops->signal( obj, get_handle_access( current->process, handle ));
+        unsigned int status, access = get_handle_access( current->process, handle );
+        if ((status = check_signal_access( obj, access ))) set_error( status );
+        else ret = object_sync_signal( obj );
         release_object( obj );
     }
     return ret;
@@ -1442,7 +1482,11 @@ 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 */
+    {
+        if (apc->call.type == APC_USER && thread->alert_sync)
+            signal_sync( thread->alert_sync );
         wake_thread( thread );
+    }
 
     return 1;
 }
@@ -1474,6 +1518,8 @@ void thread_cancel_apc( struct thread *thread, struct object *owner, enum apc_ty
         apc->executed = 1;
         signal_sync( apc->sync );
         release_object( apc );
+        if (list_empty( &thread->user_apc ) && thread->alert_sync)
+            reset_sync( thread->alert_sync );
         return;
     }
 }
@@ -1488,6 +1534,8 @@ 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->alert_sync)
+            reset_sync( thread->alert_sync );
     }
     return apc;
 }
@@ -1736,6 +1784,8 @@ static int init_thread( struct thread *thread, int reply_fd, int wait_fd )
 DECL_HANDLER(init_first_thread)
 {
     struct process *process = current->process;
+    unsigned char type;
+    int fd;
 
     if (!init_thread( current, req->reply_fd, req->wait_fd )) return;
 
@@ -1758,11 +1808,25 @@ DECL_HANDLER(init_first_thread)
     reply->server_start = server_start_time;
     set_reply_data( supported_machines,
                     min( supported_machines_count * sizeof(unsigned short), get_reply_max_size() ));
+
+    if ((fd = get_inproc_device_fd()) >= 0)
+    {
+        reply->inproc_device = get_process_id( process ) | 1;
+        send_client_fd( process, fd, reply->inproc_device );
+    }
+    if (current->alert_sync && (fd = get_inproc_sync_fd( current->alert_sync, &type )) >= 0)
+    {
+        reply->alert_handle = get_thread_id( current ) | 1;
+        send_client_fd( process, fd, reply->alert_handle );
+    }
 }
 
 /* initialize a new thread */
 DECL_HANDLER(init_thread)
 {
+    unsigned char type;
+    int fd;
+
     if (!init_thread( current, req->reply_fd, req->wait_fd )) return;
 
     if (!is_valid_address(req->teb))
@@ -1782,6 +1846,12 @@ DECL_HANDLER(init_thread)
     set_thread_affinity( current, current->affinity );
 
     reply->suspend = (is_thread_suspended( current ) || current->context != NULL);
+
+    if (current->alert_sync && (fd = get_inproc_sync_fd( current->alert_sync, &type )) >= 0)
+    {
+        reply->alert_handle = get_thread_id( current ) | 1;
+        send_client_fd( current->process, fd, reply->alert_handle );
+    }
 }
 
 /* terminate a thread */
diff --git a/server/thread.h b/server/thread.h
index 0b017e0..88a17f1 100644
--- a/server/thread.h
+++ b/server/thread.h
@@ -50,7 +50,8 @@ struct inflight_fd
 struct thread
 {
     struct object          obj;           /* object header */
-    struct event_sync     *sync;          /* sync object for wait/signal */
+    struct object         *sync;          /* sync object for wait/signal */
+    struct object         *alert_sync;    /* thread alert sync */
     struct list            entry;         /* entry in system-wide thread list */
     struct list            proc_entry;    /* entry in per-process thread list */
     struct list            desktop_entry; /* entry in per-desktop thread list */
@@ -118,6 +119,7 @@ extern void set_wait_status( struct wait_queue_entry *entry, int status );
 extern void stop_thread( struct thread *thread );
 extern int wake_thread( struct thread *thread );
 extern int wake_thread_queue_entry( struct wait_queue_entry *entry );
+extern unsigned int check_signal_access( struct object *obj, unsigned int access );
 extern int add_queue( struct object *obj, struct wait_queue_entry *entry );
 extern void remove_queue( struct object *obj, struct wait_queue_entry *entry );
 extern void kill_thread( struct thread *thread, int violent_death );
diff --git a/server/timer.c b/server/timer.c
index 522fc6a..3911db6 100644
--- a/server/timer.c
+++ b/server/timer.c
@@ -53,7 +53,7 @@ struct type_descr timer_type =
 struct timer
 {
     struct object        obj;       /* object header */
-    struct event_sync   *sync;      /* sync object for wait/signal */
+    struct object       *sync;      /* sync object for wait/signal */
     int                  manual;    /* manual reset */
     int                  signaled;  /* current signaled state */
     unsigned int         period;    /* timer period in ms */
@@ -77,7 +77,7 @@ static const struct object_ops timer_ops =
     NULL,                      /* remove_queue */
     NULL,                      /* signaled */
     NULL,                      /* satisfied */
-    no_signal,                 /* signal */
+    NULL,                      /* signal */
     no_get_fd,                 /* get_fd */
     timer_get_sync,            /* get_sync */
     default_map_access,        /* map_access */
diff --git a/server/token.c b/server/token.c
index 5ce7298..c7d93e9 100644
--- a/server/token.c
+++ b/server/token.c
@@ -142,13 +142,13 @@ static const struct object_ops token_ops =
     sizeof(struct token),      /* size */
     &token_type,               /* type */
     token_dump,                /* dump */
-    no_add_queue,              /* add_queue */
+    NULL,                      /* add_queue */
     NULL,                      /* remove_queue */
     NULL,                      /* signaled */
     NULL,                      /* satisfied */
-    no_signal,                 /* signal */
+    NULL,                      /* signal */
     no_get_fd,                 /* get_fd */
-    default_get_sync,          /* get_sync */
+    no_get_sync,               /* get_sync */
     default_map_access,        /* map_access */
     default_get_sd,            /* get_sd */
     token_set_sd,              /* set_sd */
diff --git a/server/user.h b/server/user.h
index 0ebda06..7a97ef3 100644
--- a/server/user.h
+++ b/server/user.h
@@ -119,7 +119,10 @@ extern void add_queue_hook_count( struct thread *thread, unsigned int index, int
 extern void inc_queue_paint_count( struct thread *thread, int incr );
 extern void queue_cleanup_window( struct thread *thread, user_handle_t win );
 extern int init_thread_queue( struct thread *thread );
+extern int thread_queue_select( struct thread *thread, int select );
+extern void thread_queue_satisfied( struct thread *thread );
 extern void check_thread_queue_idle( struct thread *thread );
+extern struct object *thread_queue_inproc_sync( struct thread *thread );
 extern int attach_thread_input( struct thread *thread_from, struct thread *thread_to );
 extern void detach_thread_input( struct thread *thread_from );
 extern void set_clip_rectangle( struct desktop *desktop, const struct rectangle *rect,
diff --git a/server/window.c b/server/window.c
index e237d40..1c0d655 100644
--- a/server/window.c
+++ b/server/window.c
@@ -105,13 +105,13 @@ static const struct object_ops window_ops =
     sizeof(struct window),    /* size */
     &no_type,                 /* type */
     window_dump,              /* dump */
-    no_add_queue,             /* add_queue */
+    NULL,                     /* add_queue */
     NULL,                     /* remove_queue */
     NULL,                     /* signaled */
     NULL,                     /* satisfied */
-    no_signal,                /* signal */
+    NULL,                     /* signal */
     no_get_fd,                /* get_fd */
-    default_get_sync,         /* get_sync */
+    no_get_sync,              /* get_sync */
     default_map_access,       /* map_access */
     default_get_sd,           /* get_sd */
     default_set_sd,           /* set_sd */
diff --git a/server/winstation.c b/server/winstation.c
index bfcd4a5..3d153a4 100644
--- a/server/winstation.c
+++ b/server/winstation.c
@@ -73,13 +73,13 @@ static const struct object_ops winstation_ops =
     sizeof(struct winstation),    /* size */
     &winstation_type,             /* type */
     winstation_dump,              /* dump */
-    no_add_queue,                 /* add_queue */
+    NULL,                         /* add_queue */
     NULL,                         /* remove_queue */
     NULL,                         /* signaled */
     NULL,                         /* satisfied */
-    no_signal,                    /* signal */
+    NULL,                         /* signal */
     no_get_fd,                    /* get_fd */
-    default_get_sync,             /* get_sync */
+    no_get_sync,                  /* get_sync */
     default_map_access,           /* map_access */
     default_get_sd,               /* get_sd */
     default_set_sd,               /* set_sd */
@@ -114,13 +114,13 @@ static const struct object_ops desktop_ops =
     sizeof(struct desktop),       /* size */
     &desktop_type,                /* type */
     desktop_dump,                 /* dump */
-    no_add_queue,                 /* add_queue */
+    NULL,                         /* add_queue */
     NULL,                         /* remove_queue */
     NULL,                         /* signaled */
     NULL,                         /* satisfied */
-    no_signal,                    /* signal */
+    NULL,                         /* signal */
     no_get_fd,                    /* get_fd */
-    default_get_sync,             /* get_sync */
+    no_get_sync,                  /* get_sync */
     default_map_access,           /* map_access */
     default_get_sd,               /* get_sd */
     default_set_sd,               /* set_sd */
openSUSE Build Service is sponsored by