File ntsync5-mainline.patch of Package wine-test

From 9fde9619983713909a965e5c22c8f4a6c21620d3 Mon Sep 17 00:00:00 2001
From: Elizabeth Figura <zfigura@codeweavers.com>
Date: Tue, 9 Mar 2021 12:08:29 -0600
Subject: [PATCH 01/29] ntdll: Add some traces to synchronization methods.

Normally we can rely on +server for these, but with fast synchronization we'll
be skipping the server.
---
 dlls/ntdll/unix/sync.c | 59 ++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 57 insertions(+), 2 deletions(-)

diff --git a/dlls/ntdll/unix/sync.c b/dlls/ntdll/unix/sync.c
index 2b01aaf83b8..774266ae14e 100644
--- a/dlls/ntdll/unix/sync.c
+++ b/dlls/ntdll/unix/sync.c
@@ -74,9 +74,11 @@ HANDLE keyed_event = 0;
 static const char *debugstr_timeout( const LARGE_INTEGER *timeout )
 {
     if (!timeout) return "(infinite)";
-    return wine_dbgstr_longlong( timeout->QuadPart );
+    return wine_dbg_sprintf( "%lld.%07ld", (long long)(timeout->QuadPart / TICKSPERSEC),
+                             (long)(timeout->QuadPart % TICKSPERSEC) );
 }
 
+
 /* return a monotonic time counter, in Win32 ticks */
 static inline ULONGLONG monotonic_counter(void)
 {
@@ -311,6 +313,9 @@ NTSTATUS WINAPI NtCreateSemaphore( HANDLE *handle, ACCESS_MASK access, const OBJ
     data_size_t len;
     struct object_attributes *objattr;
 
+    TRACE( "access %#x, name %s, initial %d, max %d\n", (int)access,
+           attr ? debugstr_us(attr->ObjectName) : "(null)", (int)initial, (int)max );
+
     *handle = 0;
     if (max <= 0 || initial < 0 || initial > max) return STATUS_INVALID_PARAMETER;
     if ((ret = alloc_object_attributes( attr, &objattr, &len ))) return ret;
@@ -338,6 +343,8 @@ NTSTATUS WINAPI NtOpenSemaphore( HANDLE *handle, ACCESS_MASK access, const OBJEC
 {
     unsigned int ret;
 
+    TRACE( "access %#x, name %s\n", (int)access, attr ? debugstr_us(attr->ObjectName) : "(null)" );
+
     *handle = 0;
     if ((ret = validate_open_object_attributes( attr ))) return ret;
 
@@ -397,6 +404,8 @@ NTSTATUS WINAPI NtReleaseSemaphore( HANDLE handle, ULONG count, ULONG *previous
 {
     unsigned int ret;
 
+    TRACE( "handle %p, count %u, prev_count %p\n", handle, (int)count, previous );
+
     SERVER_START_REQ( release_semaphore )
     {
         req->handle = wine_server_obj_handle( handle );
@@ -421,6 +430,9 @@ NTSTATUS WINAPI NtCreateEvent( HANDLE *handle, ACCESS_MASK access, const OBJECT_
     data_size_t len;
     struct object_attributes *objattr;
 
+    TRACE( "access %#x, name %s, type %u, state %u\n", (int)access,
+           attr ? debugstr_us(attr->ObjectName) : "(null)", type, state );
+
     *handle = 0;
     if (type != NotificationEvent && type != SynchronizationEvent) return STATUS_INVALID_PARAMETER;
     if ((ret = alloc_object_attributes( attr, &objattr, &len ))) return ret;
@@ -448,6 +460,8 @@ NTSTATUS WINAPI NtOpenEvent( HANDLE *handle, ACCESS_MASK access, const OBJECT_AT
 {
     unsigned int ret;
 
+    TRACE( "access %#x, name %s\n", (int)access, attr ? debugstr_us(attr->ObjectName) : "(null)" );
+
     *handle = 0;
     if ((ret = validate_open_object_attributes( attr ))) return ret;
 
@@ -473,6 +487,8 @@ NTSTATUS WINAPI NtSetEvent( HANDLE handle, LONG *prev_state )
 {
     unsigned int ret;
 
+    TRACE( "handle %p, prev_state %p\n", handle, prev_state );
+
     SERVER_START_REQ( event_op )
     {
         req->handle = wine_server_obj_handle( handle );
@@ -492,6 +508,8 @@ NTSTATUS WINAPI NtResetEvent( HANDLE handle, LONG *prev_state )
 {
     unsigned int ret;
 
+    TRACE( "handle %p, prev_state %p\n", handle, prev_state );
+
     SERVER_START_REQ( event_op )
     {
         req->handle = wine_server_obj_handle( handle );
@@ -521,6 +539,8 @@ NTSTATUS WINAPI NtPulseEvent( HANDLE handle, LONG *prev_state )
 {
     unsigned int ret;
 
+    TRACE( "handle %p, prev_state %p\n", handle, prev_state );
+
     SERVER_START_REQ( event_op )
     {
         req->handle = wine_server_obj_handle( handle );
@@ -577,6 +597,9 @@ NTSTATUS WINAPI NtCreateMutant( HANDLE *handle, ACCESS_MASK access, const OBJECT
     data_size_t len;
     struct object_attributes *objattr;
 
+    TRACE( "access %#x, name %s, owned %u\n", (int)access,
+           attr ? debugstr_us(attr->ObjectName) : "(null)", owned );
+
     *handle = 0;
     if ((ret = alloc_object_attributes( attr, &objattr, &len ))) return ret;
 
@@ -602,6 +625,8 @@ NTSTATUS WINAPI NtOpenMutant( HANDLE *handle, ACCESS_MASK access, const OBJECT_A
 {
     unsigned int ret;
 
+    TRACE( "access %#x, name %s\n", (int)access, attr ? debugstr_us(attr->ObjectName) : "(null)" );
+
     *handle = 0;
     if ((ret = validate_open_object_attributes( attr ))) return ret;
 
@@ -627,6 +652,8 @@ NTSTATUS WINAPI NtReleaseMutant( HANDLE handle, LONG *prev_count )
 {
     unsigned int ret;
 
+    TRACE( "handle %p, prev_count %p\n", handle, prev_count );
+
     SERVER_START_REQ( release_mutex )
     {
         req->handle = wine_server_obj_handle( handle );
@@ -1426,6 +1453,9 @@ NTSTATUS WINAPI NtCreateTimer( HANDLE *handle, ACCESS_MASK access, const OBJECT_
     data_size_t len;
     struct object_attributes *objattr;
 
+    TRACE( "access %#x, name %s, type %u\n", (int)access,
+           attr ? debugstr_us(attr->ObjectName) : "(null)", type );
+
     *handle = 0;
     if (type != NotificationTimer && type != SynchronizationTimer) return STATUS_INVALID_PARAMETER;
     if ((ret = alloc_object_attributes( attr, &objattr, &len ))) return ret;
@@ -1453,6 +1483,8 @@ NTSTATUS WINAPI NtOpenTimer( HANDLE *handle, ACCESS_MASK access, const OBJECT_AT
 {
     unsigned int ret;
 
+    TRACE( "access %#x, name %s\n", (int)access, attr ? debugstr_us(attr->ObjectName) : "(null)" );
+
     *handle = 0;
     if ((ret = validate_open_object_attributes( attr ))) return ret;
 
@@ -1506,6 +1538,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 +1608,23 @@ 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 (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;
 }
 
 
@@ -1602,6 +1646,8 @@ NTSTATUS WINAPI NtSignalAndWaitForSingleObject( HANDLE signal, HANDLE wait,
     union select_op select_op;
     UINT flags = SELECT_INTERRUPTIBLE;
 
+    TRACE( "signal %p, wait %p, alertable %u, timeout %s\n", signal, wait, alertable, debugstr_timeout(timeout) );
+
     if (!signal) return STATUS_INVALID_HANDLE;
 
     if (alertable) flags |= SELECT_ALERTABLE;
@@ -1846,6 +1892,9 @@ NTSTATUS WINAPI NtCreateKeyedEvent( HANDLE *handle, ACCESS_MASK access,
     data_size_t len;
     struct object_attributes *objattr;
 
+    TRACE( "access %#x, name %s, flags %#x\n", (int)access,
+           attr ? debugstr_us(attr->ObjectName) : "(null)", (int)flags );
+
     *handle = 0;
     if ((ret = alloc_object_attributes( attr, &objattr, &len ))) return ret;
 
@@ -1870,6 +1919,8 @@ NTSTATUS WINAPI NtOpenKeyedEvent( HANDLE *handle, ACCESS_MASK access, const OBJE
 {
     unsigned int ret;
 
+    TRACE( "access %#x, name %s\n", (int)access, attr ? debugstr_us(attr->ObjectName) : "(null)" );
+
     *handle = 0;
     if ((ret = validate_open_object_attributes( attr ))) return ret;
 
@@ -1896,6 +1947,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 +1968,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;
-- 
GitLab


From c61235f607aafe429962712b424eb969f2e9c3fc Mon Sep 17 00:00:00 2001
From: Elizabeth Figura <zfigura@codeweavers.com>
Date: Mon, 10 Feb 2025 16:18:15 -0600
Subject: [PATCH 02/29] ntdll: Add stub functions for in-process
 synchronization.

---
 dlls/ntdll/unix/sync.c | 96 ++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 96 insertions(+)

diff --git a/dlls/ntdll/unix/sync.c b/dlls/ntdll/unix/sync.c
index 774266ae14e..0a296d86d6d 100644
--- a/dlls/ntdll/unix/sync.c
+++ b/dlls/ntdll/unix/sync.c
@@ -303,6 +303,59 @@ static unsigned int validate_open_object_attributes( const OBJECT_ATTRIBUTES *at
 }
 
 
+static NTSTATUS inproc_release_semaphore( HANDLE handle, ULONG count, ULONG *prev_count )
+{
+    return STATUS_NOT_IMPLEMENTED;
+}
+
+static NTSTATUS inproc_query_semaphore( HANDLE handle, SEMAPHORE_BASIC_INFORMATION *info )
+{
+    return STATUS_NOT_IMPLEMENTED;
+}
+
+static NTSTATUS inproc_set_event( HANDLE handle, LONG *prev_state )
+{
+    return STATUS_NOT_IMPLEMENTED;
+}
+
+static NTSTATUS inproc_reset_event( HANDLE handle, LONG *prev_state )
+{
+    return STATUS_NOT_IMPLEMENTED;
+}
+
+static NTSTATUS inproc_pulse_event( HANDLE handle, LONG *prev_state )
+{
+    return STATUS_NOT_IMPLEMENTED;
+}
+
+static NTSTATUS inproc_query_event( HANDLE handle, EVENT_BASIC_INFORMATION *info )
+{
+    return STATUS_NOT_IMPLEMENTED;
+}
+
+static NTSTATUS inproc_release_mutex( HANDLE handle, LONG *prev_count )
+{
+    return STATUS_NOT_IMPLEMENTED;
+}
+
+static NTSTATUS inproc_query_mutex( HANDLE handle, MUTANT_BASIC_INFORMATION *info )
+{
+    return STATUS_NOT_IMPLEMENTED;
+}
+
+static NTSTATUS inproc_wait( DWORD count, const HANDLE *handles, BOOLEAN wait_any,
+                             BOOLEAN alertable, const LARGE_INTEGER *timeout )
+{
+    return STATUS_NOT_IMPLEMENTED;
+}
+
+static NTSTATUS inproc_signal_and_wait( HANDLE signal, HANDLE wait,
+                                        BOOLEAN alertable, const LARGE_INTEGER *timeout )
+{
+    return STATUS_NOT_IMPLEMENTED;
+}
+
+
 /******************************************************************************
  *              NtCreateSemaphore (NTDLL.@)
  */
@@ -382,6 +435,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 );
@@ -406,6 +465,9 @@ NTSTATUS WINAPI NtReleaseSemaphore( HANDLE handle, ULONG count, ULONG *previous
 
     TRACE( "handle %p, count %u, prev_count %p\n", handle, (int)count, previous );
 
+    if ((ret = inproc_release_semaphore( handle, count, previous )) != STATUS_NOT_IMPLEMENTED)
+        return ret;
+
     SERVER_START_REQ( release_semaphore )
     {
         req->handle = wine_server_obj_handle( handle );
@@ -489,6 +551,9 @@ NTSTATUS WINAPI NtSetEvent( HANDLE handle, LONG *prev_state )
 
     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 );
@@ -510,6 +575,9 @@ NTSTATUS WINAPI NtResetEvent( HANDLE handle, LONG *prev_state )
 
     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 );
@@ -541,6 +609,9 @@ NTSTATUS WINAPI NtPulseEvent( HANDLE handle, LONG *prev_state )
 
     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 );
@@ -572,6 +643,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 );
@@ -654,6 +731,9 @@ NTSTATUS WINAPI NtReleaseMutant( HANDLE handle, LONG *prev_count )
 
     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 );
@@ -684,6 +764,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 );
@@ -1619,6 +1705,12 @@ NTSTATUS WINAPI NtWaitForMultipleObjects( DWORD count, const HANDLE *handles, BO
         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] );
@@ -1645,11 +1737,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 );
-- 
GitLab


From bb691bf5e01d630382f4ece6a40a9bcb5d13e42b Mon Sep 17 00:00:00 2001
From: Elizabeth Figura <zfigura@codeweavers.com>
Date: Mon, 10 Feb 2025 16:44:49 -0600
Subject: [PATCH 03/29] ntdll: Retrieve and cache an ntsync device in wait
 calls.

---
 configure                      |   6 ++
 configure.ac                   |   1 +
 dlls/ntdll/unix/server.c       |   8 +--
 dlls/ntdll/unix/sync.c         | 107 +++++++++++++++++++++++++++++++++
 dlls/ntdll/unix/unix_private.h |   3 +
 include/config.h.in            |   3 +
 include/wine/server_protocol.h |  15 +++++
 server/Makefile.in             |   1 +
 server/inproc_sync.c           |  72 ++++++++++++++++++++++
 server/protocol.def            |   5 ++
 server/request_handlers.h      |   3 +
 server/request_trace.h         |   7 +++
 12 files changed, 227 insertions(+), 4 deletions(-)
 create mode 100644 server/inproc_sync.c

diff --git a/configure b/configure
index 4597282deee..51c62da7371 100755
--- a/configure
+++ b/configure
@@ -7772,6 +7772,12 @@ if test "x$ac_cv_header_linux_major_h" = xyes
 then :
   printf "%s\n" "#define HAVE_LINUX_MAJOR_H 1" >>confdefs.h
 
+fi
+ac_fn_c_check_header_compile "$LINENO" "linux/ntsync.h" "ac_cv_header_linux_ntsync_h" "$ac_includes_default"
+if test "x$ac_cv_header_linux_ntsync_h" = xyes
+then :
+  printf "%s\n" "#define HAVE_LINUX_NTSYNC_H 1" >>confdefs.h
+
 fi
 ac_fn_c_check_header_compile "$LINENO" "linux/param.h" "ac_cv_header_linux_param_h" "$ac_includes_default"
 if test "x$ac_cv_header_linux_param_h" = xyes
diff --git a/configure.ac b/configure.ac
index 2a1c91c8479..ef05a8c0e95 100644
--- a/configure.ac
+++ b/configure.ac
@@ -390,6 +390,7 @@ AC_CHECK_HEADERS(\
 	linux/input.h \
 	linux/ioctl.h \
 	linux/major.h \
+	linux/ntsync.h \
 	linux/param.h \
 	linux/serial.h \
 	linux/types.h \
diff --git a/dlls/ntdll/unix/server.c b/dlls/ntdll/unix/server.c
index a6e07215479..4d1d19e166e 100644
--- a/dlls/ntdll/unix/server.c
+++ b/dlls/ntdll/unix/server.c
@@ -103,7 +103,7 @@ sigset_t server_block_set;  /* signals to block during server calls */
 static int fd_socket = -1;  /* socket to exchange file descriptors with the server */
 static int initial_cwd = -1;
 static pid_t server_pid;
-static pthread_mutex_t fd_cache_mutex = PTHREAD_MUTEX_INITIALIZER;
+pthread_mutex_t fd_cache_mutex = PTHREAD_MUTEX_INITIALIZER;
 
 /* atomically exchange a 64-bit value */
 static inline LONG64 interlocked_xchg64( LONG64 *dest, LONG64 val )
@@ -932,7 +932,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;
@@ -1127,7 +1127,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 ||
@@ -1580,7 +1580,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 );
+    ntdll_get_thread_data()->request_fd = wine_server_receive_fd( &version );
 
 #ifdef SO_PASSCRED
     /* now that we hopefully received the server_pid, disable SO_PASSCRED */
diff --git a/dlls/ntdll/unix/sync.c b/dlls/ntdll/unix/sync.c
index 0a296d86d6d..ee88c14957e 100644
--- a/dlls/ntdll/unix/sync.c
+++ b/dlls/ntdll/unix/sync.c
@@ -57,6 +57,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
@@ -303,6 +306,44 @@ static unsigned int validate_open_object_attributes( const OBJECT_ATTRIBUTES *at
 }
 
 
+#ifdef NTSYNC_IOC_EVENT_READ
+
+static int get_linux_sync_device(void)
+{
+    static LONG device = -2;
+
+    if (device == -2)
+    {
+        obj_handle_t handle;
+        sigset_t sigset;
+        NTSTATUS ret;
+        int fd = -1;
+
+        /* 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 );
+
+        if (device == -2)
+        {
+            SERVER_START_REQ( get_linux_sync_device )
+            {
+                if (!(ret = wine_server_call( req )))
+                {
+                    fd = wine_server_receive_fd( &handle );
+                    assert( !handle );
+                }
+            }
+            SERVER_END_REQ;
+
+            device = fd;
+        }
+
+        server_leave_uninterrupted_section( &fd_cache_mutex, &sigset );
+    }
+    return device;
+}
+
 static NTSTATUS inproc_release_semaphore( HANDLE handle, ULONG count, ULONG *prev_count )
 {
     return STATUS_NOT_IMPLEMENTED;
@@ -346,15 +387,81 @@ static NTSTATUS inproc_query_mutex( HANDLE handle, MUTANT_BASIC_INFORMATION *inf
 static NTSTATUS inproc_wait( DWORD count, const HANDLE *handles, BOOLEAN wait_any,
                              BOOLEAN alertable, const LARGE_INTEGER *timeout )
 {
+    int device;
+
+    if ((device = get_linux_sync_device()) < 0)
+        return STATUS_NOT_IMPLEMENTED;
+
     return STATUS_NOT_IMPLEMENTED;
 }
 
 static NTSTATUS inproc_signal_and_wait( HANDLE signal, HANDLE wait,
                                         BOOLEAN alertable, const LARGE_INTEGER *timeout )
+{
+    int device;
+
+    if ((device = get_linux_sync_device()) < 0)
+        return STATUS_NOT_IMPLEMENTED;
+
+    return STATUS_NOT_IMPLEMENTED;
+}
+
+#else
+
+static NTSTATUS inproc_release_semaphore( HANDLE handle, ULONG count, ULONG *prev_count )
+{
+    return STATUS_NOT_IMPLEMENTED;
+}
+
+static NTSTATUS inproc_query_semaphore( HANDLE handle, SEMAPHORE_BASIC_INFORMATION *info )
 {
     return STATUS_NOT_IMPLEMENTED;
 }
 
+static NTSTATUS inproc_set_event( HANDLE handle, LONG *prev_state )
+{
+    return STATUS_NOT_IMPLEMENTED;
+}
+
+static NTSTATUS inproc_reset_event( HANDLE handle, LONG *prev_state )
+{
+    return STATUS_NOT_IMPLEMENTED;
+}
+
+static NTSTATUS inproc_pulse_event( HANDLE handle, LONG *prev_state )
+{
+    return STATUS_NOT_IMPLEMENTED;
+}
+
+static NTSTATUS inproc_query_event( HANDLE handle, EVENT_BASIC_INFORMATION *info )
+{
+    return STATUS_NOT_IMPLEMENTED;
+}
+
+static NTSTATUS inproc_release_mutex( HANDLE handle, LONG *prev_count )
+{
+    return STATUS_NOT_IMPLEMENTED;
+}
+
+static NTSTATUS inproc_query_mutex( HANDLE handle, MUTANT_BASIC_INFORMATION *info )
+{
+    return STATUS_NOT_IMPLEMENTED;
+}
+
+static NTSTATUS inproc_wait( DWORD count, const HANDLE *handles, BOOLEAN wait_any,
+                             BOOLEAN alertable, const LARGE_INTEGER *timeout )
+{
+    return STATUS_NOT_IMPLEMENTED;
+}
+
+static NTSTATUS inproc_signal_and_wait( HANDLE signal, HANDLE wait,
+                                        BOOLEAN alertable, const LARGE_INTEGER *timeout )
+{
+    return STATUS_NOT_IMPLEMENTED;
+}
+
+#endif
+
 
 /******************************************************************************
  *              NtCreateSemaphore (NTDLL.@)
diff --git a/dlls/ntdll/unix/unix_private.h b/dlls/ntdll/unix/unix_private.h
index 2e80c8bd02b..5cf1877e36e 100644
--- a/dlls/ntdll/unix/unix_private.h
+++ b/dlls/ntdll/unix/unix_private.h
@@ -206,6 +206,8 @@ extern NTSTATUS load_start_exe( WCHAR **image, void **module );
 extern ULONG_PTR redirect_arm64ec_rva( void *module, ULONG_PTR rva, const IMAGE_ARM64EC_METADATA *metadata );
 extern void start_server( BOOL debug );
 
+extern pthread_mutex_t fd_cache_mutex;
+
 extern unsigned int server_call_unlocked( void *req_ptr );
 extern void server_enter_uninterrupted_section( pthread_mutex_t *mutex, sigset_t *sigset );
 extern void server_leave_uninterrupted_section( pthread_mutex_t *mutex, sigset_t *sigset );
@@ -218,6 +220,7 @@ extern unsigned int server_queue_process_apc( HANDLE process, const union apc_ca
 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);
diff --git a/include/config.h.in b/include/config.h.in
index 7ac924a077c..410e2877e15 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/wine/server_protocol.h b/include/wine/server_protocol.h
index 3cdc9375e2d..4e4dd4ef181 100644
--- a/include/wine/server_protocol.h
+++ b/include/wine/server_protocol.h
@@ -5903,6 +5903,18 @@ struct set_keyboard_repeat_reply
 };
 
 
+
+struct get_linux_sync_device_request
+{
+    struct request_header __header;
+    char __pad_12[4];
+};
+struct get_linux_sync_device_reply
+{
+    struct reply_header __header;
+};
+
+
 enum request
 {
     REQ_new_process,
@@ -6198,6 +6210,7 @@ enum request
     REQ_get_next_process,
     REQ_get_next_thread,
     REQ_set_keyboard_repeat,
+    REQ_get_linux_sync_device,
     REQ_NB_REQUESTS
 };
 
@@ -6498,6 +6511,7 @@ 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_linux_sync_device_request get_linux_sync_device_request;
 };
 union generic_reply
 {
@@ -6796,6 +6810,7 @@ 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_linux_sync_device_reply get_linux_sync_device_reply;
 };
 
 #define SERVER_PROTOCOL_VERSION 877
diff --git a/server/Makefile.in b/server/Makefile.in
index 6e4e68d6ebf..57250fd0332 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/inproc_sync.c b/server/inproc_sync.c
new file mode 100644
index 00000000000..de07f674bda
--- /dev/null
+++ b/server/inproc_sync.c
@@ -0,0 +1,72 @@
+/*
+ * In-process synchronization primitives
+ *
+ * Copyright (C) 2021-2022 Elizabeth Figura for CodeWeavers
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include "config.h"
+
+#include <assert.h>
+#include <stdint.h>
+#include <stdio.h>
+
+#include "ntstatus.h"
+#define WIN32_NO_STATUS
+#include "winternl.h"
+
+#include "file.h"
+#include "handle.h"
+#include "request.h"
+#include "thread.h"
+
+#ifdef HAVE_LINUX_NTSYNC_H
+# include <linux/ntsync.h>
+#endif
+
+#ifdef NTSYNC_IOC_EVENT_READ
+
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+static int get_linux_device(void)
+{
+    static int fd = -2;
+
+    if (fd == -2)
+        fd = open( "/dev/ntsync", O_CLOEXEC | O_RDONLY );
+
+    return fd;
+}
+
+#endif
+
+
+DECL_HANDLER(get_linux_sync_device)
+{
+#ifdef NTSYNC_IOC_EVENT_READ
+    int fd;
+
+    if ((fd = get_linux_device()) >= 0)
+        send_client_fd( current->process, fd, 0 );
+    else
+        set_error( STATUS_NOT_IMPLEMENTED );
+#else
+    set_error( STATUS_NOT_IMPLEMENTED );
+#endif
+}
diff --git a/server/protocol.def b/server/protocol.def
index 63bb0111473..f1bb5c5e07f 100644
--- a/server/protocol.def
+++ b/server/protocol.def
@@ -4094,3 +4094,8 @@ struct handle_info
 @REPLY
     int enable;                /* previous state of auto-repeat enable */
 @END
+
+
+/* Obtain a fd for the ntsync device */
+@REQ(get_linux_sync_device)
+@END
diff --git a/server/request_handlers.h b/server/request_handlers.h
index 2afc9707869..9011ff3d4f0 100644
--- a/server/request_handlers.h
+++ b/server/request_handlers.h
@@ -300,6 +300,7 @@ DECL_HANDLER(resume_process);
 DECL_HANDLER(get_next_process);
 DECL_HANDLER(get_next_thread);
 DECL_HANDLER(set_keyboard_repeat);
+DECL_HANDLER(get_linux_sync_device);
 
 typedef void (*req_handler)( const void *req, void *reply );
 static const req_handler req_handlers[REQ_NB_REQUESTS] =
@@ -597,6 +598,7 @@ 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_linux_sync_device,
 };
 
 C_ASSERT( sizeof(abstime_t) == 8 );
@@ -2281,3 +2283,4 @@ C_ASSERT( offsetof(struct set_keyboard_repeat_request, period) == 20 );
 C_ASSERT( sizeof(struct set_keyboard_repeat_request) == 24 );
 C_ASSERT( offsetof(struct set_keyboard_repeat_reply, enable) == 8 );
 C_ASSERT( sizeof(struct set_keyboard_repeat_reply) == 16 );
+C_ASSERT( sizeof(struct get_linux_sync_device_request) == 16 );
diff --git a/server/request_trace.h b/server/request_trace.h
index bd961791024..23ae334c85a 100644
--- a/server/request_trace.h
+++ b/server/request_trace.h
@@ -3359,6 +3359,10 @@ static void dump_set_keyboard_repeat_reply( const struct set_keyboard_repeat_rep
     fprintf( stderr, " enable=%d", req->enable );
 }
 
+static void dump_get_linux_sync_device_request( const struct get_linux_sync_device_request *req )
+{
+}
+
 typedef void (*dump_func)( const void *req );
 
 static const dump_func req_dumpers[REQ_NB_REQUESTS] =
@@ -3656,6 +3660,7 @@ 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_linux_sync_device_request,
 };
 
 static const dump_func reply_dumpers[REQ_NB_REQUESTS] =
@@ -3953,6 +3958,7 @@ 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,
+    NULL,
 };
 
 static const char * const req_names[REQ_NB_REQUESTS] =
@@ -4250,6 +4256,7 @@ static const char * const req_names[REQ_NB_REQUESTS] =
     "get_next_process",
     "get_next_thread",
     "set_keyboard_repeat",
+    "get_linux_sync_device",
 };
 
 static const struct
-- 
GitLab


From c18eb4311d0fe871b1032e56729f73478ec99546 Mon Sep 17 00:00:00 2001
From: Elizabeth Figura <zfigura@codeweavers.com>
Date: Mon, 8 Mar 2021 14:31:06 -0600
Subject: [PATCH 04/29] server: Add an object operation to retrieve an
 in-process synchronization object.

---
 include/wine/server_protocol.h | 14 +++++++++++++-
 server/async.c                 |  2 ++
 server/atom.c                  |  1 +
 server/change.c                |  1 +
 server/clipboard.c             |  1 +
 server/completion.c            |  2 ++
 server/console.c               |  7 +++++++
 server/debugger.c              |  2 ++
 server/device.c                |  4 ++++
 server/directory.c             |  2 ++
 server/event.c                 |  2 ++
 server/fd.c                    |  4 ++++
 server/file.c                  |  1 +
 server/handle.c                |  1 +
 server/hook.c                  |  1 +
 server/mailslot.c              |  4 ++++
 server/mapping.c               |  3 +++
 server/mutex.c                 |  1 +
 server/named_pipe.c            |  6 ++++++
 server/object.c                |  7 +++++++
 server/object.h                |  7 +++++++
 server/process.c               |  3 +++
 server/protocol.def            | 12 ++++++++++++
 server/queue.c                 |  2 ++
 server/registry.c              |  1 +
 server/request.c               |  1 +
 server/semaphore.c             |  1 +
 server/serial.c                |  1 +
 server/signal.c                |  1 +
 server/sock.c                  |  3 +++
 server/symlink.c               |  1 +
 server/thread.c                |  3 +++
 server/timer.c                 |  1 +
 server/token.c                 |  1 +
 server/window.c                |  1 +
 server/winstation.c            |  2 ++
 36 files changed, 106 insertions(+), 1 deletion(-)

diff --git a/include/wine/server_protocol.h b/include/wine/server_protocol.h
index 4e4dd4ef181..372b6652766 100644
--- a/include/wine/server_protocol.h
+++ b/include/wine/server_protocol.h
@@ -5902,6 +5902,18 @@ struct set_keyboard_repeat_reply
     char __pad_12[4];
 };
 
+enum inproc_sync_type
+{
+    INPROC_SYNC_UNKNOWN,
+    INPROC_SYNC_AUTO_EVENT,
+    INPROC_SYNC_MANUAL_EVENT,
+    INPROC_SYNC_SEMAPHORE,
+    INPROC_SYNC_MUTEX,
+    INPROC_SYNC_AUTO_SERVER,
+    INPROC_SYNC_MANUAL_SERVER,
+    INPROC_SYNC_QUEUE,
+};
+
 
 
 struct get_linux_sync_device_request
@@ -6813,6 +6825,6 @@ union generic_reply
     struct get_linux_sync_device_reply get_linux_sync_device_reply;
 };
 
-#define SERVER_PROTOCOL_VERSION 877
+#define SERVER_PROTOCOL_VERSION 878
 
 #endif /* __WINE_WINE_SERVER_PROTOCOL_H */
diff --git a/server/async.c b/server/async.c
index d2d929c9709..e8b50a72f7e 100644
--- a/server/async.c
+++ b/server/async.c
@@ -90,6 +90,7 @@ static const struct object_ops async_ops =
     NULL,                      /* unlink_name */
     no_open_file,              /* open_file */
     no_kernel_obj_list,        /* get_kernel_obj_list */
+    no_get_inproc_sync,        /* get_inproc_sync */
     no_close_handle,           /* close_handle */
     async_destroy              /* destroy */
 };
@@ -710,6 +711,7 @@ static const struct object_ops iosb_ops =
     NULL,                     /* unlink_name */
     no_open_file,             /* open_file */
     no_kernel_obj_list,       /* get_kernel_obj_list */
+    no_get_inproc_sync,       /* get_inproc_sync */
     no_close_handle,          /* close_handle */
     iosb_destroy              /* destroy */
 };
diff --git a/server/atom.c b/server/atom.c
index ff0799f5880..04951e628ba 100644
--- a/server/atom.c
+++ b/server/atom.c
@@ -91,6 +91,7 @@ static const struct object_ops atom_table_ops =
     NULL,                         /* unlink_name */
     no_open_file,                 /* open_file */
     no_kernel_obj_list,           /* get_kernel_obj_list */
+    no_get_inproc_sync,           /* get_inproc_sync */
     no_close_handle,              /* close_handle */
     atom_table_destroy            /* destroy */
 };
diff --git a/server/change.c b/server/change.c
index f42ce066340..54fcfade398 100644
--- a/server/change.c
+++ b/server/change.c
@@ -124,6 +124,7 @@ static const struct object_ops dir_ops =
     NULL,                     /* unlink_name */
     no_open_file,             /* open_file */
     no_kernel_obj_list,       /* get_kernel_obj_list */
+    no_get_inproc_sync,       /* get_inproc_sync */
     dir_close_handle,         /* close_handle */
     dir_destroy               /* destroy */
 };
diff --git a/server/clipboard.c b/server/clipboard.c
index 91f159bc7c9..a2258ae869d 100644
--- a/server/clipboard.c
+++ b/server/clipboard.c
@@ -88,6 +88,7 @@ static const struct object_ops clipboard_ops =
     NULL,                         /* unlink_name */
     no_open_file,                 /* open_file */
     no_kernel_obj_list,           /* get_kernel_obj_list */
+    no_get_inproc_sync,           /* get_inproc_sync */
     no_close_handle,              /* close_handle */
     clipboard_destroy             /* destroy */
 };
diff --git a/server/completion.c b/server/completion.c
index 99680ae0680..ac598e85229 100644
--- a/server/completion.c
+++ b/server/completion.c
@@ -104,6 +104,7 @@ static const struct object_ops completion_wait_ops =
     NULL,                           /* unlink_name */
     no_open_file,                   /* open_file */
     no_kernel_obj_list,             /* get_kernel_obj_list */
+    no_get_inproc_sync,             /* get_inproc_sync */
     no_close_handle,                /* close_handle */
     completion_wait_destroy         /* destroy */
 };
@@ -178,6 +179,7 @@ static const struct object_ops completion_ops =
     default_unlink_name,       /* unlink_name */
     no_open_file,              /* open_file */
     no_kernel_obj_list,        /* get_kernel_obj_list */
+    no_get_inproc_sync,        /* get_inproc_sync */
     completion_close_handle,   /* close_handle */
     completion_destroy         /* destroy */
 };
diff --git a/server/console.c b/server/console.c
index de6f4e73e31..50304f746e5 100644
--- a/server/console.c
+++ b/server/console.c
@@ -96,6 +96,7 @@ static const struct object_ops console_ops =
     NULL,                             /* unlink_name */
     console_open_file,                /* open_file */
     no_kernel_obj_list,               /* get_kernel_obj_list */
+    no_get_inproc_sync,               /* get_inproc_sync */
     no_close_handle,                  /* close_handle */
     console_destroy                   /* destroy */
 };
@@ -173,6 +174,7 @@ static const struct object_ops console_server_ops =
     NULL,                             /* unlink_name */
     console_server_open_file,         /* open_file */
     no_kernel_obj_list,               /* get_kernel_obj_list */
+    no_get_inproc_sync,               /* get_inproc_sync */
     no_close_handle,                  /* close_handle */
     console_server_destroy            /* destroy */
 };
@@ -242,6 +244,7 @@ static const struct object_ops screen_buffer_ops =
     NULL,                             /* unlink_name */
     screen_buffer_open_file,          /* open_file */
     no_kernel_obj_list,               /* get_kernel_obj_list */
+    no_get_inproc_sync,               /* get_inproc_sync */
     no_close_handle,                  /* close_handle */
     screen_buffer_destroy             /* destroy */
 };
@@ -291,6 +294,7 @@ static const struct object_ops console_device_ops =
     default_unlink_name,              /* unlink_name */
     console_device_open_file,         /* open_file */
     no_kernel_obj_list,               /* get_kernel_obj_list */
+    no_get_inproc_sync,               /* get_inproc_sync */
     no_close_handle,                  /* close_handle */
     no_destroy                        /* destroy */
 };
@@ -330,6 +334,7 @@ static const struct object_ops console_input_ops =
     default_unlink_name,              /* unlink_name */
     console_input_open_file,          /* open_file */
     no_kernel_obj_list,               /* get_kernel_obj_list */
+    no_get_inproc_sync,               /* get_inproc_sync */
     no_close_handle,                  /* close_handle */
     console_input_destroy             /* destroy */
 };
@@ -389,6 +394,7 @@ static const struct object_ops console_output_ops =
     default_unlink_name,              /* unlink_name */
     console_output_open_file,         /* open_file */
     no_kernel_obj_list,               /* get_kernel_obj_list */
+    no_get_inproc_sync,               /* get_inproc_sync */
     no_close_handle,                  /* close_handle */
     console_output_destroy            /* destroy */
 };
@@ -447,6 +453,7 @@ static const struct object_ops console_connection_ops =
     default_unlink_name,              /* unlink_name */
     console_connection_open_file,     /* open_file */
     no_kernel_obj_list,               /* get_kernel_obj_list */
+    no_get_inproc_sync,               /* get_inproc_sync */
     console_connection_close_handle,  /* close_handle */
     console_connection_destroy        /* destroy */
 };
diff --git a/server/debugger.c b/server/debugger.c
index 39a740e07e5..cab5f95eefd 100644
--- a/server/debugger.c
+++ b/server/debugger.c
@@ -98,6 +98,7 @@ static const struct object_ops debug_event_ops =
     NULL,                          /* unlink_name */
     no_open_file,                  /* open_file */
     no_kernel_obj_list,            /* get_kernel_obj_list */
+    no_get_inproc_sync,            /* get_inproc_sync */
     no_close_handle,               /* close_handle */
     debug_event_destroy            /* destroy */
 };
@@ -126,6 +127,7 @@ static const struct object_ops debug_obj_ops =
     default_unlink_name,           /* unlink_name */
     no_open_file,                  /* open_file */
     no_kernel_obj_list,            /* get_kernel_obj_list */
+    no_get_inproc_sync,            /* get_inproc_sync */
     no_close_handle,               /* close_handle */
     debug_obj_destroy              /* destroy */
 };
diff --git a/server/device.c b/server/device.c
index 1f93cca437d..253bdf44aae 100644
--- a/server/device.c
+++ b/server/device.c
@@ -78,6 +78,7 @@ static const struct object_ops irp_call_ops =
     NULL,                             /* unlink_name */
     no_open_file,                     /* open_file */
     no_kernel_obj_list,               /* get_kernel_obj_list */
+    no_get_inproc_sync,               /* get_inproc_sync */
     no_close_handle,                  /* close_handle */
     irp_call_destroy                  /* destroy */
 };
