1674ff8b5dc3095f027cd6ffd60d1562e5e056d4
[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                 ManualResetEvent endEventOut = new ManualResetEvent (false);
13                 ManualResetEvent endEventErr = new ManualResetEvent (false);
14                 ManualResetEvent endEventExit = new ManualResetEvent (false);
15                 bool done;
16                 bool disposed;
17                 object lockObj = new object ();
18
19                 public ProcessWrapper ()
20                 {
21                 }
22
23                 public new void Start ()
24                 {
25                         CheckDisposed ();
26
27                         base.EnableRaisingEvents = true;
28
29                         base.Exited += (s, args) => {
30                                 try {
31                                         endEventExit.Set ();
32                                         WaitHandle.WaitAll (new WaitHandle[] { endEventOut, endEventErr });
33                                 } catch (ObjectDisposedException) {
34                                         return; // we already called Dispose
35                                 }
36
37                                 OnExited (this, EventArgs.Empty);
38                         };
39
40                         base.OutputDataReceived += (s, args) => {
41                                 if (args.Data == null) {
42                                         try {
43                                                 endEventOut.Set ();
44                                         } catch (ObjectDisposedException) {
45                                                 return; // we already called Dispose
46                                         }
47                                 } else {
48                                         ProcessEventHandler handler = OutputStreamChanged;
49                                         if (handler != null)
50                                                 handler (this, args.Data + Environment.NewLine);
51                                 }
52                         };
53
54                         base.ErrorDataReceived += (s, args) => {
55                                 if (args.Data == null) {
56                                         try {
57                                                 endEventErr.Set ();
58                                         } catch (ObjectDisposedException) {
59                                                 return; // we already called Dispose
60                                         }
61                                 } else {
62                                         ProcessEventHandler handler = ErrorStreamChanged;
63                                         if (handler != null)
64                                                 handler (this, args.Data + Environment.NewLine);
65                                 }
66                         };
67
68                         base.Start ();
69
70                         base.BeginOutputReadLine ();
71                         base.BeginErrorReadLine ();
72                 }
73
74                 public void WaitForOutput (int milliseconds)
75                 {
76                         CheckDisposed ();
77                         WaitHandle.WaitAll (new WaitHandle[] { endEventOut, endEventErr, endEventExit }, milliseconds);
78                 }
79
80                 public void WaitForOutput ()
81                 {
82                         WaitForOutput (-1);
83                 }
84
85                 protected override void Dispose (bool disposing)
86                 {
87                         if (disposed)
88                                 return;
89
90                         if (!done)
91                                 ((IAsyncOperation)this).Cancel ();
92
93                         // if we race with base.Exited, we don't want to hang on WaitAll (endEventOut, endEventErr)
94                         endEventOut.Set ();
95                         endEventErr.Set ();
96
97                         endEventOut.Close ();
98                         endEventErr.Close ();
99                         endEventExit.Close ();
100
101                         disposed = true;
102
103                         base.Dispose (disposing);
104                 }
105
106                 void CheckDisposed ()
107                 {
108                         if (disposed)
109                                 throw new ObjectDisposedException ("ProcessWrapper");
110                 }
111
112                 int IProcessAsyncOperation.ExitCode {
113                         get { return ExitCode; }
114                 }
115
116                 int IProcessAsyncOperation.ProcessId {
117                         get { return Id; }
118                 }
119
120                 void IAsyncOperation.Cancel ()
121                 {
122                         try {
123                                 if (!done) {
124                                         try {
125                                                 Kill ();
126                                         } catch {
127                                                 // Ignore
128                                         }
129                                         try {
130                                                 base.CancelOutputRead ();
131                                         } catch (InvalidOperationException) {
132                                                 // Ignore: might happen if Start wasn't called
133                                         }
134                                         try {
135                                                 base.CancelErrorRead ();
136                                         } catch (InvalidOperationException) {
137                                                 // Ignore: might happen if Start wasn't called
138                                         }
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                         lock (lockObj) {
155                                 done = true;
156                                 try {
157                                         OperationHandler handler = completedEvent;
158                                         if (handler != null)
159                                                 handler (this);
160                                 } catch {
161                                         // Ignore
162                                 }
163                         }
164                 }
165
166                 event OperationHandler IAsyncOperation.Completed {
167                         add {
168                                 bool raiseNow = false;
169                                 lock (lockObj) {
170                                         if (done)
171                                                 raiseNow = true;
172                                         else
173                                                 completedEvent += value;
174                                 }
175                                 if (raiseNow)
176                                         value (this);
177                         }
178                         remove {
179                                 lock (lockObj) {
180                                         completedEvent -= value;
181                                 }
182                         }
183                 }
184
185                 bool IAsyncOperation.Success {
186                         get { return done ? ExitCode == 0 : false; }
187                 }
188
189                 bool IAsyncOperation.SuccessWithWarnings {
190                         get { return false; }
191                 }
192
193                 bool IAsyncOperation.IsCompleted {
194                         get { return done; }
195                 }
196
197                 event OperationHandler completedEvent;
198
199                 public event ProcessEventHandler OutputStreamChanged;
200                 public event ProcessEventHandler ErrorStreamChanged;
201         }
202 }