X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=blobdiff_plain;f=mcs%2Fclass%2Fcorlib%2FSystem.Runtime.InteropServices%2FSafeHandle.cs;h=52e48cda10ea798161a44672c54fb2eb7214d5e4;hb=83dccf8fe7cf27e92061daec886e235ce6c7c57b;hp=3efec0fa14fd5a2b6da4dbf6f8246dc436015f1d;hpb=61baf9612831574d6fd89beb8c7277e18def9dfa;p=mono.git diff --git a/mcs/class/corlib/System.Runtime.InteropServices/SafeHandle.cs b/mcs/class/corlib/System.Runtime.InteropServices/SafeHandle.cs index 3efec0fa14f..52e48cda10e 100644 --- a/mcs/class/corlib/System.Runtime.InteropServices/SafeHandle.cs +++ b/mcs/class/corlib/System.Runtime.InteropServices/SafeHandle.cs @@ -22,76 +22,221 @@ // 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