// For details, see:
// http://blogs.msdn.com/cbrumme/archive/2004/02/20/77460.aspx
//
+// CER-like behavior is implemented for Close and DangerousAddRef
+// via the try/finally uninterruptible pattern in case of async
+// exceptions like ThreadAbortException.
+//
// On implementing SafeHandles:
// http://blogs.msdn.com/bclteam/archive/2005/03/15/396335.aspx
//
// Issues:
-// The System.Runtime.ConstrainedExecution.ReliabilityContractAttribute has
-// not been applied to any APIs here yet.
//
// TODO: Although DangerousAddRef has been implemented, I need to
// find out whether the runtime performs the P/Invoke if the
// handle has been disposed already.
//
+//
-#if NET_2_0
using System;
using System.Runtime.InteropServices;
using System.Runtime.ConstrainedExecution;
+using System.Runtime.CompilerServices;
+using System.Threading;
namespace System.Runtime.InteropServices
{
+ [StructLayout (LayoutKind.Sequential)]
public abstract class SafeHandle : CriticalFinalizerObject, IDisposable {
//
// Warning: the offset of handle is mapped inside the runtime
// MonoSafeHandle
//
protected IntPtr handle;
- object handle_lock = new object ();
IntPtr invalid_handle_value;
int refcount = 0;
bool owns_handle;
+#if NET_2_1
+ protected SafeHandle ()
+ {
+ throw new NotImplementedException ();
+ }
+#endif
+ [ReliabilityContract (Consistency.WillNotCorruptState, Cer.MayFail)]
protected SafeHandle (IntPtr invalidHandleValue, bool ownsHandle)
{
invalid_handle_value = invalidHandleValue;
refcount = 1;
}
+ [ReliabilityContract (Consistency.WillNotCorruptState, Cer.Success)]
public void Close ()
{
if (refcount == 0)
throw new ObjectDisposedException (GetType ().FullName);
- lock (handle_lock){
- refcount--;
- if (refcount == 0 && owns_handle){
+ int newcount = 0, current = 0;
+ bool registered = false;
+ RuntimeHelpers.PrepareConstrainedRegions ();
+ try {
+ do {
+ current = refcount;
+ newcount = current-1;
+
+ // perform changes in finally to avoid async interruptions
+ try {}
+ finally {
+ if (Interlocked.CompareExchange (ref refcount, newcount, current) == current)
+ registered = true;
+ }
+ } while (!registered);
+ } finally {
+ if (registered && newcount == 0 && owns_handle && !IsInvalid){
ReleaseHandle ();
handle = invalid_handle_value;
+ refcount = -1;
}
}
}
// try { x.DangerousAddRef (ref release); ... }
// finally { if (release) x.DangerousRelease (); }
//
+ [ReliabilityContract (Consistency.WillNotCorruptState, Cer.MayFail)]
public void DangerousAddRef (ref bool success)
{
- if (refcount == 0)
+ if (refcount <= 0)
throw new ObjectDisposedException (GetType ().FullName);
- lock (handle_lock){
- if (handle == invalid_handle_value || refcount == 0){
+ bool registered = false;
+ int newcount, current;
+ do {
+ current = refcount;
+ newcount = current + 1;
+
+ if (current <= 0){
//
// In MS, calling sf.Close () followed by a call
// to P/Invoke with SafeHandles throws this, but
//
throw new ObjectDisposedException (GetType ().FullName);
}
-
- refcount++;
- success = true;
- }
+
+ // perform changes in finally to avoid async interruptions
+ RuntimeHelpers.PrepareConstrainedRegions ();
+ try {}
+ finally {
+ if (Interlocked.CompareExchange (ref refcount, newcount, current) == current)
+ registered = success = true;
+ }
+ } while (!registered);
}
+ [ReliabilityContract (Consistency.WillNotCorruptState, Cer.Success)]
public IntPtr DangerousGetHandle ()
{
- if (refcount == 0){
+ if (refcount <= 0){
throw new ObjectDisposedException (GetType ().FullName);
}
return handle;
}
+ [ReliabilityContract (Consistency.WillNotCorruptState, Cer.Success)]
public void DangerousRelease ()
{
- if (refcount == 0)
+ if (refcount <= 0)
throw new ObjectDisposedException (GetType ().FullName);
- lock (handle_lock){
- refcount--;
- if (refcount == 0 && owns_handle){
- ReleaseHandle ();
- handle = invalid_handle_value;
- }
+ int newcount, current;
+ do {
+ current = refcount;
+ newcount = current-1;
+ } while (Interlocked.CompareExchange (ref refcount, newcount, current) != current);
+
+ if (newcount == 0 && owns_handle && !IsInvalid){
+ ReleaseHandle ();
+ handle = invalid_handle_value;
}
}
- public virtual void Dispose ()
+ [ReliabilityContract (Consistency.WillNotCorruptState, Cer.Success)]
+ public void Dispose ()
{
Dispose (true);
GC.SuppressFinalize (this);
// See documentation, this invalidates the handle without
// closing it.
//
+ [ReliabilityContract (Consistency.WillNotCorruptState, Cer.Success)]
public void SetHandleAsInvalid ()
{
handle = invalid_handle_value;
}
+ [ReliabilityContract (Consistency.WillNotCorruptState, Cer.Success)]
protected virtual void Dispose (bool disposing)
{
if (disposing)
}
}
+ [ReliabilityContract (Consistency.WillNotCorruptState, Cer.Success)]
protected abstract bool ReleaseHandle ();
+ [ReliabilityContract (Consistency.WillNotCorruptState, Cer.Success)]
protected void SetHandle (IntPtr handle)
{
this.handle = handle;
}
public bool IsClosed {
+ [ReliabilityContract (Consistency.WillNotCorruptState, Cer.Success)]
get {
- return refcount == 0;
+ return refcount <= 0;
}
}
public abstract bool IsInvalid {
+ [ReliabilityContract (Consistency.WillNotCorruptState, Cer.Success)]
get;
}
~SafeHandle ()
{
- if (owns_handle){
+ if (owns_handle && !IsInvalid){
ReleaseHandle ();
handle = invalid_handle_value;
}
}
}
}
-#endif