Thread safety of events

A

Armin Zingler

Hi,

I have an object O1 that executes code in thread T1. O1 raises events.
There is an object O2 that runs in thread T2. O2 catches O1's events.

Question:
What happens if O2 is detaching the event handler at the same time as O1
is raising the event? Do I have to handle this special situation explicitly?
Honestly I haven't read/heard that anybody ever cared about this. It would
be pretty much work to do because every event handling object would have to
prevent the event raiser from raising the event while detaching it's own
handlers. Not a good OO way also. I know the "instance members are not
thread safe" part of the Delegate/Multicastdelegate documentation, but I
also think that Events are a basic element in the runtime's infrastructure,
so maybe I don't have to handle the situation explicitly?


Armin
 
P

Peter Duniho

I have an object O1 that executes code in thread T1. O1 raises events.
There is an object O2 that runs in thread T2. O2 catches O1's events.

Question:
What happens if O2 is detaching the event handler at the same time as O1
is raising the event? Do I have to handle this special situation
explicitly?

You sort of do.

First, keep in mind that when an event is raised in O1, even though O2
normally runs in T2, its event handlers are executed on T1, where O1
raised the event. You probably already knew that, but just want to
clarify.

Now, that means you do have the potential of having O2 running on T2
unsubscribing at the same time that O1 running on T1 raises the event.
There's a lot of event-based code out there that doesn't handle this
situation, and usually it's okay because everything is actually running on
the same thread. But when the potential for this condition exists, at a
minimum you need to do something like this:

OnRaiseEvent(...)
{
EventType eventInstance = _eventField;

if (eventInstance != null)
{
eventInstance(...);
}
}

This copies the event member field to the local variable eventInstance so
that if the member field is set to null between the time it's checked for
null and the time it's actually used, that's not an issue. By copying it,
you ensure that what's checked for null is your local variable, not the
member field that could change.

Now, there is the separate issue of O2 having unsubscribed while O1 now
has a reference to it in its local variable. This means that even though
O2 has unsubscribed, it's not quite done yet. O1 still has a reference to
O2, via the local variable, and O2 will still be called when the event
variable is executed.

Whether this is a problem depends a lot on the design of O2 (and even of
O1 to some extent). In particular, if O2 can still respond to an event,
even though you have discarded it somewhere else, then there's really no
problem. On the other hand, if you have a situation where O2 is
disposable, and as part of disposing that's where you unsubscribe, and
something else that is released as part of disposing is required in order
to handle the event (get all that? :) ), then yes...you need to be able to
sychronize access to the thing that is required, and in the event handler
make an explicit check to verify that your object is in fact still in a
state in which it can handle the event. If it's not, then you would just
ignore the event as it if never happened.

Once the O1 event raising method returns, the value in the local variable
is released, and since the O1 instance member containing the event
reference no longer refers to O2 either, you can at that point be assured
the event handler will not be called again. But you do need to be able to
handle correctly the transient condition in which you think you've
unsubscribed but could still get one more raised event.

Pete
 
J

John Saunders [MVP]

Armin Zingler said:
Hi,

I have an object O1 that executes code in thread T1. O1 raises events.
There is an object O2 that runs in thread T2. O2 catches O1's events.

O2 doesn't "catch" events at all. When O1 raises an event, if O2 has a
delegate in the handler list of the event, then the handler in O2 will be
called in thread T1.
 
P

Peter Duniho

[...]
On the other hand, if you have a situation where O2 is disposable, and
as part of disposing that's where you unsubscribe, and something else
that is released as part of disposing is required in order to handle the
event (get all that? :) ), then yes...you need to be able to sychronize
access to the thing that is required

To clarify:

The above is an example of where you'd need to synchronize access. It's
not meant to be the only case. Obviously, putting the unsubscribe
operation somewhere other than the Dispose() method doesn't necessarily
fix things. You could still manage to successfully unsubscribe, then call
Dispose just after the event member field has been copied in the other
thread, but all before the event handlers have been called, whether the
operation to unsubscribe happens in the Dispose or not.

The key here is not where you unsubscribe, but whether disposing the
object somehow invalidates some data structure that is required in order
for the object to successfully handle the event.

