#include <mono/utils/atomic.h>
#include <mono/utils/mono-lazy-init.h>
#include <mono/utils/mono-threads.h>
+#ifdef HAVE_BACKTRACE_SYMBOLS
+#include <execinfo.h>
+#endif
+
/* TODO (missing pieces)
Add counters for:
* points to a valid value.
*/
-const MonoObjectHandle mono_null_value_handle = NULL;
-
-#define THIS_IS_AN_OK_NUMBER_OF_HANDLES 100
-
-enum {
- HANDLE_CHUNK_PTR_OBJ = 0x0, /* chunk element points to beginning of a managed object */
- HANDLE_CHUNK_PTR_INTERIOR = 0x1, /* chunk element points into the middle of a managed object */
- HANDLE_CHUNK_PTR_MASK = 0x1
-};
+#if defined(HAVE_BOEHM_GC) || defined(HAVE_NULL_GC)
+static HandleStack*
+new_handle_stack (void)
+{
+ return (HandleStack *)mono_gc_alloc_fixed (sizeof (HandleStack), MONO_GC_DESCRIPTOR_NULL, MONO_ROOT_SOURCE_HANDLE, "Thread Handle Stack");
+}
-/* number of bits in each word of the interior pointer bitmap */
-#define INTERIOR_HANDLE_BITMAP_BITS_PER_WORD (sizeof(guint32) << 3)
+static void
+free_handle_stack (HandleStack *stack)
+{
+ mono_gc_free_fixed (stack);
+}
-static gboolean
-bitset_bits_test (guint32 *bitmaps, int idx)
+static HandleChunk*
+new_handle_chunk (void)
{
- int w = idx / INTERIOR_HANDLE_BITMAP_BITS_PER_WORD;
- int b = idx % INTERIOR_HANDLE_BITMAP_BITS_PER_WORD;
- guint32 bitmap = bitmaps [w];
- guint32 mask = 1u << b;
- return ((bitmap & mask) != 0);
+#if defined(HAVE_BOEHM_GC)
+ return (HandleChunk *)GC_MALLOC (sizeof (HandleChunk));
+#elif defined(HAVE_NULL_GC)
+ return (HandleChunk *)g_malloc (sizeof (HandleChunk));
+#endif
}
static void
-bitset_bits_set (guint32 *bitmaps, int idx)
+free_handle_chunk (HandleChunk *chunk)
{
- int w = idx / INTERIOR_HANDLE_BITMAP_BITS_PER_WORD;
- int b = idx % INTERIOR_HANDLE_BITMAP_BITS_PER_WORD;
- guint32 *bitmap = &bitmaps [w];
- guint32 mask = 1u << b;
- *bitmap |= mask;
+#if defined(HAVE_NULL_GC)
+ g_free (chunk);
+#endif
+}
+#else
+static HandleStack*
+new_handle_stack (void)
+{
+ return g_new (HandleStack, 1);
}
+
static void
-bitset_bits_clear (guint32 *bitmaps, int idx)
+free_handle_stack (HandleStack *stack)
{
- int w = idx / INTERIOR_HANDLE_BITMAP_BITS_PER_WORD;
- int b = idx % INTERIOR_HANDLE_BITMAP_BITS_PER_WORD;
- guint32 *bitmap = &bitmaps [w];
- guint32 mask = ~(1u << b);
- *bitmap &= mask;
+ g_free (stack);
}
-static gpointer*
-chunk_element_objslot_init (HandleChunk *chunk, int idx, gboolean interior)
+static HandleChunk*
+new_handle_chunk (void)
{
- if (interior)
- bitset_bits_set (chunk->interior_bitmap, idx);
- else
- bitset_bits_clear (chunk->interior_bitmap, idx);
- return &chunk->elems [idx].o;
+ return g_new (HandleChunk, 1);
}
-static HandleChunkElem*
-chunk_element (HandleChunk *chunk, int idx)
+static void
+free_handle_chunk (HandleChunk *chunk)
{
- return &chunk->elems[idx];
+ g_free (chunk);
}
+#endif
-static guint
-chunk_element_kind (HandleChunk *chunk, int idx)
+const MonoObjectHandle mono_null_value_handle = NULL;
+
+#define THIS_IS_AN_OK_NUMBER_OF_HANDLES 100
+
+static HandleChunkElem*
+chunk_element (HandleChunk *chunk, int idx)
{
- return bitset_bits_test (chunk->interior_bitmap, idx) ? HANDLE_CHUNK_PTR_INTERIOR : HANDLE_CHUNK_PTR_OBJ;
+ return &chunk->elems[idx];
}
static HandleChunkElem*
}
#ifdef MONO_HANDLE_TRACK_OWNER
-#define SET_OWNER(chunk,idx) do { (chunk)->elems[(idx)].owner = owner; } while (0)
+#ifdef HAVE_BACKTRACE_SYMBOLS
+#define SET_BACKTRACE(btaddrs) do { \
+ backtrace(btaddrs, 7); \
+ } while (0)
+#else
+#define SET_BACKTRACE(btaddrs) 0
+#endif
+#define SET_OWNER(chunk,idx) do { (chunk)->elems[(idx)].owner = owner; SET_BACKTRACE (&((chunk)->elems[(idx)].backtrace_ips[0])); } while (0)
#else
#define SET_OWNER(chunk,idx) do { } while (0)
#endif
}
#endif
-MonoRawHandle
-#ifndef MONO_HANDLE_TRACK_OWNER
-mono_handle_new (MonoObject *object)
-#else
-mono_handle_new (MonoObject *object, const char *owner)
-#endif
-{
-#ifndef MONO_HANDLE_TRACK_OWNER
- return mono_handle_new_full (object, FALSE);
-#else
- return mono_handle_new_full (object, FALSE, owner);
-#endif
-}
/* Actual handles implementation */
MonoRawHandle
#ifndef MONO_HANDLE_TRACK_OWNER
-mono_handle_new_full (gpointer rawptr, gboolean interior)
+mono_handle_new (MonoObject *obj)
#else
-mono_handle_new_full (gpointer rawptr, gboolean interior, const char *owner)
+mono_handle_new (MonoObject *obj, const char *owner)
#endif
{
MonoThreadInfo *info = mono_thread_info_current ();
retry:
if (G_LIKELY (top->size < OBJECTS_PER_HANDLES_CHUNK)) {
int idx = top->size;
- gpointer* objslot = chunk_element_objslot_init (top, idx, interior);
+ gpointer* objslot = &top->elems [idx].o;
/* can be interrupted anywhere here, so:
* 1. make sure the new slot is null
* 2. make the new slot scannable (increment size)
mono_memory_write_barrier ();
top->size++;
mono_memory_write_barrier ();
- *objslot = rawptr;
+ *objslot = obj;
SET_OWNER (top,idx);
SET_SP (handles, top, idx);
return objslot;
handles->top = top;
goto retry;
}
- HandleChunk *new_chunk = g_new (HandleChunk, 1);
+ HandleChunk *new_chunk = new_handle_chunk ();
new_chunk->size = 0;
- memset (new_chunk->interior_bitmap, 0, INTERIOR_HANDLE_BITMAP_WORDS);
new_chunk->prev = top;
new_chunk->next = NULL;
/* make sure size == 0 before new chunk is visible */
goto retry;
}
+MonoRawHandle
+#ifndef MONO_HANDLE_TRACK_OWNER
+mono_handle_new_interior (gpointer rawptr)
+#else
+mono_handle_new_interior (gpointer rawptr, const char *owner)
+#endif
+{
+ MonoThreadInfo *info = mono_thread_info_current ();
+ HandleStack *handles = (HandleStack *)info->handle_stack;
+ HandleChunk *top = handles->interior;
+#ifdef MONO_HANDLE_TRACK_SP
+ mono_handle_chunk_leak_check (handles);
+#endif
+ g_assert (top);
+
+ /*
+ * Don't extend the chunk now, interior handles are
+ * only used for icall arguments, they shouldn't
+ * overflow.
+ */
+ g_assert (top->size < OBJECTS_PER_HANDLES_CHUNK);
+ int idx = top->size;
+ gpointer *objslot = &top->elems [idx].o;
+ *objslot = NULL;
+ mono_memory_write_barrier ();
+ top->size++;
+ mono_memory_write_barrier ();
+ *objslot = rawptr;
+ SET_OWNER (top,idx);
+ SET_SP (handles, top, idx);
+ return objslot;
+}
HandleStack*
mono_handle_stack_alloc (void)
{
- HandleStack *stack = g_new (HandleStack, 1);
- HandleChunk *chunk = g_new (HandleChunk, 1);
+ HandleStack *stack = new_handle_stack ();
+ HandleChunk *chunk = new_handle_chunk ();
+ HandleChunk *interior = new_handle_chunk ();
- chunk->size = 0;
- memset (chunk->interior_bitmap, 0, INTERIOR_HANDLE_BITMAP_WORDS);
chunk->prev = chunk->next = NULL;
+ chunk->size = 0;
+ interior->prev = interior->next = NULL;
+ interior->size = 0;
mono_memory_write_barrier ();
stack->top = stack->bottom = chunk;
-#ifdef MONO_HANDLE_TRACK_OWNER
+ stack->interior = interior;
+#ifdef MONO_HANDLE_TRACK_SP
stack->stackmark_sp = NULL;
#endif
return stack;
mono_memory_write_barrier ();
while (c) {
HandleChunk *next = c->next;
- g_free (c);
+ free_handle_chunk (c);
c = next;
}
- g_free (c);
- g_free (stack);
+ free_handle_chunk (c);
+ free_handle_chunk (stack->interior);
+ free_handle_stack (stack);
+}
+
+void
+mono_handle_stack_free_domain (HandleStack *stack, MonoDomain *domain)
+{
+ /* Called by the GC while clearing out objects of the given domain from the heap. */
+ /* If there are no handles-related bugs, there is nothing to do: if a
+ * thread accessed objects from the domain it was aborted, so any
+ * threads left alive cannot have any handles that point into the
+ * unloading domain. However if there is a handle leak, the handle stack is not */
+ if (!stack)
+ return;
+ /* Root domain only unloaded when mono is shutting down, don't need to check anything */
+ if (domain == mono_get_root_domain () || mono_runtime_is_shutting_down ())
+ return;
+ HandleChunk *cur = stack->bottom;
+ HandleChunk *last = stack->top;
+ if (!cur)
+ return;
+ while (cur) {
+ for (int idx = 0; idx < cur->size; ++idx) {
+ HandleChunkElem *elem = &cur->elems[idx];
+ if (!elem->o)
+ continue;
+ g_assert (mono_object_domain (elem->o) != domain);
+ }
+ if (cur == last)
+ break;
+ cur = cur->next;
+ }
+ /* We don't examine the interior pointers here because the GC treats
+ * them conservatively and anyway we don't have enough information here to
+ * find the object's vtable.
+ */
}
static void
Note that if we're running, we know the world is stopped.
*/
- HandleChunk *cur = stack->bottom;
- HandleChunk *last = stack->top;
+ if (precise) {
+ HandleChunk *cur = stack->bottom;
+ HandleChunk *last = stack->top;
- if (!cur)
- return;
-
- while (cur) {
- /* assume that object pointers will be much more common than interior pointers.
- * scan the object pointers by iterating over the chunk elements.
- * scan the interior pointers by iterating over the bitmap bits.
- */
- if (precise) {
+ while (cur) {
for (int i = 0; i < cur->size; ++i) {
HandleChunkElem* elem = chunk_element (cur, i);
- int kind = chunk_element_kind (cur, i);
gpointer* obj_slot = &elem->o;
- if (kind == HANDLE_CHUNK_PTR_OBJ && *obj_slot != NULL)
+ if (*obj_slot != NULL)
func (obj_slot, gc_data);
}
- } else {
- int elem_idx = 0;
- for (int i = 0; i < INTERIOR_HANDLE_BITMAP_WORDS; ++i) {
- elem_idx = i * INTERIOR_HANDLE_BITMAP_BITS_PER_WORD;
- if (elem_idx >= cur->size)
- break;
- /* no interior pointers in the range */
- if (cur->interior_bitmap [i] == 0)
- continue;
- for (int j = 0; j < INTERIOR_HANDLE_BITMAP_BITS_PER_WORD && elem_idx < cur->size; ++j,++elem_idx) {
- HandleChunkElem *elem = chunk_element (cur, elem_idx);
- int kind = chunk_element_kind (cur, elem_idx);
- gpointer *ptr_slot = &elem->o;
- if (kind == HANDLE_CHUNK_PTR_INTERIOR && *ptr_slot != NULL)
- func (ptr_slot, gc_data);
- }
- }
+ if (cur == last)
+ break;
+ cur = cur->next;
+ }
+ } else {
+ HandleChunk *cur = stack->interior;
+
+ if (!cur)
+ return;
+ for (int i = 0; i < cur->size; ++i) {
+ HandleChunkElem* elem = chunk_element (cur, i);
+ gpointer* ptr_slot = &elem->o;
+ if (*ptr_slot != NULL)
+ func (ptr_slot, gc_data);
}
- if (cur == last)
- break;
- cur = cur->next;
}
}
HandleChunk *chunk = chunk_element_to_chunk_idx (stack, elem, &elem_idx);
/* gchandles cannot deal with interior pointers */
g_assert (chunk != NULL);
- g_assert (chunk_element_kind (chunk, elem_idx) != HANDLE_CHUNK_PTR_INTERIOR);
return mono_gchandle_new (MONO_HANDLE_RAW (handle), pinned);
}
return mono_array_addr_with_size (raw, size, idx);
}
+gunichar2*
+mono_string_handle_pin_chars (MonoStringHandle handle, uint32_t *gchandle)
+{
+ g_assert (gchandle != NULL);
+ *gchandle = mono_gchandle_from_handle (MONO_HANDLE_CAST (MonoObject, handle), TRUE);
+ MonoString *raw = MONO_HANDLE_RAW (handle);
+ return mono_string_chars (raw);
+}
+
+gpointer
+mono_object_handle_pin_unbox (MonoObjectHandle obj, uint32_t *gchandle)
+{
+ g_assert (!MONO_HANDLE_IS_NULL (obj));
+ MonoClass *klass = mono_handle_class (obj);
+ g_assert (klass->valuetype);
+ *gchandle = mono_gchandle_from_handle (obj, TRUE);
+ return mono_object_unbox (MONO_HANDLE_RAW (obj));
+}
+
void
mono_array_handle_memcpy_refs (MonoArrayHandle dest, uintptr_t dest_idx, MonoArrayHandle src, uintptr_t src_idx, uintptr_t len)
{