3 * Handle to object in native code
6 * - Ludovic Henry <ludovic@xamarin.com>
7 * - Aleksey Klieger <aleksey.klieger@xamarin.com>
8 * - Rodrigo Kumpera <kumpera@xamarin.com>
10 * Copyright 2016 Dot net foundation.
11 * Licensed under the MIT license. See LICENSE file in the project root for full license information.
17 #include <mono/metadata/handle.h>
18 #include <mono/metadata/object-internals.h>
19 #include <mono/metadata/gc-internals.h>
20 #include <mono/utils/atomic.h>
21 #include <mono/utils/mono-lazy-init.h>
22 #include <mono/utils/mono-threads.h>
23 #ifdef HAVE_BACKTRACE_SYMBOLS
27 /* TODO (missing pieces)
32 mix/max/avg size of stack marks
35 Actually do something in mono_handle_verify
37 Shrink the handles stack in mono_handle_stack_scan
38 Properly report it to the profiler.
39 Add a boehm implementation
41 TODO (things to explore):
43 There's no convenient way to wrap the object allocation function.
45 MonoCultureInfoHandle culture = MONO_HANDLE_NEW (MonoCultureInfo, mono_object_new_checked (domain, klass, &error));
47 Maybe what we need is a round of cleanup around all exposed types in the runtime to unify all helpers under the same hoof.
48 Combine: MonoDefaults, GENERATE_GET_CLASS_WITH_CACHE, TYPED_HANDLE_DECL and friends.
49 This would solve the age old issue of making it clear which types are optional and tell that to the linker.
50 We could then generate neat type safe wrappers.
56 * If we are running with cooperative GC, all the handle stack
57 * manipulation will complete before a GC thread scans the handle
58 * stack. If we are using async suspend, however, a thread may be
59 * trying to allocate a new handle, or unwind the handle stack when
60 * the GC stops the world.
62 * In particular, we need to ensure that if the mutator thread is
63 * suspended while manipulating the handle stack, the stack is in a
64 * good enough state to be scanned. In particular, the size of each
65 * chunk should be updated before an object is written into the
66 * handle, and chunks to be scanned (between bottom and top) should
69 * Note that the handle stack is scanned PRECISELY (see
70 * sgen_client_scan_thread_data ()). That means there should not be
71 * stale objects scanned. So when we manipulate the size of a chunk,
72 * wemust ensure that the newly scannable slot is either null or
73 * points to a valid value.
76 #if defined(HAVE_BOEHM_GC) || defined(HAVE_NULL_GC)
78 new_handle_stack (void)
80 return (HandleStack *)mono_gc_alloc_fixed (sizeof (HandleStack), MONO_GC_DESCRIPTOR_NULL, MONO_ROOT_SOURCE_HANDLE, "Thread Handle Stack");
84 free_handle_stack (HandleStack *stack)
86 mono_gc_free_fixed (stack);
90 new_handle_chunk (void)
92 #if defined(HAVE_BOEHM_GC)
93 return (HandleChunk *)GC_MALLOC (sizeof (HandleChunk));
94 #elif defined(HAVE_NULL_GC)
95 return (HandleChunk *)g_malloc (sizeof (HandleChunk));
100 free_handle_chunk (HandleChunk *chunk)
102 #if defined(HAVE_NULL_GC)
108 new_handle_stack (void)
110 return g_new (HandleStack, 1);
114 free_handle_stack (HandleStack *stack)
120 new_handle_chunk (void)
122 return g_new (HandleChunk, 1);
126 free_handle_chunk (HandleChunk *chunk)
132 const MonoObjectHandle mono_null_value_handle = NULL;
134 #define THIS_IS_AN_OK_NUMBER_OF_HANDLES 100
136 static HandleChunkElem*
137 chunk_element (HandleChunk *chunk, int idx)
139 return &chunk->elems[idx];
142 static HandleChunkElem*
143 handle_to_chunk_element (MonoObjectHandle o)
145 return (HandleChunkElem*)o;
148 /* Given a HandleChunkElem* search through the current handle stack to find its chunk and offset. */
150 chunk_element_to_chunk_idx (HandleStack *stack, HandleChunkElem *elem, int *out_idx)
152 HandleChunk *top = stack->top;
153 HandleChunk *cur = stack->bottom;
157 while (cur != NULL) {
158 HandleChunkElem *front = &cur->elems [0];
159 HandleChunkElem *back = &cur->elems [cur->size];
161 if (front <= elem && elem < back) {
162 *out_idx = (int)(elem - front);
167 break; /* didn't find it. */
173 #ifdef MONO_HANDLE_TRACK_OWNER
174 #ifdef HAVE_BACKTRACE_SYMBOLS
175 #define SET_BACKTRACE(btaddrs) do { \
176 backtrace(btaddrs, 7); \
179 #define SET_BACKTRACE(btaddrs) 0
181 #define SET_OWNER(chunk,idx) do { (chunk)->elems[(idx)].owner = owner; SET_BACKTRACE (&((chunk)->elems[(idx)].backtrace_ips[0])); } while (0)
183 #define SET_OWNER(chunk,idx) do { } while (0)
186 #ifdef MONO_HANDLE_TRACK_SP
187 #define SET_SP(handles,chunk,idx) do { (chunk)->elems[(idx)].alloc_sp = handles->stackmark_sp; } while (0)
189 #define SET_SP(handles,chunk,idx) do { } while (0)
192 #ifdef MONO_HANDLE_TRACK_SP
194 mono_handle_chunk_leak_check (HandleStack *handles) {
195 if (handles->stackmark_sp) {
196 /* walk back from the top to the topmost non-empty chunk */
197 HandleChunk *c = handles->top;
198 while (c && c->size <= 0 && c != handles->bottom) {
201 if (c == NULL || c->size == 0)
203 g_assert (c && c->size > 0);
204 HandleChunkElem *e = chunk_element (c, c->size - 1);
205 if (e->alloc_sp < handles->stackmark_sp) {
206 /* If we get here, the topmost object on the handle stack was
207 * allocated from a function that is deeper in the call stack than
208 * the most recent HANDLE_FUNCTION_ENTER. That means it was
209 * probably not wrapped in a HANDLE_FUNCTION_ENTER/_RETURN pair
210 * and will never be reclaimed. */
211 g_warning ("Handle %p (object = %p) (allocated from \"%s\") is leaking.\n", e, e->o,
212 #ifdef MONO_HANDLE_TRACK_OWNER
223 /* Actual handles implementation */
225 #ifndef MONO_HANDLE_TRACK_OWNER
226 mono_handle_new (MonoObject *obj)
228 mono_handle_new (MonoObject *obj, const char *owner)
231 MonoThreadInfo *info = mono_thread_info_current ();
232 HandleStack *handles = (HandleStack *)info->handle_stack;
233 HandleChunk *top = handles->top;
234 #ifdef MONO_HANDLE_TRACK_SP
235 mono_handle_chunk_leak_check (handles);
239 if (G_LIKELY (top->size < OBJECTS_PER_HANDLES_CHUNK)) {
241 gpointer* objslot = &top->elems [idx].o;
242 /* can be interrupted anywhere here, so:
243 * 1. make sure the new slot is null
244 * 2. make the new slot scannable (increment size)
245 * 3. put a valid object in there
247 * (have to do 1 then 3 so that if we're interrupted
248 * between 1 and 2, the object is still live)
251 mono_memory_write_barrier ();
253 mono_memory_write_barrier ();
256 SET_SP (handles, top, idx);
259 if (G_LIKELY (top->next)) {
261 /* make sure size == 0 is visible to a GC thread before it sees the new top */
262 mono_memory_write_barrier ();
267 HandleChunk *new_chunk = new_handle_chunk ();
269 new_chunk->prev = top;
270 new_chunk->next = NULL;
271 /* make sure size == 0 before new chunk is visible */
272 mono_memory_write_barrier ();
273 top->next = new_chunk;
274 handles->top = new_chunk;
279 #ifndef MONO_HANDLE_TRACK_OWNER
280 mono_handle_new_interior (gpointer rawptr)
282 mono_handle_new_interior (gpointer rawptr, const char *owner)
285 MonoThreadInfo *info = mono_thread_info_current ();
286 HandleStack *handles = (HandleStack *)info->handle_stack;
287 HandleChunk *top = handles->interior;
288 #ifdef MONO_HANDLE_TRACK_SP
289 mono_handle_chunk_leak_check (handles);
295 * Don't extend the chunk now, interior handles are
296 * only used for icall arguments, they shouldn't
299 g_assert (top->size < OBJECTS_PER_HANDLES_CHUNK);
301 gpointer *objslot = &top->elems [idx].o;
303 mono_memory_write_barrier ();
305 mono_memory_write_barrier ();
308 SET_SP (handles, top, idx);
313 mono_handle_stack_alloc (void)
315 HandleStack *stack = new_handle_stack ();
316 HandleChunk *chunk = new_handle_chunk ();
317 HandleChunk *interior = new_handle_chunk ();
319 chunk->prev = chunk->next = NULL;
321 interior->prev = interior->next = NULL;
323 mono_memory_write_barrier ();
324 stack->top = stack->bottom = chunk;
325 stack->interior = interior;
326 #ifdef MONO_HANDLE_TRACK_SP
327 stack->stackmark_sp = NULL;
333 mono_handle_stack_free (HandleStack *stack)
337 HandleChunk *c = stack->bottom;
338 stack->top = stack->bottom = NULL;
339 mono_memory_write_barrier ();
341 HandleChunk *next = c->next;
342 free_handle_chunk (c);
345 free_handle_chunk (c);
346 free_handle_chunk (stack->interior);
347 free_handle_stack (stack);
351 mono_handle_stack_free_domain (HandleStack *stack, MonoDomain *domain)
353 /* Called by the GC while clearing out objects of the given domain from the heap. */
354 /* If there are no handles-related bugs, there is nothing to do: if a
355 * thread accessed objects from the domain it was aborted, so any
356 * threads left alive cannot have any handles that point into the
357 * unloading domain. However if there is a handle leak, the handle stack is not */
360 /* Root domain only unloaded when mono is shutting down, don't need to check anything */
361 if (domain == mono_get_root_domain () || mono_runtime_is_shutting_down ())
363 HandleChunk *cur = stack->bottom;
364 HandleChunk *last = stack->top;
368 for (int idx = 0; idx < cur->size; ++idx) {
369 HandleChunkElem *elem = &cur->elems[idx];
372 g_assert (mono_object_domain (elem->o) != domain);
378 /* We don't examine the interior pointers here because the GC treats
379 * them conservatively and anyway we don't have enough information here to
380 * find the object's vtable.
385 check_handle_stack_monotonic (HandleStack *stack)
387 /* check that every allocated handle in the current handle stack is at no higher in the native stack than its predecessors */
388 #ifdef MONO_HANDLE_TRACK_SP
389 HandleChunk *cur = stack->bottom;
390 HandleChunk *last = stack->top;
393 HandleChunkElem *prev = NULL;
394 gboolean monotonic = TRUE;
396 for (int i = 0;i < cur->size; ++i) {
397 HandleChunkElem *elem = chunk_element (cur, i);
398 if (prev && elem->alloc_sp < prev->alloc_sp) {
400 g_warning ("Handle %p (object %p) (allocated from \"%s\") is was allocated deeper in the call stack than its successor (allocated from \"%s\").", prev, prev->o,
401 #ifdef MONO_HANDLE_TRACK_OWNER
417 g_assert (monotonic);
422 mono_handle_stack_scan (HandleStack *stack, GcScanFunc func, gpointer gc_data, gboolean precise)
424 if (precise) /* run just once (per handle stack) per GC */
425 check_handle_stack_monotonic (stack);
427 We're called twice - on the imprecise pass we call func to pin the
428 objects where the handle points to its interior. On the precise
429 pass, we scan all the objects where the handles point to the start of
432 Note that if we're running, we know the world is stopped.
435 HandleChunk *cur = stack->bottom;
436 HandleChunk *last = stack->top;
439 for (int i = 0; i < cur->size; ++i) {
440 HandleChunkElem* elem = chunk_element (cur, i);
441 gpointer* obj_slot = &elem->o;
442 if (*obj_slot != NULL)
443 func (obj_slot, gc_data);
450 HandleChunk *cur = stack->interior;
454 for (int i = 0; i < cur->size; ++i) {
455 HandleChunkElem* elem = chunk_element (cur, i);
456 gpointer* ptr_slot = &elem->o;
457 if (*ptr_slot != NULL)
458 func (ptr_slot, gc_data);
464 mono_stack_mark_record_size (MonoThreadInfo *info, HandleStackMark *stackmark, const char *func_name)
466 HandleStack *handles = (HandleStack *)info->handle_stack;
467 HandleChunk *cur = stackmark->chunk;
468 int size = -stackmark->size; //discard the starting point of the stack
471 if (cur == handles->top)
476 if (size > THIS_IS_AN_OK_NUMBER_OF_HANDLES)
477 g_warning ("%s USED %d handles\n", func_name, size);
481 * Pop the stack until @stackmark and make @value the top value.
483 * @return the new handle for what @value points to
486 mono_stack_mark_pop_value (MonoThreadInfo *info, HandleStackMark *stackmark, MonoRawHandle value)
488 MonoObject *obj = value ? *((MonoObject**)value) : NULL;
489 mono_stack_mark_pop (info, stackmark);
490 #ifndef MONO_HANDLE_TRACK_OWNER
491 return mono_handle_new (obj);
493 return mono_handle_new (obj, "<mono_stack_mark_pop_value>");
497 /* Temporary place for some of the handle enabled wrapper functions*/
500 mono_string_new_handle (MonoDomain *domain, const char *data, MonoError *error)
502 return MONO_HANDLE_NEW (MonoString, mono_string_new_checked (domain, data, error));
506 mono_array_new_handle (MonoDomain *domain, MonoClass *eclass, uintptr_t n, MonoError *error)
508 return MONO_HANDLE_NEW (MonoArray, mono_array_new_checked (domain, eclass, n, error));
512 mono_array_new_full_handle (MonoDomain *domain, MonoClass *array_class, uintptr_t *lengths, intptr_t *lower_bounds, MonoError *error)
514 return MONO_HANDLE_NEW (MonoArray, mono_array_new_full_checked (domain, array_class, lengths, lower_bounds, error));
517 #ifdef ENABLE_CHECKED_BUILD
518 /* Checked build helpers */
520 mono_handle_verify (MonoRawHandle raw_handle)
527 mono_array_handle_length (MonoArrayHandle arr)
529 MONO_REQ_GC_UNSAFE_MODE;
531 return MONO_HANDLE_RAW (arr)->max_length;
535 mono_gchandle_from_handle (MonoObjectHandle handle, mono_bool pinned)
537 /* FIXME: chunk_element_to_chunk_idx does a linear search through the
538 * chunks and we only need it for the assert */
539 MonoThreadInfo *info = mono_thread_info_current ();
540 HandleStack *stack = (HandleStack*) info->handle_stack;
541 HandleChunkElem* elem = handle_to_chunk_element (handle);
543 HandleChunk *chunk = chunk_element_to_chunk_idx (stack, elem, &elem_idx);
544 /* gchandles cannot deal with interior pointers */
545 g_assert (chunk != NULL);
546 return mono_gchandle_new (MONO_HANDLE_RAW (handle), pinned);
550 mono_gchandle_get_target_handle (uint32_t gchandle)
552 return MONO_HANDLE_NEW (MonoObject, mono_gchandle_get_target (gchandle));
556 mono_array_handle_pin_with_size (MonoArrayHandle handle, int size, uintptr_t idx, uint32_t *gchandle)
558 g_assert (gchandle != NULL);
559 *gchandle = mono_gchandle_from_handle (MONO_HANDLE_CAST(MonoObject,handle), TRUE);
560 MonoArray *raw = MONO_HANDLE_RAW (handle);
561 return mono_array_addr_with_size (raw, size, idx);
565 mono_string_handle_pin_chars (MonoStringHandle handle, uint32_t *gchandle)
567 g_assert (gchandle != NULL);
568 *gchandle = mono_gchandle_from_handle (MONO_HANDLE_CAST (MonoObject, handle), TRUE);
569 MonoString *raw = MONO_HANDLE_RAW (handle);
570 return mono_string_chars (raw);
574 mono_object_handle_pin_unbox (MonoObjectHandle obj, uint32_t *gchandle)
576 g_assert (!MONO_HANDLE_IS_NULL (obj));
577 MonoClass *klass = mono_handle_class (obj);
578 g_assert (klass->valuetype);
579 *gchandle = mono_gchandle_from_handle (obj, TRUE);
580 return mono_object_unbox (MONO_HANDLE_RAW (obj));
584 mono_array_handle_memcpy_refs (MonoArrayHandle dest, uintptr_t dest_idx, MonoArrayHandle src, uintptr_t src_idx, uintptr_t len)
586 mono_array_memcpy_refs (MONO_HANDLE_RAW (dest), dest_idx, MONO_HANDLE_RAW (src), src_idx, len);
590 mono_handle_stack_is_empty (HandleStack *stack)
592 return (stack->top == stack->bottom && stack->top->size == 0);