OSDN Git Service

2002-02-28 Robert Collins <rbtcollins@hotmail.com>
[pf3gnuchains/pf3gnuchains3x.git] / winsup / cygserver / process.cc
1 /* cygserver_process.cc
2
3    Copyright 2001 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 #include <errno.h>
14 #include <stdio.h>
15 #include <unistd.h>
16 #include <stdlib.h>
17 #include <windows.h>
18 #include <sys/types.h>
19 #include <sys/socket.h>
20 #include <netdb.h>
21 #include "wincap.h"
22 #include <pthread.h>
23 #include <threaded_queue.h>
24 #include <cygwin/cygserver_process.h>
25
26 #define debug_printf if (DEBUG) printf
27 #define DEBUG 1
28
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
31  */
32
33 /* process cache */
34 process_cache::process_cache (unsigned int num_initial_workers):
35 head (NULL)
36 {
37   /* there can only be one */
38   InitializeCriticalSection (&cache_write_access);
39   if ((cache_add_trigger = CreateEvent (NULL, FALSE, FALSE, NULL)) == NULL)
40     {
41       printf ("Failed to create cache add trigger (%lu), terminating\n",
42               GetLastError ());
43       exit (1);
44     }
45   initial_workers = num_initial_workers;
46 }
47
48 process_cache::~process_cache ()
49 {
50 }
51
52 class process *
53 process_cache::process (long pid)
54 {
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);
58   if (!entry)
59     {
60       entry = new class process (pid);
61       entry->next =
62         (class process *) InterlockedExchangePointer (&head, entry);
63       PulseEvent (cache_add_trigger);
64     }
65   else
66     {
67       while (entry->winpid != pid && entry->next)
68         entry = entry->next;
69       if (entry->winpid != pid)
70         {
71           class process *new_entry = new class process (pid);
72           new_entry->next =
73             (class process *) InterlockedExchangePointer (&entry->next,
74                                                           new_entry);
75           entry = new_entry;
76           PulseEvent (cache_add_trigger);
77         }
78     }
79   LeaveCriticalSection (&cache_write_access);
80   return entry;
81 }
82
83 static DWORD WINAPI
84 request_loop (LPVOID LpParam)
85 {
86   class process_process_param *params = (process_process_param *) LpParam;
87   return params->request_loop ();
88 }
89
90 void
91 process_cache::process_requests ()
92 {
93   class process_process_param *params = new process_process_param;
94   threaded_queue::process_requests (params, request_loop);
95 }
96
97 void
98 process_cache::add_task (class process * theprocess)
99 {
100   /* safe to not "Try" because workers don't hog this, they wait on the event
101    */
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);
107 }
108
109 /* NOT fully MT SAFE: must be called by only one thread in a program */
110 void
111 process_cache::remove_process (class process *theprocess)
112 {
113   class process *entry = head;
114   /* unlink */
115   EnterCriticalSection (&cache_write_access);
116   if (entry == theprocess)
117     {
118       entry = (class process *) InterlockedExchangePointer (&head, theprocess->next);
119       if (entry != theprocess)
120         {
121           printf ("Bug encountered, process cache corrupted\n");
122           exit (1);
123         }
124     }
125   else
126     {
127       while (entry->next && entry->next != theprocess)
128         entry = entry->next;
129       class process *temp = (class process *) InterlockedExchangePointer (&entry->next, theprocess->next);
130       if (temp != theprocess)
131         {
132           printf ("Bug encountered, process cache corrupted\n");
133           exit (1);
134         }
135     }
136   LeaveCriticalSection (&cache_write_access);
137   /* Process any cleanup tasks */
138   add_task (theprocess);
139 }
140         
141       
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.
146  */
147 int
148 process_cache::handle_snapshot (HANDLE * hdest, class process ** edest,
149                                 ssize_t max_copy, int begin_at)
150 {
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)
156     {
157       if (entry->exit_code () == STILL_ACTIVE)
158         count--;
159       entry = entry->next;
160     }
161   /* hit the end of the list within begin_at entries */
162   if (count)
163     return 0;
164   HANDLE *hto = hdest;
165   class process **eto = edest;
166   while (entry && count < max_copy)
167     {
168       /* hack */
169       if (entry->exit_code () == STILL_ACTIVE)
170         {
171           *hto = entry->handle ();
172           *eto = entry;
173           count++;
174           hto++;
175           eto++;
176         }
177       entry = entry->next;
178     }
179   return count;
180 }
181
182 /* process's */
183 /* global process crit section */
184 static CRITICAL_SECTION process_access;
185 static pthread_once_t process_init;
186
187 void
188 do_process_init (void)
189 {
190   InitializeCriticalSection (&process_access);
191   /* we don't have a cache shutdown capability today */
192 }
193
194 process::process (long pid):
195 winpid (pid), next (NULL), cleaning_up (0), head (NULL), _exit_status (STILL_ACTIVE)
196 {
197   pthread_once (&process_init, do_process_init);
198   EnterCriticalSection (&process_access);
199   thehandle = OpenProcess (PROCESS_ALL_ACCESS, FALSE, pid);
200   if (!thehandle)
201     {
202       printf ("unable to obtain handle for new cache process %ld\n", pid);
203       thehandle = INVALID_HANDLE_VALUE;
204     }
205   debug_printf ("Got handle %p for new cache process %ld\n", thehandle, pid);
206   InitializeCriticalSection (&access);
207   LeaveCriticalSection (&process_access);
208 }
209
210 process::~process ()
211 {
212   DeleteCriticalSection (&access);
213 }
214
215 HANDLE
216 process::handle ()
217 {
218 //  DWORD exitstate = exit_code ();
219 //  if (exitstate == STILL_ACTIVE)
220   return thehandle;
221
222   /* FIXME: call the cleanup list ? */
223
224 //  CloseHandle (thehandle);
225 //  debug_printf ("Process id %ld has terminated, attempting to open a new handle\n",
226 //       winpid);
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 ? */
230 //  if (thehandle) 
231 //    {
232 //      _exit_status = STILL_ACTIVE;
233 //      exit_code ();
234 //    }
235 //  else
236 //    thehandle = INVALID_HANDLE_VALUE;
237 //  return thehandle;
238 }
239
240 DWORD process::exit_code ()
241 {
242   if (_exit_status != STILL_ACTIVE)
243     return _exit_status;
244   bool
245     err = GetExitCodeProcess (thehandle, &_exit_status);
246   if (!err)
247     {
248       debug_printf ("Failed to retrieve exit code (%ld)\n", GetLastError ());
249       thehandle = INVALID_HANDLE_VALUE;
250       return _exit_status;
251     }
252   else if (_exit_status == STILL_ACTIVE)
253     return _exit_status;
254   /* add new cleanup task etc etc ? */
255   return _exit_status;
256 }
257
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 */
260 void
261 process::cleanup ()
262 {
263   /* Serialize this */
264   EnterCriticalSection (&access);
265   InterlockedIncrement (&(long)cleaning_up);
266   class cleanup_routine *entry = head;
267   while (entry)
268     {
269       class cleanup_routine *temp;
270       entry->cleanup (winpid);
271       temp = entry->next;
272       delete entry;
273       entry = temp;
274     }
275   LeaveCriticalSection (&access);
276 }
277
278 bool
279 process::add_cleanup_routine (class cleanup_routine *new_cleanup)
280 {
281   if (cleaning_up)
282     return false;
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
287    */
288   if (cleaning_up)
289     {
290       LeaveCriticalSection (&access);
291       return false;
292     }
293   new_cleanup->next = head;
294   head = new_cleanup;
295   LeaveCriticalSection (&access);
296   return true;
297 }
298
299 /* process_cleanup */
300 void
301 process_cleanup::process ()
302 {
303   theprocess->cleanup ();
304   delete theprocess;
305 }
306
307 /* process_process_param */
308 DWORD
309 process_process_param::request_loop ()
310 {
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)
320     {
321       int copied;
322       copied = -1;
323       int offset;
324       offset = 1;
325       int count;
326       count = 2;
327       while ((copied == HandlesSize - 2 - offset) || copied < 0)
328         {
329           /* we need more storage to cope with all the HANDLES */
330           if (copied == HandlesSize - 2 - offset)
331             {
332               HANDLE *temp = (HANDLE *) realloc (Handles,
333                                                  sizeof (HANDLE) *
334                                                  HandlesSize + 10);
335               if (!temp)
336                 {
337                   printf
338                     ("cannot allocate more storage for the handle array!\n");
339                   exit (1);
340                 }
341               Handles = temp;
342               process **ptemp = (process **) realloc (Entries,
343                                                       sizeof (LPVOID) *
344                                                       HandlesSize + 10);
345               if (!ptemp)
346                 {
347                   printf
348                     ("cannot allocate more storage for the handle array!\n");
349                   exit (1);
350                 }
351               Entries = ptemp;
352               HandlesSize += 10;
353             }
354           offset += copied;
355           copied =
356             cache->handle_snapshot (&Handles[2], &Entries[2],
357                                     HandlesSize - 2 - offset, offset);
358           count += copied;
359         }
360       debug_printf ("waiting on %u objects\n", count);
361       DWORD rc = WaitForMultipleObjects (count, Handles, FALSE, INFINITE);
362       if (rc == WAIT_FAILED)
363         {
364           printf ("Could not wait on the process handles (%ld)!\n",
365                   GetLastError ());
366           exit (1);
367         }
368       int objindex = rc - WAIT_OBJECT_0;
369       if (objindex > 1 && objindex < count)
370         {
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]);
375         }
376       else if (objindex >= 0 && objindex < 2)
377         {
378           /* 0 is shutdown - do nothing */
379           /* 1 is a cache add event - just rebuild the object list */
380         }
381       else
382         {
383           printf
384             ("unexpected return code from WaitForMultiple objects in process_process_param::request_loop\n");
385         }
386     }
387   running = false;
388   return 0;
389 }