[sgen] Use the lock free allocator as the internal allocator for SGen.
authorMark Probst <mark.probst@gmail.com>
Sat, 23 Apr 2011 10:53:59 +0000 (12:53 +0200)
committerMark Probst <mark.probst@gmail.com>
Sun, 1 May 2011 18:49:15 +0000 (20:49 +0200)
mono/metadata/Makefile.am
mono/metadata/sgen-cardtable.c
mono/metadata/sgen-gc.c
mono/metadata/sgen-gc.h
mono/metadata/sgen-gray.c
mono/metadata/sgen-internal.c
mono/metadata/sgen-major-copying.c
mono/metadata/sgen-major-scan-object.h
mono/metadata/sgen-pinned-allocator.c [new file with mode: 0644]
mono/metadata/sgen-workers.c

index 2daabdeac9dd7ef5e7035c4ff15d7c6299f39830..0d5843eec6b06770f62fd3aa2f851a008150a1ae 100644 (file)
@@ -186,6 +186,7 @@ libmonoruntime_la_SOURCES = \
        sgen-os-mach.c          \
        sgen-gc.c               \
        sgen-internal.c         \
+       sgen-pinned-allocator.c \
        sgen-marksweep.c        \
        sgen-marksweep-fixed.c  \
        sgen-marksweep-par.c    \
index 382d4e95faa77ec1608ab03d945e70d6da47febe..5738458ffa68b1dbac955bafc59618b8747e4b78 100644 (file)
@@ -456,7 +456,7 @@ LOOP_HEAD:
                                                major_collector.copy_object ((void**)elem, queue);
                                                new = *(gpointer*)elem;
                                                if (G_UNLIKELY (ptr_in_nursery (new)))
-                                                       mono_sgen_add_to_global_remset (queue->allocator, elem);
+                                                       mono_sgen_add_to_global_remset (elem);
                                        }
                                }
                        }
index ce4cb969857e2ad1408c831826b5b89053001674..a32cc52ad059e64dbddf909e6d08921b14516716 100644 (file)
@@ -243,7 +243,8 @@ enum {
  * ######################################################################
  */
 
-static int gc_initialized = 0;
+/* 0 means not initialized, 1 is initialized, -1 means in progress */
+static gint32 gc_initialized = 0;
 /* If set, do a minor collection before every X allocation */
 static guint32 collect_before_allocs = 0;
 /* If set, do a heap consistency check before each minor collection */
@@ -408,7 +409,7 @@ struct _RootRecord {
  * NULL to simplify the elimination of consecutive duplicate
  * entries.
  */
-#define STORE_REMSET_BUFFER_SIZE       1024
+#define STORE_REMSET_BUFFER_SIZE       1023
 
 typedef struct _GenericStoreRememberedSet GenericStoreRememberedSet;
 struct _GenericStoreRememberedSet {
@@ -442,8 +443,7 @@ static gpointer global_remset_cache [2];
  * and doesn't waste any alloc paddin space.
  */
 #define DEFAULT_REMSET_SIZE 1024
-static RememberedSet* alloc_remset (int size, gpointer id);
-static RememberedSet* alloc_global_remset (SgenInternalAllocator *alc, int size, gpointer id);
+static RememberedSet* alloc_remset (int size, gpointer id, gboolean global);
 
 #define object_is_forwarded    SGEN_OBJECT_IS_FORWARDED
 #define object_is_pinned       SGEN_OBJECT_IS_PINNED
@@ -1665,7 +1665,7 @@ global_remset_location_was_not_added (gpointer ptr)
  * lock must be held.  For serial collectors that is not necessary.
  */
 void
-mono_sgen_add_to_global_remset (SgenInternalAllocator *alc, gpointer ptr)
+mono_sgen_add_to_global_remset (gpointer ptr)
 {
        RememberedSet *rs;
        gboolean lock = major_collector.is_parallel;
@@ -1696,7 +1696,7 @@ mono_sgen_add_to_global_remset (SgenInternalAllocator *alc, gpointer ptr)
                *(global_remset->store_next++) = (mword)ptr;
                goto done;
        }
-       rs = alloc_global_remset (alc, global_remset->end_set - global_remset->data, NULL);
+       rs = alloc_remset (global_remset->end_set - global_remset->data, NULL, TRUE);
        rs->next = global_remset;
        global_remset = rs;
        *(global_remset->store_next++) = (mword)ptr;
@@ -3132,7 +3132,7 @@ collect_nursery (size_t requested_size)
 
        try_calculate_minor_collection_allowance (FALSE);
 
-       gray_object_queue_init (&gray_queue, mono_sgen_get_unmanaged_allocator ());
+       gray_object_queue_init (&gray_queue);
        workers_init_distribute_gray_queue ();
 
        num_minor_gcs++;
@@ -3172,7 +3172,7 @@ collect_nursery (size_t requested_size)
 
        sfrjd.heap_start = nursery_start;
        sfrjd.heap_end = nursery_next;
-       workers_enqueue_job (workers_distribute_gray_queue.allocator, job_scan_from_remsets, &sfrjd);
+       workers_enqueue_job (job_scan_from_remsets, &sfrjd);
 
        /* we don't have complete write barrier yet, so we scan all the old generation sections */
        TV_GETTIME (btv);
@@ -3202,13 +3202,13 @@ collect_nursery (size_t requested_size)
        scrrjd_normal.heap_start = nursery_start;
        scrrjd_normal.heap_end = nursery_next;
        scrrjd_normal.root_type = ROOT_TYPE_NORMAL;
-       workers_enqueue_job (workers_distribute_gray_queue.allocator, job_scan_from_registered_roots, &scrrjd_normal);
+       workers_enqueue_job (job_scan_from_registered_roots, &scrrjd_normal);
 
        scrrjd_wbarrier.func = major_collector.copy_object;
        scrrjd_wbarrier.heap_start = nursery_start;
        scrrjd_wbarrier.heap_end = nursery_next;
        scrrjd_wbarrier.root_type = ROOT_TYPE_WBARRIER;
-       workers_enqueue_job (workers_distribute_gray_queue.allocator, job_scan_from_registered_roots, &scrrjd_wbarrier);
+       workers_enqueue_job (job_scan_from_registered_roots, &scrrjd_wbarrier);
 
        TV_GETTIME (btv);
        time_minor_scan_registered_roots += TV_ELAPSED_MS (atv, btv);
@@ -3216,7 +3216,7 @@ collect_nursery (size_t requested_size)
        /* thread data */
        stdjd.heap_start = nursery_start;
        stdjd.heap_end = nursery_next;
-       workers_enqueue_job (workers_distribute_gray_queue.allocator, job_scan_thread_data, &stdjd);
+       workers_enqueue_job (job_scan_thread_data, &stdjd);
 
        TV_GETTIME (atv);
        time_minor_scan_thread_data += TV_ELAPSED_MS (btv, atv);
@@ -3352,7 +3352,7 @@ major_do_collection (const char *reason)
 
        binary_protocol_collection (GENERATION_OLD);
        check_scan_starts ();
-       gray_object_queue_init (&gray_queue, mono_sgen_get_unmanaged_allocator ());
+       gray_object_queue_init (&gray_queue);
        workers_init_distribute_gray_queue ();
 
        degraded_mode = 0;
@@ -3454,13 +3454,13 @@ major_do_collection (const char *reason)
        scrrjd_normal.heap_start = heap_start;
        scrrjd_normal.heap_end = heap_end;
        scrrjd_normal.root_type = ROOT_TYPE_NORMAL;
-       workers_enqueue_job (workers_distribute_gray_queue.allocator, job_scan_from_registered_roots, &scrrjd_normal);
+       workers_enqueue_job (job_scan_from_registered_roots, &scrrjd_normal);
 
        scrrjd_wbarrier.func = major_collector.copy_or_mark_object;
        scrrjd_wbarrier.heap_start = heap_start;
        scrrjd_wbarrier.heap_end = heap_end;
        scrrjd_wbarrier.root_type = ROOT_TYPE_WBARRIER;
-       workers_enqueue_job (workers_distribute_gray_queue.allocator, job_scan_from_registered_roots, &scrrjd_wbarrier);
+       workers_enqueue_job (job_scan_from_registered_roots, &scrrjd_wbarrier);
 
        TV_GETTIME (btv);
        time_major_scan_registered_roots += TV_ELAPSED_MS (atv, btv);
@@ -3468,7 +3468,7 @@ major_do_collection (const char *reason)
        /* Threads */
        stdjd.heap_start = heap_start;
        stdjd.heap_end = heap_end;
-       workers_enqueue_job (workers_distribute_gray_queue.allocator, job_scan_thread_data, &stdjd);
+       workers_enqueue_job (job_scan_thread_data, &stdjd);
 
        TV_GETTIME (atv);
        time_major_scan_thread_data += TV_ELAPSED_MS (btv, atv);
@@ -3481,10 +3481,10 @@ major_do_collection (const char *reason)
 
        /* scan the list of objects ready for finalization */
        sfejd_fin_ready.list = fin_ready_list;
-       workers_enqueue_job (workers_distribute_gray_queue.allocator, job_scan_finalizer_entries, &sfejd_fin_ready);
+       workers_enqueue_job (job_scan_finalizer_entries, &sfejd_fin_ready);
 
        sfejd_critical_fin.list = critical_fin_list;
-       workers_enqueue_job (workers_distribute_gray_queue.allocator, job_scan_finalizer_entries, &sfejd_critical_fin);
+       workers_enqueue_job (job_scan_finalizer_entries, &sfejd_critical_fin);
 
        TV_GETTIME (atv);
        time_major_scan_finalized += TV_ELAPSED_MS (btv, atv);
@@ -4486,11 +4486,11 @@ clear_unreachable_ephemerons (CopyOrMarkObjectFunc copy_func, char *start, char
                        if (was_promoted) {
                                if (ptr_in_nursery (key)) {/*key was not promoted*/
                                        DEBUG (5, fprintf (gc_debug_file, "\tAdded remset to key %p\n", key));
-                                       mono_sgen_add_to_global_remset (queue->allocator, &cur->key);
+                                       mono_sgen_add_to_global_remset (&cur->key);
                                }
                                if (ptr_in_nursery (cur->value)) {/*value was not promoted*/
                                        DEBUG (5, fprintf (gc_debug_file, "\tAdded remset to value %p\n", cur->value));
-                                       mono_sgen_add_to_global_remset (queue->allocator, &cur->value);
+                                       mono_sgen_add_to_global_remset (&cur->value);
                                }
                        }
                }
@@ -5588,7 +5588,7 @@ handle_remset (mword *p, void *start_nursery, void *end_nursery, gboolean global
                                 * becomes part of the global remset, which can grow very large.
                                 */
                                DEBUG (9, fprintf (gc_debug_file, "Add to global remset because of pinning %p (%p %s)\n", ptr, *ptr, safe_name (*ptr)));
-                               mono_sgen_add_to_global_remset (queue->allocator, ptr);
+                               mono_sgen_add_to_global_remset (ptr);
                        }
                } else {
                        DEBUG (9, fprintf (gc_debug_file, "Skipping remset at %p holding %p\n", ptr, *ptr));
@@ -5603,7 +5603,7 @@ handle_remset (mword *p, void *start_nursery, void *end_nursery, gboolean global
                        major_collector.copy_object (ptr, queue);
                        DEBUG (9, fprintf (gc_debug_file, "Overwrote remset at %p with %p (count: %d)\n", ptr, *ptr, (int)count));
                        if (!global && *ptr >= start_nursery && *ptr < end_nursery)
-                               mono_sgen_add_to_global_remset (queue->allocator, ptr);
+                               mono_sgen_add_to_global_remset (ptr);
                        ++ptr;
                }
                return p + 2;
@@ -5853,8 +5853,7 @@ clear_remsets (void)
                remset->next = NULL;
                if (remset != global_remset) {
                        DEBUG (4, fprintf (gc_debug_file, "Freed remset at %p\n", remset->data));
-                       mono_sgen_free_internal_dynamic_delayed (remset, remset_byte_size (remset), INTERNAL_MEM_REMSET,
-                                       mono_sgen_get_unmanaged_allocator ());
+                       mono_sgen_free_internal_dynamic (remset, remset_byte_size (remset), INTERNAL_MEM_REMSET);
                }
        }
        /* the generic store ones */
@@ -5978,7 +5977,7 @@ sgen_thread_register (SgenThreadInfo* info, void *addr)
        stack_end = info->stack_end;
 #endif
 
-       info->remset = alloc_remset (DEFAULT_REMSET_SIZE, info);
+       info->remset = alloc_remset (DEFAULT_REMSET_SIZE, info, FALSE);
        pthread_setspecific (remembered_set_key, info->remset);
 #ifdef HAVE_KW_THREAD
        remembered_set = info->remset;
@@ -6132,23 +6131,13 @@ dummy_use (gpointer v) {
 
 
 static RememberedSet*
-alloc_remset (int size, gpointer id) {
-       RememberedSet* res = mono_sgen_alloc_internal_dynamic (sizeof (RememberedSet) + (size * sizeof (gpointer)), INTERNAL_MEM_REMSET);
-       res->store_next = res->data;
-       res->end_set = res->data + size;
-       res->next = NULL;
-       DEBUG (4, fprintf (gc_debug_file, "Allocated remset size %d at %p for %p\n", size, res->data, id));
-       return res;
-}
-
-static RememberedSet*
-alloc_global_remset (SgenInternalAllocator *alc, int size, gpointer id)
+alloc_remset (int size, gpointer id, gboolean global)
 {
-       RememberedSet* res = mono_sgen_alloc_internal_full (alc, sizeof (RememberedSet) + (size * sizeof (gpointer)), INTERNAL_MEM_REMSET);
+       RememberedSet* res = mono_sgen_alloc_internal_dynamic (sizeof (RememberedSet) + (size * sizeof (gpointer)), INTERNAL_MEM_REMSET);
        res->store_next = res->data;
        res->end_set = res->data + size;
        res->next = NULL;
-       DEBUG (4, fprintf (gc_debug_file, "Allocated global remset size %d at %p for %p\n", size, res->data, id));
+       DEBUG (4, fprintf (gc_debug_file, "Allocated%s remset size %d at %p for %p\n", global ? " global" : "", size, res->data, id));
        return res;
 }
 
@@ -6184,7 +6173,7 @@ mono_gc_wbarrier_set_field (MonoObject *obj, gpointer field_ptr, MonoObject* val
                        UNLOCK_GC;
                        return;
                }
-               rs = alloc_remset (rs->end_set - rs->data, (void*)1);
+               rs = alloc_remset (rs->end_set - rs->data, (void*)1, FALSE);
                rs->next = REMEMBERED_SET;
                REMEMBERED_SET = rs;
 #ifdef HAVE_KW_THREAD
@@ -6222,7 +6211,7 @@ mono_gc_wbarrier_set_arrayref (MonoArray *arr, gpointer slot_ptr, MonoObject* va
                        UNLOCK_GC;
                        return;
                }
-               rs = alloc_remset (rs->end_set - rs->data, (void*)1);
+               rs = alloc_remset (rs->end_set - rs->data, (void*)1, FALSE);
                rs->next = REMEMBERED_SET;
                REMEMBERED_SET = rs;
 #ifdef HAVE_KW_THREAD
@@ -6285,7 +6274,7 @@ mono_gc_wbarrier_arrayref_copy (gpointer dest_ptr, gpointer src_ptr, int count)
                        UNLOCK_GC;
                        return;
                }
-               rs = alloc_remset (rs->end_set - rs->data, (void*)1);
+               rs = alloc_remset (rs->end_set - rs->data, (void*)1, FALSE);
                rs->next = REMEMBERED_SET;
                REMEMBERED_SET = rs;
 #ifdef HAVE_KW_THREAD
@@ -6475,7 +6464,7 @@ mono_gc_wbarrier_value_copy (gpointer dest, gpointer src, int count, MonoClass *
                        UNLOCK_GC;
                        return;
                }
-               rs = alloc_remset (rs->end_set - rs->data, (void*)1);
+               rs = alloc_remset (rs->end_set - rs->data, (void*)1, FALSE);
                rs->next = REMEMBERED_SET;
                REMEMBERED_SET = rs;
 #ifdef HAVE_KW_THREAD
@@ -6517,7 +6506,7 @@ mono_gc_wbarrier_object_copy (MonoObject* obj, MonoObject *src)
                UNLOCK_GC;
                return;
        }
-       rs = alloc_remset (rs->end_set - rs->data, (void*)1);
+       rs = alloc_remset (rs->end_set - rs->data, (void*)1, FALSE);
        rs->next = REMEMBERED_SET;
        REMEMBERED_SET = rs;
 
@@ -7146,17 +7135,28 @@ mono_gc_base_init (void)
        struct sigaction sinfo;
        glong max_heap = 0;
        int num_workers;
+       int result;
+
+       do {
+               result = InterlockedCompareExchange (&gc_initialized, -1, 0);
+               switch (result) {
+               case 1:
+                       /* already inited */
+                       return;
+               case -1:
+                       /* being inited by another thread */
+                       usleep (1000);
+                       break;
+               case 0:
+                       /* we will init it */
+                       break;
+               default:
+                       g_assert_not_reached ();
+               }
+       } while (result != 0);
 
-       /* the gc_initialized guard seems to imply this method is
-          idempotent, but LOCK_INIT(gc_mutex) might not be.  It's
-          defined in sgen-gc.h as nothing, so there's no danger at
-          present. */
        LOCK_INIT (gc_mutex);
        LOCK_GC;
-       if (gc_initialized) {
-               UNLOCK_GC;
-               return;
-       }
        pagesize = mono_pagesize ();
        gc_debug_file = stdout;
 
@@ -7405,6 +7405,23 @@ mono_gc_base_init (void)
        if (major_collector.post_param_init)
                major_collector.post_param_init ();
 
+       global_remset = alloc_remset (1024, NULL, FALSE);
+       global_remset->next = NULL;
+
+       pthread_key_create (&remembered_set_key, NULL);
+
+#ifndef HAVE_KW_THREAD
+       pthread_key_create (&thread_info_key, NULL);
+#endif
+
+       if (use_cardtable)
+               card_table_init ();
+
+       /*
+        * This needs to happen before any internal allocations because
+        * it inits the small id which is required for hazard pointer
+        * operations.
+        */
        suspend_ack_semaphore_ptr = &suspend_ack_semaphore;
        MONO_SEM_INIT (&suspend_ack_semaphore, 0);
 
@@ -7423,21 +7440,11 @@ mono_gc_base_init (void)
        sigfillset (&suspend_signal_mask);
        sigdelset (&suspend_signal_mask, restart_signal_num);
 
-       global_remset = alloc_remset (1024, NULL);
-       global_remset->next = NULL;
-
-       pthread_key_create (&remembered_set_key, NULL);
-
-#ifndef HAVE_KW_THREAD
-       pthread_key_create (&thread_info_key, NULL);
-#endif
-
-       if (use_cardtable)
-               card_table_init ();
-
-       gc_initialized = TRUE;
        UNLOCK_GC;
+
        mono_thread_info_attach (&sinfo);
+
+       gc_initialized = 1;
 }
 
 int
