1

Let's say you have a piece of code like:

resource = allocateResource();
try { /* dangerous code here  */ }
finally { free(resource); }

I'm not referring to any specific language here, but I guess Java, C#, and C++ would be good examples (assuming you're using __try/__finally in MSVC++).

Is this exception-safe?

Personally, I don't think this is exception-safe, because what if there's an exception before you enter the try block? Then your resource will leak.

I've seen this enough times, though, that I think it I'm missing something... am I? Or is this really unsafe?


Edit:

I'm not asking about allocateResource throwing an exception, but a situation in which you get an exception after that function has returned, but before resource is assigned.

Community
  • 1
  • 1
user541686
  • 205,094
  • 128
  • 528
  • 886
  • 1
    Why not move the resource acquisition into the `try`, then check if it's null before freeing it in the `finally`? Though the allocation itself would still need to be all-or-nothing exception safe... – Cameron Jun 04 '11 at 04:59
  • @Cameron: Because you still can't be sure that the exception can't happen before `resource` is assigned, whether it's inside the block or outside. – user541686 Jun 04 '11 at 05:00
  • Are you worried about a scenario where a custom assignment operator throws an exception? – Chris Bednarski Jun 04 '11 at 05:09
  • 1
    This could be one of the dumbest questions I have read. If `allocateResource` is dangerous then put it in the try block, and it is exception safe. – Nix Jun 04 '11 at 05:09
  • @Chris: No, just a regular assignment, nothing fancy going on here. (You can't even overload assignment in C#/Java.) I'm worried about a situation in which I get an exception thrown before `resource` is fully assigned, **NOT** caused by `allocateResource`, but caused by the OS in a situation I can't foresee. – user541686 Jun 04 '11 at 05:10
  • What kind of exception are you expecting? I can't imagine assigning an variable can throw exception. – J-16 SDiZ Jun 04 '11 at 05:11
  • @J-16: Something caused by the OS, which I might have no control over. – user541686 Jun 04 '11 at 05:14
  • @Nix: Sorry if this is confusing; see my other comments and it should make more sense, as that's not quite the question I'm asking. – user541686 Jun 04 '11 at 05:15
  • @Mehrdad -- which you can have no control over. (1) If this is a computer-wise problem, it won't happen in your process context -- you can't catch it. (2) if this is out of memory issues (which may happen in overcommit), your process would be killed -- you can't catch it. – J-16 SDiZ Jun 04 '11 at 05:24
  • 1
    "a situation in which you get an exception after that function has returned, but before resource is assigned." What exactly do you think can possibly happen, at all, between these two events? Is threading involved or something? – Karl Knechtel Jun 04 '11 at 06:18
  • @Karl: Threading, an interrupt, Windows might shout something, etc. – user541686 Jun 04 '11 at 06:25
  • A question about try/finally with this much low-level detail should probably not try to cover C#, Java and C++. – H H Jun 04 '11 at 08:01

7 Answers7

6

I'm not asking about allocateResource throwing an exception, but a situation in which you get an exception after that function has returned, but before resource is assigned.

It gets very messy to try to handle this aspect of exception safety, not least because the language constructs don't allow you to install your finally handler in the middle of an assignment statement.

My rationale for all this is that if you can't get from the end of a function call to assigning to a variable then your system is already hosed. Who cares if you leak memory when you can't assign to a variable?

David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
  • +1 it seems like you're one of the few people here who understood my question well and gave a reasonable answer, thanks. :) – user541686 Jun 04 '11 at 05:17
3

The point is to have all the code that can throw exception inside the try block. In your case:

try
{
    resource = allocateResource();
    //...
}
finally { free(resource); }

Otherwise - no, of course its not safe.

littleadv
  • 20,100
  • 2
  • 36
  • 50
  • @littleadv: But that still doesn't work -- what if there's an exception before `resource` is actually assigned? – user541686 Jun 04 '11 at 04:59
  • @Mehrdad: Then there's no resource to worry about? – GManNickG Jun 04 '11 at 05:01
  • @GMan? Huh? The resource is allocated, an exception is thrown, and you can't free it because you don't have access to it, since `resource` was never fully assigned. Not sure what you mean... – user541686 Jun 04 '11 at 05:02
  • @Mehhrdad: `some_type resource = null; try { resource = get(); /* ... */ } finally { free(resource); /* check for null for necessary */ }`? Not that it matters, getting it can be done just above the try or first-thing, there's no difference whatsoever. – GManNickG Jun 04 '11 at 05:06
  • @Mehrdad If the allocation fails and the finally block is called, you would just check to see if resource is null or not: `if (resource != null) free(resource);` You declare resource outside of the try and attempt to allocate it inside of the try. This allows the variable to have the proper scope to be accessed in the finally block. – Michael Dean Jun 04 '11 at 05:06
  • @Michael, @GMan: My question is about an exception happening in the **in-between** phase: *before* `resource` is assigned, but *after* the function returns. Isn't that a race condition? – user541686 Jun 04 '11 at 05:07
  • @Mehrdad: Of course this scheme only works if the resource acquisition itself inside `allocateResource()` is "safe" / protected by a try/catch/finally, it trickles down that way. There can't really be an exception "when the function returns" because that is just unwinding the stack. – BrokenGlass Jun 04 '11 at 05:07
  • @Mehrdad: Race conditions have to do with threads, of which I see no mention anywhere. And no, assigning a reference (or pointer) in these languages can't possibly throw an exception. `int* i = new int; delete i;` will never leak. The assignment to `i` cannot throw, ever. – GManNickG Jun 04 '11 at 05:11
  • @BrokenGlass: Really? What if the OS decides to throw a random exception for a reason I can't predict? @GMan: Not necessarily threads, but maybe the OS decides to throw an exception that I wasn't planning on. Can't that happen? – user541686 Jun 04 '11 at 05:12
  • 3
    @Mehrdad: Sure, you can come up with an infinite number of fantastic scenarios where some magical thing happens that ruins everything. But that's not reality. According to these languages, such an assignment is uninterrupted. *If the OS throws an exception, you've already left language-land, and asking about language behavior is pointless.* – GManNickG Jun 04 '11 at 05:15
  • @GMan: Okay, thanks. So what I'm understanding is that there's no solution for this? – user541686 Jun 04 '11 at 05:20
  • @Mehrdad: Like I said, you can always come up with some crazy hypothetical scenario where you simply cannot win. But that's not reality (I think). You *do* know what platforms you'll be using, what the language requires, and can plan accordingly. – GManNickG Jun 04 '11 at 05:24
  • @GMan: My only argument against that is that it's not "crazy", but a completely valid feature of the processor to avoid executing two consecutive instructions consecutively (e.g. say, if an interrupt happens). So there definitely *can* be a race condition in perfectly normally behaving processors. It might be unlikely, but I'm not sure how hypothetical or crazy it is. – user541686 Jun 04 '11 at 05:27
  • @Mehrdad: But notice the difference. You first commented: "There's no solution for this?" and all I said was "Yes, in general there's no solution, but asking for a general solution is crazy", and you said "But it's not crazy in a specific case"...which is exactly what I had said: you *do* know what platforms you'll be using, etc. – GManNickG Jun 04 '11 at 05:32
  • @GMan: Okay, I guess I'll rephrase this: Do you know of *any* modern processor that can't be interrupted in the middle of two instructions? Is this really that atypical or too specific? – user541686 Jun 04 '11 at 05:34
  • @Mehrdad: All processors can have their power removed. :) The existence of the situation is not atypical, *coding* for it is. It's simply not practical to care about it; the language certainly doesn't have support for caring about it. Generally, the cases in which this would happen are the cases in which resource management no longer matters anyway: forceful process shutdown? Resources no longer exist anyway, *and* a forceful shutdown means, by definition, the user no longer cares about the resources (even if he should). Etc. – GManNickG Jun 04 '11 at 05:37
  • @GMan: I think your extremes are a lot worse than mine, but I get your point, thanks. ;) – user541686 Jun 04 '11 at 05:38