@@ -118,6 +119,7 @@ static const struct object_ops device_manager_ops =
     NULL,                             /* unlink_name */
     no_open_file,                     /* open_file */
     no_kernel_obj_list,               /* get_kernel_obj_list */
+    no_get_inproc_sync,               /* get_inproc_sync */
     no_close_handle,                  /* close_handle */
     device_manager_destroy            /* destroy */
 };
@@ -175,6 +177,7 @@ static const struct object_ops device_ops =
     default_unlink_name,              /* unlink_name */
     device_open_file,                 /* open_file */
     device_get_kernel_obj_list,       /* get_kernel_obj_list */
+    no_get_inproc_sync,               /* get_inproc_sync */
     no_close_handle,                  /* close_handle */
     device_destroy                    /* destroy */
 };
@@ -227,6 +230,7 @@ static const struct object_ops device_file_ops =
     NULL,                             /* unlink_name */
     no_open_file,                     /* open_file */
     device_file_get_kernel_obj_list,  /* get_kernel_obj_list */
+    no_get_inproc_sync,               /* get_inproc_sync */
     device_file_close_handle,         /* close_handle */
     device_file_destroy               /* destroy */
 };
diff --git a/server/directory.c b/server/directory.c
index fd689c561bc..40cb9b95999 100644
--- a/server/directory.c
+++ b/server/directory.c
@@ -81,6 +81,7 @@ static const struct object_ops object_type_ops =
     default_unlink_name,          /* unlink_name */
     no_open_file,                 /* open_file */
     no_kernel_obj_list,           /* get_kernel_obj_list */
+    no_get_inproc_sync,           /* get_inproc_sync */
     no_close_handle,              /* close_handle */
     no_destroy                    /* destroy */
 };
@@ -131,6 +132,7 @@ static const struct object_ops directory_ops =
     default_unlink_name,          /* unlink_name */
     no_open_file,                 /* open_file */
     no_kernel_obj_list,           /* get_kernel_obj_list */
+    no_get_inproc_sync,           /* get_inproc_sync */
     no_close_handle,              /* close_handle */
     directory_destroy             /* destroy */
 };
diff --git a/server/event.c b/server/event.c
index ad7c09acc99..c19cfa5ba3e 100644
--- a/server/event.c
+++ b/server/event.c
@@ -84,6 +84,7 @@ static const struct object_ops event_ops =
     default_unlink_name,       /* unlink_name */
     no_open_file,              /* open_file */
     event_get_kernel_obj_list, /* get_kernel_obj_list */
+    no_get_inproc_sync,        /* get_inproc_sync */
     no_close_handle,           /* close_handle */
     no_destroy                 /* destroy */
 };
@@ -131,6 +132,7 @@ static const struct object_ops keyed_event_ops =
     default_unlink_name,         /* unlink_name */
     no_open_file,                /* open_file */
     no_kernel_obj_list,          /* get_kernel_obj_list */
+    no_get_inproc_sync,          /* get_inproc_sync */
     no_close_handle,             /* close_handle */
     no_destroy                   /* destroy */
 };
diff --git a/server/fd.c b/server/fd.c
index bb0e90d99d0..2cd1fb7be01 100644
--- a/server/fd.c
+++ b/server/fd.c
@@ -179,6 +179,7 @@ static const struct object_ops fd_ops =
     NULL,                     /* unlink_name */
     no_open_file,             /* open_file */
     no_kernel_obj_list,       /* get_kernel_obj_list */
+    no_get_inproc_sync,       /* get_inproc_sync */
     no_close_handle,          /* close_handle */
     fd_destroy                /* destroy */
 };
@@ -220,6 +221,7 @@ static const struct object_ops device_ops =
     NULL,                     /* unlink_name */
     no_open_file,             /* open_file */
     no_kernel_obj_list,       /* get_kernel_obj_list */
+    no_get_inproc_sync,       /* get_inproc_sync */
     no_close_handle,          /* close_handle */
     device_destroy            /* destroy */
 };
@@ -260,6 +262,7 @@ static const struct object_ops inode_ops =
     NULL,                     /* unlink_name */
     no_open_file,             /* open_file */
     no_kernel_obj_list,       /* get_kernel_obj_list */
+    no_get_inproc_sync,       /* get_inproc_sync */
     no_close_handle,          /* close_handle */
     inode_destroy             /* destroy */
 };
@@ -302,6 +305,7 @@ static const struct object_ops file_lock_ops =
     NULL,                       /* unlink_name */
     no_open_file,               /* open_file */
     no_kernel_obj_list,         /* get_kernel_obj_list */
+    no_get_inproc_sync,         /* get_inproc_sync */
     no_close_handle,            /* close_handle */
     no_destroy                  /* destroy */
 };
diff --git a/server/file.c b/server/file.c
index 8f6566a0077..dbbbad43707 100644
--- a/server/file.c
+++ b/server/file.c
@@ -106,6 +106,7 @@ static const struct object_ops file_ops =
     NULL,                         /* unlink_name */
     file_open_file,               /* open_file */
     file_get_kernel_obj_list,     /* get_kernel_obj_list */
+    no_get_inproc_sync,           /* get_inproc_sync */
     no_close_handle,              /* close_handle */
     file_destroy                  /* destroy */
 };
diff --git a/server/handle.c b/server/handle.c
index 8968df73647..58b0fe290d2 100644
--- a/server/handle.c
+++ b/server/handle.c
@@ -138,6 +138,7 @@ static const struct object_ops handle_table_ops =
     NULL,                            /* unlink_name */
     no_open_file,                    /* open_file */
     no_kernel_obj_list,              /* get_kernel_obj_list */
+    no_get_inproc_sync,              /* get_inproc_sync */
     no_close_handle,                 /* close_handle */
     handle_table_destroy             /* destroy */
 };
diff --git a/server/hook.c b/server/hook.c
index ffe7206369e..5faa63e8e10 100644
--- a/server/hook.c
+++ b/server/hook.c
@@ -93,6 +93,7 @@ static const struct object_ops hook_table_ops =
     NULL,                         /* unlink_name */
     no_open_file,                 /* open_file */
     no_kernel_obj_list,           /* get_kernel_obj_list */
+    no_get_inproc_sync,           /* get_inproc_sync */
     no_close_handle,              /* close_handle */
     hook_table_destroy            /* destroy */
 };
diff --git a/server/mailslot.c b/server/mailslot.c
index c54281c2101..8de68b3dede 100644
--- a/server/mailslot.c
+++ b/server/mailslot.c
@@ -93,6 +93,7 @@ static const struct object_ops mailslot_ops =
     default_unlink_name,       /* unlink_name */
     mailslot_open_file,        /* open_file */
     no_kernel_obj_list,        /* get_kernel_obj_list */
+    no_get_inproc_sync,        /* get_inproc_sync */
     no_close_handle,           /* close_handle */
     mailslot_destroy           /* destroy */
 };
@@ -154,6 +155,7 @@ static const struct object_ops mail_writer_ops =
     NULL,                       /* unlink_name */
     no_open_file,               /* open_file */
     no_kernel_obj_list,         /* get_kernel_obj_list */
+    no_get_inproc_sync,         /* get_inproc_sync */
     no_close_handle,            /* close_handle */
     mail_writer_destroy         /* destroy */
 };
@@ -219,6 +221,7 @@ static const struct object_ops mailslot_device_ops =
     default_unlink_name,            /* unlink_name */
     mailslot_device_open_file,      /* open_file */
     no_kernel_obj_list,             /* get_kernel_obj_list */
+    no_get_inproc_sync,             /* get_inproc_sync */
     no_close_handle,                /* close_handle */
     mailslot_device_destroy         /* destroy */
 };
@@ -249,6 +252,7 @@ static const struct object_ops mailslot_device_file_ops =
     NULL,                                   /* unlink_name */
     no_open_file,                           /* open_file */
     no_kernel_obj_list,                     /* get_kernel_obj_list */
+    no_get_inproc_sync,                     /* get_inproc_sync */
     no_close_handle,                        /* close_handle */
     mailslot_device_file_destroy            /* destroy */
 };
diff --git a/server/mapping.c b/server/mapping.c
index 247b28cf6f5..19fba5cd5d7 100644
--- a/server/mapping.c
+++ b/server/mapping.c
@@ -79,6 +79,7 @@ static const struct object_ops ranges_ops =
     NULL,                      /* unlink_name */
     no_open_file,              /* open_file */
     no_kernel_obj_list,        /* get_kernel_obj_list */
+    no_get_inproc_sync,        /* get_inproc_sync */
     no_close_handle,           /* close_handle */
     ranges_destroy             /* destroy */
 };
@@ -115,6 +116,7 @@ static const struct object_ops shared_map_ops =
     NULL,                      /* unlink_name */
     no_open_file,              /* open_file */
     no_kernel_obj_list,        /* get_kernel_obj_list */
+    no_get_inproc_sync,        /* get_inproc_sync */
     no_close_handle,           /* close_handle */
     shared_map_destroy         /* destroy */
 };
@@ -188,6 +190,7 @@ static const struct object_ops mapping_ops =
     default_unlink_name,         /* unlink_name */
     no_open_file,                /* open_file */
     no_kernel_obj_list,          /* get_kernel_obj_list */
+    no_get_inproc_sync,          /* get_inproc_sync */
     no_close_handle,             /* close_handle */
     mapping_destroy              /* destroy */
 };
diff --git a/server/mutex.c b/server/mutex.c
index af0efe72132..4218423ebac 100644
--- a/server/mutex.c
+++ b/server/mutex.c
@@ -85,6 +85,7 @@ static const struct object_ops mutex_ops =
     default_unlink_name,       /* unlink_name */
     no_open_file,              /* open_file */
     no_kernel_obj_list,        /* get_kernel_obj_list */
+    no_get_inproc_sync,        /* get_inproc_sync */
     no_close_handle,           /* close_handle */
     mutex_destroy              /* destroy */
 };
diff --git a/server/named_pipe.c b/server/named_pipe.c
index 6e4ae371a1b..d5b1462ebf7 100644
--- a/server/named_pipe.c
+++ b/server/named_pipe.c
@@ -131,6 +131,7 @@ static const struct object_ops named_pipe_ops =
     default_unlink_name,          /* unlink_name */
     named_pipe_open_file,         /* open_file */
     no_kernel_obj_list,           /* get_kernel_obj_list */
+    no_get_inproc_sync,           /* get_inproc_sync */
     no_close_handle,              /* close_handle */
     named_pipe_destroy            /* destroy */
 };
@@ -179,6 +180,7 @@ static const struct object_ops pipe_server_ops =
     NULL,                         /* unlink_name */
     pipe_server_open_file,        /* open_file */
     no_kernel_obj_list,           /* get_kernel_obj_list */
+    no_get_inproc_sync,           /* get_inproc_sync */
     async_close_obj_handle,       /* close_handle */
     pipe_server_destroy           /* destroy */
 };
@@ -223,6 +225,7 @@ static const struct object_ops pipe_client_ops =
     NULL,                         /* unlink_name */
     no_open_file,                 /* open_file */
     no_kernel_obj_list,           /* get_kernel_obj_list */
+    no_get_inproc_sync,           /* get_inproc_sync */
     async_close_obj_handle,       /* close_handle */
     pipe_end_destroy              /* destroy */
 };
@@ -270,6 +273,7 @@ static const struct object_ops named_pipe_device_ops =
     default_unlink_name,              /* unlink_name */
     named_pipe_device_open_file,      /* open_file */
     no_kernel_obj_list,               /* get_kernel_obj_list */
+    no_get_inproc_sync,               /* get_inproc_sync */
     no_close_handle,                  /* close_handle */
     named_pipe_device_destroy         /* destroy */
 };
@@ -301,6 +305,7 @@ static const struct object_ops named_pipe_device_file_ops =
     NULL,                                    /* unlink_name */
     no_open_file,                            /* open_file */
     no_kernel_obj_list,                      /* get_kernel_obj_list */
+    no_get_inproc_sync,                      /* get_inproc_sync */
     no_close_handle,                         /* close_handle */
     named_pipe_device_file_destroy           /* destroy */
 };
@@ -351,6 +356,7 @@ static const struct object_ops named_pipe_dir_ops =
     NULL,                                    /* unlink_name */
     named_pipe_dir_open_file,                /* open_file */
     no_kernel_obj_list,                      /* get_kernel_obj_list */
+    no_get_inproc_sync,                      /* get_inproc_sync */
     no_close_handle,                         /* close_handle */
     named_pipe_dir_destroy                   /* destroy */
 };
diff --git a/server/object.c b/server/object.c
index cd368ef724a..1aa264613a0 100644
--- a/server/object.c
+++ b/server/object.c
@@ -120,6 +120,7 @@ static const struct object_ops apc_reserve_ops =
     default_unlink_name,        /* unlink_name */
     no_open_file,               /* open_file */
     no_kernel_obj_list,         /* get_kernel_obj_list */
+    no_get_inproc_sync,         /* get_inproc_sync */
     no_close_handle,            /* close_handle */
     no_destroy                  /* destroy */
 };
@@ -144,6 +145,7 @@ static const struct object_ops completion_reserve_ops =
     default_unlink_name,       /* unlink_name */
     no_open_file,              /* open_file */
     no_kernel_obj_list,        /* get_kernel_obj_list */
+    no_get_inproc_sync,        /* get_inproc_sync */
     no_close_handle,           /* close_handle */
     no_destroy                 /* destroy */
 };
@@ -639,6 +641,11 @@ struct fd *no_get_fd( struct object *obj )
     return NULL;
 }
 
+int no_get_inproc_sync( struct object *obj, enum inproc_sync_type *type )
+{
+    return -1;
+}
+
 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 1058f9bfb0a..08ad412dc5e 100644
--- a/server/object.h
+++ b/server/object.h
@@ -42,6 +42,7 @@ struct async;
 struct async_queue;
 struct winstation;
 struct object_type;
+struct inproc_sync;
 
 
 struct unicode_str
@@ -103,6 +104,8 @@ struct object_ops
                                 unsigned int options);
     /* return list of kernel objects */
     struct list *(*get_kernel_obj_list)(struct object *);
+    /* get a client-waitable in-process synchronization fd for this object */
+    int (*get_inproc_sync)(struct object *, enum inproc_sync_type *type);
     /* close a handle to this object */
     int (*close_handle)(struct object *,struct process *,obj_handle_t);
     /* destroy on refcount == 0 */
@@ -228,6 +231,10 @@ extern void reset_event( struct event *event );
 
 extern void abandon_mutexes( struct thread *thread );
 
+/* in-process synchronization functions */
+
+extern int no_get_inproc_sync( struct object *obj, enum inproc_sync_type *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 b161e3394ba..68df57311c2 100644
--- a/server/process.c
+++ b/server/process.c
@@ -117,6 +117,7 @@ static const struct object_ops process_ops =
     NULL,                        /* unlink_name */
     no_open_file,                /* open_file */
     process_get_kernel_obj_list, /* get_kernel_obj_list */
+    no_get_inproc_sync,          /* get_inproc_sync */
     no_close_handle,             /* close_handle */
     process_destroy              /* destroy */
 };
@@ -168,6 +169,7 @@ static const struct object_ops startup_info_ops =
     NULL,                          /* unlink_name */
     no_open_file,                  /* open_file */
     no_kernel_obj_list,            /* get_kernel_obj_list */
+    no_get_inproc_sync,            /* get_inproc_sync */
     no_close_handle,               /* close_handle */
     startup_info_destroy           /* destroy */
 };
@@ -229,6 +231,7 @@ static const struct object_ops job_ops =
     default_unlink_name,           /* unlink_name */
     no_open_file,                  /* open_file */
     no_kernel_obj_list,            /* get_kernel_obj_list */
+    no_get_inproc_sync,            /* get_inproc_sync */
     job_close_handle,              /* close_handle */
     job_destroy                    /* destroy */
 };
diff --git a/server/protocol.def b/server/protocol.def
index f1bb5c5e07f..ce2e5cfb01d 100644
--- a/server/protocol.def
+++ b/server/protocol.def
@@ -4095,6 +4095,18 @@ struct handle_info
     int enable;                /* previous state of auto-repeat enable */
 @END
 
+enum inproc_sync_type
+{
+    INPROC_SYNC_UNKNOWN,
+    INPROC_SYNC_AUTO_EVENT,
+    INPROC_SYNC_MANUAL_EVENT,
+    INPROC_SYNC_SEMAPHORE,
+    INPROC_SYNC_MUTEX,
+    INPROC_SYNC_AUTO_SERVER,
+    INPROC_SYNC_MANUAL_SERVER,
+    INPROC_SYNC_QUEUE,
+};
+
 
 /* Obtain a fd for the ntsync device */
 @REQ(get_linux_sync_device)
diff --git a/server/queue.c b/server/queue.c
index b4b6069f927..b8c7a48669d 100644
--- a/server/queue.c
+++ b/server/queue.c
@@ -177,6 +177,7 @@ static const struct object_ops msg_queue_ops =
     NULL,                      /* unlink_name */
     no_open_file,              /* open_file */
     no_kernel_obj_list,        /* get_kernel_obj_list */
+    no_get_inproc_sync,        /* get_inproc_sync */
     no_close_handle,           /* close_handle */
     msg_queue_destroy          /* destroy */
 };
@@ -214,6 +215,7 @@ static const struct object_ops thread_input_ops =
     NULL,                         /* unlink_name */
     no_open_file,                 /* open_file */
     no_kernel_obj_list,           /* get_kernel_obj_list */
+    no_get_inproc_sync,           /* get_inproc_sync */
     no_close_handle,              /* close_handle */
     thread_input_destroy          /* destroy */
 };
diff --git a/server/registry.c b/server/registry.c
index c60c737feff..8aaf105d70f 100644
--- a/server/registry.c
+++ b/server/registry.c
@@ -192,6 +192,7 @@ static const struct object_ops key_ops =
     key_unlink_name,         /* unlink_name */
     no_open_file,            /* open_file */
     no_kernel_obj_list,      /* get_kernel_obj_list */
+    no_get_inproc_sync,      /* get_inproc_sync */
     key_close_handle,        /* close_handle */
     key_destroy              /* destroy */
 };
diff --git a/server/request.c b/server/request.c
index 2254315b79e..24a4f779e68 100644
--- a/server/request.c
+++ b/server/request.c
@@ -101,6 +101,7 @@ static const struct object_ops master_socket_ops =
     NULL,                          /* unlink_name */
     no_open_file,                  /* open_file */
     no_kernel_obj_list,            /* get_kernel_obj_list */
+    no_get_inproc_sync,            /* get_inproc_sync */
     no_close_handle,               /* close_handle */
     master_socket_destroy          /* destroy */
 };
diff --git a/server/semaphore.c b/server/semaphore.c
index 53b42a886df..094741bf36d 100644
--- a/server/semaphore.c
+++ b/server/semaphore.c
@@ -82,6 +82,7 @@ static const struct object_ops semaphore_ops =
     default_unlink_name,           /* unlink_name */
     no_open_file,                  /* open_file */
     no_kernel_obj_list,            /* get_kernel_obj_list */
+    no_get_inproc_sync,            /* get_inproc_sync */
     no_close_handle,               /* close_handle */
     no_destroy                     /* destroy */
 };
diff --git a/server/serial.c b/server/serial.c
index 209f2e9174e..df242058a43 100644
--- a/server/serial.c
+++ b/server/serial.c
@@ -103,6 +103,7 @@ static const struct object_ops serial_ops =
     NULL,                         /* unlink_name */
     no_open_file,                 /* open_file */
     no_kernel_obj_list,           /* get_kernel_obj_list */
+    no_get_inproc_sync,           /* get_inproc_sync */
     no_close_handle,              /* close_handle */
     serial_destroy                /* destroy */
 };
diff --git a/server/signal.c b/server/signal.c
index 19b76d44c16..85d16baa98c 100644
--- a/server/signal.c
+++ b/server/signal.c
@@ -74,6 +74,7 @@ static const struct object_ops handler_ops =
     NULL,                     /* unlink_name */
     no_open_file,             /* open_file */
     no_kernel_obj_list,       /* get_kernel_obj_list */
