From f92fff66a2462c90c89a09b08c1920faa01f6592 Mon Sep 17 00:00:00 2001
From: Mike McCormack <mike@codeweavers.com>
Date: Sun, 24 Apr 2005 17:35:52 +0000
Subject: [PATCH] Implemented NtSignalAndWaitForSingleObject.

---
 dlls/ntdll/ntdll_misc.h        |  2 +-
 dlls/ntdll/signal_i386.c       |  2 +-
 dlls/ntdll/sync.c              | 33 ++++++++++--------
 include/wine/server_protocol.h |  3 +-
 include/winternl.h             |  2 +-
 server/atom.c                  |  3 +-
 server/change.c                |  1 +
 server/console.c               |  3 ++
 server/debugger.c              |  2 ++
 server/event.c                 | 16 +++++++++
 server/fd.c                    |  3 ++
 server/file.c                  |  1 +
 server/handle.c                | 11 ++++++
 server/handle.h                |  1 +
 server/hook.c                  |  1 +
 server/mailslot.c              |  2 ++
 server/mapping.c               |  1 +
 server/mutex.c                 | 21 +++++++++++
 server/named_pipe.c            |  3 ++
 server/object.c                |  6 ++++
 server/object.h                |  3 ++
 server/process.c               |  2 ++
 server/protocol.def            |  1 +
 server/queue.c                 |  2 ++
 server/registry.c              |  1 +
 server/request.c               |  1 +
 server/semaphore.c             | 64 +++++++++++++++++++++-------------
 server/serial.c                |  1 +
 server/signal.c                |  1 +
 server/snapshot.c              |  1 +
 server/sock.c                  |  1 +
 server/thread.c                | 32 +++++++++++++++--
 server/timer.c                 |  1 +
 server/token.c                 |  3 +-
 server/trace.c                 |  1 +
 35 files changed, 186 insertions(+), 46 deletions(-)

diff --git a/dlls/ntdll/ntdll_misc.h b/dlls/ntdll/ntdll_misc.h
index e680e8c34b0..bf3b6ade560 100644
--- a/dlls/ntdll/ntdll_misc.h
+++ b/dlls/ntdll/ntdll_misc.h
@@ -44,7 +44,7 @@ extern void dump_ObjectAttributes (const OBJECT_ATTRIBUTES *ObjectAttributes);
 extern void NTDLL_get_server_timeout( abs_time_t *when, const LARGE_INTEGER *timeout );
 extern void NTDLL_from_server_timeout( LARGE_INTEGER *timeout, const abs_time_t *when );
 extern NTSTATUS NTDLL_wait_for_multiple_objects( UINT count, const HANDLE *handles, UINT flags,
-                                                 const LARGE_INTEGER *timeout );
+                                                 const LARGE_INTEGER *timeout, HANDLE signal_object );
 
 /* init routines */
 extern BOOL SIGNAL_Init(void);
diff --git a/dlls/ntdll/signal_i386.c b/dlls/ntdll/signal_i386.c
index dd903cb610e..5fb6cb243b3 100644
--- a/dlls/ntdll/signal_i386.c
+++ b/dlls/ntdll/signal_i386.c
@@ -1156,7 +1156,7 @@ static HANDLER_DEF(usr1_handler)
     init_handler( HANDLER_CONTEXT );
     /* wait with 0 timeout, will only return once the thread is no longer suspended */
     timeout.QuadPart = 0;
-    NTDLL_wait_for_multiple_objects( 0, NULL, 0, &timeout );
+    NTDLL_wait_for_multiple_objects( 0, NULL, 0, &timeout, 0 );
 }
 
 
