Add all missing sequential layout directives to corlib and system.
[mono.git] / mcs / class / corlib / System.Runtime.InteropServices / SafeHandle.cs
index 3efec0fa14fd5a2b6da4dbf6f8246dc436015f1d..52e48cda10ea798161a44672c54fb2eb7214d5e4 100644 (file)
 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 //
+// Notes:
+//     This code is only API complete, but it lacks the runtime support
+//     for CriticalFinalizerObject and any P/Invoke wrapping that might
+//     happen.
+//
+//     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:
+//
+//     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
 {
-       [MonoTODO("Not implemented")]
+       [StructLayout (LayoutKind.Sequential)]
        public abstract class SafeHandle : CriticalFinalizerObject, IDisposable {
+               //
+               // 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;
-
-               [MonoTODO("Not implemented")]
-               protected SafeHandle (IntPtr invalidHandleValue, bool ownsHandle)
+               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;
+                       owns_handle = ownsHandle;
+                       refcount = 1;
+               }
 
-               [MonoTODO("Not implemented")]
-               public void Close () {
-                       throw new NotImplementedException ();
+               [ReliabilityContract (Consistency.WillNotCorruptState, Cer.Success)]
+               public void Close ()
+               {
+                       if (refcount == 0)
+                               throw new ObjectDisposedException (GetType ().FullName);
+
+                       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;
+                               }
+                       }
                }
 
-               [MonoTODO("Not implemented")]
-               public void DangerousAddRef (ref bool success) {
-                       throw new NotImplementedException ();
+               //
+               // I do not know when we could not be able to increment the
+               // reference count and set success to false.   It might just
+               // be a convention used for the following code pattern:
+               //
+               // bool release = false
+               // try { x.DangerousAddRef (ref release); ... }
+               // finally { if (release) x.DangerousRelease (); }
+               //
+               [ReliabilityContract (Consistency.WillNotCorruptState, Cer.MayFail)]
+               public void DangerousAddRef (ref bool success)
+               {
+                       if (refcount <= 0)
+                               throw new ObjectDisposedException (GetType ().FullName);
+
+                       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
+                                       // am left wondering: when would "success" be
+                                       // set to false?
+                                       //
+                                       throw new ObjectDisposedException (GetType ().FullName);
+                               }
+
+                               // 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);
                }
 
-               [MonoTODO("Not implemented")]
-               public IntPtr DangerousGetHandle () {
-                       throw new NotImplementedException ();
+               [ReliabilityContract (Consistency.WillNotCorruptState, Cer.Success)]
+               public IntPtr DangerousGetHandle ()
+               {
+                       if (refcount <= 0){
+                               throw new ObjectDisposedException (GetType ().FullName);
+                       }
+
+                       return handle;
                }
 
-               [MonoTODO("Not implemented")]
-               public void DangerousRelease () {
-                       throw new NotImplementedException ();
+               [ReliabilityContract (Consistency.WillNotCorruptState, Cer.Success)]
+               public void DangerousRelease ()
+               {
+                       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;
+                       }
                }
 
-               [MonoTODO("Not implemented")]
-               public virtual void Dispose () {
-                       throw new NotImplementedException ();
+               [ReliabilityContract (Consistency.WillNotCorruptState, Cer.Success)]
+               public void Dispose ()
+               {
+                       Dispose (true);
+                       GC.SuppressFinalize (this);
                }
 
-               [MonoTODO("Not implemented")]
-               public void SetHandleAsInvalid () {
-                       throw new NotImplementedException ();
+               //
+               // See documentation, this invalidates the handle without
+               // closing it.
+               //
+               [ReliabilityContract (Consistency.WillNotCorruptState, Cer.Success)]
+               public void SetHandleAsInvalid ()
+               {
+                       handle = invalid_handle_value;
                }
                
-               [MonoTODO("Not implemented")]
-               protected virtual void Dispose (bool disposing) {
-                       throw new NotImplementedException ();
+               [ReliabilityContract (Consistency.WillNotCorruptState, Cer.Success)]
+               protected virtual void Dispose (bool disposing)
+               {
+                       if (disposing)
+                               Close ();
+                       else {
+                               //
+                               // The docs say `never call this with disposing=false',
+                               // 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 ();
 
-               [MonoTODO("Not implemented")]
-               protected void SetHandle (IntPtr handle) {
-                       throw new NotImplementedException ();
+               [ReliabilityContract (Consistency.WillNotCorruptState, Cer.Success)]
+               protected void SetHandle (IntPtr handle)
+               {
+                       this.handle = handle;
                }
 
-               [MonoTODO("Not implemented")]
                public bool IsClosed {
+                       [ReliabilityContract (Consistency.WillNotCorruptState, Cer.Success)]
                        get {
-                               throw new NotImplementedException ();
+                               return refcount <= 0;
                        }
                }
 
                public abstract bool IsInvalid {
+                       [ReliabilityContract (Consistency.WillNotCorruptState, Cer.Success)]
                        get;
                }
+
+               ~SafeHandle ()
+               {
+                       if (owns_handle && !IsInvalid){
+                               ReleaseHandle ();
+                               handle = invalid_handle_value;
+                       }
+               }
        }
 }
-#endif