开发者

Is Try/Finally actually exception-safe?

开发者 https://www.devze.com 2023-03-10 03:24 出处:网络
Let\'s say you have a piece of code like: resource = allocateResource(); try { /* dangerous code here*/ }

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 (assum开发者_开发知识库ing 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.


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?


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.


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.


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.


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.


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


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

0

精彩评论

暂无评论...
验证码 换一张
取 消