+    no_get_inproc_sync,       /* get_inproc_sync */
     no_close_handle,          /* close_handle */
     handler_destroy           /* destroy */
 };
diff --git a/server/sock.c b/server/sock.c
index e064f867ff4..4d711c61f22 100644
--- a/server/sock.c
+++ b/server/sock.c
@@ -498,6 +498,7 @@ static const struct object_ops sock_ops =
     NULL,                         /* unlink_name */
     no_open_file,                 /* open_file */
     no_kernel_obj_list,           /* get_kernel_obj_list */
+    no_get_inproc_sync,           /* get_inproc_sync */
     sock_close_handle,            /* close_handle */
     sock_destroy                  /* destroy */
 };
@@ -3707,6 +3708,7 @@ static const struct object_ops ifchange_ops =
     NULL,                    /* unlink_name */
     no_open_file,            /* open_file */
     no_kernel_obj_list,      /* get_kernel_obj_list */
+    no_get_inproc_sync,      /* get_inproc_sync */
     no_close_handle,         /* close_handle */
     ifchange_destroy         /* destroy */
 };
@@ -3928,6 +3930,7 @@ static const struct object_ops socket_device_ops =
     default_unlink_name,        /* unlink_name */
     socket_device_open_file,    /* open_file */
     no_kernel_obj_list,         /* get_kernel_obj_list */
+    no_get_inproc_sync,         /* get_inproc_sync */
     no_close_handle,            /* close_handle */
     no_destroy                  /* destroy */
 };
diff --git a/server/symlink.c b/server/symlink.c
index 74b60162c01..f0314f2ee90 100644
--- a/server/symlink.c
+++ b/server/symlink.c
@@ -83,6 +83,7 @@ static const struct object_ops symlink_ops =
     default_unlink_name,          /* unlink_name */
     no_open_file,                 /* open_file */
     no_kernel_obj_list,           /* get_kernel_obj_list */
+    no_get_inproc_sync,           /* get_inproc_sync */
     no_close_handle,              /* close_handle */
     symlink_destroy               /* destroy */
 };
diff --git a/server/thread.c b/server/thread.c
index 27a40ce38ad..ce78b50593c 100644
--- a/server/thread.c
+++ b/server/thread.c
@@ -118,6 +118,7 @@ static const struct object_ops thread_apc_ops =
     NULL,                       /* unlink_name */
     no_open_file,               /* open_file */
     no_kernel_obj_list,         /* get_kernel_obj_list */
+    no_get_inproc_sync,         /* get_inproc_sync */
     no_close_handle,            /* close_handle */
     thread_apc_destroy          /* destroy */
 };
@@ -160,6 +161,7 @@ static const struct object_ops context_ops =
     NULL,                       /* unlink_name */
     no_open_file,               /* open_file */
     no_kernel_obj_list,         /* get_kernel_obj_list */
+    no_get_inproc_sync,         /* get_inproc_sync */
     no_close_handle,            /* close_handle */
     no_destroy                  /* destroy */
 };
@@ -209,6 +211,7 @@ static const struct object_ops thread_ops =
     NULL,                       /* unlink_name */
     no_open_file,               /* open_file */
     thread_get_kernel_obj_list, /* get_kernel_obj_list */
+    no_get_inproc_sync,         /* get_inproc_sync */
     no_close_handle,            /* close_handle */
     destroy_thread              /* destroy */
 };
diff --git a/server/timer.c b/server/timer.c
index b0b6ec81535..fef447bc4aa 100644
--- a/server/timer.c
+++ b/server/timer.c
@@ -88,6 +88,7 @@ static const struct object_ops timer_ops =
     default_unlink_name,       /* unlink_name */
     no_open_file,              /* open_file */
     no_kernel_obj_list,        /* get_kernel_obj_list */
+    no_get_inproc_sync,        /* get_inproc_sync */
     no_close_handle,           /* close_handle */
     timer_destroy              /* destroy */
 };
diff --git a/server/token.c b/server/token.c
index 7e20c670a16..844ad2814f4 100644
--- a/server/token.c
+++ b/server/token.c
@@ -157,6 +157,7 @@ static const struct object_ops token_ops =
     NULL,                      /* unlink_name */
     no_open_file,              /* open_file */
     no_kernel_obj_list,        /* get_kernel_obj_list */
+    no_get_inproc_sync,        /* get_inproc_sync */
     no_close_handle,           /* close_handle */
     token_destroy              /* destroy */
 };
diff --git a/server/window.c b/server/window.c
index f7f9d5e517f..32afdf82c29 100644
--- a/server/window.c
+++ b/server/window.c
@@ -119,6 +119,7 @@ static const struct object_ops window_ops =
     NULL,                     /* unlink_name */
     no_open_file,             /* open_file */
     no_kernel_obj_list,       /* get_kernel_obj_list */
+    no_get_inproc_sync,       /* get_inproc_sync */
     no_close_handle,          /* close_handle */
     window_destroy            /* destroy */
 };
diff --git a/server/winstation.c b/server/winstation.c
index b3746090ccf..4b8a4972971 100644
--- a/server/winstation.c
+++ b/server/winstation.c
@@ -88,6 +88,7 @@ static const struct object_ops winstation_ops =
     default_unlink_name,          /* unlink_name */
     no_open_file,                 /* open_file */
     no_kernel_obj_list,           /* get_kernel_obj_list */
+    no_get_inproc_sync,           /* get_inproc_sync */
     winstation_close_handle,      /* close_handle */
     winstation_destroy            /* destroy */
 };
@@ -128,6 +129,7 @@ static const struct object_ops desktop_ops =
     default_unlink_name,          /* unlink_name */
     no_open_file,                 /* open_file */
     no_kernel_obj_list,           /* get_kernel_obj_list */
+    no_get_inproc_sync,           /* get_inproc_sync */
     desktop_close_handle,         /* close_handle */
     desktop_destroy               /* destroy */
 };
-- 
GitLab


From c246d5b912f826df3cea55a5f9c631187a8fd644 Mon Sep 17 00:00:00 2001
From: Elizabeth Figura <zfigura@codeweavers.com>
Date: Thu, 13 Mar 2025 14:09:42 -0500
Subject: [PATCH 05/29] server: Create in-process synchronization objects for
 events.

---
 server/event.c       | 65 ++++++++++++++++++++++++++++++++++----------
 server/inproc_sync.c | 59 ++++++++++++++++++++++++++++++++++++++++
 server/object.h      |  4 +++
 3 files changed, 113 insertions(+), 15 deletions(-)

diff --git a/server/event.c b/server/event.c
index c19cfa5ba3e..6f1b0bec022 100644
--- a/server/event.c
+++ b/server/event.c
@@ -25,6 +25,7 @@
 #include <stdlib.h>
 #include <stdarg.h>
 #include <sys/types.h>
+#include <unistd.h>
 
 #include "ntstatus.h"
 #define WIN32_NO_STATUS
@@ -55,7 +56,11 @@ struct event
     struct object  obj;             /* object header */
     struct list    kernel_object;   /* list of kernel object pointers */
     int            manual_reset;    /* is it a manual reset event? */
-    int            signaled;        /* event has been signaled */
+    union
+    {
+        int        signaled;        /* event has been signaled */
+        int        inproc_sync;     /* in-process synchronization object */
+    } u;
 };
 
 static void event_dump( struct object *obj, int verbose );
@@ -63,6 +68,8 @@ static int event_signaled( struct object *obj, struct wait_queue_entry *entry );
 static void event_satisfied( struct object *obj, struct wait_queue_entry *entry );
 static int event_signal( struct object *obj, unsigned int access);
 static struct list *event_get_kernel_obj_list( struct object *obj );
+static int event_get_inproc_sync( struct object *obj, enum inproc_sync_type *type );
+static void event_destroy( struct object *obj );
 
 static const struct object_ops event_ops =
 {
@@ -84,9 +91,9 @@ static const struct object_ops event_ops =
     default_unlink_name,       /* unlink_name */
     no_open_file,              /* open_file */
     event_get_kernel_obj_list, /* get_kernel_obj_list */
-    no_get_inproc_sync,        /* get_inproc_sync */
+    event_get_inproc_sync,     /* get_inproc_sync */
     no_close_handle,           /* close_handle */
-    no_destroy                 /* destroy */
+    event_destroy              /* destroy */
 };
 
 
@@ -151,7 +158,10 @@ struct event *create_event( struct object *root, const struct unicode_str *name,
             /* initialize it if it didn't already exist */
             list_init( &event->kernel_object );
             event->manual_reset = manual_reset;
-            event->signaled     = initial_state;
+            if (use_inproc_sync())
+                event->u.inproc_sync = create_inproc_event( event->manual_reset, initial_state );
+            else
+                event->u.signaled = initial_state;
         }
     }
     return event;
@@ -164,22 +174,32 @@ struct event *get_event_obj( struct process *process, obj_handle_t handle, unsig
 
 static void pulse_event( struct event *event )
 {
-    event->signaled = 1;
+    event->u.signaled = 1;
     /* wake up all waiters if manual reset, a single one otherwise */
     wake_up( &event->obj, !event->manual_reset );
-    event->signaled = 0;
+    event->u.signaled = 0;
 }
 
 void set_event( struct event *event )
 {
-    event->signaled = 1;
-    /* wake up all waiters if manual reset, a single one otherwise */
-    wake_up( &event->obj, !event->manual_reset );
+    if (use_inproc_sync())
+    {
+        set_inproc_event( event->u.inproc_sync );
+    }
+    else
+    {
+        event->u.signaled = 1;
+        /* wake up all waiters if manual reset, a single one otherwise */
+        wake_up( &event->obj, !event->manual_reset );
+    }
 }
 
 void reset_event( struct event *event )
 {
-    event->signaled = 0;
+    if (use_inproc_sync())
+        reset_inproc_event( event->u.inproc_sync );
+    else
+        event->u.signaled = 0;
 }
 
 static void event_dump( struct object *obj, int verbose )
@@ -187,14 +207,14 @@ static void event_dump( struct object *obj, int verbose )
     struct event *event = (struct event *)obj;
     assert( obj->ops == &event_ops );
     fprintf( stderr, "Event manual=%d signaled=%d\n",
-             event->manual_reset, event->signaled );
+             event->manual_reset, event->u.signaled );
 }
 
 static int event_signaled( struct object *obj, struct wait_queue_entry *entry )
 {
     struct event *event = (struct event *)obj;
     assert( obj->ops == &event_ops );
-    return event->signaled;
+    return event->u.signaled;
 }
 
 static void event_satisfied( struct object *obj, struct wait_queue_entry *entry )
@@ -202,7 +222,7 @@ static void event_satisfied( struct object *obj, struct wait_queue_entry *entry
     struct event *event = (struct event *)obj;
     assert( obj->ops == &event_ops );
     /* Reset if it's an auto-reset event */
-    if (!event->manual_reset) event->signaled = 0;
+    if (!event->manual_reset) event->u.signaled = 0;
 }
 
 static int event_signal( struct object *obj, unsigned int access )
@@ -225,6 +245,21 @@ static struct list *event_get_kernel_obj_list( struct object *obj )
     return &event->kernel_object;
 }
 
+static int event_get_inproc_sync( struct object *obj, enum inproc_sync_type *type )
+{
+    struct event *event = (struct event *)obj;
+
+    *type = event->manual_reset ? INPROC_SYNC_MANUAL_EVENT : INPROC_SYNC_AUTO_EVENT;
+    return event->u.inproc_sync;
+}
+
+static void event_destroy( struct object *obj )
+{
+    struct event *event = (struct event *)obj;
+
+    if (use_inproc_sync()) close( event->u.inproc_sync );
+}
+
 struct keyed_event *create_keyed_event( struct object *root, const struct unicode_str *name,
                                         unsigned int attr, const struct security_descriptor *sd )
 {
@@ -318,7 +353,7 @@ DECL_HANDLER(event_op)
     struct event *event;
 
     if (!(event = get_event_obj( current->process, req->handle, EVENT_MODIFY_STATE ))) return;
-    reply->state = event->signaled;
+    reply->state = event->u.signaled;
     switch(req->op)
     {
     case PULSE_EVENT:
@@ -345,7 +380,7 @@ DECL_HANDLER(query_event)
     if (!(event = get_event_obj( current->process, req->handle, EVENT_QUERY_STATE ))) return;
 
     reply->manual_reset = event->manual_reset;
-    reply->state = event->signaled;
+    reply->state = event->u.signaled;
 
     release_object( event );
 }
diff --git a/server/inproc_sync.c b/server/inproc_sync.c
index de07f674bda..da148496285 100644
--- a/server/inproc_sync.c
+++ b/server/inproc_sync.c
@@ -54,6 +54,65 @@ static int get_linux_device(void)
     return fd;
 }
 
+int use_inproc_sync(void)
+{
+    return 0;
+}
+
+int create_inproc_event( int manual_reset, int signaled )
+{
+    struct ntsync_event_args args;
+    int device;
+
+    if ((device = get_linux_device()) < 0) return -1;
+
+    args.signaled = signaled;
+    args.manual = manual_reset;
+    return ioctl( device, NTSYNC_IOC_CREATE_EVENT, &args );
+}
+
+void set_inproc_event( int event )
+{
+    __u32 count;
+
+    if (!use_inproc_sync()) return;
+
+    if (debug_level) fprintf( stderr, "set_inproc_event %d\n", event );
+
+    ioctl( event, NTSYNC_IOC_EVENT_SET, &count );
+}
+
+void reset_inproc_event( int event )
+{
+    __u32 count;
+
+    if (!use_inproc_sync()) return;
+
+    if (debug_level) fprintf( stderr, "reset_inproc_event %d\n", event );
+
+    ioctl( event, NTSYNC_IOC_EVENT_RESET, &count );
+}
+
+#else
+
+int use_inproc_sync(void)
+{
+    return 0;
+}
+
+int create_inproc_event( int manual_reset, int signaled )
+{
+    return -1;
+}
+
+void set_inproc_event( int event )
+{
+}
+
+void reset_inproc_event( int event )
+{
+}
+
 #endif
 
 
diff --git a/server/object.h b/server/object.h
index 08ad412dc5e..612770235dd 100644
--- a/server/object.h
+++ b/server/object.h
@@ -233,7 +233,11 @@ extern void abandon_mutexes( struct thread *thread );
 
 /* in-process synchronization functions */
 
+extern int use_inproc_sync(void);
+extern int create_inproc_event( int manual_reset, int signaled );
 extern int no_get_inproc_sync( struct object *obj, enum inproc_sync_type *type );
+extern void set_inproc_event( int event );
+extern void reset_inproc_event( int event );
 
 /* serial functions */
 
-- 
GitLab


From feccb11674afb87d7086df092fe9cfcd61e415c4 Mon Sep 17 00:00:00 2001
From: Elizabeth Figura <zfigura@codeweavers.com>
Date: Thu, 13 Mar 2025 14:32:22 -0500
Subject: [PATCH 06/29] server: Create in-process synchronization objects for
 semaphores.

---
 server/inproc_sync.c | 17 ++++++++++++
 server/object.h      |  1 +
 server/semaphore.c   | 66 ++++++++++++++++++++++++++++++++------------
 3 files changed, 67 insertions(+), 17 deletions(-)

diff --git a/server/inproc_sync.c b/server/inproc_sync.c
index da148496285..f2aee9ece3f 100644
--- a/server/inproc_sync.c
+++ b/server/inproc_sync.c
@@ -71,6 +71,18 @@ int create_inproc_event( int manual_reset, int signaled )
     return ioctl( device, NTSYNC_IOC_CREATE_EVENT, &args );
 }
 
+int create_inproc_semaphore( unsigned int count, unsigned int max )
+{
+    struct ntsync_sem_args args;
+    int device;
+
+    if ((device = get_linux_device()) < 0) return -1;
+
+    args.count = count;
+    args.max = max;
+    return ioctl( device, NTSYNC_IOC_CREATE_SEM, &args );
+}
+
 void set_inproc_event( int event )
 {
     __u32 count;
@@ -105,6 +117,11 @@ int create_inproc_event( int manual_reset, int signaled )
     return -1;
 }
 
+int create_inproc_semaphore( unsigned int count, unsigned int max )
+{
+    return -1;
+}
+
 void set_inproc_event( int event )
 {
 }
diff --git a/server/object.h b/server/object.h
index 612770235dd..e7be6de6333 100644
--- a/server/object.h
+++ b/server/object.h
@@ -235,6 +235,7 @@ extern void abandon_mutexes( struct thread *thread );
 
 extern int use_inproc_sync(void);
 extern int create_inproc_event( int manual_reset, int signaled );
+extern int create_inproc_semaphore( unsigned int count, unsigned int max );
 extern int no_get_inproc_sync( struct object *obj, enum inproc_sync_type *type );
 extern void set_inproc_event( int event );
 extern void reset_inproc_event( int event );
diff --git a/server/semaphore.c b/server/semaphore.c
index 094741bf36d..f3b8179f5fe 100644
--- a/server/semaphore.c
+++ b/server/semaphore.c
@@ -25,6 +25,7 @@
 #include <stdlib.h>
 #include <stdarg.h>
 #include <sys/types.h>
+#include <unistd.h>
 
 #include "ntstatus.h"
 #define WIN32_NO_STATUS
@@ -53,14 +54,23 @@ struct type_descr semaphore_type =
 struct semaphore
 {
     struct object  obj;    /* object header */
-    unsigned int   count;  /* current count */
-    unsigned int   max;    /* maximum possible count */
+    union
+    {
+        struct
+        {
+            unsigned int count;  /* current count */
+            unsigned int max;    /* maximum possible count */
+        } s;
+        int inproc_sync;         /* in-process synchronization object */
+    } u;
 };
 
 static void semaphore_dump( struct object *obj, int verbose );
 static int semaphore_signaled( struct object *obj, struct wait_queue_entry *entry );
 static void semaphore_satisfied( struct object *obj, struct wait_queue_entry *entry );
 static int semaphore_signal( struct object *obj, unsigned int access );
+static int semaphore_get_inproc_sync( struct object *obj, enum inproc_sync_type *type );
+static void semaphore_destroy( struct object *obj );
 
 static const struct object_ops semaphore_ops =
 {
@@ -82,9 +92,9 @@ static const struct object_ops semaphore_ops =
     default_unlink_name,           /* unlink_name */
     no_open_file,                  /* open_file */
     no_kernel_obj_list,            /* get_kernel_obj_list */
-    no_get_inproc_sync,            /* get_inproc_sync */
+    semaphore_get_inproc_sync,     /* get_inproc_sync */
     no_close_handle,               /* close_handle */
-    no_destroy                     /* destroy */
+    semaphore_destroy              /* destroy */
 };
 
 
@@ -104,8 +114,15 @@ static struct semaphore *create_semaphore( struct object *root, const struct uni
         if (get_error() != STATUS_OBJECT_NAME_EXISTS)
         {
             /* initialize it if it didn't already exist */
-            sem->count = initial;
-            sem->max   = max;
+            if (use_inproc_sync())
+            {
+                sem->u.inproc_sync = create_inproc_semaphore( initial, max );
+            }
+            else
+            {
+                sem->u.s.count = initial;
+                sem->u.s.max   = max;
+            }
         }
     }
     return sem;
@@ -114,20 +131,20 @@ static struct semaphore *create_semaphore( struct object *root, const struct uni
 static int release_semaphore( struct semaphore *sem, unsigned int count,
                               unsigned int *prev )
 {
-    if (prev) *prev = sem->count;
-    if (sem->count + count < sem->count || sem->count + count > sem->max)
+    if (prev) *prev = sem->u.s.count;
+    if (sem->u.s.count + count < sem->u.s.count || sem->u.s.count + count > sem->u.s.max)
     {
         set_error( STATUS_SEMAPHORE_LIMIT_EXCEEDED );
         return 0;
     }
-    else if (sem->count)
+    else if (sem->u.s.count)
     {
         /* there cannot be any thread to wake up if the count is != 0 */
-        sem->count += count;
+        sem->u.s.count += count;
     }
     else
     {
-        sem->count = count;
+        sem->u.s.count = count;
         wake_up( &sem->obj, count );
     }
     return 1;
@@ -137,22 +154,22 @@ static void semaphore_dump( struct object *obj, int verbose )
 {
     struct semaphore *sem = (struct semaphore *)obj;
     assert( obj->ops == &semaphore_ops );
-    fprintf( stderr, "Semaphore count=%d max=%d\n", sem->count, sem->max );
+    fprintf( stderr, "Semaphore count=%d max=%d\n", sem->u.s.count, sem->u.s.max );
 }
 
 static int semaphore_signaled( struct object *obj, struct wait_queue_entry *entry )
 {
     struct semaphore *sem = (struct semaphore *)obj;
     assert( obj->ops == &semaphore_ops );
-    return (sem->count > 0);
+    return (sem->u.s.count > 0);
 }
 
 static void semaphore_satisfied( struct object *obj, struct wait_queue_entry *entry )
 {
     struct semaphore *sem = (struct semaphore *)obj;
     assert( obj->ops == &semaphore_ops );
-    assert( sem->count );
-    sem->count--;
+    assert( sem->u.s.count );
+    sem->u.s.count--;
 }
 
 static int semaphore_signal( struct object *obj, unsigned int access )
@@ -168,6 +185,21 @@ static int semaphore_signal( struct object *obj, unsigned int access )
     return release_semaphore( sem, 1, NULL );
 }
 
+static int semaphore_get_inproc_sync( struct object *obj, enum inproc_sync_type *type )
+{
+    struct semaphore *semaphore = (struct semaphore *)obj;
+
+    *type = INPROC_SYNC_SEMAPHORE;
+    return semaphore->u.inproc_sync;
+}
+
+static void semaphore_destroy( struct object *obj )
+{
+    struct semaphore *semaphore = (struct semaphore *)obj;
+
+    if (use_inproc_sync()) close( semaphore->u.inproc_sync );
+}
+
 /* create a semaphore */
 DECL_HANDLER(create_semaphore)
 {
@@ -222,8 +254,8 @@ DECL_HANDLER(query_semaphore)
     if ((sem = (struct semaphore *)get_handle_obj( current->process, req->handle,
                                                    SEMAPHORE_QUERY_STATE, &semaphore_ops )))
     {
-        reply->current = sem->count;
-        reply->max = sem->max;
+        reply->current = sem->u.s.count;
+        reply->max = sem->u.s.max;
         release_object( sem );
     }
 }
-- 
GitLab


From 319a63789e3fdb26a4d49490a21c588085ce838c Mon Sep 17 00:00:00 2001
From: Elizabeth Figura <zfigura@codeweavers.com>
Date: Thu, 13 Mar 2025 14:49:49 -0500
Subject: [PATCH 07/29] server: Create in-process synchronization objects for
 mutexes.

---
 server/inproc_sync.c |  26 ++++++++++
 server/mutex.c       | 115 +++++++++++++++++++++++++++++--------------
 server/object.h      |   2 +
 3 files changed, 107 insertions(+), 36 deletions(-)

diff --git a/server/inproc_sync.c b/server/inproc_sync.c
index f2aee9ece3f..6471364c4de 100644
--- a/server/inproc_sync.c
+++ b/server/inproc_sync.c
@@ -83,6 +83,18 @@ int create_inproc_semaphore( unsigned int count, unsigned int max )
     return ioctl( device, NTSYNC_IOC_CREATE_SEM, &args );
 }
 
+int create_inproc_mutex( thread_id_t owner, unsigned int count )
+{
+    struct ntsync_mutex_args args;
+    int device;
+
+    if ((device = get_linux_device()) < 0) return -1;
+
+    args.owner = owner;
+    args.count = count;
+    return ioctl( device, NTSYNC_IOC_CREATE_MUTEX, &args );
+}
+
 void set_inproc_event( int event )
 {
     __u32 count;
@@ -105,6 +117,11 @@ void reset_inproc_event( int event )
     ioctl( event, NTSYNC_IOC_EVENT_RESET, &count );
 }
 
+void abandon_inproc_mutex( thread_id_t tid, int mutex )
+{
+    ioctl( mutex, NTSYNC_IOC_MUTEX_KILL, &tid );
+}
+
 #else
 
 int use_inproc_sync(void)
@@ -122,6 +139,11 @@ int create_inproc_semaphore( unsigned int count, unsigned int max )
     return -1;
 }
 
+int create_inproc_mutex( thread_id_t owner, unsigned int count )
+{
+    return -1;
+}
+
 void set_inproc_event( int event )
 {
 }
@@ -130,6 +152,10 @@ void reset_inproc_event( int event )
 {
 }
 
+void abandon_inproc_mutex( thread_id_t tid, int mutex )
+{
+}
+
 #endif
 
 
diff --git a/server/mutex.c b/server/mutex.c
index 4218423ebac..671e1a77e36 100644
--- a/server/mutex.c
+++ b/server/mutex.c
@@ -25,6 +25,7 @@
 #include <stdlib.h>
 #include <stdarg.h>
 #include <sys/types.h>
+#include <unistd.h>
 
 #include "ntstatus.h"
 #define WIN32_NO_STATUS
@@ -38,6 +39,8 @@
 
 static const WCHAR mutex_name[] = {'M','u','t','a','n','t'};
 
+static struct list inproc_mutexes = LIST_INIT(inproc_mutexes);
+
 struct type_descr mutex_type =
 {
     { mutex_name, sizeof(mutex_name) },   /* name */
@@ -53,10 +56,21 @@ struct type_descr mutex_type =
 struct mutex
 {
     struct object  obj;             /* object header */
-    struct thread *owner;           /* mutex owner */
-    unsigned int   count;           /* recursion count */
-    int            abandoned;       /* has it been abandoned? */
-    struct list    entry;           /* entry in owner thread mutex list */
+    union
+    {
+        struct
+        {
+            struct thread *owner;   /* mutex owner */
+            unsigned int count;     /* recursion count */
+            int abandoned;          /* has it been abandoned? */
+            struct list entry;      /* entry in owner thread mutex list */
+        } server;
+        struct
+        {
+            int inproc_sync;        /* in-process synchronization object */
+            struct list entry;      /* entry in inproc_mutexes list */
+        } inproc;
+    } u;
 };
 
 static void mutex_dump( struct object *obj, int verbose );
@@ -64,6 +78,7 @@ static int mutex_signaled( struct object *obj, struct wait_queue_entry *entry );
 static void mutex_satisfied( struct object *obj, struct wait_queue_entry *entry );
 static void mutex_destroy( struct object *obj );
 static int mutex_signal( struct object *obj, unsigned int access );
+static int mutex_get_inproc_sync( struct object *obj, enum inproc_sync_type *type );
 
 static const struct object_ops mutex_ops =
 {
@@ -85,7 +100,7 @@ static const struct object_ops mutex_ops =
     default_unlink_name,       /* unlink_name */
     no_open_file,              /* open_file */
     no_kernel_obj_list,        /* get_kernel_obj_list */
-    no_get_inproc_sync,        /* get_inproc_sync */
+    mutex_get_inproc_sync,     /* get_inproc_sync */
     no_close_handle,           /* close_handle */
     mutex_destroy              /* destroy */
 };
@@ -94,23 +109,23 @@ static const struct object_ops mutex_ops =
 /* grab a mutex for a given thread */
 static void do_grab( struct mutex *mutex, struct thread *thread )
 {
-    assert( !mutex->count || (mutex->owner == thread) );
+    assert( !mutex->u.server.count || (mutex->u.server.owner == thread) );
 
-    if (!mutex->count++)  /* FIXME: avoid wrap-around */
+    if (!mutex->u.server.count++)  /* FIXME: avoid wrap-around */
     {
-        assert( !mutex->owner );
-        mutex->owner = thread;
-        list_add_head( &thread->mutex_list, &mutex->entry );
+        assert( !mutex->u.server.owner );
+        mutex->u.server.owner = thread;
+        list_add_head( &thread->mutex_list, &mutex->u.server.entry );
     }
 }
 
 /* release a mutex once the recursion count is 0 */
 static void do_release( struct mutex *mutex )
 {
-    assert( !mutex->count );
+    assert( !mutex->u.server.count );
     /* remove the mutex from the thread list of owned mutexes */
-    list_remove( &mutex->entry );
-    mutex->owner = NULL;
+    list_remove( &mutex->u.server.entry );
+    mutex->u.server.owner = NULL;
     wake_up( &mutex->obj, 0 );
 }
 
@@ -124,10 +139,18 @@ static struct mutex *create_mutex( struct object *root, const struct unicode_str
         if (get_error() != STATUS_OBJECT_NAME_EXISTS)
         {
             /* initialize it if it didn't already exist */
-            mutex->count = 0;
-            mutex->owner = NULL;
-            mutex->abandoned = 0;
-            if (owned) do_grab( mutex, current );
+            if (use_inproc_sync())
+            {
+                mutex->u.inproc.inproc_sync = create_inproc_mutex( owned ? current->id : 0, owned ? 1 : 0 );
+                list_add_tail( &inproc_mutexes, &mutex->u.inproc.entry );
+            }
+            else
+            {
+                mutex->u.server.count = 0;
+                mutex->u.server.owner = NULL;
+                mutex->u.server.abandoned = 0;
+                if (owned) do_grab( mutex, current );
+            }
         }
     }
     return mutex;
@@ -135,30 +158,34 @@ static struct mutex *create_mutex( struct object *root, const struct unicode_str
 
 void abandon_mutexes( struct thread *thread )
 {
+    struct mutex *mutex;
     struct list *ptr;
 
     while ((ptr = list_head( &thread->mutex_list )) != NULL)
     {
-        struct mutex *mutex = LIST_ENTRY( ptr, struct mutex, entry );
-        assert( mutex->owner == thread );
-        mutex->count = 0;
-        mutex->abandoned = 1;
+        mutex = LIST_ENTRY( ptr, struct mutex, u.server.entry );
+        assert( mutex->u.server.owner == thread );
+        mutex->u.server.count = 0;
+        mutex->u.server.abandoned = 1;
         do_release( mutex );
     }
+
+    LIST_FOR_EACH_ENTRY(mutex, &inproc_mutexes, struct mutex, u.inproc.entry)
+        abandon_inproc_mutex( thread->id, mutex->u.inproc.inproc_sync );
 }
 
 static void mutex_dump( struct object *obj, int verbose )
 {
     struct mutex *mutex = (struct mutex *)obj;
     assert( obj->ops == &mutex_ops );
-    fprintf( stderr, "Mutex count=%u owner=%p\n", mutex->count, mutex->owner );
+    fprintf( stderr, "Mutex count=%u owner=%p\n", mutex->u.server.count, mutex->u.server.owner );
 }
 
 static int mutex_signaled( struct object *obj, struct wait_queue_entry *entry )
 {
     struct mutex *mutex = (struct mutex *)obj;
     assert( obj->ops == &mutex_ops );
-    return (!mutex->count || (mutex->owner == get_wait_queue_thread( entry )));
+    return (!mutex->u.server.count || (mutex->u.server.owner == get_wait_queue_thread( entry )));
 }
 
 static void mutex_satisfied( struct object *obj, struct wait_queue_entry *entry )
@@ -167,8 +194,8 @@ static void mutex_satisfied( struct object *obj, struct wait_queue_entry *entry
     assert( obj->ops == &mutex_ops );
 
     do_grab( mutex, get_wait_queue_thread( entry ));
-    if (mutex->abandoned) make_wait_abandoned( entry );
-    mutex->abandoned = 0;
+    if (mutex->u.server.abandoned) make_wait_abandoned( entry );
+    mutex->u.server.abandoned = 0;
 }
 
 static int mutex_signal( struct object *obj, unsigned int access )
@@ -181,23 +208,39 @@ static int mutex_signal( struct object *obj, unsigned int access )
         set_error( STATUS_ACCESS_DENIED );
         return 0;
     }
-    if (!mutex->count || (mutex->owner != current))
+    if (!mutex->u.server.count || (mutex->u.server.owner != current))
     {
         set_error( STATUS_MUTANT_NOT_OWNED );
         return 0;
     }
-    if (!--mutex->count) do_release( mutex );
+    if (!--mutex->u.server.count) do_release( mutex );
     return 1;
 }
 
