diff options
Diffstat (limited to 'winsup/cygserver/process.cc')
-rw-r--r-- | winsup/cygserver/process.cc | 500 |
1 files changed, 0 insertions, 500 deletions
diff --git a/winsup/cygserver/process.cc b/winsup/cygserver/process.cc deleted file mode 100644 index cc665d88e..000000000 --- a/winsup/cygserver/process.cc +++ /dev/null @@ -1,500 +0,0 @@ -/* process.cc - - Copyright 2001, 2002, 2003, 2004, 2005 Red Hat Inc. - - Written by Robert Collins <rbtcollins@hotmail.com> - -This file is part of Cygwin. - -This software is a copyrighted work licensed under the terms of the -Cygwin license. Please consult the file "CYGWIN_LICENSE" for -details. */ - -#ifdef __OUTSIDE_CYGWIN__ -#include "woutsup.h" - -#include <sys/types.h> - -#include <assert.h> -#include <stdlib.h> - -#include "process.h" - -/*****************************************************************************/ - -#define elements(ARRAY) (sizeof (ARRAY) / sizeof (*ARRAY)) - -/*****************************************************************************/ - -process_cleanup::~process_cleanup () -{ - delete _process; -} - -void -process_cleanup::process () -{ - _process->cleanup (); -} - -/*****************************************************************************/ - -process::process (const pid_t cygpid, const DWORD winpid, HANDLE signal_arrived) - : _cygpid (cygpid), - _winpid (winpid), - _hProcess (NULL), - _signal_arrived (INVALID_HANDLE_VALUE), - _cleaning_up (false), - _exit_status (STILL_ACTIVE), - _routines_head (NULL), - _next (NULL) -{ - _hProcess = OpenProcess (PROCESS_ALL_ACCESS, FALSE, winpid); - if (!_hProcess) - { - system_printf ("unable to obtain handle for new cache process %d(%lu)", - _cygpid, _winpid); - _hProcess = INVALID_HANDLE_VALUE; - _exit_status = 0; - } - else - debug_printf ("got handle %p for new cache process %d(%lu)", - _hProcess, _cygpid, _winpid); - if (!signal_arrived) - system_printf ("signal_arrived NULL for process %d(%lu)", - _cygpid, _winpid); - else if (signal_arrived != INVALID_HANDLE_VALUE) - { - if (!DuplicateHandle (_hProcess, signal_arrived, - GetCurrentProcess (), &_signal_arrived, - 0, FALSE, DUPLICATE_SAME_ACCESS)) - { - system_printf ("error getting signal_arrived to server (%lu)", - GetLastError ()); - _signal_arrived = INVALID_HANDLE_VALUE; - } - } - InitializeCriticalSection (&_access); - debug ("initialized (%lu)", _cygpid); -} - -process::~process () -{ - debug ("deleting (%lu)", _cygpid); - DeleteCriticalSection (&_access); - if (_signal_arrived && _signal_arrived != INVALID_HANDLE_VALUE) - CloseHandle (_signal_arrived); - CloseHandle (_hProcess); -} - -/* No need to be thread-safe as this is only ever called by - * process_cache::check_and_remove_process (). If it has to be made - * thread-safe later on, it should not use the `access' critical section as - * that is held by the client request handlers for an arbitrary length of time, - * i.e. while they do whatever processing is required for a client request. - */ -DWORD -process::check_exit_code () -{ - if (_hProcess && _hProcess != INVALID_HANDLE_VALUE - && _exit_status == STILL_ACTIVE - && !GetExitCodeProcess (_hProcess, &_exit_status)) - { - system_printf ("failed to retrieve exit code for %d(%lu), error = %lu", - _cygpid, _winpid, GetLastError ()); - _hProcess = INVALID_HANDLE_VALUE; - } - return _exit_status; -} - -bool -process::add (cleanup_routine *const entry) -{ - assert (entry); - - bool res = false; - hold (); - - if (!_cleaning_up) - { - entry->_next = _routines_head; - _routines_head = entry; - res = true; - } - - release (); - return res; -} - -bool -process::remove (const cleanup_routine *const entry) -{ - assert (entry); - - bool res = false; - hold (); - - if (!_cleaning_up) - { - cleanup_routine *previous = NULL; - - for (cleanup_routine *ptr = _routines_head; - ptr; - previous = ptr, ptr = ptr->_next) - { - if (*ptr == *entry) - { - if (previous) - previous->_next = ptr->_next; - else - _routines_head = ptr->_next; - - delete ptr; - res = true; - break; - } - } - } - - release (); - return res; -} - -/* This is single threaded. It's called after the process is removed - * from the cache, but inserts may be attemped by worker threads that - * have a pointer to it. - */ -void -process::cleanup () -{ - hold (); - assert (!is_active ()); - assert (!_cleaning_up); - InterlockedExchange (&_cleaning_up, true); - cleanup_routine *entry = _routines_head; - _routines_head = NULL; - release (); - - while (entry) - { - cleanup_routine *const ptr = entry; - entry = entry->_next; - ptr->cleanup (this); - delete ptr; - } -} - -/*****************************************************************************/ - -void -process_cache::submission_loop::request_loop () -{ - assert (this); - assert (_cache); - assert (_interrupt_event); - - while (_running) - _cache->wait_for_processes (_interrupt_event); -} - -/*****************************************************************************/ - -process_cache::process_cache (const size_t max_procs, - const unsigned int initial_workers) - : _queue (initial_workers), - _submitter (this, &_queue), // true == interruptible - _processes_count (0), - _max_process_count (max_procs), - _processes_head (NULL), - _cache_add_trigger (NULL) -{ - /* there can only be one */ - InitializeCriticalSection (&_cache_write_access); - - _cache_add_trigger = CreateEvent (NULL, // SECURITY_ATTRIBUTES - TRUE, // Manual-reset - FALSE, // Initially non-signalled - NULL); // Anonymous - - if (!_cache_add_trigger) - { - system_printf ("failed to create cache add trigger, error = %lu", - GetLastError ()); - abort (); - } - - _queue.add_submission_loop (&_submitter); -} - -process_cache::~process_cache () -{ - (void) CloseHandle (_cache_add_trigger); - DeleteCriticalSection (&_cache_write_access); -} - -/* This returns the process object to the caller already locked, that - * is, with the object's `access' critical region entered. Thus the - * caller must unlock the object when it's finished with it (via - * process::release ()). It must then not try to access the object - * afterwards, except by going through this routine again, as it may - * have been deleted once it has been unlocked. - */ -class process * -process_cache::process (const pid_t cygpid, const DWORD winpid, - HANDLE signal_arrived) -{ - /* TODO: make this more granular, so a search doesn't involve the - * write lock. - */ - EnterCriticalSection (&_cache_write_access); - class process *previous = NULL; - class process *entry = find (winpid, &previous); - - if (!entry) - { - if (_processes_count >= _max_process_count) - { - LeaveCriticalSection (&_cache_write_access); - system_printf (("process limit (%d processes) reached; " - "new connection refused for %d(%lu)"), - _max_process_count, cygpid, winpid); - return NULL; - } - - entry = new class process (cygpid, winpid, signal_arrived); - if (!entry->is_active ()) - { - LeaveCriticalSection (&_cache_write_access); - delete entry; - return NULL; - } - - if (previous) - { - entry->_next = previous->_next; - previous->_next = entry; - } - else - { - entry->_next = _processes_head; - _processes_head = entry; - } - - _processes_count += 1; - SetEvent (_cache_add_trigger); - } - - entry->hold (); // To be released by the caller. - LeaveCriticalSection (&_cache_write_access); - assert (entry); - assert (entry->_winpid == winpid); - return entry; -} - -struct pcache_wait_t -{ - size_t index; - size_t count; - HANDLE *hdls; -}; - -static DWORD WINAPI -pcache_wait_thread (const LPVOID param) -{ - pcache_wait_t *p = (pcache_wait_t *) param; - - DWORD rc = WaitForMultipleObjects (p->count, p->hdls, FALSE, INFINITE); - ExitThread (rc == WAIT_FAILED ? rc : rc + p->index); -} - -void -process_cache::wait_for_processes (const HANDLE interrupt_event) -{ - // Update `_wait_array' with handles of all current processes. - size_t idx; - const size_t count = sync_wait_array (interrupt_event); - - debug_printf ("waiting on %u objects in total (%u processes)", - count, _processes_count); - - DWORD rc = WAIT_FAILED; - - if (count <= 64) - { - /* If count <= 64, a single WaitForMultipleObjects is sufficient and - we can simply wait in the main thread. */ - rc = WaitForMultipleObjects (count, _wait_array, FALSE, INFINITE); - if (rc == WAIT_FAILED) - { - system_printf ("could not wait on the process handles, error = %lu", - GetLastError ()); - abort (); - } - } - else - { - /* If count > 64 we have to create sub-threads which wait for the - actual wait objects and the main thread waits for the termination - of one of the threads. */ - HANDLE main_wait_array[5] = { NULL }; - DWORD mcount = 0; - - for (idx = 0; idx < count; idx += 64) - { - pcache_wait_t p = { idx, min (count - idx, 64), _wait_array + idx }; - main_wait_array[mcount++] = CreateThread (NULL, 0, pcache_wait_thread, - &p, 0, NULL); - } - - rc = WaitForMultipleObjects (mcount, main_wait_array, FALSE, INFINITE); - if (rc == WAIT_FAILED) - { - system_printf ("could not wait on the process handles, error = %lu", - GetLastError ()); - abort (); - } - - /* Check for error condition on signalled sub-thread. */ - GetExitCodeThread (main_wait_array[rc], &rc); - if (rc == WAIT_FAILED) - { - system_printf ("could not wait on the process handles, error = %lu", - GetLastError ()); - abort (); - } - - /* Wake up all waiting threads. _cache_add_trigger gets reset - in sync_wait_array again. */ - SetEvent (_cache_add_trigger); - WaitForMultipleObjects (mcount, main_wait_array, TRUE, INFINITE); - for (idx = 0; idx < mcount; idx++) - CloseHandle (main_wait_array[idx]); - } - - /* Tell all processes the bad news. This one formerly only checked - processes beginning with the index of the signalled process, but - this can result in processes which are signalled but never removed - under heavy load conditions. */ - for (idx = 0; idx < count; idx++) - if (_process_array[idx]) - check_and_remove_process (idx); -} - -/* - * process_cache::sync_wait_array () - * - * Fill-in the wait array with the handles that the cache needs to wait on. - * These handles are: - * - the process_process_param's interrupt event - * - the process_cache's cache_add_trigger event - * - the handle for each live process in the cache. - * - * Return value: the number of live handles in the array. - */ - -size_t -process_cache::sync_wait_array (const HANDLE interrupt_event) -{ - assert (this); - assert (interrupt_event && interrupt_event != INVALID_HANDLE_VALUE); - - /* Always reset _cache_add_trigger before filling up the array again. */ - ResetEvent (_cache_add_trigger); - - EnterCriticalSection (&_cache_write_access); - - size_t index = 0; - - for (class process *ptr = _processes_head; ptr; ptr = ptr->_next) - { - assert (ptr->_hProcess && ptr->_hProcess != INVALID_HANDLE_VALUE); - assert (ptr->is_active ()); - - _wait_array[index] = ptr->handle (); - _process_array[index++] = ptr; - - if (!ptr->_next || index % 64 == 62) - { - /* Added at the end of each thread's array part for efficiency. */ - _wait_array[index] = interrupt_event; - _process_array[index++] = NULL; - _wait_array[index] = _cache_add_trigger; - _process_array[index++] = NULL; - } - } - - if (!index) - { - /* To get at least *something* to wait for. */ - _wait_array[index] = interrupt_event; - _process_array[index++] = NULL; - _wait_array[index] = _cache_add_trigger; - _process_array[index++] = NULL; - } - - assert (index <= elements (_wait_array)); - - LeaveCriticalSection (&_cache_write_access); - - return index; -} - -void -process_cache::check_and_remove_process (const size_t index) -{ - assert (this); - assert (index < elements (_wait_array) - SPECIALS_COUNT); - - class process *const process = _process_array[index]; - - assert (process); - assert (process->handle () == _wait_array[index]); - - if (process->check_exit_code () == STILL_ACTIVE) - return; - - debug_printf ("process %d(%lu) has left the building ($? = %lu)", - process->_cygpid, process->_winpid, process->_exit_status); - - /* Unlink the process object from the process list. */ - - EnterCriticalSection (&_cache_write_access); - - class process *previous = NULL; - - const class process *const tmp = find (process->_winpid, &previous); - - assert (tmp == process); - assert (previous ? previous->_next == process : _processes_head == process); - - if (previous) - previous->_next = process->_next; - else - _processes_head = process->_next; - - _processes_count -= 1; - LeaveCriticalSection (&_cache_write_access); - - /* Schedule any cleanup tasks for this process. */ - _queue.add (new process_cleanup (process)); -} - -class process * -process_cache::find (const DWORD winpid, class process **previous) -{ - if (previous) - *previous = NULL; - - for (class process *ptr = _processes_head; ptr; ptr = ptr->_next) - if (ptr->_winpid == winpid) - return ptr; - else if (ptr->_winpid > winpid) // The list is sorted by winpid. - return NULL; - else if (previous) - *previous = ptr; - - return NULL; -} - -/*****************************************************************************/ -#endif /* __OUTSIDE_CYGWIN__ */ |