2

In the case of C# it is considered unsafe, because a ThreadAbortException can be thrown between the resource allocation and the beginning of the try block. For this reason, C#4 changes the expansion of a using block to move the resource allocation inside the try, and the finally block uses a hidden boolean (or tests against null—I can’t remember exactly) to determine whether the allocation actually took place.

Jeffrey L Whitledge
  • 58,241
  • 9
  • 71
  • 99
  • 1
    Can you provide a reference for the C#4 `using` behavior you describe? Disassembly shows otherwise and the documentation contradicts you: http://msdn.microsoft.com/en-us/library/yh598w02.aspx – Rick Sladkey Jun 04 '11 at 05:36
1

It depends on how allocateResource is written. Given the snippet above allocateResource can result in two outcomes: 1) It allocates and returns a resource 2) It excepts (and therefore does not return a resource)

So if allocateResource is sure to not leak any allocations internally before throw-ing, the above will not leak resource since that method cannot both throw and return.

dkackman
  • 15,179
  • 13
  • 69
  • 123
  • @dkackman: I'm not asking about a situation in which `allocateResource` throws, but a situation in which there's an exception *after* it returns. – user541686 Jun 04 '11 at 05:03
  • Oh. Isn't clear from your snippet. Any code outside of the try but after the allocation would indeed leak. The finally block would never be hit. The stack starts unwinding at the point of the exception before the try block is entered. And the finally executes on the exit of the try block not the method. – dkackman Jun 04 '11 at 05:06
  • 1
    @Mehrdad: You mean between the return and the try block? There is no code between the return and the try block, so no exception. At least none that a finally block could recover from (e.g. an exception could occur if the DLL containing this function was unloaded, but then the finally block is gone too). – Ben Voigt Jun 04 '11 at 05:06
  • @Ben: No code --> No exception? What about a situation in which the OS throws an exception? (Say, if I press Ctrl-C in a console application, if there's an APC callback, etc.) Why can you assume there's no exception because there's no code? Can't things happen that are beyond your control, which could still throw exceptions at the wrong time? – user541686 Jun 04 '11 at 05:09
  • @Mehrdad: We're talking Windows model, right? Windows doesn't have asynchronous signals. Ctrl-C handlers run on another thread. APCs only run during alertable wait. You could get an access violation if the code is unloaded or corrupted, or the memory access flags on the stack are changed, but in either case your finally block would be dead in the water. And I would not consider these "beyond your control". – Ben Voigt Jun 04 '11 at 05:11
  • @Ben: Yeah, I'm talking about Windows (I'm not familiar with other OSes much). So there's *no* situation whatsoever in which you can get an exception thrown in the middle of that code? – user541686 Jun 04 '11 at 05:13
  • 2
    @Mehrdad: I just listed two situations when you could have an exception thrown after the function returns and before the variable is assigned, but both are catastrophic anyway. Another way is `SetThreadContext` -- equally catastrophic (this is how ThreadAbortException is usually implemented, I believe). – Ben Voigt Jun 04 '11 at 05:15
  • Ctrl-C in a console will not leak because the process will clean up and further there will be no exception raised in your code. And I can't see how an asynchornous call could change the example as stated. I think you'd have to contrive a pretty extreme case to leak resource in the above example at which point leaking resource is likely the least of your worries. – dkackman Jun 04 '11 at 05:16
  • @dkackman: "The least of your worries" doesn't quite cut it for me, but thanks for the input anyway. – user541686 Jun 04 '11 at 05:19
  • I say "least of your worries" because if the worry is a few bytes of memory, then the only way the above code can leak is if your process is about crash anyways. If "resource" is something outside of the scope of your process and your process dies then yeah the above code has a problem but you'd have to manage that problem outside of your process in that case so I still see the above as exception safe. Anyway - best of luck. – dkackman Jun 04 '11 at 05:24
