249

I noticed in System.Threading.TimerBase.Dispose() the method has a try{} finally{} block but the try{} is empty.

Is there any value in using try{} finally{} with an empty try?

http://labs.developerfusion.co.uk/SourceViewer/browse.aspx?assembly=SSCLI&namespace=System.Threading&type=TimerBase

[ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
internal bool Dispose(WaitHandle notifyObject)
{
    bool status = false;
    bool bLockTaken = false;
    RuntimeHelpers.PrepareConstrainedRegions();
    try {
    }
    finally {
        do {
            if (Interlocked.CompareExchange(ref m_lock, 1, 0) == 0) {
                bLockTaken = true;
                try {
                    status = DeleteTimerNative(notifyObject.SafeWaitHandle);
                }
                finally {
                    m_lock = 0;
                }
            }
            Thread.SpinWait(1);
            // yield to processor
        }
        while (!bLockTaken);
        GC.SuppressFinalize(this);
    }

    return status;
}
Matt
  • 25,467
  • 18
  • 120
  • 187
Samuel Neff
  • 73,278
  • 17
  • 138
  • 182
  • System.Diagnostics.Process around line 2144 as well: https://referencesource.microsoft.com/#System/services/monitoring/system/diagnosticts/Process.cs,2114 – Patrick Artner Dec 18 '18 at 07:29

2 Answers2

179

From http://blog.somecreativity.com/2008/04/10/the-empty-try-block-mystery/:

This methodology guards against a Thread.Abort call interrupting the processing. The MSDN page of Thread.Abort says that “Unexecuted finally blocks are executed before the thread is aborted”. So in order to guarantee that your processing finishes even if your thread is aborted in the middle by someone calling Abort on your thread, you can place all your code in the finally block (the alternative is to write code in the “catch” block to determine where you were before “try” was interrupted by Abort and proceed from there if you want to).

DixonD
  • 6,557
  • 5
  • 31
  • 52
danben
  • 80,905
  • 18
  • 123
  • 145
  • 6
    Why not use http://msdn.microsoft.com/en-us/library/system.threading.thread.begincriticalregion.aspx? – Rob Fonseca-Ensor Feb 02 '10 at 17:21
  • 15
    Because that wasn't available until .NET 2.0 – Hans Passant Feb 02 '10 at 18:22
  • 6
    @RobFonseca-Ensor: Because `Thread.BeginCriticalRegion()` does not *prevent* a thread from being aborted, rather tells the runtime that *if* a thread is aborted, then global state is corrupt, and the whole appdomain is up to a mercy killing. – kkm inactive - support strike Dec 05 '14 at 23:15
  • 9
    @HansPassant: `BeginCriticalSection()` really was not there in .NET 1.x, but there is no cause and effect, that you imply in saying *because*. In fact, in .NET 1.x, even a `finally` block could have been interrupted by a thread abort. These mechanisms serve a different purpose: doing work in a `finally` prevents aborting mid-way in code, while `BeginCriticalSection()` only declares to the runtime that the global state is at risk. – kkm inactive - support strike Dec 05 '14 at 23:19
  • 1
    If the developer has a while(true) in the finally would the abort ever complete or could an abort technically be ignored indefinitely? – Max Young Aug 06 '19 at 16:29
  • Interesting. So essentially, finally makes a block of code 'atomic' with respect to the exception handling system. But what happens if an exception other than a ThreadAbortException is thrown during execution of the finally block? – Stewart Nov 09 '20 at 13:55
65

This is to guard against Thread.Abort interrupting a process. Documentation for this method says that:

Unexecuted finally blocks are executed before the thread is aborted.

This is because in order to recover successfully from an error, your code will need to clean up after itself. Since C# doesn't have C++-style destructors, finally and using blocks are the only reliable way of ensuring that such cleanup is performed reliably. Remember that using block turns into this by the compiler:

try {
    ...
}
finally {
    if(obj != null)
        ((IDisposable)obj).Dispose();
}

In .NET 1.x, there was a chance that finally block will get aborted. This behavior was changed in .NET 2.0.

Moreover, empty try blocks never get optimized away by the compiler.

Anton Gogolev
  • 113,561
  • 39
  • 200
  • 288
  • Thanks for the insight on the using block. – Stefan Feb 03 '10 at 03:30
  • @Anton I understand that using is a best practice. But for mocking purposes, sometimes a wrapper class needs to be implemented and the disposable object becomes a private class variable. If we make this wrapper disposable, isn't GC handling the disposing of the private class variable automatically then? – Ozkan Sep 17 '18 at 06:57
  • @Ozkan the GC does not dispose anything automatically. You would need to implement a finalizer, typically calling a `Dispose(false);`. https://learn.microsoft.com/en-us/dotnet/standard/garbage-collection/implementing-dispose – Thorarin Apr 02 '19 at 05:49
  • @Thorarin sorry but you are wrong by saying GC not disposing (finalizing) automatically. – Ozkan Apr 17 '19 at 06:02