Pete
 
A

Armin Zingler

John Saunders said:
O2 doesn't "catch" events at all.

No, it does. I wouldn't say it if it didn't. "Catch" is short for "contains
a procedure that is the event handler of an event of" - I thought, this is a
common term.
When O1 raises an event, if O2 has
a delegate in the handler list of the event, then the handler in O2
will be called in thread T1.

Right.


Armin
 
A

Armin Zingler

Peter Duniho said:
[...]
On the other hand, if you have a situation where O2 is disposable,
and as part of disposing that's where you unsubscribe, and
something else that is released as part of disposing is required
in order to handle the event (get all that? :) ), then yes...you
need to be able to sychronize access to the thing that is required

To clarify:

The above is an example of where you'd need to synchronize access. It's
not meant to be the only case. Obviously, putting the
unsubscribe operation somewhere other than the Dispose() method
doesn't necessarily fix things. You could still manage to
successfully unsubscribe, then call Dispose just after the event
member field has been copied in the other thread, but all before the
event handlers have been called, whether the operation to
unsubscribe happens in the Dispose or not.

The key here is not where you unsubscribe, but whether disposing the
object somehow invalidates some data structure that is required in
order for the object to successfully handle the event.


I think I understand. :) And yes, I know that the event handler is execute
in the same thread.

Though, what you described is not the main problem, sorry. :) I can handle
the states and whatever. What I worried about was the fact, that the
invocation list is modified by removing one event handler (in T2) at the
same time as it is processed in order to raise the event (in T1). Raising
the event might lead to an exception due to the manipulation from the other
thread (T2).

In other words, do I have to do the following? (example only; shall only
show the concept)

Class C1
Public Event Progress()
Public Locker As New Object
Public Sub DoWork()
'...
SyncLock Locker
RaiseEvent Progress()
End SyncLock
'...
End Sub
End Class

Class C2
Public Sub DoWork()
Dim O1 As New C1
addhandler O1.Progress,...
'...
SyncLock O1.Locker
removehandler O1.Progress,...
End SyncLock
End Sub
End Class

In this case, it's ensured that the event will not be raised (in T1) while
the handler is being removed (in T2).


Armin
 
P

Peter Duniho

No, it does. I wouldn't say it if it didn't. "Catch" is short for
"contains a procedure that is the event handler of an event of" - I
thought, this is a common term.

In .NET, "catch" is typically used for exceptions, while "handles" is
typically used for events.

While I might find less point in quibbling over the semantics than John,
if we're going to argue about common usage, John's point is correct.
"Catch" is not at all a common term used to describe event handling.

Pete
 
P

Peter Duniho

[...]
Though, what you described is not the main problem, sorry. :) I can
handle the states and whatever. What I worried about was the fact, that
the invocation list is modified by removing one event handler (in T2) at
the same time as it is processed in order to raise the event (in T1).
Raising the event might lead to an exception due to the manipulation
from the other thread (T2).

I know. That is why in the code I posted, the invocation list is copied
into a local variable before invoking the handlers.
[...]
In this case, it's ensured that the event will not be raised (in T1)
while the handler is being removed (in T2).

Yes, that is another way to do it. I don't like exposing a
synchronization object from one class to another class, but if you choose
to do it that way, I believe it should work fine. The big problem with
that mechanism is that you wind up requiring every subscriber to the event
to use the synchronization object. Since events are supposed to be
general purpose, this is an unusual design, and one that is likely to lead
to maintenance issues in the future.

But if you are the only person who will ever maintain the code and the
event is only to be used in this one very specific situation, it is a risk
that you can probably afford.

You do need to synchronize access somehow though, however you do it.

Pete
 
W