+static int mutex_get_inproc_sync( struct object *obj, enum inproc_sync_type *type )
+{
+    struct mutex *mutex = (struct mutex *)obj;
+
+    *type = INPROC_SYNC_MUTEX;
+    return mutex->u.inproc.inproc_sync;
+}
+
 static void mutex_destroy( struct object *obj )
 {
     struct mutex *mutex = (struct mutex *)obj;
     assert( obj->ops == &mutex_ops );
 
-    if (!mutex->count) return;
-    mutex->count = 0;
-    do_release( mutex );
+    if (use_inproc_sync())
+    {
+        close( mutex->u.inproc.inproc_sync );
+        list_remove( &mutex->u.inproc.entry );
+    }
+    else
+    {
+        if (!mutex->u.server.count) return;
+        mutex->u.server.count = 0;
+        do_release( mutex );
+    }
 }
 
 /* create a mutex */
@@ -241,11 +284,11 @@ DECL_HANDLER(release_mutex)
     if ((mutex = (struct mutex *)get_handle_obj( current->process, req->handle,
                                                  0, &mutex_ops )))
     {
-        if (!mutex->count || (mutex->owner != current)) set_error( STATUS_MUTANT_NOT_OWNED );
+        if (!mutex->u.server.count || (mutex->u.server.owner != current)) set_error( STATUS_MUTANT_NOT_OWNED );
         else
         {
-            reply->prev_count = mutex->count;
-            if (!--mutex->count) do_release( mutex );
+            reply->prev_count = mutex->u.server.count;
+            if (!--mutex->u.server.count) do_release( mutex );
         }
         release_object( mutex );
     }
@@ -259,9 +302,9 @@ DECL_HANDLER(query_mutex)
     if ((mutex = (struct mutex *)get_handle_obj( current->process, req->handle,
                                                  MUTANT_QUERY_STATE, &mutex_ops )))
     {
-        reply->count = mutex->count;
-        reply->owned = (mutex->owner == current);
-        reply->abandoned = mutex->abandoned;
+        reply->count = mutex->u.server.count;
+        reply->owned = (mutex->u.server.owner == current);
+        reply->abandoned = mutex->u.server.abandoned;
 
         release_object( mutex );
     }
diff --git a/server/object.h b/server/object.h
index e7be6de6333..6678b439e8c 100644
--- a/server/object.h
+++ b/server/object.h
@@ -235,10 +235,12 @@ extern void abandon_mutexes( struct thread *thread );
 
 extern int use_inproc_sync(void);
 extern int create_inproc_event( int manual_reset, int signaled );
+extern int create_inproc_mutex( thread_id_t owner, unsigned int count );
 extern int create_inproc_semaphore( unsigned int count, unsigned int max );
 extern int no_get_inproc_sync( struct object *obj, enum inproc_sync_type *type );
 extern void set_inproc_event( int event );
 extern void reset_inproc_event( int event );
+extern void abandon_inproc_mutex( thread_id_t tid, int mutex );
 
 /* serial functions */
 
-- 
GitLab


From 99125a77c00d5e9d2bc3ff5af53ccf954360d8ab Mon Sep 17 00:00:00 2001
From: Elizabeth Figura <zfigura@codeweavers.com>
Date: Thu, 11 Mar 2021 20:32:58 -0600
Subject: [PATCH 08/29] server: Create in-process synchronization objects for
 completion ports.

---
 server/completion.c | 24 ++++++++++++++++++++++--
 1 file changed, 22 insertions(+), 2 deletions(-)

diff --git a/server/completion.c b/server/completion.c
index ac598e85229..11e39c93a99 100644
--- a/server/completion.c
+++ b/server/completion.c
@@ -25,6 +25,7 @@
 
 #include <stdarg.h>
 #include <stdio.h>
+#include <unistd.h>
 
 #include "ntstatus.h"
 #define WIN32_NO_STATUS
@@ -77,6 +78,7 @@ struct completion
     struct list    wait_queue;
     unsigned int   depth;
     int            closed;
+    int            inproc_sync;
 };
 
 static void completion_wait_dump( struct object*, int );
@@ -157,6 +159,7 @@ static void completion_wait_satisfied( struct object *obj, struct wait_queue_ent
 static void completion_dump( struct object*, int );
 static int completion_signaled( struct object *obj, struct wait_queue_entry *entry );
 static int completion_close_handle( struct object *obj, struct process *process, obj_handle_t handle );
+static int completion_get_inproc_sync( struct object *obj, enum inproc_sync_type *type );
 static void completion_destroy( struct object * );
 
 static const struct object_ops completion_ops =
@@ -179,7 +182,7 @@ static const struct object_ops completion_ops =
     default_unlink_name,       /* unlink_name */
     no_open_file,              /* open_file */
     no_kernel_obj_list,        /* get_kernel_obj_list */
-    no_get_inproc_sync,        /* get_inproc_sync */
+    completion_get_inproc_sync,/* get_inproc_sync */
     completion_close_handle,   /* close_handle */
     completion_destroy         /* destroy */
 };
@@ -193,6 +196,7 @@ static void completion_destroy( struct object *obj)
     {
         free( tmp );
     }
+    if (use_inproc_sync()) close( completion->inproc_sync );
 }
 
 static void completion_dump( struct object *obj, int verbose )
@@ -230,6 +234,7 @@ static int completion_close_handle( struct object *obj, struct process *process,
     }
     completion->closed = 1;
     wake_up( obj, 0 );
+    set_inproc_event( completion->inproc_sync );
     return 1;
 }
 
@@ -263,6 +268,14 @@ static struct completion_wait *create_completion_wait( struct thread *thread )
     return wait;
 }
 
+static int completion_get_inproc_sync( struct object *obj, enum inproc_sync_type *type )
+{
+    struct completion *completion = (struct completion *)obj;
+
+    *type = INPROC_SYNC_MANUAL_SERVER;
+    return completion->inproc_sync;
+}
+
 static struct completion *create_completion( struct object *root, const struct unicode_str *name,
                                              unsigned int attr, unsigned int concurrent,
                                              const struct security_descriptor *sd )
@@ -277,6 +290,7 @@ static struct completion *create_completion( struct object *root, const struct u
             list_init( &completion->wait_queue );
             completion->depth = 0;
             completion->closed = 0;
+            completion->inproc_sync = create_inproc_event( TRUE, FALSE );
         }
     }
 
@@ -309,7 +323,11 @@ void add_completion( struct completion *completion, apc_param_t ckey, apc_param_
         wake_up( &wait->obj, 1 );
         if (list_empty( &completion->queue )) return;
     }
-    if (!list_empty( &completion->queue )) wake_up( &completion->obj, 0 );
+    if (!list_empty( &completion->queue ))
+    {
+        wake_up( &completion->obj, 0 );
+        set_inproc_event( completion->inproc_sync );
+    }
 }
 
 /* create a completion */
@@ -410,6 +428,8 @@ DECL_HANDLER(remove_completion)
         reply->information = msg->information;
         free( msg );
         reply->wait_handle = 0;
+        if (list_empty( &completion->queue ))
+            reset_inproc_event( completion->inproc_sync );
     }
 
     release_object( completion );
-- 
GitLab


From 6880a8091a69582e17880060bdda5dc13dbcf5cd Mon Sep 17 00:00:00 2001
From: Elizabeth Figura <zfigura@codeweavers.com>
Date: Wed, 19 Mar 2025 16:43:36 -0500
Subject: [PATCH 09/29] server: Create in-process synchronization objects for
 consoles.

---
 server/console.c | 29 +++++++++++++++++++++++++++--
 1 file changed, 27 insertions(+), 2 deletions(-)

diff --git a/server/console.c b/server/console.c
index 50304f746e5..135f7336a47 100644
--- a/server/console.c
+++ b/server/console.c
@@ -54,6 +54,7 @@ struct console
 {
     struct object                obj;           /* object header */
     int                          signaled;      /* is console signaled */
+    int                          inproc_sync;   /* in-process synchronization object */
     struct thread               *renderer;      /* console renderer thread */
     struct screen_buffer        *active;        /* active screen buffer */
     struct console_server       *server;        /* console server object */
@@ -74,6 +75,7 @@ static struct object *console_lookup_name( struct object *obj, struct unicode_st
                                            unsigned int attr, struct object *root );
 static struct object *console_open_file( struct object *obj, unsigned int access,
                                          unsigned int sharing, unsigned int options );
+static int console_get_inproc_sync( struct object *obj, enum inproc_sync_type *type );
 static int console_add_queue( struct object *obj, struct wait_queue_entry *entry );
 
 static const struct object_ops console_ops =
@@ -96,7 +98,7 @@ static const struct object_ops console_ops =
     NULL,                             /* unlink_name */
     console_open_file,                /* open_file */
     no_kernel_obj_list,               /* get_kernel_obj_list */
-    no_get_inproc_sync,               /* get_inproc_sync */
+    console_get_inproc_sync,          /* get_inproc_sync */
     no_close_handle,                  /* close_handle */
     console_destroy                   /* destroy */
 };
@@ -547,6 +549,7 @@ static struct object *create_console(void)
 
     console->renderer      = NULL;
     console->signaled      = 0;
+    console->inproc_sync   = create_inproc_event( TRUE, console->signaled );
     console->active        = NULL;
     console->server        = NULL;
     console->fd            = NULL;
@@ -785,6 +788,7 @@ static void console_destroy( struct object *obj )
     free_async_queue( &console->read_q );
     if (console->fd)
         release_object( console->fd );
+    if (use_inproc_sync()) close( console->inproc_sync );
 }
 
 static struct object *create_console_connection( struct console *console )
@@ -838,6 +842,22 @@ static struct object *console_open_file( struct object *obj, unsigned int access
     return grab_object( obj );
 }
 
+static int console_get_inproc_sync( struct object *obj, enum inproc_sync_type *type )
+{
+    struct console *console = (struct console *)obj;
+
+    /* cf. console_add_queue() */
+    if (console->server && !console->server->once_input)
+    {
+        console->server->once_input = 1;
+        if (console->server->term_fd == -1)
+            queue_host_ioctl( console->server, IOCTL_CONDRV_PEEK, 0, NULL, NULL );
+    }
+
+    *type = INPROC_SYNC_MANUAL_SERVER;
+    return console->inproc_sync;
+}
+
 static void screen_buffer_dump( struct object *obj, int verbose )
 {
     struct screen_buffer *screen_buffer = (struct screen_buffer *)obj;
@@ -1583,11 +1603,16 @@ DECL_HANDLER(get_next_console_request)
 
     if (!server->console->renderer) server->console->renderer = current;
 
-    if (!req->signal) server->console->signaled = 0;
+    if (!req->signal)
+    {
+        server->console->signaled = 0;
+        reset_inproc_event( server->console->inproc_sync );
+    }
     else if (!server->console->signaled)
     {
         server->console->signaled = 1;
         wake_up( &server->console->obj, 0 );
+        set_inproc_event( server->console->inproc_sync );
         LIST_FOR_EACH_ENTRY( screen_buffer, &server->console->screen_buffers, struct screen_buffer, entry )
             wake_up( &screen_buffer->obj, 0 );
         LIST_FOR_EACH_ENTRY( input, &server->console->inputs, struct console_input, entry )
-- 
GitLab


From 75f286998d096585171ae9cde03e642812c4fdde Mon Sep 17 00:00:00 2001
From: Elizabeth Figura <zfigura@codeweavers.com>
Date: Wed, 19 Mar 2025 17:06:29 -0500
Subject: [PATCH 10/29] server: Create in-process synchronization objects for
 console servers.

---
 server/console.c | 22 +++++++++++++++++++++-
 1 file changed, 21 insertions(+), 1 deletion(-)

diff --git a/server/console.c b/server/console.c
index 135f7336a47..64e827f84a2 100644
--- a/server/console.c
+++ b/server/console.c
@@ -145,6 +145,7 @@ struct console_server
     unsigned int          once_input : 1; /* flag if input thread has already been requested */
     int                   term_fd;        /* UNIX terminal fd */
     struct termios        termios;        /* original termios */
+    int                   inproc_sync;    /* in-process synchronization object */
 };
 
 static void console_server_dump( struct object *obj, int verbose );
@@ -155,6 +156,7 @@ static struct object *console_server_lookup_name( struct object *obj, struct uni
                                                 unsigned int attr, struct object *root );
 static struct object *console_server_open_file( struct object *obj, unsigned int access,
                                                 unsigned int sharing, unsigned int options );
+static int console_server_get_inproc_sync( struct object *obj, enum inproc_sync_type *type );
 
 static const struct object_ops console_server_ops =
 {
@@ -176,7 +178,7 @@ static const struct object_ops console_server_ops =
     NULL,                             /* unlink_name */
     console_server_open_file,         /* open_file */
     no_kernel_obj_list,               /* get_kernel_obj_list */
-    no_get_inproc_sync,               /* get_inproc_sync */
+    console_server_get_inproc_sync,   /* get_inproc_sync */
     no_close_handle,                  /* close_handle */
     console_server_destroy            /* destroy */
 };
@@ -596,6 +598,7 @@ static int queue_host_ioctl( struct console_server *server, unsigned int code, u
     }
     list_add_tail( &server->queue, &ioctl->entry );
     wake_up( &server->obj, 0 );
+    set_inproc_event( server->inproc_sync );
     if (async) set_error( STATUS_PENDING );
     return 1;
 }
@@ -628,6 +631,7 @@ static void disconnect_console_server( struct console_server *server )
         server->console->server = NULL;
         server->console = NULL;
         wake_up( &server->obj, 0 );
+        set_inproc_event( server->inproc_sync );
     }
 }
 
@@ -919,6 +923,7 @@ static void console_server_destroy( struct object *obj )
     assert( obj->ops == &console_server_ops );
     disconnect_console_server( server );
     if (server->fd) release_object( server->fd );
+    if (use_inproc_sync()) close( server->inproc_sync );
 }
 
 static struct object *console_server_lookup_name( struct object *obj, struct unicode_str *name,
@@ -979,6 +984,14 @@ static struct object *console_server_open_file( struct object *obj, unsigned int
     return grab_object( obj );
 }
 
+static int console_server_get_inproc_sync( struct object *obj, enum inproc_sync_type *type )
+{
+    struct console_server *server = (struct console_server *)obj;
+
+    *type = INPROC_SYNC_MANUAL_SERVER;
+    return server->inproc_sync;
+}
+
 static struct object *create_console_server( void )
 {
     struct console_server *server;
@@ -988,6 +1001,7 @@ static struct object *create_console_server( void )
     server->busy       = 0;
     server->once_input = 0;
     server->term_fd    = -1;
+    server->inproc_sync = create_inproc_event( TRUE, TRUE );
     list_init( &server->queue );
     list_init( &server->read_queue );
     server->fd = alloc_pseudo_fd( &console_server_fd_ops, &server->obj, FILE_SYNCHRONOUS_IO_NONALERT );
@@ -1642,6 +1656,9 @@ DECL_HANDLER(get_next_console_request)
         list_remove( &ioctl->entry );
     }
 
+    if (list_empty( &server->queue ))
+        reset_inproc_event( server->inproc_sync );
+
     if (ioctl)
     {
         struct async *async = ioctl->async;
@@ -1726,5 +1743,8 @@ DECL_HANDLER(get_next_console_request)
         set_error( STATUS_PENDING );
     }
 
+    if (list_empty( &server->queue ))
+        reset_inproc_event( server->inproc_sync );
+
     release_object( server );
 }
-- 
GitLab


From 723da38c8bf4e22a6b7e00f09e7f77e7c348f305 Mon Sep 17 00:00:00 2001
From: Elizabeth Figura <zfigura@codeweavers.com>
Date: Wed, 19 Mar 2025 17:14:45 -0500
Subject: [PATCH 11/29] server: Create in-process synchronization objects for
 screen buffers.

---
 server/console.c | 22 +++++++++++++++++++++-
 1 file changed, 21 insertions(+), 1 deletion(-)

diff --git a/server/console.c b/server/console.c
index 64e827f84a2..3fa994c4fb2 100644
--- a/server/console.c
+++ b/server/console.c
@@ -219,6 +219,7 @@ struct screen_buffer
     unsigned int          id;            /* buffer id */
     struct fd            *fd;            /* for bare console, attached output fd */
     struct async_queue    ioctl_q;       /* ioctl queue */
+    int                   inproc_sync;   /* in-process synchronization object */
 };
 
 static void screen_buffer_dump( struct object *obj, int verbose );
@@ -227,6 +228,7 @@ static int screen_buffer_signaled( struct object *obj, struct wait_queue_entry *
 static struct fd *screen_buffer_get_fd( struct object *obj );
 static struct object *screen_buffer_open_file( struct object *obj, unsigned int access,
                                                unsigned int sharing, unsigned int options );
+static int screen_buffer_get_inproc_sync( struct object *obj, enum inproc_sync_type *type );
 
 static const struct object_ops screen_buffer_ops =
 {
@@ -248,7 +250,7 @@ static const struct object_ops screen_buffer_ops =
     NULL,                             /* unlink_name */
     screen_buffer_open_file,          /* open_file */
     no_kernel_obj_list,               /* get_kernel_obj_list */
-    no_get_inproc_sync,               /* get_inproc_sync */
+    screen_buffer_get_inproc_sync,    /* get_inproc_sync */
     no_close_handle,                  /* close_handle */
     screen_buffer_destroy             /* destroy */
 };
@@ -660,6 +662,7 @@ static struct object *create_screen_buffer( struct console *console )
 
     screen_buffer->id    = ++console->last_id;
     screen_buffer->input = console;
+    screen_buffer->inproc_sync = create_inproc_event( TRUE, console->signaled );
     init_async_queue( &screen_buffer->ioctl_q );
     list_add_head( &console->screen_buffers, &screen_buffer->entry );
 
@@ -780,7 +783,10 @@ static void console_destroy( struct object *obj )
     console->active = NULL;
 
     LIST_FOR_EACH_ENTRY( curr, &console->screen_buffers, struct screen_buffer, entry )
+    {
         curr->input = NULL;
+        reset_inproc_event( curr->inproc_sync );
+    }
 
     LIST_FOR_EACH_ENTRY( input, &console->inputs, struct console_input, entry )
         input->console = NULL;
@@ -885,6 +891,7 @@ static void screen_buffer_destroy( struct object *obj )
     }
     if (screen_buffer->fd) release_object( screen_buffer->fd );
     free_async_queue( &screen_buffer->ioctl_q );
+    if (use_inproc_sync()) close( screen_buffer->inproc_sync );
 }
 
 static int screen_buffer_signaled( struct object *obj, struct wait_queue_entry *entry )
@@ -901,6 +908,14 @@ static struct object *screen_buffer_open_file( struct object *obj, unsigned int
     return grab_object( obj );
 }
 
+static int screen_buffer_get_inproc_sync( struct object *obj, enum inproc_sync_type *type )
+{
+    struct screen_buffer *screen_buffer = (struct screen_buffer *)obj;
+
+    *type = INPROC_SYNC_MANUAL_SERVER;
+    return screen_buffer->inproc_sync;
+}
+
 static struct fd *screen_buffer_get_fd( struct object *obj )
 {
     struct screen_buffer *screen_buffer = (struct screen_buffer*)obj;
@@ -1621,6 +1636,8 @@ DECL_HANDLER(get_next_console_request)
     {
         server->console->signaled = 0;
         reset_inproc_event( server->console->inproc_sync );
+        LIST_FOR_EACH_ENTRY( screen_buffer, &server->console->screen_buffers, struct screen_buffer, entry )
+            reset_inproc_event( screen_buffer->inproc_sync );
     }
     else if (!server->console->signaled)
     {
@@ -1628,7 +1645,10 @@ DECL_HANDLER(get_next_console_request)
         wake_up( &server->console->obj, 0 );
         set_inproc_event( server->console->inproc_sync );
         LIST_FOR_EACH_ENTRY( screen_buffer, &server->console->screen_buffers, struct screen_buffer, entry )
+        {
             wake_up( &screen_buffer->obj, 0 );
+            set_inproc_event( screen_buffer->inproc_sync );
+        }
         LIST_FOR_EACH_ENTRY( input, &server->console->inputs, struct console_input, entry )
             wake_up( &input->obj, 0 );
         LIST_FOR_EACH_ENTRY( output, &server->console->outputs, struct console_output, entry )
-- 
GitLab


From e0c7fcaeb52bc735b95fcacb5a339407dcb52e87 Mon Sep 17 00:00:00 2001
From: Elizabeth Figura <zfigura@codeweavers.com>
Date: Wed, 19 Mar 2025 17:30:31 -0500
Subject: [PATCH 12/29] server: Create in-process synchronization objects for
 console inputs.

---
 server/console.c | 22 +++++++++++++++++++++-
 1 file changed, 21 insertions(+), 1 deletion(-)

diff --git a/server/console.c b/server/console.c
index 3fa994c4fb2..2a36d301bae 100644
--- a/server/console.c
+++ b/server/console.c
@@ -311,12 +311,14 @@ struct console_input
     struct fd            *fd;          /* pseudo-fd */
     struct list           entry;       /* entry in console->inputs */
     struct console       *console;     /* associated console at creation time */
+    int                   inproc_sync; /* in-process synchronization object */
 };
 
 static void console_input_dump( struct object *obj, int verbose );
 static int console_input_signaled( struct object *obj, struct wait_queue_entry *entry );
 static struct object *console_input_open_file( struct object *obj, unsigned int access,
                                                unsigned int sharing, unsigned int options );
+static int console_input_get_inproc_sync( struct object *obj, enum inproc_sync_type *type );
 static struct fd *console_input_get_fd( struct object *obj );
 static void console_input_destroy( struct object *obj );
 
@@ -340,7 +342,7 @@ static const struct object_ops console_input_ops =
     default_unlink_name,              /* unlink_name */
     console_input_open_file,          /* open_file */
     no_kernel_obj_list,               /* get_kernel_obj_list */
-    no_get_inproc_sync,               /* get_inproc_sync */
+    console_input_get_inproc_sync,    /* get_inproc_sync */
     no_close_handle,                  /* close_handle */
     console_input_destroy             /* destroy */
 };
@@ -789,7 +791,10 @@ static void console_destroy( struct object *obj )
     }
 
     LIST_FOR_EACH_ENTRY( input, &console->inputs, struct console_input, entry )
+    {
         input->console = NULL;
+        reset_inproc_event( input->inproc_sync );
+    }
 
     LIST_FOR_EACH_ENTRY( output, &console->outputs, struct console_output, entry )
         output->console = NULL;
@@ -1402,6 +1407,7 @@ static struct object *console_device_lookup_name( struct object *obj, struct uni
         }
         console_input->console = current->process->console;
         list_add_head( &current->process->console->inputs, &console_input->entry );
+        console_input->inproc_sync = create_inproc_event( TRUE, current->process->console->signaled );
         return &console_input->obj;
     }
 
@@ -1500,6 +1506,14 @@ static struct object *console_input_open_file( struct object *obj, unsigned int
     return grab_object( obj );
 }
 
+static int console_input_get_inproc_sync( struct object *obj, enum inproc_sync_type *type )
+{
+    struct console_input *console_input = (struct console_input *)obj;
+
+    *type = INPROC_SYNC_MANUAL_SERVER;
+    return console_input->inproc_sync;
+}
+
 static void console_input_destroy( struct object *obj )
 {
     struct console_input *console_input = (struct console_input *)obj;
@@ -1507,6 +1521,7 @@ static void console_input_destroy( struct object *obj )
     assert( obj->ops == &console_input_ops );
     if (console_input->fd) release_object( console_input->fd );
     if (console_input->console) list_remove( &console_input->entry );
+    if (use_inproc_sync()) close( console_input->inproc_sync );
 }
 
 static void console_input_ioctl( struct fd *fd, ioctl_code_t code, struct async *async )
@@ -1638,6 +1653,8 @@ DECL_HANDLER(get_next_console_request)
         reset_inproc_event( server->console->inproc_sync );
         LIST_FOR_EACH_ENTRY( screen_buffer, &server->console->screen_buffers, struct screen_buffer, entry )
             reset_inproc_event( screen_buffer->inproc_sync );
+        LIST_FOR_EACH_ENTRY( input, &server->console->inputs, struct console_input, entry )
+            reset_inproc_event( input->inproc_sync );
     }
     else if (!server->console->signaled)
     {
@@ -1650,7 +1667,10 @@ DECL_HANDLER(get_next_console_request)
             set_inproc_event( screen_buffer->inproc_sync );
         }
         LIST_FOR_EACH_ENTRY( input, &server->console->inputs, struct console_input, entry )
+        {
             wake_up( &input->obj, 0 );
+            set_inproc_event( input->inproc_sync );
+        }
         LIST_FOR_EACH_ENTRY( output, &server->console->outputs, struct console_output, entry )
             wake_up( &output->obj, 0 );
     }
-- 
GitLab


From bb887418db840d081990c3bf99d7f1a7b8820a10 Mon Sep 17 00:00:00 2001
From: Elizabeth Figura <zfigura@codeweavers.com>
Date: Wed, 19 Mar 2025 17:35:15 -0500
Subject: [PATCH 13/29] server: Create in-process synchronization objects for
 console outputs.

---
 server/console.c | 22 +++++++++++++++++++++-
 1 file changed, 21 insertions(+), 1 deletion(-)

diff --git a/server/console.c b/server/console.c
index 2a36d301bae..58ba8ee4634 100644
--- a/server/console.c
+++ b/server/console.c
@@ -373,6 +373,7 @@ struct console_output
     struct fd            *fd;          /* pseudo-fd */
     struct list           entry;       /* entry in console->outputs */
     struct console       *console;     /* associated console at creation time */
+    int                   inproc_sync; /* in-process synchronization object */
 };
 
 static void console_output_dump( struct object *obj, int verbose );
