// For details, see:
// http://blogs.msdn.com/cbrumme/archive/2004/02/20/77460.aspx
//
+// 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.Threading;
namespace System.Runtime.InteropServices
{
public abstract class SafeHandle : CriticalFinalizerObject, IDisposable {
- object handle_lock = new object ();
+ //
+ // Warning: the offset of handle is mapped inside the runtime
+ // if you move this, you must updated the runtime definition of
+ // MonoSafeHandle
+ //
protected IntPtr handle;
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 ()
{
- lock (handle_lock){
- refcount--;
- if (refcount == 0){
- ReleaseHandle ();
- handle = invalid_handle_value;
- }
+ if (refcount == 0)
+ throw new ObjectDisposedException (GetType ().FullName);
+
+ 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;
+ refcount = -1;
}
}
// try { x.DangerousAddRef (ref release); ... }
// finally { if (release) x.DangerousRelease (); }
//
+ [ReliabilityContract (Consistency.WillNotCorruptState, Cer.MayFail)]
public void DangerousAddRef (ref bool success)
{
- lock (handle_lock){
- refcount++;
- success = true;
- }
+ if (refcount <= 0)
+ throw new ObjectDisposedException (GetType ().FullName);
+
+ 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
+ // am left wondering: when would "success" be
+ // set to false?
+ //
+ throw new ObjectDisposedException (GetType ().FullName);
+ }
+ } while (Interlocked.CompareExchange (ref refcount, newcount, current) != current);
+ success = true;
}
+ [ReliabilityContract (Consistency.WillNotCorruptState, Cer.Success)]
public IntPtr DangerousGetHandle ()
{
+ if (refcount <= 0){
+ throw new ObjectDisposedException (GetType ().FullName);
+ }
+
return handle;
}
+ [ReliabilityContract (Consistency.WillNotCorruptState, Cer.Success)]
public void DangerousRelease ()
{
- lock (handle_lock){
- refcount--;
- if (refcount == 0)
- ReleaseHandle ();
+ if (refcount <= 0)
+ throw new ObjectDisposedException (GetType ().FullName);
+
+ 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)
// the question is whether:
// * The runtime will ever call Dipose(false) for SafeHandles (special runtime case)
// * Whether we should just call ReleaseHandle regardless?
- //
+ //
}
}
+ [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 && !IsInvalid){
+ ReleaseHandle ();
+ handle = invalid_handle_value;
+ }
+ }
}
}
-#endif