Merge pull request #2810 from kumpera/fix_hazard_free
[mono.git] / mcs / class / corlib / System.Runtime.InteropServices / GCHandle.cs
index 67218468375d7caea990078900c470a6bdc7d22a..ec83a3e0756b6f79737d2acb38b87f4ea11ba02b 100644 (file)
@@ -7,7 +7,7 @@
 //
 
 //
-// Copyright (C) 2004 Novell, Inc (http://www.novell.com)
+// Copyright (C) 2004, 2009 Novell, Inc (http://www.novell.com)
 //
 // Permission is hereby granted, free of charge, to any person obtaining
 // a copy of this software and associated documentation files (the
 //
 
 using System;
+using System.Threading;
 using System.Runtime.CompilerServices;
 using System.Runtime.InteropServices;
 
 namespace System.Runtime.InteropServices
 {
 
-#if NET_2_0
        [ComVisible(true)]
-#endif
        [MonoTODO("Struct should be [StructLayout(LayoutKind.Sequential)] but will need to be reordered for that.")]
        public struct GCHandle 
        {
@@ -55,8 +54,11 @@ namespace System.Runtime.InteropServices
                        : this(obj, GCHandleType.Normal)
                {}
 
-               private GCHandle(object value, GCHandleType type)
+               internal GCHandle(object value, GCHandleType type)
                {
+                       // MS does not crash/throw on (most) invalid GCHandleType values (except -1)
+                       if ((type < GCHandleType.Weak) || (type > GCHandleType.Pinned))
+                               type = GCHandleType.Normal;
                        handle = GetTargetHandle (value, 0, type);
                }
 
@@ -74,6 +76,8 @@ namespace System.Runtime.InteropServices
                { 
                        get
                        {
+                               if (!IsAllocated)
+                                       throw new InvalidOperationException (Locale.GetText ("Handle is not allocated"));
                                return GetTarget (handle);
                        } 
                        set
@@ -86,10 +90,10 @@ namespace System.Runtime.InteropServices
                public IntPtr AddrOfPinnedObject()
                {
                        IntPtr res = GetAddrOfPinnedObject(handle);
-                       if (res == IntPtr.Zero)
-                               throw new InvalidOperationException("The handle is not of Pinned type");
                        if (res == (IntPtr)(-1))
                                throw new ArgumentException ("Object contains non-primitive or non-blittable data.");
+                       if (res == (IntPtr)(-2))
+                               throw new InvalidOperationException("Handle is not pinned.");
                        return res;
                }
 
@@ -105,8 +109,17 @@ namespace System.Runtime.InteropServices
 
                public void Free()
                {
-                       FreeHandle(handle);
-                       handle = 0;
+                       // Copy the handle instance member to a local variable. This is required to prevent
+                       // race conditions releasing the handle.
+                       int local_handle = handle;
+
+                       // Free the handle if it hasn't already been freed.
+                       if (local_handle != 0 && Interlocked.CompareExchange (ref handle, 0, local_handle) == local_handle) {
+                               FreeHandle (local_handle);
+                       }
+                       else {
+                               throw new InvalidOperationException ("Handle is not initialized.");
+                       }
                }
                
                public static explicit operator IntPtr (GCHandle value)
@@ -117,7 +130,7 @@ namespace System.Runtime.InteropServices
                public static explicit operator GCHandle(IntPtr value)
                {
                        if (value == IntPtr.Zero)
-                               throw new ArgumentException ("GCHandle value cannot be zero");
+                               throw new InvalidOperationException ("GCHandle value cannot be zero");
                        if (!CheckCurrentDomain ((int)value))
                                throw new ArgumentException ("GCHandle value belongs to a different domain");
                        return new GCHandle (value);
@@ -138,23 +151,19 @@ namespace System.Runtime.InteropServices
                [MethodImplAttribute(MethodImplOptions.InternalCall)]
                private extern static IntPtr GetAddrOfPinnedObject(int handle);
 
-#if NET_2_0
                public static bool operator ==(GCHandle a, GCHandle b)
                {
-                       return a.Equals(b);
+                       return a.handle == b.handle;
                }
 
                public static bool operator !=(GCHandle a, GCHandle b)
                {
-                       return (!(a.Equals(b)));
+                       return !(a == b);
                }
                
                public override bool Equals(object o)
                {
-                       if (o == null || !(o is GCHandle))
-                               return false;
-
-                       return (handle == ((GCHandle)o).handle);
+                       return o is GCHandle ? this == (GCHandle)o : false;
                }
 
                public override int GetHashCode()
@@ -171,7 +180,6 @@ namespace System.Runtime.InteropServices
                {
                        return (IntPtr)value;
                }
-#endif
        } 
 }