@@ -380,6 +381,7 @@ static int console_output_signaled( struct object *obj, struct wait_queue_entry
 static struct fd *console_output_get_fd( struct object *obj );
 static struct object *console_output_open_file( struct object *obj, unsigned int access,
                                                 unsigned int sharing, unsigned int options );
+static int console_output_get_inproc_sync( struct object *obj, enum inproc_sync_type *type );
 static void console_output_destroy( struct object *obj );
 
 static const struct object_ops console_output_ops =
@@ -402,7 +404,7 @@ static const struct object_ops console_output_ops =
     default_unlink_name,              /* unlink_name */
     console_output_open_file,         /* open_file */
     no_kernel_obj_list,               /* get_kernel_obj_list */
-    no_get_inproc_sync,               /* get_inproc_sync */
+    console_output_get_inproc_sync,   /* get_inproc_sync */
     no_close_handle,                  /* close_handle */
     console_output_destroy            /* destroy */
 };
@@ -797,7 +799,10 @@ static void console_destroy( struct object *obj )
     }
 
     LIST_FOR_EACH_ENTRY( output, &console->outputs, struct console_output, entry )
+    {
         output->console = NULL;
+        reset_inproc_event( output->inproc_sync );
+    }
 
     free_async_queue( &console->ioctl_q );
     free_async_queue( &console->read_q );
@@ -1432,6 +1437,7 @@ static struct object *console_device_lookup_name( struct object *obj, struct uni
         }
         console_output->console = current->process->console;
         list_add_head( &current->process->console->outputs, &console_output->entry );
+        console_output->inproc_sync = create_inproc_event( TRUE, current->process->console->signaled );
         return &console_output->obj;
     }
 
@@ -1586,6 +1592,14 @@ static struct object *console_output_open_file( struct object *obj, unsigned int
     return grab_object( obj );
 }
 
+static int console_output_get_inproc_sync( struct object *obj, enum inproc_sync_type *type )
+{
+    struct console_output *console_output = (struct console_output *)obj;
+
+    *type = INPROC_SYNC_MANUAL_SERVER;
+    return console_output->inproc_sync;
+}
+
 static void console_output_destroy( struct object *obj )
 {
     struct console_output *console_output = (struct console_output *)obj;
@@ -1593,6 +1607,7 @@ static void console_output_destroy( struct object *obj )
     assert( obj->ops == &console_output_ops );
     if (console_output->fd) release_object( console_output->fd );
     if (console_output->console) list_remove( &console_output->entry );
+    if (use_inproc_sync()) close( console_output->inproc_sync );
 }
 
 static void console_output_ioctl( struct fd *fd, ioctl_code_t code, struct async *async )
@@ -1655,6 +1670,8 @@ DECL_HANDLER(get_next_console_request)
             reset_inproc_event( screen_buffer->inproc_sync );
         LIST_FOR_EACH_ENTRY( input, &server->console->inputs, struct console_input, entry )
             reset_inproc_event( input->inproc_sync );
+        LIST_FOR_EACH_ENTRY( output, &server->console->outputs, struct console_output, entry )
+            reset_inproc_event( output->inproc_sync );
     }
     else if (!server->console->signaled)
     {
@@ -1672,7 +1689,10 @@ DECL_HANDLER(get_next_console_request)
             set_inproc_event( input->inproc_sync );
         }
         LIST_FOR_EACH_ENTRY( output, &server->console->outputs, struct console_output, entry )
+        {
             wake_up( &output->obj, 0 );
+            set_inproc_event( output->inproc_sync );
+        }
     }
 
     if (req->read)
-- 
GitLab


From 13f89c789ce9ed525ce7de7e0b3338f4646f4b87 Mon Sep 17 00:00:00 2001
From: Elizabeth Figura <zfigura@codeweavers.com>
Date: Mon, 8 Mar 2021 17:11:03 -0600
Subject: [PATCH 14/29] server: Create in-process synchronization objects for
 debug objects.

---
 server/debugger.c | 20 +++++++++++++++++++-
 1 file changed, 19 insertions(+), 1 deletion(-)

diff --git a/server/debugger.c b/server/debugger.c
index cab5f95eefd..2aaa87ed792 100644
--- a/server/debugger.c
+++ b/server/debugger.c
@@ -71,6 +71,7 @@ struct debug_obj
     struct object        obj;         /* object header */
     struct list          event_queue; /* pending events queue */
     unsigned int         flags;       /* debug flags */
+    int                  inproc_sync; /* in-process synchronization object */
 };
 
 
@@ -105,6 +106,7 @@ static const struct object_ops debug_event_ops =
 
 static void debug_obj_dump( struct object *obj, int verbose );
 static int debug_obj_signaled( struct object *obj, struct wait_queue_entry *entry );
+static int debug_obj_get_inproc_sync( struct object *obj, enum inproc_sync_type *type );
 static void debug_obj_destroy( struct object *obj );
 
 static const struct object_ops debug_obj_ops =
@@ -127,7 +129,7 @@ static const struct object_ops debug_obj_ops =
     default_unlink_name,           /* unlink_name */
     no_open_file,                  /* open_file */
     no_kernel_obj_list,            /* get_kernel_obj_list */
-    no_get_inproc_sync,            /* get_inproc_sync */
+    debug_obj_get_inproc_sync,     /* get_inproc_sync */
     no_close_handle,               /* close_handle */
     debug_obj_destroy              /* destroy */
 };
@@ -255,6 +257,7 @@ static void link_event( struct debug_obj *debug_obj, struct debug_event *event )
         /* grab reference since debugger could be killed while trying to wake up */
         grab_object( debug_obj );
         wake_up( &debug_obj->obj, 0 );
+        set_inproc_event( debug_obj->inproc_sync );
         release_object( debug_obj );
     }
 }
@@ -267,6 +270,7 @@ static void resume_event( struct debug_obj *debug_obj, struct debug_event *event
     {
         grab_object( debug_obj );
         wake_up( &debug_obj->obj, 0 );
+        set_inproc_event( debug_obj->inproc_sync );
         release_object( debug_obj );
     }
 }
@@ -332,6 +336,14 @@ static int debug_obj_signaled( struct object *obj, struct wait_queue_entry *entr
     return find_event_to_send( debug_obj ) != NULL;
 }
 
+static int debug_obj_get_inproc_sync( struct object *obj, enum inproc_sync_type *type )
+{
+    struct debug_obj *debug_obj = (struct debug_obj *)obj;
+
+    *type = INPROC_SYNC_MANUAL_SERVER;
+    return debug_obj->inproc_sync;
+}
+
 static void debug_obj_destroy( struct object *obj )
 {
     struct list *ptr;
@@ -344,6 +356,8 @@ static void debug_obj_destroy( struct object *obj )
     /* free all pending events */
     while ((ptr = list_head( &debug_obj->event_queue )))
         unlink_event( debug_obj, LIST_ENTRY( ptr, struct debug_event, entry ));
+
+    if (use_inproc_sync()) close( debug_obj->inproc_sync );
 }
 
 struct debug_obj *get_debug_obj( struct process *process, obj_handle_t handle, unsigned int access )
@@ -363,6 +377,7 @@ static struct debug_obj *create_debug_obj( struct object *root, const struct uni
         {
             debug_obj->flags = flags;
             list_init( &debug_obj->event_queue );
+            debug_obj->inproc_sync = create_inproc_event( TRUE, FALSE );
         }
     }
     return debug_obj;
@@ -571,6 +586,9 @@ DECL_HANDLER(wait_debug_event)
         reply->tid = get_thread_id( event->sender );
         alloc_event_handles( event, current->process );
         set_reply_data( &event->data, min( get_reply_max_size(), sizeof(event->data) ));
+
+        if (!find_event_to_send( debug_obj ))
+            reset_inproc_event( debug_obj->inproc_sync );
     }
     else
     {
-- 
GitLab


From 8b08e04ce84bc7713baba8b7bbc2c07340d72784 Mon Sep 17 00:00:00 2001
From: Elizabeth Figura <zfigura@codeweavers.com>
Date: Wed, 10 Mar 2021 11:02:42 -0600
Subject: [PATCH 15/29] server: Create in-process synchronization objects for
 device managers.

---
 server/device.c | 30 ++++++++++++++++++++++++++++--
 1 file changed, 28 insertions(+), 2 deletions(-)

diff --git a/server/device.c b/server/device.c
index 253bdf44aae..e8671f18f9e 100644
--- a/server/device.c
+++ b/server/device.c
@@ -25,6 +25,7 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <stdarg.h>
+#include <unistd.h>
 
 #include "ntstatus.h"
 #define WIN32_NO_STATUS
@@ -93,10 +94,12 @@ struct device_manager
     struct list            requests;       /* list of pending irps across all devices */
     struct irp_call       *current_call;   /* call currently executed on client side */
     struct wine_rb_tree    kernel_objects; /* map of objects that have client side pointer associated */
+    int                    inproc_sync;    /* in-process synchronization object */
 };
 
 static void device_manager_dump( struct object *obj, int verbose );
 static int device_manager_signaled( struct object *obj, struct wait_queue_entry *entry );
+static int device_manager_get_inproc_sync( struct object *obj, enum inproc_sync_type *type );
 static void device_manager_destroy( struct object *obj );
 
 static const struct object_ops device_manager_ops =
@@ -119,7 +122,7 @@ static const struct object_ops device_manager_ops =
     NULL,                             /* unlink_name */
     no_open_file,                     /* open_file */
     no_kernel_obj_list,               /* get_kernel_obj_list */
-    no_get_inproc_sync,               /* get_inproc_sync */
+    device_manager_get_inproc_sync,   /* get_inproc_sync */
     no_close_handle,                  /* close_handle */
     device_manager_destroy            /* destroy */
 };
@@ -422,7 +425,12 @@ static void add_irp_to_queue( struct device_manager *manager, struct irp_call *i
     irp->thread = thread ? (struct thread *)grab_object( thread ) : NULL;
     if (irp->file) list_add_tail( &irp->file->requests, &irp->dev_entry );
     list_add_tail( &manager->requests, &irp->mgr_entry );
-    if (list_head( &manager->requests ) == &irp->mgr_entry) wake_up( &manager->obj, 0 );  /* first one */
+    if (list_head( &manager->requests ) == &irp->mgr_entry)
+    {
+        /* first one */
+        wake_up( &manager->obj, 0 );
+        set_inproc_event( manager->inproc_sync );
+    }
 }
 
 static struct object *device_open_file( struct object *obj, unsigned int access,
@@ -756,6 +764,9 @@ static void delete_file( struct device_file *file )
         set_irp_result( irp, STATUS_FILE_DELETED, NULL, 0, 0 );
     }
 
+    if (list_empty( &file->device->manager->requests ))
+        reset_inproc_event( file->device->manager->inproc_sync );
+
     release_object( file );
 }
 
@@ -787,6 +798,14 @@ static int device_manager_signaled( struct object *obj, struct wait_queue_entry
     return !list_empty( &manager->requests );
 }
 
+static int device_manager_get_inproc_sync( struct object *obj, enum inproc_sync_type *type )
+{
+    struct device_manager *manager = (struct device_manager *)obj;
+
+    *type = INPROC_SYNC_MANUAL_SERVER;
+    return manager->inproc_sync;
+}
+
 static void device_manager_destroy( struct object *obj )
 {
     struct device_manager *manager = (struct device_manager *)obj;
@@ -821,6 +840,8 @@ static void device_manager_destroy( struct object *obj )
         assert( !irp->file && !irp->async );
         release_object( irp );
     }
+
+    if (use_inproc_sync()) close( manager->inproc_sync );
 }
 
 static struct device_manager *create_device_manager(void)
@@ -830,6 +851,7 @@ static struct device_manager *create_device_manager(void)
     if ((manager = alloc_object( &device_manager_ops )))
     {
         manager->current_call = NULL;
+        manager->inproc_sync = create_inproc_event( TRUE, FALSE );
         list_init( &manager->devices );
         list_init( &manager->requests );
         wine_rb_init( &manager->kernel_objects, compare_kernel_object );
@@ -1019,6 +1041,10 @@ DECL_HANDLER(get_next_device_request)
                 }
                 list_remove( &irp->mgr_entry );
                 list_init( &irp->mgr_entry );
+
+                if (list_empty( &manager->requests ))
+                    reset_inproc_event( manager->inproc_sync );
+
                 /* we already own the object if it's only on manager queue */
                 if (irp->file) grab_object( irp );
                 manager->current_call = irp;
-- 
GitLab


From 6ff2004368fd48e908eab15fae23dd04620e365e Mon Sep 17 00:00:00 2001
From: Elizabeth Figura <zfigura@codeweavers.com>
Date: Mon, 8 Mar 2021 17:13:20 -0600
Subject: [PATCH 16/29] server: Create in-process synchronization objects for
 keyed events.

---
 server/event.c | 23 +++++++++++++++++++++--
 1 file changed, 21 insertions(+), 2 deletions(-)

diff --git a/server/event.c b/server/event.c
index 6f1b0bec022..2b93ae2fa76 100644
--- a/server/event.c
+++ b/server/event.c
@@ -114,10 +114,13 @@ struct type_descr keyed_event_type =
 struct keyed_event
 {
     struct object  obj;             /* object header */
+    int inproc_sync;                /* in-process synchronization object */
 };
 
 static void keyed_event_dump( struct object *obj, int verbose );
 static int keyed_event_signaled( struct object *obj, struct wait_queue_entry *entry );
+static int keyed_event_get_inproc_sync( struct object *obj, enum inproc_sync_type *type );
+static void keyed_event_destroy( struct object *obj );
 
 static const struct object_ops keyed_event_ops =
 {
@@ -139,9 +142,9 @@ static const struct object_ops keyed_event_ops =
     default_unlink_name,         /* unlink_name */
     no_open_file,                /* open_file */
     no_kernel_obj_list,          /* get_kernel_obj_list */
-    no_get_inproc_sync,          /* get_inproc_sync */
+    keyed_event_get_inproc_sync, /* get_inproc_sync */
     no_close_handle,             /* close_handle */
-    no_destroy                   /* destroy */
+    keyed_event_destroy          /* destroy */
 };
 
 
@@ -270,6 +273,7 @@ struct keyed_event *create_keyed_event( struct object *root, const struct unicod
         if (get_error() != STATUS_OBJECT_NAME_EXISTS)
         {
             /* initialize it if it didn't already exist */
+            event->inproc_sync = create_inproc_event( TRUE, TRUE );
         }
     }
     return event;
@@ -313,6 +317,21 @@ static int keyed_event_signaled( struct object *obj, struct wait_queue_entry *en
     return 0;
 }
 
+static int keyed_event_get_inproc_sync( struct object *obj, enum inproc_sync_type *type )
+{
+    struct keyed_event *event = (struct keyed_event *)obj;
+
+    *type = INPROC_SYNC_MANUAL_SERVER;
+    return event->inproc_sync;
+}
+
+static void keyed_event_destroy( struct object *obj )
+{
+    struct keyed_event *event = (struct keyed_event *)obj;
+
+    if (use_inproc_sync()) close( event->inproc_sync );
+}
+
 /* create an event */
 DECL_HANDLER(create_event)
 {
-- 
GitLab


From 9be175b4ec0c0d5aa68278e63814fadd70f5204d Mon Sep 17 00:00:00 2001
From: Elizabeth Figura <zfigura@codeweavers.com>
Date: Mon, 8 Mar 2021 17:16:53 -0600
Subject: [PATCH 17/29] server: Create in-process synchronization objects for
 processes.

---
 server/process.c | 15 ++++++++++++++-
 server/process.h |  1 +
 2 files changed, 15 insertions(+), 1 deletion(-)

diff --git a/server/process.c b/server/process.c
index 68df57311c2..d9ca8e6240a 100644
--- a/server/process.c
+++ b/server/process.c
@@ -94,6 +94,7 @@ static unsigned int process_map_access( struct object *obj, unsigned int access
 static struct security_descriptor *process_get_sd( struct object *obj );
 static void process_poll_event( struct fd *fd, int event );
 static struct list *process_get_kernel_obj_list( struct object *obj );
+static int process_get_inproc_sync( struct object *obj, enum inproc_sync_type *type );
 static void process_destroy( struct object *obj );
 static void terminate_process( struct process *process, struct thread *skip, int exit_code );
 
@@ -117,7 +118,7 @@ static const struct object_ops process_ops =
     NULL,                        /* unlink_name */
     no_open_file,                /* open_file */
     process_get_kernel_obj_list, /* get_kernel_obj_list */
-    no_get_inproc_sync,          /* get_inproc_sync */
+    process_get_inproc_sync,     /* get_inproc_sync */
     no_close_handle,             /* close_handle */
     process_destroy              /* destroy */
 };
@@ -687,6 +688,7 @@ struct process *create_process( int fd, struct process *parent, unsigned int fla
     process->rawinput_device_count = 0;
     process->rawinput_mouse  = NULL;
     process->rawinput_kbd    = NULL;
+    process->inproc_sync     = create_inproc_event( TRUE, FALSE );
     memset( &process->image_info, 0, sizeof(process->image_info) );
     list_init( &process->rawinput_entry );
     list_init( &process->kernel_object );
@@ -787,6 +789,8 @@ static void process_destroy( struct object *obj )
     free( process->rawinput_devices );
     free( process->dir_cache );
     free( process->image );
+
+    if (use_inproc_sync()) close( process->inproc_sync );
 }
 
 /* dump a process on stdout for debugging purposes */
@@ -820,6 +824,14 @@ static struct list *process_get_kernel_obj_list( struct object *obj )
     return &process->kernel_object;
 }
 
+static int process_get_inproc_sync( struct object *obj, enum inproc_sync_type *type )
+{
+    struct process *process = (struct process *)obj;
+
+    *type = INPROC_SYNC_MANUAL_SERVER;
+    return process->inproc_sync;
+}
+
 static struct security_descriptor *process_get_sd( struct object *obj )
 {
     static struct security_descriptor *process_default_sd;
@@ -984,6 +996,7 @@ static void process_killed( struct process *process )
     release_job_process( process );
     start_sigkill_timer( process );
     wake_up( &process->obj, 0 );
+    set_inproc_event( process->inproc_sync );
 }
 
 /* add a thread to a process running threads list */
diff --git a/server/process.h b/server/process.h
index e49529b06fa..f1dd3a418cf 100644
--- a/server/process.h
+++ b/server/process.h
@@ -86,6 +86,7 @@ struct process
     struct list          rawinput_entry;  /* entry in the rawinput process list */
     struct list          kernel_object;   /* list of kernel object pointers */
     struct pe_image_info image_info;      /* main exe image info */
+    int                  inproc_sync;     /* in-process synchronization object */
 };
 
 /* process functions */
-- 
GitLab


From ed2d9667304915f3a9b3c43a759a4335eaf22313 Mon Sep 17 00:00:00 2001
From: Elizabeth Figura <zfigura@codeweavers.com>
Date: Mon, 8 Mar 2021 17:17:35 -0600
Subject: [PATCH 18/29] server: Create in-process synchronization objects for
 jobs.

---
 server/process.c | 16 +++++++++++++++-
 1 file changed, 15 insertions(+), 1 deletion(-)

diff --git a/server/process.c b/server/process.c
index d9ca8e6240a..a182fc7a7d9 100644
--- a/server/process.c
+++ b/server/process.c
@@ -193,6 +193,7 @@ struct type_descr job_type =
 
 static void job_dump( struct object *obj, int verbose );
 static int job_signaled( struct object *obj, struct wait_queue_entry *entry );
+static int job_get_inproc_sync( struct object *obj, enum inproc_sync_type *type );
 static int job_close_handle( struct object *obj, struct process *process, obj_handle_t handle );
 static void job_destroy( struct object *obj );
 
@@ -210,6 +211,7 @@ struct job
     struct job *parent;
     struct list parent_job_entry;  /* list entry for parent job */
     struct list child_job_list;    /* list of child jobs */
+    int inproc_sync;               /* in-process synchronization object */
 };
 
 static const struct object_ops job_ops =
@@ -232,7 +234,7 @@ static const struct object_ops job_ops =
     default_unlink_name,           /* unlink_name */
     no_open_file,                  /* open_file */
     no_kernel_obj_list,            /* get_kernel_obj_list */
-    no_get_inproc_sync,            /* get_inproc_sync */
+    job_get_inproc_sync,           /* get_inproc_sync */
     job_close_handle,              /* close_handle */
     job_destroy                    /* destroy */
 };
@@ -257,6 +259,7 @@ static struct job *create_job_object( struct object *root, const struct unicode_
             job->completion_port = NULL;
             job->completion_key = 0;
             job->parent = NULL;
+            job->inproc_sync = create_inproc_event( TRUE, FALSE );
         }
     }
     return job;
@@ -413,6 +416,15 @@ static void terminate_job( struct job *job, int exit_code )
     job->terminating = 0;
     job->signaled = 1;
     wake_up( &job->obj, 0 );
+    set_inproc_event( job->inproc_sync );
+}
+
+static int job_get_inproc_sync( struct object *obj, enum inproc_sync_type *type )
+{
+    struct job *job = (struct job *)obj;
+
+    *type = INPROC_SYNC_MANUAL_SERVER;
+    return job->inproc_sync;
 }
 
 static int job_close_handle( struct object *obj, struct process *process, obj_handle_t handle )
@@ -443,6 +455,8 @@ static void job_destroy( struct object *obj )
         list_remove( &job->parent_job_entry );
         release_object( job->parent );
     }
+
+    if (use_inproc_sync()) close( job->inproc_sync );
 }
 
 static void job_dump( struct object *obj, int verbose )
-- 
GitLab


From 46af13075e471a4ef286710d98cb0f0a97c93f11 Mon Sep 17 00:00:00 2001
From: Elizabeth Figura <zfigura@codeweavers.com>
Date: Mon, 8 Mar 2021 17:24:15 -0600
Subject: [PATCH 19/29] server: Create in-process synchronization objects for
 message queues.

---
 server/queue.c | 41 ++++++++++++++++++++++++++++++++++++++---
 1 file changed, 38 insertions(+), 3 deletions(-)

diff --git a/server/queue.c b/server/queue.c
index b8c7a48669d..489767bc709 100644
--- a/server/queue.c
+++ b/server/queue.c
@@ -133,6 +133,7 @@ struct msg_queue
     struct hook_table     *hooks;           /* hook table */
     timeout_t              last_get_msg;    /* time of last get message call */
     int                    keystate_lock;   /* owns an input keystate lock */
+    int                    inproc_sync;     /* in-process synchronization object */
     queue_shm_t           *shared;          /* queue in session shared memory */
 };
 
@@ -151,6 +152,7 @@ static int msg_queue_add_queue( struct object *obj, struct wait_queue_entry *ent
 static void msg_queue_remove_queue( struct object *obj, struct wait_queue_entry *entry );
 static int msg_queue_signaled( struct object *obj, struct wait_queue_entry *entry );
 static void msg_queue_satisfied( struct object *obj, struct wait_queue_entry *entry );
+static int msg_queue_get_inproc_sync( struct object *obj, enum inproc_sync_type *type );
 static void msg_queue_destroy( struct object *obj );
 static void msg_queue_poll_event( struct fd *fd, int event );
 static void thread_input_dump( struct object *obj, int verbose );
@@ -177,7 +179,7 @@ static const struct object_ops msg_queue_ops =
     NULL,                      /* unlink_name */
     no_open_file,              /* open_file */
     no_kernel_obj_list,        /* get_kernel_obj_list */
-    no_get_inproc_sync,        /* get_inproc_sync */
+    msg_queue_get_inproc_sync, /* get_inproc_sync */
     no_close_handle,           /* close_handle */
     msg_queue_destroy          /* destroy */
 };
@@ -315,6 +317,7 @@ static struct msg_queue *create_msg_queue( struct thread *thread, struct thread_
         queue->hooks           = NULL;
         queue->last_get_msg    = current_time;
         queue->keystate_lock   = 0;
+        queue->inproc_sync     = create_inproc_event( TRUE, FALSE );
         list_init( &queue->send_result );
         list_init( &queue->callback_result );
         list_init( &queue->pending_timers );
@@ -731,7 +734,11 @@ static inline void set_queue_bits( struct msg_queue *queue, unsigned int bits )
     }
     SHARED_WRITE_END;
 
-    if (is_signaled( queue )) wake_up( &queue->obj, 0 );
+    if (is_signaled( queue ))
+    {
+        wake_up( &queue->obj, 0 );
+        set_inproc_event( queue->inproc_sync );
+    }
 }
 
 /* clear some queue bits */
@@ -751,6 +758,8 @@ static inline void clear_queue_bits( struct msg_queue *queue, unsigned int bits
         if (queue->keystate_lock) unlock_input_keystate( queue->input );
         queue->keystate_lock = 0;
     }
+    if (!is_signaled( queue ))
+        reset_inproc_event( queue->inproc_sync );
 }
 
 /* check if message is matched by the filter */
@@ -1335,6 +1344,15 @@ static void msg_queue_satisfied( struct object *obj, struct wait_queue_entry *en
         shared->changed_mask = 0;
     }
     SHARED_WRITE_END;
+    reset_inproc_event( queue->inproc_sync );
+}
+
+static int msg_queue_get_inproc_sync( struct object *obj, enum inproc_sync_type *type )
+{
+    struct msg_queue *queue = (struct msg_queue *)obj;
+
+    *type = INPROC_SYNC_QUEUE;
+    return queue->inproc_sync;
 }
 
 static void msg_queue_destroy( struct object *obj )
@@ -1380,6 +1398,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 (use_inproc_sync()) close( queue->inproc_sync );
 }
 
 static void msg_queue_poll_event( struct fd *fd, int event )
@@ -1390,6 +1409,7 @@ static void msg_queue_poll_event( struct fd *fd, int event )
     if (event & (POLLERR | POLLHUP)) set_fd_events( fd, -1 );
     else set_fd_events( queue->fd, 0 );
     wake_up( &queue->obj, 0 );
+    set_inproc_event( queue->inproc_sync );
 }
 
 static void thread_input_dump( struct object *obj, int verbose )
@@ -3166,7 +3186,15 @@ DECL_HANDLER(set_queue_mask)
                 }
                 SHARED_WRITE_END;
             }
-            else wake_up( &queue->obj, 0 );
+            else
+            {
+                wake_up( &queue->obj, 0 );
+                set_inproc_event( queue->inproc_sync );
+            }
+        }
+        else
+        {
+            reset_inproc_event( queue->inproc_sync );
         }
     }
 }
@@ -3188,6 +3216,9 @@ DECL_HANDLER(get_queue_status)
             shared->changed_bits &= ~req->clear_bits;
         }
         SHARED_WRITE_END;
+
+        if (!is_signaled( queue ))
+            reset_inproc_event( queue->inproc_sync );
     }
     else reply->wake_bits = reply->changed_bits = 0;
 }
@@ -3385,6 +3416,9 @@ DECL_HANDLER(get_message)
     }
     SHARED_WRITE_END;
 
+    if (!is_signaled( queue ))
+        reset_inproc_event( queue->inproc_sync );
+
     /* then check for posted messages */
     if ((filter & QS_POSTMESSAGE) &&
         get_posted_message( queue, get_win, req->get_first, req->get_last, req->flags, reply ))
@@ -3444,6 +3478,7 @@ DECL_HANDLER(get_message)
     }
     SHARED_WRITE_END;
 
+    reset_inproc_event( queue->inproc_sync );
     set_error( STATUS_PENDING );  /* FIXME */
 }
 
-- 
GitLab


From edefc1d363d16e3a0c5b89d16bf45fb029793907 Mon Sep 17 00:00:00 2001
From: Elizabeth Figura <zfigura@codeweavers.com>
Date: Mon, 8 Mar 2021 17:26:35 -0600
Subject: [PATCH 20/29] server: Create in-process synchronization objects for
 threads.

---
 server/thread.c | 14 +++++++++++++-
 server/thread.h |  1 +
 2 files changed, 14 insertions(+), 1 deletion(-)

diff --git a/server/thread.c b/server/thread.c
index ce78b50593c..ab448346319 100644
--- a/server/thread.c
+++ b/server/thread.c
@@ -189,6 +189,7 @@ static int thread_signaled( struct object *obj, struct wait_queue_entry *entry )
 static unsigned int thread_map_access( struct object *obj, unsigned int access );
 static void thread_poll_event( struct fd *fd, int event );
 static struct list *thread_get_kernel_obj_list( struct object *obj );
+static int thread_get_inproc_sync( struct object *obj, enum inproc_sync_type *type );
 static void destroy_thread( struct object *obj );
 
 static const struct object_ops thread_ops =
@@ -211,7 +212,7 @@ static const struct object_ops thread_ops =
     NULL,                       /* unlink_name */
     no_open_file,               /* open_file */
     thread_get_kernel_obj_list, /* get_kernel_obj_list */
-    no_get_inproc_sync,         /* get_inproc_sync */
+    thread_get_inproc_sync,     /* get_inproc_sync */
     no_close_handle,            /* close_handle */
     destroy_thread              /* destroy */
 };
@@ -416,6 +417,7 @@ static inline void init_thread_structure( struct thread *thread )
     thread->token           = NULL;
     thread->desc            = NULL;
     thread->desc_len        = 0;
+    thread->inproc_sync     = create_inproc_event( TRUE, FALSE );
 
     thread->creation_time = current_time;
     thread->exit_time     = 0;
@@ -567,6 +569,14 @@ static struct list *thread_get_kernel_obj_list( struct object *obj )
     return &thread->kernel_object;
 }
 