diff --git a/dlls/ntdll/sync.c b/dlls/ntdll/sync.c
index 2e97b35912d..87e2abb5d53 100644
--- a/dlls/ntdll/sync.c
+++ b/dlls/ntdll/sync.c
@@ -701,7 +701,7 @@ static void call_apcs( BOOL alertable )
  * Implementation of NtWaitForMultipleObjects
  */
 NTSTATUS NTDLL_wait_for_multiple_objects( UINT count, const HANDLE *handles, UINT flags,
-                                          const LARGE_INTEGER *timeout )
+                                          const LARGE_INTEGER *timeout, HANDLE signal_object )
 {
     NTSTATUS ret;
     int cookie;
@@ -713,6 +713,7 @@ NTSTATUS NTDLL_wait_for_multiple_objects( UINT count, const HANDLE *handles, UIN
         {
             req->flags   = flags;
             req->cookie  = &cookie;
+            req->signal  = signal_object;
             NTDLL_get_server_timeout( &req->timeout, timeout );
             wine_server_add_data( req, handles, count * sizeof(HANDLE) );
             ret = wine_server_call( req );
@@ -722,6 +723,7 @@ NTSTATUS NTDLL_wait_for_multiple_objects( UINT count, const HANDLE *handles, UIN
         if (ret != STATUS_USER_APC) break;
         call_apcs( (flags & SELECT_ALERTABLE) != 0 );
         if (flags & SELECT_ALERTABLE) break;
+        signal_object = 0;  /* don't signal it multiple times */
     }
 
     /* A test on Windows 2000 shows that Windows always yields during
@@ -748,7 +750,7 @@ NTSTATUS WINAPI NtWaitForMultipleObjects( DWORD count, const HANDLE *handles,
 
     if (wait_all) flags |= SELECT_ALL;
     if (alertable) flags |= SELECT_ALERTABLE;
-    return NTDLL_wait_for_multiple_objects( count, handles, flags, timeout );
+    return NTDLL_wait_for_multiple_objects( count, handles, flags, timeout, 0 );
 }
 
 
@@ -761,6 +763,20 @@ NTSTATUS WINAPI NtWaitForSingleObject(HANDLE handle, BOOLEAN alertable, const LA
 }
 
 
+/******************************************************************
+ *		NtSignalAndWaitForSingleObject (NTDLL.@)
+ */
+NTSTATUS WINAPI NtSignalAndWaitForSingleObject( HANDLE hSignalObject, HANDLE hWaitObject,
+                                                BOOLEAN alertable, const LARGE_INTEGER *timeout )
+{
+    UINT flags = SELECT_INTERRUPTIBLE;
+
+    if (!hSignalObject) return STATUS_INVALID_HANDLE;
+    if (alertable) flags |= SELECT_ALERTABLE;
+    return NTDLL_wait_for_multiple_objects( 1, &hWaitObject, flags, timeout, hSignalObject );
+}
+
+
 /******************************************************************
  *		NtYieldExecution (NTDLL.@)
  */
@@ -785,7 +801,7 @@ NTSTATUS WINAPI NtDelayExecution( BOOLEAN alertable, const LARGE_INTEGER *timeou
     {
         UINT flags = SELECT_INTERRUPTIBLE;
         if (alertable) flags |= SELECT_ALERTABLE;
-        return NTDLL_wait_for_multiple_objects( 0, NULL, flags, timeout );
+        return NTDLL_wait_for_multiple_objects( 0, NULL, flags, timeout, 0 );
     }
 
     if (!timeout)  /* sleep forever */
@@ -819,14 +835,3 @@ NTSTATUS WINAPI NtDelayExecution( BOOLEAN alertable, const LARGE_INTEGER *timeou
     }
     return STATUS_SUCCESS;
 }
-
-
-/******************************************************************
- *		NtSignalAndWaitForSingleObject (NTDLL.@)
- */
-NTSTATUS WINAPI NtSignalAndWaitForSingleObject( HANDLE hSignalObject, HANDLE hWaitObject,
-                                                BOOLEAN bAlertable, PLARGE_INTEGER Timeout )
-{
-    FIXME("%p %p %d %p\n", hSignalObject, hWaitObject, bAlertable, Timeout);
-    return STATUS_SUCCESS;
-}
diff --git a/include/wine/server_protocol.h b/include/wine/server_protocol.h
index eb415561c9a..fd3f6401c7d 100644
--- a/include/wine/server_protocol.h
+++ b/include/wine/server_protocol.h
@@ -603,6 +603,7 @@ struct select_request
     struct request_header __header;
     int          flags;
     void*        cookie;
+    obj_handle_t signal;
     abs_time_t   timeout;
     /* VARARG(handles,handles); */
 };
@@ -3902,6 +3903,6 @@ union generic_reply
     struct set_mailslot_info_reply set_mailslot_info_reply;
 };
 
-#define SERVER_PROTOCOL_VERSION 170
+#define SERVER_PROTOCOL_VERSION 171
 
 #endif /* __WINE_WINE_SERVER_PROTOCOL_H */
diff --git a/include/winternl.h b/include/winternl.h
index 23cd84927e1..09524673ea1 100644
--- a/include/winternl.h
+++ b/include/winternl.h
@@ -1521,7 +1521,7 @@ NTSTATUS  WINAPI NtSetSecurityObject(HANDLE,SECURITY_INFORMATION,PSECURITY_DESCR
 NTSTATUS  WINAPI NtSetSystemTime(const LARGE_INTEGER*,LARGE_INTEGER*);
 NTSTATUS  WINAPI NtSetTimer(HANDLE, const LARGE_INTEGER*, PTIMERAPCROUTINE, PVOID, BOOLEAN, ULONG, BOOLEAN*);
 NTSTATUS  WINAPI NtSetValueKey(HKEY,const UNICODE_STRING *,ULONG,ULONG,const void *,ULONG);
-NTSTATUS  WINAPI NtSignalAndWaitForSingleObject(HANDLE,HANDLE,BOOLEAN,PLARGE_INTEGER);
+NTSTATUS  WINAPI NtSignalAndWaitForSingleObject(HANDLE,HANDLE,BOOLEAN,const LARGE_INTEGER*);
 NTSTATUS  WINAPI NtSuspendThread(HANDLE,PULONG);
 NTSTATUS  WINAPI NtTerminateProcess(HANDLE,LONG);
 NTSTATUS  WINAPI NtTerminateThread(HANDLE,LONG);
diff --git a/server/atom.c b/server/atom.c
index e14c05eff2b..341cf6b8c17 100644
--- a/server/atom.c
+++ b/server/atom.c
@@ -70,7 +70,8 @@ static const struct object_ops atom_table_ops =
     no_add_queue,                 /* add_queue */
     NULL,                         /* remove_queue */
     NULL,                         /* signaled */
-    NULL,                         /* satified */
+    NULL,                         /* satisfied */
+    no_signal,                    /* signal */
     no_get_fd,                    /* get_fd */
     atom_table_destroy            /* destroy */
 };
diff --git a/server/change.c b/server/change.c
index 2d426443794..51867dd7ab9 100644
--- a/server/change.c
+++ b/server/change.c
@@ -71,6 +71,7 @@ static const struct object_ops change_ops =
     remove_queue,             /* remove_queue */
     change_signaled,          /* signaled */
     no_satisfied,             /* satisfied */
+    no_signal,                /* signal */
     no_get_fd,                /* get_fd */
     change_destroy            /* destroy */
 };
