Question: Is a call to GC.KeepAlive(this) required in Dispose?
Answer: Only in pathological cases.
Rationale:
Dispose() must be safe to call multiple times.
To prevent multiple disposal of unmanaged resources, there must exist some kind of object-level flag (e.g., “bool disposed”) or state (e.g., an invalid handle value) to detect if the object has been disposed.
This flag must be set by Dispose (or a method invoked by Dispose) after it is checked.
If the flag is set after disposing the unmanaged resource, then it acts as an equivalent to GC.KeepAlive(this).
This flag must be checked by the finalizer (or a method invoked by the finalizer).
If the flag is set before disposing the unmanaged resource, it is still set after it has been checked.
This example uses a “bool disposed” flag, set before disposing the unmanaged resource:
Example 2 not requiring GC.KeepAlive(this)
This example uses a “bool disposed” flag, set after disposing the unmanaged resource:
Example 3 not requiring GC.KeepAlive(this)
This example uses an “invalid handle” flag:
Example 4 - requiring GC.KeepAlive(this)
It is possible to create a more pathological case where GC.KeepAlive(this) is required; the code below requires GC.KeepAlive because it holds its actual handle value inside of another class:
Output with [2] commented out (as written above):
Thread 1: CloseHandle starting
Thread 1: Garbage collection in CloseHandle!
Thread 2: Finalizer called
Thread 2: CloseHandle starting
Thread 2: Released handle 0x1
Thread 1: CloseHandle continuing after garbage collection
Thread 1: Released handle 0x1
ReleaseHandle double-released a handle! Bad, bad, bad!
Thread 1: CloseHandle ending
Thread 1: Returning from Main
Thread 2: CloseHandle ending
Output with [1] and [2] commented out:
Thread 1: CloseHandle starting
Thread 1: Garbage collection in CloseHandle!
Thread 2: Finalizer called
Thread 2: CloseHandle starting
Thread 2: Released handle 0x1
Thread 2: CloseHandle ending
Thread 1: CloseHandle continuing after garbage collection
Thread 1: Released handle 0x0
ReleaseHandle released a bad handle! Bad, bad, bad!
Thread 1: CloseHandle ending
Thread 1: Returning from Main
Output with neither line commented out, OR with just [1] commented out:
Thread 1: CloseHandle starting
Thread 1: Garbage collection in CloseHandle!
Thread 1: CloseHandle continuing after garbage collection
Thread 1: Released handle 0x1
Thread 1: CloseHandle ending
Thread 1: Returning from Main
Thread 2: Finalizer called
Thread 2: CloseHandle starting