index 8af465059c697bf0a2f10b619cfe72a9c82b44ff..ec625281b6461e1336134c19a15668d2ed17a590 100644 (file)
@@ -62,8 +62,6 @@ typedef struct _SgenThreadInfo SgenThreadInfo;
 
 #define SGEN_MAX_DEBUG_LEVEL 2
 
-//#define SGEN_DEBUG_INTERNAL_ALLOC
-
 #define GC_BITS_PER_WORD (sizeof (mword) * 8)
 
 
@@ -138,8 +136,7 @@ struct _SgenThreadInfo {
 enum {
        MEMORY_ROLE_GEN0,
        MEMORY_ROLE_GEN1,
-       MEMORY_ROLE_PINNED,
-       MEMORY_ROLE_INTERNAL
+       MEMORY_ROLE_PINNED
 };
 
 typedef struct _SgenBlock SgenBlock;
@@ -539,8 +536,6 @@ gsize* mono_sgen_get_complex_descriptor (GCVTable *vt) MONO_INTERNAL;
                }       \
        } while (0)
 
-typedef struct _SgenInternalAllocator SgenInternalAllocator;
-
 #define SGEN_GRAY_QUEUE_SECTION_SIZE   (128 - 3)
 
 /*
@@ -559,7 +554,6 @@ typedef struct _SgenGrayQueue SgenGrayQueue;
 typedef void (*GrayQueueAllocPrepareFunc) (SgenGrayQueue*);
 
 struct _SgenGrayQueue {
-       SgenInternalAllocator *allocator;
        GrayQueueSection *first;
        GrayQueueSection *free_list;
        int balance;
@@ -616,7 +610,6 @@ void mono_sgen_check_section_scan_starts (GCMemSection *section) MONO_INTERNAL;
 
 /* Keep in sync with mono_sgen_dump_internal_mem_usage() in dump_heap()! */
 enum {
-       INTERNAL_MEM_MANAGED,
        INTERNAL_MEM_PIN_QUEUE,
        INTERNAL_MEM_FRAGMENT,
        INTERNAL_MEM_SECTION,
@@ -640,17 +633,13 @@ enum {
        INTERNAL_MEM_MAX
 };
 
-#define SGEN_INTERNAL_FREELIST_NUM_SLOTS       30
+#define SGEN_PINNED_FREELIST_NUM_SLOTS 30
 
-struct _SgenInternalAllocator {
-#ifdef SGEN_DEBUG_INTERNAL_ALLOC
-       pthread_t thread;
-#endif
+typedef struct {
        SgenPinnedChunk *chunk_list;
-       SgenPinnedChunk *free_lists [SGEN_INTERNAL_FREELIST_NUM_SLOTS];
-       void *delayed_free_lists [SGEN_INTERNAL_FREELIST_NUM_SLOTS];
-       long small_internal_mem_bytes [INTERNAL_MEM_MAX];
-};
+       SgenPinnedChunk *free_lists [SGEN_PINNED_FREELIST_NUM_SLOTS];
+       void *delayed_free_lists [SGEN_PINNED_FREELIST_NUM_SLOTS];
+} SgenPinnedAllocator;
 
 enum {
        GENERATION_NURSERY,
@@ -659,12 +648,10 @@ enum {
 };
 
 void mono_sgen_init_internal_allocator (void) MONO_INTERNAL;
+void mono_sgen_init_pinned_allocator (void) MONO_INTERNAL;
 
-SgenInternalAllocator* mono_sgen_get_unmanaged_allocator (void) MONO_INTERNAL;
-
-const char* mono_sgen_internal_mem_type_name (int type) MONO_INTERNAL;
 void mono_sgen_report_internal_mem_usage (void) MONO_INTERNAL;
-void mono_sgen_report_internal_mem_usage_full (SgenInternalAllocator *alc) MONO_INTERNAL;
+void mono_sgen_report_pinned_mem_usage (SgenPinnedAllocator *alc) MONO_INTERNAL;
 void mono_sgen_dump_internal_mem_usage (FILE *heap_dump_file) MONO_INTERNAL;
 void mono_sgen_dump_section (GCMemSection *section, const char *type) MONO_INTERNAL;
 void mono_sgen_dump_occupied (char *start, char *end, char *section_start) MONO_INTERNAL;
@@ -679,24 +666,18 @@ void mono_sgen_free_internal (void *addr, int type) MONO_INTERNAL;
 void* mono_sgen_alloc_internal_dynamic (size_t size, int type) MONO_INTERNAL;
 void mono_sgen_free_internal_dynamic (void *addr, size_t size, int type) MONO_INTERNAL;
 
-void* mono_sgen_alloc_internal_fixed (SgenInternalAllocator *allocator, int type) MONO_INTERNAL;
-void mono_sgen_free_internal_fixed (SgenInternalAllocator *allocator, void *addr, int type) MONO_INTERNAL;
-
-void* mono_sgen_alloc_internal_full (SgenInternalAllocator *allocator, size_t size, int type) MONO_INTERNAL;
-void mono_sgen_free_internal_full (SgenInternalAllocator *allocator, void *addr, size_t size, int type) MONO_INTERNAL;
-
-void mono_sgen_free_internal_delayed (void *addr, int type, SgenInternalAllocator *thread_allocator) MONO_INTERNAL;
-void mono_sgen_free_internal_dynamic_delayed (void *addr, size_t size, int type, SgenInternalAllocator *thread_allocator) MONO_INTERNAL;
+void* mono_sgen_alloc_pinned (SgenPinnedAllocator *allocator, size_t size) MONO_INTERNAL;
+void mono_sgen_free_pinned (SgenPinnedAllocator *allocator, void *addr, size_t size) MONO_INTERNAL;
 
 
 void mono_sgen_debug_printf (int level, const char *format, ...) MONO_INTERNAL;
 
 gboolean mono_sgen_parse_environment_string_extract_number (const char *str, glong *out) MONO_INTERNAL;
 
-void mono_sgen_internal_scan_objects (SgenInternalAllocator *alc, IterateObjectCallbackFunc callback, void *callback_data) MONO_INTERNAL;
-void mono_sgen_internal_scan_pinned_objects (SgenInternalAllocator *alc, IterateObjectCallbackFunc callback, void *callback_data) MONO_INTERNAL;
+void mono_sgen_pinned_scan_objects (SgenPinnedAllocator *alc, IterateObjectCallbackFunc callback, void *callback_data) MONO_INTERNAL;
+void mono_sgen_pinned_scan_pinned_objects (SgenPinnedAllocator *alc, IterateObjectCallbackFunc callback, void *callback_data) MONO_INTERNAL;
 
-void mono_sgen_internal_update_heap_boundaries (SgenInternalAllocator *alc) MONO_INTERNAL;
+void mono_sgen_pinned_update_heap_boundaries (SgenPinnedAllocator *alc) MONO_INTERNAL;
 
 void** mono_sgen_find_optimized_pin_queue_area (void *start, void *end, int *num) MONO_INTERNAL;
 void mono_sgen_find_section_pin_queue_start_end (GCMemSection *section) MONO_INTERNAL;
@@ -704,7 +685,7 @@ void mono_sgen_pin_objects_in_section (GCMemSection *section, SgenGrayQueue *que
 
 void mono_sgen_pin_stats_register_object (char *obj, size_t size);
 
-void mono_sgen_add_to_global_remset (SgenInternalAllocator *alc, gpointer ptr) MONO_INTERNAL;
+void mono_sgen_add_to_global_remset (gpointer ptr) MONO_INTERNAL;
 
 int mono_sgen_get_current_collection_generation (void) MONO_INTERNAL;
 
index 5c57fb42551bd72b0258d3a36dc7987318023986..958e86e961775e1ca137fbd9113abee72b2bda56 100644 (file)
@@ -39,7 +39,7 @@ gray_object_alloc_queue_section (GrayQueue *queue)
                queue->free_list = section->next;
        } else {
                /* Allocate a new section */
-               section = mono_sgen_alloc_internal_fixed (queue->allocator, INTERNAL_MEM_GRAY_QUEUE);
+               section = mono_sgen_alloc_internal (INTERNAL_MEM_GRAY_QUEUE);
        }
 
        section->end = 0;
@@ -50,9 +50,9 @@ gray_object_alloc_queue_section (GrayQueue *queue)
 }
 
 static void
-gray_object_free_queue_section (GrayQueueSection *section, SgenInternalAllocator *thread_allocator)
+gray_object_free_queue_section (GrayQueueSection *section)
 {
-       mono_sgen_free_internal_delayed (section, INTERNAL_MEM_GRAY_QUEUE, thread_allocator);
+       mono_sgen_free_internal (section, INTERNAL_MEM_GRAY_QUEUE);
 }
 
 static inline gboolean
@@ -127,7 +127,7 @@ gray_object_enqueue_section (GrayQueue *queue, GrayQueueSection *section)
 }
 
 static void
