3 Copyright 2001, 2002 Red Hat Inc.
5 Written by Robert Collins <rbtcollins@hotmail.com>
7 This file is part of Cygwin.
9 This software is a copyrighted work licensed under the terms of the
10 Cygwin license. Please consult the file "CYGWIN_LICENSE" for
15 #include <sys/types.h>
24 /*****************************************************************************/
26 #define elements(ARRAY) (sizeof (ARRAY) / sizeof (*ARRAY))
28 /*****************************************************************************/
30 process_cleanup::~process_cleanup ()
36 process_cleanup::process ()
41 /*****************************************************************************/
44 cleanup_routine::~cleanup_routine ()
48 /*****************************************************************************/
50 process::process (const pid_t cygpid, const DWORD winpid)
55 _exit_status (STILL_ACTIVE),
56 _routines_head (NULL),
59 _hProcess = OpenProcess (PROCESS_ALL_ACCESS, FALSE, winpid);
62 system_printf ("unable to obtain handle for new cache process %d(%lu)",
64 _hProcess = INVALID_HANDLE_VALUE;
68 debug_printf ("got handle %p for new cache process %d(%lu)",
69 _hProcess, _cygpid, _winpid);
70 InitializeCriticalSection (&_access);
75 DeleteCriticalSection (&_access);
76 (void) CloseHandle (_hProcess);
79 /* No need to be thread-safe as this is only ever called by
80 * process_cache::remove_process (). If it has to be made thread-safe
81 * later on, it should not use the `access' critical section as that
82 * is held by the client request handlers for an arbitrary length of
83 * time, i.e. while they do whatever processing is required for a
87 process::check_exit_code ()
89 if (_hProcess && _hProcess != INVALID_HANDLE_VALUE
90 && _exit_status == STILL_ACTIVE
91 && !GetExitCodeProcess (_hProcess, &_exit_status))
93 system_printf ("failed to retrieve exit code for %d(%lu), error = %lu",
94 _cygpid, _winpid, GetLastError ());
95 _hProcess = INVALID_HANDLE_VALUE;
101 process::add (cleanup_routine *const entry)
106 EnterCriticalSection (&_access);
110 entry->_next = _routines_head;
111 _routines_head = entry;
115 LeaveCriticalSection (&_access);
120 process::remove (const cleanup_routine *const entry)
125 EnterCriticalSection (&_access);
129 cleanup_routine *previous = NULL;
131 for (cleanup_routine *ptr = _routines_head;
133 previous = ptr, ptr = ptr->_next)
138 previous->_next = ptr->_next;
140 _routines_head = ptr->_next;
149 LeaveCriticalSection (&_access);
153 /* This is single threaded. It's called after the process is removed
154 * from the cache, but inserts may be attemped by worker threads that
155 * have a pointer to it.
160 EnterCriticalSection (&_access);
161 assert (!is_active ());
162 assert (!_cleaning_up);
163 InterlockedExchange (&_cleaning_up, true);
164 cleanup_routine *entry = _routines_head;
165 _routines_head = NULL;
166 LeaveCriticalSection (&_access);
170 cleanup_routine *const ptr = entry;
171 entry = entry->_next;
177 /*****************************************************************************/
180 process_cache::submission_loop::request_loop ()
184 assert (_interrupt_event);
187 _cache->wait_for_processes (_interrupt_event);
190 /*****************************************************************************/
192 process_cache::process_cache (const unsigned int initial_workers)
193 : _queue (initial_workers),
194 _submitter (this, &_queue), // true == interruptible
195 _processes_count (0),
196 _processes_head (NULL),
197 _cache_add_trigger (NULL)
199 /* there can only be one */
200 InitializeCriticalSection (&_cache_write_access);
202 _cache_add_trigger = CreateEvent (NULL, // SECURITY_ATTRIBUTES
204 FALSE, // Initially non-signalled
207 if (!_cache_add_trigger)
209 system_printf ("failed to create cache add trigger, error = %lu",
214 _queue.add_submission_loop (&_submitter);
217 process_cache::~process_cache ()
219 (void) CloseHandle (_cache_add_trigger);
220 DeleteCriticalSection (&_cache_write_access);
223 /* This returns the process object to the caller already locked, that
224 * is, with the object's `access' critical region entered. Thus the
225 * caller must unlock the object when it's finished with it (via
226 * process::release ()). It must then not try to access the object
227 * afterwards, except by going through this routine again, as it may
228 * have been deleted once it has been unlocked.
231 process_cache::process (const pid_t cygpid, const DWORD winpid)
233 /* TODO: make this more granular, so a search doesn't involve the
236 EnterCriticalSection (&_cache_write_access);
237 class process *previous = NULL;
238 class process *entry = find (winpid, &previous);
242 if (_processes_count + SPECIALS_COUNT >= MAXIMUM_WAIT_OBJECTS)
244 LeaveCriticalSection (&_cache_write_access);
245 system_printf (("process limit (%d processes) reached; "
246 "new connection refused for %d(%lu)"),
247 MAXIMUM_WAIT_OBJECTS - SPECIALS_COUNT,
253 entry = new class process (cygpid, winpid);
254 if (!entry->is_active ())
256 LeaveCriticalSection (&_cache_write_access);
264 entry->_next = previous->_next;
265 previous->_next = entry;
269 entry->_next = _processes_head;
270 _processes_head = entry;
273 _processes_count += 1;
274 SetEvent (_cache_add_trigger);
277 EnterCriticalSection (&entry->_access); // To be released by the caller.
278 LeaveCriticalSection (&_cache_write_access);
280 assert (entry->_winpid == winpid);
285 process_cache::wait_for_processes (const HANDLE interrupt_event)
287 // Update `_wait_array' with handles of all current processes.
288 const size_t count = sync_wait_array (interrupt_event);
290 debug_printf ("waiting on %u objects in total (%u processes)",
291 count, _processes_count);
293 const DWORD rc = WaitForMultipleObjects (count, _wait_array,
296 if (rc == WAIT_FAILED)
298 system_printf ("could not wait on the process handles, error = %lu",
303 const size_t start = rc - WAIT_OBJECT_0;
305 if (rc < WAIT_OBJECT_0 || start > count)
307 system_printf (("unexpected return code %rc "
308 "from WaitForMultipleObjects: "
309 "expected [%u .. %u)"),
310 rc, WAIT_OBJECT_0, WAIT_OBJECT_0 + count);
314 // Tell all the processes, from the signalled point up, the bad news.
315 for (size_t index = start; index != count; index++)
316 if (_process_array[index])
317 check_and_remove_process (index);
321 * process_cache::sync_wait_array ()
323 * Fill-in the wait array with the handles that the cache needs to wait on.
325 * - the process_process_param's interrupt event
326 * - the process_cache's cache_add_trigger event
327 * - the handle for each live process in the cache.
329 * Return value: the number of live handles in the array.
333 process_cache::sync_wait_array (const HANDLE interrupt_event)
336 assert (_cache_add_trigger && _cache_add_trigger != INVALID_HANDLE_VALUE);
337 assert (interrupt_event && interrupt_event != INVALID_HANDLE_VALUE);
339 EnterCriticalSection (&_cache_write_access);
341 assert (_processes_count + SPECIALS_COUNT <= elements (_wait_array));
345 for (class process *ptr = _processes_head; ptr; ptr = ptr->_next)
347 assert (ptr->_hProcess && ptr->_hProcess != INVALID_HANDLE_VALUE);
348 assert (ptr->is_active ());
350 _wait_array[index] = ptr->handle ();
351 _process_array[index++] = ptr;
353 assert (index <= elements (_wait_array));
356 /* Sorry for shouting, but THESE MUST BE ADDED AT THE END! */
357 /* Well, not strictly `must', but it's more efficient if they are :-) */
359 _wait_array[index] = interrupt_event;
360 _process_array[index++] = NULL;
362 _wait_array[index] = _cache_add_trigger;
363 _process_array[index++] = NULL;
365 /* Phew, back to normal volume now. */
367 assert (index <= elements (_wait_array));
369 LeaveCriticalSection (&_cache_write_access);
375 process_cache::check_and_remove_process (const size_t index)
378 assert (index < elements (_wait_array) - SPECIALS_COUNT);
380 class process *const process = _process_array[index];
383 assert (process->handle () == _wait_array[index]);
385 if (process->check_exit_code () == STILL_ACTIVE)
388 debug_printf ("process %d(%lu) has left the building ($? = %lu)",
389 process->_cygpid, process->_winpid, process->_exit_status);
391 /* Unlink the process object from the process list. */
393 EnterCriticalSection (&_cache_write_access);
395 class process *previous = NULL;
397 const class process *const tmp = find (process->_winpid, &previous);
399 assert (tmp == process);
400 assert (previous ? previous->_next == process : _processes_head == process);
403 previous->_next = process->_next;
405 _processes_head = process->_next;
407 _processes_count -= 1;
408 LeaveCriticalSection (&_cache_write_access);
410 /* Schedule any cleanup tasks for this process. */
411 _queue.add (new process_cleanup (process));
415 process_cache::find (const DWORD winpid, class process **previous)
420 for (class process *ptr = _processes_head; ptr; ptr = ptr->_next)
421 if (ptr->_winpid == winpid)
423 else if (ptr->_winpid > winpid) // The list is sorted by winpid.
431 /*****************************************************************************/