Author: gonzalo
Date: 2006-08-09 17:13:11 -0400 (Wed, 09 Aug 2006)
New Revision: 63565

Modified:
   trunk/mcs/class/System/System.Diagnostics/ChangeLog
   trunk/mcs/class/System/System.Diagnostics/Process.cs
   trunk/mono/mono/metadata/ChangeLog
   trunk/mono/mono/metadata/threadpool.c
Log:
2006-08-09 Gonzalo Paniagua Javier <[EMAIL PROTECTED]>

        * mcs/class/System/System.Diagnostics/Process.cs: add support for 2.0
        asynchronous reads on stdout and stderr.

        * mono/mono/metadata/threadpool.c: treat pipes from process
        asynchronous reads as sockets when reading from them, so we get
        select/poll or epoll to wait for data.



Modified: trunk/mcs/class/System/System.Diagnostics/ChangeLog
===================================================================
--- trunk/mcs/class/System/System.Diagnostics/ChangeLog 2006-08-09 21:11:38 UTC 
(rev 63564)
+++ trunk/mcs/class/System/System.Diagnostics/ChangeLog 2006-08-09 21:13:11 UTC 
(rev 63565)
@@ -1,3 +1,8 @@
+2006-08-09 Gonzalo Paniagua Javier <[EMAIL PROTECTED]>
+
+       * Process.cs: add support for 2.0 asynchronous reads on stdout and
+       stderr.
+
 2006-08-09  Gert Driesen  <[EMAIL PROTECTED]>
 
        * EventSourceCreationData.cs: Marked ctor internal, and removed ctor.

Modified: trunk/mcs/class/System/System.Diagnostics/Process.cs
===================================================================
--- trunk/mcs/class/System/System.Diagnostics/Process.cs        2006-08-09 
21:11:38 UTC (rev 63564)
+++ trunk/mcs/class/System/System.Diagnostics/Process.cs        2006-08-09 
21:13:11 UTC (rev 63565)
@@ -8,7 +8,7 @@
 //
 // (C) 2002 Ximian, Inc.
 // (C) 2003 Andreas Nahr
-// (c) 2004 Novell, Inc. (http://www.novell.com)
+// (c) 2004,2005,2006 Novell, Inc. (http://www.novell.com)
 //
 
 //
@@ -33,6 +33,7 @@
 //
 
 using System.IO;
+using System.Text;
 using System.ComponentModel;
 using System.ComponentModel.Design;
 using System.Runtime.CompilerServices;
@@ -71,6 +72,8 @@
                bool already_waiting;
                ISynchronizeInvoke synchronizingObject;
                EventHandler exited_event;
+               IntPtr stdout_rd;
+               IntPtr stderr_rd;
                
                /* Private constructor called from other methods */
                private Process(IntPtr handle, int id) {
@@ -556,7 +559,13 @@
                                if (error_stream == null) {
                                        throw new 
InvalidOperationException("Standard error has not been redirected");
                                }
+#if NET_2_0
+                               if ((async_mode & AsyncModes.AsyncError) != 0)
+                                       throw new InvalidOperationException 
("Cannot mix asynchronous and synchonous reads.");
 
+                               async_mode |= AsyncModes.SyncError;
+#endif
+
                                return(error_stream);
                        }
                }
@@ -584,7 +593,13 @@
                                if (output_stream == null) {
                                        throw new 
InvalidOperationException("Standard output has not been redirected");
                                }
+#if NET_2_0
+                               if ((async_mode & AsyncModes.AsyncOutput) != 0)
+                                       throw new InvalidOperationException 
("Cannot mix asynchronous and synchonous reads.");
 
+                               async_mode |= AsyncModes.SyncOutput;
+#endif
+
                                return(output_stream);
                        }
                }