diff --git a/server/console.c b/server/console.c
index b51f56de2b8..a513538615c 100644
--- a/server/console.c
+++ b/server/console.c
@@ -46,6 +46,7 @@ static const struct object_ops console_input_ops =
     NULL,                             /* remove_queue */
     NULL,                             /* signaled */
     no_satisfied,                     /* satisfied */
+    no_signal,                        /* signal */
     no_get_fd,                        /* get_fd */
     console_input_destroy             /* destroy */
 };
@@ -70,6 +71,7 @@ static const struct object_ops console_input_events_ops =
     remove_queue,                     /* remove_queue */
     console_input_events_signaled,    /* signaled */
     no_satisfied,                     /* satisfied */
+    no_signal,                        /* signal */
     no_get_fd,                        /* get_fd */
     console_input_events_destroy      /* destroy */
 };
@@ -105,6 +107,7 @@ static const struct object_ops screen_buffer_ops =
     NULL,                             /* remove_queue */
     NULL,                             /* signaled */
     NULL,                             /* satisfied */
+    no_signal,                        /* signal */
     no_get_fd,                        /* get_fd */
     screen_buffer_destroy             /* destroy */
 };
diff --git a/server/debugger.c b/server/debugger.c
index bde88a2ea4f..b1ba9b15c49 100644
--- a/server/debugger.c
+++ b/server/debugger.c
@@ -72,6 +72,7 @@ static const struct object_ops debug_event_ops =
     remove_queue,                  /* remove_queue */
     debug_event_signaled,          /* signaled */
     no_satisfied,                  /* satisfied */
+    no_signal,                     /* signal */
     no_get_fd,                     /* get_fd */
     debug_event_destroy            /* destroy */
 };
@@ -88,6 +89,7 @@ static const struct object_ops debug_ctx_ops =
     remove_queue,                  /* remove_queue */
     debug_ctx_signaled,            /* signaled */
     no_satisfied,                  /* satisfied */
+    no_signal,                     /* signal */
     no_get_fd,                     /* get_fd */
     debug_ctx_destroy              /* destroy */
 };
diff --git a/server/event.c b/server/event.c
index e1487d3062c..fa1dbbfba2c 100644
--- a/server/event.c
+++ b/server/event.c
@@ -41,6 +41,7 @@ struct event
 static void event_dump( struct object *obj, int verbose );
 static int event_signaled( struct object *obj, struct thread *thread );
 static int event_satisfied( struct object *obj, struct thread *thread );
+static int event_signal( struct object *obj, unsigned int access);
 
 static const struct object_ops event_ops =
 {
@@ -50,6 +51,7 @@ static const struct object_ops event_ops =
     remove_queue,              /* remove_queue */
     event_signaled,            /* signaled */
     event_satisfied,           /* satisfied */
+    event_signal,              /* signal */
     no_get_fd,                 /* get_fd */
     no_destroy                 /* destroy */
 };
@@ -123,6 +125,20 @@ static int event_satisfied( struct object *obj, struct thread *thread )
     return 0;  /* Not abandoned */
 }
 
