|
Next: In-Memory Assemblies
From: tb2000 on 6 Nov 2007 09:40 pls disregard the 6:20AM message - I hit some key which in turn fired the posting too early. @!"ยง$%&/()=?@ sorry tb
From: Peter Duniho on 6 Nov 2007 12:51 On 2007-11-06 06:37:03 -0800, tb2000 <tb2000(a)discussions.microsoft.com> said: > [...] > Anyways, hopefully this is a more precise description so that the issue can > be explained? Well, it wasn't quite a "concise-but-complete" sample of code (it was 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
From: tb2000 on 6 Nov 2007 18:23 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
From: Peter Duniho on 6 Nov 2007 18:53 On 2007-11-06 15:23:01 -0800, tb2000 <tb2000(a)discussions.microsoft.com> said: > 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. "Concise" means that you post the bare minimum of code required to 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 > into the code while I was hoping I could release it when done in my main > thread. Well, for sure a semaphore won't work if both sections of code are run in the same 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. A thread can only execute one statement of code at a time. So if both 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 > 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. You haven't posted enough code for anyone to comment on what's actually 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 > 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. IMHO, it looks to me like a design problem in your own code. I'm not 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 > same time? It is not only illegal, it's impossible. Execution in a given thread 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' > mutex. I don't see how your "homebuilt mutex" changes any of the blocking 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 > 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? Maybe. But only if you post a concise-but-complete example of code that reliably demonstrates the problem. Pete
From: tb2000 on 8 Nov 2007 07:35
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"); } } } |