include/ansidecl.h: import from binutils-gdb
[newlib-cygwin.git] / winsup / cygserver / process.cc
blobc26341470fcf21acb5dcf082844282b2d88f79fd
1 /* process.cc
3 Written by Robert Collins <rbtcollins@hotmail.com>
5 This file is part of Cygwin.
7 This software is a copyrighted work licensed under the terms of the
8 Cygwin license. Please consult the file "CYGWIN_LICENSE" for
9 details. */
11 #ifdef __OUTSIDE_CYGWIN__
12 #include "woutsup.h"
14 #include <sys/types.h>
16 #include <assert.h>
17 #include <stdlib.h>
19 #include "process.h"
21 #include "cygserver_ipc.h"
23 /*****************************************************************************/
25 #define elements(ARRAY) (sizeof (ARRAY) / sizeof (*ARRAY))
27 /*****************************************************************************/
29 process_cleanup::~process_cleanup ()
31 delete _process;
34 void
35 process_cleanup::process ()
37 _process->cleanup ();
40 /*****************************************************************************/
42 process::process (const pid_t cygpid, const DWORD winpid)
43 : _cygpid (cygpid),
44 _winpid (winpid),
45 _hProcess (NULL),
46 _cleaning_up (false),
47 _exit_status (STILL_ACTIVE),
48 _routines_head (NULL),
49 _next (NULL)
51 _hProcess = OpenProcess (PROCESS_ALL_ACCESS, FALSE, winpid);
52 if (!_hProcess)
54 system_printf ("unable to obtain handle for new cache process %d(%u)",
55 _cygpid, _winpid);
56 _hProcess = INVALID_HANDLE_VALUE;
57 _exit_status = 0;
59 else
60 debug_printf ("got handle %p for new cache process %d(%u)",
61 _hProcess, _cygpid, _winpid);
62 InitializeCriticalSection (&_access);
63 debug ("initialized (%u)", _cygpid);
66 process::~process ()
68 debug ("deleting (%u)", _cygpid);
69 DeleteCriticalSection (&_access);
70 CloseHandle (_hProcess);
73 /* No need to be thread-safe as this is only ever called by
74 * process_cache::check_and_remove_process (). If it has to be made
75 * thread-safe later on, it should not use the `access' critical section as
76 * that is held by the client request handlers for an arbitrary length of time,
77 * i.e. while they do whatever processing is required for a client request.
79 DWORD
80 process::check_exit_code ()
82 if (_hProcess && _hProcess != INVALID_HANDLE_VALUE
83 && _exit_status == STILL_ACTIVE
84 && !GetExitCodeProcess (_hProcess, &_exit_status))
86 system_printf ("failed to retrieve exit code for %d(%u), error = %u",
87 _cygpid, _winpid, GetLastError ());
88 _hProcess = INVALID_HANDLE_VALUE;
90 return _exit_status;
93 bool
94 process::add (cleanup_routine *const entry)
96 assert (entry);
98 bool res = false;
99 hold ();
101 if (!_cleaning_up)
103 entry->_next = _routines_head;
104 _routines_head = entry;
105 res = true;
108 release ();
109 return res;
112 bool
113 process::remove (const cleanup_routine *const entry)
115 assert (entry);
117 bool res = false;
118 hold ();
120 if (!_cleaning_up)
122 cleanup_routine *previous = NULL;
124 for (cleanup_routine *ptr = _routines_head;
125 ptr;
126 previous = ptr, ptr = ptr->_next)
128 if (*ptr == *entry)
130 if (previous)
131 previous->_next = ptr->_next;
132 else
133 _routines_head = ptr->_next;
135 delete ptr;
136 res = true;
137 break;
142 release ();
143 return res;
146 /* This is single threaded. It's called after the process is removed
147 * from the cache, but inserts may be attemped by worker threads that
148 * have a pointer to it.
150 void
151 process::cleanup ()
153 hold ();
154 assert (!is_active ());
155 assert (!_cleaning_up);
156 InterlockedExchange (&_cleaning_up, true);
157 cleanup_routine *entry = _routines_head;
158 _routines_head = NULL;
159 release ();
161 while (entry)
163 cleanup_routine *const ptr = entry;
164 entry = entry->_next;
165 ptr->cleanup (this);
166 delete ptr;
170 /*****************************************************************************/
172 void
173 process_cache::submission_loop::request_loop ()
175 assert (_cache);
176 assert (_interrupt_event);
178 while (_running)
179 _cache->wait_for_processes (_interrupt_event);
182 /*****************************************************************************/
184 process_cache::process_cache (const size_t max_procs,
185 const unsigned int initial_workers)
186 : _queue (initial_workers),
187 _submitter (this, &_queue), // true == interruptible
188 _processes_count (0),
189 _max_process_count (max_procs),
190 _processes_head (NULL),
191 _cache_add_trigger (NULL)
193 /* there can only be one */
194 InitializeCriticalSection (&_cache_write_access);
196 _cache_add_trigger = CreateEvent (NULL, // SECURITY_ATTRIBUTES
197 TRUE, // Manual-reset
198 FALSE, // Initially non-signalled
199 NULL); // Anonymous
201 if (!_cache_add_trigger)
203 system_printf ("failed to create cache add trigger, error = %u",
204 GetLastError ());
205 abort ();
208 _queue.add_submission_loop (&_submitter);
211 process_cache::~process_cache ()
213 (void) CloseHandle (_cache_add_trigger);
214 DeleteCriticalSection (&_cache_write_access);
217 /* This returns the process object to the caller already locked, that
218 * is, with the object's `access' critical region entered. Thus the
219 * caller must unlock the object when it's finished with it (via
220 * process::release ()). It must then not try to access the object
221 * afterwards, except by going through this routine again, as it may
222 * have been deleted once it has been unlocked.
224 class process *
225 process_cache::process (const pid_t cygpid, const DWORD winpid)
227 /* TODO: make this more granular, so a search doesn't involve the
228 * write lock.
230 EnterCriticalSection (&_cache_write_access);
231 class process *previous = NULL;
232 class process *entry = find (winpid, &previous);
234 if (!entry)
236 if (_processes_count >= _max_process_count)
238 LeaveCriticalSection (&_cache_write_access);
239 system_printf (("process limit (%d processes) reached; "
240 "new connection refused for %d(%u)"),
241 _max_process_count, cygpid, winpid);
242 return NULL;
245 entry = new class process (cygpid, winpid);
246 if (!entry->is_active ())
248 LeaveCriticalSection (&_cache_write_access);
249 delete entry;
250 return NULL;
253 if (previous)
255 entry->_next = previous->_next;
256 previous->_next = entry;
258 else
260 entry->_next = _processes_head;
261 _processes_head = entry;
264 _processes_count += 1;
265 SetEvent (_cache_add_trigger);
268 entry->hold (); // To be released by the caller.
269 LeaveCriticalSection (&_cache_write_access);
270 assert (entry);
271 assert (entry->_winpid == winpid);
272 return entry;
275 struct pcache_wait_t
277 size_t index;
278 size_t count;
279 HANDLE *hdls;
282 static DWORD WINAPI
283 pcache_wait_thread (const LPVOID param)
285 pcache_wait_t *p = (pcache_wait_t *) param;
287 DWORD rc = WaitForMultipleObjects (p->count, p->hdls, FALSE, INFINITE);
288 ExitThread (rc == WAIT_FAILED ? rc : rc + p->index);
291 void
292 process_cache::wait_for_processes (const HANDLE interrupt_event)
294 // Update `_wait_array' with handles of all current processes.
295 size_t idx;
296 const size_t count = sync_wait_array (interrupt_event);
298 debug_printf ("waiting on %u objects in total (%u processes)",
299 count, _processes_count);
301 DWORD rc = WAIT_FAILED;
303 if (count <= 64)
305 /* If count <= 64, a single WaitForMultipleObjects is sufficient and
306 we can simply wait in the main thread. */
307 rc = WaitForMultipleObjects (count, _wait_array, FALSE, INFINITE);
308 if (rc == WAIT_FAILED)
310 system_printf ("could not wait on the process handles, error = %u",
311 GetLastError ());
312 abort ();
315 else
317 /* If count > 64 we have to create sub-threads which wait for the
318 actual wait objects and the main thread waits for the termination
319 of one of the threads. */
320 HANDLE main_wait_array[5] = { NULL };
321 DWORD mcount = 0;
323 for (idx = 0; idx < count; idx += 64)
325 pcache_wait_t p = { idx, min (count - idx, 64), _wait_array + idx };
326 main_wait_array[mcount++] = CreateThread (NULL, 0, pcache_wait_thread,
327 &p, 0, NULL);
330 rc = WaitForMultipleObjects (mcount, main_wait_array, FALSE, INFINITE);
331 if (rc == WAIT_FAILED)
333 system_printf ("could not wait on the process handles, error = %u",
334 GetLastError ());
335 abort ();
338 /* Check for error condition on signalled sub-thread. */
339 GetExitCodeThread (main_wait_array[rc], &rc);
340 if (rc == WAIT_FAILED)
342 system_printf ("could not wait on the process handles, error = %u",
343 GetLastError ());
344 abort ();
347 /* Wake up all waiting threads. _cache_add_trigger gets reset
348 in sync_wait_array again. */
349 SetEvent (_cache_add_trigger);
350 WaitForMultipleObjects (mcount, main_wait_array, TRUE, INFINITE);
351 for (idx = 0; idx < mcount; idx++)
352 CloseHandle (main_wait_array[idx]);
355 /* Tell all processes the bad news. This one formerly only checked
356 processes beginning with the index of the signalled process, but
357 this can result in processes which are signalled but never removed
358 under heavy load conditions. */
359 for (idx = 0; idx < count; idx++)
360 if (_process_array[idx])
361 check_and_remove_process (idx);
365 * process_cache::sync_wait_array ()
367 * Fill-in the wait array with the handles that the cache needs to wait on.
368 * These handles are:
369 * - the process_process_param's interrupt event
370 * - the process_cache's cache_add_trigger event
371 * - the handle for each live process in the cache.
373 * Return value: the number of live handles in the array.
376 size_t
377 process_cache::sync_wait_array (const HANDLE interrupt_event)
379 assert (interrupt_event && interrupt_event != INVALID_HANDLE_VALUE);
381 /* Always reset _cache_add_trigger before filling up the array again. */
382 ResetEvent (_cache_add_trigger);
384 EnterCriticalSection (&_cache_write_access);
386 size_t index = 0;
388 for (class process *ptr = _processes_head; ptr; ptr = ptr->_next)
390 assert (ptr->_hProcess && ptr->_hProcess != INVALID_HANDLE_VALUE);
391 assert (ptr->is_active ());
393 _wait_array[index] = ptr->handle ();
394 _process_array[index++] = ptr;
396 if (!ptr->_next || index % 64 == 62)
398 /* Added at the end of each thread's array part for efficiency. */
399 _wait_array[index] = interrupt_event;
400 _process_array[index++] = NULL;
401 _wait_array[index] = _cache_add_trigger;
402 _process_array[index++] = NULL;
406 if (!index)
408 /* To get at least *something* to wait for. */
409 _wait_array[index] = interrupt_event;
410 _process_array[index++] = NULL;
411 _wait_array[index] = _cache_add_trigger;
412 _process_array[index++] = NULL;
415 assert (index <= elements (_wait_array));
417 LeaveCriticalSection (&_cache_write_access);
419 return index;
422 void
423 process_cache::check_and_remove_process (const size_t index)
425 assert (index < elements (_wait_array) - SPECIALS_COUNT);
427 class process *const process = _process_array[index];
429 assert (process);
430 assert (process->handle () == _wait_array[index]);
432 if (process->check_exit_code () == STILL_ACTIVE)
433 return;
435 debug_printf ("process %d(%u) has left the building ($? = %u)",
436 process->_cygpid, process->_winpid, process->_exit_status);
438 /* Unlink the process object from the process list. */
440 EnterCriticalSection (&_cache_write_access);
442 class process *previous = NULL;
444 const class process *const tmp = find (process->_winpid, &previous);
446 assert (tmp == process);
447 assert (previous ? previous->_next == process : _processes_head == process);
449 if (previous)
450 previous->_next = process->_next;
451 else
452 _processes_head = process->_next;
454 _processes_count -= 1;
455 LeaveCriticalSection (&_cache_write_access);
457 /* Schedule any cleanup tasks for this process. */
458 _queue.add (new process_cleanup (process));
461 class process *
462 process_cache::find (const DWORD winpid, class process **previous)
464 if (previous)
465 *previous = NULL;
467 for (class process *ptr = _processes_head; ptr; ptr = ptr->_next)
468 if (ptr->_winpid == winpid)
469 return ptr;
470 else if (ptr->_winpid > winpid) // The list is sorted by winpid.
471 return NULL;
472 else if (previous)
473 *previous = ptr;
475 return NULL;
478 void
479 thread::dup_signal_arrived ()
481 if (ipcblk && ipcblk->signal_arrived
482 && !DuplicateHandle (client->handle (), ipcblk->signal_arrived,
483 GetCurrentProcess (), &ipcblk->signal_arrived,
484 0, FALSE, DUPLICATE_SAME_ACCESS))
486 system_printf ("error duplicating thread's signal_arrived "
487 "to server (%u)", GetLastError ());
488 ipcblk->signal_arrived = NULL;
492 void
493 thread::close_signal_arrived ()
495 if (ipcblk && ipcblk->signal_arrived)
496 CloseHandle (ipcblk->signal_arrived);
499 /*****************************************************************************/
500 #endif /* __OUTSIDE_CYGWIN__ */