+static int event_signal( struct object *obj, unsigned int access )
+{
+    struct event *event = (struct event *)obj;
+    assert( obj->ops == &event_ops );
+
+    if (!(access & EVENT_MODIFY_STATE))
+    {
+        set_error( STATUS_ACCESS_DENIED );
+        return 0;
+    }
+    set_event( event );
+    return 1;
+}
+
 /* create an event */
 DECL_HANDLER(create_event)
 {
diff --git a/server/fd.c b/server/fd.c
index ef5b31cf413..72c3d9faa7c 100644
--- a/server/fd.c
+++ b/server/fd.c
@@ -159,6 +159,7 @@ static const struct object_ops fd_ops =
     NULL,                     /* remove_queue */
     NULL,                     /* signaled */
     NULL,                     /* satisfied */
+    no_signal,                /* signal */
     no_get_fd,                /* get_fd */
     fd_destroy                /* destroy */
 };
@@ -188,6 +189,7 @@ static const struct object_ops inode_ops =
     NULL,                     /* remove_queue */
     NULL,                     /* signaled */
     NULL,                     /* satisfied */
+    no_signal,                /* signal */
     no_get_fd,                /* get_fd */
     inode_destroy             /* destroy */
 };
@@ -218,6 +220,7 @@ static const struct object_ops file_lock_ops =
     remove_queue,               /* remove_queue */
     file_lock_signaled,         /* signaled */
     no_satisfied,               /* satisfied */
+    no_signal,                  /* signal */
     no_get_fd,                  /* get_fd */
     no_destroy                  /* destroy */
 };
diff --git a/server/file.c b/server/file.c
index d28dc0d42d0..45f2bc0a8a6 100644
--- a/server/file.c
+++ b/server/file.c
@@ -83,6 +83,7 @@ static const struct object_ops file_ops =
     default_fd_remove_queue,      /* remove_queue */
     default_fd_signaled,          /* signaled */
     no_satisfied,                 /* satisfied */
+    no_signal,                    /* signal */
     file_get_fd,                  /* get_fd */
     file_destroy                  /* destroy */
 };
diff --git a/server/handle.c b/server/handle.c
index ae68a7a4ef4..347960dd06e 100644
--- a/server/handle.c
+++ b/server/handle.c
@@ -106,6 +106,7 @@ static const struct object_ops handle_table_ops =
     NULL,                            /* remove_queue */
     NULL,                            /* signaled */
     NULL,                            /* satisfied */
+    no_signal,                       /* signal */
     no_get_fd,                       /* get_fd */
     handle_table_destroy             /* destroy */
 };
@@ -385,6 +386,16 @@ struct object *get_handle_obj( struct process *process, obj_handle_t handle,
     return grab_object( obj );
 }
 
+/* retrieve the access rights of a given handle */
+unsigned int get_handle_access( struct process *process, obj_handle_t handle )
+{
+    struct handle_entry *entry;
+
+    if (get_magic_handle( handle )) return ~0U;  /* magic handles have all access rights */
+    if (!(entry = get_handle( process, handle ))) return 0;
+    return entry->access;
+}
+
 /* retrieve the cached fd for a given handle */
 int get_handle_unix_fd( struct process *process, obj_handle_t handle, unsigned int access )
 {
diff --git a/server/handle.h b/server/handle.h
index 91b7a2cbe5d..84faae1e517 100644
--- a/server/handle.h
+++ b/server/handle.h
@@ -38,6 +38,7 @@ extern obj_handle_t alloc_handle( struct process *process, void *obj,
 extern int close_handle( struct process *process, obj_handle_t handle, int *fd );
 extern struct object *get_handle_obj( struct process *process, obj_handle_t handle,
                                       unsigned int access, const struct object_ops *ops );
+extern unsigned int get_handle_access( struct process *process, obj_handle_t handle );
 extern int get_handle_unix_fd( struct process *process, obj_handle_t handle, unsigned int access );
 extern int set_handle_info( struct process *process, obj_handle_t handle, int mask, int flags, int *fd );
 extern obj_handle_t duplicate_handle( struct process *src, obj_handle_t src_handle, struct process *dst,
diff --git a/server/hook.c b/server/hook.c
index 293b20c5dd2..c228f987deb 100644
--- a/server/hook.c
+++ b/server/hook.c
@@ -77,6 +77,7 @@ static const struct object_ops hook_table_ops =
     NULL,                         /* remove_queue */
     NULL,                         /* signaled */
     NULL,                         /* satisfied */
+    no_signal,                    /* signal */
     no_get_fd,                    /* get_fd */
     hook_table_destroy            /* destroy */
 };
diff --git a/server/mailslot.c b/server/mailslot.c
index 7a6230f2e6d..4066e57e817 100644
--- a/server/mailslot.c
+++ b/server/mailslot.c
@@ -72,6 +72,7 @@ static const struct object_ops mailslot_ops =
     default_fd_remove_queue,   /* remove_queue */
     default_fd_signaled,       /* signaled */
     no_satisfied,              /* satisfied */
+    no_signal,                 /* signal */
     mailslot_get_fd,           /* get_fd */
     mailslot_destroy           /* destroy */
 };
@@ -113,6 +114,7 @@ static const struct object_ops mail_writer_ops =
     NULL,                       /* remove_queue */
     NULL,                       /* signaled */
     NULL,                       /* satisfied */
+    no_signal,                  /* signal */
     mail_writer_get_fd,         /* get_fd */
     mail_writer_destroy         /* destroy */
 };
