Later, a similar inproc sync as the queue will be created for user APC alerts and sent on thread init as well, saving us the need to special case those in the get_inproc_sync request and in the cache.
-- v6: ntdll: Notify wineserver when waiting on inproc queue. ntdll: Add a reference count to inproc_sync objects. server: Create an inproc sync for message queue signaling. win32u: Move server queue handle to ntuser_thread_info. ntdll: Introduce a helper to wait on an server-side sync object. ntdll: Check inproc sync signal rights in signal and wait. server: Use a specific type for internal inproc event syncs. wineserver: Request RLIMIT_NOFILE maximum allowed value.
From: Rémi Bernon rbernon@codeweavers.com
--- server/main.c | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+)
diff --git a/server/main.c b/server/main.c index e014ec535ff..86f9cfdb984 100644 --- a/server/main.c +++ b/server/main.c @@ -28,6 +28,9 @@ #include <stdlib.h> #include <sys/time.h> #include <unistd.h> +#ifdef HAVE_SYS_RESOURCE_H +# include <sys/resource.h> +#endif
#include "object.h" #include "file.h" @@ -212,6 +215,42 @@ static void sigterm_handler( int signum ) exit(1); /* make sure atexit functions get called */ }
+#ifdef RLIMIT_NOFILE +static void set_max_limit( int limit ) +{ + struct rlimit rlimit; + + if (!getrlimit( limit, &rlimit )) + { + rlimit.rlim_cur = rlimit.rlim_max; + if (!setrlimit( limit, &rlimit )) return; + +#if defined(__APPLE__) && defined(RLIMIT_NOFILE) && defined(OPEN_MAX) + if (limit == RLIMIT_NOFILE) + { + unsigned int nlimit = 0; + size_t size; + + /* On Leopard, setrlimit(RLIMIT_NOFILE, ...) fails on attempts to set + * rlim_cur above OPEN_MAX (even if rlim_max > OPEN_MAX). + * + * In later versions it can be set to kern.maxfilesperproc (from + * sysctl). In Big Sur and later it can be set to rlim_max. */ + size = sizeof(nlimit); + if (sysctlbyname("kern.maxfilesperproc", &nlimit, &size, NULL, 0) != 0 || nlimit < OPEN_MAX) + rlimit.rlim_cur = OPEN_MAX; + else + rlimit.rlim_cur = nlimit; + + if (!setrlimit( limit, &rlimit )) return; + rlimit.rlim_cur = OPEN_MAX; + if (!setrlimit( limit, &rlimit )) return; + } +#endif + } +} +#endif /* RLIMIT_NOFILE */ + int main( int argc, char *argv[] ) { setvbuf( stderr, NULL, _IOLBF, 0 ); @@ -225,6 +264,9 @@ int main( int argc, char *argv[] ) signal( SIGQUIT, sigterm_handler ); signal( SIGTERM, sigterm_handler ); signal( SIGABRT, sigterm_handler ); +#ifdef RLIMIT_NOFILE + set_max_limit( RLIMIT_NOFILE ); +#endif
sock_init(); open_master_socket();
From: Rémi Bernon rbernon@codeweavers.com
--- server/inproc_sync.c | 7 ++++--- server/object.h | 2 +- server/protocol.def | 4 ++-- server/queue.c | 2 +- 4 files changed, 8 insertions(+), 7 deletions(-)
diff --git a/server/inproc_sync.c b/server/inproc_sync.c index f20410c85ed..afad68664d1 100644 --- a/server/inproc_sync.c +++ b/server/inproc_sync.c @@ -87,14 +87,15 @@ static const struct object_ops inproc_sync_ops = inproc_sync_destroy, /* destroy */ };
-struct inproc_sync *create_inproc_event_sync( int manual, int signaled ) +struct inproc_sync *create_inproc_internal_sync( int manual, int signaled ) { struct ntsync_event_args args = {.signaled = signaled, .manual = manual}; struct inproc_sync *event;
if (!(event = alloc_object( &inproc_sync_ops ))) return NULL; - event->type = INPROC_SYNC_EVENT; + event->type = INPROC_SYNC_INTERNAL; event->fd = ioctl( get_inproc_device_fd(), NTSYNC_IOC_CREATE_EVENT, &args ); + assert( event->fd != -1 );
return event; } @@ -154,7 +155,7 @@ int get_inproc_device_fd(void) return -1; }
-struct inproc_sync *create_inproc_event_sync( int manual, int signaled ) +struct inproc_sync *create_inproc_internal_sync( int manual, int signaled ) { return NULL; } diff --git a/server/object.h b/server/object.h index 51b74429e73..203734a565f 100644 --- a/server/object.h +++ b/server/object.h @@ -244,7 +244,7 @@ extern void abandon_mutexes( struct thread *thread );
struct inproc_sync; extern int get_inproc_device_fd(void); -extern struct inproc_sync *create_inproc_event_sync( int manual, int signaled ); +extern struct inproc_sync *create_inproc_internal_sync( int manual, int signaled ); extern void signal_inproc_sync( struct inproc_sync *sync ); extern void reset_inproc_sync( struct inproc_sync *sync );
diff --git a/server/protocol.def b/server/protocol.def index 59c6436fa88..09435813dc0 100644 --- a/server/protocol.def +++ b/server/protocol.def @@ -4126,8 +4126,8 @@ struct handle_info
enum inproc_sync_type { - INPROC_SYNC_UNKNOWN = 0, - INPROC_SYNC_EVENT = 1, + INPROC_SYNC_UNKNOWN = 0, + INPROC_SYNC_INTERNAL = 1, };
/* Get the in-process synchronization fd associated with the waitable handle */ diff --git a/server/queue.c b/server/queue.c index 596742bc64c..0e286063285 100644 --- a/server/queue.c +++ b/server/queue.c @@ -328,7 +328,7 @@ static struct msg_queue *create_msg_queue( struct thread *thread, struct thread_ list_init( &queue->expired_timers ); for (i = 0; i < NB_MSG_KINDS; i++) list_init( &queue->msg_list[i] );
- if (get_inproc_device_fd() >= 0 && !(queue->inproc_sync = create_inproc_event_sync( 1, 0 ))) goto error; + if (get_inproc_device_fd() >= 0 && !(queue->inproc_sync = create_inproc_internal_sync( 1, 0 ))) goto error; if (!(queue->shared = alloc_shared_object())) goto error;
SHARED_WRITE_BEGIN( queue->shared, queue_shm_t )
From: Elizabeth Figura zfigura@codeweavers.com
--- dlls/ntdll/unix/sync.c | 37 ++++++++++++++++++++++++++++++++++--- 1 file changed, 34 insertions(+), 3 deletions(-)
diff --git a/dlls/ntdll/unix/sync.c b/dlls/ntdll/unix/sync.c index acdb7b3d9f8..57e67933cd3 100644 --- a/dlls/ntdll/unix/sync.c +++ b/dlls/ntdll/unix/sync.c @@ -309,9 +309,9 @@ static unsigned int validate_open_object_attributes( const OBJECT_ATTRIBUTES *at
struct inproc_sync { - int fd; - unsigned int access; - unsigned int type : 2; + int fd; /* unix file descriptor */ + unsigned int access; /* handle access rights */ + unsigned int type; /* enum inproc_sync_type as short to save space */ };
static void release_inproc_sync( struct inproc_sync *sync ) @@ -355,6 +355,18 @@ static NTSTATUS get_inproc_sync( HANDLE handle, ACCESS_MASK desired_access, stru return STATUS_SUCCESS; }
+extern unsigned int check_signal_access( struct inproc_sync *sync ) +{ + switch (sync->type) + { + case INPROC_SYNC_INTERNAL: + return STATUS_OBJECT_TYPE_MISMATCH; + } + + assert( 0 ); + return STATUS_OBJECT_TYPE_MISMATCH; +} + static NTSTATUS inproc_release_semaphore( HANDLE handle, ULONG count, ULONG *prev_count ) { if (inproc_device_fd < 0) return STATUS_NOT_IMPLEMENTED; @@ -429,7 +441,26 @@ static NTSTATUS inproc_wait( DWORD count, const HANDLE *handles, BOOLEAN wait_an static NTSTATUS inproc_signal_and_wait( HANDLE signal, HANDLE wait, BOOLEAN alertable, const LARGE_INTEGER *timeout ) { + struct inproc_sync stack_signal, stack_wait, *signal_sync = &stack_signal, *wait_sync = &stack_wait; + NTSTATUS ret; + if (inproc_device_fd < 0) return STATUS_NOT_IMPLEMENTED; + + if ((ret = get_inproc_sync( signal, 0, signal_sync ))) return ret; + if ((ret = check_signal_access( signal_sync ))) + { + release_inproc_sync( signal_sync ); + return ret; + } + + if ((ret = get_inproc_sync( wait, SYNCHRONIZE, wait_sync ))) + { + release_inproc_sync( signal_sync ); + return ret; + } + + release_inproc_sync( signal_sync ); + release_inproc_sync( wait_sync ); return STATUS_NOT_IMPLEMENTED; }
From: Elizabeth Figura zfigura@codeweavers.com
Some objects like async or completion waits are internal only, and their sync logic is still a bit complicated as they have various server side effects. They are only waited on alone, we can keep using server-side syncs for them. --- dlls/ntdll/unix/server.c | 15 +++++++++++++++ dlls/ntdll/unix/sync.c | 4 ++-- dlls/ntdll/unix/unix_private.h | 3 ++- 3 files changed, 19 insertions(+), 3 deletions(-)
diff --git a/dlls/ntdll/unix/server.c b/dlls/ntdll/unix/server.c index e8da94ebc64..25cc9bf0cbf 100644 --- a/dlls/ntdll/unix/server.c +++ b/dlls/ntdll/unix/server.c @@ -818,6 +818,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.@) */ diff --git a/dlls/ntdll/unix/sync.c b/dlls/ntdll/unix/sync.c index 57e67933cd3..817bca2a649 100644 --- a/dlls/ntdll/unix/sync.c +++ b/dlls/ntdll/unix/sync.c @@ -2319,7 +2319,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;
@@ -2383,7 +2383,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/unix_private.h b/dlls/ntdll/unix/unix_private.h index 974766b97a7..bd2d6a19334 100644 --- a/dlls/ntdll/unix/unix_private.h +++ b/dlls/ntdll/unix/unix_private.h @@ -230,6 +230,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, @@ -470,7 +471,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)
From: Rémi Bernon rbernon@codeweavers.com
--- dlls/win32u/message.c | 6 +++--- dlls/win32u/ntuser_private.h | 1 - dlls/win32u/sysparams.c | 4 +++- include/ntuser.h | 1 + 4 files changed, 7 insertions(+), 5 deletions(-)
diff --git a/dlls/win32u/message.c b/dlls/win32u/message.c index 035ab6574e4..aafb46a7cc0 100644 --- a/dlls/win32u/message.c +++ b/dlls/win32u/message.c @@ -3103,10 +3103,10 @@ static void process_sent_messages(void) */ static HANDLE get_server_queue_handle(void) { - struct user_thread_info *thread_info = get_user_thread_info(); + struct ntuser_thread_info *thread_info = NtUserGetThreadInfo(); HANDLE ret;
- if (!(ret = thread_info->server_queue)) + if (!(ret = UlongToHandle( thread_info->server_queue ))) { SERVER_START_REQ( get_msg_queue_handle ) { @@ -3114,7 +3114,7 @@ static HANDLE get_server_queue_handle(void) ret = wine_server_ptr_handle( reply->handle ); } SERVER_END_REQ; - thread_info->server_queue = ret; + thread_info->server_queue = HandleToUlong( ret ); if (!ret) ERR( "Cannot get server thread queue\n" ); } return ret; diff --git a/dlls/win32u/ntuser_private.h b/dlls/win32u/ntuser_private.h index f5559c5ca4f..e546703f71b 100644 --- a/dlls/win32u/ntuser_private.h +++ b/dlls/win32u/ntuser_private.h @@ -100,7 +100,6 @@ static inline BOOL is_broadcast( HWND hwnd ) struct user_thread_info { struct ntuser_thread_info client_info; /* Data shared with client */ - HANDLE server_queue; /* Handle to server-side queue */ DWORD last_getmsg_time; /* Get/PeekMessage last request time */ LONGLONG last_driver_time; /* Get/PeekMessage driver event time */ WORD hook_call_depth; /* Number of recursively called hook procs */ diff --git a/dlls/win32u/sysparams.c b/dlls/win32u/sysparams.c index a242bc493fa..5f391a50a86 100644 --- a/dlls/win32u/sysparams.c +++ b/dlls/win32u/sysparams.c @@ -7047,6 +7047,7 @@ BOOL is_exiting_thread( DWORD tid ) static void thread_detach(void) { struct user_thread_info *thread_info = get_user_thread_info(); + HANDLE server_queue = UlongToHandle( thread_info->client_info.server_queue );
destroy_thread_windows(); user_driver->pThreadDetach(); @@ -7054,7 +7055,8 @@ static void thread_detach(void) free( thread_info->rawinput );
cleanup_imm_thread(); - NtClose( thread_info->server_queue ); + if (server_queue) NtClose( server_queue ); + thread_info->client_info.server_queue = 0; free( thread_info->session_data );
exiting_thread_id = 0; diff --git a/include/ntuser.h b/include/ntuser.h index ef09d7e97bb..1de2569bade 100644 --- a/include/ntuser.h +++ b/include/ntuser.h @@ -134,6 +134,7 @@ struct ntuser_thread_info UINT default_imc; /* default input context */ UINT64 client_imm; /* client IMM thread info */ UINT64 wmchar_data; /* client data for WM_CHAR mappings */ + UINT server_queue; /* handle of the server-side queue */ };
static inline struct ntuser_thread_info *NtUserGetThreadInfo(void)
From: Rémi Bernon rbernon@codeweavers.com
--- dlls/ntdll/unix/server.c | 5 +++++ dlls/ntdll/unix/thread.c | 20 ++++++++++++++++++-- dlls/ntdll/unix/unix_private.h | 1 + dlls/ntdll/unix/virtual.c | 9 +++++---- server/inproc_sync.c | 19 ++++++++----------- server/object.h | 1 + server/protocol.def | 2 ++ server/queue.c | 2 +- server/thread.c | 22 +++++++++++++++++++++- server/thread.h | 1 + 10 files changed, 63 insertions(+), 19 deletions(-)
diff --git a/dlls/ntdll/unix/server.c b/dlls/ntdll/unix/server.c index 25cc9bf0cbf..220abacd4d2 100644 --- a/dlls/ntdll/unix/server.c +++ b/dlls/ntdll/unix/server.c @@ -1677,6 +1677,11 @@ size_t server_init_process(void) inproc_device_fd = wine_server_receive_fd( &handle ); assert( handle == reply->inproc_device ); } + if (reply->queue_handle) + { + data->queue_sync_fd = wine_server_receive_fd( &handle ); + assert( handle == reply->queue_handle ); + } } } SERVER_END_REQ; diff --git a/dlls/ntdll/unix/thread.c b/dlls/ntdll/unix/thread.c index 7f61a37bf2b..9eaae846970 100644 --- a/dlls/ntdll/unix/thread.c +++ b/dlls/ntdll/unix/thread.c @@ -1104,6 +1104,7 @@ static void contexts_from_server( CONTEXT *context, struct context_data server_c */ static DECLSPEC_NORETURN void pthread_exit_wrapper( int status ) { + close( ntdll_get_thread_data()->queue_sync_fd ); close( ntdll_get_thread_data()->wait_fd[0] ); close( ntdll_get_thread_data()->wait_fd[1] ); close( ntdll_get_thread_data()->reply_fd ); @@ -1324,7 +1325,7 @@ NTSTATUS WINAPI NtCreateThreadEx( HANDLE *handle, ACCESS_MASK access, OBJECT_ATT struct object_attributes *objattr; struct ntdll_thread_data *thread_data; DWORD tid = 0; - int request_pipe[2]; + int request_pipe[2], queue_sync_fd = -1; TEB *teb; unsigned int status;
@@ -1376,6 +1377,11 @@ NTSTATUS WINAPI NtCreateThreadEx( HANDLE *handle, ACCESS_MASK access, OBJECT_ATT
if (!access) access = THREAD_ALL_ACCESS;
+ /* 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( new_thread ) { req->process = wine_server_obj_handle( process ); @@ -1385,6 +1391,12 @@ NTSTATUS WINAPI NtCreateThreadEx( HANDLE *handle, ACCESS_MASK access, OBJECT_ATT wine_server_add_data( req, objattr, len ); if (!(status = wine_server_call( req ))) { + obj_handle_t token; + if (reply->queue_handle) + { + queue_sync_fd = wine_server_receive_fd( &token ); + assert( token == reply->queue_handle ); + } *handle = wine_server_ptr_handle( reply->handle ); tid = reply->tid; } @@ -1392,6 +1404,8 @@ NTSTATUS WINAPI NtCreateThreadEx( HANDLE *handle, ACCESS_MASK access, OBJECT_ATT } SERVER_END_REQ;
+ server_leave_uninterrupted_section( &fd_cache_mutex, &sigset ); + free( objattr ); if (status) { @@ -1412,7 +1426,8 @@ NTSTATUS WINAPI NtCreateThreadEx( HANDLE *handle, ACCESS_MASK access, OBJECT_ATT set_thread_id( teb, GetCurrentProcessId(), tid );
thread_data = (struct ntdll_thread_data *)&teb->GdiTebBatch; - thread_data->request_fd = request_pipe[1]; + thread_data->request_fd = request_pipe[1]; + thread_data->queue_sync_fd = queue_sync_fd; thread_data->start = start; thread_data->param = param;
@@ -1434,6 +1449,7 @@ done: if (status) { NtClose( *handle ); + if (queue_sync_fd != -1) close( queue_sync_fd ); close( request_pipe[1] ); return status; } diff --git a/dlls/ntdll/unix/unix_private.h b/dlls/ntdll/unix/unix_private.h index bd2d6a19334..98fb4dd6c74 100644 --- a/dlls/ntdll/unix/unix_private.h +++ b/dlls/ntdll/unix/unix_private.h @@ -108,6 +108,7 @@ struct ntdll_thread_data int request_fd; /* fd for sending server requests */ int reply_fd; /* fd for receiving server replies */ int wait_fd[2]; /* fd for sleeping server requests */ + int queue_sync_fd; /* inproc sync fd for message queue */ BOOL allow_writes; /* ThreadAllowWrites flags */ pthread_t pthread_id; /* pthread thread id */ void *kernel_stack; /* stack for thread startup and kernel syscalls */ diff --git a/dlls/ntdll/unix/virtual.c b/dlls/ntdll/unix/virtual.c index 4b4d9d6cd7f..2b21715e1eb 100644 --- a/dlls/ntdll/unix/virtual.c +++ b/dlls/ntdll/unix/virtual.c @@ -4022,10 +4022,11 @@ static TEB *init_teb( void *ptr, BOOL is_wow ) teb->StaticUnicodeString.Buffer = teb->StaticUnicodeBuffer; teb->StaticUnicodeString.MaximumLength = sizeof(teb->StaticUnicodeBuffer); thread_data = (struct ntdll_thread_data *)&teb->GdiTebBatch; - thread_data->request_fd = -1; - thread_data->reply_fd = -1; - thread_data->wait_fd[0] = -1; - thread_data->wait_fd[1] = -1; + thread_data->request_fd = -1; + thread_data->reply_fd = -1; + thread_data->wait_fd[0] = -1; + thread_data->wait_fd[1] = -1; + thread_data->queue_sync_fd = -1; list_add_head( &teb_list, &thread_data->entry ); return teb; } diff --git a/server/inproc_sync.c b/server/inproc_sync.c index afad68664d1..66469bca407 100644 --- a/server/inproc_sync.c +++ b/server/inproc_sync.c @@ -128,21 +128,18 @@ static void inproc_sync_destroy( struct object *obj ) close( sync->fd ); }
-static int get_inproc_sync_fd( struct object *obj, int *type ) +int get_inproc_sync_fd( struct object *obj, int *type ) { + struct inproc_sync *inproc; struct object *sync; int fd = -1;
- if (obj != (struct object *)current->queue) sync = get_obj_sync( obj ); - else sync = thread_queue_inproc_sync( current ); - if (!sync) return -1; + if (!(sync = get_obj_sync( obj ))) return -1; + assert( sync->ops == &inproc_sync_ops );
- if (sync->ops == &inproc_sync_ops) - { - struct inproc_sync *inproc = (struct inproc_sync *)sync; - *type = inproc->type; - fd = inproc->fd; - } + inproc = (struct inproc_sync *)sync; + *type = inproc->type; + fd = inproc->fd;
release_object( sync ); return fd; @@ -168,7 +165,7 @@ void reset_inproc_sync( struct inproc_sync *sync ) { }
-static int get_inproc_sync_fd( struct object *obj, int *type ) +int get_inproc_sync_fd( struct object *obj, int *type ) { return -1; } diff --git a/server/object.h b/server/object.h index 203734a565f..46e4ac7e78d 100644 --- a/server/object.h +++ b/server/object.h @@ -247,6 +247,7 @@ extern int get_inproc_device_fd(void); extern struct inproc_sync *create_inproc_internal_sync( int manual, int signaled ); extern void signal_inproc_sync( struct inproc_sync *sync ); extern void reset_inproc_sync( struct inproc_sync *sync ); +extern int get_inproc_sync_fd( struct object *obj, int *type );
/* serial functions */
diff --git a/server/protocol.def b/server/protocol.def index 09435813dc0..2bcdf0f1c9e 100644 --- a/server/protocol.def +++ b/server/protocol.def @@ -1091,6 +1091,7 @@ struct obj_locator @REPLY thread_id_t tid; /* thread id */ obj_handle_t handle; /* thread handle (in the current process) */ + obj_handle_t queue_handle; /* inproc queue fd in flight with this handle */ @END
@@ -1127,6 +1128,7 @@ struct obj_locator timeout_t server_start; /* server start time */ unsigned int session_id; /* process session id */ obj_handle_t inproc_device;/* inproc device fd in flight with this handle */ + obj_handle_t queue_handle; /* inproc queue fd in flight with this handle */ data_size_t info_size; /* total size of startup info */ VARARG(machines,ushorts); /* array of supported machines */ @END diff --git a/server/queue.c b/server/queue.c index 0e286063285..7eb10913cda 100644 --- a/server/queue.c +++ b/server/queue.c @@ -328,7 +328,7 @@ static struct msg_queue *create_msg_queue( struct thread *thread, struct thread_ list_init( &queue->expired_timers ); for (i = 0; i < NB_MSG_KINDS; i++) list_init( &queue->msg_list[i] );
- if (get_inproc_device_fd() >= 0 && !(queue->inproc_sync = create_inproc_internal_sync( 1, 0 ))) goto error; + if (thread->queue_sync) queue->inproc_sync = (struct inproc_sync *)grab_object( thread->queue_sync ); if (!(queue->shared = alloc_shared_object())) goto error;
SHARED_WRITE_BEGIN( queue->shared, queue_shm_t ) diff --git a/server/thread.c b/server/thread.c index 64cd1a74702..ccb253e7ce5 100644 --- a/server/thread.c +++ b/server/thread.c @@ -397,6 +397,7 @@ static inline void init_thread_structure( struct thread *thread ) int i;
thread->sync = NULL; + thread->queue_sync = NULL; thread->unix_pid = -1; /* not known yet */ thread->unix_tid = -1; /* not known yet */ thread->context = NULL; @@ -560,6 +561,10 @@ struct thread *create_thread( int fd, struct process *process, const struct secu } if (!(thread->request_fd = create_anonymous_fd( &thread_fd_ops, fd, &thread->obj, 0 ))) goto error; if (!(thread->sync = create_event_sync( 1, 0 ))) goto error; + if (get_inproc_device_fd() >= 0) + { + if (!(thread->queue_sync = create_inproc_internal_sync( 1, 0 ))) goto error; + }
if (process->desktop) { @@ -654,6 +659,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 (thread->queue_sync) release_object( thread->queue_sync ); if (thread->sync) release_object( thread->sync ); }
@@ -1621,6 +1627,7 @@ DECL_HANDLER(new_thread) const struct security_descriptor *sd; const struct object_attributes *objattr = get_req_object_attributes( &sd, &name, NULL ); int request_fd = thread_get_inflight_fd( current, req->request_fd ); + int fd, type;
if (!(process = get_process_from_handle( req->process, 0 ))) { @@ -1665,6 +1672,14 @@ DECL_HANDLER(new_thread) if ((reply->handle = alloc_handle_no_access_check( current->process, thread, req->access, objattr->attributes ))) { + if (request_fd != -1) /* first thread fds will be sent in init_first_thread */ + { + if (thread->queue_sync && (fd = get_inproc_sync_fd( (struct object *)thread->queue_sync, &type )) >= 0) + { + reply->queue_handle = get_thread_id( thread ) | 1; + send_client_fd( thread->process, fd, reply->queue_handle ); + } + } /* thread object will be released when the thread gets killed */ goto done; } @@ -1709,7 +1724,7 @@ static int init_thread( struct thread *thread, int reply_fd, int wait_fd ) DECL_HANDLER(init_first_thread) { struct process *process = current->process; - int fd; + int fd, type;
if (!init_thread( current, req->reply_fd, req->wait_fd )) return;
@@ -1738,6 +1753,11 @@ DECL_HANDLER(init_first_thread) reply->inproc_device = get_process_id( process ) | 1; send_client_fd( process, fd, reply->inproc_device ); } + if (current->queue_sync && (fd = get_inproc_sync_fd( (struct object *)current->queue_sync, &type )) >= 0) + { + reply->queue_handle = get_thread_id( current ) | 1; + send_client_fd( process, fd, reply->queue_handle ); + } }
/* initialize a new thread */ diff --git a/server/thread.h b/server/thread.h index b33a00d9f26..535a168ee1b 100644 --- a/server/thread.h +++ b/server/thread.h @@ -51,6 +51,7 @@ struct thread { struct object obj; /* object header */ struct event_sync *sync; /* sync object for wait/signal */ + struct inproc_sync *queue_sync; /* inproc sync for message queue */ struct list entry; /* entry in system-wide thread list */ struct list proc_entry; /* entry in per-process thread list */ struct list desktop_entry; /* entry in per-desktop thread list */
From: Elizabeth Figura zfigura@codeweavers.com
--- dlls/ntdll/unix/sync.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/dlls/ntdll/unix/sync.c b/dlls/ntdll/unix/sync.c index 817bca2a649..f7efd78b4c4 100644 --- a/dlls/ntdll/unix/sync.c +++ b/dlls/ntdll/unix/sync.c @@ -309,6 +309,7 @@ static unsigned int validate_open_object_attributes( const OBJECT_ATTRIBUTES *at
struct inproc_sync { + LONG refcount; /* reference count of the sync object */ int fd; /* unix file descriptor */ unsigned int access; /* handle access rights */ unsigned int type; /* enum inproc_sync_type as short to save space */ @@ -316,7 +317,9 @@ struct inproc_sync
static void release_inproc_sync( struct inproc_sync *sync ) { - close( sync->fd ); + LONG ref = InterlockedDecrement( &sync->refcount ); + assert( ref >= 0 ); + if (!ref) close( sync->fd ); }
static NTSTATUS get_inproc_sync( HANDLE handle, ACCESS_MASK desired_access, struct inproc_sync *sync ) @@ -335,6 +338,7 @@ static NTSTATUS get_inproc_sync( HANDLE handle, ACCESS_MASK desired_access, stru if (!(ret = wine_server_call( req ))) { obj_handle_t fd_handle; + sync->refcount = 1; sync->fd = wine_server_receive_fd( &fd_handle ); assert( wine_server_ptr_handle(fd_handle) == handle ); sync->access = reply->access;
From: Elizabeth Figura zfigura@codeweavers.com
--- dlls/ntdll/unix/sync.c | 48 ++++++++++++++++++++++++++++++++++++++---- server/inproc_sync.c | 21 ++++++++++++------ server/protocol.def | 7 ++++++ server/queue.c | 10 +++++++++ server/user.h | 2 ++ 5 files changed, 77 insertions(+), 11 deletions(-)
diff --git a/dlls/ntdll/unix/sync.c b/dlls/ntdll/unix/sync.c index f7efd78b4c4..2ea8061e506 100644 --- a/dlls/ntdll/unix/sync.c +++ b/dlls/ntdll/unix/sync.c @@ -359,6 +359,13 @@ static NTSTATUS get_inproc_sync( HANDLE handle, ACCESS_MASK desired_access, stru return STATUS_SUCCESS; }
+static NTSTATUS get_inproc_queue_sync( struct inproc_sync *sync ) +{ + sync->refcount = 2; /* prevent closing */ + sync->fd = ntdll_get_thread_data()->queue_sync_fd; + return STATUS_SUCCESS; +} + extern unsigned int check_signal_access( struct inproc_sync *sync ) { switch (sync->type) @@ -371,6 +378,26 @@ extern unsigned int check_signal_access( struct inproc_sync *sync ) return STATUS_OBJECT_TYPE_MISMATCH; }
+static void select_queue(void) +{ + SERVER_START_REQ( select_inproc_queue ) + { + req->select = 1; + wine_server_call( req ); + } + SERVER_END_REQ; +} + +static void unselect_queue( BOOL signaled ) +{ + SERVER_START_REQ( select_inproc_queue ) + { + req->signaled = signaled; + wine_server_call( req ); + } + SERVER_END_REQ; +} + static NTSTATUS inproc_release_semaphore( HANDLE handle, ULONG count, ULONG *prev_count ) { if (inproc_device_fd < 0) return STATUS_NOT_IMPLEMENTED; @@ -422,7 +449,9 @@ 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 ) { + HANDLE server_queue = UlongToHandle( NtUserGetThreadInfo()->server_queue ); struct inproc_sync *syncs[64], stack[ARRAY_SIZE(syncs)]; + UINT queue = -1; NTSTATUS ret;
if (inproc_device_fd < 0) return STATUS_NOT_IMPLEMENTED; @@ -430,7 +459,8 @@ static NTSTATUS inproc_wait( DWORD count, const HANDLE *handles, BOOLEAN wait_an assert( count <= ARRAY_SIZE(syncs) ); for (int i = 0; i < count; ++i) { - if ((ret = get_inproc_sync( handles[i], SYNCHRONIZE, stack + i ))) + if (server_queue && handles[i] == server_queue && !get_inproc_queue_sync( stack + i )) queue = i; + else if ((ret = get_inproc_sync( handles[i], SYNCHRONIZE, stack + i ))) { while (i--) release_inproc_sync( syncs[i] ); return ret; @@ -438,13 +468,18 @@ static NTSTATUS inproc_wait( DWORD count, const HANDLE *handles, BOOLEAN wait_an syncs[i] = stack + i; }
+ if (queue != -1) select_queue(); + ret = STATUS_NOT_IMPLEMENTED; + if (queue != -1) unselect_queue( ret == queue ); + while (count--) release_inproc_sync( syncs[count] ); - return STATUS_NOT_IMPLEMENTED; + return ret; }
static NTSTATUS inproc_signal_and_wait( HANDLE signal, HANDLE wait, BOOLEAN alertable, const LARGE_INTEGER *timeout ) { + HANDLE server_queue = UlongToHandle( NtUserGetThreadInfo()->server_queue ); struct inproc_sync stack_signal, stack_wait, *signal_sync = &stack_signal, *wait_sync = &stack_wait; NTSTATUS ret;
@@ -457,15 +492,20 @@ static NTSTATUS inproc_signal_and_wait( HANDLE signal, HANDLE wait, return ret; }
- if ((ret = get_inproc_sync( wait, SYNCHRONIZE, wait_sync ))) + if (server_queue && wait == server_queue) get_inproc_queue_sync( wait_sync ); + else if ((ret = get_inproc_sync( wait, SYNCHRONIZE, wait_sync ))) { release_inproc_sync( signal_sync ); return ret; }
+ if (server_queue && wait == server_queue) select_queue(); + ret = STATUS_NOT_IMPLEMENTED; + if (server_queue && wait == server_queue) unselect_queue( !ret ); + release_inproc_sync( signal_sync ); release_inproc_sync( wait_sync ); - return STATUS_NOT_IMPLEMENTED; + return ret; }
diff --git a/server/inproc_sync.c b/server/inproc_sync.c index 66469bca407..6a601eff178 100644 --- a/server/inproc_sync.c +++ b/server/inproc_sync.c @@ -130,18 +130,18 @@ static void inproc_sync_destroy( struct object *obj )
int get_inproc_sync_fd( struct object *obj, int *type ) { - struct inproc_sync *inproc; struct object *sync; int fd = -1;
if (!(sync = get_obj_sync( obj ))) return -1; - assert( sync->ops == &inproc_sync_ops ); - - inproc = (struct inproc_sync *)sync; - *type = inproc->type; - fd = inproc->fd; - + if (sync->ops == &inproc_sync_ops) + { + struct inproc_sync *inproc = (struct inproc_sync *)sync; + *type = inproc->type; + fd = inproc->fd; + } release_object( sync ); + return fd; }
@@ -186,3 +186,10 @@ DECL_HANDLER(get_inproc_sync_fd)
release_object( obj ); } + +DECL_HANDLER(select_inproc_queue) +{ + if (!thread_queue_select( current, req->select )) return; + if (req->select) check_thread_queue_idle( current ); + if (req->signaled) thread_queue_satisfied( current ); +} diff --git a/server/protocol.def b/server/protocol.def index 2bcdf0f1c9e..2a7e3c3cdd2 100644 --- a/server/protocol.def +++ b/server/protocol.def @@ -4139,3 +4139,10 @@ enum inproc_sync_type int type; /* inproc sync type */ unsigned int access; /* handle access rights */ @END + + +/* Begin/end a client-side wait on a message queue */ +@REQ(select_inproc_queue) + int select; /* 1: begin waiting, 0: end waiting */ + int signaled; /* was the queue signaled? */ +@END diff --git a/server/queue.c b/server/queue.c index 7eb10913cda..b0cd37e4684 100644 --- a/server/queue.c +++ b/server/queue.c @@ -1359,6 +1359,16 @@ static void msg_queue_satisfied( struct object *obj, struct wait_queue_entry *en reset_queue_sync( queue ); }
+int thread_queue_select( struct thread *thread, int select ) +{ + return msg_queue_select( thread->queue, select ); +} + +void thread_queue_satisfied( struct thread *thread ) +{ + msg_queue_satisfied( &thread->queue->obj, NULL ); +} + static void msg_queue_destroy( struct object *obj ) { struct msg_queue *queue = (struct msg_queue *)obj; diff --git a/server/user.h b/server/user.h index 0ec6ef3145e..7a97ef3aef5 100644 --- a/server/user.h +++ b/server/user.h @@ -119,6 +119,8 @@ extern void add_queue_hook_count( struct thread *thread, unsigned int index, int extern void inc_queue_paint_count( struct thread *thread, int incr ); extern void queue_cleanup_window( struct thread *thread, user_handle_t win ); extern int init_thread_queue( struct thread *thread ); +extern int thread_queue_select( struct thread *thread, int select ); +extern void thread_queue_satisfied( struct thread *thread ); extern void check_thread_queue_idle( struct thread *thread ); extern struct object *thread_queue_inproc_sync( struct thread *thread ); extern int attach_thread_input( struct thread *thread_from, struct thread *thread_to );
v5: Add a change to request hard RLIMIT_NOFILE on wineserver side, we'll need a lot more fds now. Check that thread has a queue when comparing with handles, introduce the inproc sync refcount to already skip calling get_inproc_sync_fds on queues, and remove unnecessary special case for queues on the server side.