From: Rémi Bernon <rbernon@codeweavers.com> --- dlls/ntdll/Makefile.in | 1 + dlls/ntdll/unix/env.c | 2 ++ dlls/ntdll/unix/loader.c | 2 +- dlls/ntdll/unix/sched.c | 35 +++++++++++++++++++++++++++++++++ dlls/ntdll/unix/server.c | 24 ++++++++++------------- dlls/ntdll/unix/thread.c | 6 +++++- dlls/ntdll/unix/unix_private.h | 5 ++++- server/process.c | 31 ++++++++++++++++++++++------- server/process.h | 3 +++ server/protocol.def | 11 ++--------- server/request.c | 2 +- server/thread.c | 36 +++++++++++++++++++++++----------- server/thread.h | 5 +++-- 13 files changed, 116 insertions(+), 47 deletions(-) create mode 100644 dlls/ntdll/unix/sched.c diff --git a/dlls/ntdll/Makefile.in b/dlls/ntdll/Makefile.in index ad5c3bdc60f..5515e836129 100644 --- a/dlls/ntdll/Makefile.in +++ b/dlls/ntdll/Makefile.in @@ -56,6 +56,7 @@ SOURCES = \ unix/loadorder.c \ unix/process.c \ unix/registry.c \ + unix/sched.c \ unix/security.c \ unix/serial.c \ unix/server.c \ diff --git a/dlls/ntdll/unix/env.c b/dlls/ntdll/unix/env.c index c8ba6cf4371..39702e5e0a4 100644 --- a/dlls/ntdll/unix/env.c +++ b/dlls/ntdll/unix/env.c @@ -68,6 +68,7 @@ PEB *peb = NULL; WOW_PEB *wow_peb = NULL; USHORT *uctable = NULL, *lctable = NULL; BOOL is_prefix_bootstrap = FALSE; +LONG init_redirect = FALSE; static const WCHAR bootstrapW[] = {'W','I','N','E','B','O','O','T','S','T','R','A','P','M','O','D','E'}; @@ -1990,6 +1991,7 @@ static RTL_USER_PROCESS_PARAMETERS *build_initial_params( void **module ) get_initial_console( params ); + init_redirect = NtCurrentTeb64() && NtCurrentTeb64()->TlsSlots[WOW64_TLS_FILESYSREDIR]; return params; } diff --git a/dlls/ntdll/unix/loader.c b/dlls/ntdll/unix/loader.c index 20b050c3999..bfa9121e813 100644 --- a/dlls/ntdll/unix/loader.c +++ b/dlls/ntdll/unix/loader.c @@ -1873,7 +1873,7 @@ static void start_main_thread(void) load_ntdll(); load_wow64_ntdll( main_image_info.Machine ); load_apiset_dll(); - server_init_process_done(); + server_start_main_thread(); } #ifdef __ANDROID__ diff --git a/dlls/ntdll/unix/sched.c b/dlls/ntdll/unix/sched.c new file mode 100644 index 00000000000..583f7dee1fe --- /dev/null +++ b/dlls/ntdll/unix/sched.c @@ -0,0 +1,35 @@ +/* + * Copyright 2026 Rémi Bernon 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 + */ + +#if 0 +#pragma makedep unix +#endif + +#include "config.h" + +#include <stddef.h> +#include <stdarg.h> + +#include <poll.h> + +#include "unix_private.h" + +void sched_run(void) +{ + for (;;) poll( NULL, 0, -1 ); +} diff --git a/dlls/ntdll/unix/server.c b/dlls/ntdll/unix/server.c index 5ccd649fdc8..0da12a920de 100644 --- a/dlls/ntdll/unix/server.c +++ b/dlls/ntdll/unix/server.c @@ -1668,7 +1668,7 @@ size_t server_init_process(void) reply_pipe = init_thread_pipe(); - SERVER_START_REQ( init_first_thread ) + SERVER_START_REQ( init_process ) { req->unix_pid = getpid(); req->unix_tid = get_unix_tid(); @@ -1695,7 +1695,7 @@ size_t server_init_process(void) SERVER_END_REQ; close( reply_pipe ); - if (ret) server_protocol_error( "init_first_thread failed with status %x\n", ret ); + if (ret) server_protocol_error( "init_process failed with status %x\n", ret ); if (!supported_machines_count) fatal_error( "'%s' is a 64-bit installation, it cannot be used with a 32-bit wineserver.\n", @@ -1730,14 +1730,14 @@ size_t server_init_process(void) /*********************************************************************** - * server_init_process_done + * server_start_main_thread */ -void server_init_process_done(void) +void server_start_main_thread(void) { unsigned int status; - int suspend; FILE_FS_DEVICE_INFORMATION info; struct ntdll_thread_data *thread_data = ntdll_get_thread_data(); + HANDLE handle; if (!get_device_info( initial_cwd, &info ) && (info.Characteristics & FILE_REMOVABLE_MEDIA)) chdir( "/" ); @@ -1750,16 +1750,12 @@ void server_init_process_done(void) thread_data->syscall_table = KeServiceDescriptorTable; thread_data->syscall_trace = TRACE_ON(syscall); - /* Signal the parent process to continue */ - SERVER_START_REQ( init_process_done ) - { - status = wine_server_call( req ); - suspend = reply->suspend; - } - SERVER_END_REQ; - + status = NtCreateThreadEx( &handle, THREAD_ALL_ACCESS, NULL, NtCurrentProcess(), + main_image_info.TransferAddress, peb, 0, 0, 0, 0, NULL ); assert( !status ); - signal_start_thread( main_image_info.TransferAddress, peb, suspend, NtCurrentTeb() ); + NtClose( handle ); + + sched_run(); } diff --git a/dlls/ntdll/unix/thread.c b/dlls/ntdll/unix/thread.c index cba34ed8ca7..1c458c61dae 100644 --- a/dlls/ntdll/unix/thread.c +++ b/dlls/ntdll/unix/thread.c @@ -84,7 +84,7 @@ WINE_DECLARE_DEBUG_CHANNEL(threadname); pthread_key_t teb_key = 0; -static LONG nb_threads = 1; +static LONG nb_threads = 0; static inline int get_unix_exit_code( NTSTATUS status ) { @@ -1436,6 +1436,10 @@ NTSTATUS WINAPI NtCreateThreadEx( HANDLE *handle, ACCESS_MASK access, OBJECT_ATT wow_teb->SkipThreadAttach = teb->SkipThreadAttach; wow_teb->SkipLoaderInit = teb->SkipLoaderInit; } +#ifndef _WIN64 + if (InterlockedExchange( &init_redirect, FALSE ) && teb->GdiBatchCount) + ((TEB64 *)teb->GdiBatchCount)->TlsSlots[WOW64_TLS_FILESYSREDIR] = TRUE; +#endif thread_data = (struct ntdll_thread_data *)&teb->GdiTebBatch; thread_data->request_fd = request_pipe[1]; diff --git a/dlls/ntdll/unix/unix_private.h b/dlls/ntdll/unix/unix_private.h index c547b334fa5..ff705312515 100644 --- a/dlls/ntdll/unix/unix_private.h +++ b/dlls/ntdll/unix/unix_private.h @@ -189,6 +189,7 @@ extern PEB *peb; extern USHORT *uctable; extern USHORT *lctable; extern BOOL is_prefix_bootstrap; +extern LONG init_redirect; extern int main_argc; extern char **main_argv; extern WCHAR **main_wargv; @@ -236,7 +237,7 @@ extern int server_get_unix_fd( HANDLE handle, unsigned int wanted_access, int *u extern int wine_server_receive_fd( obj_handle_t *handle ); extern void process_exit_wrapper( int status ) DECLSPEC_NORETURN; extern size_t server_init_process(void); -extern void server_init_process_done(void); +extern void server_start_main_thread(void); extern void server_init_thread( void *entry_point, BOOL *suspend ); extern int server_pipe( int fd[2] ); @@ -387,6 +388,8 @@ extern NTSTATUS wow64_wine_server_handle_to_fd( void *args ); extern NTSTATUS wow64_wine_spawnvp( void *args ); #endif +extern void sched_run(void); + extern void dbg_init(void); extern void close_inproc_sync( HANDLE handle ); diff --git a/server/process.c b/server/process.c index e54a8f13df8..77755f69bf0 100644 --- a/server/process.c +++ b/server/process.c @@ -665,6 +665,7 @@ struct process *create_process( int fd, struct process *parent, unsigned int fla } process->sync = NULL; process->parent_id = 0; + process->sched_thread = NULL; process->debug_obj = NULL; process->debug_event = NULL; process->handles = NULL; @@ -773,6 +774,15 @@ data_size_t get_process_startup_info_size( struct process *process ) return info->data_size; } +struct security_descriptor *get_first_thread_info( struct process *process, unsigned int *flags ) +{ + struct startup_info *info = process->startup_info; + + if (!info) return NULL; + *flags = process->thread_flags; + return process->thread_sd; +} + /* destroy a process when its refcount is 0 */ static void process_destroy( struct object *obj ) { @@ -780,6 +790,7 @@ static void process_destroy( struct object *obj ) assert( obj->ops == &process_ops ); /* we can't have a thread remaining */ + assert( !process->sched_thread ); assert( list_empty( &process->thread_list )); assert( list_empty( &process->asyncs )); @@ -984,6 +995,11 @@ void kill_console_processes( struct thread *renderer, int exit_code ) /* a process has been killed (i.e. its last thread died) */ static void process_killed( struct process *process ) { + assert( process->sched_thread ); + kill_thread( process->sched_thread, 0 ); + release_object( process->sched_thread ); + process->sched_thread = NULL; + assert( list_empty( &process->thread_list )); process->end_time = current_time; close_process_desktop( process ); @@ -1009,6 +1025,8 @@ static void process_killed( struct process *process ) /* add a thread to a process running threads list */ void add_process_thread( struct process *process, struct thread *thread ) { + assert( process->sched_thread ); + list_add_tail( &process->thread_list, &thread->proc_entry ); if (!process->running_threads++) { @@ -1031,6 +1049,7 @@ void remove_process_thread( struct process *process, struct thread *thread ) { assert( process->running_threads > 0 ); assert( !list_empty( &process->thread_list )); + assert( process->sched_thread ); list_remove( &thread->proc_entry ); @@ -1416,7 +1435,7 @@ DECL_HANDLER(new_process) info->process = (struct process *)grab_object( process ); - if (!(thread = create_thread( -1, process, process->thread_flags, process->thread_sd ))) goto done; + if (!(thread = create_thread( -1, process, 0, NULL ))) goto done; thread->system_regs = current->system_regs; reply->info = alloc_handle( current->process, info, SYNCHRONIZE, 0 ); @@ -1511,24 +1530,19 @@ DECL_HANDLER(get_startup_info) } /* signal the end of the process initialization */ -DECL_HANDLER(init_process_done) +void init_process_done( struct process *process ) { - struct process *process = current->process; - if (is_process_init_done(process)) { set_error( STATUS_INVALID_PARAMETER ); return; } - process->start_time = current_time; - generate_startup_debug_events( process ); set_process_startup_state( process, STARTUP_DONE ); if (process->image_info.subsystem != IMAGE_SUBSYSTEM_WINDOWS_CUI) process->idle_event = create_event( NULL, NULL, 0, 1, 0, NULL ); if (process->debug_obj) set_process_debug_flag( process, 1 ); - reply->suspend = (current->suspend || process->suspend); } /* open a handle to a process */ @@ -1706,6 +1720,7 @@ void set_process_base_priority( struct process *process, int base_priority ) process->base_priority = base_priority; + if ((thread = process->sched_thread)) set_thread_base_priority( thread, thread->base_priority ); LIST_FOR_EACH_ENTRY( thread, &process->thread_list, struct thread, proc_entry ) { set_thread_base_priority( thread, thread->base_priority ); @@ -1751,6 +1766,7 @@ static void set_process_disable_boost( struct process *process, int disable_boos process->disable_boost = disable_boost; + if ((thread = process->sched_thread)) set_thread_disable_boost( thread, disable_boost ); LIST_FOR_EACH_ENTRY( thread, &process->thread_list, struct thread, proc_entry ) { set_thread_disable_boost( thread, disable_boost ); @@ -1769,6 +1785,7 @@ static void set_process_affinity( struct process *process, affinity_t affinity ) process->affinity = affinity; + if ((thread = process->sched_thread)) set_thread_affinity( thread, affinity ); LIST_FOR_EACH_ENTRY( thread, &process->thread_list, struct thread, proc_entry ) { set_thread_affinity( thread, affinity ); diff --git a/server/process.h b/server/process.h index b2292440c8b..d9fc2b9c3a1 100644 --- a/server/process.h +++ b/server/process.h @@ -39,6 +39,7 @@ struct process struct object *sync; /* sync object for wait/signal */ struct list entry; /* entry in system-wide process list */ process_id_t parent_id; /* parent process id (at the time of creation) */ + struct thread *sched_thread; /* sched / unix main thread */ struct list thread_list; /* thread list */ struct debug_obj *debug_obj; /* debug object debugging this process */ struct debug_event *debug_event; /* debug event being sent to debugger */ @@ -101,7 +102,9 @@ extern struct process *create_process( int fd, struct process *parent, unsigned const struct startup_info_data *info, const struct security_descriptor *sd, const obj_handle_t *handles, unsigned int handle_count, struct token *token ); +extern void init_process_done( struct process *process ); extern data_size_t get_process_startup_info_size( struct process *process ); +extern struct security_descriptor *get_first_thread_info( struct process *process, unsigned int *flags ); extern struct thread *get_process_first_thread( struct process *process ); extern struct process *get_process_from_id( process_id_t id ); extern struct process *get_process_from_handle( obj_handle_t handle, unsigned int access ); diff --git a/server/protocol.def b/server/protocol.def index 3d5c14bdc03..4abe764961d 100644 --- a/server/protocol.def +++ b/server/protocol.def @@ -1147,15 +1147,8 @@ typedef volatile struct @END -/* Signal the end of the process initialization */ -@REQ(init_process_done) -@REPLY - int suspend; /* is process suspended? */ -@END - - -/* Initialize the first thread of a new process */ -@REQ(init_first_thread) +/* Initialize a new process */ +@REQ(init_process) int unix_pid; /* Unix pid of new process */ int unix_tid; /* Unix tid of new thread */ int debug_level; /* new debug level */ diff --git a/server/request.c b/server/request.c index 89bba1d9faa..045d13126d8 100644 --- a/server/request.c +++ b/server/request.c @@ -410,7 +410,7 @@ int receive_fd( struct process *process ) struct thread *thread; if (data.tid) thread = get_thread_from_id( data.tid ); - else thread = (struct thread *)grab_object( get_process_first_thread( process )); + else thread = (struct thread *)grab_object( process->sched_thread ); if (!thread || thread->process != process || thread->state == TERMINATED) { diff --git a/server/thread.c b/server/thread.c index 54b1b5e2c00..8005d51a973 100644 --- a/server/thread.c +++ b/server/thread.c @@ -506,9 +506,9 @@ struct thread *create_thread( int fd, struct process *process, unsigned int flag { struct desktop *desktop; struct thread *thread; - int request_pipe[2]; + int is_sched, request_pipe[2]; - if (fd == -1) + if ((is_sched = (fd == -1))) { if (pipe( request_pipe ) == -1) { @@ -540,13 +540,16 @@ struct thread *create_thread( int fd, struct process *process, unsigned int flag init_thread_structure( thread ); + thread->is_sched = is_sched; thread->process = (struct process *)grab_object( process ); thread->desktop = 0; thread->affinity = process->affinity; thread->disable_boost = process->disable_boost; if (!current) current = thread; - list_add_tail( &thread_list, &thread->entry ); + /* avoid adding kernel threads to the global thread list */ + if (thread->is_sched) list_init( &thread->entry ); + else list_add_tail( &thread_list, &thread->entry ); if (sd && !set_sd_defaults_from_token( &thread->obj, sd, OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | @@ -567,7 +570,7 @@ struct thread *create_thread( int fd, struct process *process, unsigned int flag if (!(thread->sync = create_internal_sync( 1, 0 ))) goto error; if (get_inproc_device_fd() >= 0 && !(thread->alert_sync = create_inproc_internal_sync( 1, 0 ))) goto error; - if (process->desktop) + if (!thread->is_sched && process->desktop) { if (!(desktop = get_desktop_obj( process, process->desktop, 0 ))) clear_error(); /* ignore errors */ else @@ -578,7 +581,8 @@ struct thread *create_thread( int fd, struct process *process, unsigned int flag } set_fd_events( thread->request_fd, POLLIN ); /* start listening to events */ - add_process_thread( thread->process, thread ); + if (is_sched) process->sched_thread = (struct thread *)grab_object( thread ); + else add_process_thread( process, thread ); if (flags & THREAD_CREATE_FLAGS_CREATE_SUSPENDED) thread->suspend++; thread->dbg_hidden = !!(flags & THREAD_CREATE_FLAGS_HIDE_FROM_DEBUGGER); @@ -1612,6 +1616,7 @@ int thread_get_inflight_fd( struct thread *thread, int client ) /* kill a thread on the spot */ void kill_thread( struct thread *thread, int violent_death ) { + struct process *process = thread->process; if (thread->state == TERMINATED) return; /* already killed */ thread->state = TERMINATED; thread->exit_time = current_time; @@ -1631,7 +1636,8 @@ void kill_thread( struct thread *thread, int violent_death ) signal_sync( thread->sync ); if (violent_death) send_thread_signal( thread, SIGQUIT ); cleanup_thread( thread ); - remove_process_thread( thread->process, thread ); + if (thread->is_sched) kill_process( process, violent_death ); + else remove_process_thread( process, thread ); release_object( thread ); } @@ -1664,9 +1670,10 @@ DECL_HANDLER(new_thread) struct thread *thread; struct process *process; struct unicode_str name; - const struct security_descriptor *sd; + const struct security_descriptor *sd, *first_sd; const struct object_attributes *objattr = get_req_object_attributes( &sd, &name, NULL ); int request_fd = thread_get_inflight_fd( current, req->request_fd ); + unsigned int flags = req->flags; if (!(process = get_process_from_handle( req->process, 0 ))) { @@ -1693,7 +1700,10 @@ DECL_HANDLER(new_thread) goto done; } - if ((thread = create_thread( request_fd, process, req->flags, sd ))) + /* if first thread, use security descriptor from startup info */ + if ((first_sd = get_first_thread_info( process, &flags ))) sd = first_sd; + + if ((thread = create_thread( request_fd, process, flags, sd ))) { thread->system_regs = current->system_regs; reply->tid = get_thread_id( thread ); @@ -1740,8 +1750,8 @@ static int init_thread( struct thread *thread, int reply_fd, int wait_fd ) return 0; } -/* initialize the first thread of a new process */ -DECL_HANDLER(init_first_thread) +/* initialize a new process */ +DECL_HANDLER(init_process) { struct process *process = current->process; int fd; @@ -1750,6 +1760,7 @@ DECL_HANDLER(init_first_thread) current->unix_pid = process->unix_pid = req->unix_pid; current->unix_tid = req->unix_tid; + process->start_time = current_time; if (!process->parent_id) process->affinity = current->affinity = get_thread_affinity( current ); @@ -1778,6 +1789,7 @@ DECL_HANDLER(init_first_thread) /* initialize a new thread */ DECL_HANDLER(init_thread) { + struct thread *first_thread = get_process_first_thread( current->process ); if (!init_thread( current, req->reply_fd, req->wait_fd )) return; if (!is_valid_address(req->teb)) @@ -1792,11 +1804,13 @@ DECL_HANDLER(init_thread) current->entry_point = req->entry; init_thread_context( current ); - generate_debug_event( current, DbgCreateThreadStateChange, &req->entry ); + if (current == first_thread) generate_startup_debug_events( current->process ); + else generate_debug_event( current, DbgCreateThreadStateChange, &req->entry ); set_thread_base_priority( current, current->base_priority ); set_thread_affinity( current, current->affinity ); reply->suspend = (is_thread_suspended( current ) || current->context != NULL); + if (current == first_thread) init_process_done( current->process ); } /* terminate a thread */ diff --git a/server/thread.h b/server/thread.h index 317979fc6cd..0a1210f82d4 100644 --- a/server/thread.h +++ b/server/thread.h @@ -88,8 +88,9 @@ struct thread int base_priority; /* base priority level (relative to process base priority class) */ int disable_boost; /* disable thread priority boost */ int suspend; /* suspend count */ - int dbg_hidden; /* hidden from debugger */ - int bypass_proc_suspend; /* will still run if the process is suspended */ + unsigned int is_sched:1; /* sched / unix main thread */ + unsigned int dbg_hidden:1; /* hidden from debugger */ + unsigned int bypass_proc_suspend:1; /* will still run if the process is suspended */ obj_handle_t desktop; /* desktop handle */ int desktop_users; /* number of objects using the thread desktop */ timeout_t creation_time; /* Thread creation time */ -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10058