-gray_object_queue_init (GrayQueue *queue, SgenInternalAllocator *allocator)
+gray_object_queue_init (GrayQueue *queue)
 {
        GrayQueueSection *section, *next;
        int i;
@@ -135,8 +135,6 @@ gray_object_queue_init (GrayQueue *queue, SgenInternalAllocator *allocator)
        g_assert (gray_object_queue_is_empty (queue));
        DEBUG (9, g_assert (queue->balance == 0));
 
-       queue->allocator = allocator;
-
        /* Free the extra sections allocated during the last collection */
        i = 0;
        for (section = queue->free_list; section && i < GRAY_QUEUE_LENGTH_LIMIT - 1; section = section->next)
@@ -146,14 +144,14 @@ gray_object_queue_init (GrayQueue *queue, SgenInternalAllocator *allocator)
        while (section->next) {
                next = section->next;
                section->next = next->next;
-               gray_object_free_queue_section (next, allocator);
+               gray_object_free_queue_section (next);
        }
 }
 
 static void
-gray_object_queue_init_with_alloc_prepare (GrayQueue *queue, SgenInternalAllocator *allocator, GrayQueueAllocPrepareFunc func, void *data)
+gray_object_queue_init_with_alloc_prepare (GrayQueue *queue, GrayQueueAllocPrepareFunc func, void *data)
 {
-       gray_object_queue_init (queue, allocator);
+       gray_object_queue_init (queue);
        queue->alloc_prepare_func = func;
        queue->alloc_prepare_data = data;
 }
index 9b78b1186048a224bf80a3de1b655475cffa62b3..4d44b8a5ff926315830f334985c70bd84951139e 100644 (file)
-/*
- * sgen-gc.c: Simple generational GC.
- *
- * Author:
- *     Paolo Molaro (lupus@ximian.com)
- *
- * Copyright 2005-2010 Novell, Inc (http://www.novell.com)
- *
- * Thread start/stop adapted from Boehm's GC:
- * Copyright (c) 1994 by Xerox Corporation.  All rights reserved.
- * Copyright (c) 1996 by Silicon Graphics.  All rights reserved.
- * Copyright (c) 1998 by Fergus Henderson.  All rights reserved.
- * Copyright (c) 2000-2004 by Hewlett-Packard Company.  All rights reserved.
- *
- * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED
- * OR IMPLIED.  ANY USE IS AT YOUR OWN RISK.
- *
- * Permission is hereby granted to use or copy this program
- * for any purpose,  provided the above notices are retained on all copies.
- * Permission to modify the code and to distribute modified code is granted,
- * provided the above notices are retained, and a notice that the code was
- * modified is included with the above copyright notice.
- *
- *
- * Copyright 2001-2003 Ximian, Inc
- * Copyright 2003-2010 Novell, Inc.
- * 
- * Permission is hereby granted, free of charge, to any person obtaining
- * a copy of this software and associated documentation files (the
- * "Software"), to deal in the Software without restriction, including
- * without limitation the rights to use, copy, modify, merge, publish,
- * distribute, sublicense, and/or sell copies of the Software, and to
- * permit persons to whom the Software is furnished to do so, subject to
- * the following conditions:
- * 
- * The above copyright notice and this permission notice shall be
- * included in all copies or substantial portions of the Software.
- * 
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
- * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
- * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
- * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- */
-
 #ifdef HAVE_SGEN_GC
 
 #include "utils/mono-counters.h"
 #include "metadata/sgen-gc.h"
-
-/* Pinned objects are allocated in the LOS space if bigger than half a page
- * or from freelists otherwise. We assume that pinned objects are relatively few
- * and they have a slow dying speed (like interned strings, thread objects).
- * As such they will be collected only at major collections.
- * free lists are not global: when we need memory we allocate a PinnedChunk.
- * Each pinned chunk is made of several pages, the first of wich is used
- * internally for bookeeping (here think of a page as 4KB). The bookeeping
- * includes the freelists vectors and info about the object size of each page
- * in the pinned chunk. So, when needed, a free page is found in a pinned chunk,
- * a size is assigned to it, the page is divided in the proper chunks and each
- * chunk is added to the freelist. To not waste space, the remaining space in the
- * first page is used as objects of size 16 or 32 (need to measure which are more
- * common).
- * We use this same structure to allocate memory used internally by the GC, so
- * we never use malloc/free if we need to alloc during collection: the world is stopped
- * and malloc/free will deadlock.
- * When we want to iterate over pinned objects, we just scan a page at a time
- * linearly according to the size of objects in the page: the next pointer used to link
- * the items in the freelist uses the same word as the vtable. Since we keep freelists
- * for each pinned chunk, if the word points outside the pinned chunk it means
- * it is an object.
- * We could avoid this expensive scanning in creative ways. We could have a policy
- * of putting in the pinned space only objects we know about that have no struct fields
- * with references and we can easily use a even expensive write barrier for them,
- * since pointer writes on such objects should be rare.
- * The best compromise is to just alloc interned strings and System.MonoType in them.
- * It would be nice to allocate MonoThread in it, too: must check that we properly
- * use write barriers so we don't have to do any expensive scanning of the whole pinned
- * chunk list during minor collections. We can avoid it now because we alloc in it only
- * reference-free objects.
- */
-struct _SgenPinnedChunk {
-       SgenBlock block;
-       int num_pages;
-       SgenInternalAllocator *allocator;
-       int *page_sizes; /* a 0 means the page is still unused */
-       void **free_list;
-       SgenPinnedChunk *free_list_nexts [SGEN_INTERNAL_FREELIST_NUM_SLOTS];
-       void *start_data;
-       void *data [1]; /* page sizes and free lists are stored here */
-};
-
-#define PINNED_FIRST_SLOT_SIZE (sizeof (gpointer) * 4)
-#define MAX_FREELIST_SIZE 8192
-
-/* This is a fixed value used for pinned chunks, not the system pagesize */
-#define FREELIST_PAGESIZE (16*1024)
+#include "utils/lock-free-alloc.h"
 
 /* keep each size a multiple of ALLOC_ALIGN */
