/// <summary>Creates a SafeHandle class.</summary> protected SafeHandle(IntPtr invalidHandleValue, bool ownsHandle) { handle = invalidHandleValue; _state = StateBits.RefCountOne; // Ref count 1 and not closed or disposed. _ownsHandle = ownsHandle; if (!ownsHandle) { GC.SuppressFinalize(this); } #if DEBUG else if (s_logFinalization) { int lastError = Marshal.GetLastPInvokeError(); _ctorStackTrace = Environment.StackTrace; Marshal.SetLastPInvokeError(lastError); } #endif _fullyInitialized = true; }
private void Cleanup() { if (IsClosed) { return; } _isClosed = true; if (IsInvalid) { return; } // Save last error from P/Invoke in case the implementation of // ReleaseHandle trashes it (important because this ReleaseHandle could // occur implicitly as part of unmarshaling another P/Invoke). int lastError = Marshal.GetLastPInvokeError(); ReleaseHandle(); Marshal.SetLastPInvokeError(lastError); GC.SuppressFinalize(this); }
private void InternalRelease(bool disposeOrFinalizeOperation) { Debug.Assert(_fullyInitialized || disposeOrFinalizeOperation); // See AddRef above for the design of the synchronization here. Basically we // will try to decrement the current ref count and, if that would take us to // zero refs, set the closed state on the handle as well. bool performRelease = false; // Might have to perform the following steps multiple times due to // interference from other AddRef's and Release's. int oldState, newState; do { // First step is to read the current handle state. We use this cached // value to predicate any modification we might decide to make to the // state). oldState = _state; // If this is a Dispose operation we have additional requirements (to // ensure that Dispose happens at most once as the comments in AddRef // detail). We must check that the dispose bit is not set in the old // state and, in the case of successful state update, leave the disposed // bit set. Silently do nothing if Dispose has already been called. if (disposeOrFinalizeOperation && ((oldState & StateBits.Disposed) != 0)) { return; } // We should never see a ref count of zero (that would imply we have // unbalanced AddRef and Releases). (We might see a closed state before // hitting zero though -- that can happen if SetHandleAsInvalid is // used). if ((oldState & StateBits.RefCount) == 0) { throw new ObjectDisposedException(nameof(SafeHandle), SR.ObjectDisposed_SafeHandleClosed); } // If we're proposing a decrement to zero and the handle is not closed // and we own the handle then we need to release the handle upon a // successful state update. If so we need to check whether the handle is // currently invalid by asking the SafeHandle subclass. We must do this before // transitioning the handle to closed, however, since setting the closed // state will cause IsInvalid to always return true. performRelease = ((oldState & (StateBits.RefCount | StateBits.Closed)) == StateBits.RefCountOne) && _ownsHandle && !IsInvalid; // Attempt the update to the new state, fail and retry if the initial // state has been modified in the meantime. Decrement the ref count by // substracting StateBits.RefCountOne from the state then OR in the bits for // Dispose (if that's the reason for the Release) and closed (if the // initial ref count was 1). newState = oldState - StateBits.RefCountOne; if ((oldState & StateBits.RefCount) == StateBits.RefCountOne) { newState |= StateBits.Closed; } if (disposeOrFinalizeOperation) { newState |= StateBits.Disposed; } } while (Interlocked.CompareExchange(ref _state, newState, oldState) != oldState); // If we get here we successfully decremented the ref count. Additionally we // may have decremented it to zero and set the handle state as closed. In // this case (providng we own the handle) we will call the ReleaseHandle // method on the SafeHandle subclass. if (performRelease) { // Save last error from P/Invoke in case the implementation of ReleaseHandle // trashes it (important because this ReleaseHandle could occur implicitly // as part of unmarshaling another P/Invoke). int lastError = Marshal.GetLastPInvokeError(); ReleaseHandle(); Marshal.SetLastPInvokeError(lastError); } }