0

Only the code inside the try{} block is safe. And only if all the exceptions are catched correctly.

Of course, the code outside the block will not be, and that is exactly the desired behaviour.

Please, note also that the code in the finally{} block can throw exceptions as well, so you might need to include try-catch blocks inside the finally or catch blocks.

e.g.:

try {
    // your code here
} finally {
    try {
        // if the code in finally can throw another exception, you need to catch it inside it
    } catch (Exception e) {
       // probably not much to do besides telling why it failed
    }
} catch (Exception e) {
    try {
        // your error handling routine here
    } catch (Exception e) {
       // probably not much to do besides telling why it failed
    }
}
  • If the exception is thrown before the allocation, then there is nothing to be freed and thus there is nothing to leak.
  • If the exception occurs after the allocation and inside the try/catch block, then it will be handled by finally
  • if the exception can occur after the allocation and before the try/catch block, then the code should be reconsidered and those problematic lines should be moved inside the block.
Aleadam
  • 40,203
  • 9
  • 86
  • 108
0

I think you are answering your own question. If allocateResource allocates and then throws an exception before resource is assigned to the variable then resource will leak, and there is nothing try/finally block can do about it, because try/finally blocks in most languages (including Java and C#) do not actually know about the resources you use inside them.

So, in order for try/finally to be effective allocateResource has to somehow guarantee to be atomic; either allocate and assign to variable without fail, or not allocate at all and fail. Since there is not such guarantee (especially considering unpredictable thread deaths), then try/finally blocks cannot be effectively safe.

Some languages start to support using or with clauses that know about the resource, and are therefore able to close them safely (although that would depend on the implementation of the interpreter/compiler/runtime, etc.)

rodion
  • 14,729
  • 3
  • 53
  • 55
0

In C#, any managed and unreferenced resource will be garbage collected during the next run, so you have no problem there.

Chris Bednarski
  • 3,364
  • 25
  • 33
  • It's not always practical to make resources tolerant of abandonment. Finalizers can often help, but not always nicely. As a simple example, suppose an IEnumerator holds a lock (not uncommon in iterators). Is there any mechanism by which a Finalize() method could clean things up if the enumerator is abandoned without being Disposed? – supercat Aug 10 '11 at 17:04