From eb2e77fd6f779cda59c1ef82786bbfde9b962676 Mon Sep 17 00:00:00 2001 From: Alexandre Julliard <julliard@winehq.org> Date: Fri, 4 Jun 1999 19:49:54 +0000 Subject: [PATCH] Made handle table a separate object. Global handle table is no longer bound to a process. Removed special handling of the initial process. --- server/handle.c | 278 +++++++++++++++++++++++++++++------------------ server/handle.h | 5 +- server/process.c | 114 ++++++++++--------- server/process.h | 6 +- server/thread.c | 26 ++--- 5 files changed, 252 insertions(+), 177 deletions(-) diff --git a/server/handle.c b/server/handle.c index 996d1d14c2f..bb6b64e10ac 100644 --- a/server/handle.c +++ b/server/handle.c @@ -23,7 +23,17 @@ struct handle_entry unsigned int access; }; -static struct process *initial_process; +struct handle_table +{ + struct object obj; /* object header */ + struct process *process; /* process owning this table */ + int count; /* number of allocated entries */ + int last; /* last used entry */ + int free; /* first entry that may be free */ + struct handle_entry *entries; /* handle entries */ +}; + +static struct handle_table *global_table; /* reserved handle access rights */ #define RESERVED_SHIFT 25 @@ -39,67 +49,169 @@ static struct process *initial_process; #define MIN_HANDLE_ENTRIES 32 + +static void handle_table_dump( struct object *obj, int verbose ); +static void handle_table_destroy( struct object *obj ); + +static const struct object_ops handle_table_ops = +{ + handle_table_dump, + no_add_queue, + NULL, /* should never get called */ + NULL, /* should never get called */ + NULL, /* should never get called */ + no_read_fd, + no_write_fd, + no_flush, + no_get_file_info, + handle_table_destroy +}; + +/* dump a handle table */ +static void handle_table_dump( struct object *obj, int verbose ) +{ + int i; + struct handle_table *table = (struct handle_table *)obj; + struct handle_entry *entry = table->entries; + + assert( obj->ops == &handle_table_ops ); + + fprintf( stderr, "Handle table last=%d count=%d process=%p\n", + table->last, table->count, table->process ); + if (!verbose) return; + entry = table->entries; + for (i = 0; i <= table->last; i++, entry++) + { + if (!entry->ptr) continue; + fprintf( stderr, "%9d: %p %08x ", i + 1, entry->ptr, entry->access ); + entry->ptr->ops->dump( entry->ptr, 0 ); + } +} + +/* destroy a handle table */ +static void handle_table_destroy( struct object *obj ) +{ + int i; + struct handle_table *table = (struct handle_table *)obj; + struct handle_entry *entry = table->entries; + + assert( obj->ops == &handle_table_ops ); + + for (i = 0; i <= table->last; i++, entry++) + { + struct object *obj = entry->ptr; + entry->ptr = NULL; + if (obj) release_object( obj ); + } + free( table->entries ); + free( table ); +} + +/* allocate a new handle table */ +struct object *alloc_handle_table( struct process *process, int count ) +{ + struct handle_table *table; + + if (count < MIN_HANDLE_ENTRIES) count = MIN_HANDLE_ENTRIES; + if (!(table = alloc_object( sizeof(*table), &handle_table_ops, NULL ))) + return NULL; + table->process = process; + table->count = count; + table->last = -1; + table->free = 0; + if ((table->entries = mem_alloc( count * sizeof(*table->entries) ))) return &table->obj; + release_object( table ); + return NULL; +} + /* grow a handle table */ -/* return 1 if OK, 0 on error */ -static int grow_handle_table( struct process *process ) +static int grow_handle_table( struct handle_table *table ) { struct handle_entry *new_entries; - int count = process->handle_count; + int count = table->count; if (count >= INT_MAX / 2) return 0; count *= 2; - if (!(new_entries = realloc( process->entries, count * sizeof(struct handle_entry) ))) + if (!(new_entries = realloc( table->entries, count * sizeof(struct handle_entry) ))) { SET_ERROR( ERROR_OUTOFMEMORY ); return 0; } - process->handle_count = count; - process->entries = new_entries; + table->entries = new_entries; + table->count = count; return 1; } +/* find the first free entry in the handle table */ +static struct handle_entry *get_free_entry( struct handle_table *table, int *phandle ) +{ + struct handle_entry *entry = table->entries + table->free; + int handle; + + for (handle = table->free; handle <= table->last; handle++, entry++) + if (!entry->ptr) goto found; + if (handle >= table->count) + { + if (!grow_handle_table( table )) return NULL; + entry = table->entries + handle; /* the entries may have moved */ + } + table->last = handle; + found: + table->free = *phandle = handle + 1; /* avoid handle 0 */ + return entry; +} + /* allocate a handle for an object, incrementing its refcount */ /* return the handle, or -1 on error */ int alloc_handle( struct process *process, void *obj, unsigned int access, int inherit ) { + struct handle_table *table = (struct handle_table *)process->handles; struct handle_entry *entry; int handle; + assert( table ); assert( !(access & RESERVED_ALL) ); if (inherit) access |= RESERVED_INHERIT; - /* find the first free entry */ + if (!(entry = get_free_entry( table, &handle ))) return -1; + entry->ptr = grab_object( obj ); + entry->access = access; + return handle; +} - if (!(entry = process->entries)) return -1; - for (handle = 0; handle <= process->handle_last; handle++, entry++) - if (!entry->ptr) goto found; +/* allocate a global handle for an object, incrementing its refcount */ +/* return the handle, or -1 on error */ +static int alloc_global_handle( void *obj, unsigned int access ) +{ + struct handle_entry *entry; + int handle; - if (handle >= process->handle_count) + if (!global_table) { - if (!grow_handle_table( process )) return -1; - entry = process->entries + handle; /* the table may have moved */ + if (!(global_table = (struct handle_table *)alloc_handle_table( NULL, 0 ))) return -1; } - process->handle_last = handle; - - found: + if (!(entry = get_free_entry( global_table, &handle ))) return -1; entry->ptr = grab_object( obj ); entry->access = access; - return handle + 1; /* avoid handle 0 */ + return HANDLE_LOCAL_TO_GLOBAL(handle); } /* return an handle entry, or NULL if the handle is invalid */ static struct handle_entry *get_handle( struct process *process, int handle ) { + struct handle_table *table = (struct handle_table *)process->handles; struct handle_entry *entry; if (HANDLE_IS_GLOBAL(handle)) { handle = HANDLE_GLOBAL_TO_LOCAL(handle); - process = initial_process; + table = global_table; } + if (!table) goto error; handle--; /* handles start at 1 */ - if ((handle < 0) || (handle > process->handle_last)) goto error; - entry = process->entries + handle; + if (handle < 0) goto error; + if (handle > table->last) goto error; + entry = table->entries + handle; if (!entry->ptr) goto error; return entry; @@ -109,59 +221,45 @@ static struct handle_entry *get_handle( struct process *process, int handle ) } /* attempt to shrink a table */ -/* return 1 if OK, 0 on error */ -static int shrink_handle_table( struct process *process ) +static void shrink_handle_table( struct handle_table *table ) { + struct handle_entry *entry = table->entries + table->last; struct handle_entry *new_entries; - struct handle_entry *entry = process->entries + process->handle_last; - int count = process->handle_count; + int count = table->count; - while (process->handle_last >= 0) + while (table->last >= 0) { if (entry->ptr) break; - process->handle_last--; + table->last--; entry--; } - if (process->handle_last >= count / 4) return 1; /* no need to shrink */ - if (count < MIN_HANDLE_ENTRIES * 2) return 1; /* too small to shrink */ + if (table->last >= count / 4) return; /* no need to shrink */ + if (count < MIN_HANDLE_ENTRIES * 2) return; /* too small to shrink */ count /= 2; - if (!(new_entries = realloc( process->entries, - count * sizeof(struct handle_entry) ))) - return 0; - process->handle_count = count; - process->entries = new_entries; - return 1; + if (!(new_entries = realloc( table->entries, count * sizeof(*new_entries) ))) return; + table->count = count; + table->entries = new_entries; } /* copy the handle table of the parent process */ /* return 1 if OK, 0 on error */ -int copy_handle_table( struct process *process, struct process *parent ) +struct object *copy_handle_table( struct process *process, struct process *parent ) { - struct handle_entry *ptr; - int i, count, last; + struct handle_table *parent_table = (struct handle_table *)parent->handles; + struct handle_table *table; + int i; - if (!parent) /* first process */ - { - if (!initial_process) initial_process = process; - count = MIN_HANDLE_ENTRIES; - last = -1; - } - else - { - assert( parent->entries ); - count = parent->handle_count; - last = parent->handle_last; - } + assert( parent_table ); + assert( parent_table->obj.ops == &handle_table_ops ); - if (!(ptr = mem_alloc( count * sizeof(struct handle_entry)))) return 0; - process->entries = ptr; - process->handle_count = count; - process->handle_last = last; + if (!(table = (struct handle_table *)alloc_handle_table( process, parent_table->count ))) + return NULL; - if (last >= 0) + if ((table->last = parent_table->last) >= 0) { - memcpy( ptr, parent->entries, (last + 1) * sizeof(struct handle_entry) ); - for (i = 0; i <= last; i++, ptr++) + struct handle_entry *ptr = table->entries; + memcpy( ptr, parent_table->entries, (table->last + 1) * sizeof(struct handle_entry) ); + for (i = 0; i <= table->last; i++, ptr++) { if (!ptr->ptr) continue; if (ptr->access & RESERVED_INHERIT) grab_object( ptr->ptr ); @@ -169,31 +267,39 @@ int copy_handle_table( struct process *process, struct process *parent ) } } /* attempt to shrink the table */ - shrink_handle_table( process ); - return 1; + shrink_handle_table( table ); + return &table->obj; } /* close a handle and decrement the refcount of the associated object */ /* return 1 if OK, 0 on error */ int close_handle( struct process *process, int handle ) { + struct handle_table *table; struct handle_entry *entry; struct object *obj; - if (HANDLE_IS_GLOBAL(handle)) - { - handle = HANDLE_GLOBAL_TO_LOCAL(handle); - process = initial_process; - } if (!(entry = get_handle( process, handle ))) return 0; if (entry->access & RESERVED_CLOSE_PROTECT) return 0; /* FIXME: error code */ obj = entry->ptr; entry->ptr = NULL; - if (handle-1 == process->handle_last) shrink_handle_table( process ); + table = HANDLE_IS_GLOBAL(handle) ? global_table : (struct handle_table *)process->handles; + if (entry < table->entries + table->free) table->free = entry - table->entries; + if (entry == table->entries + table->last) shrink_handle_table( table ); release_object( obj ); return 1; } +/* close all the global handles */ +void close_global_handles(void) +{ + if (global_table) + { + release_object( global_table ); + global_table = NULL; + } +} + /* retrieve the object corresponding to a handle, incrementing its refcount */ struct object *get_handle_obj( struct process *process, int handle, unsigned int access, const struct object_ops *ops ) @@ -259,33 +365,15 @@ int duplicate_handle( struct process *src, int src_handle, struct process *dst, CLEAR_ERROR(); } } - if (options & DUP_HANDLE_MAKE_GLOBAL) dst = initial_process; access &= ~RESERVED_ALL; - res = alloc_handle( dst, obj, access, inherit ); + if (options & DUP_HANDLE_MAKE_GLOBAL) + res = alloc_global_handle( obj, access ); + else + res = alloc_handle( dst, obj, access, inherit ); release_object( obj ); - if ((options & DUP_HANDLE_MAKE_GLOBAL) && (res != -1)) res = HANDLE_LOCAL_TO_GLOBAL(res); return res; } -/* free the process handle entries */ -void free_handles( struct process *process ) -{ - struct handle_entry *entry; - int handle; - - if (!(entry = process->entries)) return; - for (handle = 0; handle <= process->handle_last; handle++, entry++) - { - struct object *obj = entry->ptr; - entry->ptr = NULL; - if (obj) release_object( obj ); - } - free( process->entries ); - process->handle_count = 0; - process->handle_last = -1; - process->entries = NULL; -} - /* open a new handle to an existing object */ int open_object( const char *name, const struct object_ops *ops, unsigned int access, int inherit ) @@ -305,22 +393,6 @@ int open_object( const char *name, const struct object_ops *ops, return alloc_handle( current->process, obj, access, inherit ); } -/* dump a handle table on stdout */ -void dump_handles( struct process *process ) -{ - struct handle_entry *entry; - int i; - - if (!process->entries) return; - entry = process->entries; - for (i = 0; i <= process->handle_last; i++, entry++) - { - if (!entry->ptr) continue; - printf( "%5d: %p %08x ", i + 1, entry->ptr, entry->access ); - entry->ptr->ops->dump( entry->ptr, 0 ); - } -} - /* close a handle */ DECL_HANDLER(close_handle) { diff --git a/server/handle.h b/server/handle.h index 827dbc67d28..cd42e874230 100644 --- a/server/handle.h +++ b/server/handle.h @@ -27,7 +27,8 @@ extern int duplicate_handle( struct process *src, int src_handle, struct process unsigned int access, int inherit, int options ); extern int open_object( const char *name, const struct object_ops *ops, unsigned int access, int inherit ); -extern int copy_handle_table( struct process *process, struct process *parent ); -extern void free_handles( struct process *process ); +extern struct object *alloc_handle_table( struct process *process, int count ); +extern struct object *copy_handle_table( struct process *process, struct process *parent ); +extern void close_global_handles(void); #endif /* __WINE_SERVER_HANDLE_H */ diff --git a/server/process.c b/server/process.c index f7617f0734e..125b0897764 100644 --- a/server/process.c +++ b/server/process.c @@ -24,8 +24,7 @@ /* process structure */ -static struct process initial_process; -static struct process *first_process = &initial_process; +static struct process *first_process; static int running_processes; /* process operations */ @@ -49,16 +48,20 @@ static const struct object_ops process_ops = }; -/* initialization of a process structure */ -static void init_process( struct process *process ) +/* create a new process */ +static struct process *create_process( struct process *parent, + struct new_process_request *req, const char *cmd_line ) { - init_object( &process->obj, &process_ops, NULL ); + struct process *process; + + if (!(process = alloc_object( sizeof(*process), &process_ops, NULL ))) return NULL; process->next = NULL; process->prev = NULL; process->thread_list = NULL; process->debug_next = NULL; process->debug_prev = NULL; process->debugger = NULL; + process->handles = NULL; process->exit_code = 0x103; /* STILL_ACTIVE */ process->running_threads = 0; process->priority = NORMAL_PRIORITY_CLASS; @@ -68,47 +71,15 @@ static void init_process( struct process *process ) process->console_out = NULL; process->info = NULL; gettimeofday( &process->start_time, NULL ); - /* alloc a handle for the process itself */ - alloc_handle( process, process, PROCESS_ALL_ACCESS, 0 ); -} -/* create the initial process */ -struct process *create_initial_process(void) -{ - struct new_process_request *info; - - copy_handle_table( &initial_process, NULL ); - init_process( &initial_process ); - - if (!alloc_console( &initial_process )) return NULL; - if (!(info = mem_alloc( sizeof(*info) ))) return NULL; - info->start_flags = STARTF_USESTDHANDLES; - info->hstdin = alloc_handle( &initial_process, initial_process.console_in, - GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE, 1 ); - info->hstdout = alloc_handle( &initial_process, initial_process.console_out, - GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE, 1 ); - info->hstderr = alloc_handle( &initial_process, initial_process.console_out, - GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE, 1 ); - info->env_ptr = NULL; - initial_process.info = info; - grab_object( &initial_process ); /* so that we never free it */ - return &initial_process; - -} - -/* create a new process */ -static struct process *create_process( struct new_process_request *req, const char *cmd_line ) -{ - struct process *process = NULL; - struct process *parent = current->process; + if (req->inherit_all) + process->handles = copy_handle_table( process, parent ); + else + process->handles = alloc_handle_table( process, 0 ); + if (!process->handles) goto error; - if (!(process = mem_alloc( sizeof(*process) ))) return NULL; - if (!copy_handle_table( process, req->inherit_all ? parent : NULL )) - { - free( process ); - return NULL; - } - init_process( process ); + /* alloc a handle for the process itself */ + alloc_handle( process, process, PROCESS_ALL_ACCESS, 0 ); if (!(process->info = mem_alloc( sizeof(*process->info) + strlen(cmd_line) + 1 ))) goto error; memcpy( process->info, req, sizeof(*req) ); @@ -138,33 +109,60 @@ static struct process *create_process( struct new_process_request *req, const ch /* attach to the debugger if requested */ if (req->create_flags & DEBUG_PROCESS) debugger_attach( process, current ); - else if (parent->debugger && !(req->create_flags & DEBUG_ONLY_THIS_PROCESS)) + else if (parent && parent->debugger && !(req->create_flags & DEBUG_ONLY_THIS_PROCESS)) debugger_attach( process, parent->debugger ); - process->next = first_process; - first_process->prev = process; + if ((process->next = first_process) != NULL) process->next->prev = process; first_process = process; return process; error: + free_console( process ); + if (process->info) free( process->info ); + if (process->handles) release_object( process->handles ); release_object( process ); return NULL; } +/* create the initial process */ +struct process *create_initial_process(void) +{ + struct process *process; + struct new_process_request req; + + req.inherit = 0; + req.inherit_all = 0; + req.create_flags = CREATE_NEW_CONSOLE; + req.start_flags = STARTF_USESTDHANDLES; + req.hstdin = -1; + req.hstdout = -1; + req.hstderr = -1; + req.cmd_show = 0; + req.env_ptr = NULL; + if ((process = create_process( NULL, &req, "" ))) + { + process->info->hstdin = alloc_handle( process, process->console_in, + GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE, 1 ); + process->info->hstdout = alloc_handle( process, process->console_out, + GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE, 1 ); + process->info->hstderr = alloc_handle( process, process->console_out, + GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE, 1 ); + release_object( process ); + } + return process; +} + /* destroy a process when its refcount is 0 */ static void process_destroy( struct object *obj ) { struct process *process = (struct process *)obj; assert( obj->ops == &process_ops ); - assert( process != &initial_process ); /* we can't have a thread remaining */ assert( !process->thread_list ); if (process->next) process->next->prev = process->prev; if (process->prev) process->prev->next = process->next; else first_process = process->next; - free_console( process ); - free_handles( process ); if (process->info) free( process->info ); if (debug_level) memset( process, 0xbb, sizeof(process) ); /* catch errors */ free( process ); @@ -176,7 +174,9 @@ static void process_dump( struct object *obj, int verbose ) struct process *process = (struct process *)obj; assert( obj->ops == &process_ops ); - printf( "Process next=%p prev=%p\n", process->next, process->prev ); + fprintf( stderr, "Process next=%p prev=%p console=%p/%p handles=%p\n", + process->next, process->prev, process->console_in, process->console_out, + process->handles ); } static int process_signaled( struct object *obj, struct thread *thread ) @@ -209,8 +209,15 @@ static void process_killed( struct process *process, int exit_code ) assert( !process->thread_list ); process->exit_code = exit_code; gettimeofday( &process->end_time, NULL ); + release_object( process->handles ); + process->handles = NULL; + free_console( process ); wake_up( &process->obj, 0 ); - free_handles( process ); + if (!--running_processes) + { + /* last process died, close global handles */ + close_global_handles(); + } } /* add a thread to a process running threads list */ @@ -237,7 +244,6 @@ void remove_process_thread( struct process *process, struct thread *thread ) if (!--process->running_threads) { /* we have removed the last running thread, exit the process */ - running_processes--; process_killed( process, thread->exit_code ); } release_object( thread ); @@ -366,7 +372,7 @@ DECL_HANDLER(new_process) char *cmd_line = (char *)data; CHECK_STRING( "new_process", cmd_line, len ); - if ((process = create_process( req, cmd_line ))) + if ((process = create_process( current->process, req, cmd_line ))) { reply.pid = process; reply.handle = alloc_handle( current->process, process, diff --git a/server/process.h b/server/process.h index b9b44fc1b13..5dbc1ee5bfe 100644 --- a/server/process.h +++ b/server/process.h @@ -15,8 +15,6 @@ /* process structures */ -struct handle_entry; - struct process { struct object obj; /* object header */ @@ -26,9 +24,7 @@ struct process struct process *debug_next; /* per-debugger process list */ struct process *debug_prev; struct thread *debugger; /* thread debugging this process */ - struct handle_entry *entries; /* handle entries */ - int handle_last; /* last valid handle */ - int handle_count; /* allocated table entries */ + struct object *handles; /* handle entries */ int exit_code; /* process exit code */ int running_threads; /* number of threads running in this process */ struct timeval start_time; /* absolute time at process start */ diff --git a/server/thread.c b/server/thread.c index 28cdefb346a..2e4f9e5af25 100644 --- a/server/thread.c +++ b/server/thread.c @@ -74,8 +74,7 @@ static const struct object_ops thread_ops = destroy_thread }; -static struct thread initial_thread; -static struct thread *first_thread = &initial_thread; +static struct thread *first_thread; /* initialization of a thread structure */ static void init_thread( struct thread *thread, int fd ) @@ -104,12 +103,14 @@ static void init_thread( struct thread *thread, int fd ) /* create the initial thread and start the main server loop */ void create_initial_thread( int fd ) { - current = &initial_thread; - init_thread( &initial_thread, fd ); - initial_thread.process = create_initial_process(); - add_process_thread( initial_thread.process, &initial_thread ); - initial_thread.client = add_client( fd, &initial_thread ); - grab_object( &initial_thread ); /* so that we never free it */ + first_thread = mem_alloc( sizeof(*first_thread) ); + assert( first_thread ); + + current = first_thread; + init_thread( first_thread, fd ); + first_thread->process = create_initial_process(); + add_process_thread( first_thread->process, first_thread ); + first_thread->client = add_client( fd, first_thread ); select_loop(); } @@ -131,8 +132,7 @@ static struct thread *create_thread( int fd, void *pid, int suspend, int inherit if (suspend) thread->suspend++; - thread->next = first_thread; - first_thread->prev = thread; + if ((thread->next = first_thread) != NULL) thread->next->prev = thread; first_thread = thread; add_process_thread( process, thread ); @@ -146,7 +146,7 @@ static struct thread *create_thread( int fd, void *pid, int suspend, int inherit return thread; error: - if (current) close_handle( current->process, *handle ); + close_handle( current->process, *handle ); remove_process_thread( process, thread ); release_object( thread ); return NULL; @@ -174,8 +174,8 @@ static void dump_thread( struct object *obj, int verbose ) struct thread *thread = (struct thread *)obj; assert( obj->ops == &thread_ops ); - fprintf( stderr, "Thread pid=%d teb=%p client=%p\n", - thread->unix_pid, thread->teb, thread->client ); + fprintf( stderr, "Thread pid=%d teb=%p state=%d\n", + thread->unix_pid, thread->teb, thread->state ); } static int thread_signaled( struct object *obj, struct thread *thread ) -- GitLab