X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=blobdiff_plain;f=mcs%2Fclass%2Fcorlib%2FSystem.Runtime.InteropServices%2FSafeHandle.cs;h=8825118ecb83e6e06b849df27427131a5ef4fb0b;hb=d49951ccf584ba637afb1dab7fff714478e3174d;hp=62ebf95ea4cdf560ce1ab2f34bdddfe9cf55eccf;hpb=da4f9e9b2afb23791029d0bb09d78b868aabd870;p=mono.git diff --git a/mcs/class/corlib/System.Runtime.InteropServices/SafeHandle.cs b/mcs/class/corlib/System.Runtime.InteropServices/SafeHandle.cs index 62ebf95ea4c..8825118ecb8 100644 --- a/mcs/class/corlib/System.Runtime.InteropServices/SafeHandle.cs +++ b/mcs/class/corlib/System.Runtime.InteropServices/SafeHandle.cs @@ -22,76 +22,181 @@ // 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 +// +// 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 { - [MonoTODO] 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] + IntPtr invalid_handle_value; + int refcount = 0; + bool owns_handle; + protected SafeHandle (IntPtr invalidHandleValue, bool ownsHandle) { - throw new NotImplementedException (); + invalid_handle_value = invalidHandleValue; + owns_handle = ownsHandle; + refcount = 1; } - [MonoTODO] - public void Close () { - throw new NotImplementedException (); + public void Close () + { + 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){ + ReleaseHandle (); + handle = invalid_handle_value; + } } - [MonoTODO] - 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 (); } + // + public void DangerousAddRef (ref bool success) + { + if (refcount == 0) + throw new ObjectDisposedException (GetType ().FullName); + + int newcount, current; + do { + current = refcount; + newcount = current + 1; + + if (handle == invalid_handle_value || 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; } - [MonoTODO] - public IntPtr DangerousGetHandle () { - throw new NotImplementedException (); + public IntPtr DangerousGetHandle () + { + if (refcount == 0){ + throw new ObjectDisposedException (GetType ().FullName); + } + + return handle; } - [MonoTODO] - public void DangerousRelease () { - throw new NotImplementedException (); + 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){ + ReleaseHandle (); + handle = invalid_handle_value; + } } - [MonoTODO] - public virtual void Dispose () { - throw new NotImplementedException (); + public virtual void Dispose () + { + Dispose (true); + GC.SuppressFinalize (this); } - [MonoTODO] - public void SetHandleAsInvalid () { - throw new NotImplementedException (); + // + // See documentation, this invalidates the handle without + // closing it. + // + public void SetHandleAsInvalid () + { + handle = invalid_handle_value; } - [MonoTODO] - protected virtual void Dispose (bool disposing) { - throw new NotImplementedException (); + 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? + // + } } protected abstract bool ReleaseHandle (); - [MonoTODO] - protected void SetHandle (IntPtr handle) { - throw new NotImplementedException (); + protected void SetHandle (IntPtr handle) + { + this.handle = handle; } - [MonoTODO] public bool IsClosed { get { - throw new NotImplementedException (); + return refcount == 0; } } public abstract bool IsInvalid { get; } + + ~SafeHandle () + { + if (owns_handle){ + ReleaseHandle (); + handle = invalid_handle_value; + } + } } } #endif