Merge pull request #1193 from esdrubal/decimal-round
[mono.git] / mono / metadata / sgen-nursery-allocator.c
index c681a1f5a02c4ea52c59db97396caceb96e94dd6..3d422e6b7b8aac5c8d9765bb61523a105ede4e44 100644 (file)
@@ -1,31 +1,24 @@
 /*
  * sgen-nursery-allocator.c: Nursery allocation code.
  *
- *
  * Copyright 2009-2010 Novell, Inc.
  *           2011 Rodrigo Kumpera
  * 
  * Copyright 2011 Xamarin Inc  (http://www.xamarin.com)
+ * Copyright (C) 2012 Xamarin Inc
  *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License 2.0 as published by the Free Software Foundation;
  *
- * 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.
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License 2.0 along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
 /*
@@ -77,6 +70,7 @@
 #include "metadata/sgen-protocol.h"
 #include "metadata/sgen-archdep.h"
 #include "metadata/sgen-bridge.h"
+#include "metadata/sgen-memory-governor.h"
 #include "metadata/mono-gc.h"
 #include "metadata/method-builder.h"
 #include "metadata/profiler-private.h"
@@ -107,14 +101,14 @@ char *sgen_nursery_start;
 char *sgen_nursery_end;
 
 #ifdef USER_CONFIG
-int sgen_nursery_size = (1 << 22);
+size_t sgen_nursery_size = (1 << 22);
 #ifdef SGEN_ALIGN_NURSERY
 int sgen_nursery_bits = 22;
 #endif
 #endif
 
 char *sgen_space_bitmap MONO_INTERNAL;
-int sgen_space_bitmap_size MONO_INTERNAL;
+size_t sgen_space_bitmap_size MONO_INTERNAL;
 
 #ifdef HEAVY_STATISTICS
 
@@ -158,6 +152,8 @@ static AllocRecord *alloc_records;
 static volatile int next_record;
 static volatile int alloc_count;
 
+void dump_alloc_records (void);
+void verify_alloc_records (void);
 
 static const char*
 get_reason_name (AllocRecord *rec)
@@ -206,7 +202,7 @@ void
 dump_alloc_records (void)
 {
        int i;
-       qsort (alloc_records, next_record, sizeof (AllocRecord), comp_alloc_record);
+       sgen_qsort (alloc_records, next_record, sizeof (AllocRecord), comp_alloc_record);
 
        printf ("------------------------------------DUMP RECORDS----------------------------\n");
        for (i = 0; i < next_record; ++i) {
@@ -224,7 +220,7 @@ verify_alloc_records (void)
        int max_hole = 0;
        AllocRecord *prev = NULL;
 
-       qsort (alloc_records, next_record, sizeof (AllocRecord), comp_alloc_record);
+       sgen_qsort (alloc_records, next_record, sizeof (AllocRecord), comp_alloc_record);
        printf ("------------------------------------DUMP RECORDS- %d %d---------------------------\n", next_record, alloc_count);
        for (i = 0; i < next_record; ++i) {
                AllocRecord *rec = alloc_records + i;
@@ -397,7 +393,7 @@ par_alloc_from_fragment (SgenFragmentAllocator *allocator, SgenFragment *frag, s
                 * allocating from this dying fragment as it doesn't respect SGEN_MAX_NURSERY_WASTE
                 * when doing second chance allocation.
                 */