+static int thread_get_inproc_sync( struct object *obj, enum inproc_sync_type *type )
+{
+    struct thread *thread = (struct thread *)obj;
+
+    *type = INPROC_SYNC_MANUAL_SERVER;
+    return thread->inproc_sync;
+}
+
 /* cleanup everything that is no longer needed by a dead thread */
 /* used by destroy_thread and kill_thread */
 static void cleanup_thread( struct thread *thread )
@@ -622,6 +632,7 @@ static void destroy_thread( struct object *obj )
     release_object( thread->process );
     if (thread->id) free_ptid( thread->id );
     if (thread->token) release_object( thread->token );
+    if (use_inproc_sync()) close( thread->inproc_sync );
 }
 
 /* dump a thread on stdout for debugging purposes */
@@ -1512,6 +1523,7 @@ void kill_thread( struct thread *thread, int violent_death )
     kill_console_processes( thread, 0 );
     abandon_mutexes( thread );
     wake_up( &thread->obj, 0 );
+    set_inproc_event( thread->inproc_sync );
     if (violent_death) send_thread_signal( thread, SIGQUIT );
     cleanup_thread( thread );
     remove_process_thread( thread->process, thread );
diff --git a/server/thread.h b/server/thread.h
index 7fdae3a629f..7c31d4e3326 100644
--- a/server/thread.h
+++ b/server/thread.h
@@ -94,6 +94,7 @@ struct thread
     data_size_t            desc_len;      /* thread description length in bytes */
     WCHAR                 *desc;          /* thread description string */
     struct completion_wait *completion_wait; /* completion port wait object the thread is associated with */
+    int                    inproc_sync;   /* in-process synchronization object */
 };
 
 extern struct thread *current;
-- 
GitLab


From 3fb13363b478481f5750b0b07f7b6c59b44202c7 Mon Sep 17 00:00:00 2001
From: Elizabeth Figura <zfigura@codeweavers.com>
Date: Mon, 8 Mar 2021 17:29:38 -0600
Subject: [PATCH 21/29] server: Create in-process synchronization objects for
 timers.

---
 server/timer.c | 35 +++++++++++++++++++++++++----------
 1 file changed, 25 insertions(+), 10 deletions(-)

diff --git a/server/timer.c b/server/timer.c
index fef447bc4aa..d763d4695a7 100644
--- a/server/timer.c
+++ b/server/timer.c
@@ -26,6 +26,7 @@
 #include <sys/time.h>
 #include <sys/types.h>
 #include <stdarg.h>
+#include <unistd.h>
 
 #include "ntstatus.h"
 #define WIN32_NO_STATUS
@@ -52,20 +53,22 @@ struct type_descr timer_type =
 
 struct timer
 {
-    struct object        obj;       /* object header */
-    int                  manual;    /* manual reset */
-    int                  signaled;  /* current signaled state */
-    unsigned int         period;    /* timer period in ms */
-    abstime_t            when;      /* next expiration */
-    struct timeout_user *timeout;   /* timeout user */
-    struct thread       *thread;    /* thread that set the APC function */
-    client_ptr_t         callback;  /* callback APC function */
-    client_ptr_t         arg;       /* callback argument */
+    struct object        obj;           /* object header */
+    int                  manual;        /* manual reset */
+    int                  signaled;      /* current signaled state */
+    unsigned int         period;        /* timer period in ms */
+    abstime_t            when;          /* next expiration */
+    struct timeout_user *timeout;       /* timeout user */
+    struct thread       *thread;        /* thread that set the APC function */
+    client_ptr_t         callback;      /* callback APC function */
+    client_ptr_t         arg;           /* callback argument */
+    int                  inproc_sync;   /* in-process synchronization object */
 };
 
 static void timer_dump( struct object *obj, int verbose );
 static int timer_signaled( struct object *obj, struct wait_queue_entry *entry );
 static void timer_satisfied( struct object *obj, struct wait_queue_entry *entry );
+static int timer_get_inproc_sync( struct object *obj, enum inproc_sync_type *type );
 static void timer_destroy( struct object *obj );
 
 static const struct object_ops timer_ops =
@@ -88,7 +91,7 @@ static const struct object_ops timer_ops =
     default_unlink_name,       /* unlink_name */
     no_open_file,              /* open_file */
     no_kernel_obj_list,        /* get_kernel_obj_list */
-    no_get_inproc_sync,        /* get_inproc_sync */
+    timer_get_inproc_sync,     /* get_inproc_sync */
     no_close_handle,           /* close_handle */
     timer_destroy              /* destroy */
 };
@@ -111,6 +114,7 @@ static struct timer *create_timer( struct object *root, const struct unicode_str
             timer->period   = 0;
             timer->timeout  = NULL;
             timer->thread   = NULL;
+            timer->inproc_sync = create_inproc_event( manual, FALSE );
         }
     }
     return timer;
@@ -152,6 +156,7 @@ static void timer_callback( void *private )
     /* wake up waiters */
     timer->signaled = 1;
     wake_up( &timer->obj, 0 );
+    set_inproc_event( timer->inproc_sync );
 }
 
 /* cancel a running timer */
@@ -182,6 +187,7 @@ static int set_timer( struct timer *timer, timeout_t expire, unsigned int period
     {
         period = 0;  /* period doesn't make any sense for a manual timer */
         timer->signaled = 0;
+        reset_inproc_event( timer->inproc_sync );
     }
     timer->when     = (expire <= 0) ? expire - monotonic_time : max( expire, current_time );
     timer->period   = period;
@@ -216,6 +222,14 @@ static void timer_satisfied( struct object *obj, struct wait_queue_entry *entry
     if (!timer->manual) timer->signaled = 0;
 }
 
+static int timer_get_inproc_sync( struct object *obj, enum inproc_sync_type *type )
+{
+    struct timer *timer = (struct timer *)obj;
+
+    *type = timer->manual ? INPROC_SYNC_MANUAL_SERVER : INPROC_SYNC_AUTO_SERVER;
+    return timer->inproc_sync;
+}
+
 static void timer_destroy( struct object *obj )
 {
     struct timer *timer = (struct timer *)obj;
@@ -223,6 +237,7 @@ static void timer_destroy( struct object *obj )
 
     if (timer->timeout) remove_timeout_user( timer->timeout );
     if (timer->thread) release_object( timer->thread );
+    if (use_inproc_sync()) close( timer->inproc_sync );
 }
 
 /* create a timer */
-- 
GitLab


From d6f35271e5b861ed71ae8f0e043f869594e8a782 Mon Sep 17 00:00:00 2001
From: Elizabeth Figura <zfigura@codeweavers.com>
Date: Mon, 8 Mar 2021 17:40:57 -0600
Subject: [PATCH 22/29] server: Create in-process synchronization objects for
 fd-based objects.

---
 server/change.c     |  2 +-
 server/device.c     |  2 +-
 server/fd.c         | 25 ++++++++++++++++++++++++-
 server/file.c       |  2 +-
 server/file.h       |  1 +
 server/mailslot.c   |  6 +++---
 server/named_pipe.c |  8 ++++----
 server/serial.c     |  2 +-
 server/sock.c       |  2 +-
 9 files changed, 37 insertions(+), 13 deletions(-)

diff --git a/server/change.c b/server/change.c
index 54fcfade398..92d321c8784 100644
--- a/server/change.c
+++ b/server/change.c
@@ -124,7 +124,7 @@ static const struct object_ops dir_ops =
     NULL,                     /* unlink_name */
     no_open_file,             /* open_file */
     no_kernel_obj_list,       /* get_kernel_obj_list */
-    no_get_inproc_sync,       /* get_inproc_sync */
+    default_fd_get_inproc_sync, /* get_inproc_sync */
     dir_close_handle,         /* close_handle */
     dir_destroy               /* destroy */
 };
diff --git a/server/device.c b/server/device.c
index e8671f18f9e..2eb38b0674b 100644
--- a/server/device.c
+++ b/server/device.c
@@ -233,7 +233,7 @@ static const struct object_ops device_file_ops =
     NULL,                             /* unlink_name */
     no_open_file,                     /* open_file */
     device_file_get_kernel_obj_list,  /* get_kernel_obj_list */
-    no_get_inproc_sync,               /* get_inproc_sync */
+    default_fd_get_inproc_sync,       /* get_inproc_sync */
     device_file_close_handle,         /* close_handle */
     device_file_destroy               /* destroy */
 };
diff --git a/server/fd.c b/server/fd.c
index 2cd1fb7be01..de234047f06 100644
--- a/server/fd.c
+++ b/server/fd.c
@@ -154,6 +154,7 @@ struct fd
     struct completion   *completion;  /* completion object attached to this fd */
     apc_param_t          comp_key;    /* completion key to set in completion events */
     unsigned int         comp_flags;  /* completion flags */
+    int                  inproc_sync; /* in-process synchronization object */
 };
 
 static void fd_dump( struct object *obj, int verbose );
@@ -1588,6 +1589,7 @@ static void fd_destroy( struct object *obj )
         if (fd->unix_fd != -1) close( fd->unix_fd );
         free( fd->unix_name );
     }
+    if (use_inproc_sync()) close( fd->inproc_sync );
 }
 
 /* check if the desired access is possible without violating */
@@ -1706,6 +1708,7 @@ static struct fd *alloc_fd_object(void)
     fd->poll_index = -1;
     fd->completion = NULL;
     fd->comp_flags = 0;
+    fd->inproc_sync = create_inproc_event( TRUE, fd->signaled );
     init_async_queue( &fd->read_q );
     init_async_queue( &fd->write_q );
     init_async_queue( &fd->wait_q );
@@ -1746,6 +1749,7 @@ struct fd *alloc_pseudo_fd( const struct fd_ops *fd_user_ops, struct object *use
     fd->poll_index = -1;
     fd->completion = NULL;
     fd->comp_flags = 0;
+    fd->inproc_sync = create_inproc_event( TRUE, fd->signaled );
     fd->no_fd_status = STATUS_BAD_DEVICE_TYPE;
     init_async_queue( &fd->read_q );
     init_async_queue( &fd->write_q );
@@ -2156,7 +2160,15 @@ void set_fd_signaled( struct fd *fd, int signaled )
 {
     if (fd->comp_flags & FILE_SKIP_SET_EVENT_ON_HANDLE) return;
     fd->signaled = signaled;
-    if (signaled) wake_up( fd->user, 0 );
+    if (signaled)
+    {
+        wake_up( fd->user, 0 );
+        set_inproc_event( fd->inproc_sync );
+    }
+    else
+    {
+        reset_inproc_event( fd->inproc_sync );
+    }
 }
 
 /* check if events are pending and if yes return which one(s) */
@@ -2182,6 +2194,17 @@ int default_fd_signaled( struct object *obj, struct wait_queue_entry *entry )
     return ret;
 }
 
+int default_fd_get_inproc_sync( struct object *obj, enum inproc_sync_type *type )
+{
+    struct fd *fd = get_obj_fd( obj );
+    int ret;
+
+    *type = INPROC_SYNC_MANUAL_SERVER;
+    ret = fd->inproc_sync;
+    release_object( fd );
+    return ret;
+}
+
 int default_fd_get_poll_events( struct fd *fd )
 {
     int events = 0;
diff --git a/server/file.c b/server/file.c
index dbbbad43707..bab203b1914 100644
--- a/server/file.c
+++ b/server/file.c
@@ -106,7 +106,7 @@ static const struct object_ops file_ops =
     NULL,                         /* unlink_name */
     file_open_file,               /* open_file */
     file_get_kernel_obj_list,     /* get_kernel_obj_list */
-    no_get_inproc_sync,           /* get_inproc_sync */
+    default_fd_get_inproc_sync,   /* get_inproc_sync */
     no_close_handle,              /* close_handle */
     file_destroy                  /* destroy */
 };
diff --git a/server/file.h b/server/file.h
index 59b73c0245c..dc01e538df8 100644
--- a/server/file.h
+++ b/server/file.h
@@ -109,6 +109,7 @@ extern char *dup_fd_name( struct fd *root, const char *name ) __WINE_DEALLOC(fre
 extern void get_nt_name( struct fd *fd, struct unicode_str *name );
 
 extern int default_fd_signaled( struct object *obj, struct wait_queue_entry *entry );
+extern int default_fd_get_inproc_sync( struct object *obj, enum inproc_sync_type *type );
 extern int default_fd_get_poll_events( struct fd *fd );
 extern void default_poll_event( struct fd *fd, int event );
 extern void fd_cancel_async( struct fd *fd, struct async *async );
diff --git a/server/mailslot.c b/server/mailslot.c
index 8de68b3dede..929ca3ecb82 100644
--- a/server/mailslot.c
+++ b/server/mailslot.c
@@ -93,7 +93,7 @@ static const struct object_ops mailslot_ops =
     default_unlink_name,       /* unlink_name */
     mailslot_open_file,        /* open_file */
     no_kernel_obj_list,        /* get_kernel_obj_list */
-    no_get_inproc_sync,        /* get_inproc_sync */
+    default_fd_get_inproc_sync,/* get_inproc_sync */
     no_close_handle,           /* close_handle */
     mailslot_destroy           /* destroy */
 };
@@ -155,7 +155,7 @@ static const struct object_ops mail_writer_ops =
     NULL,                       /* unlink_name */
     no_open_file,               /* open_file */
     no_kernel_obj_list,         /* get_kernel_obj_list */
-    no_get_inproc_sync,         /* get_inproc_sync */
+    default_fd_get_inproc_sync, /* get_inproc_sync */
     no_close_handle,            /* close_handle */
     mail_writer_destroy         /* destroy */
 };
@@ -252,7 +252,7 @@ static const struct object_ops mailslot_device_file_ops =
     NULL,                                   /* unlink_name */
     no_open_file,                           /* open_file */
     no_kernel_obj_list,                     /* get_kernel_obj_list */
-    no_get_inproc_sync,                     /* get_inproc_sync */
+    default_fd_get_inproc_sync,             /* get_inproc_sync */
     no_close_handle,                        /* close_handle */
     mailslot_device_file_destroy            /* destroy */
 };
diff --git a/server/named_pipe.c b/server/named_pipe.c
index d5b1462ebf7..2ce27560845 100644
--- a/server/named_pipe.c
+++ b/server/named_pipe.c
@@ -180,7 +180,7 @@ static const struct object_ops pipe_server_ops =
     NULL,                         /* unlink_name */
     pipe_server_open_file,        /* open_file */
     no_kernel_obj_list,           /* get_kernel_obj_list */
-    no_get_inproc_sync,           /* get_inproc_sync */
+    default_fd_get_inproc_sync,   /* get_inproc_sync */
     async_close_obj_handle,       /* close_handle */
     pipe_server_destroy           /* destroy */
 };
@@ -225,7 +225,7 @@ static const struct object_ops pipe_client_ops =
     NULL,                         /* unlink_name */
     no_open_file,                 /* open_file */
     no_kernel_obj_list,           /* get_kernel_obj_list */
-    no_get_inproc_sync,           /* get_inproc_sync */
+    default_fd_get_inproc_sync,   /* get_inproc_sync */
     async_close_obj_handle,       /* close_handle */
     pipe_end_destroy              /* destroy */
 };
@@ -305,7 +305,7 @@ static const struct object_ops named_pipe_device_file_ops =
     NULL,                                    /* unlink_name */
     no_open_file,                            /* open_file */
     no_kernel_obj_list,                      /* get_kernel_obj_list */
-    no_get_inproc_sync,                      /* get_inproc_sync */
+    default_fd_get_inproc_sync,              /* get_inproc_sync */
     no_close_handle,                         /* close_handle */
     named_pipe_device_file_destroy           /* destroy */
 };
@@ -356,7 +356,7 @@ static const struct object_ops named_pipe_dir_ops =
     NULL,                                    /* unlink_name */
     named_pipe_dir_open_file,                /* open_file */
     no_kernel_obj_list,                      /* get_kernel_obj_list */
-    no_get_inproc_sync,                      /* get_inproc_sync */
+    default_fd_get_inproc_sync,              /* get_inproc_sync */
     no_close_handle,                         /* close_handle */
     named_pipe_dir_destroy                   /* destroy */
 };
diff --git a/server/serial.c b/server/serial.c
index df242058a43..93753328d4d 100644
--- a/server/serial.c
+++ b/server/serial.c
@@ -103,7 +103,7 @@ static const struct object_ops serial_ops =
     NULL,                         /* unlink_name */
     no_open_file,                 /* open_file */
     no_kernel_obj_list,           /* get_kernel_obj_list */
-    no_get_inproc_sync,           /* get_inproc_sync */
+    default_fd_get_inproc_sync,   /* get_inproc_sync */
     no_close_handle,              /* close_handle */
     serial_destroy                /* destroy */
 };
diff --git a/server/sock.c b/server/sock.c
index 4d711c61f22..42e81e2beec 100644
--- a/server/sock.c
+++ b/server/sock.c
@@ -498,7 +498,7 @@ static const struct object_ops sock_ops =
     NULL,                         /* unlink_name */
     no_open_file,                 /* open_file */
     no_kernel_obj_list,           /* get_kernel_obj_list */
-    no_get_inproc_sync,           /* get_inproc_sync */
+    default_fd_get_inproc_sync,   /* get_inproc_sync */
     sock_close_handle,            /* close_handle */
     sock_destroy                  /* destroy */
 };
-- 
GitLab


From 513cf65742ab8740ecdcedf828dc7467a182a1fb Mon Sep 17 00:00:00 2001
From: Elizabeth Figura <zfigura@codeweavers.com>
Date: Wed, 19 Mar 2025 19:32:55 -0500
Subject: [PATCH 23/29] server: Add a request to retrieve the in-process
 synchronization object from a handle.

---
 include/wine/server_protocol.h | 18 +++++++++++++++++-
 server/inproc_sync.c           | 26 ++++++++++++++++++++++++++
 server/protocol.def            |  8 ++++++++
 server/request_handlers.h      |  7 +++++++
 server/request_trace.h         | 14 ++++++++++++++
 5 files changed, 72 insertions(+), 1 deletion(-)

diff --git a/include/wine/server_protocol.h b/include/wine/server_protocol.h
index 372b6652766..0a15e94322f 100644
--- a/include/wine/server_protocol.h
+++ b/include/wine/server_protocol.h
@@ -5927,6 +5927,19 @@ struct get_linux_sync_device_reply
 };
 
 
+struct get_linux_sync_obj_request
+{
+    struct request_header __header;
+    obj_handle_t handle;
+};
+struct get_linux_sync_obj_reply
+{
+    struct reply_header __header;
+    int type;
+    unsigned int access;
+};
+
+
 enum request
 {
     REQ_new_process,
@@ -6223,6 +6236,7 @@ enum request
     REQ_get_next_thread,
     REQ_set_keyboard_repeat,
     REQ_get_linux_sync_device,
+    REQ_get_linux_sync_obj,
     REQ_NB_REQUESTS
 };
 
@@ -6524,6 +6538,7 @@ union generic_request
     struct get_next_thread_request get_next_thread_request;
     struct set_keyboard_repeat_request set_keyboard_repeat_request;
     struct get_linux_sync_device_request get_linux_sync_device_request;
+    struct get_linux_sync_obj_request get_linux_sync_obj_request;
 };
 union generic_reply
 {
@@ -6823,8 +6838,9 @@ union generic_reply
     struct get_next_thread_reply get_next_thread_reply;
     struct set_keyboard_repeat_reply set_keyboard_repeat_reply;
     struct get_linux_sync_device_reply get_linux_sync_device_reply;
+    struct get_linux_sync_obj_reply get_linux_sync_obj_reply;
 };
 
-#define SERVER_PROTOCOL_VERSION 878
+#define SERVER_PROTOCOL_VERSION 879
 
 #endif /* __WINE_WINE_SERVER_PROTOCOL_H */
diff --git a/server/inproc_sync.c b/server/inproc_sync.c
index 6471364c4de..c6f79081b6f 100644
--- a/server/inproc_sync.c
+++ b/server/inproc_sync.c
@@ -172,3 +172,29 @@ DECL_HANDLER(get_linux_sync_device)
     set_error( STATUS_NOT_IMPLEMENTED );
 #endif
 }
+
+DECL_HANDLER(get_linux_sync_obj)
+{
+#ifdef NTSYNC_IOC_EVENT_READ
+    struct object *obj;
+    int fd;
+
+    if ((obj = get_handle_obj( current->process, req->handle, 0, NULL )))
+    {
+        enum inproc_sync_type type;
+
+        if ((fd = obj->ops->get_inproc_sync( obj, &type )) >= 0)
+        {
+            reply->type = type;
+            reply->access = get_handle_access( current->process, req->handle );
+            send_client_fd( current->process, fd, req->handle );
+        }
+        else
+            set_error( STATUS_NOT_IMPLEMENTED );
+
+        release_object( obj );
+    }
+#else
+    set_error( STATUS_NOT_IMPLEMENTED );
+#endif
+}
diff --git a/server/protocol.def b/server/protocol.def
index ce2e5cfb01d..faebb0c5632 100644
--- a/server/protocol.def
+++ b/server/protocol.def
@@ -4111,3 +4111,11 @@ enum inproc_sync_type
 /* Obtain a fd for the ntsync device */
 @REQ(get_linux_sync_device)
 @END
+
+/* Get the in-process synchronization fd associated with the waitable handle */
+@REQ(get_linux_sync_obj)
+    obj_handle_t handle;        /* handle to the object */
+@REPLY
+    int type;                   /* object type */
+    unsigned int access;        /* handle access rights */
+@END
diff --git a/server/request_handlers.h b/server/request_handlers.h
index 9011ff3d4f0..c047cdcb435 100644
--- a/server/request_handlers.h
+++ b/server/request_handlers.h
@@ -301,6 +301,7 @@ DECL_HANDLER(get_next_process);
 DECL_HANDLER(get_next_thread);
 DECL_HANDLER(set_keyboard_repeat);
 DECL_HANDLER(get_linux_sync_device);
+DECL_HANDLER(get_linux_sync_obj);
 
 typedef void (*req_handler)( const void *req, void *reply );
 static const req_handler req_handlers[REQ_NB_REQUESTS] =
@@ -599,6 +600,7 @@ static const req_handler req_handlers[REQ_NB_REQUESTS] =
     (req_handler)req_get_next_thread,
     (req_handler)req_set_keyboard_repeat,
     (req_handler)req_get_linux_sync_device,
+    (req_handler)req_get_linux_sync_obj,
 };
 
 C_ASSERT( sizeof(abstime_t) == 8 );
@@ -2284,3 +2286,8 @@ C_ASSERT( sizeof(struct set_keyboard_repeat_request) == 24 );
 C_ASSERT( offsetof(struct set_keyboard_repeat_reply, enable) == 8 );
 C_ASSERT( sizeof(struct set_keyboard_repeat_reply) == 16 );
 C_ASSERT( sizeof(struct get_linux_sync_device_request) == 16 );
+C_ASSERT( offsetof(struct get_linux_sync_obj_request, handle) == 12 );
+C_ASSERT( sizeof(struct get_linux_sync_obj_request) == 16 );
+C_ASSERT( offsetof(struct get_linux_sync_obj_reply, type) == 8 );
+C_ASSERT( offsetof(struct get_linux_sync_obj_reply, access) == 12 );
+C_ASSERT( sizeof(struct get_linux_sync_obj_reply) == 16 );
diff --git a/server/request_trace.h b/server/request_trace.h
index 23ae334c85a..ba854fae5b1 100644
--- a/server/request_trace.h
+++ b/server/request_trace.h
@@ -3363,6 +3363,17 @@ static void dump_get_linux_sync_device_request( const struct get_linux_sync_devi
 {
 }
 
+static void dump_get_linux_sync_obj_request( const struct get_linux_sync_obj_request *req )
+{
+    fprintf( stderr, " handle=%04x", req->handle );
+}
+
+static void dump_get_linux_sync_obj_reply( const struct get_linux_sync_obj_reply *req )
+{
+    fprintf( stderr, " type=%d", req->type );
+    fprintf( stderr, ", access=%08x", req->access );
+}
+
 typedef void (*dump_func)( const void *req );
 
 static const dump_func req_dumpers[REQ_NB_REQUESTS] =
@@ -3661,6 +3672,7 @@ static const dump_func req_dumpers[REQ_NB_REQUESTS] =
     (dump_func)dump_get_next_thread_request,
     (dump_func)dump_set_keyboard_repeat_request,
     (dump_func)dump_get_linux_sync_device_request,
+    (dump_func)dump_get_linux_sync_obj_request,
 };
 
 static const dump_func reply_dumpers[REQ_NB_REQUESTS] =
@@ -3959,6 +3971,7 @@ static const dump_func reply_dumpers[REQ_NB_REQUESTS] =
     (dump_func)dump_get_next_thread_reply,
     (dump_func)dump_set_keyboard_repeat_reply,
     NULL,
+    (dump_func)dump_get_linux_sync_obj_reply,
 };
 
 static const char * const req_names[REQ_NB_REQUESTS] =
@@ -4257,6 +4270,7 @@ static const char * const req_names[REQ_NB_REQUESTS] =
     "get_next_thread",
     "set_keyboard_repeat",
     "get_linux_sync_device",
+    "get_linux_sync_obj",
 };
 
 static const struct
-- 
GitLab


From daaef037c1ca1ace4b4979bb8814aaf609ee0a94 Mon Sep 17 00:00:00 2001
From: Elizabeth Figura <zfigura@codeweavers.com>
Date: Tue, 9 Mar 2021 11:32:25 -0600
Subject: [PATCH 24/29] server: Introduce select_inproc_queue and
 unselect_inproc_queue requests.

---
 include/wine/server_protocol.h | 32 +++++++++++++++++++++++-
 server/protocol.def            | 11 +++++++++
 server/queue.c                 | 45 ++++++++++++++++++++++++++++++++++
 server/request_handlers.h      |  7 ++++++
 server/request_trace.h         | 15 ++++++++++++
 5 files changed, 109 insertions(+), 1 deletion(-)

diff --git a/include/wine/server_protocol.h b/include/wine/server_protocol.h
index 0a15e94322f..9919589dcfb 100644
--- a/include/wine/server_protocol.h
+++ b/include/wine/server_protocol.h
@@ -5940,6 +5940,30 @@ struct get_linux_sync_obj_reply
 };
 
 
+
+struct select_inproc_queue_request
+{
+    struct request_header __header;
+    char __pad_12[4];
+};
+struct select_inproc_queue_reply
+{
+    struct reply_header __header;
+};
+
+
+
+struct unselect_inproc_queue_request
+{
+    struct request_header __header;
+    int          signaled;
+};
+struct unselect_inproc_queue_reply
+{
+    struct reply_header __header;
+};
+
+
 enum request
 {
     REQ_new_process,
@@ -6237,6 +6261,8 @@ enum request
     REQ_set_keyboard_repeat,
     REQ_get_linux_sync_device,
     REQ_get_linux_sync_obj,
+    REQ_select_inproc_queue,
+    REQ_unselect_inproc_queue,
     REQ_NB_REQUESTS
 };
 
@@ -6539,6 +6565,8 @@ union generic_request
     struct set_keyboard_repeat_request set_keyboard_repeat_request;
     struct get_linux_sync_device_request get_linux_sync_device_request;
     struct get_linux_sync_obj_request get_linux_sync_obj_request;
+    struct select_inproc_queue_request select_inproc_queue_request;
+    struct unselect_inproc_queue_request unselect_inproc_queue_request;
 };
 union generic_reply
 {
@@ -6839,8 +6867,10 @@ union generic_reply
     struct set_keyboard_repeat_reply set_keyboard_repeat_reply;
     struct get_linux_sync_device_reply get_linux_sync_device_reply;
     struct get_linux_sync_obj_reply get_linux_sync_obj_reply;
+    struct select_inproc_queue_reply select_inproc_queue_reply;
+    struct unselect_inproc_queue_reply unselect_inproc_queue_reply;
 };
 
-#define SERVER_PROTOCOL_VERSION 879
+#define SERVER_PROTOCOL_VERSION 880
 
 #endif /* __WINE_WINE_SERVER_PROTOCOL_H */
diff --git a/server/protocol.def b/server/protocol.def
index faebb0c5632..b6ea0951b64 100644
--- a/server/protocol.def
+++ b/server/protocol.def
@@ -4119,3 +4119,14 @@ enum inproc_sync_type
     int type;                   /* object type */
     unsigned int access;        /* handle access rights */
 @END
+
+
+/* Begin a client-side wait on a message queue */
+@REQ(select_inproc_queue)
+@END
+
+
+/* End a client-side wait on a message queue */
+@REQ(unselect_inproc_queue)
+    int          signaled;        /* was the queue signaled? */
+@END
diff --git a/server/queue.c b/server/queue.c
index 489767bc709..457a32d78a6 100644
--- a/server/queue.c
+++ b/server/queue.c
@@ -134,6 +134,7 @@ struct msg_queue
     timeout_t              last_get_msg;    /* time of last get message call */
     int                    keystate_lock;   /* owns an input keystate lock */
     int                    inproc_sync;     /* in-process synchronization object */
+    int                    in_inproc_wait;  /* are we in a client-side wait? */
     queue_shm_t           *shared;          /* queue in session shared memory */
 };
 
