Merge pull request #1691 from esdrubal/exitevent
authorMarcos Henrich <marcoshenrich@gmail.com>
Mon, 20 Apr 2015 14:04:32 +0000 (15:04 +0100)
committerMarcos Henrich <marcoshenrich@gmail.com>
Mon, 20 Apr 2015 14:04:32 +0000 (15:04 +0100)
Improved Process.Exited

mcs/class/System/System.Diagnostics/Process.cs
mcs/class/System/Test/System.Diagnostics/ProcessTest.cs

index a394dc4fa78f55ec9c309f9004db034585b58d8b..2910f943c5e1a04be83b417ffe9ff0bc000d8801 100644 (file)
@@ -75,7 +75,7 @@ namespace System.Diagnostics {
                IntPtr process_handle;
                int pid;
                bool enableRaisingEvents;
-               bool already_waiting;
+               RegisteredWaitHandle exitWaitHandle;
                ISynchronizeInvoke synchronizingObject;
                EventHandler exited_event;
                IntPtr stdout_rd;
@@ -102,12 +102,11 @@ namespace System.Diagnostics {
 
                void StartExitCallbackIfNeeded ()
                {
-                       bool start = (!already_waiting && enableRaisingEvents && exited_event != null);
+                       bool start = (exitWaitHandle == null && enableRaisingEvents && exited_event != null);
                        if (start && process_handle != IntPtr.Zero) {
                                WaitOrTimerCallback cb = new WaitOrTimerCallback (CBOnExit);
                                ProcessWaitHandle h = new ProcessWaitHandle (process_handle);
-                               ThreadPool.RegisterWaitForSingleObject (h, cb, this, -1, true);
-                               already_waiting = true;
+                               exitWaitHandle = ThreadPool.RegisterWaitForSingleObject (h, cb, this, -1, true);
                        }
                }
 
@@ -1257,7 +1256,13 @@ namespace System.Diagnostics {
                                                return false;
                                }
                        }
-                       return WaitForExit_internal (process_handle, ms);
+
+                       bool exited = WaitForExit_internal (process_handle, ms);
+
+                       if (exited)
+                               OnExited ();
+
+                       return exited;
                }
 
                /* Waits up to ms milliseconds for process 'handle' to 
@@ -1604,7 +1609,16 @@ namespace System.Diagnostics {
                static void CBOnExit (object state, bool unused)
                {
                        Process p = (Process) state;
-                       p.already_waiting = false;
+
+                       if (!p.HasExited) {
+                               if (p.exitWaitHandle != null) {
+                                       p.exitWaitHandle.Unregister (null);
+                                       p.exitWaitHandle = null;
+                               }
+                               p.StartExitCallbackIfNeeded ();
+                               return;
+                       }
+
                        p.OnExited ();
                }
 
@@ -1613,6 +1627,11 @@ namespace System.Diagnostics {
                        if (exited_event == null)
                                return;
 
+                       if (exitWaitHandle != null) {
+                               exitWaitHandle.Unregister (null);
+                               exitWaitHandle = null;
+                       }
+
                        if (synchronizingObject == null) {
                                foreach (EventHandler d in exited_event.GetInvocationList ()) {
                                        try {
index de12a43d3cfc5bc82742870ecb5fe81e345725ee..b286100b932504342602c79ed75073df342023dc 100644 (file)
@@ -777,11 +777,87 @@ namespace MonoTests.System.Diagnostics
 
                        Assert.AreEqual (true, r, "Null Argument Events Raised");
                }
+
+               [Test]
+               [NUnit.Framework.Category ("MobileNotWorking")]
+               public void TestEnableEventsAfterExitedEvent ()
+               {
+                       Process p = new Process ();
+                       
+                       p.StartInfo = GetCrossPlatformStartInfo ();
+                       p.StartInfo.UseShellExecute = false;
+                       p.StartInfo.RedirectStandardOutput = true;
+                       p.StartInfo.RedirectStandardError = true;
+
+                       var exitedCalledCounter = 0;
+                       p.Exited += (object sender, EventArgs e) => {
+                               exitedCalledCounter++;
+                               Assert.IsTrue (p.HasExited);
+                       };
+
+                       p.EnableRaisingEvents = true;
+
+                       p.Start ();
+                       p.BeginErrorReadLine ();
+                       p.BeginOutputReadLine ();
+                       p.WaitForExit ();
+
+                       Assert.AreEqual (1, exitedCalledCounter);
+                       Thread.Sleep (50);
+                       Assert.AreEqual (1, exitedCalledCounter);
+               }
+
+               [Test]
+               [NUnit.Framework.Category ("MobileNotWorking")]
+               public void TestEnableEventsBeforeExitedEvent ()
+               {
+                       Process p = new Process ();
+                       
+                       p.StartInfo = GetCrossPlatformStartInfo ();
+                       p.StartInfo.UseShellExecute = false;
+                       p.StartInfo.RedirectStandardOutput = true;
+                       p.StartInfo.RedirectStandardError = true;
+
+                       p.EnableRaisingEvents = true;
+
+                       var exitedCalledCounter = 0;
+                       p.Exited += (object sender, EventArgs e) => {
+                               exitedCalledCounter++;
+                               Assert.IsTrue (p.HasExited);
+                       };
+
+                       p.Start ();
+                       p.BeginErrorReadLine ();
+                       p.BeginOutputReadLine ();
+                       p.WaitForExit ();
+
+                       Assert.AreEqual (1, exitedCalledCounter);
+                       Thread.Sleep (50);
+                       Assert.AreEqual (1, exitedCalledCounter);
+               }
+
                
                private ProcessStartInfo GetCrossPlatformStartInfo ()
                {
                        return RunningOnUnix ? new ProcessStartInfo ("/bin/ls", "/") : new ProcessStartInfo ("help", "");
                }
+
+               [Test] // Covers #26362
+               public void TestExitedEvent ()
+               {
+                       var falseExitedEvents = 0;
+                       var cp = Process.GetCurrentProcess ();
+                       foreach (var p in Process.GetProcesses ()) {
+                               if (p.Id != cp.Id && !p.HasExited) {
+                                       p.EnableRaisingEvents = true;
+                                       p.Exited += (s, e) => {
+                                               if (!p.HasExited)
+                                                       falseExitedEvents++;
+                                       };
+                               }
+                       }
+                       Assert.AreEqual (0, falseExitedEvents);
+               }
                
                [Test]
                public void ProcessName_NotStarted ()