Initial commit
[mono.git] / mcs / class / referencesource / mscorlib / system / runtime / interopservices / gchandle.cs
1 // ==++==
2 // 
3 //   Copyright (c) Microsoft Corporation.  All rights reserved.
4 // 
5 // ==--==
6 namespace System.Runtime.InteropServices
7 {    
8     using System;
9     using System.Security.Permissions;
10     using System.Runtime.CompilerServices;
11     using System.Threading;
12     using System.Runtime.Versioning;
13     using System.Diagnostics.Contracts;
14
15     // These are the types of handles used by the EE.  
16     // IMPORTANT: These must match the definitions in ObjectHandle.h in the EE. 
17     // IMPORTANT: If new values are added to the enum the GCHandle::MaxHandleType
18     //            constant must be updated.
19     [Serializable]
20     [System.Runtime.InteropServices.ComVisible(true)]
21     public enum GCHandleType
22     {
23         Weak = 0,
24         WeakTrackResurrection = 1,
25         Normal = 2,
26         Pinned = 3
27     }
28
29     // This class allows you to create an opaque, GC handle to any 
30     // COM+ object. A GC handle is used when an object reference must be
31     // reachable from unmanaged memory.  There are 3 kinds of roots:
32     // Normal - keeps the object from being collected.
33     // Weak - allows object to be collected and handle contents will be zeroed.
34     //          Weak references are zeroed before the finalizer runs, so if the
35     //          object is resurrected in the finalizer the weak reference is
36     //          still zeroed.
37     // WeakTrackResurrection - Same as weak, but stays until after object is
38     //          really gone.
39     // Pinned - same as normal, but allows the address of the actual object
40     //          to be taken.
41     //
42
43     [StructLayout(LayoutKind.Sequential)]
44     [System.Runtime.InteropServices.ComVisible(true)]
45     public struct GCHandle
46     {
47         // IMPORTANT: This must be kept in [....] with the GCHandleType enum.
48         private const GCHandleType MaxHandleType = GCHandleType.Pinned;
49
50 #if MDA_SUPPORTED
51         [System.Security.SecuritySafeCritical]  // auto-generated
52         static GCHandle()
53         {
54             s_probeIsActive = Mda.IsInvalidGCHandleCookieProbeEnabled();
55             if (s_probeIsActive)
56                 s_cookieTable = new GCHandleCookieTable();
57         }
58 #endif
59
60         // Allocate a handle storing the object and the type.
61         [System.Security.SecurityCritical]  // auto-generated
62         internal GCHandle(Object value, GCHandleType type)
63         {
64             // Make sure the type parameter is within the valid range for the enum.
65             if ((uint)type > (uint)MaxHandleType)
66                 throw new ArgumentOutOfRangeException("type", Environment.GetResourceString("ArgumentOutOfRange_Enum"));
67             Contract.EndContractBlock();
68
69             m_handle = InternalAlloc(value, type);
70
71             // Record if the handle is pinned.
72             if (type == GCHandleType.Pinned)
73                 SetIsPinned();
74         }  
75
76         // Used in the conversion functions below.
77         [System.Security.SecurityCritical]  // auto-generated
78         internal GCHandle(IntPtr handle)
79         {
80             InternalCheckDomain(handle);
81             m_handle = handle;
82         }
83
84         // Creates a new GC handle for an object.
85         //
86         // value - The object that the GC handle is created for.
87         // type - The type of GC handle to create.
88         // 
89         // returns a new GC handle that protects the object.
90         [System.Security.SecurityCritical]  // auto-generated_required
91         public static GCHandle Alloc(Object value)
92         {
93             return new GCHandle(value, GCHandleType.Normal);
94         }
95
96         [System.Security.SecurityCritical]  // auto-generated_required
97         public static GCHandle Alloc(Object value, GCHandleType type)
98         {
99             return new GCHandle(value, type);
100         }
101
102
103         // Frees a GC handle.
104         [System.Security.SecurityCritical]  // auto-generated_required
105         public void Free()
106         {
107             // Copy the handle instance member to a local variable. This is required to prevent
108             // race conditions releasing the handle.
109             IntPtr handle = m_handle;
110
111             // Free the handle if it hasn't already been freed.
112             if (handle != IntPtr.Zero && Interlocked.CompareExchange(ref m_handle, IntPtr.Zero, handle) == handle)
113             {
114 #if MDA_SUPPORTED
115                 // If this handle was passed out to unmanaged code, we need to remove it
116                 // from the cookie table.
117                 // NOTE: the entry in the cookie table must be released before the
118                 // internal handle is freed to prevent a race with reusing GC handles.
119                 if (s_probeIsActive)
120                     s_cookieTable.RemoveHandleIfPresent(handle);
121 #endif
122
123 #if WIN32
124                 InternalFree((IntPtr)(((int)handle) & ~1));
125 #else
126                 InternalFree((IntPtr)(((long)handle) & ~1L));
127 #endif
128             }
129             else
130             {
131                 throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_HandleIsNotInitialized"));
132             }
133         }
134         
135         // Target property - allows getting / updating of the handle's referent.
136         public Object Target
137         {
138             [System.Security.SecurityCritical]  // auto-generated_required
139             get
140             {
141                 // Check if the handle was never initialized or was freed.
142                 if (m_handle == IntPtr.Zero)
143                     throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_HandleIsNotInitialized"));
144
145                 return InternalGet(GetHandleValue());
146             }
147     
148             [System.Security.SecurityCritical]  // auto-generated_required
149             set
150             {
151                 // Check if the handle was never initialized or was freed.
152                 if (m_handle == IntPtr.Zero)
153                     throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_HandleIsNotInitialized"));
154
155                 InternalSet(GetHandleValue(), value, IsPinned());
156             }
157         }
158         
159         // Retrieve the address of an object in a Pinned handle.  This throws
160         // an exception if the handle is any type other than Pinned.
161         [System.Security.SecurityCritical]  // auto-generated_required
162         public IntPtr AddrOfPinnedObject()
163         {
164             // Check if the handle was not a pinned handle.
165             if (!IsPinned())
166             {
167                 // Check if the handle was never initialized for was freed.
168                 if (m_handle == IntPtr.Zero)
169                     throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_HandleIsNotInitialized"));
170
171                 // You can only get the address of pinned handles.
172                 throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_HandleIsNotPinned"));
173             }
174
175             // Get the address.
176             return InternalAddrOfPinnedObject(GetHandleValue());
177         }
178
179         // Determine whether this handle has been allocated or not.
180         public bool IsAllocated
181         {
182             get
183             {
184                 return m_handle != IntPtr.Zero;
185             }
186         }
187
188         // Used to create a GCHandle from an int.  This is intended to
189         // be used with the reverse conversion.
190         [System.Security.SecurityCritical]  // auto-generated_required
191         public static explicit operator GCHandle(IntPtr value)
192         {
193             return FromIntPtr(value);
194         }
195
196         [System.Security.SecurityCritical]  // auto-generated_required
197         public static GCHandle FromIntPtr(IntPtr value)
198         {
199             if (value == IntPtr.Zero)
200                 throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_HandleIsNotInitialized"));
201             Contract.EndContractBlock();
202
203             IntPtr handle = value;
204             
205 #if MDA_SUPPORTED
206             if (s_probeIsActive)
207             {
208                 // Make sure this cookie matches up with a GCHandle we've passed out a cookie for.
209                 handle = s_cookieTable.GetHandle(value);
210                 if (IntPtr.Zero == handle)
211                 {
212                     // Fire an MDA if we were unable to retrieve the GCHandle.
213                     Mda.FireInvalidGCHandleCookieProbe(value);
214                     return new GCHandle(IntPtr.Zero);
215                 }
216             }
217 #endif
218
219             return new GCHandle(handle);
220         }
221
222         // Used to get the internal integer representation of the handle out.
223         public static explicit operator IntPtr(GCHandle value)
224         {
225             return ToIntPtr(value);
226         }
227
228         public static IntPtr ToIntPtr(GCHandle value)
229         {
230 #if MDA_SUPPORTED
231             if (s_probeIsActive)
232             {
233                 // Remember that we passed this GCHandle out by storing the cookie we returned so we
234                 //  can later validate.
235                 return s_cookieTable.FindOrAddHandle(value.m_handle);
236             }
237 #endif
238             return value.m_handle;
239         }       
240
241         public override int GetHashCode()
242         {
243             return m_handle.GetHashCode();
244         }
245
246         public override bool Equals(Object o)
247         {
248             GCHandle hnd;
249             
250             // Check that o is a GCHandle first
251             if(o == null || !(o is GCHandle))
252                 return false;
253             else 
254                 hnd = (GCHandle) o;
255
256             return m_handle == hnd.m_handle;
257         }
258
259         public static bool operator ==(GCHandle a, GCHandle b)
260         {
261             return a.m_handle == b.m_handle;
262         }
263
264         public static bool operator !=(GCHandle a, GCHandle b)
265         {
266             return a.m_handle != b.m_handle;
267         }
268
269         internal IntPtr GetHandleValue()
270         {
271 #if WIN32
272             return new IntPtr(((int)m_handle) & ~1);
273 #else
274             return new IntPtr(((long)m_handle) & ~1L);
275 #endif
276         }
277
278         internal bool IsPinned()
279         {
280 #if WIN32
281             return (((int)m_handle) & 1) != 0;
282 #else
283             return (((long)m_handle) & 1) != 0;
284 #endif
285         }
286
287         internal void SetIsPinned()
288         {
289 #if WIN32
290             m_handle = new IntPtr(((int)m_handle) | 1);
291 #else
292             m_handle = new IntPtr(((long)m_handle) | 1L);
293 #endif
294         }
295
296         // Internal native calls that this implementation uses.
297         [System.Security.SecurityCritical]  // auto-generated
298         [MethodImplAttribute(MethodImplOptions.InternalCall)]
299         [ResourceExposure(ResourceScope.None)]
300         internal static extern IntPtr InternalAlloc(Object value, GCHandleType type);
301         [System.Security.SecurityCritical]  // auto-generated
302         [ResourceExposure(ResourceScope.None)]
303         [MethodImplAttribute(MethodImplOptions.InternalCall)]
304         internal static extern void InternalFree(IntPtr handle);
305         [System.Security.SecurityCritical]  // auto-generated
306         [ResourceExposure(ResourceScope.None)]
307         [MethodImplAttribute(MethodImplOptions.InternalCall)]
308         internal static extern Object InternalGet(IntPtr handle);
309         [System.Security.SecurityCritical]  // auto-generated
310         [ResourceExposure(ResourceScope.None)]
311         [MethodImplAttribute(MethodImplOptions.InternalCall)]
312         internal static extern void InternalSet(IntPtr handle, Object value, bool isPinned);
313         [System.Security.SecurityCritical]  // auto-generated
314         [ResourceExposure(ResourceScope.None)]
315         [MethodImplAttribute(MethodImplOptions.InternalCall)]
316         internal static extern Object InternalCompareExchange(IntPtr handle, Object value, Object oldValue, bool isPinned);
317         [System.Security.SecurityCritical]  // auto-generated
318         [ResourceExposure(ResourceScope.None)]
319         [MethodImplAttribute(MethodImplOptions.InternalCall)]
320         internal static extern IntPtr InternalAddrOfPinnedObject(IntPtr handle);
321         [System.Security.SecurityCritical]  // auto-generated
322         [ResourceExposure(ResourceScope.None)]
323         [MethodImplAttribute(MethodImplOptions.InternalCall)]
324         internal static extern void InternalCheckDomain(IntPtr handle);
325         [System.Security.SecurityCritical]  // auto-generated
326         [ResourceExposure(ResourceScope.None)]
327         [MethodImplAttribute(MethodImplOptions.InternalCall)]
328         internal static extern GCHandleType InternalGetHandleType(IntPtr handle);
329
330         // The actual integer handle value that the EE uses internally.
331         private IntPtr m_handle;
332
333 #if MDA_SUPPORTED
334         // The GCHandle cookie table.
335         static private volatile GCHandleCookieTable s_cookieTable = null;
336         static private volatile bool s_probeIsActive = false;
337 #endif
338     }
339 }