-/* on 64 bit systems 8 is likely completely unused. */
-static const int freelist_sizes [] = {
+static const int allocator_sizes [] = {
           8,   16,   24,   32,   40,   48,   64,   80,
-         96,  128,  160,  192,  224,  256,  320,  384,
-        448,  512,  584,  680,  816, 1024, 1360, 2048,
-       2336, 2728, 3272, 4096, 5456, 8192 };
-
-/*
- * Slot indexes for the fixed INTERNAL_MEM_XXX types.  -1 if that type
- * is dynamic.
- */
-static int fixed_type_freelist_slots [INTERNAL_MEM_MAX];
-
-static SgenInternalAllocator unmanaged_allocator;
-
-#define LARGE_INTERNAL_MEM_HEADER_MAGIC        0x7d289f3a
-
-typedef struct _LargeInternalMemHeader LargeInternalMemHeader;
-struct _LargeInternalMemHeader {
-       guint32 magic;
-       size_t size;
-       double data[0];
-};
+         96,  128,  160,  192,  224,  248,  320,  384,
+        448,  528,  584,  680,  816, 1088, 1360, 2040,
+       2336, 2728, 3272, 4088, 5456, 8184 };
 
-static long long pinned_chunk_bytes_alloced = 0;
-static long long large_internal_bytes_alloced = 0;
+#define NUM_ALLOCATORS (sizeof (allocator_sizes) / sizeof (int))
 
-#ifdef HEAVY_STATISTICS
-static long long stat_internal_alloc = 0;
-#endif
-
-#ifdef SGEN_DEBUG_INTERNAL_ALLOC
-extern pthread_t main_gc_thread;
-#endif
-
-/*
- * Debug reporting.
- */
-static void
-report_pinned_chunk (SgenPinnedChunk *chunk, int seq) {
-       void **p;
-       int i, free_pages, num_free, free_mem;
-       free_pages = 0;
-       for (i = 0; i < chunk->num_pages; ++i) {
-               if (!chunk->page_sizes [i])
-                       free_pages++;
-       }
-       printf ("Pinned chunk %d at %p, size: %d, pages: %d, free: %d\n", seq, chunk, chunk->num_pages * FREELIST_PAGESIZE, chunk->num_pages, free_pages);
-       free_mem = FREELIST_PAGESIZE * free_pages;
-       for (i = 0; i < SGEN_INTERNAL_FREELIST_NUM_SLOTS; ++i) {
-               if (!chunk->free_list [i])
-                       continue;
-               num_free = 0;
-               p = chunk->free_list [i];
-               while (p) {
-                       num_free++;
-                       p = *p;
-               }
-               printf ("\tfree list of size %d, %d items\n", freelist_sizes [i], num_free);
-               free_mem += freelist_sizes [i] * num_free;
-       }
-       printf ("\tfree memory in chunk: %d\n", free_mem);
-}
+static MonoLockFreeAllocSizeClass size_classes [NUM_ALLOCATORS];
+static MonoLockFreeAllocator allocators [NUM_ALLOCATORS];
 
 /*
- * Debug reporting.
- */
-void
-mono_sgen_report_internal_mem_usage_full (SgenInternalAllocator *alc)
-{
-       SgenPinnedChunk *chunk;
-       int i = 0;
-       for (chunk = alc->chunk_list; chunk; chunk = chunk->block.next)
-               report_pinned_chunk (chunk, i++);
-}
-
-void
-mono_sgen_report_internal_mem_usage (void)
-{
-       mono_sgen_report_internal_mem_usage_full (&unmanaged_allocator);
-}
-
-/*
- * Find the slot number in the freelist for memory chunks that
- * can contain @size objects.
+ * Find the allocator index for memory chunks that can contain @size
+ * objects.
  */
 static int
