1 /* cygserver_process.cc
3 Copyright 2001 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
18 #include <sys/types.h>
19 #include <sys/socket.h>
23 #include <threaded_queue.h>
24 #include <cygwin/cygserver_process.h>
26 #define debug_printf if (DEBUG) printf
29 /* the cache structures and classes are designed for one cache per server process.
30 * To make multiple process caches, a redesign will be needed
34 process_cache::process_cache (unsigned int num_initial_workers):
37 /* there can only be one */
38 InitializeCriticalSection (&cache_write_access);
39 if ((cache_add_trigger = CreateEvent (NULL, FALSE, FALSE, NULL)) == NULL)
41 printf ("Failed to create cache add trigger (%lu), terminating\n",
45 initial_workers = num_initial_workers;
48 process_cache::~process_cache ()
53 process_cache::process (long pid)
55 class process *entry = head;
56 /* TODO: make this more granular, so a search doesn't involve the write lock */
57 EnterCriticalSection (&cache_write_access);
60 entry = new class process (pid);
62 (class process *) InterlockedExchangePointer (&head, entry);
63 PulseEvent (cache_add_trigger);
67 while (entry->winpid != pid && entry->next)
69 if (entry->winpid != pid)
71 class process *new_entry = new class process (pid);
73 (class process *) InterlockedExchangePointer (&entry->next,
76 PulseEvent (cache_add_trigger);
79 LeaveCriticalSection (&cache_write_access);
84 request_loop (LPVOID LpParam)
86 class process_process_param *params = (process_process_param *) LpParam;
87 return params->request_loop ();
91 process_cache::process_requests ()
93 class process_process_param *params = new process_process_param;
94 threaded_queue::process_requests (params, request_loop);
98 process_cache::add_task (class process * theprocess)
100 /* safe to not "Try" because workers don't hog this, they wait on the event
102 /* every derived ::add must enter the section! */
103 EnterCriticalSection (&queuelock);
104 queue_request *listrequest = new process_cleanup (theprocess);
105 threaded_queue::add (listrequest);
106 LeaveCriticalSection (&queuelock);
109 /* NOT fully MT SAFE: must be called by only one thread in a program */
111 process_cache::remove_process (class process *theprocess)
113 class process *entry = head;
115 EnterCriticalSection (&cache_write_access);
116 if (entry == theprocess)
118 entry = (class process *) InterlockedExchangePointer (&head, theprocess->next);
119 if (entry != theprocess)
121 printf ("Bug encountered, process cache corrupted\n");
127 while (entry->next && entry->next != theprocess)
129 class process *temp = (class process *) InterlockedExchangePointer (&entry->next, theprocess->next);
130 if (temp != theprocess)
132 printf ("Bug encountered, process cache corrupted\n");
136 LeaveCriticalSection (&cache_write_access);
137 /* Process any cleanup tasks */
138 add_task (theprocess);
142 /* copy <= max_copy HANDLEs to dest[], starting at an offset into _our list_ of
143 * begin_at. (Ie begin_at = 5, the first copied handle is still written to dest[0]
144 * NOTE: Thread safe, but not thread guaranteed - a newly added process may be missed.
145 * Who cares - It'll get caught the next time.
148 process_cache::handle_snapshot (HANDLE * hdest, class process ** edest,
149 ssize_t max_copy, int begin_at)
151 /* TODO:? grab a delete-lock, to prevent deletes during this process ? */
152 class process *entry = head;
153 int count = begin_at;
154 /* skip begin_at entries */
155 while (entry && count)
157 if (entry->exit_code () == STILL_ACTIVE)
161 /* hit the end of the list within begin_at entries */
165 class process **eto = edest;
166 while (entry && count < max_copy)
169 if (entry->exit_code () == STILL_ACTIVE)
171 *hto = entry->handle ();
183 /* global process crit section */
184 static CRITICAL_SECTION process_access;
185 static pthread_once_t process_init;
188 do_process_init (void)
190 InitializeCriticalSection (&process_access);
191 /* we don't have a cache shutdown capability today */
194 process::process (long pid):
195 winpid (pid), next (NULL), cleaning_up (0), head (NULL), _exit_status (STILL_ACTIVE)
197 pthread_once (&process_init, do_process_init);
198 EnterCriticalSection (&process_access);
199 thehandle = OpenProcess (PROCESS_ALL_ACCESS, FALSE, pid);
202 printf ("unable to obtain handle for new cache process %ld\n", pid);
203 thehandle = INVALID_HANDLE_VALUE;
205 debug_printf ("Got handle %p for new cache process %ld\n", thehandle, pid);
206 InitializeCriticalSection (&access);
207 LeaveCriticalSection (&process_access);
212 DeleteCriticalSection (&access);
218 // DWORD exitstate = exit_code ();
219 // if (exitstate == STILL_ACTIVE)
222 /* FIXME: call the cleanup list ? */
224 // CloseHandle (thehandle);
225 // debug_printf ("Process id %ld has terminated, attempting to open a new handle\n",
227 // thehandle = OpenProcess (PROCESS_ALL_ACCESS, FALSE, winpid);
228 // debug_printf ("Got handle %p when refreshing cache process %ld\n", thehandle, winpid);
229 // /* FIXME: what if OpenProcess fails ? */
232 // _exit_status = STILL_ACTIVE;
236 // thehandle = INVALID_HANDLE_VALUE;
240 DWORD process::exit_code ()
242 if (_exit_status != STILL_ACTIVE)
245 err = GetExitCodeProcess (thehandle, &_exit_status);
248 debug_printf ("Failed to retrieve exit code (%ld)\n", GetLastError ());
249 thehandle = INVALID_HANDLE_VALUE;
252 else if (_exit_status == STILL_ACTIVE)
254 /* add new cleanup task etc etc ? */
258 /* this is single threaded. It's called after the process is removed from the cache,
259 * but inserts may be attemped by worker threads that have a pointer to it */
264 EnterCriticalSection (&access);
265 InterlockedIncrement (&(long)cleaning_up);
266 class cleanup_routine *entry = head;
269 class cleanup_routine *temp;
270 entry->cleanup (winpid);
275 LeaveCriticalSection (&access);
279 process::add_cleanup_routine (class cleanup_routine *new_cleanup)
283 EnterCriticalSection (&access);
284 /* check that we didn't block with ::cleanup ()
285 * This rigmarole is to get around win9x's glaring missing TryEnterCriticalSection call
286 * which would be a whole lot easier
290 LeaveCriticalSection (&access);
293 new_cleanup->next = head;
295 LeaveCriticalSection (&access);
299 /* process_cleanup */
301 process_cleanup::process ()
303 theprocess->cleanup ();
307 /* process_process_param */
309 process_process_param::request_loop ()
311 process_cache *cache = (process_cache *) queue;
312 /* always malloc one, so there is no special case in the loop */
313 ssize_t HandlesSize = 2;
314 HANDLE *Handles = (HANDLE *) malloc (sizeof (HANDLE) * HandlesSize);
315 process **Entries = (process **) malloc (sizeof (LPVOID) * HandlesSize);
316 /* TODO: put [1] at the end as it will also get done if a process dies? */
317 Handles[0] = interrupt;
318 Handles[1] = cache->cache_add_trigger;
319 while (cache->active && !shutdown)
327 while ((copied == HandlesSize - 2 - offset) || copied < 0)
329 /* we need more storage to cope with all the HANDLES */
330 if (copied == HandlesSize - 2 - offset)
332 HANDLE *temp = (HANDLE *) realloc (Handles,
338 ("cannot allocate more storage for the handle array!\n");
342 process **ptemp = (process **) realloc (Entries,
348 ("cannot allocate more storage for the handle array!\n");
356 cache->handle_snapshot (&Handles[2], &Entries[2],
357 HandlesSize - 2 - offset, offset);
360 debug_printf ("waiting on %u objects\n", count);
361 DWORD rc = WaitForMultipleObjects (count, Handles, FALSE, INFINITE);
362 if (rc == WAIT_FAILED)
364 printf ("Could not wait on the process handles (%ld)!\n",
368 int objindex = rc - WAIT_OBJECT_0;
369 if (objindex > 1 && objindex < count)
371 debug_printf ("Process %ld has left the building\n",
372 Entries[objindex]->winpid);
373 /* fire off the termination routines */
374 cache->remove_process (Entries[objindex]);
376 else if (objindex >= 0 && objindex < 2)
378 /* 0 is shutdown - do nothing */
379 /* 1 is a cache add event - just rebuild the object list */
384 ("unexpected return code from WaitForMultiple objects in process_process_param::request_loop\n");