diff --git a/server/mapping.c b/server/mapping.c
index 8992986f081..046bc0b86a6 100644
--- a/server/mapping.c
+++ b/server/mapping.c
@@ -62,6 +62,7 @@ static const struct object_ops mapping_ops =
     NULL,                        /* remove_queue */
     NULL,                        /* signaled */
     NULL,                        /* satisfied */
+    no_signal,                   /* signal */
     mapping_get_fd,              /* get_fd */
     mapping_destroy              /* destroy */
 };
diff --git a/server/mutex.c b/server/mutex.c
index 1b1ef37b21e..c1b59bb85bd 100644
--- a/server/mutex.c
+++ b/server/mutex.c
@@ -44,6 +44,7 @@ static void mutex_dump( struct object *obj, int verbose );
 static int mutex_signaled( struct object *obj, struct thread *thread );
 static int mutex_satisfied( struct object *obj, struct thread *thread );
 static void mutex_destroy( struct object *obj );
+static int mutex_signal( struct object *obj, unsigned int access );
 
 static const struct object_ops mutex_ops =
 {
@@ -53,6 +54,7 @@ static const struct object_ops mutex_ops =
     remove_queue,              /* remove_queue */
     mutex_signaled,            /* signaled */
     mutex_satisfied,           /* satisfied */
+    mutex_signal,              /* signal */
     no_get_fd,                 /* get_fd */
     mutex_destroy              /* destroy */
 };
@@ -133,6 +135,25 @@ static int mutex_satisfied( struct object *obj, struct thread *thread )
     return 1;
 }
 
+static int mutex_signal( struct object *obj, unsigned int access )
+{
+    struct mutex *mutex = (struct mutex *)obj;
+    assert( obj->ops == &mutex_ops );
+
+    if (!(access & SYNCHRONIZE))  /* FIXME: MUTEX_MODIFY_STATE? */
+    {
+        set_error( STATUS_ACCESS_DENIED );
+        return 0;
+    }
+    if (!mutex->count || (mutex->owner != current))
+    {
+        set_error( STATUS_MUTANT_NOT_OWNED );
+        return 0;
+    }
+    if (!--mutex->count) do_release( mutex );
+    return 1;
+}
+
 static void mutex_destroy( struct object *obj )
 {
     struct mutex *mutex = (struct mutex *)obj;
diff --git a/server/named_pipe.c b/server/named_pipe.c
index dd1d3dda91c..b5077be1691 100644
--- a/server/named_pipe.c
+++ b/server/named_pipe.c
@@ -106,6 +106,7 @@ static const struct object_ops named_pipe_ops =
     NULL,                         /* remove_queue */
     NULL,                         /* signaled */
     NULL,                         /* satisfied */
+    no_signal,                    /* signal */
     no_get_fd,                    /* get_fd */
     named_pipe_destroy            /* destroy */
 };
@@ -128,6 +129,7 @@ static const struct object_ops pipe_server_ops =
     default_fd_remove_queue,      /* remove_queue */
     default_fd_signaled,          /* signaled */
     no_satisfied,                 /* satisfied */
+    no_signal,                    /* signal */
     pipe_server_get_fd,           /* get_fd */
     pipe_server_destroy           /* destroy */
 };
@@ -156,6 +158,7 @@ static const struct object_ops pipe_client_ops =
     default_fd_remove_queue,      /* remove_queue */
     default_fd_signaled,          /* signaled */
     no_satisfied,                 /* satisfied */
+    no_signal,                    /* signal */
     pipe_client_get_fd,           /* get_fd */
     pipe_client_destroy           /* destroy */
 };
diff --git a/server/object.c b/server/object.c
index 91959b65c5a..479b09c646a 100644
--- a/server/object.c
+++ b/server/object.c
@@ -273,6 +273,12 @@ int no_satisfied( struct object *obj, struct thread *thread )
     return 0;  /* not abandoned */
 }
 