-slot_for_size (size_t size)
+index_for_size (size_t size)
 {
        int slot;
        /* do a binary search or lookup table later. */
-       for (slot = 0; slot < SGEN_INTERNAL_FREELIST_NUM_SLOTS; ++slot) {
-               if (freelist_sizes [slot] >= size)
+       for (slot = 0; slot < NUM_ALLOCATORS; ++slot) {
+               if (allocator_sizes [slot] >= size)
                        return slot;
        }
        g_assert_not_reached ();
        return -1;
 }
 
-void
-mono_sgen_register_fixed_internal_mem_type (int type, size_t size)
-{
-       int slot;
-
-       g_assert (type >= 0 && type < INTERNAL_MEM_MAX);
-       g_assert (fixed_type_freelist_slots [type] == -1);
-
-       slot = slot_for_size (size);
-       g_assert (slot >= 0);
-
-       fixed_type_freelist_slots [type] = slot;
-}
-
 /*
- * Build a free list for @size memory chunks from the memory area between
- * start_page and end_page.
+ * Allocator indexes for the fixed INTERNAL_MEM_XXX types.  -1 if that
+ * type is dynamic.
  */
-static void
-build_freelist (SgenInternalAllocator *alc, SgenPinnedChunk *chunk, int slot, int size, char *start_page, char *end_page)
-{
-       void **p, **end;
-       int count = 0;
-       /*g_print ("building freelist for slot %d, size %d in %p\n", slot, size, chunk);*/
-       p = (void**)start_page;
-       end = (void**)(end_page - size);
-       g_assert (!chunk->free_list [slot]);
-       chunk->free_list [slot] = p;
-       while ((char*)p + size <= (char*)end) {
-               count++;
-               *p = (void*)((char*)p + size);
-               p = *p;
-       }
-       *p = NULL;
-       /*g_print ("%d items created, max: %d\n", count, (end_page - start_page) / size);*/
-
-       g_assert (!chunk->free_list_nexts [slot]);
-       chunk->free_list_nexts [slot] = alc->free_lists [slot];
-       alc->free_lists [slot] = chunk;
-}
-
-static SgenPinnedChunk*
-alloc_pinned_chunk (SgenInternalAllocator *alc, gboolean managed)
-{
-       SgenPinnedChunk *chunk;
-       int offset;
-       int size = SGEN_PINNED_CHUNK_SIZE;
-
-       chunk = mono_sgen_alloc_os_memory_aligned (size, size, TRUE);
-       chunk->block.role = managed ? MEMORY_ROLE_PINNED : MEMORY_ROLE_INTERNAL;
-
-       if (managed)
-               mono_sgen_update_heap_boundaries ((mword)chunk, ((mword)chunk + size));
-
-       pinned_chunk_bytes_alloced += size;
+static int fixed_type_allocator_indexes [INTERNAL_MEM_MAX];
 
-       /* setup the bookeeping fields */
-       chunk->num_pages = size / FREELIST_PAGESIZE;
-       offset = G_STRUCT_OFFSET (SgenPinnedChunk, data);
-       chunk->page_sizes = (void*)((char*)chunk + offset);
-       offset += sizeof (int) * chunk->num_pages;
-       offset = SGEN_ALIGN_UP (offset);
-       chunk->free_list = (void*)((char*)chunk + offset);
-       offset += sizeof (void*) * SGEN_INTERNAL_FREELIST_NUM_SLOTS;
-       offset = SGEN_ALIGN_UP (offset);
-       chunk->start_data = (void*)((char*)chunk + offset);
-
-       /* allocate the first page to the freelist */
-       chunk->page_sizes [0] = PINNED_FIRST_SLOT_SIZE;
-       build_freelist (alc, chunk, slot_for_size (PINNED_FIRST_SLOT_SIZE), PINNED_FIRST_SLOT_SIZE,
-                       chunk->start_data, ((char*)chunk + FREELIST_PAGESIZE));
-       mono_sgen_debug_printf (4, "Allocated pinned chunk %p, size: %d\n", chunk, size);
-
-       chunk->block.next = alc->chunk_list;
-       alc->chunk_list = chunk;
-
-       chunk->allocator = alc;
-
-       return chunk;
-}
-
-/* Must be called with an empty freelist for the given slot. */
-static gboolean
-populate_chunk_page (SgenInternalAllocator *alc, SgenPinnedChunk *chunk, int slot)
-{
-       int size = freelist_sizes [slot];
-       int i;
-       g_assert (!chunk->free_list [slot]);
-       g_assert (!chunk->free_list_nexts [slot]);
-       for (i = 0; i < chunk->num_pages; ++i) {
-               if (chunk->page_sizes [i])
-                       continue;
-               chunk->page_sizes [i] = size;
-               build_freelist (alc, chunk, slot, size, (char*)chunk + FREELIST_PAGESIZE * i, (char*)chunk + FREELIST_PAGESIZE * (i + 1));
-               return TRUE;
-       }
-       return FALSE;
-}
-
-static void*
-alloc_from_slot (SgenInternalAllocator *alc, int slot, int type)
-{
-       SgenPinnedChunk *pchunk;
-       size_t size = freelist_sizes [slot];
-
-#ifdef SGEN_DEBUG_INTERNAL_ALLOC
-       if (type == INTERNAL_MEM_MS_BLOCK_INFO) {
-               g_assert (alc == &unmanaged_allocator);
-       } else {
-               if (main_gc_thread)
-                       g_assert (pthread_self () == alc->thread);
-       }
-#endif
-
-       alc->small_internal_mem_bytes [type] += size;
-
-       if (alc->delayed_free_lists [slot]) {
-               void **p;
-               do {
-                       p = alc->delayed_free_lists [slot];
-               } while (SGEN_CAS_PTR (&alc->delayed_free_lists [slot], *p, p) != p);
-               memset (p, 0, size);
-               return p;
-       }
-
- restart:
-       pchunk = alc->free_lists [slot];
-       if (pchunk) {
-               void **p = pchunk->free_list [slot];
-               void *next;
-
-               g_assert (p);
-
-               next = *p;
-               pchunk->free_list [slot] = next;
-
-               if (!next) {
-                       alc->free_lists [slot] = pchunk->free_list_nexts [slot];
-                       pchunk->free_list_nexts [slot] = NULL;
-               }
-
-               memset (p, 0, size);
-               return p;
-       }
-
-       for (pchunk = alc->chunk_list; pchunk; pchunk = pchunk->block.next) {
-               if (populate_chunk_page (alc, pchunk, slot))
-                       goto restart;
-       }
-
-       pchunk = alloc_pinned_chunk (alc, type == INTERNAL_MEM_MANAGED);
-       /* FIXME: handle OOM */
-       if (pchunk->free_list [slot])
-               goto restart;
-       if (!populate_chunk_page (alc, pchunk, slot))
-               g_assert_not_reached ();
-       goto restart;
-}
-
-/* used for the GC-internal data structures */
-void*
-mono_sgen_alloc_internal_full (SgenInternalAllocator *alc, size_t size, int type)
+void
+mono_sgen_register_fixed_internal_mem_type (int type, size_t size)
 {
        int slot;
-       void *res = NULL;
-
-       g_assert (fixed_type_freelist_slots [type] == -1);
-
-       HEAVY_STAT (++stat_internal_alloc);
-
-       if (size > freelist_sizes [SGEN_INTERNAL_FREELIST_NUM_SLOTS - 1]) {
-               LargeInternalMemHeader *mh;
-
-               size += sizeof (LargeInternalMemHeader);
-               mh = mono_sgen_alloc_os_memory (size, TRUE);
-               mh->magic = LARGE_INTERNAL_MEM_HEADER_MAGIC;
-               mh->size = size;
-               /* FIXME: do a CAS here */
-               large_internal_bytes_alloced += size;
-               return mh->data;
-       }
 
-       slot = slot_for_size (size);
-       g_assert (size <= freelist_sizes [slot]);
-       res = alloc_from_slot (alc, slot, type);
-
-       return res;
-}
+       g_assert (type >= 0 && type < INTERNAL_MEM_MAX);
+       g_assert (fixed_type_allocator_indexes [type] == -1);
 
-void*
-mono_sgen_alloc_internal_fixed (SgenInternalAllocator *allocator, int type)
-{
-       int slot = fixed_type_freelist_slots [type];
+       slot = index_for_size (size);
        g_assert (slot >= 0);
-       return alloc_from_slot (allocator, slot, type);
-}
 
-void*
-mono_sgen_alloc_internal (int type)
-{
-       return mono_sgen_alloc_internal_fixed (&unmanaged_allocator, type);
+       fixed_type_allocator_indexes [type] = slot;
 }
 
 void*
 mono_sgen_alloc_internal_dynamic (size_t size, int type)
 {
-       return mono_sgen_alloc_internal_full (&unmanaged_allocator, size, type);
-}
-
-static void
-free_from_slot (SgenInternalAllocator *alc, void *addr, int slot, int type)
-{
-       SgenPinnedChunk *pchunk = (SgenPinnedChunk*)SGEN_PINNED_CHUNK_FOR_PTR (addr);
-       void **p = addr;
-       void *next;
-
-#ifdef SGEN_DEBUG_INTERNAL_ALLOC
-       if (alc->thread)
-               g_assert (pthread_self () == alc->thread);
-       else if (main_gc_thread)
-               g_assert (pthread_self () == main_gc_thread);
-#endif
+       int index;
+       void *p;
 
-       g_assert (addr >= (void*)pchunk && (char*)addr < (char*)pchunk + pchunk->num_pages * FREELIST_PAGESIZE);
-       if (type == INTERNAL_MEM_MANAGED)
-               g_assert (pchunk->block.role == MEMORY_ROLE_PINNED);
-       else
-               g_assert (pchunk->block.role == MEMORY_ROLE_INTERNAL);
+       if (size > allocator_sizes [NUM_ALLOCATORS - 1])
+               return mono_sgen_alloc_os_memory (size, TRUE);
 
-       next = pchunk->free_list [slot];
-       *p = next;
-       pchunk->free_list [slot] = p;
-
-       if (!next) {
-               g_assert (!pchunk->free_list_nexts [slot]);
-               pchunk->free_list_nexts [slot] = alc->free_lists [slot];
-               alc->free_lists [slot] = pchunk;
-       }
+       index = index_for_size (size);
 
-       alc->small_internal_mem_bytes [type] -= freelist_sizes [slot];
+       p = mono_lock_free_alloc (&allocators [index]);
+       memset (p, 0, size);
+       return p;
 }
 
 void
-mono_sgen_free_internal_full (SgenInternalAllocator *alc, void *addr, size_t size, int type)
+mono_sgen_free_internal_dynamic (void *addr, size_t size, int type)
 {
-       LargeInternalMemHeader *mh;
-
-       g_assert (fixed_type_freelist_slots [type] == -1);
+       int index;
 
        if (!addr)
                return;
 
-       if (size <= freelist_sizes [SGEN_INTERNAL_FREELIST_NUM_SLOTS - 1]) {
-               int slot = slot_for_size (size);
-               free_from_slot (alc, addr, slot, type);
-               return;
-       }
-
-       mh = (LargeInternalMemHeader*)((char*)addr - G_STRUCT_OFFSET (LargeInternalMemHeader, data));
-       g_assert (mh->magic == LARGE_INTERNAL_MEM_HEADER_MAGIC);
-       g_assert (mh->size == size + sizeof (LargeInternalMemHeader));
-       /* FIXME: do a CAS */
-       large_internal_bytes_alloced -= mh->size;
-       mono_sgen_free_os_memory (mh, mh->size);
-}
+       if (size > allocator_sizes [NUM_ALLOCATORS - 1])
+               return mono_sgen_free_os_memory (addr, size);
 
-void
-mono_sgen_free_internal_fixed (SgenInternalAllocator *allocator, void *addr, int type)
-{
-       int slot = fixed_type_freelist_slots [type];
-       g_assert (slot >= 0);
+       index = index_for_size (size);
 
-       free_from_slot (allocator, addr, slot, type);
+       mono_lock_free_free (addr);
 }
 
-void
-mono_sgen_free_internal (void *addr, int type)
+void*
+mono_sgen_alloc_internal (int type)
 {
-       mono_sgen_free_internal_fixed (&unmanaged_allocator, addr, type);
+       int index = fixed_type_allocator_indexes [type];
+       void *p;
+       g_assert (index >= 0 && index < NUM_ALLOCATORS);
+       p = mono_lock_free_alloc (&allocators [index]);
+       memset (p, 0, allocator_sizes [index]);
+       return p;
 }
 
 void
-mono_sgen_free_internal_dynamic (void *addr, size_t size, int type)
+mono_sgen_free_internal (void *addr, int type)
 {
-       mono_sgen_free_internal_full (&unmanaged_allocator, addr, size, type);
-}
+       int index;
 
-static void
-free_from_slot_delayed (void *addr, size_t size, int slot, int type, SgenInternalAllocator *thread_allocator)
-{
-       SgenPinnedChunk *pchunk = (SgenPinnedChunk*)SGEN_PINNED_CHUNK_FOR_PTR (addr);
-       SgenInternalAllocator *alc = pchunk->allocator;
-       void *next;
-
-       if (alc == thread_allocator) {
-               if (size > 0)
-                       mono_sgen_free_internal_full (alc, addr, size, type);
-               else
-                       mono_sgen_free_internal_fixed (alc, addr, type);
+       if (!addr)
                return;
-       }
-
-       do {
-               next = alc->delayed_free_lists [slot];
-               *(void**)addr = next;
-       } while (SGEN_CAS_PTR (&alc->delayed_free_lists [slot], addr, next) != next);
-}
 
-void
-mono_sgen_free_internal_delayed (void *addr, int type, SgenInternalAllocator *thread_allocator)
-{
-       free_from_slot_delayed (addr, 0, fixed_type_freelist_slots [type], type, thread_allocator);
-}
+       index = fixed_type_allocator_indexes [type];
+       g_assert (index >= 0 && index < NUM_ALLOCATORS);
 
-void
-mono_sgen_free_internal_dynamic_delayed (void *addr, size_t size, int type, SgenInternalAllocator *thread_allocator)
-{
-       if (size > freelist_sizes [SGEN_INTERNAL_FREELIST_NUM_SLOTS - 1]) {
-               mono_sgen_free_internal_full (NULL, addr, size, type);
-               return;
-       }
-
-       free_from_slot_delayed (addr, size, slot_for_size (size), type, thread_allocator);
+       mono_lock_free_free (addr);
 }
 
 void
 mono_sgen_dump_internal_mem_usage (FILE *heap_dump_file)
 {
-       static char const *internal_mem_names [] = { "managed", "pin-queue", "fragment", "section", "scan-starts",
+       /*
+       static char const *internal_mem_names [] = { "pin-queue", "fragment", "section", "scan-starts",
                                                     "fin-table", "finalize-entry", "dislink-table",
                                                     "dislink", "roots-table", "root-record", "statistics",
                                                     "remset", "gray-queue", "store-remset", "marksweep-tables",
@@ -541,124 +129,32 @@ mono_sgen_dump_internal_mem_usage (FILE *heap_dump_file)
                fprintf (heap_dump_file, "<other-mem-usage type=\"%s\" size=\"%ld\"/>\n",
                                internal_mem_names [i], unmanaged_allocator.small_internal_mem_bytes [i]);
        }
+       */
 }
 
 void
-mono_sgen_init_internal_allocator (void)
-{
-       int i;
-
-       g_assert (SGEN_INTERNAL_FREELIST_NUM_SLOTS == sizeof (freelist_sizes) / sizeof (freelist_sizes [0]));
-
-       for (i = 0; i < INTERNAL_MEM_MAX; ++i)
-               fixed_type_freelist_slots [i] = -1;
-
-#ifdef HEAVY_STATISTICS
-       mono_counters_register ("Internal allocs", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_internal_alloc);
-#endif
-}
-
-SgenInternalAllocator*
-mono_sgen_get_unmanaged_allocator (void)
+mono_sgen_report_internal_mem_usage (void)
 {
-       return &unmanaged_allocator;
+       /* FIXME: implement */
+       printf ("not implemented yet\n");
 }
 
 void
-mono_sgen_internal_scan_objects (SgenInternalAllocator *alc, IterateObjectCallbackFunc callback, void *callback_data)
+mono_sgen_init_internal_allocator (void)
 {
-       SgenPinnedChunk *chunk;
-       int i, obj_size;
-       char *p, *endp;
-       void **ptr;
-       void *end_chunk;
-       for (chunk = alc->chunk_list; chunk; chunk = chunk->block.next) {
-               end_chunk = (char*)chunk + chunk->num_pages * FREELIST_PAGESIZE;
-               mono_sgen_debug_printf (6, "Scanning pinned chunk %p (range: %p-%p)\n", chunk, chunk->start_data, end_chunk);
-               for (i = 0; i < chunk->num_pages; ++i) {
-                       obj_size = chunk->page_sizes [i];
-                       if (!obj_size)
-                               continue;
-                       p = i? (char*)chunk + i * FREELIST_PAGESIZE: chunk->start_data;
-                       endp = i? p + FREELIST_PAGESIZE: (char*)chunk + FREELIST_PAGESIZE;
-                       mono_sgen_debug_printf (6, "Page %d (size: %d, range: %p-%p)\n", i, obj_size, p, endp);
-                       while (p + obj_size <= endp) {
-                               ptr = (void**)p;
-                               /* if the first word (the vtable) is outside the chunk we have an object */
-                               if (*ptr && (*ptr < (void*)chunk || *ptr >= end_chunk))
-                                       callback ((char*)ptr, obj_size, callback_data);
-                               p += obj_size;
-                       }
-               }
-       }
-}
+       int i;
 
-void
-mono_sgen_internal_update_heap_boundaries (SgenInternalAllocator *alc)
-{
-       SgenPinnedChunk *chunk;
-       for (chunk = alc->chunk_list; chunk; chunk = chunk->block.next) {
-               char *end_chunk = (char*)chunk + chunk->num_pages * FREELIST_PAGESIZE;
-               mono_sgen_update_heap_boundaries ((mword)chunk, (mword)end_chunk);
-       }
-}
+       for (i = 0; i < INTERNAL_MEM_MAX; ++i)
+               fixed_type_allocator_indexes [i] = -1;
 
-/*
- * the array of pointers from @start to @end contains conservative
- * pointers to objects inside @chunk: mark each referenced object
- * with the PIN bit.
- */
-static void
-mark_pinned_from_addresses (SgenPinnedChunk *chunk, void **start, void **end, IterateObjectCallbackFunc callback, void *callback_data)
-{
-       for (; start < end; start++) {
-               char *addr = *start;
-               int offset = (char*)addr - (char*)chunk;
-               int page = offset / FREELIST_PAGESIZE;
-               int obj_offset = page == 0? offset - ((char*)chunk->start_data - (char*)chunk): offset % FREELIST_PAGESIZE;
-               int slot_size = chunk->page_sizes [page];
-               void **ptr;
-               /* the page is not allocated */
-               if (!slot_size)
-                       continue;
-               /* would be faster if we restrict the sizes to power of two,
-                * but that's a waste of memory: need to measure. it could reduce
-                * fragmentation since there are less pages needed, if for example
-                * someone interns strings of each size we end up with one page per
-                * interned string (still this is just ~40 KB): with more fine-grained sizes
-                * this increases the number of used pages.
-                */
-               if (page == 0) {
-                       obj_offset /= slot_size;
-                       obj_offset *= slot_size;
-                       addr = (char*)chunk->start_data + obj_offset;
-               } else {
-                       obj_offset /= slot_size;
-                       obj_offset *= slot_size;
-                       addr = (char*)chunk + page * FREELIST_PAGESIZE + obj_offset;
-               }
-               ptr = (void**)addr;
-               /* if the vtable is inside the chunk it's on the freelist, so skip */
-               /* FIXME: is it possible that we're pinning objects more than once here? */
-               if (*ptr && (*ptr < (void*)chunk->start_data || *ptr > (void*)((char*)chunk + chunk->num_pages * FREELIST_PAGESIZE)))
-                       callback (addr, slot_size, callback_data);
+       for (i = 0; i < NUM_ALLOCATORS; ++i) {
+               mono_lock_free_allocator_init_size_class (&size_classes [i], allocator_sizes [i]);
+               mono_lock_free_allocator_init_allocator (&allocators [i], &size_classes [i]);
        }
-}
 
-void
-mono_sgen_internal_scan_pinned_objects (SgenInternalAllocator *alc, IterateObjectCallbackFunc callback, void *callback_data)
-{
-       SgenPinnedChunk *chunk;
-
-       /* look for pinned addresses for pinned-alloc objects */
-       mono_sgen_debug_printf (6, "Pinning from pinned-alloc objects\n");
-       for (chunk = alc->chunk_list; chunk; chunk = chunk->block.next) {
-               int num_pinned;
-               void **pinned = mono_sgen_find_optimized_pin_queue_area (chunk->start_data,
-                               (char*)chunk + chunk->num_pages * FREELIST_PAGESIZE, &num_pinned);
-               if (num_pinned)
-                       mark_pinned_from_addresses (chunk, pinned, pinned + num_pinned, callback, callback_data);
-       }
+#ifdef HEAVY_STATISTICS
+       mono_counters_register ("Internal allocs", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_internal_alloc);
+#endif
 }
 
 #endif
index 15bfb44afb025c685d5f14c3b4fef12d634fd52c..95b003232cb2930fb2f9eba94e0fa7b39555b306 100644 (file)
@@ -65,7 +65,7 @@ static int num_major_sections = 0;
 
 static GCMemSection *section_list = NULL;
 
-static SgenInternalAllocator pinned_allocator;
+static SgenPinnedAllocator pinned_allocator;
 
 static gboolean have_swept;
 
@@ -113,7 +113,7 @@ obj_is_from_pinned_alloc (char *p)
 static void
 free_pinned_object (char *obj, size_t size)
 {
-       mono_sgen_free_internal_full (&pinned_allocator, obj, size, INTERNAL_MEM_MANAGED);
+       mono_sgen_free_pinned (&pinned_allocator, obj, size);
 }
 
 /*
@@ -243,7 +243,7 @@ major_is_object_live (char *obj)
 static void*
 major_alloc_small_pinned_obj (size_t size, gboolean has_references)
 {
-       return mono_sgen_alloc_internal_full (&pinned_allocator, size, INTERNAL_MEM_MANAGED);
+       return mono_sgen_alloc_pinned (&pinned_allocator, size);
 }
 
 /*
@@ -438,7 +438,7 @@ sweep_pinned_objects_callback (char *ptr, size_t size, void *data)
 static void
 sweep_pinned_objects (void)
 {
-       mono_sgen_internal_scan_objects (&pinned_allocator, sweep_pinned_objects_callback, NULL);
+       mono_sgen_pinned_scan_objects (&pinned_allocator, sweep_pinned_objects_callback, NULL);
 }
 
 static void
@@ -450,7 +450,7 @@ major_iterate_objects (gboolean non_pinned, gboolean pinned, IterateObjectCallba
                        mono_sgen_scan_area_with_callback (section->data, section->end_data, callback, data, FALSE);
        }
        if (pinned)
-               mono_sgen_internal_scan_objects (&pinned_allocator, callback, data);
+               mono_sgen_pinned_scan_objects (&pinned_allocator, callback, data);
 }
 
 static void
@@ -477,7 +477,7 @@ major_find_pin_queue_start_ends (SgenGrayQueue *queue)
 
        for (section = section_list; section; section = section->block.next)
                mono_sgen_find_section_pin_queue_start_end (section);
-       mono_sgen_internal_scan_pinned_objects (&pinned_allocator, (IterateObjectCallbackFunc)pin_pinned_object_callback, queue);
+       mono_sgen_pinned_scan_pinned_objects (&pinned_allocator, (IterateObjectCallbackFunc)pin_pinned_object_callback, queue);
 }
 
 static void
@@ -506,7 +506,7 @@ major_sweep (void)
        /* unpin objects from the pinned chunks and free the unmarked ones */
        sweep_pinned_objects ();
 
-       mono_sgen_internal_update_heap_boundaries (&pinned_allocator);
+       mono_sgen_pinned_update_heap_boundaries (&pinned_allocator);
 
        /* free the unused sections */
        prev_section = NULL;
@@ -630,7 +630,7 @@ major_ptr_is_in_non_pinned_space (char *ptr)
 static void
 major_report_pinned_memory_usage (void)
 {
-       mono_sgen_report_internal_mem_usage_full (&pinned_allocator);
+       mono_sgen_report_pinned_mem_usage (&pinned_allocator);
 }
 
 static int
index a09b38e0ba4270ab0dd330551c720b1730e7721e..bcd37489ad1507ce52de71ddfe872f959211090f 100644 (file)
@@ -33,7 +33,7 @@ extern long long stat_scan_object_called_major;
                        __copy = *(ptr);        \
                        DEBUG (9, if (__old != __copy) fprintf (gc_debug_file, "Overwrote field at %p with %p (was: %p)\n", (ptr), *(ptr), __old));     \
                        if (G_UNLIKELY (ptr_in_nursery (__copy) && !ptr_in_nursery ((ptr)))) \
-                               mono_sgen_add_to_global_remset (queue->allocator, (ptr));       \
+                               mono_sgen_add_to_global_remset ((ptr)); \
                }       \
        } while (0)
 
@@ -104,7 +104,7 @@ minor_scan_vtype (char *start, mword desc, char* from_start, char* from_end, Sge
                        __copy = *(ptr);                                \
                        DEBUG (9, if (__old != __copy) mono_sgen_debug_printf (9, "Overwrote field at %p with %p (was: %p)\n", (ptr), *(ptr), __old)); \
                        if (G_UNLIKELY (ptr_in_nursery (__copy) && !ptr_in_nursery ((ptr)))) \
-                               mono_sgen_add_to_global_remset (queue->allocator, (ptr));       \
+                               mono_sgen_add_to_global_remset ((ptr)); \
                }                                                       \
        } while (0)
 
