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
11 #ifdef __OUTSIDE_CYGWIN__
14 #include <sys/types.h>
21 #include "cygserver_ipc.h"
23 /*****************************************************************************/
25 #define elements(ARRAY) (sizeof (ARRAY) / sizeof (*ARRAY))
27 /*****************************************************************************/
29 process_cleanup::~process_cleanup ()
35 process_cleanup::process ()
40 /*****************************************************************************/
42 process::process (const pid_t cygpid
, const DWORD winpid
)
47 _exit_status (STILL_ACTIVE
),
48 _routines_head (NULL
),
51 _hProcess
= OpenProcess (PROCESS_ALL_ACCESS
, FALSE
, winpid
);
54 system_printf ("unable to obtain handle for new cache process %d(%u)",
56 _hProcess
= INVALID_HANDLE_VALUE
;
60 debug_printf ("got handle %p for new cache process %d(%u)",
61 _hProcess
, _cygpid
, _winpid
);
62 InitializeCriticalSection (&_access
);
63 debug ("initialized (%u)", _cygpid
);
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.
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
;
94 process::add (cleanup_routine
*const entry
)
103 entry
->_next
= _routines_head
;
104 _routines_head
= entry
;
113 process::remove (const cleanup_routine
*const entry
)
122 cleanup_routine
*previous
= NULL
;
124 for (cleanup_routine
*ptr
= _routines_head
;
126 previous
= ptr
, ptr
= ptr
->_next
)
131 previous
->_next
= ptr
->_next
;
133 _routines_head
= ptr
->_next
;
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.
154 assert (!is_active ());
155 assert (!_cleaning_up
);
156 InterlockedExchange (&_cleaning_up
, true);
157 cleanup_routine
*entry
= _routines_head
;
158 _routines_head
= NULL
;
163 cleanup_routine
*const ptr
= entry
;
164 entry
= entry
->_next
;
170 /*****************************************************************************/
173 process_cache::submission_loop::request_loop ()
176 assert (_interrupt_event
);
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
201 if (!_cache_add_trigger
)
203 system_printf ("failed to create cache add trigger, error = %u",
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.
225 process_cache::process (const pid_t cygpid
, const DWORD winpid
)
227 /* TODO: make this more granular, so a search doesn't involve the
230 EnterCriticalSection (&_cache_write_access
);
231 class process
*previous
= NULL
;
232 class process
*entry
= find (winpid
, &previous
);
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
);
245 entry
= new class process (cygpid
, winpid
);
246 if (!entry
->is_active ())
248 LeaveCriticalSection (&_cache_write_access
);
255 entry
->_next
= previous
->_next
;
256 previous
->_next
= entry
;
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
);
271 assert (entry
->_winpid
== winpid
);
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
);
292 process_cache::wait_for_processes (const HANDLE interrupt_event
)
294 // Update `_wait_array' with handles of all current processes.
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
;
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",
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
};
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
,
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",
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",
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.
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.
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
);
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
;
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
);
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
];
430 assert (process
->handle () == _wait_array
[index
]);
432 if (process
->check_exit_code () == STILL_ACTIVE
)
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
);
450 previous
->_next
= process
->_next
;
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
));
462 process_cache::find (const DWORD winpid
, class process
**previous
)
467 for (class process
*ptr
= _processes_head
; ptr
; ptr
= ptr
->_next
)
468 if (ptr
->_winpid
== winpid
)
470 else if (ptr
->_winpid
> winpid
) // The list is sorted by winpid.
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
;
493 thread::close_signal_arrived ()
495 if (ipcblk
&& ipcblk
->signal_arrived
)
496 CloseHandle (ipcblk
->signal_arrived
);
499 /*****************************************************************************/
500 #endif /* __OUTSIDE_CYGWIN__ */