First set of licensing changes
[mono.git] / mono / metadata / boehm-gc.c
index aa58c6af9de1d6bba8b662c3d0c81ba1a4b13979..43844ad98304cfed42194dc622df44a161fe27ab 100644 (file)
@@ -4,6 +4,7 @@
  * Copyright 2001-2003 Ximian, Inc (http://www.ximian.com)
  * Copyright 2004-2011 Novell, Inc (http://www.novell.com)
  * Copyright 2011-2012 Xamarin, Inc (http://www.xamarin.com)
+ * Licensed under the MIT license. See LICENSE file in the project root for full license information.
  */
 
 #include "config.h"
@@ -11,7 +12,7 @@
 #include <string.h>
 
 #define GC_I_HIDE_POINTERS
-#include <mono/metadata/gc-internal.h>
+#include <mono/metadata/gc-internals.h>
 #include <mono/metadata/mono-gc.h>
 #include <mono/metadata/profiler-private.h>
 #include <mono/metadata/class-internals.h>
 #include <mono/metadata/runtime.h>
 #include <mono/metadata/sgen-toggleref.h>
 #include <mono/utils/atomic.h>
-#include <mono/utils/mono-logger-internal.h>
+#include <mono/utils/mono-logger-internals.h>
 #include <mono/utils/mono-memory-model.h>
 #include <mono/utils/mono-time.h>
 #include <mono/utils/mono-threads.h>
 #include <mono/utils/dtrace.h>
 #include <mono/utils/gc_wrapper.h>
-#include <mono/utils/mono-mutex.h>
+#include <mono/utils/mono-os-mutex.h>
 #include <mono/utils/mono-counters.h>
 
 #if HAVE_BOEHM_GC
@@ -61,6 +62,34 @@ register_test_toggleref_callback (void);
 #define BOEHM_GC_BIT_FINALIZER_AWARE 1
 static MonoGCFinalizerCallbacks fin_callbacks;
 
+/* GC Handles */
+
+static mono_mutex_t handle_section;
+#define lock_handles(handles) mono_os_mutex_lock (&handle_section)
+#define unlock_handles(handles) mono_os_mutex_unlock (&handle_section)
+
+typedef struct {
+       guint32  *bitmap;
+       gpointer *entries;
+       guint32   size;
+       guint8    type;
+       guint     slot_hint : 24; /* starting slot for search in bitmap */
+       /* 2^16 appdomains should be enough for everyone (though I know I'll regret this in 20 years) */
+       /* we alloc this only for weak refs, since we can get the domain directly in the other cases */
+       guint16  *domain_ids;
+} HandleData;
+
+#define EMPTY_HANDLE_DATA(type) {NULL, NULL, 0, (type), 0, NULL}
+
+/* weak and weak-track arrays will be allocated in malloc memory 
+ */
+static HandleData gc_handles [] = {
+       EMPTY_HANDLE_DATA (HANDLE_WEAK),
+       EMPTY_HANDLE_DATA (HANDLE_WEAK_TRACK),
+       EMPTY_HANDLE_DATA (HANDLE_NORMAL),
+       EMPTY_HANDLE_DATA (HANDLE_PINNED)
+};
+
 static void
 mono_gc_warning (char *msg, GC_word arg)
 {
@@ -152,7 +181,7 @@ mono_gc_base_init (void)
                        for (char **ptr = opts; ptr && *ptr; ptr ++) {
                                char *opt = *ptr;
                                if (!strcmp (opt, "do-not-finalize")) {
-                                       do_not_finalize = 1;
+                                       mono_do_not_finalize = 1;
                                } else if (!strcmp (opt, "log-finalizers")) {
                                        log_finalizers = 1;
                                }
@@ -162,12 +191,12 @@ mono_gc_base_init (void)
 
        GC_init ();
 
-       GC_oom_fn = mono_gc_out_of_memory;
        GC_set_warn_proc (mono_gc_warning);
        GC_finalize_on_demand = 1;
        GC_finalizer_notifier = mono_gc_finalize_notify;
 
        GC_init_gcj_malloc (5, NULL);
+       GC_allow_register_threads ();
 
        if ((env = g_getenv ("MONO_GC_PARAMS"))) {
                char **ptr, **opts = g_strsplit (env, ",", -1);
@@ -206,17 +235,28 @@ mono_gc_base_init (void)
        memset (&cb, 0, sizeof (cb));
        cb.thread_register = boehm_thread_register;
        cb.thread_unregister = boehm_thread_unregister;
-       cb.mono_method_is_critical = (gpointer)mono_runtime_is_critical_method;
+       cb.mono_method_is_critical = (gboolean (*)(void *))mono_runtime_is_critical_method;
 
        mono_threads_init (&cb, sizeof (MonoThreadInfo));
-       mono_mutex_init (&mono_gc_lock);
+       mono_os_mutex_init (&mono_gc_lock);
+       mono_os_mutex_init_recursive (&handle_section);
 
        mono_thread_info_attach (&dummy);
 
        mono_gc_enable_events ();
+
+       MONO_GC_REGISTER_ROOT_FIXED (gc_handles [HANDLE_NORMAL].entries, MONO_ROOT_SOURCE_GC_HANDLE, "gc handles table");
+       MONO_GC_REGISTER_ROOT_FIXED (gc_handles [HANDLE_PINNED].entries, MONO_ROOT_SOURCE_GC_HANDLE, "gc handles table");
+
        gc_initialized = TRUE;
 }
 
+void
+mono_gc_base_cleanup (void)
+{
+       GC_finalizer_notifier = NULL;
+}
+
 /**
  * mono_gc_collect:
  * @generation: GC generation identifier
@@ -331,8 +371,6 @@ mono_gc_is_gc_thread (void)
        return GC_thread_is_registered ();
 }
 
-extern int GC_thread_register_foreign (void *base_addr);
-
 gboolean
 mono_gc_register_thread (void *baseptr)
 {
@@ -342,13 +380,15 @@ mono_gc_register_thread (void *baseptr)
 static void*
 boehm_thread_register (MonoThreadInfo* info, void *baseptr)
 {
-       if (mono_gc_is_gc_thread())
-               return info;
-#if !defined(HOST_WIN32)
-       return GC_thread_register_foreign (baseptr) ? info : NULL;
-#else
-       return NULL;
-#endif
+       struct GC_stack_base sb;
+       int res;
+
+       /* TODO: use GC_get_stack_base instead of baseptr. */
+       sb.mem_base = baseptr;
+       res = GC_register_my_thread (&sb);
+       if (res == GC_UNIMPLEMENTED)
+           return NULL; /* Cannot happen with GC v7+. */
+       return info;
 }
 
 static void
@@ -365,7 +405,7 @@ boehm_thread_unregister (MonoThreadInfo *p)
 gboolean
 mono_object_is_alive (MonoObject* o)
 {
-       return GC_is_marked ((gpointer)o);
+       return GC_is_marked ((ptr_t)o);
 }
 
 int
@@ -377,7 +417,7 @@ mono_gc_walk_heap (int flags, MonoGCReferences callback, void *data)
 static gint64 gc_start_time;
 
 static void
-on_gc_notification (GCEventType event)
+on_gc_notification (GC_EventType event)
 {
        MonoGCEvent e = (MonoGCEvent)event;
 
@@ -456,7 +496,7 @@ on_gc_heap_resize (size_t new_size)
 void
 mono_gc_enable_events (void)
 {
-       GC_notify_event = on_gc_notification;
+       GC_set_on_collection_event (on_gc_notification);
        GC_on_heap_resize = on_gc_heap_resize;
 }
 
@@ -469,7 +509,7 @@ mono_gc_enable_alloc_events (void)
 }
 
 int
-mono_gc_register_root (char *start, size_t size, void *descr)
+mono_gc_register_root (char *start, size_t size, void *descr, MonoGCRootSource source, const char *msg)
 {
        /* for some strange reason, they want one extra byte on the end */
        GC_add_roots (start, start + size + 1);
@@ -487,7 +527,7 @@ mono_gc_deregister_root (char* addr)
 #endif
 }
 
-void
+static void
 mono_gc_weak_link_add (void **link_addr, MonoObject *obj, gboolean track)
 {
        /* libgc requires that we use HIDE_POINTER... */
@@ -498,7 +538,7 @@ mono_gc_weak_link_add (void **link_addr, MonoObject *obj, gboolean track)
                GC_GENERAL_REGISTER_DISAPPEARING_LINK (link_addr, obj);
 }
 
-void
+static void
 mono_gc_weak_link_remove (void **link_addr, gboolean track)
 {
        if (track)
@@ -511,14 +551,14 @@ mono_gc_weak_link_remove (void **link_addr, gboolean track)
 static gpointer
 reveal_link (gpointer link_addr)
 {
-       void **link_a = link_addr;
+       void **link_a = (void **)link_addr;
        return REVEAL_POINTER (*link_a);
 }
 
-MonoObject*
+static MonoObject *
 mono_gc_weak_link_get (void **link_addr)
 {
-       MonoObject *obj = GC_call_with_alloc_lock (reveal_link, link_addr);
+       MonoObject *obj = (MonoObject *)GC_call_with_alloc_lock (reveal_link, link_addr);
        if (obj == (MonoObject *) -1)
                return NULL;
        return obj;
@@ -560,7 +600,7 @@ mono_gc_make_root_descr_all_refs (int numbits)
 }
 
 void*
-mono_gc_alloc_fixed (size_t size, void *descr)
+mono_gc_alloc_fixed (size_t size, void *descr, MonoGCRootSource source, const char *msg)
 {
        /* To help track down typed allocation bugs */
        /*
@@ -589,16 +629,22 @@ mono_gc_alloc_obj (MonoVTable *vtable, size_t size)
        MonoObject *obj;
 
        if (!vtable->klass->has_references) {
-               obj = GC_MALLOC_ATOMIC (size);
+               obj = (MonoObject *)GC_MALLOC_ATOMIC (size);
+               if (G_UNLIKELY (!obj))
+                       return NULL;
 
                obj->vtable = vtable;
                obj->synchronisation = NULL;
 
                memset ((char *) obj + sizeof (MonoObject), 0, size - sizeof (MonoObject));
        } else if (vtable->gc_descr != GC_NO_DESCRIPTOR) {
-               obj = GC_GCJ_MALLOC (size, vtable);
+               obj = (MonoObject *)GC_GCJ_MALLOC (size, vtable);
+               if (G_UNLIKELY (!obj))
+                       return NULL;
        } else {
-               obj = GC_MALLOC (size);
+               obj = (MonoObject *)GC_MALLOC (size);
+               if (G_UNLIKELY (!obj))
+                       return NULL;
 
                obj->vtable = vtable;
        }
@@ -615,16 +661,22 @@ mono_gc_alloc_vector (MonoVTable *vtable, size_t size, uintptr_t max_length)
        MonoArray *obj;
 
        if (!vtable->klass->has_references) {
-               obj = GC_MALLOC_ATOMIC (size);
+               obj = (MonoArray *)GC_MALLOC_ATOMIC (size);
+               if (G_UNLIKELY (!obj))
+                       return NULL;
 
                obj->obj.vtable = vtable;
                obj->obj.synchronisation = NULL;
 
                memset ((char *) obj + sizeof (MonoObject), 0, size - sizeof (MonoObject));
        } else if (vtable->gc_descr != GC_NO_DESCRIPTOR) {
-               obj = GC_GCJ_MALLOC (size, vtable);
+               obj = (MonoArray *)GC_GCJ_MALLOC (size, vtable);
+               if (G_UNLIKELY (!obj))
+                       return NULL;
        } else {
-               obj = GC_MALLOC (size);
+               obj = (MonoArray *)GC_MALLOC (size);
+               if (G_UNLIKELY (!obj))
+                       return NULL;
 
                obj->obj.vtable = vtable;
        }
@@ -643,16 +695,22 @@ mono_gc_alloc_array (MonoVTable *vtable, size_t size, uintptr_t max_length, uint
        MonoArray *obj;
 
        if (!vtable->klass->has_references) {
-               obj = GC_MALLOC_ATOMIC (size);
+               obj = (MonoArray *)GC_MALLOC_ATOMIC (size);
+               if (G_UNLIKELY (!obj))
+                       return NULL;
 
                obj->obj.vtable = vtable;
                obj->obj.synchronisation = NULL;
 
                memset ((char *) obj + sizeof (MonoObject), 0, size - sizeof (MonoObject));
        } else if (vtable->gc_descr != GC_NO_DESCRIPTOR) {
-               obj = GC_GCJ_MALLOC (size, vtable);
+               obj = (MonoArray *)GC_GCJ_MALLOC (size, vtable);
+               if (G_UNLIKELY (!obj))
+                       return NULL;
        } else {
-               obj = GC_MALLOC (size);
+               obj = (MonoArray *)GC_MALLOC (size);
+               if (G_UNLIKELY (!obj))
+                       return NULL;
 
                obj->obj.vtable = vtable;
        }
@@ -671,7 +729,9 @@ mono_gc_alloc_array (MonoVTable *vtable, size_t size, uintptr_t max_length, uint
 void *
 mono_gc_alloc_string (MonoVTable *vtable, size_t size, gint32 len)
 {
-       MonoString *obj = GC_MALLOC_ATOMIC (size);
+       MonoString *obj = (MonoString *)GC_MALLOC_ATOMIC (size);
+       if (G_UNLIKELY (!obj))
+               return NULL;
 
        obj->object.vtable = vtable;
        obj->object.synchronisation = NULL;
@@ -684,6 +744,18 @@ mono_gc_alloc_string (MonoVTable *vtable, size_t size, gint32 len)
        return obj;
 }
 
+void*
+mono_gc_alloc_mature (MonoVTable *vtable, size_t size)
+{
+       return mono_gc_alloc_obj (vtable, size);
+}
+
+void*
+mono_gc_alloc_pinned_obj (MonoVTable *vtable, size_t size)
+{
+       return mono_gc_alloc_obj (vtable, size);
+}
+
 int
 mono_gc_invoke_finalizers (void)
 {
@@ -730,7 +802,7 @@ mono_gc_wbarrier_generic_store (gpointer ptr, MonoObject* value)
 void
 mono_gc_wbarrier_generic_store_atomic (gpointer ptr, MonoObject *value)
 {
-       InterlockedWritePointer (ptr, value);
+       InterlockedWritePointer ((volatile gpointer *)ptr, value);
 }
 
 void
@@ -766,7 +838,7 @@ mono_gc_get_suspend_signal (void)
 int
 mono_gc_get_restart_signal (void)
 {
-       return GC_get_restart_signal ();
+       return GC_get_thr_restart_signal ();
 }
 
 #if defined(USE_COMPILER_TLS) && defined(__linux__) && (defined(__i386__) || defined(__x86_64__))
@@ -801,7 +873,7 @@ create_allocator (int atype, int tls_key, gboolean slowpath)
        MonoMethod *res;
        MonoMethodSignature *csig;
        const char *name = NULL;
-       AllocatorWrapperInfo *info;
+       WrapperInfo *info;
 
        if (atype == ATYPE_FREEPTR) {
                name = slowpath ? "SlowAllocPtrfree" : "AllocPtrfree";
@@ -885,11 +957,17 @@ create_allocator (int atype, int tls_key, gboolean slowpath)
        mono_mb_emit_byte (mb, 0x0D); /* CEE_MONO_TLS */
        mono_mb_emit_i4 (mb, tls_key);
        if (atype == ATYPE_FREEPTR || atype == ATYPE_FREEPTR_FOR_BOX || atype == ATYPE_STRING)
-               mono_mb_emit_icon (mb, G_STRUCT_OFFSET (struct GC_Thread_Rep, ptrfree_freelists));
+               mono_mb_emit_icon (mb, G_STRUCT_OFFSET (struct GC_Thread_Rep, tlfs)
+                                       + G_STRUCT_OFFSET (struct thread_local_freelists,
+                                                          ptrfree_freelists));
        else if (atype == ATYPE_NORMAL)
-               mono_mb_emit_icon (mb, G_STRUCT_OFFSET (struct GC_Thread_Rep, normal_freelists));
+               mono_mb_emit_icon (mb, G_STRUCT_OFFSET (struct GC_Thread_Rep, tlfs)
+                                       + G_STRUCT_OFFSET (struct thread_local_freelists,
+                                                          normal_freelists));
        else if (atype == ATYPE_GCJ)
-               mono_mb_emit_icon (mb, G_STRUCT_OFFSET (struct GC_Thread_Rep, gcj_freelists));
+               mono_mb_emit_icon (mb, G_STRUCT_OFFSET (struct GC_Thread_Rep, tlfs)
+                                       + G_STRUCT_OFFSET (struct thread_local_freelists,
+                                                          gcj_freelists));
        else
                g_assert_not_reached ();
        mono_mb_emit_byte (mb, MONO_CEE_ADD);
@@ -991,22 +1069,21 @@ create_allocator (int atype, int tls_key, gboolean slowpath)
  always_slowpath:
        if (atype == ATYPE_STRING) {
                mono_mb_emit_ldarg (mb, 1);
-               mono_mb_emit_icall (mb, mono_string_alloc);
+               mono_mb_emit_icall (mb, ves_icall_string_alloc);
        } else {
                mono_mb_emit_ldarg (mb, 0);
-               mono_mb_emit_icall (mb, mono_object_new_specific);
+               mono_mb_emit_icall (mb, ves_icall_object_new_specific);
        }
 
        mono_mb_emit_byte (mb, MONO_CEE_RET);
 
-       res = mono_mb_create_method (mb, csig, 8);
-       mono_mb_free (mb);
-       mono_method_get_header (res)->init_locals = FALSE;
+       info = mono_wrapper_info_create (mb, WRAPPER_SUBTYPE_NONE);
+       info->d.alloc.gc_name = "boehm";
+       info->d.alloc.alloc_type = atype;
+       mb->init_locals = FALSE;
 
-       info = mono_image_alloc0 (mono_defaults.corlib, sizeof (AllocatorWrapperInfo));
-       info->gc_name = "boehm";
-       info->alloc_type = atype;
-       mono_marshal_set_wrapper_info (res, info);
+       res = mono_mb_create (mb, csig, 8, info);
+       mono_mb_free (mb);
 
        return res;
 }
@@ -1056,6 +1133,8 @@ mono_gc_get_managed_allocator (MonoClass *klass, gboolean for_box, gboolean know
                return NULL;
        if (klass->byval_arg.type == MONO_TYPE_STRING) {
                atype = ATYPE_STRING;
+       } else if (!known_instance_size) {
+               return NULL;
        } else if (!klass->has_references) {
                if (for_box)
                        atype = ATYPE_FREEPTR_FOR_BOX;
@@ -1101,7 +1180,7 @@ mono_gc_get_managed_allocator_by_type (int atype, gboolean slowpath)
                return res;
 
        res = create_allocator (atype, TLS_KEY_BOEHM_GC_THREAD, slowpath);
-       mono_mutex_lock (&mono_gc_lock);
+       mono_os_mutex_lock (&mono_gc_lock);
        if (cache [atype]) {
                mono_free_method (res);
                res = cache [atype];
@@ -1109,7 +1188,7 @@ mono_gc_get_managed_allocator_by_type (int atype, gboolean slowpath)
                mono_memory_barrier ();
                cache [atype] = res;
        }
-       mono_mutex_unlock (&mono_gc_lock);
+       mono_os_mutex_unlock (&mono_gc_lock);
        return res;
 }
 
@@ -1307,7 +1386,7 @@ mono_gc_register_for_finalization (MonoObject *obj, void *user_data)
        g_assert (GC_base (obj) == (char*)obj - offset);
 #endif
 
-       GC_REGISTER_FINALIZER_NO_ORDER ((char*)obj - offset, user_data, GUINT_TO_POINTER (offset), NULL, NULL);
+       GC_REGISTER_FINALIZER_NO_ORDER ((char*)obj - offset, (GC_finalization_proc)user_data, GUINT_TO_POINTER (offset), NULL, NULL);
 }
 
 #ifndef HOST_WIN32
@@ -1328,10 +1407,10 @@ BOOL APIENTRY mono_gc_dllmain (HMODULE module_handle, DWORD reason, LPVOID reser
 #endif
 
 guint
-mono_gc_get_vtable_bits (MonoClass *class)
+mono_gc_get_vtable_bits (MonoClass *klass)
 {
        if (fin_callbacks.is_class_finalization_aware) {
-               if (fin_callbacks.is_class_finalization_aware (class))
+               if (fin_callbacks.is_class_finalization_aware (klass))
                        return BOEHM_GC_BIT_FINALIZER_AWARE;
        }
        return 0;
@@ -1380,23 +1459,19 @@ mono_gc_make_root_descr_user (MonoGCRootMarkFunc marker)
        return NULL;
 }
 
-gboolean
-mono_gc_set_allow_synchronous_major (gboolean flag)
-{
-       return flag;
-}
 /* Toggleref support */
 
 void
 mono_gc_toggleref_add (MonoObject *object, mono_bool strong_ref)
 {
-       GC_toggleref_add ((GC_PTR)object, (int)strong_ref);
+       if (GC_toggleref_add ((GC_PTR)object, (int)strong_ref) != GC_SUCCESS)
+           g_error ("GC_toggleref_add failed\n");
 }
 
 void
 mono_gc_toggleref_register_callback (MonoToggleRefStatus (*proccess_toggleref) (MonoObject *obj))
 {
-       GC_toggleref_register_callback ((int (*) (GC_PTR obj)) proccess_toggleref);
+       GC_set_toggleref_func ((GC_ToggleRefStatus (*) (GC_PTR obj)) proccess_toggleref);
 }
 
 /* Test support code */
@@ -1405,7 +1480,7 @@ static MonoToggleRefStatus
 test_toggleref_callback (MonoObject *obj)
 {
        static MonoClassField *mono_toggleref_test_field;
-       int status = MONO_TOGGLE_REF_DROP;
+       MonoToggleRefStatus status = MONO_TOGGLE_REF_DROP;
 
        if (!mono_toggleref_test_field) {
                mono_toggleref_test_field = mono_class_get_field_from_name (mono_object_get_class (obj), "__test");
@@ -1445,7 +1520,269 @@ mono_gc_register_finalizer_callbacks (MonoGCFinalizerCallbacks *callbacks)
 
        fin_callbacks = *callbacks;
 
-       GC_set_finalizer_notify_proc ((void (*) (GC_PTR))fin_notifier);
+       GC_set_await_finalize_proc ((void (*) (GC_PTR))fin_notifier);
+}
+
+#define BITMAP_SIZE (sizeof (*((HandleData *)NULL)->bitmap) * CHAR_BIT)
+
+static inline gboolean
+slot_occupied (HandleData *handles, guint slot) {
+       return handles->bitmap [slot / BITMAP_SIZE] & (1 << (slot % BITMAP_SIZE));
+}
+
+static inline void
+vacate_slot (HandleData *handles, guint slot) {
+       handles->bitmap [slot / BITMAP_SIZE] &= ~(1 << (slot % BITMAP_SIZE));
+}
+
+static inline void
+occupy_slot (HandleData *handles, guint slot) {
+       handles->bitmap [slot / BITMAP_SIZE] |= 1 << (slot % BITMAP_SIZE);
+}
+
+static int
+find_first_unset (guint32 bitmap)
+{
+       int i;
+       for (i = 0; i < 32; ++i) {
+               if (!(bitmap & (1 << i)))
+                       return i;
+       }
+       return -1;
+}
+
+static void
+handle_data_alloc_entries (HandleData *handles)
+{
+       handles->size = 32;
+       if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) {
+               handles->entries = (void **)g_malloc0 (sizeof (*handles->entries) * handles->size);
+               handles->domain_ids = (guint16 *)g_malloc0 (sizeof (*handles->domain_ids) * handles->size);
+       } else {
+               handles->entries = (void **)mono_gc_alloc_fixed (sizeof (*handles->entries) * handles->size, NULL, MONO_ROOT_SOURCE_GC_HANDLE, "gc handles table");
+       }
+       handles->bitmap = (guint32 *)g_malloc0 (handles->size / CHAR_BIT);
+}
+
+static gint
+handle_data_next_unset (HandleData *handles)
+{
+       gint slot;
+       for (slot = handles->slot_hint; slot < handles->size / BITMAP_SIZE; ++slot) {
+               if (handles->bitmap [slot] == 0xffffffff)
+                       continue;
+               handles->slot_hint = slot;
+               return find_first_unset (handles->bitmap [slot]);
+       }
+       return -1;
+}
+
+static gint
+handle_data_first_unset (HandleData *handles)
+{
+       gint slot;
+       for (slot = 0; slot < handles->slot_hint; ++slot) {
+               if (handles->bitmap [slot] == 0xffffffff)
+                       continue;
+               handles->slot_hint = slot;
+               return find_first_unset (handles->bitmap [slot]);
+       }
+       return -1;
+}
+
+/* Returns the index of the current slot in the bitmap. */
+static void
+handle_data_grow (HandleData *handles, gboolean track)
+{
+       guint32 *new_bitmap;
+       guint32 new_size = handles->size * 2; /* always double: we memset to 0 based on this below */
+
+       /* resize and copy the bitmap */
+       new_bitmap = (guint32 *)g_malloc0 (new_size / CHAR_BIT);
+       memcpy (new_bitmap, handles->bitmap, handles->size / CHAR_BIT);
+       g_free (handles->bitmap);
+       handles->bitmap = new_bitmap;
+
+       /* resize and copy the entries */
+       if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) {
+               gpointer *entries;
+               guint16 *domain_ids;
+               gint i;
+               domain_ids = (guint16 *)g_malloc0 (sizeof (*handles->domain_ids) * new_size);
+               entries = (void **)g_malloc0 (sizeof (*handles->entries) * new_size);
+               memcpy (domain_ids, handles->domain_ids, sizeof (*handles->domain_ids) * handles->size);
+               for (i = 0; i < handles->size; ++i) {
+                       MonoObject *obj = mono_gc_weak_link_get (&(handles->entries [i]));
+                       if (obj) {
+                               mono_gc_weak_link_add (&(entries [i]), obj, track);
+                               mono_gc_weak_link_remove (&(handles->entries [i]), track);
+                       } else {
+                               g_assert (!handles->entries [i]);
+                       }
+               }
+               g_free (handles->entries);
+               g_free (handles->domain_ids);
+               handles->entries = entries;
+               handles->domain_ids = domain_ids;
+       } else {
+               gpointer *entries;
+               entries = (void **)mono_gc_alloc_fixed (sizeof (*handles->entries) * new_size, NULL, MONO_ROOT_SOURCE_GC_HANDLE, "gc handles table");
+               mono_gc_memmove_aligned (entries, handles->entries, sizeof (*handles->entries) * handles->size);
+               mono_gc_free_fixed (handles->entries);
+               handles->entries = entries;
+       }
+       handles->slot_hint = handles->size / BITMAP_SIZE;
+       handles->size = new_size;
+}
+
+static guint32
+alloc_handle (HandleData *handles, MonoObject *obj, gboolean track)
+{
+       gint slot, i;
+       guint32 res;
+       lock_handles (handles);
+       if (!handles->size)
+               handle_data_alloc_entries (handles);
+       i = handle_data_next_unset (handles);
+       if (i == -1 && handles->slot_hint != 0)
+               i = handle_data_first_unset (handles);
+       if (i == -1) {
+               handle_data_grow (handles, track);
+               i = 0;
+       }
+       slot = handles->slot_hint * BITMAP_SIZE + i;
+       occupy_slot (handles, slot);
+       handles->entries [slot] = NULL;
+       if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) {
+               /*FIXME, what to use when obj == null?*/
+               handles->domain_ids [slot] = (obj ? mono_object_get_domain (obj) : mono_domain_get ())->domain_id;
+               if (obj)
+                       mono_gc_weak_link_add (&(handles->entries [slot]), obj, track);
+       } else {
+               handles->entries [slot] = obj;
+       }
+
+#ifndef DISABLE_PERFCOUNTERS
+       mono_perfcounters->gc_num_handles++;
+#endif
+       unlock_handles (handles);
+       res = MONO_GC_HANDLE (slot, handles->type);
+       mono_profiler_gc_handle (MONO_PROFILER_GC_HANDLE_CREATED, handles->type, res, obj);
+       return res;
+}
+
+/**
+ * mono_gchandle_new:
+ * @obj: managed object to get a handle for
+ * @pinned: whether the object should be pinned
+ *
+ * This returns a handle that wraps the object, this is used to keep a
+ * reference to a managed object from the unmanaged world and preventing the
+ * object from being disposed.
+ * 
+ * If @pinned is false the address of the object can not be obtained, if it is
+ * true the address of the object can be obtained.  This will also pin the
+ * object so it will not be possible by a moving garbage collector to move the
+ * object. 
+ * 
+ * Returns: a handle that can be used to access the object from
+ * unmanaged code.
+ */
+guint32
+mono_gchandle_new (MonoObject *obj, gboolean pinned)
+{
+       return alloc_handle (&gc_handles [pinned? HANDLE_PINNED: HANDLE_NORMAL], obj, FALSE);
+}
+
+/**
+ * mono_gchandle_new_weakref:
+ * @obj: managed object to get a handle for
+ * @track_resurrection: Determines how long to track the object, if this is set to TRUE, the object is tracked after finalization, if FALSE, the object is only tracked up until the point of finalization.
+ *
+ * This returns a weak handle that wraps the object, this is used to
+ * keep a reference to a managed object from the unmanaged world.
+ * Unlike the mono_gchandle_new the object can be reclaimed by the
+ * garbage collector.  In this case the value of the GCHandle will be
+ * set to zero.
+ * 
+ * If @track_resurrection is TRUE the object will be tracked through
+ * finalization and if the object is resurrected during the execution
+ * of the finalizer, then the returned weakref will continue to hold
+ * a reference to the object.   If @track_resurrection is FALSE, then
+ * the weak reference's target will become NULL as soon as the object
+ * is passed on to the finalizer.
+ * 
+ * Returns: a handle that can be used to access the object from
+ * unmanaged code.
+ */
+guint32
+mono_gchandle_new_weakref (MonoObject *obj, gboolean track_resurrection)
+{
+       return alloc_handle (&gc_handles [track_resurrection? HANDLE_WEAK_TRACK: HANDLE_WEAK], obj, track_resurrection);
+}
+
+/**
+ * mono_gchandle_get_target:
+ * @gchandle: a GCHandle's handle.
+ *
+ * The handle was previously created by calling `mono_gchandle_new` or
+ * `mono_gchandle_new_weakref`.
+ *
+ * Returns: A pointer to the `MonoObject*` represented by the handle or
+ * NULL for a collected object if using a weakref handle.
+ */
+MonoObject*
+mono_gchandle_get_target (guint32 gchandle)
+{
+       guint slot = MONO_GC_HANDLE_SLOT (gchandle);
+       guint type = MONO_GC_HANDLE_TYPE (gchandle);
+       HandleData *handles = &gc_handles [type];
+       MonoObject *obj = NULL;
+       if (type >= HANDLE_TYPE_MAX)
+               return NULL;
+
+       lock_handles (handles);
+       if (slot < handles->size && slot_occupied (handles, slot)) {
+               if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) {
+                       obj = mono_gc_weak_link_get (&handles->entries [slot]);
+               } else {
+                       obj = (MonoObject *)handles->entries [slot];
+               }
+       } else {
+               /* print a warning? */
+       }
+       unlock_handles (handles);
+       /*g_print ("get target of entry %d of type %d: %p\n", slot, handles->type, obj);*/
+       return obj;
+}
+
+void
+mono_gchandle_set_target (guint32 gchandle, MonoObject *obj)
+{
+       guint slot = MONO_GC_HANDLE_SLOT (gchandle);
+       guint type = MONO_GC_HANDLE_TYPE (gchandle);
+       HandleData *handles = &gc_handles [type];
+       MonoObject *old_obj = NULL;
+
+       g_assert (type < HANDLE_TYPE_MAX);
+       lock_handles (handles);
+       if (slot < handles->size && slot_occupied (handles, slot)) {
+               if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) {
+                       old_obj = (MonoObject *)handles->entries [slot];
+                       if (handles->entries [slot])
+                               mono_gc_weak_link_remove (&handles->entries [slot], handles->type == HANDLE_WEAK_TRACK);
+                       if (obj)
+                               mono_gc_weak_link_add (&handles->entries [slot], obj, handles->type == HANDLE_WEAK_TRACK);
+                       /*FIXME, what to use when obj == null?*/
+                       handles->domain_ids [slot] = (obj ? mono_object_get_domain (obj) : mono_domain_get ())->domain_id;
+               } else {
+                       handles->entries [slot] = obj;
+               }
+       } else {
+               /* print a warning? */
+       }
+       /*g_print ("changed entry %d of type %d to object %p (in slot: %p)\n", slot, handles->type, obj, handles->entries [slot]);*/
+       unlock_handles (handles);
 }
 
 gboolean
@@ -1454,4 +1791,118 @@ mono_gc_is_null (void)
        return FALSE;
 }
 
+/**
+ * mono_gchandle_is_in_domain:
+ * @gchandle: a GCHandle's handle.
+ * @domain: An application domain.
+ *
+ * Use this function to determine if the @gchandle points to an
+ * object allocated in the specified @domain.
+ *
+ * Returns: TRUE if the object wrapped by the @gchandle belongs to the specific @domain.
+ */
+gboolean
+mono_gchandle_is_in_domain (guint32 gchandle, MonoDomain *domain)
+{
+       guint slot = MONO_GC_HANDLE_SLOT (gchandle);
+       guint type = MONO_GC_HANDLE_TYPE (gchandle);
+       HandleData *handles = &gc_handles [type];
+       gboolean result = FALSE;
+
+       if (type >= HANDLE_TYPE_MAX)
+               return FALSE;
+
+       lock_handles (handles);
+       if (slot < handles->size && slot_occupied (handles, slot)) {
+               if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) {
+                       result = domain->domain_id == handles->domain_ids [slot];
+               } else {
+                       MonoObject *obj;
+                       obj = (MonoObject *)handles->entries [slot];
+                       if (obj == NULL)
+                               result = TRUE;
+                       else
+                               result = domain == mono_object_domain (obj);
+               }
+       } else {
+               /* print a warning? */
+       }
+       unlock_handles (handles);
+       return result;
+}
+
+/**
+ * mono_gchandle_free:
+ * @gchandle: a GCHandle's handle.
+ *
+ * Frees the @gchandle handle.  If there are no outstanding
+ * references, the garbage collector can reclaim the memory of the
+ * object wrapped. 
+ */
+void
+mono_gchandle_free (guint32 gchandle)
+{
+       guint slot = MONO_GC_HANDLE_SLOT (gchandle);
+       guint type = MONO_GC_HANDLE_TYPE (gchandle);
+       HandleData *handles = &gc_handles [type];
+       if (type >= HANDLE_TYPE_MAX)
+               return;
+
+       lock_handles (handles);
+       if (slot < handles->size && slot_occupied (handles, slot)) {
+               if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) {
+                       if (handles->entries [slot])
+                               mono_gc_weak_link_remove (&handles->entries [slot], handles->type == HANDLE_WEAK_TRACK);
+               } else {
+                       handles->entries [slot] = NULL;
+               }
+               vacate_slot (handles, slot);
+       } else {
+               /* print a warning? */
+       }
+#ifndef DISABLE_PERFCOUNTERS
+       mono_perfcounters->gc_num_handles--;
+#endif
+       /*g_print ("freed entry %d of type %d\n", slot, handles->type);*/
+       unlock_handles (handles);
+       mono_profiler_gc_handle (MONO_PROFILER_GC_HANDLE_DESTROYED, handles->type, gchandle, NULL);
+}
+
+/**
+ * mono_gchandle_free_domain:
+ * @domain: domain that is unloading
+ *
+ * Function used internally to cleanup any GC handle for objects belonging
+ * to the specified domain during appdomain unload.
+ */
+void
+mono_gchandle_free_domain (MonoDomain *domain)
+{
+       guint type;
+
+       for (type = HANDLE_TYPE_MIN; type < HANDLE_PINNED; ++type) {
+               guint slot;
+               HandleData *handles = &gc_handles [type];
+               lock_handles (handles);
+               for (slot = 0; slot < handles->size; ++slot) {
+                       if (!slot_occupied (handles, slot))
+                               continue;
+                       if (MONO_GC_HANDLE_TYPE_IS_WEAK (type)) {
+                               if (domain->domain_id == handles->domain_ids [slot]) {
+                                       vacate_slot (handles, slot);
+                                       if (handles->entries [slot])
+                                               mono_gc_weak_link_remove (&handles->entries [slot], handles->type == HANDLE_WEAK_TRACK);
+                               }
+                       } else {
+                               if (handles->entries [slot] && mono_object_domain (handles->entries [slot]) == domain) {
+                                       vacate_slot (handles, slot);
+                                       handles->entries [slot] = NULL;
+                               }
+                       }
+               }
+               unlock_handles (handles);
+       }
+
+}
+
 #endif /* no Boehm GC */