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 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.
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.
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
68 new_handle_stack (void)
70 return g_new (HandleStack
, 1);
74 free_handle_stack (HandleStack
*stack
)
80 new_handle_chunk (void)
82 return g_new (HandleChunk
, 1);
86 free_handle_chunk (HandleChunk
*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); \
106 #define SET_BACKTRACE(btaddrs) 0
108 #define SET_OWNER(chunk,idx) do { (chunk)->elems[(idx)].owner = owner; SET_BACKTRACE (&((chunk)->elems[(idx)].backtrace_ips[0])); } while (0)
110 #define SET_OWNER(chunk,idx) do { } while (0)
113 #ifdef MONO_HANDLE_TRACK_SP
114 #define SET_SP(handles,chunk,idx) do { (chunk)->elems[(idx)].alloc_sp = handles->stackmark_sp; } while (0)
116 #define SET_SP(handles,chunk,idx) do { } while (0)
119 #ifdef MONO_HANDLE_TRACK_SP
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
) {
128 if (c
== NULL
|| c
->size
== 0)
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
150 // There are deliberately locals and a constant NULL global with this same name.
152 extern MonoThreadInfo
* const mono_thread_info_current_var
= NULL
;
154 MonoThreadInfo
* const mono_thread_info_current_var
= NULL
;
157 /* Actual handles implementation */
159 #ifndef MONO_HANDLE_TRACK_OWNER
160 mono_handle_new (MonoObject
*obj
, MonoThreadInfo
*info
)
162 mono_handle_new (MonoObject
*obj
, MonoThreadInfo
*info
, const char *owner
)
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
);
173 // FIXME: Since we scan the handle stack inprecisely, some of the
174 // membars could be removed
177 if (G_LIKELY (top
->size
< OBJECTS_PER_HANDLES_CHUNK
)) {
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)
190 SET_SP (handles
, top
, idx
);
191 mono_memory_write_barrier ();
193 mono_memory_write_barrier ();
197 if (G_LIKELY (top
->next
)) {
199 /* make sure size == 0 is visible to a GC thread before it sees the new top */
200 mono_memory_write_barrier ();
205 HandleChunk
*new_chunk
= new_handle_chunk ();
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
;
217 mono_handle_stack_alloc (void)
219 HandleStack
*stack
= new_handle_stack ();
220 HandleChunk
*chunk
= new_handle_chunk ();
222 chunk
->prev
= chunk
->next
= NULL
;
224 mono_memory_write_barrier ();
225 stack
->top
= stack
->bottom
= chunk
;
226 #ifdef MONO_HANDLE_TRACK_SP
227 stack
->stackmark_sp
= NULL
;
233 mono_handle_stack_free (HandleStack
*stack
)
237 HandleChunk
*c
= stack
->bottom
;
238 stack
->top
= stack
->bottom
= NULL
;
239 mono_memory_write_barrier ();
241 HandleChunk
*next
= c
->next
;
242 free_handle_chunk (c
);
245 free_handle_chunk (c
);
246 free_handle_stack (stack
);
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 */
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 ())
262 HandleChunk
*cur
= stack
->bottom
;
263 HandleChunk
*last
= stack
->top
;
267 for (int idx
= 0; idx
< cur
->size
; ++idx
) {
268 HandleChunkElem
*elem
= &cur
->elems
[idx
];
271 g_assert (mono_object_domain (elem
->o
) != domain
);
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
;
288 HandleChunkElem
*prev
= NULL
;
289 gboolean monotonic
= TRUE
;
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
) {
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
);
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
);
307 g_assert (monotonic
);
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.
325 HandleChunk
*cur
= stack
->bottom
;
326 HandleChunk
*last
= stack
->top
;
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
);
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
351 if (cur
== handles
->top
)
356 if (size
> THIS_IS_AN_OK_NUMBER_OF_HANDLES
)
357 g_warning ("%s USED %d handles\n", func_name
, size
);
363 * Pop the stack until @stackmark and make @value the top value.
365 * @return the new handle for what @value points to
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
);
375 return mono_handle_new (obj
, info
, "<mono_stack_mark_pop_value>");
379 /* Temporary place for some of the handle enabled wrapper functions*/
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
));
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
));
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
));
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
);
412 mono_gchandle_get_target_handle (MonoGCHandle gchandle
)
414 return MONO_HANDLE_NEW (MonoObject
, mono_gchandle_get_target_internal (gchandle
));
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
);
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
);
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
));
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
);
453 mono_handle_stack_is_empty (HandleStack
*stack
)
455 return stack
->top
== stack
->bottom
&& stack
->top
->size
== 0;
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
);
469 mono_gchandle_target_is_null_or_equal (MonoGCHandle gchandle
, MonoObjectHandle equal
, gboolean
*is_null
,
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
);
481 mono_gchandle_set_target_handle (MonoGCHandle gchandle
, MonoObjectHandle obj
)
483 mono_gchandle_set_target (gchandle
, MONO_HANDLE_RAW (obj
));
488 mono_gchandle_new_weakref_from_handle (MonoObjectHandle handle
)
490 return mono_gchandle_new_weakref_internal (MONO_HANDLE_SUPPRESS (MONO_HANDLE_RAW (handle
)), FALSE
);
495 mono_handle_hash (MonoObjectHandle object
)
497 return mono_object_hash_internal (MONO_HANDLE_SUPPRESS (MONO_HANDLE_RAW (object
)));
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
);
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
));