William Stacey [C# MVP]

I agree. You throw and catch exceptions. We "invoke" (i.e. raise or call)
events. catch is not normal usage for events. The thread raising the event
executes/runs the handler (delegate) code (unless you have some other
special need to invoke handler(s) delegates on other threads).

--
William Stacey [C# MVP]



| On Tue, 10 Jul 2007 16:49:27 -0700, Armin Zingler <[email protected]>
| wrote:
|
| >> O2 doesn't "catch" events at all.
| >
| > No, it does. I wouldn't say it if it didn't. "Catch" is short for
| > "contains a procedure that is the event handler of an event of" - I
| > thought, this is a common term.
|
| In .NET, "catch" is typically used for exceptions, while "handles" is
| typically used for events.
|
| While I might find less point in quibbling over the semantics than John,
| if we're going to argue about common usage, John's point is correct.
| "Catch" is not at all a common term used to describe event handling.
|
| Pete
 
A

Armin Zingler

Peter Duniho said:
In .NET, "catch" is typically used for exceptions, while "handles"
is typically used for events.

While I might find less point in quibbling over the semantics than
John, if we're going to argue about common usage, John's point is
correct. "Catch" is not at all a common term used to describe event
handling.

I wrote "catch an event", so what do you think I am referring to? An event
or an exception? Mind the context. Too unambigious to misunderstand.


Armin
 
A

Armin Zingler

William Stacey said:
I agree. You throw and catch exceptions. We "invoke" (i.e. raise or
call) events. catch is not normal usage for events. The thread
raising the event executes/runs the handler (delegate) code (unless
you have some other special need to invoke handler(s) delegates on
other threads).

Strictly speaking yes, but the context was clear, so... Was just a
variation of "handle", "receive", "listen to"(, "catch"). Please note that
English is not my native language, therefore I thought it was clear in the
context. Really, "catch an event" is ambiguous? Ok, I'll remember. :)


Armin
 
P

Peter Duniho

I wrote "catch an event", so what do you think I am referring to? An
event or an exception? Mind the context. Too unambigious to
misunderstand.

Sigh.

It's not a question of whether we understood what you meant. It's a
question of whether you wrote the correct thing.

People post all sorts of crazy things here, and those of us trying to help
them are tasked with deciphering what they wrote. Sometimes we can figure
it out, sometimes we can't. Sometimes we think we figured it out, but we
didn't.

You using the word "catch" when what you really should have written was
"handle" isn't the worst thing we've ever seen around here, by any stretch
of the imagination. I doubt anyone reading your post had any trouble
understanding what you meant. But that doesn't make it correct.

IMHO, it was a waste of bandwidth for John to comment on the misuse.
However, he didn't actually post anything that was incorrect, and it's an
even bigger waste of bandwidth for you to argue about whether "catch" is
the right verb to use or not. I doubt you can find a single page in MSDN
that uses the word "catch" in connection with having the handler for an
event called when that event is raised.

Pete
 
J

Jon Skeet [C# MVP]

Armin Zingler said:
I have an object O1 that executes code in thread T1. O1 raises events.
There is an object O2 that runs in thread T2. O2 catches O1's events.

Question:
What happens if O2 is detaching the event handler at the same time as O1
is raising the event? Do I have to handle this special situation explicitly?

You should do, yes - at least if you foresee people subscribing to or
unsubscribing from the events from a different thread.
Honestly I haven't read/heard that anybody ever cared about this.

See http://pobox.com/~skeet/csharp/threads/lockchoice.shtml
 
W

William Stacey [C# MVP]

| Really, "catch an event" is ambiguous? Ok, I'll remember. :)

IMO, yes. Because your not catching anything, nor is there any context
switch happening - the calling thread is just running some delegates in a
list (top to bottom).
 
A

Armin Zingler

William Stacey said:
| Really, "catch an event" is ambiguous? Ok, I'll remember. :)

IMO, yes. Because your not catching anything, nor is there any
context switch happening - the calling thread is just running some
delegates in a list (top to bottom).


In the German language, it is ok to use variations and different words for
the same thing as long as the context is clear and it is unambigiuous. What
is done here we would call splitting hairs. More important is to understand
each other and not to insist on the correct technical terms - as long as the
discussion is not about the correct terms. "Catch" was more metaphorically
spoken (catch it, handle it, grab it, get it, react on it or whatever). I
feel sorry that I really didn't know that in the English language only the
one technical term "handle" is allowed. Sorry, but I'm restricted by my
school English (which is some years old). Please take this into
consideration.

I also thought that it is valid to say "fire" an event. Now that I know that
I always must use the correct technical term "raise" I will do this
invariably in future.


Armin
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top