2

I discovered this post on StackOverflow about events and races which helped me immensely -

C# Events and Thread Safety

The key statement here is that 'Event handlers are required to be robust in the face of being called even after the event has been unsubscribed'

I take this to mean that when you subscribe to an event, you must be prepared for that event to be raised even after you have unsubscribed to it, and do some sort of check to see whether the event should be processed.

This could be something as simple (and ugly) as

bool _acceptEvents;

// event handler
void LoggedIn(object sender, EventArgs a)
{
    if (!_acceptEvents) return;

    Evt("Now logged in");
}

// code to unsubscribe to event
_acceptEvents = false;
_parent.LoggedIn -= new LoggedInEventHandler(LoggedIn);

Now obviously the above code is god awful to look at, but it serves the purpose required.

My question is, what would be a more elegant way of doing this? What is the typical way of handling this situation?

I had thought perhaps you could do

if (!_parent.LoggedIn.Contains(myhandler)) return;

but I tried, and I guess events are designed in a way to prevent you from seeing other subscribers.

What do you think?

Thanks

Community
  • 1
  • 1
NoPyGod
  • 4,905
  • 3
  • 44
  • 72

2 Answers2

1

imho it's a non-existing problem. Have you ever had any code that have generated a bug (or will generate a bug) because an event was called after it was unsubscribed?

jgauffin
  • 99,844
  • 45
  • 235
  • 372
  • The code i'm working on now could very likely encounter this problem. I'm working on socket code, and there are points where asking the socket to close, and having the server close the socket for you, could very likely occur in close proximity. – NoPyGod Apr 17 '11 at 18:57
  • I think the best solution may just be to handle the case where the socket has already started to be closed. It will happen relatively infrequently. And any rate, external events could cause things to change on the socket anyway, so you should code all interactions with the socket defensively. – Andrew Barber Apr 17 '11 at 19:36
  • As for sockets, it can have been closed at any time by network failure, by the remote end point or any other reason that you may not control. What I'm saying is that the socket can be invalid for any number of reasons, and the event will not be the largest problem. – jgauffin Apr 17 '11 at 19:39
  • @NoPyGod: The proper remedy I would think would be to wrap the socket closure in a method that uses Interlocked.Exchange and/or Interlocked.CompareExchange to ensure that any socket-close attempt after the first will be ignored. – supercat Jul 29 '11 at 21:25
0

Indeed events encapsulate a multicast delegate's invocation list so you can not directly remove your subscription.

The main reason that a handler can be called after unregistering (even if you could remove your subscription from the list) is that the event owner could have made a copy of the invocation list.

Look at this code:

var ev = TheEvent;
if(ev != null)
{
    ev(this, EventArgs.Empty);
}

The first line creates a copy of the invocation list to prevent from raising the event in the 4th line on an empty list (and thereby protecting you from a null reference exception)

So if you unregister between lines 1 and 4 your handler will still be invoked because a reference to it has been copied in ev.

Emond
  • 50,210
  • 11
  • 84
  • 115
  • Yes I understand this fully thanks. My question is, how does an event subscriber elegantly code with this particular gotcha in mind? – NoPyGod Apr 17 '11 at 18:49
  • Any kind of flag should do. Just be careful reading an setting the flag from several threads. – Emond Apr 17 '11 at 18:52