diff --git a/mono/metadata/sgen-pinned-allocator.c b/mono/metadata/sgen-pinned-allocator.c
new file mode 100644 (file)
index 0000000..3be32c0
--- /dev/null
@@ -0,0 +1,493 @@
+/*
+ * sgen-pinned-allocator.c: Simple generational GC.
+ *
+ * Author:
+ *     Paolo Molaro (lupus@ximian.com)
+ *
+ * Copyright 2005-2010 Novell, Inc (http://www.novell.com)
+ *
+ * Thread start/stop adapted from Boehm's GC:
+ * Copyright (c) 1994 by Xerox Corporation.  All rights reserved.
+ * Copyright (c) 1996 by Silicon Graphics.  All rights reserved.
+ * Copyright (c) 1998 by Fergus Henderson.  All rights reserved.
+ * Copyright (c) 2000-2004 by Hewlett-Packard Company.  All rights reserved.
+ *
+ * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED
+ * OR IMPLIED.  ANY USE IS AT YOUR OWN RISK.
+ *
+ * Permission is hereby granted to use or copy this program
+ * for any purpose,  provided the above notices are retained on all copies.
+ * Permission to modify the code and to distribute modified code is granted,
+ * provided the above notices are retained, and a notice that the code was
+ * modified is included with the above copyright notice.
+ *
+ *
+ * Copyright 2001-2003 Ximian, Inc
+ * Copyright 2003-2010 Novell, Inc.
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#ifdef HAVE_SGEN_GC
+
+#include "utils/mono-counters.h"
+#include "metadata/sgen-gc.h"
+
+/* Pinned objects are allocated in the LOS space if bigger than half a page
+ * or from freelists otherwise. We assume that pinned objects are relatively few
+ * and they have a slow dying speed (like interned strings, thread objects).
+ * As such they will be collected only at major collections.
+ * free lists are not global: when we need memory we allocate a PinnedChunk.
+ * Each pinned chunk is made of several pages, the first of wich is used
+ * internally for bookeeping (here think of a page as 4KB). The bookeeping
+ * includes the freelists vectors and info about the object size of each page
+ * in the pinned chunk. So, when needed, a free page is found in a pinned chunk,
+ * a size is assigned to it, the page is divided in the proper chunks and each
+ * chunk is added to the freelist. To not waste space, the remaining space in the
+ * first page is used as objects of size 16 or 32 (need to measure which are more
+ * common).
+ * We use this same structure to allocate memory used internally by the GC, so
+ * we never use malloc/free if we need to alloc during collection: the world is stopped
+ * and malloc/free will deadlock.
+ * When we want to iterate over pinned objects, we just scan a page at a time
+ * linearly according to the size of objects in the page: the next pointer used to link
+ * the items in the freelist uses the same word as the vtable. Since we keep freelists
+ * for each pinned chunk, if the word points outside the pinned chunk it means
+ * it is an object.
+ * We could avoid this expensive scanning in creative ways. We could have a policy
+ * of putting in the pinned space only objects we know about that have no struct fields
+ * with references and we can easily use a even expensive write barrier for them,
+ * since pointer writes on such objects should be rare.
+ * The best compromise is to just alloc interned strings and System.MonoType in them.
+ * It would be nice to allocate MonoThread in it, too: must check that we properly
+ * use write barriers so we don't have to do any expensive scanning of the whole pinned
+ * chunk list during minor collections. We can avoid it now because we alloc in it only
+ * reference-free objects.
+ */
+struct _SgenPinnedChunk {
+       SgenBlock block;
+       int num_pages;
+       SgenPinnedAllocator *allocator;
+       int *page_sizes; /* a 0 means the page is still unused */
+       void **free_list;
+       SgenPinnedChunk *free_list_nexts [SGEN_PINNED_FREELIST_NUM_SLOTS];
+       void *start_data;
+       void *data [1]; /* page sizes and free lists are stored here */
+};
+
+#define PINNED_FIRST_SLOT_SIZE (sizeof (gpointer) * 4)
+#define MAX_FREELIST_SIZE 8192
+
+/* This is a fixed value used for pinned chunks, not the system pagesize */
+#define FREELIST_PAGESIZE (16*1024)
+
+/* keep each size a multiple of ALLOC_ALIGN */
+/* on 64 bit systems 8 is likely completely unused. */
+static const int freelist_sizes [] = {
+          8,   16,   24,   32,   40,   48,   64,   80,
+         96,  128,  160,  192,  224,  256,  320,  384,
+        448,  512,  584,  680,  816, 1024, 1360, 2048,
+       2336, 2728, 3272, 4096, 5456, 8192 };
+
+#define LARGE_PINNED_MEM_HEADER_MAGIC  0x7d289f3a
+
+typedef struct _LargePinnedMemHeader LargePinnedMemHeader;
+struct _LargePinnedMemHeader {
+       guint32 magic;
+       size_t size;
+       double data[0];
+};
+
+static long long pinned_chunk_bytes_alloced = 0;
+static long long large_pinned_bytes_alloced = 0;
+
+#ifdef HEAVY_STATISTICS
+static long long stat_pinned_alloc = 0;
+#endif
+
+/*
+ * Debug reporting.
+ */
+static void
+report_pinned_chunk (SgenPinnedChunk *chunk, int seq) {
+       void **p;
+       int i, free_pages, num_free, free_mem;
+       free_pages = 0;
+       for (i = 0; i < chunk->num_pages; ++i) {
+               if (!chunk->page_sizes [i])
+                       free_pages++;
+       }
+       printf ("Pinned chunk %d at %p, size: %d, pages: %d, free: %d\n", seq, chunk, chunk->num_pages * FREELIST_PAGESIZE, chunk->num_pages, free_pages);
+       free_mem = FREELIST_PAGESIZE * free_pages;
+       for (i = 0; i < SGEN_PINNED_FREELIST_NUM_SLOTS; ++i) {
+               if (!chunk->free_list [i])
+                       continue;
+               num_free = 0;
+               p = chunk->free_list [i];
+               while (p) {
+                       num_free++;
+                       p = *p;
+               }
+               printf ("\tfree list of size %d, %d items\n", freelist_sizes [i], num_free);
+               free_mem += freelist_sizes [i] * num_free;
+       }
+       printf ("\tfree memory in chunk: %d\n", free_mem);
+}
+
+/*
+ * Debug reporting.
+ */
+void
+mono_sgen_report_pinned_mem_usage (SgenPinnedAllocator *alc)
+{
+       SgenPinnedChunk *chunk;
+       int i = 0;
+       for (chunk = alc->chunk_list; chunk; chunk = chunk->block.next)
+               report_pinned_chunk (chunk, i++);
+}
+
+/*
+ * Find the slot number in the freelist for memory chunks that
+ * can contain @size objects.
+ */
+static int
+slot_for_size (size_t size)
+{
+       int slot;
+       /* do a binary search or lookup table later. */
+       for (slot = 0; slot < SGEN_PINNED_FREELIST_NUM_SLOTS; ++slot) {
+               if (freelist_sizes [slot] >= size)
+                       return slot;
+       }
+       g_assert_not_reached ();
+       return -1;
+}
+
+/*
+ * Build a free list for @size memory chunks from the memory area between
+ * start_page and end_page.
+ */
+static void
+build_freelist (SgenPinnedAllocator *alc, SgenPinnedChunk *chunk, int slot, int size, char *start_page, char *end_page)
+{
+       void **p, **end;
+       int count = 0;
+       /*g_print ("building freelist for slot %d, size %d in %p\n", slot, size, chunk);*/
+       p = (void**)start_page;
+       end = (void**)(end_page - size);
+       g_assert (!chunk->free_list [slot]);
+       chunk->free_list [slot] = p;
+       while ((char*)p + size <= (char*)end) {
+               count++;
+               *p = (void*)((char*)p + size);
+               p = *p;
+       }
+       *p = NULL;
+       /*g_print ("%d items created, max: %d\n", count, (end_page - start_page) / size);*/
+
+       g_assert (!chunk->free_list_nexts [slot]);
+       chunk->free_list_nexts [slot] = alc->free_lists [slot];
+       alc->free_lists [slot] = chunk;
+}
+
+static SgenPinnedChunk*
+alloc_pinned_chunk (SgenPinnedAllocator *alc)
+{
+       SgenPinnedChunk *chunk;
+       int offset;
+       int size = SGEN_PINNED_CHUNK_SIZE;
+
+       chunk = mono_sgen_alloc_os_memory_aligned (size, size, TRUE);
+       chunk->block.role = MEMORY_ROLE_PINNED;
+
+       mono_sgen_update_heap_boundaries ((mword)chunk, ((mword)chunk + size));
+
+       pinned_chunk_bytes_alloced += size;
+
+       /* setup the bookeeping fields */
+       chunk->num_pages = size / FREELIST_PAGESIZE;
+       offset = G_STRUCT_OFFSET (SgenPinnedChunk, data);
+       chunk->page_sizes = (void*)((char*)chunk + offset);
+       offset += sizeof (int) * chunk->num_pages;
+       offset = SGEN_ALIGN_UP (offset);
+       chunk->free_list = (void*)((char*)chunk + offset);
+       offset += sizeof (void*) * SGEN_PINNED_FREELIST_NUM_SLOTS;
+       offset = SGEN_ALIGN_UP (offset);
+       chunk->start_data = (void*)((char*)chunk + offset);
+
+       /* allocate the first page to the freelist */
+       chunk->page_sizes [0] = PINNED_FIRST_SLOT_SIZE;
+       build_freelist (alc, chunk, slot_for_size (PINNED_FIRST_SLOT_SIZE), PINNED_FIRST_SLOT_SIZE,
+                       chunk->start_data, ((char*)chunk + FREELIST_PAGESIZE));
+       mono_sgen_debug_printf (4, "Allocated pinned chunk %p, size: %d\n", chunk, size);
+
+       chunk->block.next = alc->chunk_list;
+       alc->chunk_list = chunk;
+
+       chunk->allocator = alc;
+
+       return chunk;
+}
+
+/* Must be called with an empty freelist for the given slot. */
+static gboolean
+populate_chunk_page (SgenPinnedAllocator *alc, SgenPinnedChunk *chunk, int slot)
+{
+       int size = freelist_sizes [slot];
+       int i;
+       g_assert (!chunk->free_list [slot]);
+       g_assert (!chunk->free_list_nexts [slot]);
+       for (i = 0; i < chunk->num_pages; ++i) {
+               if (chunk->page_sizes [i])
+                       continue;
+               chunk->page_sizes [i] = size;
+               build_freelist (alc, chunk, slot, size, (char*)chunk + FREELIST_PAGESIZE * i, (char*)chunk + FREELIST_PAGESIZE * (i + 1));
+               return TRUE;
+       }
+       return FALSE;
+}
+
+static void*
+alloc_from_slot (SgenPinnedAllocator *alc, int slot)
+{
+       SgenPinnedChunk *pchunk;
+       size_t size = freelist_sizes [slot];
+
+       if (alc->delayed_free_lists [slot]) {
+               void **p;
+               do {
+                       p = alc->delayed_free_lists [slot];
+               } while (SGEN_CAS_PTR (&alc->delayed_free_lists [slot], *p, p) != p);
+               memset (p, 0, size);
+               return p;
+       }
+
+ restart:
+       pchunk = alc->free_lists [slot];
+       if (pchunk) {
+               void **p = pchunk->free_list [slot];
+               void *next;
+
+               g_assert (p);
+
+               next = *p;
+               pchunk->free_list [slot] = next;
+
+               if (!next) {
+                       alc->free_lists [slot] = pchunk->free_list_nexts [slot];
+                       pchunk->free_list_nexts [slot] = NULL;
+               }
+
+               memset (p, 0, size);
+               return p;
+       }
+
+       for (pchunk = alc->chunk_list; pchunk; pchunk = pchunk->block.next) {
+               if (populate_chunk_page (alc, pchunk, slot))
+                       goto restart;
+       }
+
+       pchunk = alloc_pinned_chunk (alc);
+       /* FIXME: handle OOM */
+       if (pchunk->free_list [slot])
+               goto restart;
+       if (!populate_chunk_page (alc, pchunk, slot))
+               g_assert_not_reached ();
+       goto restart;
+}
+
+/* used for the GC-internal data structures */
+void*
+mono_sgen_alloc_pinned (SgenPinnedAllocator *alc, size_t size)
+{
+       int slot;
+       void *res = NULL;
+
+       HEAVY_STAT (++stat_pinned_alloc);
+
+       if (size > freelist_sizes [SGEN_PINNED_FREELIST_NUM_SLOTS - 1]) {
+               LargePinnedMemHeader *mh;
+
+               size += sizeof (LargePinnedMemHeader);
+               mh = mono_sgen_alloc_os_memory (size, TRUE);
+               mh->magic = LARGE_PINNED_MEM_HEADER_MAGIC;
+               mh->size = size;
+               /* FIXME: do a CAS here */
+               large_pinned_bytes_alloced += size;
+               return mh->data;
+       }
+
+       slot = slot_for_size (size);
+       g_assert (size <= freelist_sizes [slot]);
+       res = alloc_from_slot (alc, slot);
+
+       return res;
+}
+
+static void
+free_from_slot (SgenPinnedAllocator *alc, void *addr, int slot)
+{
+       SgenPinnedChunk *pchunk = (SgenPinnedChunk*)SGEN_PINNED_CHUNK_FOR_PTR (addr);
+       void **p = addr;
+       void *next;
+
+       g_assert (addr >= (void*)pchunk && (char*)addr < (char*)pchunk + pchunk->num_pages * FREELIST_PAGESIZE);
+
+       next = pchunk->free_list [slot];
+       *p = next;
+       pchunk->free_list [slot] = p;
+
+       if (!next) {
+               g_assert (!pchunk->free_list_nexts [slot]);
+               pchunk->free_list_nexts [slot] = alc->free_lists [slot];
+               alc->free_lists [slot] = pchunk;
+       }
+}
+
+void
+mono_sgen_free_pinned (SgenPinnedAllocator *alc, void *addr, size_t size)
+{
+       LargePinnedMemHeader *mh;
+
+       if (!addr)
+               return;
+
+       if (size <= freelist_sizes [SGEN_PINNED_FREELIST_NUM_SLOTS - 1]) {
+               int slot = slot_for_size (size);
+               free_from_slot (alc, addr, slot);
+               return;
+       }
+
+       mh = (LargePinnedMemHeader*)((char*)addr - G_STRUCT_OFFSET (LargePinnedMemHeader, data));
+       g_assert (mh->magic == LARGE_PINNED_MEM_HEADER_MAGIC);
+       g_assert (mh->size == size + sizeof (LargePinnedMemHeader));
+       /* FIXME: do a CAS */
+       large_pinned_bytes_alloced -= mh->size;
+       mono_sgen_free_os_memory (mh, mh->size);
+}
+
+void
+mono_sgen_init_pinned_allocator (void)
+{
+       g_assert (SGEN_PINNED_FREELIST_NUM_SLOTS == sizeof (freelist_sizes) / sizeof (freelist_sizes [0]));
+
+#ifdef HEAVY_STATISTICS
+       mono_counters_register ("Pinned allocs", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_pinned_alloc);
+#endif
+}
+
+void
+mono_sgen_pinned_scan_objects (SgenPinnedAllocator *alc, IterateObjectCallbackFunc callback, void *callback_data)
+{
+       SgenPinnedChunk *chunk;
+       int i, obj_size;
+       char *p, *endp;
+       void **ptr;
+       void *end_chunk;
+       for (chunk = alc->chunk_list; chunk; chunk = chunk->block.next) {
+               end_chunk = (char*)chunk + chunk->num_pages * FREELIST_PAGESIZE;
+               mono_sgen_debug_printf (6, "Scanning pinned chunk %p (range: %p-%p)\n", chunk, chunk->start_data, end_chunk);
+               for (i = 0; i < chunk->num_pages; ++i) {
+                       obj_size = chunk->page_sizes [i];
+                       if (!obj_size)
+                               continue;
+                       p = i? (char*)chunk + i * FREELIST_PAGESIZE: chunk->start_data;
+                       endp = i? p + FREELIST_PAGESIZE: (char*)chunk + FREELIST_PAGESIZE;
+                       mono_sgen_debug_printf (6, "Page %d (size: %d, range: %p-%p)\n", i, obj_size, p, endp);
+                       while (p + obj_size <= endp) {
+                               ptr = (void**)p;
+                               /* if the first word (the vtable) is outside the chunk we have an object */
+                               if (*ptr && (*ptr < (void*)chunk || *ptr >= end_chunk))
+                                       callback ((char*)ptr, obj_size, callback_data);
+                               p += obj_size;
+                       }
+               }
+       }
+}
+
+void
+mono_sgen_pinned_update_heap_boundaries (SgenPinnedAllocator *alc)
+{
+       SgenPinnedChunk *chunk;
+       for (chunk = alc->chunk_list; chunk; chunk = chunk->block.next) {
+               char *end_chunk = (char*)chunk + chunk->num_pages * FREELIST_PAGESIZE;
+               mono_sgen_update_heap_boundaries ((mword)chunk, (mword)end_chunk);
+       }
+}
+
+/*
+ * the array of pointers from @start to @end contains conservative
+ * pointers to objects inside @chunk: mark each referenced object
+ * with the PIN bit.
+ */
+static void
+mark_pinned_from_addresses (SgenPinnedChunk *chunk, void **start, void **end, IterateObjectCallbackFunc callback, void *callback_data)
+{
+       for (; start < end; start++) {
+               char *addr = *start;
+               int offset = (char*)addr - (char*)chunk;
+               int page = offset / FREELIST_PAGESIZE;
+               int obj_offset = page == 0? offset - ((char*)chunk->start_data - (char*)chunk): offset % FREELIST_PAGESIZE;
+               int slot_size = chunk->page_sizes [page];
+               void **ptr;
+               /* the page is not allocated */
+               if (!slot_size)
+                       continue;
+               /* would be faster if we restrict the sizes to power of two,
+                * but that's a waste of memory: need to measure. it could reduce
+                * fragmentation since there are less pages needed, if for example
+                * someone interns strings of each size we end up with one page per
+                * interned string (still this is just ~40 KB): with more fine-grained sizes
+                * this increases the number of used pages.
+                */
+               if (page == 0) {
+                       obj_offset /= slot_size;
+                       obj_offset *= slot_size;
+                       addr = (char*)chunk->start_data + obj_offset;
+               } else {
+                       obj_offset /= slot_size;
+                       obj_offset *= slot_size;
+                       addr = (char*)chunk + page * FREELIST_PAGESIZE + obj_offset;
+               }
+               ptr = (void**)addr;
+               /* if the vtable is inside the chunk it's on the freelist, so skip */
+               /* FIXME: is it possible that we're pinning objects more than once here? */
+               if (*ptr && (*ptr < (void*)chunk->start_data || *ptr > (void*)((char*)chunk + chunk->num_pages * FREELIST_PAGESIZE)))
+                       callback (addr, slot_size, callback_data);
+       }
+}
+
+void
+mono_sgen_pinned_scan_pinned_objects (SgenPinnedAllocator *alc, IterateObjectCallbackFunc callback, void *callback_data)
+{
+       SgenPinnedChunk *chunk;
+
+       /* look for pinned addresses for pinned-alloc objects */
+       mono_sgen_debug_printf (6, "Pinning from pinned-alloc objects\n");
+       for (chunk = alc->chunk_list; chunk; chunk = chunk->block.next) {
+               int num_pinned;
+               void **pinned = mono_sgen_find_optimized_pin_queue_area (chunk->start_data,
+                               (char*)chunk + chunk->num_pages * FREELIST_PAGESIZE, &num_pinned);
+               if (num_pinned)
+                       mark_pinned_from_addresses (chunk, pinned, pinned + num_pinned, callback, callback_data);
+       }
+}
+
+#endif
index d16a90afed8a11fd438b64428825f0e5b1eb7562..497a5c0d44a65a2d191c3d8a9128a5faff3b43b9 100644 (file)
@@ -51,7 +51,6 @@ static WorkerData *workers_data;
 static WorkerData workers_gc_thread_data;
 
 static GrayQueue workers_distribute_gray_queue;
