2

Background

My program communicates with a device that is usually asynchronous, but can sometimes behave synchronously. I have an event handler that I use for receiving the data asynchronously, then I have a separate event handler that I use when the device is in the synchronous mode. So, when I need to communicate with the device synchronously I will unsubscribe from the asynchronous handler, and subscribe to the synchronous handler as follows:

try
{
    // temporarily receive data synchronously
    this.myDevice.DataReceived 
        -= this.AsynchronousHandler;
    this.myDevice.DataReceived 
        += new SerialDataReceivedEventHandler(this.SynchronousHandler);

    // do the communication...
}
finally
{
    // go back to receiving asynchronously
    this.myDevice.DataReceived 
        -= this.SynchronousHandler;
    this.myDevice.DataReceived 
        += new SerialDataReceivedEventHandler(this.AsynchronousHandler);
}

Now, what I am anticipating is that there could be some kind of a problem with subscription/un-subscribing in the finally clause. Now, I realize that this will be highly unlikely, because the subscription process is the very last statement of the finally clause... However, let's say that I receive some data while the finally clause is still being executed and that the event-handler gets called.

Question

Will the event get handled properly even though it was handled while a finally clause is still being executed? Or, does it not matter at all?

Snoop
  • 2,718
  • 5
  • 24
  • 52
  • 1
    Is there a race here? Is it possible that the data arrives after the unsubscribe, but before the subscribe? – Eric Lippert Jun 17 '16 at 17:29
  • @EricLippert I do not know the answer to that at the moment. But I am more overall just curious what happens (if the data did show up). – Snoop Jun 17 '16 at 17:32

1 Answers1

4

It shouldn't matter. finally blocks are special, but they are not special in a way that matters to your program.

Finally blocks are special because, for example, the runtime will delay a thread abort exception until after control leaves a finally block. (And if you are now thinking "if I have an infinite loop inside a finally block doesn't that mean that the thread cannot be aborted?" then you are thinking correctly. This is just one of many reasons why trying to abort a thread is a bad idea.)

But from the perspective of your control flow, you can think of a finally as simply yet another block of code that runs when the try block is done. (Regardless of whether the try block is done normally or exceptionally.)

My concern here is not the finally, but rather all the possible races. There is a moment in time where the unsubscribe has happened, and the subscribe has not. Could an event be triggered in that moment and then lost, since there is no subscriber? In a single threaded program, plainly not, but suppose the event source is running on another thread? There's a short gap in there where there are no listeners.

Robert Harvey
  • 198,589
  • 55
  • 464
  • 673
Eric Lippert
  • 45,799
  • 22
  • 87
  • 126
  • When you say "infinite loop", do you maybe mean some situation where the data keeps on coming asynchronously so it becomes impossible to leave the finally? – Snoop Jun 17 '16 at 17:37
  • @StevieV: No, I mean, suppose you have a third party library, you start a thread, the library has a method that goes into an infinite loop inside a finally, and you try to abort the thread. Does the thread actually abort? No. Finally blocks are special. If you have a third party library like that, find a better one. In your case, all the finally does is update a list of handlers. Remember, the *handling* doesn't run in the finally, *updating the list of handlers* runs in the finally in your code. – Eric Lippert Jun 17 '16 at 17:39
  • I didn't actually think "if I have an infinite loop inside a `finally` block doesn't that mean that the thread cannot be aborted?" when I read that. I thought "what if you're inside a `finally` block further up the call stack?" – Mason Wheeler Jun 17 '16 at 17:41
  • Why would anyone ever put an infinite loop in a `finally` block in the first place? That sounds like a monumentally bad idea. – Robert Harvey Jun 17 '16 at 17:42
  • @RobertHarvey Isn't an infinite loop always a bad idea? – Snoop Jun 17 '16 at 17:43
  • @StevieV: Only if it's truly infinite. You can have a `while(true)` loop, so long as you have some valid exit condition that can break out of it. A `finally` block is a special case, since the whole purpose of a `finally` block is to provide some cleanup and exit, not hang out there forever. – Robert Harvey Jun 17 '16 at 17:44
  • @StevieV: To put it another way, if you own all of the code, I would expect the correct course of action would be to fix the loop problem in the `finally`, not impose some overly draconian restriction like "never terminate a thread," unless there are other considerations having to do with terminating threads that I don't know about. Really, threaded code should be designed to fall over gracefully if the thread happens to terminate for whatever reason. – Robert Harvey Jun 17 '16 at 17:51
  • @RobertHarvey I've never had to abort or terminate a thread. Using a token to stop a thread always seems to cancel them naturally. – Snoop Jun 17 '16 at 17:53
  • @StevieV: Fair enough, though I would imagine there are other ways a thread could terminate besides actually killing the thread yourself. – Robert Harvey Jun 17 '16 at 17:54
  • 1
    @RobertHarvey: The issue I am alluding to is people sometimes have this crazy idea that they can put ill-behaved or possibly-hostile code in its own thread and then think that they have some sort of control over that thread by way of aborting it if something goes wrong. Bad idea. Hostile code that runs on a thread can pwn the thread quite thoroughly. You need to isolate buggy / hostile code to its own appdomain, or even better, process. – Eric Lippert Jun 17 '16 at 18:16
  • @EricLippert I used to think that was how threads were properly handled. – Snoop Jun 17 '16 at 21:42