@@ -318,6 +319,7 @@ static struct msg_queue *create_msg_queue( struct thread *thread, struct thread_
         queue->last_get_msg    = current_time;
         queue->keystate_lock   = 0;
         queue->inproc_sync     = create_inproc_event( TRUE, FALSE );
+        queue->in_inproc_wait  = 0;
         list_init( &queue->send_result );
         list_init( &queue->callback_result );
         list_init( &queue->pending_timers );
@@ -1279,6 +1281,10 @@ static int is_queue_hung( struct msg_queue *queue )
         if (get_wait_queue_thread(entry)->queue == queue)
             return 0;  /* thread is waiting on queue -> not hung */
     }
+
+    if (queue->in_inproc_wait)
+        return 0;  /* thread is waiting on queue in absentia -> not hung */
+
     return 1;
 }
 
@@ -4272,3 +4278,42 @@ DECL_HANDLER(set_keyboard_repeat)
 
     release_object( desktop );
 }
+
+DECL_HANDLER(select_inproc_queue)
+{
+    struct msg_queue *queue = current->queue;
+
+    if (queue->in_inproc_wait)
+    {
+        set_error( STATUS_ACCESS_DENIED );
+    }
+    else
+    {
+        check_thread_queue_idle( current );
+
+        if (queue->fd)
+            set_fd_events( queue->fd, POLLIN );
+
+        queue->in_inproc_wait = 1;
+    }
+}
+
+DECL_HANDLER(unselect_inproc_queue)
+{
+    struct msg_queue *queue = current->queue;
+
+    if (!queue->in_inproc_wait)
+    {
+        set_error( STATUS_ACCESS_DENIED );
+    }
+    else
+    {
+        if (queue->fd)
+            set_fd_events( queue->fd, 0 );
+
+        if (req->signaled)
+            msg_queue_satisfied( &queue->obj, NULL );
+
+        queue->in_inproc_wait = 0;
+    }
+}
diff --git a/server/request_handlers.h b/server/request_handlers.h
index c047cdcb435..cd9e52aeffe 100644
--- a/server/request_handlers.h
+++ b/server/request_handlers.h
@@ -302,6 +302,8 @@ DECL_HANDLER(get_next_thread);
 DECL_HANDLER(set_keyboard_repeat);
 DECL_HANDLER(get_linux_sync_device);
 DECL_HANDLER(get_linux_sync_obj);
+DECL_HANDLER(select_inproc_queue);
+DECL_HANDLER(unselect_inproc_queue);
 
 typedef void (*req_handler)( const void *req, void *reply );
 static const req_handler req_handlers[REQ_NB_REQUESTS] =
@@ -601,6 +603,8 @@ static const req_handler req_handlers[REQ_NB_REQUESTS] =
     (req_handler)req_set_keyboard_repeat,
     (req_handler)req_get_linux_sync_device,
     (req_handler)req_get_linux_sync_obj,
+    (req_handler)req_select_inproc_queue,
+    (req_handler)req_unselect_inproc_queue,
 };
 
 C_ASSERT( sizeof(abstime_t) == 8 );
@@ -2291,3 +2295,6 @@ C_ASSERT( sizeof(struct get_linux_sync_obj_request) == 16 );
 C_ASSERT( offsetof(struct get_linux_sync_obj_reply, type) == 8 );
 C_ASSERT( offsetof(struct get_linux_sync_obj_reply, access) == 12 );
 C_ASSERT( sizeof(struct get_linux_sync_obj_reply) == 16 );
+C_ASSERT( sizeof(struct select_inproc_queue_request) == 16 );
+C_ASSERT( offsetof(struct unselect_inproc_queue_request, signaled) == 12 );
+C_ASSERT( sizeof(struct unselect_inproc_queue_request) == 16 );
diff --git a/server/request_trace.h b/server/request_trace.h
index ba854fae5b1..870df471c1d 100644
--- a/server/request_trace.h
+++ b/server/request_trace.h
@@ -3374,6 +3374,15 @@ static void dump_get_linux_sync_obj_reply( const struct get_linux_sync_obj_reply
     fprintf( stderr, ", access=%08x", req->access );
 }
 
+static void dump_select_inproc_queue_request( const struct select_inproc_queue_request *req )
+{
+}
+
+static void dump_unselect_inproc_queue_request( const struct unselect_inproc_queue_request *req )
+{
+    fprintf( stderr, " signaled=%d", req->signaled );
+}
+
 typedef void (*dump_func)( const void *req );
 
 static const dump_func req_dumpers[REQ_NB_REQUESTS] =
@@ -3673,6 +3682,8 @@ static const dump_func req_dumpers[REQ_NB_REQUESTS] =
     (dump_func)dump_set_keyboard_repeat_request,
     (dump_func)dump_get_linux_sync_device_request,
     (dump_func)dump_get_linux_sync_obj_request,
+    (dump_func)dump_select_inproc_queue_request,
+    (dump_func)dump_unselect_inproc_queue_request,
 };
 
 static const dump_func reply_dumpers[REQ_NB_REQUESTS] =
@@ -3972,6 +3983,8 @@ static const dump_func reply_dumpers[REQ_NB_REQUESTS] =
     (dump_func)dump_set_keyboard_repeat_reply,
     NULL,
     (dump_func)dump_get_linux_sync_obj_reply,
+    NULL,
+    NULL,
 };
 
 static const char * const req_names[REQ_NB_REQUESTS] =
@@ -4271,6 +4284,8 @@ static const char * const req_names[REQ_NB_REQUESTS] =
     "set_keyboard_repeat",
     "get_linux_sync_device",
     "get_linux_sync_obj",
+    "select_inproc_queue",
+    "unselect_inproc_queue",
 };
 
 static const struct
-- 
GitLab


From cfa47ddb276388754344517fd902dc2cdeed5325 Mon Sep 17 00:00:00 2001
From: Elizabeth Figura <zfigura@codeweavers.com>
Date: Thu, 21 Apr 2022 16:11:14 -0500
Subject: [PATCH 25/29] server: Allow creating an event object for client-side
 user APC signaling.

---
 include/wine/server_protocol.h | 19 ++++++++++++++++++-
 server/protocol.def            |  7 +++++++
 server/request_handlers.h      |  5 +++++
 server/request_trace.h         | 12 ++++++++++++
 server/thread.c                | 21 +++++++++++++++++++++
 server/thread.h                |  1 +
 6 files changed, 64 insertions(+), 1 deletion(-)

diff --git a/include/wine/server_protocol.h b/include/wine/server_protocol.h
index 9919589dcfb..45b8d41d474 100644
--- a/include/wine/server_protocol.h
+++ b/include/wine/server_protocol.h
@@ -5964,6 +5964,20 @@ struct unselect_inproc_queue_reply
 };
 
 
+
+struct get_inproc_alert_event_request
+{
+    struct request_header __header;
+    char __pad_12[4];
+};
+struct get_inproc_alert_event_reply
+{
+    struct reply_header __header;
+    obj_handle_t handle;
+    char __pad_12[4];
+};
+
+
 enum request
 {
     REQ_new_process,
@@ -6263,6 +6277,7 @@ enum request
     REQ_get_linux_sync_obj,
     REQ_select_inproc_queue,
     REQ_unselect_inproc_queue,
+    REQ_get_inproc_alert_event,
     REQ_NB_REQUESTS
 };
 
@@ -6567,6 +6582,7 @@ union generic_request
     struct get_linux_sync_obj_request get_linux_sync_obj_request;
     struct select_inproc_queue_request select_inproc_queue_request;
     struct unselect_inproc_queue_request unselect_inproc_queue_request;
+    struct get_inproc_alert_event_request get_inproc_alert_event_request;
 };
 union generic_reply
 {
@@ -6869,8 +6885,9 @@ union generic_reply
     struct get_linux_sync_obj_reply get_linux_sync_obj_reply;
     struct select_inproc_queue_reply select_inproc_queue_reply;
     struct unselect_inproc_queue_reply unselect_inproc_queue_reply;
+    struct get_inproc_alert_event_reply get_inproc_alert_event_reply;
 };
 
-#define SERVER_PROTOCOL_VERSION 880
+#define SERVER_PROTOCOL_VERSION 881
 
 #endif /* __WINE_WINE_SERVER_PROTOCOL_H */
diff --git a/server/protocol.def b/server/protocol.def
index b6ea0951b64..b098ee6c49b 100644
--- a/server/protocol.def
+++ b/server/protocol.def
@@ -4130,3 +4130,10 @@ enum inproc_sync_type
 @REQ(unselect_inproc_queue)
     int          signaled;        /* was the queue signaled? */
 @END
+
+
+/* Get an event handle to be used for thread alerts with in-process synchronization */
+@REQ(get_inproc_alert_event)
+@REPLY
+    obj_handle_t handle;          /* handle to the event */
+@END
diff --git a/server/request_handlers.h b/server/request_handlers.h
index cd9e52aeffe..a5f48e0e968 100644
--- a/server/request_handlers.h
+++ b/server/request_handlers.h
@@ -304,6 +304,7 @@ DECL_HANDLER(get_linux_sync_device);
 DECL_HANDLER(get_linux_sync_obj);
 DECL_HANDLER(select_inproc_queue);
 DECL_HANDLER(unselect_inproc_queue);
+DECL_HANDLER(get_inproc_alert_event);
 
 typedef void (*req_handler)( const void *req, void *reply );
 static const req_handler req_handlers[REQ_NB_REQUESTS] =
@@ -605,6 +606,7 @@ static const req_handler req_handlers[REQ_NB_REQUESTS] =
     (req_handler)req_get_linux_sync_obj,
     (req_handler)req_select_inproc_queue,
     (req_handler)req_unselect_inproc_queue,
+    (req_handler)req_get_inproc_alert_event,
 };
 
 C_ASSERT( sizeof(abstime_t) == 8 );
@@ -2298,3 +2300,6 @@ C_ASSERT( sizeof(struct get_linux_sync_obj_reply) == 16 );
 C_ASSERT( sizeof(struct select_inproc_queue_request) == 16 );
 C_ASSERT( offsetof(struct unselect_inproc_queue_request, signaled) == 12 );
 C_ASSERT( sizeof(struct unselect_inproc_queue_request) == 16 );
+C_ASSERT( sizeof(struct get_inproc_alert_event_request) == 16 );
+C_ASSERT( offsetof(struct get_inproc_alert_event_reply, handle) == 8 );
+C_ASSERT( sizeof(struct get_inproc_alert_event_reply) == 16 );
diff --git a/server/request_trace.h b/server/request_trace.h
index 870df471c1d..dbe5bd8abc5 100644
--- a/server/request_trace.h
+++ b/server/request_trace.h
@@ -3383,6 +3383,15 @@ static void dump_unselect_inproc_queue_request( const struct unselect_inproc_que
     fprintf( stderr, " signaled=%d", req->signaled );
 }
 
+static void dump_get_inproc_alert_event_request( const struct get_inproc_alert_event_request *req )
+{
+}
+
+static void dump_get_inproc_alert_event_reply( const struct get_inproc_alert_event_reply *req )
+{
+    fprintf( stderr, " handle=%04x", req->handle );
+}
+
 typedef void (*dump_func)( const void *req );
 
 static const dump_func req_dumpers[REQ_NB_REQUESTS] =
@@ -3684,6 +3693,7 @@ static const dump_func req_dumpers[REQ_NB_REQUESTS] =
     (dump_func)dump_get_linux_sync_obj_request,
     (dump_func)dump_select_inproc_queue_request,
     (dump_func)dump_unselect_inproc_queue_request,
+    (dump_func)dump_get_inproc_alert_event_request,
 };
 
 static const dump_func reply_dumpers[REQ_NB_REQUESTS] =
@@ -3985,6 +3995,7 @@ static const dump_func reply_dumpers[REQ_NB_REQUESTS] =
     (dump_func)dump_get_linux_sync_obj_reply,
     NULL,
     NULL,
+    (dump_func)dump_get_inproc_alert_event_reply,
 };
 
 static const char * const req_names[REQ_NB_REQUESTS] =
@@ -4286,6 +4297,7 @@ static const char * const req_names[REQ_NB_REQUESTS] =
     "get_linux_sync_obj",
     "select_inproc_queue",
     "unselect_inproc_queue",
+    "get_inproc_alert_event",
 };
 
 static const struct
diff --git a/server/thread.c b/server/thread.c
index ab448346319..843cca458a8 100644
--- a/server/thread.c
+++ b/server/thread.c
@@ -418,6 +418,7 @@ static inline void init_thread_structure( struct thread *thread )
     thread->desc            = NULL;
     thread->desc_len        = 0;
     thread->inproc_sync     = create_inproc_event( TRUE, FALSE );
+    thread->inproc_alert_event = NULL;
 
     thread->creation_time = current_time;
     thread->exit_time     = 0;
@@ -633,6 +634,7 @@ static void destroy_thread( struct object *obj )
     if (thread->id) free_ptid( thread->id );
     if (thread->token) release_object( thread->token );
     if (use_inproc_sync()) close( thread->inproc_sync );
+    if (thread->inproc_alert_event) release_object( thread->inproc_alert_event );
 }
 
 /* dump a thread on stdout for debugging purposes */
@@ -1380,8 +1382,13 @@ static int queue_apc( struct process *process, struct thread *thread, struct thr
     grab_object( apc );
     list_add_tail( queue, &apc->entry );
     if (!list_prev( queue, &apc->entry ))  /* first one */
+    {
         wake_thread( thread );
 
+        if (apc->call.type == APC_USER && thread->inproc_alert_event)
+            set_event( thread->inproc_alert_event );
+    }
+
     return 1;
 }
 
@@ -1412,6 +1419,8 @@ void thread_cancel_apc( struct thread *thread, struct object *owner, enum apc_ty
         apc->executed = 1;
         wake_up( &apc->obj, 0 );
         release_object( apc );
+        if (list_empty( &thread->user_apc ) && thread->inproc_alert_event)
+            reset_event( thread->inproc_alert_event );
         return;
     }
 }
@@ -1426,6 +1435,9 @@ static struct thread_apc *thread_dequeue_apc( struct thread *thread, int system
     {
         apc = LIST_ENTRY( ptr, struct thread_apc, entry );
         list_remove( ptr );
+
+        if (list_empty( &thread->user_apc ) && thread->inproc_alert_event)
+            reset_event( thread->inproc_alert_event );
     }
     return apc;
 }
@@ -2278,3 +2290,12 @@ DECL_HANDLER(get_next_thread)
     set_error( STATUS_NO_MORE_ENTRIES );
     release_object( process );
 }
+
+DECL_HANDLER(get_inproc_alert_event)
+{
+    if (!current->inproc_alert_event)
+        current->inproc_alert_event = create_event( NULL, NULL, 0, 1, !list_empty( &current->user_apc ), NULL );
+
+    if (current->inproc_alert_event)
+        reply->handle = alloc_handle( current->process, current->inproc_alert_event, SYNCHRONIZE, 0 );
+}
diff --git a/server/thread.h b/server/thread.h
index 7c31d4e3326..e7a63a1a6ce 100644
--- a/server/thread.h
+++ b/server/thread.h
@@ -95,6 +95,7 @@ struct thread
     WCHAR                 *desc;          /* thread description string */
     struct completion_wait *completion_wait; /* completion port wait object the thread is associated with */
     int                    inproc_sync;   /* in-process synchronization object */
+    struct event          *inproc_alert_event; /* in-process synchronization alert event */
 };
 
 extern struct thread *current;
-- 
GitLab


From ef9d1305ea16d6b1558e7f6caff2988afc0e4be7 Mon Sep 17 00:00:00 2001
From: Elizabeth Figura <zfigura@codeweavers.com>
Date: Mon, 8 Mar 2021 18:07:23 -0600
Subject: [PATCH 26/29] ntdll: Introduce a helper to wait on an internal server
 handle.

---
 dlls/ntdll/unix/file.c         |  2 +-
 dlls/ntdll/unix/process.c      |  2 +-
 dlls/ntdll/unix/server.c       | 17 ++++++++++++++++-
 dlls/ntdll/unix/sync.c         |  4 ++--
 dlls/ntdll/unix/thread.c       |  2 +-
 dlls/ntdll/unix/unix_private.h |  3 ++-
 6 files changed, 23 insertions(+), 7 deletions(-)

diff --git a/dlls/ntdll/unix/file.c b/dlls/ntdll/unix/file.c
index a475fcc3225..7a8bda82cf6 100644
--- a/dlls/ntdll/unix/file.c
+++ b/dlls/ntdll/unix/file.c
@@ -6539,7 +6539,7 @@ NTSTATUS WINAPI NtLockFile( HANDLE file, HANDLE event, PIO_APC_ROUTINE apc, void
         }
         if (handle)
         {
-            NtWaitForSingleObject( handle, FALSE, NULL );
+            server_wait_for_object( handle, FALSE, NULL );
             NtClose( handle );
         }
         else  /* Unix lock conflict, sleep a bit and retry */
