// 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