|
dev
newsgroups
|
|||||||||||||||||||||||
|
|||||||||||||||||||||||
process exit event and thread synchronizationconceptually understand whats going on: I am starting a new process (.NET20, c#, command line app) which launches an external cmd shell and some external code (.NET remoted into from my manager code). I am subscribing to the exit event of the process to maintain a list of shells I have running. I also have code which can cause the remote code running in the shell to exit (which then also fires the exit event). I tried to use a mutex and a sempahore (same results) to prevent the event and my manager code run concurrently - and run into a deadlock. Analyzing this it seems the deadlock is caused because both the event and my manager code are actually running under the same thread - which seems a surprise to me as I was expecting the event from a different either process or at least thread. Apparently the event is more like an interrupt and not a separate thread or even process??? Guess I'm confused as to what's going on here - maybe someone can drop a line of help? Thanx ahead! Theo On 2007-11-04 14:41:02 -0800, tb2000 <tb2***@discussions.microsoft.com> said:
> [...] There is no particular rule about where an event must execute. It will > Analyzing this it seems the deadlock is caused because both the event and my > manager code are actually running under the same thread - which seems a > surprise to me as I was expecting the event from a different either process > or at least thread. Apparently the event is more like an interrupt and not a > separate thread or even process??? be executed in whatever thread raised the event, but different classes do different things to control which thread that is. For sure, an event will never be raised in a different process. Your code will always execute in the process in which it exists, for a variety of reasons. But your code's process can have a variety of threads. As far as the specific thread here goes, it appears that the event to which you've subscribed has some specific logic in it to raise the event on the same thread used to create the managed object. This is not unheard of; for example, the BackgroundWorker class also does this with some of its events, for the convenience of the creating thread. Now, is this really the issue you're running into? While I'm not that familiar with the events on the Process class, it seems unlikely. In particular, a given thread cannot deadlock itself. So if you're really running into deadlock, the event is definitely _not_ being raised on the same thread where you have other synchronized code (i.e. your manager code). How best to deal with the deadlock is hard to say, since you haven't provided any specific information about your implementation. I will point out that unless you have _two_ mutually dependent synchronized objects, you can't have deadlock. Sometimes that means that the solution involves getting rid of one of the synchronized objects. Alternatively, since your post only describes a single instance of synchronization, it seems possible that you're incorrect about the deadlock and that something else is going on. The best thing would be for you to post a concise-but-complete sample of code that reliably demonstrates the problem. Then we can actually look at the synchronization you're doing and understand better what might be wrong about it. Pete Pete, thanks a lot for your feedback.
Following the code snippets. This code will not cause a lock as it now uses a non blocking lock that is returning true for ownership success. However using a blocking mutex caused a deadlock, apparently because the event appeared under the same thread. So in someplace my shutdown code must have been interrupted to fire (or better propagate the OS) process exit event and in turn the same thread was apparently halted when the event entered the handling routine and tried to wait on the semaphore - which was already owned by the shutdown code section. Thanks again for lighting the darkness ;-) br Theo private void ShellStart() { string arg = HostName + @" c:/mydir/mycode.exe -h"; ProcessStartInfo psi = new ProcessStartInfo("ssh", arg); psi.WindowStyle = ProcessWindowStyle.Hidden; psi.CreateNoWindow = false; Process p = new Process(); _session.Shell = p; p.StartInfo = psi; _session.Shell.Start(); } I tried to run ShellStart() both directly or under a new worker Thread like this ..... st = new Thread(new ThreadStart(this.ShellStart)); st.Start(); .... The result is the same blocking. Here's the event callback - Lock for now is a 'homebuilt' mutex, it returns true if the caller became owner. I had the mutex at the exact same place before but it would not return as I wrote due to deadlock. void SessionExited(object sender, EventArgs e) { Manager.con.WriteLine("E Event SessionExited ENTRY " + HostName); Manager.con.WriteLine("E Event thread " + Thread.CurrentThread.ManagedThreadId); if (Lock(true)) { try { Manager.con.WriteLine( String.Format("E Session Exit on {0} {1}", HostName, (_session.Shell != null ? ("exit code " + _session.Shell.ExitCode.ToString()) : ""))); } catch { } try { if (_session.RemoteManager.IsAlive()) _session.RemoteManager.ShutdownServer(); } catch { } finally { _session.RemoteManager = null; _session.Shell = null; Manager.con.WriteLine("E Event SessionExited DONE " + HostName); Lock(false); } } else { Manager.con.WriteLine("E Event Exit blocked " + HostName); } } And here's the manual Endsession() routine: public void EndSession() { Manager.con.WriteLine("S EndSession ENTRY " + HostName); Manager.con.WriteLine("S EndSession thread " + Thread.CurrentThread.ManagedThreadId); while (!Lock(true)) { Thread.Sleep(100); } if (this.Alive && this.Active && InShellSession && InRemoteSession) { Console.WriteLine("S Ending Remote Session on host " + HostName); //this btw. seems to have no effect - I still keep getting exit events despite having Lock on _session.Shell.EnableRaisingEvents = false; try { SetChannel(); if (InRemoteSession) _session.RemoteManager.ShutdownServer(); } //will always be thrown as the connection dies from calling rm.ShutdownServe catch (WebException) { } catch (Exception e) { Console.WriteLine("Shutdown Exception for " + HostName); Console.WriteLine(e.Message); } finally { _session.RemoteManager = null; } try { if (_session._isMyShell) { int retries = 100; //10s while (!_session.Shell.HasExited && --retries > 0) { Thread.Sleep(100); } if (!_session.Shell.HasExited) { _session.Shell.Kill(); _session.Shell = null; } } } catch { Console.WriteLine("Unable to kill shell for " + HostName); } finally { _session.Shell = null; Manager.con.WriteLine("S EndSession EXIT " + HostName); Lock(false); } } } This is "my" non blocking access synchronizing mutex private bool _locked = false; private bool Lock(bool obtain) { lock (this) { if (!obtain) return _locked = false; if (_locked) return false; else return _locked = true; } } On 2007-11-05 02:24:00 -0800, tb2000 <tb2***@discussions.microsoft.com> said:
> Pete, thanks a lot for your feedback. As I wrote, a single thread cannot deadlock itself.> > Following the code snippets. This code will not cause a lock as it now uses > a non blocking lock that is returning true for ownership success. However > using a blocking mutex caused a deadlock, apparently because the event > appeared under the same thread. As I also wrote, you cannot get the best advice until you post a concise-but-complete sample of code that reliably reproduces the problem. Until you do that, one can only guess. Now, that said, a couple of points: 1) You should not write your own "homebuilt mutex". Use the built-in synchronization objects, and use them correctly. 2) It's hard to review the code you posted, because of all the extraneous stuff in it. However, one thing I notice is that in your EndSession() method, you only release the lock if you enter the true clause of the if() statement following the acquisition of the lock. If the expression in the if() statement evaluates to false, you don't release the lock. That could certainly cause a problem if you expect to be able to obtain the lock later in a different thread. Pete Pete,
thanks again - point taken; here's the minimum code needed to simulate the deadlock. All of this is in a single module host.cs which basically is a wrapper around a cmd or ssh shell. Semaphore s = new Semaphore(1, 1); public void EndSession() { Manager.con.WriteLine("S EndSession ENTRY " + HostName); Manager.con.WriteLine("S EndSession thread " + Thread.CurrentThread.ManagedThreadId); s.WaitOne(); Manager.con.WriteLine("S EndSession entered"); //now some more shutdown code //this line is what I could trace it to - apparently the Sleep allows the event to come in: Thread.Sleep(100); s.Release(); } pls disregard the 6:20AM message - I hit some key which in turn fired the
posting too early. @!"§$%&/()=?@ sorry tb Pete,
thanks again - let me be more precise: Here's the minimum code to cause the deadlock - all in a single module, a wrapper around a cmd or ssh process. The remote process is terminated by a ..net remoting call, it exits and accordingly the local shell exits in sequence. All that works fine. //blocks when reaching 0 - so we have one attempt 'for free' Semaphore s = new Semaphore(1, 1); //Event handler void SessionExited(object sender, EventArgs e) { Manager.con.WriteLine("E claim s " + HostName); Manager.con.WriteLine("E Event thread " + Thread.CurrentThread.ManagedThreadId); s.WaitOne(); Manager.con.WriteLine("E Event SessionExited ENTRY " + HostName); //some more shutdown code Manager.con.WriteLine("E Event Exit done - release lock"); s.Release(); } //Shutdown method public void EndSession() { Manager.con.WriteLine("S EndSession ENTRY " + HostName); Manager.con.WriteLine("S EndSession thread " + Thread.CurrentThread.ManagedThreadId); s.WaitOne(); Manager.con.WriteLine("S EndSession entered"); //Some more shutdown code - including a moment of task switch: Thread.Sleep(100); Manager.con.WriteLine("S Shutdown Exit done - release lock"); s.Release(); } This is the output - I numbered the lines: the event fires under (4) and the main thread (ManagedThreadID 10) is not returning; this happens as I wrote above when I have a Thread.Sleep task switch. According to the ManagedThreadId this is the same thread - which seems to make sense to explain the final lockup (the thread is correctly halted because the sem is occupied). But then according to your statement it should not lock - so it must be a differnet thread. 1 S EndSession ENTRY z1 2 S EndSession thread 10 3 S EndSession entered 4 E claim s z1 5 E Event thread 10 Anyways, hopefully this is a more precise description so that the issue can be explained? Best regards Theo On 2007-11-06 06:37:03 -0800, tb2000 <tb2***@discussions.microsoft.com> said:
> [...] Well, it wasn't quite a "concise-but-complete" sample of code (it was > Anyways, hopefully this is a more precise description so that the issue can > be explained? incomplete). However, it was enough to at least indicate one potential problem: I had missed that you were using a semaphore in one of your tests (you did mention that in your first post, but your later post only discussed a mutex). You wrote that the same thing happens when using a mutex, however the two are not the same. A semaphore is the exception to the general rule that a thread can't block itself, because the semaphore's behavior is specifically to restrict acquisition to a particular number of times. However, if you change the code you posted to use a mutex instead, there should be no such problem. A single thread can acquire and release a mutex arbitrarily many times (for all practical purposes anyway). So if the problem still happens when you use a mutex, then there's something else going on. And if that's the case, then the code you posted isn't going to be sufficient for solving the problem. Pete Pete,
thanks again, I had hoped my code snip was sufficient - if you could let me know what concisely ;-) you're looking for I'll be glad to post it. The semaphore is doing exactly what I expected it to do: it prevents entry into the code while I was hoping I could release it when done in my main thread. Purpose was to synchronize two distinctly separate code blocks (so that only one can be in execution at the same time). I don't really care if I am in the same thread context or in different threads. Actually I was expecting to be in different ones - because it looks as if I am locking my own thread with the mutex and the CLR is not getting me back to where I gave up control. The Managed ThreadId actually indicates that the event is fired under the ID of the main thread I was running -> under debugger that is until the moment when I put my main thread to sleep. I guess the issue is in the Process event itself or the thread scheduler - it just should not fire under the ID of the user thread. To me this looks like a CLR issue. Or is it legal for any same thread to be in two code places almost at the same time? Anyways, guess we've run out of options and I have to use my 'homebuilt' mutex. Maybe one could write the event handler such that it propagates onward by firing its own thread, but that's probably a bit of overkill then too. (I tried btw. to create the Process that fires the events under a worker thread as well - no change in deadlock.) I can retry the same using a mutex tomorrow, I tried that before as I said with the same resulting deadlock. Maybe someone can pick this up from here and see what's going on inside? br Theo On 2007-11-06 15:23:01 -0800, tb2000 <tb2***@discussions.microsoft.com> said:
> Pete, "Concise" means that you post the bare minimum of code required to > > thanks again, I had hoped my code snip was sufficient - if you could let me > know what concisely ;-) you're looking for I'll be glad to post it. demonstrate the problem. "Complete" means that you post _everything_ that is required to demonstrate the problem. Someone should be able to compile the code and run it, without any additions or changes, and there should be nothing in the code that distracts or otherwise makes it difficult to understand what's going on. > The semaphore is doing exactly what I expected it to do: it prevents entry Well, for sure a semaphore won't work if both sections of code are run > into the code while I was hoping I could release it when done in my main > thread. in the same thread. > Purpose was to synchronize two distinctly separate code blocks (so A thread can only execute one statement of code at a time. So if both > that only one can be in execution at the same time). I don't really care if I > am in the same thread context or in different threads. sections of code are running in the same thread, they are automatically synchronized. You can be assured that if one somehow allows another to execute, you aren't going to get back to the first section until the second is done. If the second section waits on something that will only be done by the first section, then yes...your code has essentially deadlocked itself. It's not easy to do that with a single thread, but a semaphore will. Code executing in different threads can deadlock for real, but only if they are waiting on each other. Deadlocking code is a sign of bad design. The fix is to correct the design. > Actually I was You haven't posted enough code for anyone to comment on what's actually > expecting to be in different ones - because it looks as if I am locking my > own thread with the mutex and the CLR is not getting me back to where I gave > up control. happening. Suffice to say, the code you posted won't deadlock using a mutex assuming it's all run in the same thread. > The Managed ThreadId actually indicates that the event is fired under the ID IMHO, it looks to me like a design problem in your own code. I'm not > of the main thread I was running -> under debugger that is until the moment > when I put my main thread to sleep. > > I guess the issue is in the Process event itself or the thread scheduler - > it just should not fire under the ID of the user thread. To me this looks > like a CLR issue. really sure how the Process event deals with raising events, but I suspect that what's going on is that the process event is raised once the main thread enters what is called "an alertable wait state", in which the OS can essentially run a callback within the same thread. Calling Thread.Sleep() can do that. This is why the event is raised in the same thread. This is not "a CLR issue" so much as it is a symptom of a badly designed piece of code. At a minimum, the attempt to synchronize is wrong, and it may be that there is simply a better way to accomplish whatever it is you are trying to accomplish. Without a complete code sample, it's hard to see what is really trying to be done here though. Also, you should be more clear about why it is you feel these sections of code need to be synchronized. What are they doing that conflict with each other? > Or is it legal for any same thread to be in two code places almost at the It is not only illegal, it's impossible. Execution in a given thread > same time? can take place only in a single spot in the code at any given time. > Anyways, guess we've run out of options and I have to use my 'homebuilt' I don't see how your "homebuilt mutex" changes any of the blocking > mutex. issues, and I definitely don't think it's the right solution in any case. > Maybe one could write the event handler such that it propagates onward Maybe. But only if you post a concise-but-complete example of code > by firing its own thread, but that's probably a bit of overkill then too. (I > tried btw. to create the Process that fires the events under a worker thread > as well - no change in deadlock.) > > I can retry the same using a mutex tomorrow, I tried that before as I said > with the same resulting deadlock. > > Maybe someone can pick this up from here and see what's going on inside? that reliably demonstrates the problem. Pete Pete,
thanks for staying with this - here's the next attempt in being concise ;-) This code is exactly the same sequence as in the one we discussed earlier, interestingly this one works as one would expect - the event is raised in a different thread, preventing the "self-deadlock". The main difference seems to be - as far as I can tell - that the event is appearing with its own thread idea and the main thread thus regains control and continues (after the sleep() in ShellKill()). Now I have no idea how to influence that nor do I understand why the event in my code is being raised differently. I also tried the below code with the actual ssh etc code my realworld app is using - but can not reproduce it either. You saw the process ID. Here's the console output where you can see that the main thread (ID10) is actually continuing (back from sleep) while the event handler sits on the lock. ----------- Press CR to start kill shell process ShellKill - Thread 10 ShellKill - Got lock - go for kill ShellKill - CMD killed - wait here for event to happen OnExited - Thread 13 - go for lock ShellKill - back from sleep - releasing lock OnExited - inside lock OnExited - released lock ShellKill - lock released Press CR to exit ---------------------------- using System; using System.Collections.Generic; using System.Text; using System.Diagnostics; using System.Threading; namespace Deadlock { class Deadlock { static Deadlock d = null; static Process p; static void Main(string[] args) { d = new Deadlock(); d.ShellStart(); Console.WriteLine("Press CR to start kill shell process"); Console.ReadLine(); d.ShellKill(); Console.WriteLine("Press CR to exit"); Console.ReadLine(); } Semaphore s = new Semaphore(1,1); private void ShellKill() { Console.WriteLine("ShellKill - Thread " + Thread.CurrentThread.ManagedThreadId); s.WaitOne(); Console.WriteLine("ShellKill - Got lock - go for kill"); if (p == null) { Console.WriteLine("ShellKill : Shell exited already"); } else { //this is the ultimate abbreviation for simplicity of demonstartion //in my other code a .NET remote call to a remote process is causing the //remote side to exit - which will in sequence release CMD (or ssh) p.Kill(); Console.WriteLine("ShellKill - CMD killed - wait here for event to happen"); //simulate some other cleanup work Thread.Sleep(2000); p = null; Console.WriteLine("ShellKill - back from sleep - releasing lock"); } s.Release(); Console.WriteLine("ShellKill - lock released"); } private void ShellStart() { string arg = "/c pause"; ProcessStartInfo psi = new ProcessStartInfo("cmd", arg); p = new Process(); p.StartInfo = psi; p.Exited += new EventHandler(OnExited); p.EnableRaisingEvents = true; p.Start(); } void OnExited(object sender, EventArgs e) { Console.WriteLine("OnExited - Thread " + Thread.CurrentThread.ManagedThreadId + " - go for lock"); s.WaitOne(); Console.WriteLine("OnExited - inside lock"); //dispose process (and other resources) p = null; s.Release(); Console.WriteLine("OnExited - released lock"); } } } oops, too early:
now, going back to my original code the following observation after comparing differences in code, it gets funny: case 1: Before shutting down the remote session I actally disable the process to send events - after I own the semaphore - because I really wouldn't need them any more at that point. This however seems not effective (If I do this in the trial just posted events will be shut down). The execution difference is that the shell process has actually exited AND a WebException has been thrown from the termination call into the remote object (that object actually just exits, which in turn kills the remoting channels). Thereafter - as described - the event is still raised, under my current thread ID, despite having been disabled. case 2: If I keep the event signaling alive - now the fun part - the event is coming in, but actually now as expected with its own thread ID - and best: the code continues where I gave up control after the sleep() expiry. Here's the updated code to explain; I have so far not provided the server nor the kill code using System; using System.Collections.Generic; using System.Text; using System.Diagnostics; using System.Threading; using System.Net; namespace Deadlock { class Deadlock { static Deadlock d = null; static Process p; static void Main(string[] args) { d = new Deadlock(); d.ShellStart(); Console.WriteLine("Press CR to start kill shell process"); Console.ReadLine(); d.ShellKill(); Console.WriteLine("Press CR to exit"); Console.ReadLine(); } Semaphore s = new Semaphore(1,1); private void ShellKill() { Console.WriteLine("ShellKill - Thread " + Thread.CurrentThread.ManagedThreadId); s.WaitOne(); Console.WriteLine("ShellKill - Got lock - go for kill"); //this is the bad guy - here this will work p.EnableRaisingEvents = false; if (p == null) { Console.WriteLine("ShellKill : Shell exited already"); } else { try { //remote invoke exit on the remote server - also causes shell to exit // always throws a WebException } catch (WebException) { } if (!p.HasExited) p.Kill(); Console.WriteLine("ShellKill - CMD killed - wait here for event to happen"); //simulate some other cleanup work Thread.Sleep(2000); p = null; Console.WriteLine("ShellKill - back from sleep - releasing lock"); } s.Release(); Console.WriteLine("ShellKill - lock released"); } private void ShellStart() { string arg = @"z1 c:/Programme/AmiBroker/abm/abm.exe -h"; //string arg = "z1 cmd.exe /c pause"; //string arg = "/c pause"; ProcessStartInfo psi = new ProcessStartInfo("ssh", arg); p = new Process(); p.StartInfo = psi; p.Exited += new EventHandler(OnExited); p.EnableRaisingEvents = true; p.Start(); } void OnExited(object sender, EventArgs e) { Console.WriteLine("OnExited - Thread " + Thread.CurrentThread.ManagedThreadId + " - go for lock"); s.WaitOne(); Console.WriteLine("OnExited - inside lock"); //dispose process (and other resources) p = null; s.Release(); Console.WriteLine("OnExited - released lock"); } } } Pete,
now here's the final one: If you enable the interrupts ( //p.EnableRaisingEvents = false; in line 43 - ShellKill() ) you will get eventing as expected (separate thredas, nice halt on semaphore, proper sequencing): ShellKill - Thread 10 ShellKill - Got lock - go for kill OnExited - Thread 14 - go for lock ShellKill - CMD killed - wait here for event to happen ShellKill - back from sleep - releasing lock OnExited - inside lock OnExited - released lock ShellKill - lock released Press CR to exit If you enable this line you will surprisingly (undesiredly) still get an event firing (hmm??), but the event will be in the same thread context and logically deadlock itself. Now I am curious what the real issue is! Thanks again for sticking with this! br Theo ------ using System; using System.Collections.Generic; using System.Text; using System.Diagnostics; using System.Threading; using System.Net; using System.Runtime.Remoting.Channels.Http; using System.Runtime.Remoting.Channels; using System.Runtime.Remoting; using System.Collections; namespace Deadlock { class Deadlock : MarshalByRefObject { static Deadlock d = null; static Process p; static int Main(string[] args) { d = new Deadlock(); if (args.Length == 1 && args[0] == "-h") return d.RunServer(); d.ShellStart(); Console.WriteLine("Press CR to start kill shell process"); Console.ReadLine(); d.ShellKill(); Console.WriteLine("Press CR to exit"); Console.ReadLine(); return 0; } Semaphore s = new Semaphore(1, 1); HttpChannel httpchnl = null; private void ShellKill() { Console.WriteLine("ShellKill - Thread " + Thread.CurrentThread.ManagedThreadId); s.WaitOne(); Console.WriteLine("ShellKill - Got lock - go for kill"); //comment line out and this will work p.EnableRaisingEvents = false; if (p == null) { Console.WriteLine("ShellKill : Shell exited already"); } else { try { if ((httpchnl = (HttpChannel)ChannelServices.GetChannel("http")) == null) { IDictionary props = new Hashtable(); props["timeout"] = "10000"; //in ms httpchnl = new HttpChannel( props, new SoapClientFormatterSinkProvider(), null ); ChannelServices.RegisterChannel(httpchnl, false); } //remote invoke exit on the remote server - also causes shell to exit Deadlock d = (Deadlock)Activator.GetObject( typeof(Deadlock), @"http://localhost:8085/dl"); d.ShutdownServer(); } catch (WebException) { } if (!p.HasExited) p.Kill(); Console.WriteLine("ShellKill - CMD killed - wait here for event to happen"); //simulate some other cleanup work Thread.Sleep(2000); p = null; Console.WriteLine("ShellKill - back from sleep - releasing lock"); } s.Release(); Console.WriteLine("ShellKill - lock released"); } private void ShellStart() { //string arg = @"z1 c:/Programme/AmiBroker/abm/abm.exe -h"; string arg = "cmd /c Deadlock.exe -h"; //string arg = "z1 cmd.exe /c pause"; //string arg = "/c pause"; ProcessStartInfo psi = new ProcessStartInfo("cmd", arg); p = new Process(); p.StartInfo = psi; p.Exited += new EventHandler(OnExited); p.EnableRaisingEvents = true; p.Start(); } void OnExited(object sender, EventArgs e) { Console.WriteLine("OnExited - Thread " + Thread.CurrentThread.ManagedThreadId + " - go for lock"); s.WaitOne(); Console.WriteLine("OnExited - inside lock"); //dispose process (and other resources) p = null; s.Release(); Console.WriteLine("OnExited - released lock"); } private HttpServerChannel httpserverchannel = null; private static Thread server = null; public int RunServer() { server = Thread.CurrentThread; Console.WriteLine("Server Channel registration"); httpserverchannel = new HttpServerChannel(8085); ChannelServices.RegisterChannel(httpserverchannel, false); //Register TYPE: Console.WriteLine("Type registration"); RemotingConfiguration.RegisterWellKnownServiceType( typeof(Deadlock), "dl", WellKnownObjectMode.Singleton ); try { Console.WriteLine("Server started and listening on http(8085)."); Console.WriteLine("Main Server ThreadId " + Thread.CurrentThread.ManagedThreadId); Console.WriteLine("Start time: {0}", System.DateTime.Now.ToString()); Console.WriteLine("Server " + System.Environment.MachineName + " running"); Thread.Sleep(Timeout.Infinite); } catch (Exception e) { Console.WriteLine("Server released by Exception"); Console.WriteLine(e.Message); } Console.WriteLine("\nEnd time: {0}", System.DateTime.Now.ToString()); Thread.Sleep(5000); return 0; } public int ShutdownServer() { Console.WriteLine("Shutdown signal received from client"); server.Interrupt(); server.Join(); return 0; } } } On 2007-11-08 07:48:01 -0800, tb2000 <tb2***@discussions.microsoft.com> said:
Show quote > Pete, I don't really know for sure. However, the behavior you're seeing > > now here's the final one: > > If you enable the interrupts ( //p.EnableRaisingEvents = false; in line 43 - > ShellKill() ) you will get eventing as expected (separate thredas, nice halt > on semaphore, proper sequencing): > > ShellKill - Thread 10 > ShellKill - Got lock - go for kill > OnExited - Thread 14 - go for lock > ShellKill - CMD killed - wait here for event to happen > ShellKill - back from sleep - releasing lock > OnExited - inside lock > OnExited - released lock > ShellKill - lock released > Press CR to exit > > If you enable this line you will surprisingly (undesiredly) still get an > event firing (hmm??), but the event will be in the same thread context and > logically deadlock itself. > > Now I am curious what the real issue is! could be consistent with a basic implementation detail of the Process class, if not an actual problem. Specifically, I'm pretty sure that raising the exit event requires at least a couple of things: a lower-level native OS callback hooked into the process itself, and the higher-level .NET mechanism that raises the event. It's entirely possible that when you set EnableRaisingEvents back to false, the lower-level callback cannot be disabled, or at least has been failed to be disabled. If at the same time, the .NET part of the code is actually shut down, it may be that the native OS callback, having to be called on _some_ thread, winds up in your main thread instead of the thread that .NET would have dedicated (or at least coincidentally made available) for the purpose. Whether this would be considered "by design" or a flaw in the Process class, I can't answer. I mean, obviously in your own context you would prefer that the event not be raised at all, and/or that it always be raised in a particular thread (or at least not in your main thread). So presumably you yourself might consider it a flaw. But that doesn't mean Microsoft would consider it a flaw. This is especially true given that there's at least two good work-arounds: 1) Don't reset the EnableRaisingEvents property. It may be that like the things in the StartInfo property, this is one of those "set before you start the process" things, and you're not supposed to touch it afterwards. Presumably this would ensure that the exit event is always raised on a different thread, preventing the deadlock from happening. 2) Deal with the case where the event is raised in the same thread that already is trying to shut down the process by setting a flag indicating that you don't want to run your exit event handling. Since you're nominally trying to disable the event anyway, this should be okay with your design, right? Rather than relying on the Process class to disable the event, just set the flag and return immediately in your event handler if it's set, rather than trying to get the lock and doing some work. For what it's worth, if you're still curious about the mechanism by which the event is raised on the same thread, you might look at this: http://msdn2.microsoft.com/en-us/library/ms681951.aspx It describes the APC mechanism by which a thread in an "alertable wait state" might wind up running some code while you thought the thread was asleep. Pete Pete,
Thanks for your quick reply. As far as I am concerned: of cause it's a flaw (we used to call them bugs ages ago ;-) ), when times were less politically correct - especially as the bahaviour is inconsitent. I guess however we isolated the circumstances. The funny thing is that you can switch the event signaling on and off - only after the remote call with a web exception thrown, the event will still pop up, so something inside must go broke. Now, with the event popping up in my main thread context I consider a really bad habit. The workaround you suggested was what I actually called "homebuilt semaphore" approach earlier. Because you still have to synchronize setting and clearing that bool variable which causes the (erroneous) event to just bypass - you could otherwise be in the shutdown code while due to other reasons the shell exits. Well - workaround found for now, maybe you can pass this issue onwards to some place that would take care for the future benefit of others? Thanks again for your help, truly appreciated! Theo On 2007-11-08 10:58:03 -0800, tb2000 <tb2***@discussions.microsoft.com> said:
> [...] To whom would I "pass this issue onwards"?> Well - workaround found for now, maybe you can pass this issue onwards to > some place that would take care for the future benefit of others? If you want to report this to Microsoft, you need to do that yourself. I don't work for Microsoft, nor am I in any way part of their support staff. Just FYI. Pete
Show quote
"Peter Duniho" wrote:
> On 2007-11-08 10:58:03 -0800, tb2000 <tb2***@discussions.microsoft.com> said: > > > [...] > > Well - workaround found for now, maybe you can pass this issue onwards to > > some place that would take care for the future benefit of others? > > To whom would I "pass this issue onwards"? > > If you want to report this to Microsoft, you need to do that yourself. > I don't work for Microsoft, nor am I in any way part of their support > staff. Just FYI. > > Pete > > Pete,
Somehow my last night post was empty - maybe it was too late, so here I try again: I was under the assumption this website was run by microsoft - sorry for the confusion. Plus your message timestamps looked as if you were west coast US (I'm in Europe) begin and end of workday ;-). Anyways, no offense intended! I found the MS bug entry system and filed the topic here: http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=309324 Let's see what they come up with for an explanation. Best regards Theo On 2007-11-08 23:55:00 -0800, tb2000 <tb2***@discussions.microsoft.com> said:
> [...] None taken. I just wanted to clarify my relationship (or rather, lack > I was under the assumption this website was run by microsoft - sorry for the > confusion. Plus your message timestamps looked as if you were west coast US > (I'm in Europe) begin and end of workday ;-). Anyways, no offense intended! thereof) with Microsoft. You are accessing a Microsoft-specific Usenet newsgroup via their website portal, but it's a public newsgroup carried by a number of Usenet news services (i.e. not just Microsoft servers) and answers are frequently provided by people who simply have an interest in .NET programming. To confuse matters, this newsgroup is _also_ used as a forum for support to paid MSDN subscribers, and indeed Microsoft employees can be found here answering those questions. So your confusion is understandable. > I found the MS bug entry system and filed the topic here: Let's > > http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=309324 > That's great...looks like someone's already performed the first step of > see what they come up with for an explanation. filtering the reports. I have a suspicion the bug report will be redirected to a category that's more .NET-specific. Your submission was under the Visual Studio category, but it's not really a VS issue per se. But in any case, hopefully it will eventually find its way to the right person and get answered. :) Thank you for pasting the link here to the bug report. That will make it much easier for someone who may run into the same problem to find whatever resolution happens. Pete Pete,
now I recall from past sessions in another ms newsgroup that there is acually MS people that sometimes answer as well. Anyways, the filing system I think subsumes VS and .NET in one single entry and then the report takes addon tags which namespace is affected. Of cause the issue here may be in two namespaces (responsible for Process and Remoting) - but I guess from here on we can have MS handle this. Thanks again Theo |
|||||||||||||||||||||||