-static SgenInternalAllocator workers_distribute_gray_queue_allocator;
 
 #define WORKERS_DISTRIBUTE_GRAY_QUEUE (major_collector.is_parallel ? &workers_distribute_gray_queue : &gray_queue)
 
@@ -117,7 +116,7 @@ workers_wait (void)
 }
 
 static void
-workers_enqueue_job (SgenInternalAllocator *allocator, JobFunc func, void *data)
+workers_enqueue_job (JobFunc func, void *data)
 {
        int num_entries;
        JobQueueEntry *entry;
@@ -127,7 +126,7 @@ workers_enqueue_job (SgenInternalAllocator *allocator, JobFunc func, void *data)
                return;
        }
 
-       entry = mono_sgen_alloc_internal_fixed (allocator, INTERNAL_MEM_JOB_QUEUE_ENTRY);
+       entry = mono_sgen_alloc_internal (INTERNAL_MEM_JOB_QUEUE_ENTRY);
        entry->func = func;
        entry->data = data;
 
@@ -162,7 +161,7 @@ workers_dequeue_and_do_job (WorkerData *data)
                return FALSE;
 
        entry->func (data, entry->data);
-       mono_sgen_free_internal_delayed (entry, INTERNAL_MEM_JOB_QUEUE_ENTRY, data->private_gray_queue.allocator);
+       mono_sgen_free_internal (entry, INTERNAL_MEM_JOB_QUEUE_ENTRY);
        return TRUE;
 }
 