+int no_signal( struct object *obj, unsigned int access )
+{
+    set_error( STATUS_OBJECT_TYPE_MISMATCH );
+    return 0;
+}
+
 struct fd *no_get_fd( struct object *obj )
 {
     set_error( STATUS_OBJECT_TYPE_MISMATCH );
diff --git a/server/object.h b/server/object.h
index 44dfe18b93b..e8865263128 100644
--- a/server/object.h
+++ b/server/object.h
@@ -59,6 +59,8 @@ struct object_ops
     int  (*signaled)(struct object *,struct thread *);
     /* wait satisfied; return 1 if abandoned */
     int  (*satisfied)(struct object *,struct thread *);
+    /* signal an object */
+    int  (*signal)(struct object *, unsigned int);
     /* return an fd object that can be used to read/write from the object */
     struct fd *(*get_fd)(struct object *);
     /* destroy on refcount == 0 */
@@ -97,6 +99,7 @@ extern void release_object( void *obj );
 extern struct object *find_object( const struct namespace *namespace, const WCHAR *name, size_t len );
 extern int no_add_queue( struct object *obj, struct wait_queue_entry *entry );
 extern int no_satisfied( struct object *obj, struct thread *thread );
+extern int no_signal( struct object *obj, unsigned int access );
 extern struct fd *no_get_fd( struct object *obj );
 extern void no_destroy( struct object *obj );
 #ifdef DEBUG_OBJECTS
diff --git a/server/process.c b/server/process.c
index 12f9a1e1191..d2f8c89e857 100644
--- a/server/process.c
+++ b/server/process.c
@@ -70,6 +70,7 @@ static const struct object_ops process_ops =
     remove_queue,                /* remove_queue */
     process_signaled,            /* signaled */
     no_satisfied,                /* satisfied */
+    no_signal,                    /* signal */
     no_get_fd,                   /* get_fd */
     process_destroy              /* destroy */
 };
@@ -116,6 +117,7 @@ static const struct object_ops startup_info_ops =
     remove_queue,                  /* remove_queue */
     startup_info_signaled,         /* signaled */
     no_satisfied,                  /* satisfied */
+    no_signal,                     /* signal */
     no_get_fd,                     /* get_fd */
     startup_info_destroy           /* destroy */
 };
diff --git a/server/protocol.def b/server/protocol.def
index 3427fd070c0..fe892184ea2 100644
--- a/server/protocol.def
+++ b/server/protocol.def
@@ -480,6 +480,7 @@ enum apc_type { APC_NONE, APC_USER, APC_TIMER, APC_ASYNC_IO };
 @REQ(select)
     int          flags;        /* wait flags (see below) */
     void*        cookie;       /* magic cookie to return to client */
+    obj_handle_t signal;       /* object to signal (0 if none) */
     abs_time_t   timeout;      /* absolute timeout */
     VARARG(handles,handles);   /* handles to select on */
 @END
diff --git a/server/queue.c b/server/queue.c
index e801fb05d70..c2fb9a9c667 100644
--- a/server/queue.c
+++ b/server/queue.c
@@ -146,6 +146,7 @@ static const struct object_ops msg_queue_ops =
     msg_queue_remove_queue,    /* remove_queue */
     msg_queue_signaled,        /* signaled */
     msg_queue_satisfied,       /* satisfied */
+    no_signal,                 /* signal */
     no_get_fd,                 /* get_fd */
     msg_queue_destroy          /* destroy */
 };
@@ -159,6 +160,7 @@ static const struct object_ops thread_input_ops =
     NULL,                         /* remove_queue */
     NULL,                         /* signaled */
     NULL,                         /* satisfied */
+    no_signal,                    /* signal */
     no_get_fd,                    /* get_fd */
     thread_input_destroy          /* destroy */
 };
diff --git a/server/registry.c b/server/registry.c
index 6f85f5721cf..c7dcd73cab4 100644
--- a/server/registry.c
+++ b/server/registry.c
@@ -138,6 +138,7 @@ static const struct object_ops key_ops =
     NULL,                    /* remove_queue */
     NULL,                    /* signaled */
     NULL,                    /* satisfied */
+    no_signal,               /* signal */
     no_get_fd,               /* get_fd */
     key_destroy              /* destroy */
 };
diff --git a/server/request.c b/server/request.c
index 4f943d0b857..8d33660bf66 100644
--- a/server/request.c
+++ b/server/request.c
@@ -93,6 +93,7 @@ static const struct object_ops master_socket_ops =
     NULL,                          /* remove_queue */
     NULL,                          /* signaled */
     NULL,                          /* satisfied */
+    no_signal,                     /* signal */
     no_get_fd,                     /* get_fd */
     master_socket_destroy          /* destroy */
 };
diff --git a/server/semaphore.c b/server/semaphore.c
index 27aba0d6f7b..7886842cf07 100644
--- a/server/semaphore.c
+++ b/server/semaphore.c
@@ -41,6 +41,7 @@ struct semaphore
 static void semaphore_dump( struct object *obj, int verbose );
 static int semaphore_signaled( struct object *obj, struct thread *thread );
 static int semaphore_satisfied( struct object *obj, struct thread *thread );
