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
66 * Note that the handle stack is scanned PRECISELY (see
67 * sgen_client_scan_thread_data ()). That means there should not be
68 * stale objects scanned. So when we manipulate the size of a chunk,
69 * we must ensure that the newly scannable slot is either null or
70 * points to a valid value.
74 new_handle_stack (void)
76 return g_new (HandleStack
, 1);
80 free_handle_stack (HandleStack
*stack
)
86 new_handle_chunk (void)
88 return g_new (HandleChunk
, 1);
92 free_handle_chunk (HandleChunk
*chunk
)
97 const MonoObjectHandle mono_null_value_handle
= { 0 };
99 #define THIS_IS_AN_OK_NUMBER_OF_HANDLES 100
101 static HandleChunkElem
*
102 chunk_element (HandleChunk
*chunk
, int idx
)
104 return &chunk
->elems
[idx
];
108 #ifdef MONO_HANDLE_TRACK_OWNER
109 #ifdef HAVE_BACKTRACE_SYMBOLS
110 #define SET_BACKTRACE(btaddrs) do { \
111 backtrace(btaddrs, 7); \
114 #define SET_BACKTRACE(btaddrs) 0
116 #define SET_OWNER(chunk,idx) do { (chunk)->elems[(idx)].owner = owner; SET_BACKTRACE (&((chunk)->elems[(idx)].backtrace_ips[0])); } while (0)
118 #define SET_OWNER(chunk,idx) do { } while (0)
121 #ifdef MONO_HANDLE_TRACK_SP
122 #define SET_SP(handles,chunk,idx) do { (chunk)->elems[(idx)].alloc_sp = handles->stackmark_sp; } while (0)
124 #define SET_SP(handles,chunk,idx) do { } while (0)
127 #ifdef MONO_HANDLE_TRACK_SP
129 mono_handle_chunk_leak_check (HandleStack
*handles
) {
130 if (handles
->stackmark_sp
) {
131 /* walk back from the top to the topmost non-empty chunk */
132 HandleChunk
*c
= handles
->top
;
133 while (c
&& c
->size
<= 0 && c
!= handles
->bottom
) {
136 if (c
== NULL
|| c
->size
== 0)
138 g_assert (c
&& c
->size
> 0);
139 HandleChunkElem
*e
= chunk_element (c
, c
->size
- 1);
140 if (e
->alloc_sp
< handles
->stackmark_sp
) {
141 /* If we get here, the topmost object on the handle stack was
142 * allocated from a function that is deeper in the call stack than
143 * the most recent HANDLE_FUNCTION_ENTER. That means it was
144 * probably not wrapped in a HANDLE_FUNCTION_ENTER/_RETURN pair
145 * and will never be reclaimed. */
146 g_warning ("Handle %p (object = %p) (allocated from \"%s\") is leaking.\n", e
, e
->o
,
147 #ifdef MONO_HANDLE_TRACK_OWNER
158 // There are deliberately locals and a constant NULL global with this same name.
160 extern MonoThreadInfo
* const mono_thread_info_current_var
= NULL
;
162 MonoThreadInfo
* const mono_thread_info_current_var
= NULL
;
165 /* Actual handles implementation */
167 #ifndef MONO_HANDLE_TRACK_OWNER
168 mono_handle_new (MonoObject
*obj
, MonoThreadInfo
*info
)
170 mono_handle_new (MonoObject
*obj
, MonoThreadInfo
*info
, const char *owner
)
173 info
= info
? info
: mono_thread_info_current ();
174 HandleStack
*handles
= info
->handle_stack
;
175 HandleChunk
*top
= handles
->top
;
176 #ifdef MONO_HANDLE_TRACK_SP
177 mono_handle_chunk_leak_check (handles
);
181 if (G_LIKELY (top
->size
< OBJECTS_PER_HANDLES_CHUNK
)) {
183 gpointer
* objslot
= &top
->elems
[idx
].o
;
184 /* can be interrupted anywhere here, so:
185 * 1. make sure the new slot is null
186 * 2. make the new slot scannable (increment size)
187 * 3. put a valid object in there
189 * (have to do 1 then 3 so that if we're interrupted
190 * between 1 and 2, the object is still live)
194 SET_SP (handles
, top
, idx
);
195 mono_memory_write_barrier ();
197 mono_memory_write_barrier ();
201 if (G_LIKELY (top
->next
)) {
203 /* make sure size == 0 is visible to a GC thread before it sees the new top */
204 mono_memory_write_barrier ();
209 HandleChunk
*new_chunk
= new_handle_chunk ();
211 new_chunk
->prev
= top
;
212 new_chunk
->next
= NULL
;
213 /* make sure size == 0 before new chunk is visible */
214 mono_memory_write_barrier ();
215 top
->next
= new_chunk
;
216 handles
->top
= new_chunk
;
221 mono_handle_stack_alloc (void)
223 HandleStack
*stack
= new_handle_stack ();
224 HandleChunk
*chunk
= new_handle_chunk ();
226 chunk
->prev
= chunk
->next
= NULL
;
228 mono_memory_write_barrier ();
229 stack
->top
= stack
->bottom
= chunk
;
230 #ifdef MONO_HANDLE_TRACK_SP
231 stack
->stackmark_sp
= NULL
;
237 mono_handle_stack_free (HandleStack
*stack
)
241 HandleChunk
*c
= stack
->bottom
;
242 stack
->top
= stack
->bottom
= NULL
;
243 mono_memory_write_barrier ();
245 HandleChunk
*next
= c
->next
;
246 free_handle_chunk (c
);
249 free_handle_chunk (c
);
250 free_handle_stack (stack
);
254 mono_handle_stack_free_domain (HandleStack
*stack
, MonoDomain
*domain
)
256 /* Called by the GC while clearing out objects of the given domain from the heap. */
257 /* If there are no handles-related bugs, there is nothing to do: if a
258 * thread accessed objects from the domain it was aborted, so any
259 * threads left alive cannot have any handles that point into the
260 * unloading domain. However if there is a handle leak, the handle stack is not */
263 /* Root domain only unloaded when mono is shutting down, don't need to check anything */
264 if (domain
== mono_get_root_domain () || mono_runtime_is_shutting_down ())
266 HandleChunk
*cur
= stack
->bottom
;
267 HandleChunk
*last
= stack
->top
;
271 for (int idx
= 0; idx
< cur
->size
; ++idx
) {
272 HandleChunkElem
*elem
= &cur
->elems
[idx
];
275 g_assert (mono_object_domain (elem
->o
) != domain
);
284 check_handle_stack_monotonic (HandleStack
*stack
)
286 /* check that every allocated handle in the current handle stack is at no higher in the native stack than its predecessors */
287 #ifdef MONO_HANDLE_TRACK_SP
288 HandleChunk
*cur
= stack
->bottom
;
289 HandleChunk
*last
= stack
->top
;
292 HandleChunkElem
*prev
= NULL
;
293 gboolean monotonic
= TRUE
;
295 for (int i
= 0;i
< cur
->size
; ++i
) {
296 HandleChunkElem
*elem
= chunk_element (cur
, i
);
297 if (prev
&& elem
->alloc_sp
> prev
->alloc_sp
) {
299 #ifdef MONO_HANDLE_TRACK_OWNER
300 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
);
302 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
);
311 g_assert (monotonic
);
316 mono_handle_stack_scan (HandleStack
*stack
, GcScanFunc func
, gpointer gc_data
, gboolean precise
, gboolean check
)
318 if (check
) /* run just once (per handle stack) per GC */
319 check_handle_stack_monotonic (stack
);
322 We're called twice - on the imprecise pass we do nothing.
323 Interior pointers are retained in managed frames.
324 On the precise pass, we scan all the objects where the handles point to the start of
327 Note that if we're running, we know the world is stopped.
332 HandleChunk
*cur
= stack
->bottom
;
333 HandleChunk
*last
= stack
->top
;
336 for (int i
= 0; i
< cur
->size
; ++i
) {
337 HandleChunkElem
* elem
= chunk_element (cur
, i
);
338 gpointer
* obj_slot
= &elem
->o
;
339 if (*obj_slot
!= NULL
)
340 func (obj_slot
, gc_data
);
349 mono_stack_mark_record_size (MonoThreadInfo
*info
, HandleStackMark
*stackmark
, const char *func_name
)
351 HandleStack
*handles
= info
->handle_stack
;
352 HandleChunk
*cur
= stackmark
->chunk
;
353 int size
= -stackmark
->size
; //discard the starting point of the stack
356 if (cur
== handles
->top
)
361 if (size
> THIS_IS_AN_OK_NUMBER_OF_HANDLES
)
362 g_warning ("%s USED %d handles\n", func_name
, size
);
366 * Pop the stack until @stackmark and make @value the top value.
368 * @return the new handle for what @value points to
371 mono_stack_mark_pop_value (MonoThreadInfo
*info
, HandleStackMark
*stackmark
, MonoRawHandle value
)
373 MonoObject
*obj
= value
? *((MonoObject
**)value
) : NULL
;
374 mono_stack_mark_pop (info
, stackmark
);
375 #ifndef MONO_HANDLE_TRACK_OWNER
376 return mono_handle_new (obj
, info
);
378 return mono_handle_new (obj
, info
, "<mono_stack_mark_pop_value>");
382 /* Temporary place for some of the handle enabled wrapper functions*/
385 mono_string_new_handle (MonoDomain
*domain
, const char *data
, MonoError
*error
)
387 return MONO_HANDLE_NEW (MonoString
, mono_string_new_checked (domain
, data
, error
));
391 mono_array_new_handle (MonoDomain
*domain
, MonoClass
*eclass
, uintptr_t n
, MonoError
*error
)
393 return MONO_HANDLE_NEW (MonoArray
, mono_array_new_checked (domain
, eclass
, n
, error
));
397 mono_array_new_full_handle (MonoDomain
*domain
, MonoClass
*array_class
, uintptr_t *lengths
, intptr_t *lower_bounds
, MonoError
*error
)
399 return MONO_HANDLE_NEW (MonoArray
, mono_array_new_full_checked (domain
, array_class
, lengths
, lower_bounds
, error
));
403 mono_array_handle_length (MonoArrayHandle arr
)
405 MONO_REQ_GC_UNSAFE_MODE
;
407 return MONO_HANDLE_RAW (arr
)->max_length
;
411 mono_gchandle_from_handle (MonoObjectHandle handle
, mono_bool pinned
)
413 // FIXME This used to check for out of scope handles.
414 // Stack-based handles coming from icall wrappers do not
415 // work with that. This functionality can be largely restored
416 // by introducing a tag bit in handles -- 0 for managed stack-based,
417 // 1 for native TLS-based, having MONO_HANDLE_RAW clear it, and only
418 // doing the former checking for native TLS-based handles.
419 return mono_gchandle_new_internal (MONO_HANDLE_RAW (handle
), pinned
);
423 mono_gchandle_get_target_handle (uint32_t gchandle
)
425 return MONO_HANDLE_NEW (MonoObject
, mono_gchandle_get_target_internal (gchandle
));
429 mono_array_handle_pin_with_size (MonoArrayHandle handle
, int size
, uintptr_t idx
, uint32_t *gchandle
)
431 g_assert (gchandle
!= NULL
);
432 *gchandle
= mono_gchandle_from_handle (MONO_HANDLE_CAST(MonoObject
,handle
), TRUE
);
433 MonoArray
*raw
= MONO_HANDLE_RAW (handle
);
434 return mono_array_addr_with_size_internal (raw
, size
, idx
);
438 mono_string_handle_pin_chars (MonoStringHandle handle
, uint32_t *gchandle
)
440 g_assert (gchandle
!= NULL
);
441 *gchandle
= mono_gchandle_from_handle (MONO_HANDLE_CAST (MonoObject
, handle
), TRUE
);
442 MonoString
*raw
= MONO_HANDLE_RAW (handle
);
443 return mono_string_chars_internal (raw
);
447 mono_object_handle_pin_unbox (MonoObjectHandle obj
, uint32_t *gchandle
)
449 g_assert (!MONO_HANDLE_IS_NULL (obj
));
450 MonoClass
*klass
= mono_handle_class (obj
);
451 g_assert (m_class_is_valuetype (klass
));
452 *gchandle
= mono_gchandle_from_handle (obj
, TRUE
);
453 return mono_object_unbox_internal (MONO_HANDLE_RAW (obj
));
458 mono_array_handle_memcpy_refs (MonoArrayHandle dest
, uintptr_t dest_idx
, MonoArrayHandle src
, uintptr_t src_idx
, uintptr_t len
)
460 mono_array_memcpy_refs_internal (MONO_HANDLE_RAW (dest
), dest_idx
, MONO_HANDLE_RAW (src
), src_idx
, len
);
464 mono_handle_stack_is_empty (HandleStack
*stack
)
466 return stack
->top
== stack
->bottom
&& stack
->top
->size
== 0;
471 mono_gchandle_target_equal (uint32_t gchandle
, MonoObjectHandle equal
)
473 // This function serves to reduce coop handle creation.
474 MONO_HANDLE_SUPPRESS_SCOPE (1);
475 return mono_gchandle_get_target_internal (gchandle
) == MONO_HANDLE_RAW (equal
);
480 mono_gchandle_target_is_null_or_equal (uint32_t gchandle
, MonoObjectHandle equal
, gboolean
*is_null
,
483 // This function serves to reduce coop handle creation.
484 MONO_HANDLE_SUPPRESS_SCOPE (1);
485 MonoObject
*target
= mono_gchandle_get_target_internal (gchandle
);
486 *is_null
= target
== NULL
;
487 *is_equal
= target
== MONO_HANDLE_RAW (equal
);
492 mono_gchandle_set_target_handle (guint32 gchandle
, MonoObjectHandle obj
)
494 mono_gchandle_set_target (gchandle
, MONO_HANDLE_RAW (obj
));
499 mono_gchandle_new_weakref_from_handle (MonoObjectHandle handle
)
501 return mono_gchandle_new_weakref_internal (MONO_HANDLE_SUPPRESS (MONO_HANDLE_RAW (handle
)), FALSE
);
506 mono_handle_hash (MonoObjectHandle object
)
508 return mono_object_hash_internal (MONO_HANDLE_SUPPRESS (MONO_HANDLE_RAW (object
)));
513 mono_gchandle_new_weakref_from_handle_track_resurrection (MonoObjectHandle handle
)
515 return mono_gchandle_new_weakref_internal (MONO_HANDLE_SUPPRESS (MONO_HANDLE_RAW (handle
)), TRUE
);
520 mono_handle_array_getref (MonoObjectHandleOut dest
, MonoArrayHandle array
, uintptr_t index
)
522 MONO_HANDLE_SUPPRESS (g_assert (dest
.__raw
));
523 MONO_HANDLE_SUPPRESS (*dest
.__raw
= (MonoObject
*)mono_array_get_internal (MONO_HANDLE_RAW (array
), gpointer
, index
));