@@ -273,7 +272,7 @@ workers_gray_queue_share_redirect (GrayQueue *queue)
                if (section->end)
                        gray_object_enqueue_section (queue, section);
                else
-                       gray_object_free_queue_section (section, queue->allocator);
+                       gray_object_free_queue_section (section);
        }
 
        if (data != &workers_gc_thread_data && gray_object_queue_is_empty (queue))
@@ -289,17 +288,11 @@ static void*
 workers_thread_func (void *data_untyped)
 {
        WorkerData *data = data_untyped;
-       SgenInternalAllocator allocator;
 
        if (major_collector.init_worker_thread)
                major_collector.init_worker_thread (data->major_collector_data);
 
-       memset (&allocator, 0, sizeof (allocator));
-#ifdef SGEN_DEBUG_INTERNAL_ALLOC
-       allocator.thread = pthread_self ();
-#endif
-
-       gray_object_queue_init_with_alloc_prepare (&data->private_gray_queue, &allocator,
+       gray_object_queue_init_with_alloc_prepare (&data->private_gray_queue,
                        workers_gray_queue_share_redirect, data);
 
        for (;;) {
@@ -317,7 +310,7 @@ workers_thread_func (void *data_untyped)
                                workers_gray_queue_share_redirect (&data->private_gray_queue);
                        g_assert (gray_object_queue_is_empty (&data->private_gray_queue));
 
-                       gray_object_queue_init (&data->private_gray_queue, &allocator);
+                       gray_object_queue_init (&data->private_gray_queue);
 
                        did_work = TRUE;
                }
@@ -342,17 +335,10 @@ workers_distribute_gray_queue_sections (void)
 static void
 workers_init_distribute_gray_queue (void)
 {
-       if (!major_collector.is_parallel) {
-#ifdef SGEN_DEBUG_INTERNAL_ALLOC
-               mono_sgen_get_unmanaged_allocator ()->thread = pthread_self ();
-#endif
+       if (!major_collector.is_parallel)
                return;
-       }
 
-       gray_object_queue_init (&workers_distribute_gray_queue, &workers_distribute_gray_queue_allocator);
-#ifdef SGEN_DEBUG_INTERNAL_ALLOC
-       workers_distribute_gray_queue_allocator.thread = pthread_self ();
-#endif
+       gray_object_queue_init (&workers_distribute_gray_queue);
 }
 
 static void
@@ -373,7 +359,7 @@ workers_init (int num_workers)
        MONO_SEM_INIT (&workers_waiting_sem, 0);
        MONO_SEM_INIT (&workers_done_sem, 0);
 
-       gray_object_queue_init_with_alloc_prepare (&workers_distribute_gray_queue, &workers_distribute_gray_queue_allocator,
+       gray_object_queue_init_with_alloc_prepare (&workers_distribute_gray_queue,
                        workers_gray_queue_share_redirect, &workers_gc_thread_data);
        pthread_mutex_init (&workers_gc_thread_data.stealable_stack_mutex, NULL);
        workers_gc_thread_data.stealable_stack_fill = 0;
@@ -458,12 +444,8 @@ workers_join (void)
 {
        int i;
 
-       if (!major_collector.is_parallel) {
-#ifdef SGEN_DEBUG_INTERNAL_ALLOC
-               mono_sgen_get_unmanaged_allocator ()->thread = NULL;
-#endif
+       if (!major_collector.is_parallel)
                return;
-       }
 
        g_assert (gray_object_queue_is_empty (&workers_gc_thread_data.private_gray_queue));
        g_assert (gray_object_queue_is_empty (&workers_distribute_gray_queue));