Merge pull request #498 from Unroll-Me/master
[mono.git] / mono / metadata / sgen-pinning.c
1 /*
2  * sgen-pinning.c: The pin queue.
3  *
4  * Copyright 2001-2003 Ximian, Inc
5  * Copyright 2003-2010 Novell, Inc.
6  * Copyright (C) 2012 Xamarin Inc
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Library General Public
10  * License 2.0 as published by the Free Software Foundation;
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Library General Public License for more details.
16  *
17  * You should have received a copy of the GNU Library General Public
18  * License 2.0 along with this library; if not, write to the Free
19  * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20  */
21
22 #include "config.h"
23 #ifdef HAVE_SGEN_GC
24
25 #include "metadata/sgen-gc.h"
26 #include "metadata/sgen-pinning.h"
27
28 static void** pin_queue;
29 static int pin_queue_size = 0;
30 static int next_pin_slot = 0;
31 static int last_num_pinned = 0;
32
33 #define PIN_HASH_SIZE 1024
34 static void *pin_hash_filter [PIN_HASH_SIZE];
35
36 void
37 sgen_init_pinning (void)
38 {
39         memset (pin_hash_filter, 0, sizeof (pin_hash_filter));
40 }
41
42 void
43 sgen_finish_pinning (void)
44 {
45         last_num_pinned = next_pin_slot;
46         next_pin_slot = 0;
47 }
48
49 static void
50 realloc_pin_queue (void)
51 {
52         int new_size = pin_queue_size? pin_queue_size + pin_queue_size/2: 1024;
53         void **new_pin = sgen_alloc_internal_dynamic (sizeof (void*) * new_size, INTERNAL_MEM_PIN_QUEUE, TRUE);
54         memcpy (new_pin, pin_queue, sizeof (void*) * next_pin_slot);
55         sgen_free_internal_dynamic (pin_queue, sizeof (void*) * pin_queue_size, INTERNAL_MEM_PIN_QUEUE);
56         pin_queue = new_pin;
57         pin_queue_size = new_size;
58         SGEN_LOG (4, "Reallocated pin queue to size: %d", new_size);
59 }
60
61 void
62 sgen_pin_stage_ptr (void *ptr)
63 {
64         /*very simple multiplicative hash function, tons better than simple and'ng */ 
65         int hash_idx = ((mword)ptr * 1737350767) & (PIN_HASH_SIZE - 1);
66         if (pin_hash_filter [hash_idx] == ptr)
67                 return;
68
69         pin_hash_filter [hash_idx] = ptr;
70
71         if (next_pin_slot >= pin_queue_size)
72                 realloc_pin_queue ();
73
74         pin_queue [next_pin_slot++] = ptr;
75 }
76
77 static int
78 optimized_pin_queue_search (void *addr)
79 {
80         int first = 0, last = next_pin_slot;
81         while (first < last) {
82                 int middle = first + ((last - first) >> 1);
83                 if (addr <= pin_queue [middle])
84                         last = middle;
85                 else
86                         first = middle + 1;
87         }
88         g_assert (first == last);
89         return first;
90 }
91
92 void**
93 sgen_find_optimized_pin_queue_area (void *start, void *end, int *num)
94 {
95         int first, last;
96         first = optimized_pin_queue_search (start);
97         last = optimized_pin_queue_search (end);
98         *num = last - first;
99         if (first == last)
100                 return NULL;
101         return pin_queue + first;
102 }
103
104 void
105 sgen_find_section_pin_queue_start_end (GCMemSection *section)
106 {
107         SGEN_LOG (6, "Pinning from section %p (%p-%p)", section, section->data, section->end_data);
108         section->pin_queue_start = sgen_find_optimized_pin_queue_area (section->data, section->end_data, &section->pin_queue_num_entries);
109         SGEN_LOG (6, "Found %d pinning addresses in section %p", section->pin_queue_num_entries, section);
110 }
111
112 /*This will setup the given section for the while pin queue. */
113 void
114 sgen_pinning_setup_section (GCMemSection *section)
115 {
116         section->pin_queue_start = pin_queue;
117         section->pin_queue_num_entries = next_pin_slot;
118 }
119
120 void
121 sgen_pinning_trim_queue_to_section (GCMemSection *section)
122 {
123         next_pin_slot = section->pin_queue_num_entries;
124 }
125
126 void
127 sgen_pin_queue_clear_discarded_entries (GCMemSection *section, int max_pin_slot)
128 {
129         void **start = section->pin_queue_start + section->pin_queue_num_entries;
130         void **end = pin_queue + max_pin_slot;
131         void *addr;
132
133         if (!start)
134                 return;
135
136         for (; start < end; ++start) {
137                 addr = *start;
138                 if ((char*)addr < section->data || (char*)addr > section->end_data)
139                         break;
140                 *start = NULL;
141         }
142 }
143
144 /* reduce the info in the pin queue, removing duplicate pointers and sorting them */
145 void
146 sgen_optimize_pin_queue (int start_slot)
147 {
148         void **start, **cur, **end;
149         /* sort and uniq pin_queue: we just sort and we let the rest discard multiple values */
150         /* it may be better to keep ranges of pinned memory instead of individually pinning objects */
151         SGEN_LOG (5, "Sorting pin queue, size: %d", next_pin_slot);
152         if ((next_pin_slot - start_slot) > 1)
153                 sgen_sort_addresses (pin_queue + start_slot, next_pin_slot - start_slot);
154         start = cur = pin_queue + start_slot;
155         end = pin_queue + next_pin_slot;
156         while (cur < end) {
157                 *start = *cur++;
158                 while (*start == *cur && cur < end)
159                         cur++;
160                 start++;
161         };
162         next_pin_slot = start - pin_queue;
163         SGEN_LOG (5, "Pin queue reduced to size: %d", next_pin_slot);
164 }
165
166 int
167 sgen_get_pinned_count (void)
168 {
169         return next_pin_slot;
170 }
171
172 void
173 sgen_dump_pin_queue (void)
174 {
175         int i;
176
177         for (i = 0; i < last_num_pinned; ++i) {
178                 SGEN_LOG (3, "Bastard pinning obj %p (%s), size: %d", pin_queue [i], sgen_safe_name (pin_queue [i]), sgen_safe_object_get_size (pin_queue [i]));
179         }       
180 }
181
182 #endif /* HAVE_SGEN_GC */