-               if (sgen_get_nursery_clear_policy () == CLEAR_AT_TLAB_CREATION && claim_remaining_size (frag, end)) {
+               if ((sgen_get_nursery_clear_policy () == CLEAR_AT_TLAB_CREATION || sgen_get_nursery_clear_policy () == CLEAR_AT_TLAB_CREATION_DEBUG) && claim_remaining_size (frag, end)) {
                        sgen_clear_range (end, frag->fragment_end);
                        HEAVY_STAT (InterlockedExchangeAdd (&stat_wasted_bytes_trailer, frag->fragment_end - end));
 #ifdef NALLOC_DEBUG
@@ -418,7 +414,7 @@ par_alloc_from_fragment (SgenFragmentAllocator *allocator, SgenFragment *frag, s
                                /*frag->next read must happen before the first CAS*/
                                mono_memory_write_barrier ();
 
-                               /*Fail if the next done is removed concurrently and its CAS wins */
+                               /*Fail if the next node is removed concurrently and its CAS wins */
                                if (InterlockedCompareExchangePointer ((volatile gpointer*)&frag->next, mask (next, 1), next) != next) {
                                        continue;
                                }
@@ -428,7 +424,7 @@ par_alloc_from_fragment (SgenFragmentAllocator *allocator, SgenFragment *frag, s
                        mono_memory_write_barrier ();
 
                        /* Fail if the previous node was deleted and its CAS wins */
-                       if (InterlockedCompareExchangePointer ((volatile gpointer*)prev_ptr, next, frag) != frag) {
+                       if (InterlockedCompareExchangePointer ((volatile gpointer*)prev_ptr, unmask (next), frag) != frag) {
                                prev_ptr = find_previous_pointer_fragment (allocator, frag);
                                continue;
                        }
@@ -475,7 +471,7 @@ restart:
        for (frag = unmask (allocator->alloc_head); unmask (frag); frag = unmask (frag->next)) {
                HEAVY_STAT (InterlockedIncrement (&stat_alloc_iterations));
 
-               if (size <= (frag->fragment_end - frag->fragment_next)) {
+               if (size <= (size_t)(frag->fragment_end - frag->fragment_next)) {
                        void *p = par_alloc_from_fragment (allocator, frag, size);
                        if (!p) {
                                HEAVY_STAT (InterlockedIncrement (&stat_alloc_retries));
@@ -583,7 +579,7 @@ restart:
 #endif
 
        for (frag = unmask (allocator->alloc_head); frag; frag = unmask (frag->next)) {
-               int frag_size = frag->fragment_end - frag->fragment_next;
+               size_t frag_size = frag->fragment_end - frag->fragment_next;
 
                HEAVY_STAT (InterlockedIncrement (&stat_alloc_range_iterations));
 
@@ -612,7 +608,7 @@ restart:
 
        if (min_frag) {
                void *p;
-               int frag_size;
+               size_t frag_size;
 
                frag_size = min_frag->fragment_end - min_frag->fragment_next;
                if (frag_size < minimum_size)
@@ -643,7 +639,7 @@ sgen_clear_allocator_fragments (SgenFragmentAllocator *allocator)
        SgenFragment *frag;
 
        for (frag = unmask (allocator->alloc_head); frag; frag = unmask (frag->next)) {
-               DEBUG (4, fprintf (gc_debug_file, "Clear nursery frag %p-%p\n", frag->fragment_next, frag->fragment_end));
+               SGEN_LOG (4, "Clear nursery frag %p-%p", frag->fragment_next, frag->fragment_end);
                sgen_clear_range (frag->fragment_next, frag->fragment_end);
 #ifdef NALLOC_DEBUG
                add_alloc_record (frag->fragment_next, frag->fragment_end - frag->fragment_next, CLEAR_NURSERY_FRAGS);
@@ -655,7 +651,7 @@ sgen_clear_allocator_fragments (SgenFragmentAllocator *allocator)
 void
 sgen_clear_nursery_fragments (void)
 {
-       if (sgen_get_nursery_clear_policy () == CLEAR_AT_TLAB_CREATION) {
+       if (sgen_get_nursery_clear_policy () == CLEAR_AT_TLAB_CREATION || sgen_get_nursery_clear_policy () == CLEAR_AT_TLAB_CREATION_DEBUG) {
                sgen_clear_allocator_fragments (&mutator_allocator);
                sgen_minor_collector.clear_fragments ();
        }
@@ -689,7 +685,7 @@ sgen_clear_range (char *start, char *end)
        /* Mark this as not a real object */
        o->obj.synchronisation = GINT_TO_POINTER (-1);
        o->bounds = NULL;
-       o->max_length = size - sizeof (MonoArray);
+       o->max_length = (mono_array_size_t)(size - sizeof (MonoArray));
        sgen_set_nursery_scan_start (start);
        g_assert (start + sgen_safe_object_get_size ((MonoObject*)o) == end);
 }
@@ -710,13 +706,16 @@ static mword fragment_total = 0;
 static void
 add_nursery_frag (SgenFragmentAllocator *allocator, size_t frag_size, char* frag_start, char* frag_end)
 {
-       DEBUG (4, fprintf (gc_debug_file, "Found empty fragment: %p-%p, size: %zd\n", frag_start, frag_end, frag_size));
+       SGEN_LOG (4, "Found empty fragment: %p-%p, size: %zd", frag_start, frag_end, frag_size);
        binary_protocol_empty (frag_start, frag_size);
+       MONO_GC_NURSERY_SWEPT ((mword)frag_start, frag_end - frag_start);
        /* Not worth dealing with smaller fragments: need to tune */
        if (frag_size >= SGEN_MAX_NURSERY_WASTE) {
                /* memsetting just the first chunk start is bound to provide better cache locality */
                if (sgen_get_nursery_clear_policy () == CLEAR_AT_GC)
                        memset (frag_start, 0, frag_size);
+               else if (sgen_get_nursery_clear_policy () == CLEAR_AT_TLAB_CREATION_DEBUG)
+                       memset (frag_start, 0xff, frag_size);
 
 #ifdef NALLOC_DEBUG
                /* XXX convert this into a flight record entry
@@ -748,11 +747,11 @@ fragment_list_reverse (SgenFragmentAllocator *allocator)
 }
 
 mword
-sgen_build_nursery_fragments (GCMemSection *nursery_section, void **start, int num_entries)
+sgen_build_nursery_fragments (GCMemSection *nursery_section, void **start, size_t num_entries, SgenGrayQueue *unpin_queue)
 {
        char *frag_start, *frag_end;
        size_t frag_size;
-       int i = 0;
+       size_t i = 0;
        SgenFragment *frags_ranges;
 
 #ifdef NALLOC_DEBUG
@@ -782,7 +781,10 @@ sgen_build_nursery_fragments (GCMemSection *nursery_section, void **start, int n
                        addr1 = frags_ranges->fragment_start;
 
                if (addr0 < addr1) {
-                       SGEN_UNPIN_OBJECT (addr0);
+                       if (unpin_queue)
+                               GRAY_OBJECT_ENQUEUE (unpin_queue, addr0);
+                       else
+                               SGEN_UNPIN_OBJECT (addr0);
                        sgen_set_nursery_scan_start (addr0);
                        frag_end = addr0;
                        size = SGEN_ALIGN_UP (sgen_safe_object_get_size ((MonoObject*)addr0));
@@ -827,9 +829,9 @@ sgen_build_nursery_fragments (GCMemSection *nursery_section, void **start, int n
        sgen_minor_collector.build_fragments_finish (&mutator_allocator);
 
        if (!unmask (mutator_allocator.alloc_head)) {
-               DEBUG (1, fprintf (gc_debug_file, "Nursery fully pinned (%d)\n", num_entries));
+               SGEN_LOG (1, "Nursery fully pinned (%zd)", num_entries);
                for (i = 0; i < num_entries; ++i) {
-                       DEBUG (3, fprintf (gc_debug_file, "Bastard pinning obj %p (%s), size: %d\n", start [i], sgen_safe_name (start [i]), sgen_safe_object_get_size (start [i])));
+                       SGEN_LOG (3, "Bastard pinning obj %p (%s), size: %zd", start [i], sgen_safe_name (start [i]), sgen_safe_object_get_size (start [i]));
                }
        }
        return fragment_total;
@@ -853,10 +855,14 @@ gboolean
 sgen_can_alloc_size (size_t size)
 {
        SgenFragment *frag;
+
+       if (!SGEN_CAN_ALIGN_UP (size))
+               return FALSE;
+
        size = SGEN_ALIGN_UP (size);
 
        for (frag = unmask (mutator_allocator.alloc_head); frag; frag = unmask (frag->next)) {
-               if ((frag->fragment_end - frag->fragment_next) >= size)
+               if ((size_t)(frag->fragment_end - frag->fragment_next) >= size)
                        return TRUE;
        }
        return FALSE;
@@ -865,7 +871,9 @@ sgen_can_alloc_size (size_t size)
 void*
 sgen_nursery_alloc (size_t size)
 {
-       DEBUG (4, fprintf (gc_debug_file, "Searching nursery for size: %zd\n", size));
+       SGEN_ASSERT (1, size >= sizeof (MonoObject) && size <= SGEN_MAX_SMALL_OBJ_SIZE, "Invalid nursery object size");
+
+       SGEN_LOG (4, "Searching nursery for size: %zd", size);
        size = SGEN_ALIGN_UP (size);
 
        HEAVY_STAT (InterlockedIncrement (&stat_nursery_alloc_requests));
@@ -876,7 +884,7 @@ sgen_nursery_alloc (size_t size)
 void*
 sgen_nursery_alloc_range (size_t desired_size, size_t minimum_size, size_t *out_alloc_size)
 {
-       DEBUG (4, fprintf (gc_debug_file, "Searching for byte range desired size: %zd minimum size %zd\n", desired_size, minimum_size));
+       SGEN_LOG (4, "Searching for byte range desired size: %zd minimum size %zd", desired_size, minimum_size);
 
        HEAVY_STAT (InterlockedIncrement (&stat_nursery_alloc_range_requests));
 
@@ -910,7 +918,7 @@ sgen_init_nursery_allocator (void)
 {
        sgen_register_fixed_internal_mem_type (INTERNAL_MEM_FRAGMENT, sizeof (SgenFragment));
 #ifdef NALLOC_DEBUG
-       alloc_records = sgen_alloc_os_memory (sizeof (AllocRecord) * ALLOC_RECORD_COUNT, TRUE, "debugging memory");
+       alloc_records = sgen_alloc_os_memory (sizeof (AllocRecord) * ALLOC_RECORD_COUNT, SGEN_ALLOC_INTERNAL | SGEN_ALLOC_ACTIVATE, "debugging memory");
 #endif
 }
 
@@ -932,7 +940,12 @@ sgen_nursery_allocator_set_nursery_bounds (char *start, char *end)
        sgen_nursery_start = start;
        sgen_nursery_end = end;
 
-       sgen_space_bitmap_size = (end - start) / (SGEN_TO_SPACE_GRANULE_IN_BYTES * 8);
+       /*
+        * This will not divide evenly for tiny nurseries (<4kb), so we make sure to be on
+        * the right side of things and round up.  We could just do a MIN(1,x) instead,
+        * since the nursery size must be a power of 2.
+        */
+       sgen_space_bitmap_size = (end - start + SGEN_TO_SPACE_GRANULE_IN_BYTES * 8 - 1) / (SGEN_TO_SPACE_GRANULE_IN_BYTES * 8);
        sgen_space_bitmap = g_malloc0 (sgen_space_bitmap_size);
 
        /* Setup the single first large fragment */