Mono.Cairo: Re-order a few native methods and update API index URL
[mono.git] / mcs / class / Microsoft.Build.Utilities / Microsoft.Build.Utilities / ProcessWrapper.cs
1 // created on 12/18/2004 at 16:28
2 using System;
3 using System.Threading;
4 using System.Diagnostics;
5
6 namespace Microsoft.Build.Utilities
7 {
8         internal delegate void ProcessEventHandler(object sender, string message);
9
10         internal class ProcessWrapper : Process, IProcessAsyncOperation
11         {
12                 private Thread captureOutputThread;
13                 private Thread captureErrorThread;
14                 ManualResetEvent endEventOut = new ManualResetEvent (false);
15                 ManualResetEvent endEventErr = new ManualResetEvent (false);
16                 bool done;
17                 object lockObj = new object ();
18
19                 public ProcessWrapper ()
20                 {
21                 }
22
23                 public new void Start ()
24                 {
25                         CheckDisposed ();
26                         base.Start ();
27
28                         captureOutputThread = new Thread (new ThreadStart(CaptureOutput));
29                         captureOutputThread.IsBackground = true;
30                         captureOutputThread.Start ();
31
32                         if (ErrorStreamChanged != null) {
33                                 captureErrorThread = new Thread (new ThreadStart(CaptureError));
34                                 captureErrorThread.IsBackground = true;
35                                 captureErrorThread.Start ();
36                         } else {
37                                 endEventErr.Set ();
38                         }
39                 }
40
41                 public void WaitForOutput (int milliseconds)
42                 {
43                         CheckDisposed ();
44                         WaitForExit (milliseconds);
45                         WaitHandle.WaitAll (new WaitHandle[] {endEventOut});
46                 }
47
48                 public void WaitForOutput ()
49                 {
50                         WaitForOutput (-1);
51                 }
52
53                 private void CaptureOutput ()
54                 {
55                         try {
56                                 if (OutputStreamChanged != null) {
57                                         char[] buffer = new char [1024];
58                                         int nr;
59                                         while ((nr = StandardOutput.Read (buffer, 0, buffer.Length)) > 0) {
60                                                 if (OutputStreamChanged != null)
61                                                         OutputStreamChanged (this, new string (buffer, 0, nr));
62                                         }
63                                 }
64                         } catch (ThreadAbortException) {
65                                 // There is no need to keep propagating the abort exception
66                                 Thread.ResetAbort ();
67                         } finally {
68                                 // WORKAROUND for "Bug 410743 - wapi leak in System.Diagnostic.Process"
69                                 // Process leaks when an exit event is registered
70                                 WaitHandle.WaitAll (new WaitHandle[] {endEventErr});
71
72                                 OnExited (this, EventArgs.Empty);
73
74                                 //call this AFTER the exit event, or the ProcessWrapper may get disposed and abort this thread
75                                 if (endEventOut != null)
76                                         endEventOut.Set ();
77                         }
78                 }
79
80                 private void CaptureError ()
81                 {
82                         try {
83                                 char[] buffer = new char [1024];
84                                 int nr;
85                                 while ((nr = StandardError.Read (buffer, 0, buffer.Length)) > 0) {
86                                         if (ErrorStreamChanged != null)
87                                                 ErrorStreamChanged (this, new string (buffer, 0, nr));
88                                 }
89                         } finally {
90                                 endEventErr.Set ();
91                         }
92                 }
93
94                 protected override void Dispose (bool disposing)
95                 {
96                         lock (lockObj) {
97                                 if (endEventOut == null)
98                                         return;
99                         }
100
101                         if (!done)
102                                 ((IAsyncOperation)this).Cancel ();
103
104                         captureOutputThread = captureErrorThread = null;
105                         endEventOut.Close ();
106                         endEventErr.Close ();
107                         endEventOut = endEventErr = null;
108
109                         base.Dispose (disposing);
110                 }
111
112                 void CheckDisposed ()
113                 {
114                         if (endEventOut == null)
115                                 throw new ObjectDisposedException ("ProcessWrapper");
116                 }
117
118                 int IProcessAsyncOperation.ExitCode {
119                         get { return ExitCode; }
120                 }
121
122                 int IProcessAsyncOperation.ProcessId {
123                         get { return Id; }
124                 }
125
126                 void IAsyncOperation.Cancel ()
127                 {
128                         try {
129                                 if (!done) {
130                                         try {
131                                                 Kill ();
132                                         } catch {
133                                                 // Ignore
134                                         }
135                                         if (captureOutputThread != null)
136                                                 captureOutputThread.Abort ();
137                                         if (captureErrorThread != null)
138                                                 captureErrorThread.Abort ();
139                                 }
140                         } catch (Exception ex) {
141                                 //FIXME: Log
142                                 Console.WriteLine (ex.ToString ());
143                                 //LoggingService.LogError (ex.ToString ());
144                         }
145                 }
146
147                 void IAsyncOperation.WaitForCompleted ()
148                 {
149                         WaitForOutput ();
150                 }
151
152                 void OnExited (object sender, EventArgs args)
153                 {
154                         try {
155                                 if (!HasExited)
156                                         WaitForExit ();
157                         } catch {
158                                 // Ignore
159                         } finally {
160                                 lock (lockObj) {
161                                         done = true;
162                                         try {
163                                                 if (completedEvent != null)
164                                                         completedEvent (this);
165                                         } catch {
166                                                 // Ignore
167                                         }
168                                 }
169                         }
170                 }
171
172                 event OperationHandler IAsyncOperation.Completed {
173                         add {
174                                 bool raiseNow = false;
175                                 lock (lockObj) {
176                                         if (done)
177                                                 raiseNow = true;
178                                         else
179                                                 completedEvent += value;
180                                 }
181                                 if (raiseNow)
182                                         value (this);
183                         }
184                         remove {
185                                 lock (lockObj) {
186                                         completedEvent -= value;
187                                 }
188                         }
189                 }
190
191                 bool IAsyncOperation.Success {
192                         get { return done ? ExitCode == 0 : false; }
193                 }
194
195                 bool IAsyncOperation.SuccessWithWarnings {
196                         get { return false; }
197                 }
198
199                 bool IAsyncOperation.IsCompleted {
200                         get { return done; }
201                 }
202
203                 event OperationHandler completedEvent;
204
205                 public event ProcessEventHandler OutputStreamChanged;
206                 public event ProcessEventHandler ErrorStreamChanged;
207         }
208 }