+ do {
+ old_state = _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
+ * (because we advertise that as a semantic of Dispose). */
+ if (dispose && (old_state & (int) State.Disposed) != 0) {
+ /* we cannot use `return` in a finally block, so we have to ensure
+ * that we are not releasing the handle */
+ perform_release = false;
+ break;
+ }
+
+ /* 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 ((old_state & RefCount_Mask) == 0)
+ throw new ObjectDisposedException ("handle");
+
+ if ((old_state & RefCount_Mask) != RefCount_One)
+ perform_release = false;
+ else if ((old_state & (int) State.Closed) != 0)
+ perform_release = false;
+ else if (!_ownsHandle)
+ perform_release = false;
+ else if (IsInvalid)
+ perform_release = false;
+ else
+ perform_release = true;
+
+ /* 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 SH_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). */
+ new_state = (old_state & RefCount_Mask) - RefCount_One;
+ if ((old_state & RefCount_Mask) == RefCount_One)
+ new_state |= (int) State.Closed;
+ if (dispose)
+ new_state |= (int) State.Disposed;
+ } while (Interlocked.CompareExchange (ref _state, new_state, old_state) != old_state);
+
+ if (perform_release)
+ ReleaseHandle ();