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 /* TODO (missing pieces)
28 mix/max/avg size of stack marks
31 Actually do something in mono_handle_verify
33 Shrink the handles stack in mono_handle_stack_scan
34 Properly report it to the profiler.
35 Add a boehm implementation
37 TODO (things to explore):
39 There's no convenient way to wrap the object allocation function.
41 MonoCultureInfoHandle culture = MONO_HANDLE_NEW (MonoCultureInfo, mono_object_new_checked (domain, klass, &error));
43 Maybe what we need is a round of cleanup around all exposed types in the runtime to unify all helpers under the same hoof.
44 Combine: MonoDefaults, GENERATE_GET_CLASS_WITH_CACHE, TYPED_HANDLE_DECL and friends.
45 This would solve the age old issue of making it clear which types are optional and tell that to the linker.
46 We could then generate neat type safe wrappers.
52 * If we are running with cooperative GC, all the handle stack
53 * manipulation will complete before a GC thread scans the handle
54 * stack. If we are using async suspend, however, a thread may be
55 * trying to allocate a new handle, or unwind the handle stack when
56 * the GC stops the world.
58 * In particular, we need to ensure that if the mutator thread is
59 * suspended while manipulating the handle stack, the stack is in a
60 * good enough state to be scanned. In particular, the size of each
61 * chunk should be updated before an object is written into the
62 * handle, and chunks to be scanned (between bottom and top) should
65 * Note that the handle stack is scanned PRECISELY (see
66 * sgen_client_scan_thread_data ()). That means there should not be
67 * stale objects scanned. So when we manipulate the size of a chunk,
68 * wemust ensure that the newly scannable slot is either null or
69 * points to a valid value.
72 #if defined(HAVE_BOEHM_GC) || defined(HAVE_NULL_GC)
76 return (HandleStack *)mono_gc_alloc_fixed (sizeof (HandleStack), MONO_GC_DESCRIPTOR_NULL, MONO_ROOT_SOURCE_HANDLE, "Thread Handle Stack");
80 free_handle_stack (HandleStack *stack)
82 mono_gc_free_fixed (stack);
88 #if defined(HAVE_BOEHM_GC)
89 return (HandleChunk *)GC_MALLOC (sizeof (HandleChunk));
90 #elif defined(HAVE_NULL_GC)
91 return (HandleChunk *)g_malloc (sizeof (HandleChunk));
96 free_handle_chunk (HandleChunk *chunk)
98 #if defined(HAVE_NULL_GC)
106 return g_new (HandleStack, 1);
110 free_handle_stack (HandleStack *stack)
118 return g_new (HandleChunk, 1);
122 free_handle_chunk (HandleChunk *chunk)
128 const MonoObjectHandle mono_null_value_handle = NULL;
130 #define THIS_IS_AN_OK_NUMBER_OF_HANDLES 100
132 static HandleChunkElem*
133 chunk_element (HandleChunk *chunk, int idx)
135 return &chunk->elems[idx];
138 static HandleChunkElem*
139 handle_to_chunk_element (MonoObjectHandle o)
141 return (HandleChunkElem*)o;
144 /* Given a HandleChunkElem* search through the current handle stack to find its chunk and offset. */
146 chunk_element_to_chunk_idx (HandleStack *stack, HandleChunkElem *elem, int *out_idx)
148 HandleChunk *top = stack->top;
149 HandleChunk *cur = stack->bottom;
153 while (cur != NULL) {
154 HandleChunkElem *front = &cur->elems [0];
155 HandleChunkElem *back = &cur->elems [cur->size];
157 if (front <= elem && elem < back) {
158 *out_idx = (int)(elem - front);
163 break; /* didn't find it. */
169 #ifdef MONO_HANDLE_TRACK_OWNER
170 #define SET_OWNER(chunk,idx) do { (chunk)->elems[(idx)].owner = owner; } while (0)
172 #define SET_OWNER(chunk,idx) do { } while (0)
175 #ifdef MONO_HANDLE_TRACK_SP
176 #define SET_SP(handles,chunk,idx) do { (chunk)->elems[(idx)].alloc_sp = handles->stackmark_sp; } while (0)
178 #define SET_SP(handles,chunk,idx) do { } while (0)
181 #ifdef MONO_HANDLE_TRACK_SP
183 mono_handle_chunk_leak_check (HandleStack *handles) {
184 if (handles->stackmark_sp) {
185 /* walk back from the top to the topmost non-empty chunk */
186 HandleChunk *c = handles->top;
187 while (c && c->size <= 0 && c != handles->bottom) {
190 if (c == NULL || c->size == 0)
192 g_assert (c && c->size > 0);
193 HandleChunkElem *e = chunk_element (c, c->size - 1);
194 if (e->alloc_sp < handles->stackmark_sp) {
195 /* If we get here, the topmost object on the handle stack was
196 * allocated from a function that is deeper in the call stack than
197 * the most recent HANDLE_FUNCTION_ENTER. That means it was
198 * probably not wrapped in a HANDLE_FUNCTION_ENTER/_RETURN pair
199 * and will never be reclaimed. */
200 g_warning ("Handle %p (object = %p) (allocated from \"%s\") is leaking.\n", e, e->o,
201 #ifdef MONO_HANDLE_TRACK_OWNER
212 /* Actual handles implementation */
214 #ifndef MONO_HANDLE_TRACK_OWNER
215 mono_handle_new (MonoObject *obj)
217 mono_handle_new (MonoObject *obj, const char *owner)
220 MonoThreadInfo *info = mono_thread_info_current ();
221 HandleStack *handles = (HandleStack *)info->handle_stack;
222 HandleChunk *top = handles->top;
223 #ifdef MONO_HANDLE_TRACK_SP
224 mono_handle_chunk_leak_check (handles);
228 if (G_LIKELY (top->size < OBJECTS_PER_HANDLES_CHUNK)) {
230 gpointer* objslot = &top->elems [idx].o;
231 /* can be interrupted anywhere here, so:
232 * 1. make sure the new slot is null
233 * 2. make the new slot scannable (increment size)
234 * 3. put a valid object in there
236 * (have to do 1 then 3 so that if we're interrupted
237 * between 1 and 2, the object is still live)
240 mono_memory_write_barrier ();
242 mono_memory_write_barrier ();
245 SET_SP (handles, top, idx);
248 if (G_LIKELY (top->next)) {
250 /* make sure size == 0 is visible to a GC thread before it sees the new top */
251 mono_memory_write_barrier ();
256 HandleChunk *new_chunk = new_handle_chunk ();
258 new_chunk->prev = top;
259 new_chunk->next = NULL;
260 /* make sure size == 0 before new chunk is visible */
261 mono_memory_write_barrier ();
262 top->next = new_chunk;
263 handles->top = new_chunk;
268 #ifndef MONO_HANDLE_TRACK_OWNER
269 mono_handle_new_interior (gpointer rawptr)
271 mono_handle_new_interior (gpointer rawptr, const char *owner)
274 MonoThreadInfo *info = mono_thread_info_current ();
275 HandleStack *handles = (HandleStack *)info->handle_stack;
276 HandleChunk *top = handles->interior;
277 #ifdef MONO_HANDLE_TRACK_SP
278 mono_handle_chunk_leak_check (handles);
284 * Don't extend the chunk now, interior handles are
285 * only used for icall arguments, they shouldn't
288 g_assert (top->size < OBJECTS_PER_HANDLES_CHUNK);
290 gpointer *objslot = &top->elems [idx].o;
292 mono_memory_write_barrier ();
294 mono_memory_write_barrier ();
297 SET_SP (handles, top, idx);
302 mono_handle_stack_alloc (void)
304 HandleStack *stack = new_handle_stack ();
305 HandleChunk *chunk = new_handle_chunk ();
306 HandleChunk *interior = new_handle_chunk ();
308 chunk->prev = chunk->next = NULL;
310 interior->prev = interior->next = NULL;
312 mono_memory_write_barrier ();
313 stack->top = stack->bottom = chunk;
314 stack->interior = interior;
315 #ifdef MONO_HANDLE_TRACK_SP
316 stack->stackmark_sp = NULL;
322 mono_handle_stack_free (HandleStack *stack)
326 HandleChunk *c = stack->bottom;
327 stack->top = stack->bottom = NULL;
328 mono_memory_write_barrier ();
330 HandleChunk *next = c->next;
331 free_handle_chunk (c);
334 free_handle_chunk (c);
335 free_handle_chunk (stack->interior);
336 free_handle_stack (stack);
340 mono_handle_stack_free_domain (HandleStack *stack, MonoDomain *domain)
342 /* Called by the GC while clearing out objects of the given domain from the heap. */
343 /* If there are no handles-related bugs, there is nothing to do: if a
344 * thread accessed objects from the domain it was aborted, so any
345 * threads left alive cannot have any handles that point into the
346 * unloading domain. However if there is a handle leak, the handle stack is not */
349 /* Root domain only unloaded when mono is shutting down, don't need to check anything */
350 if (domain == mono_get_root_domain () || mono_runtime_is_shutting_down ())
352 HandleChunk *cur = stack->bottom;
353 HandleChunk *last = stack->top;
357 for (int idx = 0; idx < cur->size; ++idx) {
358 HandleChunkElem *elem = &cur->elems[idx];
361 g_assert (mono_object_domain (elem->o) != domain);
367 /* We don't examine the interior pointers here because the GC treats
368 * them conservatively and anyway we don't have enough information here to
369 * find the object's vtable.
374 check_handle_stack_monotonic (HandleStack *stack)
376 /* check that every allocated handle in the current handle stack is at no higher in the native stack than its predecessors */
377 #ifdef MONO_HANDLE_TRACK_SP
378 HandleChunk *cur = stack->bottom;
379 HandleChunk *last = stack->top;
382 HandleChunkElem *prev = NULL;
383 gboolean monotonic = TRUE;
385 for (int i = 0;i < cur->size; ++i) {
386 HandleChunkElem *elem = chunk_element (cur, i);
387 if (prev && elem->alloc_sp < prev->alloc_sp) {
389 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,
390 #ifdef MONO_HANDLE_TRACK_OWNER
406 g_assert (monotonic);
411 mono_handle_stack_scan (HandleStack *stack, GcScanFunc func, gpointer gc_data, gboolean precise)
413 if (precise) /* run just once (per handle stack) per GC */
414 check_handle_stack_monotonic (stack);
416 We're called twice - on the imprecise pass we call func to pin the
417 objects where the handle points to its interior. On the precise
418 pass, we scan all the objects where the handles point to the start of
421 Note that if we're running, we know the world is stopped.
424 HandleChunk *cur = stack->bottom;
425 HandleChunk *last = stack->top;
428 for (int i = 0; i < cur->size; ++i) {
429 HandleChunkElem* elem = chunk_element (cur, i);
430 gpointer* obj_slot = &elem->o;
431 if (*obj_slot != NULL)
432 func (obj_slot, gc_data);
439 HandleChunk *cur = stack->interior;
443 for (int i = 0; i < cur->size; ++i) {
444 HandleChunkElem* elem = chunk_element (cur, i);
445 gpointer* ptr_slot = &elem->o;
446 if (*ptr_slot != NULL)
447 func (ptr_slot, gc_data);
453 mono_stack_mark_record_size (MonoThreadInfo *info, HandleStackMark *stackmark, const char *func_name)
455 HandleStack *handles = (HandleStack *)info->handle_stack;
456 HandleChunk *cur = stackmark->chunk;
457 int size = -stackmark->size; //discard the starting point of the stack
460 if (cur == handles->top)
465 if (size > THIS_IS_AN_OK_NUMBER_OF_HANDLES)
466 g_warning ("%s USED %d handles\n", func_name, size);
470 * Pop the stack until @stackmark and make @value the top value.
472 * @return the new handle for what @value points to
475 mono_stack_mark_pop_value (MonoThreadInfo *info, HandleStackMark *stackmark, MonoRawHandle value)
477 MonoObject *obj = value ? *((MonoObject**)value) : NULL;
478 mono_stack_mark_pop (info, stackmark);
479 #ifndef MONO_HANDLE_TRACK_OWNER
480 return mono_handle_new (obj);
482 return mono_handle_new (obj, "<mono_stack_mark_pop_value>");
486 /* Temporary place for some of the handle enabled wrapper functions*/
489 mono_string_new_handle (MonoDomain *domain, const char *data, MonoError *error)
491 return MONO_HANDLE_NEW (MonoString, mono_string_new_checked (domain, data, error));
495 mono_array_new_handle (MonoDomain *domain, MonoClass *eclass, uintptr_t n, MonoError *error)
497 return MONO_HANDLE_NEW (MonoArray, mono_array_new_checked (domain, eclass, n, error));
501 mono_array_new_full_handle (MonoDomain *domain, MonoClass *array_class, uintptr_t *lengths, intptr_t *lower_bounds, MonoError *error)
503 return MONO_HANDLE_NEW (MonoArray, mono_array_new_full_checked (domain, array_class, lengths, lower_bounds, error));
506 #ifdef ENABLE_CHECKED_BUILD
507 /* Checked build helpers */
509 mono_handle_verify (MonoRawHandle raw_handle)
516 mono_array_handle_length (MonoArrayHandle arr)
518 MONO_REQ_GC_UNSAFE_MODE;
520 return MONO_HANDLE_RAW (arr)->max_length;
524 mono_gchandle_from_handle (MonoObjectHandle handle, mono_bool pinned)
526 /* FIXME: chunk_element_to_chunk_idx does a linear search through the
527 * chunks and we only need it for the assert */
528 MonoThreadInfo *info = mono_thread_info_current ();
529 HandleStack *stack = (HandleStack*) info->handle_stack;
530 HandleChunkElem* elem = handle_to_chunk_element (handle);
532 HandleChunk *chunk = chunk_element_to_chunk_idx (stack, elem, &elem_idx);
533 /* gchandles cannot deal with interior pointers */
534 g_assert (chunk != NULL);
535 return mono_gchandle_new (MONO_HANDLE_RAW (handle), pinned);
539 mono_gchandle_get_target_handle (uint32_t gchandle)
541 return MONO_HANDLE_NEW (MonoObject, mono_gchandle_get_target (gchandle));
545 mono_array_handle_pin_with_size (MonoArrayHandle handle, int size, uintptr_t idx, uint32_t *gchandle)
547 g_assert (gchandle != NULL);
548 *gchandle = mono_gchandle_from_handle (MONO_HANDLE_CAST(MonoObject,handle), TRUE);
549 MonoArray *raw = MONO_HANDLE_RAW (handle);
550 return mono_array_addr_with_size (raw, size, idx);
554 mono_string_handle_pin_chars (MonoStringHandle handle, uint32_t *gchandle)
556 g_assert (gchandle != NULL);
557 *gchandle = mono_gchandle_from_handle (MONO_HANDLE_CAST (MonoObject, handle), TRUE);
558 MonoString *raw = MONO_HANDLE_RAW (handle);
559 return mono_string_chars (raw);
563 mono_object_handle_pin_unbox (MonoObjectHandle obj, uint32_t *gchandle)
565 g_assert (!MONO_HANDLE_IS_NULL (obj));
566 MonoClass *klass = mono_handle_class (obj);
567 g_assert (klass->valuetype);
568 *gchandle = mono_gchandle_from_handle (obj, TRUE);
569 return mono_object_unbox (MONO_HANDLE_RAW (obj));
573 mono_array_handle_memcpy_refs (MonoArrayHandle dest, uintptr_t dest_idx, MonoArrayHandle src, uintptr_t src_idx, uintptr_t len)
575 mono_array_memcpy_refs (MONO_HANDLE_RAW (dest), dest_idx, MONO_HANDLE_RAW (src), src_idx, len);
579 mono_handle_stack_is_empty (HandleStack *stack)
581 return (stack->top == stack->bottom && stack->top->size == 0);