@@ -873,8 +888,8 @@
                                throw new FileNotFoundException  ("Executable 
not found: " + startInfo.FileName);
                        ProcInfo proc_info=new ProcInfo();
                        IntPtr stdin_rd, stdin_wr;
-                       IntPtr stdout_rd, stdout_wr;
-                       IntPtr stderr_rd, stderr_wr;
+                       IntPtr stdout_wr;
+                       IntPtr stderr_wr;
                        bool ret;
                        MonoIOError error;
 
@@ -904,8 +919,11 @@
                        }
 
                        if (startInfo.RedirectStandardOutput == true) {
-                               ret = MonoIO.CreatePipe (out stdout_rd,
+                               IntPtr out_rd;
+                               ret = MonoIO.CreatePipe (out out_rd,
                                                         out stdout_wr);
+
+                               process.stdout_rd = out_rd;
                                if (ret == false) {
                                        if (startInfo.RedirectStandardInput == 
true) {
                                                MonoIO.Close (stdin_rd, out 
error);
@@ -915,27 +933,30 @@
                                        throw new IOException ("Error creating 
standard output pipe");
                                }
                        } else {
-                               stdout_rd = (IntPtr)0;
+                               process.stdout_rd = (IntPtr)0;
                                stdout_wr = MonoIO.ConsoleOutput;
                        }
 
                        if (startInfo.RedirectStandardError == true) {
-                               ret = MonoIO.CreatePipe (out stderr_rd,
+                               IntPtr err_rd;
+                               ret = MonoIO.CreatePipe (out err_rd,
                                                         out stderr_wr);
+
+                               process.stderr_rd = err_rd;
                                if (ret == false) {
                                        if (startInfo.RedirectStandardInput == 
true) {
                                                MonoIO.Close (stdin_rd, out 
error);
                                                MonoIO.Close (stdin_wr, out 
error);
                                        }
                                        if (startInfo.RedirectStandardOutput == 
true) {
-                                               MonoIO.Close (stdout_rd, out 
error);
+                                               MonoIO.Close 
(process.stdout_rd, out error);
                                                MonoIO.Close (stdout_wr, out 
error);
                                        }
                                        
                                        throw new IOException ("Error creating 
standard error pipe");
                                }
                        } else {
-                               stderr_rd = (IntPtr)0;
+                               process.stderr_rd = (IntPtr)0;
                                stderr_wr = MonoIO.ConsoleError;
                        }
                        
@@ -949,12 +970,12 @@
                                }
 
                                if (startInfo.RedirectStandardOutput == true) {
-                                       MonoIO.Close (stdout_rd, out error);
+                                       MonoIO.Close (process.stdout_rd, out 
error);
                                        MonoIO.Close (stdout_wr, out error);
                                }
 
                                if (startInfo.RedirectStandardError == true) {
-                                       MonoIO.Close (stderr_rd, out error);
+                                       MonoIO.Close (process.stderr_rd, out 
error);
                                        MonoIO.Close (stderr_wr, out error);
                                }
 
@@ -976,12 +997,12 @@
 
                        if (startInfo.RedirectStandardOutput == true) {
                                MonoIO.Close (stdout_wr, out error);
-                               process.output_stream = new StreamReader (new 
FileStream (stdout_rd, FileAccess.Read, true));
+                               process.output_stream = new StreamReader (new 
FileStream (process.stdout_rd, FileAccess.Read, true));
                        }
 
                        if (startInfo.RedirectStandardError == true) {
                                MonoIO.Close (stderr_wr, out error);
-                               process.error_stream = new StreamReader (new 
FileStream (stderr_rd, FileAccess.Read, true));
+                               process.error_stream = new StreamReader (new 
FileStream (process.stderr_rd, FileAccess.Read, true));
                        }
 
                        process.StartExitCallbackIfNeeded ();
@@ -1043,18 +1064,43 @@
                 * exit.  ms can be <0 to mean wait forever.
                 */
                [MethodImplAttribute(MethodImplOptions.InternalCall)]
-               private extern bool WaitForExit_internal(IntPtr handle,
-                                                        int ms);
+               private extern bool WaitForExit_internal(IntPtr handle, int ms);
 
-               public void WaitForExit() {
-                       WaitForExit_internal(process_handle, -1);
+               public void WaitForExit ()
+               {
+                       WaitForExit (-1);
                }
 
                public bool WaitForExit(int milliseconds) {
-                       if (milliseconds == int.MaxValue)
-                               milliseconds = -1;
+                       int ms = milliseconds;
+                       if (ms == int.MaxValue)
+                               ms = -1;
 
-                       return WaitForExit_internal (process_handle, 
milliseconds);
+#if NET_2_0
+                       DateTime start = DateTime.UtcNow;
+                       if (async_output != null && !async_output.IsCompleted) {
+                               if (false == async_output.WaitHandle.WaitOne 
(ms, false))
+                                       return false; // Timed out
+
+                               if (ms >= 0) {
+                                       ms -= (int) (DateTime.UtcNow - 
start).TotalMilliseconds;
+                                       if (ms <= 0)
+                                               return false;
+                               }
+                       }
+
+                       if (async_error != null && !async_error.IsCompleted) {
+                               if (false == async_error.WaitHandle.WaitOne 
(ms, false))
+                                       return false; // Timed out
+
+                               if (ms >= 0) {
+                                       ms -= (int) (DateTime.UtcNow - 
start).TotalMilliseconds;
+                                       if (ms <= 0)
+                                               return false;
+                               }
+                       }
+#endif
+                       return WaitForExit_internal (process_handle, ms);
                }
 
                [MonoTODO]
@@ -1067,6 +1113,225 @@
                        return(false);
                }
 
+
+#if NET_2_0
+               public event DataReceivedEventHandler OutputDataReceived;
+               public event DataReceivedEventHandler ErrorDataReceived;
+
+               void OnOutputDataReceived (string str)
+               {
+                       if (OutputDataReceived != null)
+                               OutputDataReceived (this, new 
DataReceivedEventArgs (str));
+               }
+
+               void OnErrorDataReceived (string str)
+               {
+                       if (ErrorDataReceived != null)
+                               ErrorDataReceived (this, new 
DataReceivedEventArgs (str));
+               }
+
+               [Flags]
+               enum AsyncModes {
+                       NoneYet = 0,
+                       SyncOutput = 1,
+                       SyncError = 1 << 1,
+                       AsyncOutput = 1 << 2,
+                       AsyncError = 1 << 3
+               }
+
+               [StructLayout (LayoutKind.Sequential)]
+               sealed class ProcessAsyncReader
+               {
+                       /*
+                          The following fields match those of 
SocketAsyncResult.
+                          This is so that changes needed in the runtime to 
handle
+                          asynchronous reads are trivial
+                       */
+                       /* DON'T shuffle fields around. DON'T remove fields */
+                       public object Sock;
+                       public IntPtr handle;
+                       public object state;
+                       public AsyncCallback callback;
+                       public ManualResetEvent wait_handle;
+
+                       public Exception delayedException;
+
+                       public object EndPoint;
+                       byte [] buffer = new byte [4196];
+                       public int Offset;
+                       public int Size;
+                       public int SockFlags;
+
+                       public object acc_socket;
+                       public int total;
+                       public bool completed_sync;
+                       bool completed;
+                       bool err_out; // true -> stdout, false -> stderr
+                       internal int error;
+                       public int operation = 6; // MAGIC NUMBER: Maximum in 
SocketOperation + 1
+                       public object ares;
+
+
+                       // These fields are not in SocketAsyncResult
+                       Process process;
+                       Stream stream;
+                       StringBuilder sb = new StringBuilder ();
+                       public AsyncReadHandler ReadHandler;
+
+                       public ProcessAsyncReader (Process process, IntPtr 
handle, bool err_out)
+                       {
+                               this.process = process;
+                               this.handle = handle;
+                               stream = new FileStream (handle, 
FileAccess.Read, false);
+                               this.ReadHandler = new AsyncReadHandler 
(AddInput);
+                               this.err_out = err_out;
+                       }
+
+                       public void AddInput ()
+                       {
+                               lock (this) {
+                                       int nread = stream.Read (buffer, 0, 
buffer.Length);
+                                       if (nread == 0) {
+                                               completed = true;
+                                               if (wait_handle != null)
+                                                       wait_handle.Set ();
+                                               Flush (true);
+                                               return;
+                                       }
+
+                                       try {
+                                               sb.Append 
(Encoding.Default.GetString (buffer, 0, nread));
+                                       } catch {
+                                               // Just in case the encoding 
fails...
+                                               for (int i = 0; i < nread; i++) 
{
+                                                       sb.Append ((char) 
buffer [i]);
+                                               }
+                                       }
+
+                                       Flush (false);
+                                       ReadHandler.BeginInvoke (null, this);
+                               }
+                       }
+
+                       void Flush (bool last)
+                       {
+                               if (sb.Length == 0 ||
+                                   (err_out && process.output_canceled) ||
+                                   (!err_out && process.error_canceled))
+                                       return;
+
+                               string total = sb.ToString ();
+                               sb.Length = 0;
+                               string [] strs = total.Split ('\n');
+                               int len = strs.Length;
+                               if (len == 0)
+                                       return;
+
+                               for (int i = 0; i < len - 1; i++) {
+                                       if (err_out)
+                                               process.OnOutputDataReceived 
(strs [i]);
+                                       else
+                                               process.OnErrorDataReceived 
(strs [i]);
+                               }
+
+                               string end = strs [len - 1];
+                               if (last || end == "") {
+                                       if (err_out)
+                                               process.OnOutputDataReceived 
(end);
+                                       else
+                                               process.OnErrorDataReceived 
(end);
+                               } else {
+                                       sb.Append (end);
+                               }
+                       }
+
+                       public bool IsCompleted {
+                               get { return completed; }
+                       }
+
+                       public WaitHandle WaitHandle {
+                               get {
+                                       lock (this) {
+                                               if (wait_handle == null)
+                                                       wait_handle = new 
ManualResetEvent (completed);
+                                               return wait_handle;
+                                       }
+                               }
+                       }
+               }
+
+               AsyncModes async_mode;
+               bool output_canceled;
+               bool error_canceled;
+               ProcessAsyncReader async_output;
+               ProcessAsyncReader async_error;
+               delegate void AsyncReadHandler ();
+
+               [ComVisibleAttribute(false)] 
+               public void BeginOutputReadLine ()
+               {
+                       if (process_handle == IntPtr.Zero || output_stream == 
null || StartInfo.RedirectStandardOutput == false)
+                               throw new InvalidOperationException ("Standard 
output has not been redirected or process has not been started.");
+
+                       if ((async_mode & AsyncModes.SyncOutput) != 0)
+                               throw new InvalidOperationException ("Cannot 
mix asynchronous and synchonous reads.");
+
+                       async_mode |= AsyncModes.AsyncOutput;
+                       output_canceled = false;
+                       if (async_output == null) {
+                               async_output = new ProcessAsyncReader (this, 
stdout_rd, true);
+                               async_output.ReadHandler.BeginInvoke (null, 
async_output);
+                       }
+               }
+
+               [ComVisibleAttribute(false)] 
+               public void CancelOutputRead ()
+               {
+                       if (process_handle == IntPtr.Zero || output_stream == 
null || StartInfo.RedirectStandardOutput == false)
+                               throw new InvalidOperationException ("Standard 
output has not been redirected or process has not been started.");
+
+                       if ((async_mode & AsyncModes.SyncOutput) != 0)
+                               throw new InvalidOperationException 
("OutputStream is not enabled for asynchronous read operations.");
+
+                       if (async_output == null)
+                               throw new InvalidOperationException ("No async 
operation in progress.");
+
+                       output_canceled = true;
+               }
+
+               [ComVisibleAttribute(false)] 
+               public void BeginErrorReadLine ()
+               {
+                       if (process_handle == IntPtr.Zero || error_stream == 
null || StartInfo.RedirectStandardError == false)
+                               throw new InvalidOperationException ("Standard 
error has not been redirected or process has not been started.");
+
+                       if ((async_mode & AsyncModes.SyncError) != 0)
+                               throw new InvalidOperationException ("Cannot 
mix asynchronous and synchonous reads.");
+
+                       async_mode |= AsyncModes.AsyncError;
+                       error_canceled = false;
+                       if (async_error == null) {
+                               async_error = new ProcessAsyncReader (this, 
stderr_rd, false);
+                               async_error.ReadHandler.BeginInvoke (null, 
async_error);
+                       }
+               }
+
+               [ComVisibleAttribute(false)] 
+               public void CancelErrorRead ()
+               {
+                       if (process_handle == IntPtr.Zero || output_stream == 
null || StartInfo.RedirectStandardOutput == false)
+                               throw new InvalidOperationException ("Standard 
output has not been redirected or process has not been started.");
+
+                       if ((async_mode & AsyncModes.SyncOutput) != 0)
+                               throw new InvalidOperationException 
("OutputStream is not enabled for asynchronous read operations.");
+
+                       if (async_error == null)
+                               throw new InvalidOperationException ("No async 
operation in progress.");
+
+                       error_canceled = true;
+               }
+#endif
+
                [Category ("Behavior")]
                [MonitoringDescription ("Raised when this process exits.")]
                public event EventHandler Exited {

Modified: trunk/mono/mono/metadata/ChangeLog
===================================================================
--- trunk/mono/mono/metadata/ChangeLog  2006-08-09 21:11:38 UTC (rev 63564)
+++ trunk/mono/mono/metadata/ChangeLog  2006-08-09 21:13:11 UTC (rev 63565)
@@ -1,3 +1,9 @@
+2006-08-09 Gonzalo Paniagua Javier <[EMAIL PROTECTED]>
+
+       * threadpool.c: treat pipes from process asynchronous reads as sockets
+       when reading from them, so we get select/poll or epoll to wait for
+       data.
+
 2006-08-07  Sebastien Pouliot  <[EMAIL PROTECTED]>
 
        * loader.c: Fix a typo (CID #233) in the null check.

Modified: trunk/mono/mono/metadata/threadpool.c
===================================================================
--- trunk/mono/mono/metadata/threadpool.c       2006-08-09 21:11:38 UTC (rev 
63564)
+++ trunk/mono/mono/metadata/threadpool.c       2006-08-09 21:13:11 UTC (rev 
63565)
@@ -119,6 +119,7 @@
 
 static MonoClass *async_call_klass;
 static MonoClass *socket_async_call_klass;
+static MonoClass *process_async_call_klass;
 
 #define INIT_POLLFD(a, b, c) {(a)->fd = b; (a)->events = c; (a)->revents = 0;}
 enum {
@@ -131,6 +132,7 @@
        AIO_OP_SENDTO,
        AIO_OP_RECV_JUST_CALLBACK,
        AIO_OP_SEND_JUST_CALLBACK,
+       AIO_OP_READPIPE,
        AIO_OP_LAST
 };
 
@@ -180,6 +182,7 @@
        case AIO_OP_RECEIVE:
        case AIO_OP_RECV_JUST_CALLBACK:
        case AIO_OP_RECEIVEFROM:
+       case AIO_OP_READPIPE:
                return MONO_POLLIN;
        case AIO_OP_SEND:
        case AIO_OP_SEND_JUST_CALLBACK:
@@ -232,8 +235,6 @@
                if (state) {
                        InterlockedDecrement (&pending_io_items);
                        ar = state->ares;
-                       /* worker threads invokes methods in different domains,
-                        * so we need to set the right domain here */
                        switch (state->operation) {
                        case AIO_OP_RECEIVE:
                                state->total = ICALL_RECV (state);
@@ -243,6 +244,8 @@
                                break;
                        }
 
+                       /* worker threads invokes methods in different domains,
+                        * so we need to set the right domain here */
                        domain = ((MonoObject *)ar)->vtable->domain;
                        mono_thread_push_appdomain_ref (domain);
                        if (mono_domain_set (domain, FALSE)) {
@@ -877,7 +880,7 @@
 
        if (socket_async_call_klass == NULL) {
                klass = target->vtable->klass;
-               /* Check if it's SocketAsyncCall in System
+               /* Check if it's SocketAsyncCall in System.Net.Sockets
                 * FIXME: check the assembly is signed correctly for extra care
                 */
                if (klass->name [0] == 'S' && strcmp (klass->name, 
"SocketAsyncCall") == 0 
@@ -886,10 +889,20 @@
                        socket_async_call_klass = klass;
        }
 
+       if (process_async_call_klass == NULL) {
+               klass = target->vtable->klass;
+               /* Check if it's AsyncReadHandler in System.Diagnostics.Process
+                * FIXME: check the assembly is signed correctly for extra care
+                */
+               if (klass->name [0] == 'A' && strcmp (klass->name, 
"AsyncReadHandler") == 0 
+                               && strcmp (mono_image_get_name (klass->image), 
"System") == 0
+                               && klass->nested_in && strcmp 
(klass->nested_in->name, "Process") == 0)
+                       process_async_call_klass = klass;
+       }
        /* return both when socket_async_call_klass has not been seen yet and 
when
         * the object is not an instance of the class.
         */
-       if (target->vtable->klass != socket_async_call_klass)
+       if (target->vtable->klass != socket_async_call_klass && 
target->vtable->klass != process_async_call_klass)
                return FALSE;
 
        op = sock_res->operation;

_______________________________________________
Mono-patches maillist  -  [email protected]
http://lists.ximian.com/mailman/listinfo/mono-patches

Reply via email to