]> sourceware.org Git - newlib-cygwin.git/blob - winsup/cygserver/process.cc
* bsd_mutex.cc (_msleep): Whitespace fix.
[newlib-cygwin.git] / winsup / cygserver / process.cc
1 /* process.cc
2
3 Copyright 2001, 2002, 2003, 2004, 2005 Red Hat Inc.
4
5 Written by Robert Collins <rbtcollins@hotmail.com>
6
7 This file is part of Cygwin.
8
9 This software is a copyrighted work licensed under the terms of the
10 Cygwin license. Please consult the file "CYGWIN_LICENSE" for
11 details. */
12
13 #ifdef __OUTSIDE_CYGWIN__
14 #include "woutsup.h"
15
16 #include <sys/types.h>
17
18 #include <assert.h>
19 #include <stdlib.h>
20
21 #include "process.h"
22
23 /*****************************************************************************/
24
25 #define elements(ARRAY) (sizeof (ARRAY) / sizeof (*ARRAY))
26
27 /*****************************************************************************/
28
29 process_cleanup::~process_cleanup ()
30 {
31 delete _process;
32 }
33
34 void
35 process_cleanup::process ()
36 {
37 _process->cleanup ();
38 }
39
40 /*****************************************************************************/
41
42 process::process (const pid_t cygpid, const DWORD winpid, HANDLE signal_arrived)
43 : _cygpid (cygpid),
44 _winpid (winpid),
45 _hProcess (NULL),
46 _signal_arrived (INVALID_HANDLE_VALUE),
47 _cleaning_up (false),
48 _exit_status (STILL_ACTIVE),
49 _routines_head (NULL),
50 _next (NULL)
51 {
52 _hProcess = OpenProcess (PROCESS_ALL_ACCESS, FALSE, winpid);
53 if (!_hProcess)
54 {
55 system_printf ("unable to obtain handle for new cache process %d(%lu)",
56 _cygpid, _winpid);
57 _hProcess = INVALID_HANDLE_VALUE;
58 _exit_status = 0;
59 }
60 else
61 debug_printf ("got handle %p for new cache process %d(%lu)",
62 _hProcess, _cygpid, _winpid);
63 if (!signal_arrived)
64 system_printf ("signal_arrived NULL for process %d(%lu)",
65 _cygpid, _winpid);
66 else if (signal_arrived != INVALID_HANDLE_VALUE)
67 {
68 if (!DuplicateHandle (_hProcess, signal_arrived,
69 GetCurrentProcess (), &_signal_arrived,
70 0, FALSE, DUPLICATE_SAME_ACCESS))
71 system_printf ("error getting signal_arrived to server (%lu)",
72 GetLastError ());
73 _signal_arrived = INVALID_HANDLE_VALUE;
74 }
75 InitializeCriticalSection (&_access);
76 debug ("initialized (%lu)", _cygpid);
77 }
78
79 process::~process ()
80 {
81 debug ("deleting (%lu)", _cygpid);
82 DeleteCriticalSection (&_access);
83 CloseHandle (_signal_arrived);
84 CloseHandle (_hProcess);
85 }
86
87 /* No need to be thread-safe as this is only ever called by
88 * process_cache::remove_process (). If it has to be made thread-safe
89 * later on, it should not use the `access' critical section as that
90 * is held by the client request handlers for an arbitrary length of
91 * time, i.e. while they do whatever processing is required for a
92 * client request.
93 */
94 DWORD
95 process::check_exit_code ()
96 {
97 if (_hProcess && _hProcess != INVALID_HANDLE_VALUE
98 && _exit_status == STILL_ACTIVE
99 && !GetExitCodeProcess (_hProcess, &_exit_status))
100 {
101 system_printf ("failed to retrieve exit code for %d(%lu), error = %lu",
102 _cygpid, _winpid, GetLastError ());
103 _hProcess = INVALID_HANDLE_VALUE;
104 }
105 return _exit_status;
106 }
107
108 bool
109 process::add (cleanup_routine *const entry)
110 {
111 assert (entry);
112
113 bool res = false;
114 hold ();
115
116 if (!_cleaning_up)
117 {
118 entry->_next = _routines_head;
119 _routines_head = entry;
120 res = true;
121 }
122
123 release ();
124 return res;
125 }
126
127 bool
128 process::remove (const cleanup_routine *const entry)
129 {
130 assert (entry);
131
132 bool res = false;
133 hold ();
134
135 if (!_cleaning_up)
136 {
137 cleanup_routine *previous = NULL;
138
139 for (cleanup_routine *ptr = _routines_head;
140 ptr;
141 previous = ptr, ptr = ptr->_next)
142 {
143 if (*ptr == *entry)
144 {
145 if (previous)
146 previous->_next = ptr->_next;
147 else
148 _routines_head = ptr->_next;
149
150 delete ptr;
151 res = true;
152 break;
153 }
154 }
155 }
156
157 release ();
158 return res;
159 }
160
161 /* This is single threaded. It's called after the process is removed
162 * from the cache, but inserts may be attemped by worker threads that
163 * have a pointer to it.
164 */
165 void
166 process::cleanup ()
167 {
168 hold ();
169 assert (!is_active ());
170 assert (!_cleaning_up);
171 InterlockedExchange (&_cleaning_up, true);
172 cleanup_routine *entry = _routines_head;
173 _routines_head = NULL;
174 release ();
175
176 while (entry)
177 {
178 cleanup_routine *const ptr = entry;
179 entry = entry->_next;
180 ptr->cleanup (this);
181 delete ptr;
182 }
183 }
184
185 /*****************************************************************************/
186
187 void
188 process_cache::submission_loop::request_loop ()
189 {
190 assert (this);
191 assert (_cache);
192 assert (_interrupt_event);
193
194 while (_running)
195 _cache->wait_for_processes (_interrupt_event);
196 }
197
198 /*****************************************************************************/
199
200 process_cache::process_cache (const unsigned int initial_workers)
201 : _queue (initial_workers),
202 _submitter (this, &_queue), // true == interruptible
203 _processes_count (0),
204 _processes_head (NULL),
205 _cache_add_trigger (NULL)
206 {
207 /* there can only be one */
208 InitializeCriticalSection (&_cache_write_access);
209
210 _cache_add_trigger = CreateEvent (NULL, // SECURITY_ATTRIBUTES
211 FALSE, // Auto-reset
212 FALSE, // Initially non-signalled
213 NULL); // Anonymous
214
215 if (!_cache_add_trigger)
216 {
217 system_printf ("failed to create cache add trigger, error = %lu",
218 GetLastError ());
219 abort ();
220 }
221
222 _queue.add_submission_loop (&_submitter);
223 }
224
225 process_cache::~process_cache ()
226 {
227 (void) CloseHandle (_cache_add_trigger);
228 DeleteCriticalSection (&_cache_write_access);
229 }
230
231 /* This returns the process object to the caller already locked, that
232 * is, with the object's `access' critical region entered. Thus the
233 * caller must unlock the object when it's finished with it (via
234 * process::release ()). It must then not try to access the object
235 * afterwards, except by going through this routine again, as it may
236 * have been deleted once it has been unlocked.
237 */
238 class process *
239 process_cache::process (const pid_t cygpid, const DWORD winpid,
240 HANDLE signal_arrived)
241 {
242 /* TODO: make this more granular, so a search doesn't involve the
243 * write lock.
244 */
245 EnterCriticalSection (&_cache_write_access);
246 class process *previous = NULL;
247 class process *entry = find (winpid, &previous);
248
249 if (!entry)
250 {
251 if (_processes_count + SPECIALS_COUNT >= MAXIMUM_WAIT_OBJECTS)
252 {
253 LeaveCriticalSection (&_cache_write_access);
254 system_printf (("process limit (%d processes) reached; "
255 "new connection refused for %d(%lu)"),
256 MAXIMUM_WAIT_OBJECTS - SPECIALS_COUNT,
257 cygpid, winpid);
258 return NULL;
259 }
260
261 entry = new class process (cygpid, winpid, signal_arrived);
262 if (!entry->is_active ())
263 {
264 LeaveCriticalSection (&_cache_write_access);
265 delete entry;
266 return NULL;
267 }
268
269 if (previous)
270 {
271 entry->_next = previous->_next;
272 previous->_next = entry;
273 }
274 else
275 {
276 entry->_next = _processes_head;
277 _processes_head = entry;
278 }
279
280 _processes_count += 1;
281 SetEvent (_cache_add_trigger);
282 }
283
284 entry->hold (); // To be released by the caller.
285 LeaveCriticalSection (&_cache_write_access);
286 assert (entry);
287 assert (entry->_winpid == winpid);
288 return entry;
289 }
290
291 void
292 process_cache::wait_for_processes (const HANDLE interrupt_event)
293 {
294 // Update `_wait_array' with handles of all current processes.
295 const size_t count = sync_wait_array (interrupt_event);
296
297 debug_printf ("waiting on %u objects in total (%u processes)",
298 count, _processes_count);
299
300 const DWORD rc = WaitForMultipleObjects (count, _wait_array,
301 FALSE, INFINITE);
302
303 if (rc == WAIT_FAILED)
304 {
305 system_printf ("could not wait on the process handles, error = %lu",
306 GetLastError ());
307 abort ();
308 }
309
310 const size_t start = rc - WAIT_OBJECT_0;
311
312 if (rc < WAIT_OBJECT_0 || start > count)
313 {
314 system_printf (("unexpected return code %rc "
315 "from WaitForMultipleObjects: "
316 "expected [%u .. %u)"),
317 rc, WAIT_OBJECT_0, WAIT_OBJECT_0 + count);
318 abort ();
319 }
320
321 // Tell all the processes, from the signalled point up, the bad news.
322 for (size_t index = start; index != count; index++)
323 if (_process_array[index])
324 check_and_remove_process (index);
325 }
326
327 /*
328 * process_cache::sync_wait_array ()
329 *
330 * Fill-in the wait array with the handles that the cache needs to wait on.
331 * These handles are:
332 * - the process_process_param's interrupt event
333 * - the process_cache's cache_add_trigger event
334 * - the handle for each live process in the cache.
335 *
336 * Return value: the number of live handles in the array.
337 */
338
339 size_t
340 process_cache::sync_wait_array (const HANDLE interrupt_event)
341 {
342 assert (this);
343 assert (_cache_add_trigger && _cache_add_trigger != INVALID_HANDLE_VALUE);
344 assert (interrupt_event && interrupt_event != INVALID_HANDLE_VALUE);
345
346 EnterCriticalSection (&_cache_write_access);
347
348 assert (_processes_count + SPECIALS_COUNT <= elements (_wait_array));
349
350 size_t index = 0;
351
352 for (class process *ptr = _processes_head; ptr; ptr = ptr->_next)
353 {
354 assert (ptr->_hProcess && ptr->_hProcess != INVALID_HANDLE_VALUE);
355 assert (ptr->is_active ());
356
357 _wait_array[index] = ptr->handle ();
358 _process_array[index++] = ptr;
359
360 assert (index <= elements (_wait_array));
361 }
362
363 /* Sorry for shouting, but THESE MUST BE ADDED AT THE END! */
364 /* Well, not strictly `must', but it's more efficient if they are :-) */
365
366 _wait_array[index] = interrupt_event;
367 _process_array[index++] = NULL;
368
369 _wait_array[index] = _cache_add_trigger;
370 _process_array[index++] = NULL;
371
372 /* Phew, back to normal volume now. */
373
374 assert (index <= elements (_wait_array));
375
376 LeaveCriticalSection (&_cache_write_access);
377
378 return index;
379 }
380
381 void
382 process_cache::check_and_remove_process (const size_t index)
383 {
384 assert (this);
385 assert (index < elements (_wait_array) - SPECIALS_COUNT);
386
387 class process *const process = _process_array[index];
388
389 assert (process);
390 assert (process->handle () == _wait_array[index]);
391
392 if (process->check_exit_code () == STILL_ACTIVE)
393 return;
394
395 debug_printf ("process %d(%lu) has left the building ($? = %lu)",
396 process->_cygpid, process->_winpid, process->_exit_status);
397
398 /* Unlink the process object from the process list. */
399
400 EnterCriticalSection (&_cache_write_access);
401
402 class process *previous = NULL;
403
404 const class process *const tmp = find (process->_winpid, &previous);
405
406 assert (tmp == process);
407 assert (previous ? previous->_next == process : _processes_head == process);
408
409 if (previous)
410 previous->_next = process->_next;
411 else
412 _processes_head = process->_next;
413
414 _processes_count -= 1;
415 LeaveCriticalSection (&_cache_write_access);
416
417 /* Schedule any cleanup tasks for this process. */
418 _queue.add (new process_cleanup (process));
419 }
420
421 class process *
422 process_cache::find (const DWORD winpid, class process **previous)
423 {
424 if (previous)
425 *previous = NULL;
426
427 for (class process *ptr = _processes_head; ptr; ptr = ptr->_next)
428 if (ptr->_winpid == winpid)
429 return ptr;
430 else if (ptr->_winpid > winpid) // The list is sorted by winpid.
431 return NULL;
432 else if (previous)
433 *previous = ptr;
434
435 return NULL;
436 }
437
438 /*****************************************************************************/
439 #endif /* __OUTSIDE_CYGWIN__ */
This page took 0.053138 seconds and 5 git commands to generate.