+static int semaphore_signal( struct object *obj, unsigned int access );
 
 static const struct object_ops semaphore_ops =
 {
@@ -50,6 +51,7 @@ static const struct object_ops semaphore_ops =
     remove_queue,                  /* remove_queue */
     semaphore_signaled,            /* signaled */
     semaphore_satisfied,           /* satisfied */
+    semaphore_signal,              /* signal */
     no_get_fd,                     /* get_fd */
     no_destroy                     /* destroy */
 };
@@ -77,32 +79,26 @@ static struct semaphore *create_semaphore( const WCHAR *name, size_t len,
     return sem;
 }
 
-static unsigned int release_semaphore( obj_handle_t handle, unsigned int count )
+static int release_semaphore( struct semaphore *sem, unsigned int count,
+                              unsigned int *prev )
 {
-    struct semaphore *sem;
-    unsigned int prev = 0;
-
-    if ((sem = (struct semaphore *)get_handle_obj( current->process, handle,
-                                                   SEMAPHORE_MODIFY_STATE, &semaphore_ops )))
+    if (prev) *prev = sem->count;
+    if (sem->count + count < sem->count || sem->count + count > sem->max)
     {
-        prev = sem->count;
-        if (sem->count + count < sem->count || sem->count + count > sem->max)
-        {
-            set_error( STATUS_SEMAPHORE_LIMIT_EXCEEDED );
-        }
-        else if (sem->count)
-        {
-            /* there cannot be any thread to wake up if the count is != 0 */
-            sem->count += count;
-        }
-        else
-        {
-            sem->count = count;
-            wake_up( &sem->obj, count );
-        }
-        release_object( sem );
+        set_error( STATUS_SEMAPHORE_LIMIT_EXCEEDED );
+        return 0;
+    }
+    else if (sem->count)
+    {
+        /* there cannot be any thread to wake up if the count is != 0 */
+        sem->count += count;
+    }
+    else
+    {
+        sem->count = count;
+        wake_up( &sem->obj, count );
     }
-    return prev;
+    return 1;
 }
 
 static void semaphore_dump( struct object *obj, int verbose )
@@ -130,6 +126,19 @@ static int semaphore_satisfied( struct object *obj, struct thread *thread )
     return 0;  /* not abandoned */
 }
 
+static int semaphore_signal( struct object *obj, unsigned int access )
+{
+    struct semaphore *sem = (struct semaphore *)obj;
+    assert( obj->ops == &semaphore_ops );
+
+    if (!(access & SEMAPHORE_MODIFY_STATE))
+    {
+        set_error( STATUS_ACCESS_DENIED );
+        return 0;
+    }
+    return release_semaphore( sem, 1, NULL );
+}
+
 /* create a semaphore */
 DECL_HANDLER(create_semaphore)
 {
@@ -154,5 +163,12 @@ DECL_HANDLER(open_semaphore)
 /* release a semaphore */
 DECL_HANDLER(release_semaphore)
 {
-    reply->prev_count = release_semaphore( req->handle, req->count );
+    struct semaphore *sem;
+
+    if ((sem = (struct semaphore *)get_handle_obj( current->process, req->handle,
+                                                   SEMAPHORE_MODIFY_STATE, &semaphore_ops )))
+    {
+        release_semaphore( sem, req->count, &reply->prev_count );
+        release_object( sem );
+    }
 }
diff --git a/server/serial.c b/server/serial.c
index 6f90f5f3a3e..014142e190e 100644
--- a/server/serial.c
+++ b/server/serial.c
@@ -101,6 +101,7 @@ static const struct object_ops serial_ops =
     default_fd_remove_queue,      /* remove_queue */
     default_fd_signaled,          /* signaled */
     no_satisfied,                 /* satisfied */
+    no_signal,                    /* signal */
     serial_get_fd,                /* get_fd */
     serial_destroy                /* destroy */
 };
diff --git a/server/signal.c b/server/signal.c
index ea1c15b8627..470a476de9d 100644
--- a/server/signal.c
+++ b/server/signal.c
@@ -62,6 +62,7 @@ static const struct object_ops handler_ops =
     NULL,                     /* remove_queue */
     NULL,                     /* signaled */
     NULL,                     /* satisfied */
+    no_signal,                /* signal */
     no_get_fd,                /* get_fd */
     handler_destroy           /* destroy */
 };
diff --git a/server/snapshot.c b/server/snapshot.c
index 975728a8e31..61265badfe6 100644
--- a/server/snapshot.c
+++ b/server/snapshot.c
@@ -61,6 +61,7 @@ static const struct object_ops snapshot_ops =
     NULL,                         /* remove_queue */
     NULL,                         /* signaled */
     NULL,                         /* satisfied */
