OSDN Git Service

2009-08-24 Rafael Avila de Espindola <espindola@google.com>
[pf3gnuchains/gcc-fork.git] / libjava / java / lang / PosixProcess.java
1 // PosixProcess.java - Subclass of Process for POSIX systems.
2 /* Copyright (C) 1998, 1999, 2004, 2006, 2007  Free Software Foundation
3
4    This file is part of libgcj.
5
6 This software is copyrighted work licensed under the terms of the
7 Libgcj License.  Please consult the file "LIBGCJ_LICENSE" for
8 details.  */
9
10 package java.lang;
11
12 import java.io.File;
13 import java.io.IOException;
14 import java.io.InputStream;
15 import java.io.OutputStream;
16 import java.util.Iterator;
17 import java.util.LinkedList;
18
19 import gnu.gcj.RawDataManaged;
20
21 /**
22  * @author Tom Tromey <tromey@cygnus.com>
23  * @date May 3, 1999
24  * @author David Daney <ddaney@avtrex.com> Rewrote using
25  * ProcessManager
26  */
27 final class PosixProcess extends Process
28 {
29   static final class ProcessManager extends Thread
30   {
31     /**
32      * A list of {@link PosixProcess PosixProcesses} to be
33      * started.  The queueLock object is used as the lock Object
34      * for all process related operations. To avoid dead lock
35      * ensure queueLock is obtained before PosixProcess.
36      */
37     private LinkedList<PosixProcess> queue = new LinkedList<PosixProcess>();
38     private LinkedList<PosixProcess> liveProcesses =
39       new LinkedList<PosixProcess>();
40     private boolean ready = false;
41
42     static RawDataManaged nativeData;
43
44     ProcessManager()
45     {
46       // Use package private Thread constructor to place us in the
47       // root ThreadGroup with no InheritableThreadLocal.  If the
48       // InheritableThreadLocals were allowed to initialize, they could
49       // cause a Runtime.exec() to be called causing infinite
50       // recursion.
51       super("ProcessManager", true);
52       // Don't keep the (main) process from exiting on our account.
53       this.setDaemon(true);
54     }
55
56     /**
57      * Add a process to the list of running processes.  This must only
58      * be called with the queueLock held.
59      *
60      * @param p The PosixProcess.
61      */
62     void addToLiveProcesses(PosixProcess p)
63     {
64       liveProcesses.add(p);
65     }
66
67     /**
68      * Queue up the PosixProcess and awake the ProcessManager.
69      * The ProcessManager will start the PosixProcess from its
70      * thread so it can be reaped when it terminates.
71      *
72      * @param p The PosixProcess.
73      */
74     void startExecuting(PosixProcess p)
75     {
76       synchronized (queueLock)
77         {
78           queue.add(p);
79           signalReaper(); // If blocked in waitForSignal().
80           queueLock.notifyAll(); // If blocked in wait();
81         }
82     }
83
84     /**
85      * Block until the ProcessManager thread is ready to accept
86      * commands.
87      */
88     void waitUntilReady()
89     {
90       synchronized (this)
91         {
92           try
93             {
94               while (! ready)
95                 wait();
96             }
97           catch (InterruptedException ie)
98             {
99               // Ignore.
100             }
101         }
102     }
103
104     /**
105      * Main Process starting/reaping loop.
106      */
107     public void run()
108     {
109       init();
110       // Now ready to accept requests.
111       synchronized (this)
112         {
113           ready = true;
114           this.notifyAll();
115         }
116
117       for (;;)
118         {
119           try
120             {
121               synchronized (queueLock)
122                 {
123                   Iterator<PosixProcess> processIterator =
124                     liveProcesses.iterator();
125                   while (processIterator.hasNext())
126                     {
127                       boolean reaped = reap(processIterator.next());
128                       if (reaped)
129                         processIterator.remove();
130                     }
131                   if (liveProcesses.size() == 0 && queue.size() == 0)
132                     {
133                       // This reaper thread could exit, but we keep it
134                       // alive for a while in case someone wants to
135                       // start more Processes.
136                       try
137                         {
138                           queueLock.wait(1000L);
139                           if (queue.size() == 0)
140                             {
141                               processManager = null;
142                               return; // Timed out.
143                             }
144                         }
145                       catch (InterruptedException ie)
146                         {
147                           // Ignore and exit the thread.
148                           return;
149                         }
150                     }
151                   while (queue.size() > 0)
152                     {
153                       PosixProcess p = queue.remove(0);
154                       p.spawn(this);
155                     }
156                 }
157
158               // Wait for a SIGCHLD from either an exiting process or
159               // the startExecuting() method.  This is done outside of
160               // the synchronized block to allow other threads to
161               // enter and submit more jobs.
162               waitForSignal();
163             }
164           catch (Exception ex)
165             {
166               ex.printStackTrace(System.err);
167             }
168         }
169     }
170
171     /**
172      * Setup native signal handlers and other housekeeping things.
173      */
174     private native void init();
175
176     /**
177      * Block waiting for SIGCHLD.
178      *
179      */
180     private native void waitForSignal();
181
182     /**
183      * Try to reap the specified child without blocking.
184      *
185      * @param p the process to try to reap.
186      *
187      * @return true if the process terminated.
188      *
189      */
190     private native boolean reap(PosixProcess p);
191
192     /**
193      * Send SIGCHLD to the reaper thread.
194      */
195     private native void signalReaper();
196   }
197
198   public void destroy()
199   {
200     // Synchronized on the queueLock.  This ensures that the reaper
201     // thread cannot be doing a wait() on the child.
202     // Otherwise there would be a race where the OS could
203     // create a process with the same pid between the wait()
204     // and the update of the state which would cause a kill to
205     // the wrong process.
206     synchronized (queueLock)
207       {
208         synchronized (this)
209           {
210             // If there is no ProcessManager we cannot kill.
211             if (state != STATE_TERMINATED)
212               {
213                 if (processManager == null)
214                   throw new InternalError();
215                 nativeDestroy();
216               }
217           }
218       }
219   }
220
221   private native void nativeDestroy();
222
223   public int exitValue()
224   {
225     synchronized (this)
226       {
227         if (state != STATE_TERMINATED)
228           throw new IllegalThreadStateException("Process has not exited");
229       }
230     return status;
231   }
232
233   /**
234    * Called by native code when process exits.
235    *
236    * Already synchronized (this).  Close any streams that we can to
237    * conserve file descriptors.
238    *
239    * The outputStream can be closed as any future writes will
240    * generate an IOException due to EPIPE.
241    *
242    * The inputStream and errorStream can only be closed if the user
243    * has not obtained a reference to them AND they have no bytes
244    * available.  Since the process has terminated they will never have
245    * any more data available and can safely be replaced by
246    * EOFInputStreams.
247    */
248   void processTerminationCleanup()
249   {
250     try
251       {
252         outputStream.close();
253       }
254     catch (IOException ioe)
255       {
256         // Ignore.
257       }
258     try
259       {
260         if (returnedErrorStream == null && errorStream.available() == 0)
261           {
262             errorStream.close();
263             errorStream = null;
264           }
265       }
266     catch (IOException ioe)
267       {
268         // Ignore.
269       }
270     try
271       {
272         if (returnedInputStream == null && inputStream.available() == 0)
273           {
274             inputStream.close();
275             inputStream = null;
276           }
277       }
278     catch (IOException ioe)
279       {
280         // Ignore.
281       }
282   }
283
284   public synchronized InputStream getErrorStream()
285   {
286     if (returnedErrorStream != null)
287       return returnedErrorStream;
288
289     if (errorStream == null)
290       returnedErrorStream = EOFInputStream.instance;
291     else
292       returnedErrorStream = errorStream;
293
294     return returnedErrorStream;
295   }
296
297   public synchronized InputStream getInputStream()
298   {
299     if (returnedInputStream != null)
300       return returnedInputStream;
301
302     if (inputStream == null)
303       returnedInputStream = EOFInputStream.instance;
304     else
305       returnedInputStream = inputStream;
306
307     return returnedInputStream;
308   }
309
310   public OutputStream getOutputStream()
311   {
312     return outputStream;
313   }
314
315   public int waitFor() throws InterruptedException
316   {
317     synchronized (this)
318       {
319         while (state != STATE_TERMINATED)
320           wait();
321       }
322     return status;
323   }
324
325   /**
326    * Start this process running.  This should only be called by the
327    * ProcessManager with the queueLock held.
328    *
329    * @param pm The ProcessManager that made the call.
330    */
331   void spawn(ProcessManager pm)
332   {
333     synchronized (this)
334       {
335         // Do the fork/exec magic.
336         nativeSpawn();
337         // There is no race with reap() in the pidToProcess map
338         // because this is always called from the same thread
339         // doing the reaping.
340         pm.addToLiveProcesses(this);
341         state = STATE_RUNNING;
342         // Notify anybody waiting on state change.
343         this.notifyAll();
344       }
345   }
346
347   /**
348    * Do the fork and exec.
349    */
350   private native void nativeSpawn();
351
352   PosixProcess(String[] progarray, String[] envp, File dir, boolean redirect)
353     throws IOException
354   {
355     // Check to ensure there is something to run, and avoid
356     // dereferencing null pointers in native code.
357     if (progarray[0] == null)
358       throw new NullPointerException();
359
360     this.progarray = progarray;
361     this.envp = envp;
362     this.dir = dir;
363     this.redirect = redirect;
364
365     // Start a ProcessManager if there is not one already running.
366     synchronized (queueLock)
367       {
368         if (processManager == null)
369           {
370             processManager = new ProcessManager();
371             processManager.start();
372             processManager.waitUntilReady();
373           }
374
375         // Queue this PosixProcess for starting by the ProcessManager.
376         processManager.startExecuting(this);
377       }
378
379     // Wait until ProcessManager has started us.
380     synchronized (this)
381       {
382         while (state == STATE_WAITING_TO_START)
383           {
384             try
385               {
386                 wait();
387               }
388             catch (InterruptedException ie)
389               {
390                 // FIXME: What to do when interrupted while blocking in a constructor?
391                 // Ignore.
392               }
393           }
394       }
395
396     // If there was a problem, re-throw it.
397     if (exception != null)
398       {
399         if (exception instanceof IOException)
400           {
401             IOException ioe = new IOException(exception.toString());
402             ioe.initCause(exception);
403             throw ioe;
404           }
405
406         // Not an IOException.  Something bad happened.
407         InternalError ie = new InternalError(exception.toString());
408         ie.initCause(exception);
409         throw ie;
410       }
411
412     // If we get here, all is well, the Process has started.
413   }
414
415   private String[] progarray;
416   private String[] envp;
417   private File dir;
418   private boolean redirect;
419
420   /** Set by the ProcessManager on problems starting. */
421   private Throwable exception;
422
423   /** The process id.  This is cast to a pid_t on the native side. */
424   long pid;
425
426   // FIXME: Why doesn't the friend declaration in PosixProcess.h
427   // allow PosixProcess$ProcessManager native code access these
428   // when they are private?
429
430   /** Before the process is forked. */
431   static final int STATE_WAITING_TO_START = 0;
432
433   /** After the fork. */
434   static final int STATE_RUNNING = 1;
435
436   /** After exit code has been collected. */
437   static final int STATE_TERMINATED = 2;
438
439   /** One of STATE_WAITING_TO_START, STATE_RUNNING, STATE_TERMINATED. */
440   int state;
441
442   /** The exit status, if the child has exited. */
443   int status;
444
445   /** The streams. */
446   private InputStream errorStream;
447   private InputStream inputStream;
448   private OutputStream outputStream;
449
450   /** InputStreams obtained by the user.  Not null indicates that the
451    *  user has obtained the stream.
452    */
453   private InputStream returnedErrorStream;
454   private InputStream returnedInputStream;
455
456   /**
457    * Lock Object for all processManager related locking.
458    */
459   private static Object queueLock = new Object();
460   private static ProcessManager processManager;
461
462   static class EOFInputStream extends InputStream
463   {
464     static EOFInputStream instance = new EOFInputStream();
465     public int read()
466     {
467       return -1;
468     }
469   }
470 }