Apply changes from https://github.com/dotnet/runtime/commit/eb1756e97d23df13bc6fe798e...
[mono-project.git] / mono / metadata / handle.c
blobd552b3fd27b7dcbb05e656d68366292f05f7eab9
1 /**
2 * \file
3 * Handle to object in native code
5 * Authors:
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.
14 #include <config.h>
15 #include <glib.h>
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
24 #include <execinfo.h>
25 #endif
27 /* TODO (missing pieces)
29 Add counters for:
30 number of stack marks
31 stack marks per icall
32 mix/max/avg size of stack marks
33 handle stack wastage
35 Shrink the handles stack in mono_handle_stack_scan
36 Add a boehm implementation
38 TODO (things to explore):
40 There's no convenient way to wrap the object allocation function.
41 Right now we do this:
42 MonoCultureInfoHandle culture = MONO_HANDLE_NEW (MonoCultureInfo, mono_object_new_checked (domain, klass, error));
44 Maybe what we need is a round of cleanup around all exposed types in the runtime to unify all helpers under the same hoof.
45 Combine: MonoDefaults, GENERATE_GET_CLASS_WITH_CACHE, TYPED_HANDLE_DECL and friends.
46 This would solve the age old issue of making it clear which types are optional and tell that to the linker.
47 We could then generate neat type safe wrappers.
51 * NOTE: Async suspend
53 * If we are running with cooperative GC, all the handle stack
54 * manipulation will complete before a GC thread scans the handle
55 * stack. If we are using async suspend, however, a thread may be
56 * trying to allocate a new handle, or unwind the handle stack when
57 * the GC stops the world.
59 * In particular, we need to ensure that if the mutator thread is
60 * suspended while manipulating the handle stack, the stack is in a
61 * good enough state to be scanned. In particular, the size of each
62 * chunk should be updated before an object is written into the
63 * handle, and chunks to be scanned (between bottom and top) should
64 * always be valid.
67 static HandleStack*
68 new_handle_stack (void)
70 return g_new (HandleStack, 1);
73 static void
74 free_handle_stack (HandleStack *stack)
76 g_free (stack);
79 static HandleChunk*
80 new_handle_chunk (void)
82 return g_new (HandleChunk, 1);
85 static void
86 free_handle_chunk (HandleChunk *chunk)
88 g_free (chunk);
91 #define THIS_IS_AN_OK_NUMBER_OF_HANDLES 100
93 static HandleChunkElem*
94 chunk_element (HandleChunk *chunk, int idx)
96 return &chunk->elems[idx];
100 #ifdef MONO_HANDLE_TRACK_OWNER
101 #ifdef HAVE_BACKTRACE_SYMBOLS
102 #define SET_BACKTRACE(btaddrs) do { \
103 backtrace(btaddrs, 7); \
104 } while (0)
105 #else
106 #define SET_BACKTRACE(btaddrs) 0
107 #endif
108 #define SET_OWNER(chunk,idx) do { (chunk)->elems[(idx)].owner = owner; SET_BACKTRACE (&((chunk)->elems[(idx)].backtrace_ips[0])); } while (0)
109 #else
110 #define SET_OWNER(chunk,idx) do { } while (0)
111 #endif
113 #ifdef MONO_HANDLE_TRACK_SP
114 #define SET_SP(handles,chunk,idx) do { (chunk)->elems[(idx)].alloc_sp = handles->stackmark_sp; } while (0)
115 #else
116 #define SET_SP(handles,chunk,idx) do { } while (0)
117 #endif
119 #ifdef MONO_HANDLE_TRACK_SP
120 void
121 mono_handle_chunk_leak_check (HandleStack *handles) {
122 if (handles->stackmark_sp) {
123 /* walk back from the top to the topmost non-empty chunk */
124 HandleChunk *c = handles->top;
125 while (c && c->size <= 0 && c != handles->bottom) {
126 c = c->prev;
128 if (c == NULL || c->size == 0)
129 return;
130 g_assert (c && c->size > 0);
131 HandleChunkElem *e = chunk_element (c, c->size - 1);
132 if (e->alloc_sp < handles->stackmark_sp) {
133 /* If we get here, the topmost object on the handle stack was
134 * allocated from a function that is deeper in the call stack than
135 * the most recent HANDLE_FUNCTION_ENTER. That means it was
136 * probably not wrapped in a HANDLE_FUNCTION_ENTER/_RETURN pair
137 * and will never be reclaimed. */
138 g_warning ("Handle %p (object = %p) (allocated from \"%s\") is leaking.\n", e, e->o,
139 #ifdef MONO_HANDLE_TRACK_OWNER
140 e->owner
141 #else
142 "<unknown owner>"
143 #endif
148 #endif
150 // There are deliberately locals and a constant NULL global with this same name.
151 #ifdef __cplusplus
152 extern MonoThreadInfo * const mono_thread_info_current_var = NULL;
153 #else
154 MonoThreadInfo * const mono_thread_info_current_var = NULL;
155 #endif
157 /* Actual handles implementation */
158 MonoRawHandle
159 #ifndef MONO_HANDLE_TRACK_OWNER
160 mono_handle_new (MonoObject *obj, MonoThreadInfo *info)
161 #else
162 mono_handle_new (MonoObject *obj, MonoThreadInfo *info, const char *owner)
163 #endif
165 info = info ? info : mono_thread_info_current ();
167 HandleStack *handles = info->handle_stack;
168 HandleChunk *top = handles->top;
169 #ifdef MONO_HANDLE_TRACK_SP
170 mono_handle_chunk_leak_check (handles);
171 #endif
173 // FIXME: Since we scan the handle stack inprecisely, some of the
174 // membars could be removed
176 retry:
177 if (G_LIKELY (top->size < OBJECTS_PER_HANDLES_CHUNK)) {
178 int idx = top->size;
179 gpointer* objslot = &top->elems [idx].o;
180 /* can be interrupted anywhere here, so:
181 * 1. make sure the new slot is null
182 * 2. make the new slot scannable (increment size)
183 * 3. put a valid object in there
185 * (have to do 1 then 3 so that if we're interrupted
186 * between 1 and 2, the object is still live)
188 *objslot = NULL;
189 SET_OWNER (top,idx);
190 SET_SP (handles, top, idx);
191 mono_memory_write_barrier ();
192 top->size++;
193 mono_memory_write_barrier ();
194 *objslot = obj;
195 return objslot;
197 if (G_LIKELY (top->next)) {
198 top->next->size = 0;
199 /* make sure size == 0 is visible to a GC thread before it sees the new top */
200 mono_memory_write_barrier ();
201 top = top->next;
202 handles->top = top;
203 goto retry;
205 HandleChunk *new_chunk = new_handle_chunk ();
206 new_chunk->size = 0;
207 new_chunk->prev = top;
208 new_chunk->next = NULL;
209 /* make sure size == 0 before new chunk is visible */
210 mono_memory_write_barrier ();
211 top->next = new_chunk;
212 handles->top = new_chunk;
213 goto retry;
216 HandleStack*
217 mono_handle_stack_alloc (void)
219 HandleStack *stack = new_handle_stack ();
220 HandleChunk *chunk = new_handle_chunk ();
222 chunk->prev = chunk->next = NULL;
223 chunk->size = 0;
224 mono_memory_write_barrier ();
225 stack->top = stack->bottom = chunk;
226 #ifdef MONO_HANDLE_TRACK_SP
227 stack->stackmark_sp = NULL;
228 #endif
229 return stack;
232 void
233 mono_handle_stack_free (HandleStack *stack)
235 if (!stack)
236 return;
237 HandleChunk *c = stack->bottom;
238 stack->top = stack->bottom = NULL;
239 mono_memory_write_barrier ();
240 while (c) {
241 HandleChunk *next = c->next;
242 free_handle_chunk (c);
243 c = next;
245 free_handle_chunk (c);
246 free_handle_stack (stack);
249 void
250 mono_handle_stack_free_domain (HandleStack *stack, MonoDomain *domain)
252 /* Called by the GC while clearing out objects of the given domain from the heap. */
253 /* If there are no handles-related bugs, there is nothing to do: if a
254 * thread accessed objects from the domain it was aborted, so any
255 * threads left alive cannot have any handles that point into the
256 * unloading domain. However if there is a handle leak, the handle stack is not */
257 if (!stack)
258 return;
259 /* Root domain only unloaded when mono is shutting down, don't need to check anything */
260 if (domain == mono_get_root_domain () || mono_runtime_is_shutting_down ())
261 return;
262 HandleChunk *cur = stack->bottom;
263 HandleChunk *last = stack->top;
264 if (!cur)
265 return;
266 while (cur) {
267 for (int idx = 0; idx < cur->size; ++idx) {
268 HandleChunkElem *elem = &cur->elems[idx];
269 if (!elem->o)
270 continue;
271 g_assert (mono_object_domain (elem->o) != domain);
273 if (cur == last)
274 break;
275 cur = cur->next;
279 static void
280 check_handle_stack_monotonic (HandleStack *stack)
282 /* check that every allocated handle in the current handle stack is at no higher in the native stack than its predecessors */
283 #ifdef MONO_HANDLE_TRACK_SP
284 HandleChunk *cur = stack->bottom;
285 HandleChunk *last = stack->top;
286 if (!cur)
287 return;
288 HandleChunkElem *prev = NULL;
289 gboolean monotonic = TRUE;
290 while (cur) {
291 for (int i = 0;i < cur->size; ++i) {
292 HandleChunkElem *elem = chunk_element (cur, i);
293 if (prev && elem->alloc_sp > prev->alloc_sp) {
294 monotonic = FALSE;
295 #ifdef MONO_HANDLE_TRACK_OWNER
296 g_warning ("Handle %p (object %p) (allocated from \"%s\") was allocated deeper in the call stack than its successor Handle %p (object %p) (allocated from \"%s\").", prev, prev->o, prev->owner, elem, elem->o, elem->owner);
297 #else
298 g_warning ("Handle %p (object %p) was allocated deeper in the call stack than its successor Handle %p (object %p).", prev, prev->o, elem, elem->o);
299 #endif
301 prev = elem;
303 if (cur == last)
304 break;
305 cur = cur->next;
307 g_assert (monotonic);
308 #endif
311 void
312 mono_handle_stack_scan (HandleStack *stack, GcScanFunc func, gpointer gc_data, gboolean precise, gboolean check)
314 if (check) /* run just once (per handle stack) per GC */
315 check_handle_stack_monotonic (stack);
318 * We're called twice, on the precise pass we do nothing.
319 * On the inprecise pass, we pin the objects pointed to by the handles.
320 * Note that if we're running, we know the world is stopped.
322 if (precise)
323 return;
325 HandleChunk *cur = stack->bottom;
326 HandleChunk *last = stack->top;
328 while (cur) {
329 for (int i = 0; i < cur->size; ++i) {
330 HandleChunkElem* elem = chunk_element (cur, i);
331 gpointer* obj_slot = &elem->o;
332 if (*obj_slot != NULL)
333 func (obj_slot, gc_data);
335 if (cur == last)
336 break;
337 cur = cur->next;
341 MonoThreadInfo*
342 mono_stack_mark_record_size (MonoThreadInfo *info, HandleStackMark *stackmark, const char *func_name)
344 info = info ? info : mono_thread_info_current ();
346 HandleStack *handles = info->handle_stack;
347 HandleChunk *cur = stackmark->chunk;
348 int size = -stackmark->size; //discard the starting point of the stack
349 while (cur) {
350 size += cur->size;
351 if (cur == handles->top)
352 break;
353 cur = cur->next;
356 if (size > THIS_IS_AN_OK_NUMBER_OF_HANDLES)
357 g_warning ("%s USED %d handles\n", func_name, size);
359 return info;
363 * Pop the stack until @stackmark and make @value the top value.
365 * @return the new handle for what @value points to
367 MonoRawHandle
368 mono_stack_mark_pop_value (MonoThreadInfo *info, HandleStackMark *stackmark, MonoRawHandle value)
370 MonoObject *obj = value ? *((MonoObject**)value) : NULL;
371 mono_stack_mark_pop (info, stackmark);
372 #ifndef MONO_HANDLE_TRACK_OWNER
373 return mono_handle_new (obj, info);
374 #else
375 return mono_handle_new (obj, info, "<mono_stack_mark_pop_value>");
376 #endif
379 /* Temporary place for some of the handle enabled wrapper functions*/
381 MonoStringHandle
382 mono_string_new_handle (MonoDomain *domain, const char *data, MonoError *error)
384 return MONO_HANDLE_NEW (MonoString, mono_string_new_checked (domain, data, error));
387 MonoArrayHandle
388 mono_array_new_handle (MonoDomain *domain, MonoClass *eclass, uintptr_t n, MonoError *error)
390 return MONO_HANDLE_NEW (MonoArray, mono_array_new_checked (domain, eclass, n, error));
393 MonoArrayHandle
394 mono_array_new_full_handle (MonoDomain *domain, MonoClass *array_class, uintptr_t *lengths, intptr_t *lower_bounds, MonoError *error)
396 return MONO_HANDLE_NEW (MonoArray, mono_array_new_full_checked (domain, array_class, lengths, lower_bounds, error));
399 MonoGCHandle
400 mono_gchandle_from_handle (MonoObjectHandle handle, mono_bool pinned)
402 // FIXME This used to check for out of scope handles.
403 // Stack-based handles coming from icall wrappers do not
404 // work with that. This functionality can be largely restored
405 // by introducing a tag bit in handles -- 0 for managed stack-based,
406 // 1 for native TLS-based, having MONO_HANDLE_RAW clear it, and only
407 // doing the former checking for native TLS-based handles.
408 return mono_gchandle_new_internal (MONO_HANDLE_RAW (handle), pinned);
411 MonoObjectHandle
412 mono_gchandle_get_target_handle (MonoGCHandle gchandle)
414 return MONO_HANDLE_NEW (MonoObject, mono_gchandle_get_target_internal (gchandle));
417 gpointer
418 mono_array_handle_pin_with_size (MonoArrayHandle handle, int size, uintptr_t idx, MonoGCHandle *gchandle)
420 g_assert (gchandle != NULL);
421 *gchandle = mono_gchandle_from_handle (MONO_HANDLE_CAST(MonoObject,handle), TRUE);
422 MonoArray *raw = MONO_HANDLE_RAW (handle);
423 return mono_array_addr_with_size_internal (raw, size, idx);
426 gunichar2*
427 mono_string_handle_pin_chars (MonoStringHandle handle, MonoGCHandle *gchandle)
429 g_assert (gchandle != NULL);
430 *gchandle = mono_gchandle_from_handle (MONO_HANDLE_CAST (MonoObject, handle), TRUE);
431 MonoString *raw = MONO_HANDLE_RAW (handle);
432 return mono_string_chars_internal (raw);
435 gpointer
436 mono_object_handle_pin_unbox (MonoObjectHandle obj, MonoGCHandle *gchandle)
438 g_assert (!MONO_HANDLE_IS_NULL (obj));
439 MonoClass *klass = mono_handle_class (obj);
440 g_assert (m_class_is_valuetype (klass));
441 *gchandle = mono_gchandle_from_handle (obj, TRUE);
442 return mono_object_unbox_internal (MONO_HANDLE_RAW (obj));
445 //FIXME inline
446 void
447 mono_array_handle_memcpy_refs (MonoArrayHandle dest, uintptr_t dest_idx, MonoArrayHandle src, uintptr_t src_idx, uintptr_t len)
449 mono_array_memcpy_refs_internal (MONO_HANDLE_RAW (dest), dest_idx, MONO_HANDLE_RAW (src), src_idx, len);
452 gboolean
453 mono_handle_stack_is_empty (HandleStack *stack)
455 return stack->top == stack->bottom && stack->top->size == 0;
458 //FIXME inline
459 gboolean
460 mono_gchandle_target_equal (MonoGCHandle gchandle, MonoObjectHandle equal)
462 // This function serves to reduce coop handle creation.
463 MONO_HANDLE_SUPPRESS_SCOPE (1);
464 return mono_gchandle_get_target_internal (gchandle) == MONO_HANDLE_RAW (equal);
467 //FIXME inline
468 void
469 mono_gchandle_target_is_null_or_equal (MonoGCHandle gchandle, MonoObjectHandle equal, gboolean *is_null,
470 gboolean *is_equal)
472 // This function serves to reduce coop handle creation.
473 MONO_HANDLE_SUPPRESS_SCOPE (1);
474 MonoObject *target = mono_gchandle_get_target_internal (gchandle);
475 *is_null = target == NULL;
476 *is_equal = target == MONO_HANDLE_RAW (equal);
479 //FIXME inline
480 void
481 mono_gchandle_set_target_handle (MonoGCHandle gchandle, MonoObjectHandle obj)
483 mono_gchandle_set_target (gchandle, MONO_HANDLE_RAW (obj));
486 //FIXME inline
487 MonoGCHandle
488 mono_gchandle_new_weakref_from_handle (MonoObjectHandle handle)
490 return mono_gchandle_new_weakref_internal (MONO_HANDLE_SUPPRESS (MONO_HANDLE_RAW (handle)), FALSE);
493 //FIXME inline
495 mono_handle_hash (MonoObjectHandle object)
497 return mono_object_hash_internal (MONO_HANDLE_SUPPRESS (MONO_HANDLE_RAW (object)));
500 //FIXME inline
501 MonoGCHandle
502 mono_gchandle_new_weakref_from_handle_track_resurrection (MonoObjectHandle handle)
504 return mono_gchandle_new_weakref_internal (MONO_HANDLE_SUPPRESS (MONO_HANDLE_RAW (handle)), TRUE);
507 //FIXME inline
508 void
509 mono_handle_array_getref (MonoObjectHandleOut dest, MonoArrayHandle array, uintptr_t index)
511 MONO_HANDLE_SUPPRESS (g_assert (dest.__raw));
512 MONO_HANDLE_SUPPRESS (*dest.__raw = (MonoObject*)mono_array_get_internal (MONO_HANDLE_RAW (array), gpointer, index));