+    no_signal,                    /* signal */
     no_get_fd,                    /* get_fd */
     snapshot_destroy              /* destroy */
 };
diff --git a/server/sock.c b/server/sock.c
index f3f27fc6cd7..7fcb406f702 100644
--- a/server/sock.c
+++ b/server/sock.c
@@ -107,6 +107,7 @@ static const struct object_ops sock_ops =
     remove_queue,                 /* remove_queue */
     sock_signaled,                /* signaled */
     no_satisfied,                 /* satisfied */
+    no_signal,                    /* signal */
     sock_get_fd,                  /* get_fd */
     sock_destroy                  /* destroy */
 };
diff --git a/server/thread.c b/server/thread.c
index af6657efca2..2d7a61bd08c 100644
--- a/server/thread.c
+++ b/server/thread.c
@@ -92,6 +92,7 @@ static const struct object_ops thread_ops =
     remove_queue,               /* remove_queue */
     thread_signaled,            /* signaled */
     no_satisfied,               /* satisfied */
+    no_signal,                  /* signal */
     no_get_fd,                  /* get_fd */
     destroy_thread              /* destroy */
 };
@@ -516,9 +517,24 @@ static void thread_timeout( void *ptr )
     wake_thread( thread );
 }
 
+/* try signaling an event flag, a semaphore or a mutex */
+static int signal_object( obj_handle_t handle )
+{
+    struct object *obj;
+    int ret = 0;
+
+    obj = get_handle_obj( current->process, handle, 0, NULL );
+    if (obj)
+    {
+        ret = obj->ops->signal( obj, get_handle_access( current->process, handle ));
+        release_object( obj );
+    }
+    return ret;
+}
+
 /* select on a list of handles */
 static void select_on( int count, void *cookie, const obj_handle_t *handles,
-                       int flags, const abs_time_t *timeout )
+                       int flags, const abs_time_t *timeout, obj_handle_t signal_obj )
 {
     int ret, i;
     struct object *objects[MAXIMUM_WAIT_OBJECTS];
@@ -537,6 +553,18 @@ static void select_on( int count, void *cookie, const obj_handle_t *handles,
     if (i < count) goto done;
     if (!wait_on( count, objects, flags, timeout )) goto done;
 
+    /* signal the object */
+    if (signal_obj)
+    {
+        if (!signal_object( signal_obj ))
+        {
+            end_wait( current );
+            goto done;
+        }
+        /* check if we woke ourselves up */
+        if (!current->wait) goto done;
+    }
+
     if ((ret = check_wait( current )) != -1)
     {
         /* condition is already satisfied */
@@ -962,7 +990,7 @@ DECL_HANDLER(resume_thread)
 DECL_HANDLER(select)
 {
     int count = get_req_data_size() / sizeof(int);
-    select_on( count, req->cookie, get_req_data(), req->flags, &req->timeout );
+    select_on( count, req->cookie, get_req_data(), req->flags, &req->timeout, req->signal );
 }
 
 /* queue an APC for a thread */
diff --git a/server/timer.c b/server/timer.c
index 1903ba71fd7..720c6f328cc 100644
--- a/server/timer.c
+++ b/server/timer.c
@@ -59,6 +59,7 @@ static const struct object_ops timer_ops =
     remove_queue,              /* remove_queue */
     timer_signaled,            /* signaled */
     timer_satisfied,           /* satisfied */
+    no_signal,                 /* signal */
     no_get_fd,                 /* get_fd */
     timer_destroy              /* destroy */
 };
diff --git a/server/token.c b/server/token.c
index 23fe27cd852..e30f75525bf 100644
--- a/server/token.c
+++ b/server/token.c
@@ -79,7 +79,8 @@ static const struct object_ops token_ops =
     no_add_queue,              /* add_queue */
     NULL,                      /* remove_queue */
     NULL,                      /* signaled */
-    NULL,                      /* satified */
+    NULL,                      /* satisfied */
+    no_signal,                 /* signal */
     no_get_fd,                 /* get_fd */
     token_destroy              /* destroy */
 };
diff --git a/server/trace.c b/server/trace.c
index d12c962c040..2de4ddf13ac 100644
--- a/server/trace.c
+++ b/server/trace.c
@@ -760,6 +760,7 @@ static void dump_select_request( const struct select_request *req )
 {
     fprintf( stderr, " flags=%d,", req->flags );
     fprintf( stderr, " cookie=%p,", req->cookie );
+    fprintf( stderr, " signal=%p,", req->signal );
     fprintf( stderr, " timeout=" );
     dump_abs_time( &req->timeout );
     fprintf( stderr, "," );
-- 
GitLab