2010-04-02 Miguel de Icaza <miguel@novell.com>
[mono.git] / mcs / class / corlib / System.Runtime.InteropServices / SafeHandle.cs
index d7527d8c83d2b00d038b9b77a5dede82d60dd5f2..e1b0a70ffb24ddc408c4b20af7f1c0b81ae2814f 100644 (file)
 //     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;
@@ -52,14 +75,22 @@ namespace System.Runtime.InteropServices
                        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;
                        }
                }
 
@@ -72,29 +103,60 @@ namespace System.Runtime.InteropServices
                // 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);
@@ -104,11 +166,13 @@ namespace System.Runtime.InteropServices
                // 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)
@@ -119,26 +183,37 @@ namespace System.Runtime.InteropServices
                                // 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