diff --git a/dlls/ntdll/unix/process.c b/dlls/ntdll/unix/process.c
index 4d5d23c47bb..ea1b3d79d60 100644
--- a/dlls/ntdll/unix/process.c
+++ b/dlls/ntdll/unix/process.c
@@ -920,7 +920,7 @@ NTSTATUS WINAPI NtCreateUserProcess( HANDLE *process_handle_ptr, HANDLE *thread_
 
     /* wait for the new process info to be ready */
 
-    NtWaitForSingleObject( process_info, FALSE, NULL );
+    server_wait_for_object( process_info, FALSE, NULL );
     SERVER_START_REQ( get_new_process_info )
     {
         req->info = wine_server_obj_handle( process_info );
diff --git a/dlls/ntdll/unix/server.c b/dlls/ntdll/unix/server.c
index 4d1d19e166e..932e993c055 100644
--- a/dlls/ntdll/unix/server.c
+++ b/dlls/ntdll/unix/server.c
@@ -789,6 +789,21 @@ unsigned int server_wait( const union select_op *select_op, data_size_t size, UI
 }
 
 
+/* helper function to perform a server-side wait on an internal handle without
+ * using the fast synchronization path */
+unsigned int server_wait_for_object( HANDLE handle, BOOL alertable, const LARGE_INTEGER *timeout )
+{
+    union select_op select_op;
+    UINT flags = SELECT_INTERRUPTIBLE;
+
+    if (alertable) flags |= SELECT_ALERTABLE;
+
+    select_op.wait.op = SELECT_WAIT;
+    select_op.wait.handles[0] = wine_server_obj_handle( handle );
+    return server_wait( &select_op, offsetof( union select_op, wait.handles[1] ), flags, timeout );
+}
+
+
 /***********************************************************************
  *              NtContinue  (NTDLL.@)
  */
@@ -865,7 +880,7 @@ unsigned int server_queue_process_apc( HANDLE process, const union apc_call *cal
         }
         else
         {
-            NtWaitForSingleObject( handle, FALSE, NULL );
+            server_wait_for_object( handle, FALSE, NULL );
 
             SERVER_START_REQ( get_apc_result )
             {
diff --git a/dlls/ntdll/unix/sync.c b/dlls/ntdll/unix/sync.c
index ee88c14957e..f8f43a3d1b9 100644
--- a/dlls/ntdll/unix/sync.c
+++ b/dlls/ntdll/unix/sync.c
@@ -2317,7 +2317,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;
 
@@ -2381,7 +2381,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 1dd7faecb14..1bae9ab61b4 100644
--- a/dlls/ntdll/unix/thread.c
+++ b/dlls/ntdll/unix/thread.c
@@ -1801,7 +1801,7 @@ NTSTATUS get_thread_context( HANDLE handle, void *context, BOOL *self, USHORT ma
 
     if (ret == STATUS_PENDING)
     {
-        NtWaitForSingleObject( context_handle, FALSE, NULL );
+        server_wait_for_object( context_handle, FALSE, NULL );
 
         SERVER_START_REQ( get_thread_context )
         {
diff --git a/dlls/ntdll/unix/unix_private.h b/dlls/ntdll/unix/unix_private.h
index 5cf1877e36e..ca62cfec74f 100644
--- a/dlls/ntdll/unix/unix_private.h
+++ b/dlls/ntdll/unix/unix_private.h
@@ -215,6 +215,7 @@ 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,
@@ -454,7 +455,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)
-- 
GitLab


From ce6205215a7dc51ab62ced7a0b9d24063144a1cc Mon Sep 17 00:00:00 2001
From: Elizabeth Figura <zfigura@codeweavers.com>
Date: Tue, 6 Apr 2021 15:37:02 -0500
Subject: [PATCH 27/29] ntdll: Use in-process synchronization objects.

---
 dlls/ntdll/unix/sync.c         | 575 ++++++++++++++++++++++++++++++++-
 dlls/ntdll/unix/unix_private.h |   2 +
 dlls/ntdll/unix/virtual.c      |   1 +
 server/inproc_sync.c           |   2 +-
 4 files changed, 568 insertions(+), 12 deletions(-)

diff --git a/dlls/ntdll/unix/sync.c b/dlls/ntdll/unix/sync.c
index f8f43a3d1b9..47dfd4c04ae 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>
@@ -344,66 +347,616 @@ static int get_linux_sync_device(void)
     return device;
 }
 
+/* It's possible for synchronization primitives to remain alive even after being
+ * closed, because a thread is still waiting on them. It's rare in practice, and
+ * documented as being undefined behaviour by Microsoft, but it works, and some
+ * applications rely on it. This means we need to refcount handles, and defer
+ * deleting them on the server side until the refcount reaches zero. We do this
+ * by having each client process hold a handle to the in-process synchronization
+ * object, as well as a private refcount. When the client refcount reaches zero,
+ * it closes the handle; when all handles are closed, the server deletes the
+ * in-process synchronization object.
+ *
+ * We want lookup of objects from the cache to be very fast; ideally, it should
+ * be lock-free. We achieve this by using atomic modifications to "refcount",
+ * and guaranteeing that all other fields are valid and correct *as long as*
+ * refcount is nonzero, and we store the entire structure in memory which will
+ * never be freed.
+ *
+ * This means that acquiring the object can't use a simple atomic increment; it
+ * has to use a compare-and-swap loop to ensure that it doesn't try to increment
+ * an object with a zero refcount. That's still leagues better than a real lock,
+ * though, and release can be a single atomic decrement.
+ *
+ * It also means that threads modifying the cache need to take a lock, to
+ * prevent other threads from writing to it concurrently.
+ *
+ * It's possible for an object currently in use (by a waiter) to be closed and
+ * the same handle immediately reallocated to a different object. This should be
+ * a very rare situation, and in that case we simply don't cache the handle.
+ */
+struct inproc_sync_cache_entry
+{
+    LONG refcount;
+    int fd;
+    unsigned int access;
+    unsigned short type; /* enum inproc_sync_type, stored as short to save space */
+    unsigned short closed;
+};
+
+
+static void release_inproc_sync_obj( struct inproc_sync_cache_entry *cache )
+{
+    /* save the fd now; as soon as the refcount hits 0 we cannot
+     * access the cache anymore */
+    int fd = cache->fd;
+    LONG refcount = InterlockedDecrement( &cache->refcount );
+
+    assert( refcount >= 0 );
+
+    if (!refcount)
+        close( fd );
+}
+
+
+static BOOL inproc_sync_types_match( enum inproc_sync_type a, enum inproc_sync_type b )
+{
+    if (a == b) return TRUE;
+    if (a == INPROC_SYNC_AUTO_EVENT && b == INPROC_SYNC_MANUAL_EVENT) return TRUE;
+    if (b == INPROC_SYNC_AUTO_EVENT && a == INPROC_SYNC_MANUAL_EVENT) return TRUE;
+    return FALSE;
+}
+
+
+/* returns a pointer to a cache entry; if the object could not be cached,
+ * returns "stack_cache" instead, which should be allocated on stack */
+static NTSTATUS get_inproc_sync_obj( HANDLE handle, enum inproc_sync_type desired_type,
+                                     ACCESS_MASK desired_access,
+                                     struct inproc_sync_cache_entry *stack_cache,
+                                     struct inproc_sync_cache_entry **ret_cache )
+{
+    struct inproc_sync_cache_entry *cache = stack_cache;
+    sigset_t sigset;
+    NTSTATUS ret;
+
+    /* We don't need the device right now, but if we can't access it, that
+     * means ntsync isn't available. Fail fast in that case. */
+    if (get_linux_sync_device() < 0)
+        return STATUS_NOT_IMPLEMENTED;
+
+    *ret_cache = stack_cache;
+
+    /* 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( get_linux_sync_obj )
+    {
+        req->handle = wine_server_obj_handle( handle );
+        if (!(ret = wine_server_call( req )))
+        {
+            obj_handle_t fd_handle;
+            cache->fd = wine_server_receive_fd( &fd_handle );
+            assert( wine_server_ptr_handle(fd_handle) == handle );
+            cache->access = reply->access;
+            cache->type = reply->type;
+            cache->refcount = 1;
+            cache->closed = FALSE;
+        }
+    }
+    SERVER_END_REQ;
+
+    server_leave_uninterrupted_section( &fd_cache_mutex, &sigset );
+
+    if (ret) return ret;
+
+    if (desired_type && !inproc_sync_types_match( cache->type, desired_type ))
+    {
+        release_inproc_sync_obj( cache );
+        return STATUS_OBJECT_TYPE_MISMATCH;
+    }
+
+    if ((cache->access & desired_access) != desired_access)
+    {
+        release_inproc_sync_obj( cache );
+        return STATUS_ACCESS_DENIED;
+    }
+
+    return STATUS_SUCCESS;
+}
+
+
+static NTSTATUS linux_release_semaphore_obj( int obj, ULONG count, ULONG *prev_count )
+{
+    NTSTATUS ret;
+
+    ret = ioctl( obj, NTSYNC_IOC_SEM_RELEASE, &count );
+    if (ret < 0)
+    {
+        if (errno == EOVERFLOW)
+            return STATUS_SEMAPHORE_LIMIT_EXCEEDED;
+        else
+            return errno_to_status( errno );
+    }
+    if (prev_count) *prev_count = count;
+    return STATUS_SUCCESS;
+}
+
+
 static NTSTATUS inproc_release_semaphore( HANDLE handle, ULONG count, ULONG *prev_count )
 {
-    return STATUS_NOT_IMPLEMENTED;
+    struct inproc_sync_cache_entry stack_cache, *cache;
+    NTSTATUS ret;
+
+    if ((ret = get_inproc_sync_obj( handle, INPROC_SYNC_SEMAPHORE,
+                                    SEMAPHORE_MODIFY_STATE, &stack_cache, &cache )))
+        return ret;
+
+    ret = linux_release_semaphore_obj( cache->fd, count, prev_count );
+
+    release_inproc_sync_obj( cache );
+    return ret;
 }
 
+
+static NTSTATUS linux_query_semaphore_obj( int obj, SEMAPHORE_BASIC_INFORMATION *info )
+{
+    struct ntsync_sem_args args = {0};
+    NTSTATUS ret;
+
+    ret = ioctl( obj, NTSYNC_IOC_SEM_READ, &args );
+    if (ret < 0)
+        return errno_to_status( errno );
+    info->CurrentCount = args.count;
+    info->MaximumCount = args.max;
+    return STATUS_SUCCESS;
+}
+
+
 static NTSTATUS inproc_query_semaphore( HANDLE handle, SEMAPHORE_BASIC_INFORMATION *info )
 {
-    return STATUS_NOT_IMPLEMENTED;
+    struct inproc_sync_cache_entry stack_cache, *cache;
+    NTSTATUS ret;
+
+    if ((ret = get_inproc_sync_obj( handle, INPROC_SYNC_SEMAPHORE,
+                                    SEMAPHORE_QUERY_STATE, &stack_cache, &cache )))
+        return ret;
+
+    ret = linux_query_semaphore_obj( cache->fd, info );
+
+    release_inproc_sync_obj( cache );
+    return ret;
+}
+
+
+static NTSTATUS linux_set_event_obj( int obj, LONG *prev_state )
+{
+    NTSTATUS ret;
+    __u32 prev;
+
+    ret = ioctl( obj, NTSYNC_IOC_EVENT_SET, &prev );
+    if (ret < 0)
+        return errno_to_status( errno );
+    if (prev_state) *prev_state = prev;
+    return STATUS_SUCCESS;
 }
 
+
 static NTSTATUS inproc_set_event( HANDLE handle, LONG *prev_state )
 {
-    return STATUS_NOT_IMPLEMENTED;
+    struct inproc_sync_cache_entry stack_cache, *cache;
+    NTSTATUS ret;
+
+    if ((ret = get_inproc_sync_obj( handle, INPROC_SYNC_AUTO_EVENT,
+                                    EVENT_MODIFY_STATE, &stack_cache, &cache )))
+        return ret;
+
+    ret = linux_set_event_obj( cache->fd, prev_state );
+
+    release_inproc_sync_obj( cache );
+    return ret;
 }
 
+
+static NTSTATUS linux_reset_event_obj( int obj, LONG *prev_state )
+{
+    NTSTATUS ret;
+    __u32 prev;
+
+    ret = ioctl( obj, NTSYNC_IOC_EVENT_RESET, &prev );
+    if (ret < 0)
+        return errno_to_status( errno );
+    if (prev_state) *prev_state = prev;
+    return STATUS_SUCCESS;
+}
+
+
 static NTSTATUS inproc_reset_event( HANDLE handle, LONG *prev_state )
 {
-    return STATUS_NOT_IMPLEMENTED;
+    struct inproc_sync_cache_entry stack_cache, *cache;
+    NTSTATUS ret;
+
+    if ((ret = get_inproc_sync_obj( handle, INPROC_SYNC_AUTO_EVENT,
+                                    EVENT_MODIFY_STATE, &stack_cache, &cache )))
+        return ret;
+
+    ret = linux_reset_event_obj( cache->fd, prev_state );
+
+    release_inproc_sync_obj( cache );
+    return ret;
+}
+
+
+static NTSTATUS linux_pulse_event_obj( int obj, LONG *prev_state )
+{
+    NTSTATUS ret;
+    __u32 prev;
+
+    ret = ioctl( obj, NTSYNC_IOC_EVENT_PULSE, &prev );
+    if (ret < 0)
+        return errno_to_status( errno );
+    if (prev_state) *prev_state = prev;
+    return STATUS_SUCCESS;
 }
 
+
 static NTSTATUS inproc_pulse_event( HANDLE handle, LONG *prev_state )
 {
-    return STATUS_NOT_IMPLEMENTED;
+    struct inproc_sync_cache_entry stack_cache, *cache;
+    NTSTATUS ret;
+
+    if ((ret = get_inproc_sync_obj( handle, INPROC_SYNC_AUTO_EVENT,
+                                    EVENT_MODIFY_STATE, &stack_cache, &cache )))
+        return ret;
+
+    ret = linux_pulse_event_obj( cache->fd, prev_state );
+
+    release_inproc_sync_obj( cache );
+    return ret;
 }
 
+
+static NTSTATUS linux_query_event_obj( int obj, enum inproc_sync_type type, EVENT_BASIC_INFORMATION *info )
+{
+    struct ntsync_event_args args = {0};
+    NTSTATUS ret;
+
+    ret = ioctl( obj, NTSYNC_IOC_EVENT_READ, &args );
+    if (ret < 0)
+        return errno_to_status( errno );
+    info->EventType = (type == INPROC_SYNC_AUTO_EVENT) ? SynchronizationEvent : NotificationEvent;
+    info->EventState = args.signaled;
+    return STATUS_SUCCESS;
+}
+
+
 static NTSTATUS inproc_query_event( HANDLE handle, EVENT_BASIC_INFORMATION *info )
 {
-    return STATUS_NOT_IMPLEMENTED;
+    struct inproc_sync_cache_entry stack_cache, *cache;
+    NTSTATUS ret;
+
+    if ((ret = get_inproc_sync_obj( handle, INPROC_SYNC_AUTO_EVENT,
+                                    EVENT_QUERY_STATE, &stack_cache, &cache )))
+        return ret;
+
+    ret = linux_query_event_obj( cache->fd, cache->type, info );
+
+    release_inproc_sync_obj( cache );
+    return ret;
 }
 
+
+static NTSTATUS linux_release_mutex_obj( int obj, LONG *prev_count )
+{
+    struct ntsync_mutex_args args = {0};
+    NTSTATUS ret;
+
+    args.owner = GetCurrentThreadId();
+    ret = ioctl( obj, NTSYNC_IOC_MUTEX_UNLOCK, &args );
+
+    if (ret < 0)
+    {
+        if (errno == EOVERFLOW)
+            return STATUS_MUTANT_LIMIT_EXCEEDED;
+        else if (errno == EPERM)
+            return STATUS_MUTANT_NOT_OWNED;
+        else
+            return errno_to_status( errno );
+    }
+    if (prev_count) *prev_count = 1 - args.count;
+    return STATUS_SUCCESS;
+}
+
+
 static NTSTATUS inproc_release_mutex( HANDLE handle, LONG *prev_count )
 {
-    return STATUS_NOT_IMPLEMENTED;
+    struct inproc_sync_cache_entry stack_cache, *cache;
+    NTSTATUS ret;
+
+    if ((ret = get_inproc_sync_obj( handle, INPROC_SYNC_MUTEX, 0, &stack_cache, &cache )))
+        return ret;
+
+    ret = linux_release_mutex_obj( cache->fd, prev_count );
+
+    release_inproc_sync_obj( cache );
+    return ret;
 }
 
+
+static NTSTATUS linux_query_mutex_obj( int obj, MUTANT_BASIC_INFORMATION *info )
+{
+    struct ntsync_mutex_args args = {0};
+    NTSTATUS ret;
+
+    ret = ioctl( obj, NTSYNC_IOC_MUTEX_READ, &args );
+
+    if (ret < 0)
+    {
+        if (errno == EOWNERDEAD)
+        {
+            info->AbandonedState = TRUE;
+            info->OwnedByCaller = FALSE;
+            info->CurrentCount = 1;
+            return STATUS_SUCCESS;
+        }
+        else
+            return errno_to_status( errno );
+    }
+    info->AbandonedState = FALSE;
+    info->OwnedByCaller = (args.owner == GetCurrentThreadId());
+    info->CurrentCount = 1 - args.count;
+    return STATUS_SUCCESS;
+}
+
+
 static NTSTATUS inproc_query_mutex( HANDLE handle, MUTANT_BASIC_INFORMATION *info )
 {
-    return STATUS_NOT_IMPLEMENTED;
+    struct inproc_sync_cache_entry stack_cache, *cache;
+    NTSTATUS ret;
+
+    if ((ret = get_inproc_sync_obj( handle, INPROC_SYNC_MUTEX, MUTANT_QUERY_STATE,
+                                    &stack_cache, &cache )))
+        return ret;
+
+    ret = linux_query_mutex_obj( cache->fd, info );
+
+    release_inproc_sync_obj( cache );
+    return ret;
+}
+
+static void select_queue(void)
+{
+    SERVER_START_REQ( select_inproc_queue )
+    {
+        wine_server_call( req );
+    }
+    SERVER_END_REQ;
+}
+
+static void unselect_queue( BOOL signaled )
+{
+    SERVER_START_REQ( unselect_inproc_queue )
+    {
+        req->signaled = signaled;
+        wine_server_call( req );
+    }
+    SERVER_END_REQ;
+}
+
+static int get_inproc_alert_obj(void)
+{
+    struct ntdll_thread_data *data = ntdll_get_thread_data();
+    struct inproc_sync_cache_entry stack_cache, *cache;
+    HANDLE alert_handle;
+    unsigned int ret;
+
+    if (data->linux_alert_obj == -1)
+    {
+        SERVER_START_REQ( get_inproc_alert_event )
+        {
+            if ((ret = wine_server_call( req )))
+                ERR( "failed to get inproc alert event, status %#x\n", ret );
+            alert_handle = wine_server_ptr_handle( reply->handle );
+        }
+        SERVER_END_REQ;
+
+        if ((ret = get_inproc_sync_obj( alert_handle, 0, SYNCHRONIZE, &stack_cache, &cache )))
+            ERR( "failed to get inproc alert obj, status %#x\n", ret );
+        data->linux_alert_obj = cache->fd;
+        /* Set the fd to -1 so release_inproc_sync_obj() won't close it.
+         * Manhandling the cache entry here is fine since we're the only thread
+         * that can access our own alert event. */
+        cache->fd = -1;
+        release_inproc_sync_obj( cache );
+        NtClose( alert_handle );
+    }
+
+    return data->linux_alert_obj;
+}
+
+static NTSTATUS linux_wait_objs( int device, const DWORD count, const int *objs,
+                                 BOOLEAN wait_any, BOOLEAN alertable, const LARGE_INTEGER *timeout )
+{
+    struct ntsync_wait_args args = {0};
+    unsigned long request;
+    struct timespec now;
+    int ret;
+
+    if (!timeout || timeout->QuadPart == TIMEOUT_INFINITE)
+    {
+        args.timeout = ~(__u64)0;
+    }
+    else if (timeout->QuadPart <= 0)
+    {
+        clock_gettime( CLOCK_MONOTONIC, &now );
+        args.timeout = (now.tv_sec * NSECPERSEC) + now.tv_nsec + (-timeout->QuadPart * 100);
+    }
+    else
+    {
+        args.timeout = (timeout->QuadPart * 100) - (SECS_1601_TO_1970 * NSECPERSEC);
+        args.flags |= NTSYNC_WAIT_REALTIME;
+    }
+
+    args.objs = (uintptr_t)objs;
+    args.count = count;
+    args.owner = GetCurrentThreadId();
+    args.index = ~0u;
+
+    if (alertable)
+        args.alert = get_inproc_alert_obj();
+
+    if (wait_any || count == 1)
+        request = NTSYNC_IOC_WAIT_ANY;
+    else
+        request = NTSYNC_IOC_WAIT_ALL;
+
+    do
+    {
+        ret = ioctl( device, request, &args );
+    } while (ret < 0 && errno == EINTR);
+
+    if (!ret)
+    {
+        if (args.index == count)
+        {
+            static const LARGE_INTEGER timeout;
+
+            ret = server_wait( NULL, 0, SELECT_INTERRUPTIBLE | SELECT_ALERTABLE, &timeout );
+            assert( ret == STATUS_USER_APC );
+            return ret;
+        }
+
+        return wait_any ? args.index : 0;
+    }
+    else if (errno == EOWNERDEAD)
+        return STATUS_ABANDONED + (wait_any ? args.index : 0);
+    else if (errno == ETIMEDOUT)
+        return STATUS_TIMEOUT;
+    else
+        return errno_to_status( errno );
 }
 
 static NTSTATUS inproc_wait( DWORD count, const HANDLE *handles, BOOLEAN wait_any,
                              BOOLEAN alertable, const LARGE_INTEGER *timeout )
 {
-    int device;
+    struct inproc_sync_cache_entry stack_cache[64], *cache[64];
+    int device, objs[64];
+    HANDLE queue = NULL;
+    NTSTATUS ret;
+    DWORD i, j;
 
     if ((device = get_linux_sync_device()) < 0)
         return STATUS_NOT_IMPLEMENTED;
 
-    return STATUS_NOT_IMPLEMENTED;
+    for (i = 0; i < count; ++i)
+    {
+        if ((ret = get_inproc_sync_obj( handles[i], 0, SYNCHRONIZE, &stack_cache[i], &cache[i] )))
+        {
+            for (j = 0; j < i; ++j)
+                release_inproc_sync_obj( cache[j] );
+            return ret;
+        }
+        if (cache[i]->type == INPROC_SYNC_QUEUE)
+            queue = handles[i];
+
+        objs[i] = cache[i]->fd;
+    }
+
+    if (queue) select_queue();
+
+    ret = linux_wait_objs( device, count, objs, wait_any, alertable, timeout );
+
+    if (queue) unselect_queue( handles[ret] == queue );
+
+    for (i = 0; i < count; ++i)
+        release_inproc_sync_obj( cache[i] );
+
+    return ret;
 }
 
 static NTSTATUS inproc_signal_and_wait( HANDLE signal, HANDLE wait,
                                         BOOLEAN alertable, const LARGE_INTEGER *timeout )
 {
+    struct inproc_sync_cache_entry signal_stack_cache, *signal_cache;
+    struct inproc_sync_cache_entry wait_stack_cache, *wait_cache;
+    HANDLE queue = NULL;
+    NTSTATUS ret;
     int device;
 
     if ((device = get_linux_sync_device()) < 0)
         return STATUS_NOT_IMPLEMENTED;
 
-    return STATUS_NOT_IMPLEMENTED;
+    if ((ret = get_inproc_sync_obj( signal, 0, 0, &signal_stack_cache, &signal_cache )))
+        return ret;
+
+    switch (signal_cache->type)
+    {
+        case INPROC_SYNC_SEMAPHORE:
+            if (!(signal_cache->access & SEMAPHORE_MODIFY_STATE))
+            {
+                release_inproc_sync_obj( signal_cache );
+                return STATUS_ACCESS_DENIED;
+            }
+            break;
+
+        case INPROC_SYNC_AUTO_EVENT:
+        case INPROC_SYNC_MANUAL_EVENT:
+            if (!(signal_cache->access & EVENT_MODIFY_STATE))
+            {
+                release_inproc_sync_obj( signal_cache );
+                return STATUS_ACCESS_DENIED;
+            }
+            break;
+
+        case INPROC_SYNC_MUTEX:
+            break;
+
+        default:
+            /* can't be signaled */
+            release_inproc_sync_obj( signal_cache );
+            return STATUS_OBJECT_TYPE_MISMATCH;
+    }
+
+    if ((ret = get_inproc_sync_obj( wait, 0, SYNCHRONIZE, &wait_stack_cache, &wait_cache )))
+    {
+        release_inproc_sync_obj( signal_cache );
+        return ret;
+    }
+
+    if (wait_cache->type == INPROC_SYNC_QUEUE)
+        queue = wait;
+
+    switch (signal_cache->type)
+    {
+        case INPROC_SYNC_SEMAPHORE:
+            ret = linux_release_semaphore_obj( signal_cache->fd, 1, NULL );
+            break;
+
+        case INPROC_SYNC_AUTO_EVENT:
+        case INPROC_SYNC_MANUAL_EVENT:
+            ret = linux_set_event_obj( signal_cache->fd, NULL );
+            break;
+
+        case INPROC_SYNC_MUTEX:
+            ret = linux_release_mutex_obj( signal_cache->fd, NULL );
+            break;
+
+        default:
+            assert( 0 );
+            break;
+    }
+
+    if (!ret)
+    {
+        if (queue) select_queue();
+        ret = linux_wait_objs( device, 1, &wait_cache->fd, TRUE, alertable, timeout );
+        if (queue) unselect_queue( !ret );
+    }
+
+    release_inproc_sync_obj( signal_cache );
+    release_inproc_sync_obj( wait_cache );
+    return ret;
 }
 
 #else
diff --git a/dlls/ntdll/unix/unix_private.h b/dlls/ntdll/unix/unix_private.h
index ca62cfec74f..a5ca6ef99eb 100644
--- a/dlls/ntdll/unix/unix_private.h
+++ b/dlls/ntdll/unix/unix_private.h
@@ -112,6 +112,7 @@ struct ntdll_thread_data
     PRTL_THREAD_START_ROUTINE start;         /* thread entry point */
     void                     *param;         /* thread entry point parameter */
     void                     *jmp_buf;       /* setjmp buffer for exception handling */
+    int                       linux_alert_obj; /* fd for the linux in-process alert event */
 };
 
 C_ASSERT( sizeof(struct ntdll_thread_data) <= sizeof(((TEB *)0)->GdiTebBatch) );
@@ -390,6 +391,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 )
diff --git a/dlls/ntdll/unix/virtual.c b/dlls/ntdll/unix/virtual.c
index 2838472e7ba..b4d14b1beb9 100644
--- a/dlls/ntdll/unix/virtual.c
+++ b/dlls/ntdll/unix/virtual.c
@@ -3837,6 +3837,7 @@ static TEB *init_teb( void *ptr, BOOL is_wow )
     thread_data->reply_fd   = -1;
     thread_data->wait_fd[0] = -1;
     thread_data->wait_fd[1] = -1;
+    thread_data->linux_alert_obj = -1;
     list_add_head( &teb_list, &thread_data->entry );
     return teb;
 }
diff --git a/server/inproc_sync.c b/server/inproc_sync.c
index c6f79081b6f..f3c6a37fc8c 100644
--- a/server/inproc_sync.c
+++ b/server/inproc_sync.c
@@ -56,7 +56,7 @@ static int get_linux_device(void)
 
 int use_inproc_sync(void)
 {
-    return 0;
+    return get_linux_device() >= 0;
 }
 
 int create_inproc_event( int manual_reset, int signaled )
-- 
GitLab


From db543380ed223127d1707855998cc613a6e59b91 Mon Sep 17 00:00:00 2001
From: Elizabeth Figura <zfigura@codeweavers.com>
Date: Tue, 20 Apr 2021 17:55:59 -0500
Subject: [PATCH 28/29] ntdll: Use server_wait_for_object() when waiting on
 only the queue object.

---
 dlls/ntdll/unix/sync.c | 11 +++++++++++
 1 file changed, 11 insertions(+)

diff --git a/dlls/ntdll/unix/sync.c b/dlls/ntdll/unix/sync.c
index 47dfd4c04ae..c256af8a10a 100644
--- a/dlls/ntdll/unix/sync.c
+++ b/dlls/ntdll/unix/sync.c
@@ -863,6 +863,17 @@ static NTSTATUS inproc_wait( DWORD count, const HANDLE *handles, BOOLEAN wait_an
         objs[i] = cache[i]->fd;
     }
 
+    /* It's common to wait on the message queue alone. Some applications wait
+     * on it in fast paths, with a zero timeout. Since we take two server calls
+     * instead of one when going through inproc_wait(), and since we only need
+     * to go through that path if we're waiting on other objects, just delegate
+     * to the server if we're only waiting on the message queue. */
+    if (count == 1 && queue)
+    {
+        release_inproc_sync_obj( cache[0] );
+        return server_wait_for_object( handles[0], alertable, timeout );
+    }
+
     if (queue) select_queue();
 
     ret = linux_wait_objs( device, count, objs, wait_any, alertable, timeout );
-- 
GitLab


From c3a88d527aa7827eec88b8c623cf3c6ea9c039cd Mon Sep 17 00:00:00 2001
From: Elizabeth Figura <zfigura@codeweavers.com>
Date: Fri, 12 Mar 2021 15:04:17 -0600
Subject: [PATCH 29/29] ntdll: Cache in-process synchronization objects.

---
 dlls/ntdll/unix/server.c       |   9 ++
 dlls/ntdll/unix/sync.c         | 190 +++++++++++++++++++++++++++++++--
 dlls/ntdll/unix/unix_private.h |   2 +
 3 files changed, 192 insertions(+), 9 deletions(-)

diff --git a/dlls/ntdll/unix/server.c b/dlls/ntdll/unix/server.c
index 932e993c055..efb24f1cece 100644
--- a/dlls/ntdll/unix/server.c
+++ b/dlls/ntdll/unix/server.c
@@ -1808,12 +1808,17 @@ NTSTATUS WINAPI NtDuplicateObject( HANDLE source_process, HANDLE source, HANDLE
         return result.dup_handle.status;
     }
 
+    /* hold fd_cache_mutex to prevent the fd from being added again between the
+     * call to remove_fd_from_cache and close_handle */
     server_enter_uninterrupted_section( &fd_cache_mutex, &sigset );
 
     /* always remove the cached fd; if the server request fails we'll just
      * retrieve it again */
     if (options & DUPLICATE_CLOSE_SOURCE)
+    {
         fd = remove_fd_from_cache( source );
+        close_inproc_sync_obj( source );
+    }
 
     SERVER_START_REQ( dup_handle )
     {
@@ -1879,12 +1884,16 @@ NTSTATUS WINAPI NtClose( HANDLE handle )
     if (HandleToLong( handle ) >= ~5 && HandleToLong( handle ) <= ~0)
         return STATUS_SUCCESS;
 
+    /* hold fd_cache_mutex to prevent the fd from being added again between the
+     * call to remove_fd_from_cache and close_handle */
     server_enter_uninterrupted_section( &fd_cache_mutex, &sigset );
 
     /* always remove the cached fd; if the server request fails we'll just
      * retrieve it again */
     fd = remove_fd_from_cache( handle );
 
+    close_inproc_sync_obj( handle );
+
     SERVER_START_REQ( close_handle )
     {
         req->handle = wine_server_obj_handle( handle );
diff --git a/dlls/ntdll/unix/sync.c b/dlls/ntdll/unix/sync.c
index c256af8a10a..9502280b460 100644
--- a/dlls/ntdll/unix/sync.c
+++ b/dlls/ntdll/unix/sync.c
@@ -357,6 +357,12 @@ static int get_linux_sync_device(void)
  * 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*
@@ -399,6 +405,117 @@ static void release_inproc_sync_obj( struct inproc_sync_cache_entry *cache )
 }
 
 
+#define INPROC_SYNC_CACHE_BLOCK_SIZE  (65536 / sizeof(struct inproc_sync_cache_entry))
+#define INPROC_SYNC_CACHE_ENTRIES     128
+
+static struct inproc_sync_cache_entry *inproc_sync_cache[INPROC_SYNC_CACHE_ENTRIES];
+static struct inproc_sync_cache_entry inproc_sync_cache_initial_block[INPROC_SYNC_CACHE_BLOCK_SIZE];
+
+static inline unsigned int inproc_sync_handle_to_index( HANDLE handle, unsigned int *entry )
+{
+    unsigned int idx = (wine_server_obj_handle(handle) >> 2) - 1;
+    *entry = idx / INPROC_SYNC_CACHE_BLOCK_SIZE;
+    return idx % INPROC_SYNC_CACHE_BLOCK_SIZE;
+}
+
+
+static struct inproc_sync_cache_entry *cache_inproc_sync_obj( HANDLE handle, obj_handle_t inproc_sync, int fd,
+                                                              enum inproc_sync_type type, unsigned int access )
+{
+    unsigned int entry, idx = inproc_sync_handle_to_index( handle, &entry );
+    struct inproc_sync_cache_entry *cache;
+    int refcount;
+
+    /* don't cache pseudo-handles; waiting on them is pointless anyway */
+    if ((ULONG)(ULONG_PTR)handle > 0xfffffffa)
+        return FALSE;
+
+    if (entry >= INPROC_SYNC_CACHE_ENTRIES)
+    {
+        FIXME( "too many allocated handles, not caching %p\n", handle );
+        return NULL;
+    }
+
+    if (!inproc_sync_cache[entry])  /* do we need to allocate a new block of entries? */
+    {
+        if (!entry) inproc_sync_cache[0] = inproc_sync_cache_initial_block;
+        else
+        {
+            static const size_t size = INPROC_SYNC_CACHE_BLOCK_SIZE * sizeof(struct inproc_sync_cache_entry);
+            void *ptr = anon_mmap_alloc( size, PROT_READ | PROT_WRITE );
+            if (ptr == MAP_FAILED) return NULL;
+            if (InterlockedCompareExchangePointer( (void **)&inproc_sync_cache[entry], ptr, NULL ))
+                munmap( ptr, size ); /* someone beat us to it */
+        }
+    }
+
+    cache = &inproc_sync_cache[entry][idx];
+
+    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 NULL;
+    }
+
+    cache->fd = fd;
+    cache->type = type;
+    cache->access = access;
+    cache->closed = FALSE;
+    /* Make sure we set the other members before the refcount; this store needs
+     * release semantics [paired with the load in get_cached_inproc_sync_obj()].
+     * Set the refcount to 2 (one for the handle, one for the caller). */
+    refcount = InterlockedExchange( &cache->refcount, 2 );
+    assert( !refcount );
+
+    return cache;
+}
+
+
+/* returns the previous value */
+static inline LONG interlocked_inc_if_nonzero( LONG *dest )
+{
+    LONG val, tmp;
+    for (val = *dest;; val = tmp)
+    {
+        if (!val || (tmp = InterlockedCompareExchange( dest, val + 1, val )) == val)
+            break;
+    }
+    return val;
+}
+
+
+static struct inproc_sync_cache_entry *get_cached_inproc_sync_obj( HANDLE handle )
+{
+    unsigned int entry, idx = inproc_sync_handle_to_index( handle, &entry );
+    struct inproc_sync_cache_entry *cache;
+
+    if (entry >= INPROC_SYNC_CACHE_ENTRIES || !inproc_sync_cache[entry])
+        return NULL;
+
+    cache = &inproc_sync_cache[entry][idx];
+
+    /* this load needs acquire semantics [paired with the store in
+     * cache_inproc_sync_obj()] */
+    if (!interlocked_inc_if_nonzero( &cache->refcount ))
+        return NULL;
+
+    if (cache->closed)
+    {
+        /* The object is still being used, but "handle" has been closed. The
+         * handle value might have been reused for another object in the
+         * meantime, in which case we have to report that valid object, so
+         * force the caller to check the server. */
+        release_inproc_sync_obj( cache );
+        return NULL;
+    }
+
+    return cache;
+}
+
+
 static BOOL inproc_sync_types_match( enum inproc_sync_type a, enum inproc_sync_type b )
 {
     if (a == b) return TRUE;
@@ -415,41 +532,77 @@ static NTSTATUS get_inproc_sync_obj( HANDLE handle, enum inproc_sync_type desire
                                      struct inproc_sync_cache_entry *stack_cache,
                                      struct inproc_sync_cache_entry **ret_cache )
 {
-    struct inproc_sync_cache_entry *cache = stack_cache;
+    struct inproc_sync_cache_entry *cache;
+    obj_handle_t inproc_sync_handle;
+    enum inproc_sync_type type;
+    unsigned int access;
     sigset_t sigset;
     NTSTATUS ret;
+    int fd;
 
     /* We don't need the device right now, but if we can't access it, that
      * means ntsync isn't available. Fail fast in that case. */
     if (get_linux_sync_device() < 0)
         return STATUS_NOT_IMPLEMENTED;
 
-    *ret_cache = stack_cache;
+    /* try to find it in the cache already */
+    if ((cache = get_cached_inproc_sync_obj( handle )))
+    {
+        *ret_cache = cache;
+        return STATUS_SUCCESS;
+    }
 
     /* 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. */
+     * 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_obj() and close_handle. */
     server_enter_uninterrupted_section( &fd_cache_mutex, &sigset );
 
+    if ((cache = get_cached_inproc_sync_obj( handle )))
+    {
+        server_leave_uninterrupted_section( &fd_cache_mutex, &sigset );
+        *ret_cache = cache;
+        return STATUS_SUCCESS;
+    }
+
+    /* try to retrieve it from the server */
     SERVER_START_REQ( get_linux_sync_obj )
     {
         req->handle = wine_server_obj_handle( handle );
         if (!(ret = wine_server_call( req )))
         {
             obj_handle_t fd_handle;
-            cache->fd = wine_server_receive_fd( &fd_handle );
+            fd = wine_server_receive_fd( &fd_handle );
             assert( wine_server_ptr_handle(fd_handle) == handle );
-            cache->access = reply->access;
-            cache->type = reply->type;
-            cache->refcount = 1;
-            cache->closed = FALSE;
+            access = reply->access;
+            type = reply->type;
         }
     }
     SERVER_END_REQ;
 
+    if (ret)
+    {
+        server_leave_uninterrupted_section( &fd_cache_mutex, &sigset );
+        return ret;
+    }
+
+    cache = cache_inproc_sync_obj( handle, inproc_sync_handle, fd, type, access );
+
     server_leave_uninterrupted_section( &fd_cache_mutex, &sigset );
 
-    if (ret) return ret;
+    if (!cache)
+    {
+        cache = stack_cache;
+        cache->fd = fd;
+        cache->type = type;
+        cache->access = access;
+        cache->closed = FALSE;
+        cache->refcount = 1;
+    }
+
+    *ret_cache = cache;
 
     if (desired_type && !inproc_sync_types_match( cache->type, desired_type ))
     {
@@ -467,6 +620,21 @@ static NTSTATUS get_inproc_sync_obj( HANDLE handle, enum inproc_sync_type desire
 }
 
 
+/* caller must hold fd_cache_mutex */
+void close_inproc_sync_obj( HANDLE handle )
+{
+    struct inproc_sync_cache_entry *cache = get_cached_inproc_sync_obj( handle );
+
+    if (cache)
+    {
+        cache->closed = TRUE;
+        /* once for the reference we just grabbed, and once for the handle */
+        release_inproc_sync_obj( cache );
+        release_inproc_sync_obj( cache );
+    }
+}
+
+
 static NTSTATUS linux_release_semaphore_obj( int obj, ULONG count, ULONG *prev_count )
 {
     NTSTATUS ret;
@@ -972,6 +1140,10 @@ static NTSTATUS inproc_signal_and_wait( HANDLE signal, HANDLE wait,
 
 #else
 
+void close_inproc_sync_obj( HANDLE handle )
+{
+}
+
 static NTSTATUS inproc_release_semaphore( HANDLE handle, ULONG count, ULONG *prev_count )
 {
     return STATUS_NOT_IMPLEMENTED;
diff --git a/dlls/ntdll/unix/unix_private.h b/dlls/ntdll/unix/unix_private.h
index a5ca6ef99eb..250493cc3e9 100644
--- a/dlls/ntdll/unix/unix_private.h
+++ b/dlls/ntdll/unix/unix_private.h
@@ -383,6 +383,8 @@ extern NTSTATUS wow64_wine_spawnvp( void *args );
 
 extern void dbg_init(void);
 
+extern void close_inproc_sync_obj( HANDLE handle );
+
 extern NTSTATUS call_user_apc_dispatcher( CONTEXT *context_ptr, ULONG_PTR arg1, ULONG_PTR arg2, ULONG_PTR arg3,
                                           PNTAPCFUNC func, NTSTATUS status );
 extern NTSTATUS call_user_exception_dispatcher( EXCEPTION_RECORD *rec, CONTEXT *context );
-- 
GitLab

openSUSE Build Service is sponsored by