Re: process exit event and thread synchronization

by Peter Duniho on 11/4/2007 8:29:00 AM On 2007-11-04 14:41:02 -0800, tb2000 <tb2000@discussions.microsoft.com> said:

> [...]
> 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???

There is no particular rule about where an event must execute. It will
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

 

Re: process exit event and thread synchronization

by Peter Duniho on 11/5/2007 1:04:00 AM On 2007-11-05 02:24:00 -0800, tb2000 <tb2000@discussions.microsoft.com> said:

> 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.

As I wrote, a single thread cannot deadlock itself.

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

 

Re: process exit event and thread synchronization

by Peter Duniho on 11/6/2007 1:51:00 AM On 2007-11-06 06:37:03 -0800, tb2000 <tb2000@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

 

Re: process exit event and thread synchronization

by Peter Duniho on 11/6/2007 7:54:00 AM On 2007-11-06 15:23:01 -0800, tb2000 <tb2000@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

 

Re: process exit event and thread synchronization

by Peter Duniho on 11/8/2007 2:08:00 AM On 2007-11-08 07:48:01 -0800, tb2000 <tb2000@discussions.microsoft.com> said:

> 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!

I don't really know for sure. However, the behavior you're seeing
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

 

Re: process exit event and thread synchronization

by Peter Duniho on 11/8/2007 3:33:00 AM On 2007-11-08 10:58:03 -0800, tb2000 <tb2000@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

 

Re: process exit event and thread synchronization

by Peter Duniho on 11/9/2007 1:41:00 AM On 2007-11-08 23:55:00 -0800, tb2000 <tb2000@discussions.microsoft.com> said:

> [...]
> 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!

None taken. I just wanted to clarify my relationship (or rather, lack
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:
>
> http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=309324

Let's
>
> see what they come up with for an explanation.

That's great...looks like someone's already performed the first step of
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