[coop handles] Use a bitmap per HandleChunk to track interior ptr handles
[mono.git] / mono / metadata / handle.c
1 /*
2  * handle.c: Handle to object in native code
3  *
4  * Authors:
5  *  - Ludovic Henry <ludovic@xamarin.com>
6  *  - Aleksey Klieger <aleksey.klieger@xamarin.com>
7  *  - Rodrigo Kumpera <kumpera@xamarin.com>
8  *
9  * Copyright 2016 Dot net foundation.
10  * Licensed under the MIT license. See LICENSE file in the project root for full license information.
11  */
12
13 #include <config.h>
14 #include <glib.h>
15
16 #include <mono/metadata/handle.h>
17 #include <mono/metadata/object-internals.h>
18 #include <mono/metadata/gc-internals.h>
19 #include <mono/utils/atomic.h>
20 #include <mono/utils/mono-lazy-init.h>
21 #include <mono/utils/mono-threads.h>
22 /* TODO (missing pieces)
23
24 Add counters for:
25         number of stack marks
26         stack marks per icall
27         mix/max/avg size of stack marks
28         handle stack wastage
29
30 Actually do something in mono_handle_verify
31
32 Shrink the handles stack in mono_handle_stack_scan
33 Properly report it to the profiler.
34 Add a boehm implementation
35
36 TODO (things to explore):
37
38 There's no convenient way to wrap the object allocation function.
39 Right now we do this:
40         MonoCultureInfoHandle culture = MONO_HANDLE_NEW (MonoCultureInfo, mono_object_new_checked (domain, klass, &error));
41
42 Maybe what we need is a round of cleanup around all exposed types in the runtime to unify all helpers under the same hoof.
43 Combine: MonoDefaults, GENERATE_GET_CLASS_WITH_CACHE, TYPED_HANDLE_DECL and friends.
44         This would solve the age old issue of making it clear which types are optional and tell that to the linker.
45         We could then generate neat type safe wrappers.
46 */
47
48 /*
49  * NOTE: Async suspend
50  * 
51  * If we are running with cooperative GC, all the handle stack
52  * manipulation will complete before a GC thread scans the handle
53  * stack. If we are using async suspend, however, a thread may be
54  * trying to allocate a new handle, or unwind the handle stack when
55  * the GC stops the world.
56  *
57  * In particular, we need to ensure that if the mutator thread is
58  * suspended while manipulating the handle stack, the stack is in a
59  * good enough state to be scanned.  In particular, the size of each
60  * chunk should be updated before an object is written into the
61  * handle, and chunks to be scanned (between bottom and top) should
62  * always be valid.
63  *
64  * Note that the handle stack is scanned PRECISELY (see
65  * sgen_client_scan_thread_data ()).  That means there should not be
66  * stale objects scanned.  So when we manipulate the size of a chunk,
67  * wemust ensure that the newly scannable slot is either null or
68  * points to a valid value.
69  */
70
71 const MonoObjectHandle mono_null_value_handle = NULL;
72
73 #define THIS_IS_AN_OK_NUMBER_OF_HANDLES 100
74
75 enum {
76         HANDLE_CHUNK_PTR_OBJ = 0x0, /* chunk element points to beginning of a managed object */
77         HANDLE_CHUNK_PTR_INTERIOR = 0x1, /* chunk element points into the middle of a managed object */
78         HANDLE_CHUNK_PTR_MASK = 0x1
79 };
80
81 /* number of bits in each word of the interior pointer bitmap */
82 #define INTERIOR_HANDLE_BITMAP_BITS_PER_WORD (sizeof(guint32) << 3)
83
84 static gboolean
85 bitset_bits_test (guint32 *bitmaps, int idx)
86 {
87         int w = idx / INTERIOR_HANDLE_BITMAP_BITS_PER_WORD;
88         int b = idx % INTERIOR_HANDLE_BITMAP_BITS_PER_WORD;
89         guint32 bitmap = bitmaps [w];
90         guint32 mask = 1u << b;
91         return ((bitmap & mask) != 0);
92 }
93
94 static void
95 bitset_bits_set (guint32 *bitmaps, int idx)
96 {
97         int w = idx / INTERIOR_HANDLE_BITMAP_BITS_PER_WORD;
98         int b = idx % INTERIOR_HANDLE_BITMAP_BITS_PER_WORD;
99         guint32 *bitmap = &bitmaps [w];
100         guint32 mask = 1u << b;
101         *bitmap |= mask;
102 }
103 static void
104 bitset_bits_clear (guint32 *bitmaps, int idx)
105 {
106         int w = idx / INTERIOR_HANDLE_BITMAP_BITS_PER_WORD;
107         int b = idx % INTERIOR_HANDLE_BITMAP_BITS_PER_WORD;
108         guint32 *bitmap = &bitmaps [w];
109         guint32 mask = ~(1u << b);
110         *bitmap &= mask;
111 }
112
113 static gpointer*
114 chunk_element_objslot_init (HandleChunk *chunk, int idx, gboolean interior)
115 {
116         if (interior)
117                 bitset_bits_set (chunk->interior_bitmap, idx);
118         else
119                 bitset_bits_clear (chunk->interior_bitmap, idx);
120         return &chunk->elems [idx].o;
121 }
122
123 static HandleChunkElem*
124 chunk_element (HandleChunk *chunk, int idx)
125 {
126         return &chunk->elems[idx];
127 }
128
129 static guint
130 chunk_element_kind (HandleChunk *chunk, int idx)
131 {
132         return bitset_bits_test (chunk->interior_bitmap, idx) ? HANDLE_CHUNK_PTR_INTERIOR : HANDLE_CHUNK_PTR_OBJ;
133 }
134
135 static HandleChunkElem*
136 handle_to_chunk_element (MonoObjectHandle o)
137 {
138         return (HandleChunkElem*)o;
139 }
140
141 #ifdef MONO_HANDLE_TRACK_OWNER
142 #define SET_OWNER(chunk,idx) do { (chunk)->elems[(idx)].owner = owner; } while (0)
143 #else
144 #define SET_OWNER(chunk,idx) do { } while (0)
145 #endif
146
147 MonoRawHandle
148 #ifndef MONO_HANDLE_TRACK_OWNER
149 mono_handle_new (MonoObject *object)
150 #else
151 mono_handle_new (MonoObject *object const char *owner)
152 #endif
153 {
154 #ifndef MONO_HANDLE_TRACK_OWNER
155         return mono_handle_new_full (object, FALSE);
156 #else
157         return mono_handle_new_full (object, FALSE, owner);
158 #endif
159 }
160 /* Actual handles implementation */
161 MonoRawHandle
162 #ifndef MONO_HANDLE_TRACK_OWNER
163 mono_handle_new_full (gpointer rawptr, gboolean interior)
164 #else
165 mono_handle_new_full (gpointer rawptr, gboolean interior, const char *owner)
166 #endif
167 {
168         MonoThreadInfo *info = mono_thread_info_current ();
169         HandleStack *handles = (HandleStack *)info->handle_stack;
170         HandleChunk *top = handles->top;
171
172 retry:
173         if (G_LIKELY (top->size < OBJECTS_PER_HANDLES_CHUNK)) {
174                 int idx = top->size;
175                 gpointer* objslot = chunk_element_objslot_init (top, idx, interior);
176                 /* can be interrupted anywhere here, so:
177                  * 1. make sure the new slot is null
178                  * 2. make the new slot scannable (increment size)
179                  * 3. put a valid object in there
180                  *
181                  * (have to do 1 then 3 so that if we're interrupted
182                  * between 1 and 2, the object is still live)
183                  */
184                 *objslot = NULL;
185                 mono_memory_write_barrier ();
186                 top->size++;
187                 mono_memory_write_barrier ();
188                 *objslot = rawptr;
189                 SET_OWNER (top,idx);
190                 return objslot;
191         }
192         if (G_LIKELY (top->next)) {
193                 top->next->size = 0;
194                 /* make sure size == 0 is visible to a GC thread before it sees the new top */
195                 mono_memory_write_barrier ();
196                 top = top->next;
197                 handles->top = top;
198                 goto retry;
199         }
200         HandleChunk *new_chunk = g_new (HandleChunk, 1);
201         new_chunk->size = 0;
202         memset (new_chunk->interior_bitmap, 0, INTERIOR_HANDLE_BITMAP_WORDS);
203         new_chunk->prev = top;
204         new_chunk->next = NULL;
205         /* make sure size == 0 before new chunk is visible */
206         mono_memory_write_barrier ();
207         top->next = new_chunk;
208         handles->top = new_chunk;
209         goto retry;
210 }
211
212
213
214 HandleStack*
215 mono_handle_stack_alloc (void)
216 {
217         HandleStack *stack = g_new (HandleStack, 1);
218         HandleChunk *chunk = g_new (HandleChunk, 1);
219
220         chunk->size = 0;
221         memset (chunk->interior_bitmap, 0, INTERIOR_HANDLE_BITMAP_WORDS);
222         chunk->prev = chunk->next = NULL;
223         mono_memory_write_barrier ();
224         stack->top = stack->bottom = chunk;
225         return stack;
226 }
227
228 void
229 mono_handle_stack_free (HandleStack *stack)
230 {
231         if (!stack)
232                 return;
233         HandleChunk *c = stack->bottom;
234         stack->top = stack->bottom = NULL;
235         mono_memory_write_barrier ();
236         while (c) {
237                 HandleChunk *next = c->next;
238                 g_free (c);
239                 c = next;
240         }
241         g_free (c);
242         g_free (stack);
243 }
244
245 void
246 mono_handle_stack_scan (HandleStack *stack, GcScanFunc func, gpointer gc_data, gboolean precise)
247 {
248         /*
249           We're called twice - on the imprecise pass we call func to pin the
250           objects where the handle points to its interior.  On the precise
251           pass, we scan all the objects where the handles point to the start of
252           the object.
253
254           Note that if we're running, we know the world is stopped.
255         */
256         HandleChunk *cur = stack->bottom;
257         HandleChunk *last = stack->top;
258
259         if (!cur)
260                 return;
261
262         while (cur) {
263                 /* assume that object pointers will be much more common than interior pointers.
264                  * scan the object pointers by iterating over the chunk elements.
265                  * scan the interior pointers by iterating over the bitmap bits.
266                  */
267                 if (precise) {
268                         for (int i = 0; i < cur->size; ++i) {
269                                 HandleChunkElem* elem = chunk_element (cur, i);
270                                 int kind = chunk_element_kind (cur, i);
271                                 gpointer* obj_slot = &elem->o;
272                                 if (kind == HANDLE_CHUNK_PTR_OBJ && *obj_slot != NULL)
273                                         func (obj_slot, gc_data);
274                         }
275                 } else {
276                         int elem_idx = 0;
277                         for (int i = 0; i < INTERIOR_HANDLE_BITMAP_WORDS; ++i) {
278                                 elem_idx = i * INTERIOR_HANDLE_BITMAP_BITS_PER_WORD;
279                                 if (elem_idx >= cur->size)
280                                         break;
281                                 /* no interior pointers in the range */ 
282                                 if (cur->interior_bitmap [i] == 0)
283                                         continue;
284                                 for (int j = 0; j < INTERIOR_HANDLE_BITMAP_BITS_PER_WORD && elem_idx < cur->size; ++j,++elem_idx) {
285                                         HandleChunkElem *elem = chunk_element (cur, elem_idx);
286                                         int kind = chunk_element_kind (cur, elem_idx);
287                                         gpointer *ptr_slot = &elem->o;
288                                         if (kind == HANDLE_CHUNK_PTR_INTERIOR && *ptr_slot != NULL)
289                                                 func (ptr_slot, gc_data);
290                                 }
291                         }            
292                 }
293                 if (cur == last)
294                         break;
295                 cur = cur->next;
296         }
297 }
298
299 void
300 mono_stack_mark_record_size (MonoThreadInfo *info, HandleStackMark *stackmark, const char *func_name)
301 {
302         HandleStack *handles = (HandleStack *)info->handle_stack;
303         HandleChunk *cur = stackmark->chunk;
304         int size = -stackmark->size; //discard the starting point of the stack
305         while (cur) {
306                 size += cur->size;
307                 if (cur == handles->top)
308                         break;
309                 cur = cur->next;
310         }
311
312         if (size > THIS_IS_AN_OK_NUMBER_OF_HANDLES)
313                 g_warning ("%s USED %d handles\n", func_name, size);
314 }
315
316 /*
317  * Pop the stack until @stackmark and make @value the top value.
318  *
319  * @return the new handle for what @value points to 
320  */
321 MonoRawHandle
322 mono_stack_mark_pop_value (MonoThreadInfo *info, HandleStackMark *stackmark, MonoRawHandle value)
323 {
324         MonoObject *obj = value ? *((MonoObject**)value) : NULL;
325         mono_stack_mark_pop (info, stackmark);
326 #ifndef MONO_HANDLE_TRACK_OWNER
327         return mono_handle_new (obj);
328 #else
329         return mono_handle_new (obj, "<mono_stack_mark_pop_value>");
330 #endif
331 }
332
333 /* Temporary place for some of the handle enabled wrapper functions*/
334
335 MonoStringHandle
336 mono_string_new_handle (MonoDomain *domain, const char *data, MonoError *error)
337 {
338         return MONO_HANDLE_NEW (MonoString, mono_string_new_checked (domain, data, error));
339 }
340
341 MonoArrayHandle
342 mono_array_new_handle (MonoDomain *domain, MonoClass *eclass, uintptr_t n, MonoError *error)
343 {
344         return MONO_HANDLE_NEW (MonoArray, mono_array_new_checked (domain, eclass, n, error));
345 }
346
347 MonoArrayHandle
348 mono_array_new_full_handle (MonoDomain *domain, MonoClass *array_class, uintptr_t *lengths, intptr_t *lower_bounds, MonoError *error)
349 {
350         return MONO_HANDLE_NEW (MonoArray, mono_array_new_full_checked (domain, array_class, lengths, lower_bounds, error));
351 }
352
353 #ifdef ENABLE_CHECKED_BUILD
354 /* Checked build helpers */
355 void
356 mono_handle_verify (MonoRawHandle raw_handle)
357 {
358         
359 }
360 #endif
361
362 uintptr_t
363 mono_array_handle_length (MonoArrayHandle arr)
364 {
365         MONO_REQ_GC_UNSAFE_MODE;
366
367         return MONO_HANDLE_RAW (arr)->max_length;
368 }
369
370 uint32_t
371 mono_gchandle_from_handle (MonoObjectHandle handle, mono_bool pinned)
372 {
373         /* FIXME: Want to assert that handle isn't an interior pointer,
374          * gchandle can't deal with interior pointers.  But it's expensive to
375          * convert from a handle to its chunk and index. */
376         return mono_gchandle_new (MONO_HANDLE_RAW (handle), pinned);
377 }
378
379 MonoObjectHandle
380 mono_gchandle_get_target_handle (uint32_t gchandle)
381 {
382         return MONO_HANDLE_NEW (MonoObject, mono_gchandle_get_target (gchandle));
383 }
384
385 gpointer
386 mono_array_handle_pin_with_size (MonoArrayHandle handle, int size, uintptr_t idx, uint32_t *gchandle)
387 {
388         g_assert (gchandle != NULL);
389         *gchandle = mono_gchandle_from_handle (MONO_HANDLE_CAST(MonoObject,handle), TRUE);
390         MonoArray *raw = MONO_HANDLE_RAW (handle);
391         return mono_array_addr_with_size (raw, size, idx);
392 }
393
394 void
395 mono_array_handle_memcpy_refs (MonoArrayHandle dest, uintptr_t dest_idx, MonoArrayHandle src, uintptr_t src_idx, uintptr_t len)
396 {
397         mono_array_memcpy_refs (MONO_HANDLE_RAW (dest), dest_idx, MONO_HANDLE_RAW (src), src_idx, len);
398 }
399
400 gboolean
401 mono_handle_stack_is_empty (HandleStack *stack)
402 {
403         return (stack->top == stack->bottom && stack->top->size == 0);
404 }