bf0abc24e4f518b2e1b4b882d9f4d22b7347f669
[mono.git] / mono / metadata / handle.c
1 /*
2  * handle.c: Handle to object in native code
3  *
4  * Authors:
5  *  - Ludovic Henry <ludovic@xamarin.com>
6  *
7  * Copyright 2015 Xamarin, Inc. (www.xamarin.com)
8  */
9
10 #include <config.h>
11 #include <glib.h>
12
13 #include <mono/metadata/handle.h>
14 #include <mono/metadata/object-internals.h>
15 #include <mono/metadata/gc-internals.h>
16 #include <mono/utils/atomic.h>
17 #include <mono/utils/mono-lazy-init.h>
18
19 #define HANDLES_PER_CHUNK (16 - 2)
20
21 typedef struct _MonoHandleArenaChunk MonoHandleArenaChunk;
22 struct _MonoHandleArenaChunk {
23         MonoHandleArenaChunk *next;
24         gsize handles_size;
25         MonoHandleStorage handles [HANDLES_PER_CHUNK];
26 };
27
28 struct _MonoHandleArena {
29         MonoHandleArenaChunk *chunk;
30         MonoHandleArenaChunk *chunk_last;
31         MonoHandleArena *prev;
32 };
33
34 static mono_lazy_init_t arena_status = MONO_LAZY_INIT_STATUS_NOT_INITIALIZED;
35
36 static MonoGCDescriptor arena_desc = MONO_GC_DESCRIPTOR_NULL;
37
38 static MonoHandleArenaChunk *chunk_free_list = NULL;
39
40 static inline MonoHandleArenaChunk*
41 chunk_alloc (void)
42 {
43         MonoHandleArenaChunk *old, *new;
44
45         do {
46                 old = chunk_free_list;
47                 if (!old) {
48                         MonoHandleArenaChunk *chunk;
49
50                         chunk = g_malloc0 (sizeof (MonoHandleArenaChunk));
51                         g_assert (chunk);
52
53                         return chunk;
54                 }
55
56                 new = old->next;
57         } while (InterlockedCompareExchangePointer ((gpointer*) &chunk_free_list, new, old) != old);
58
59         memset (old, 0, sizeof (MonoHandleArenaChunk));
60         return old;
61 }
62
63 static inline void
64 chunk_free (MonoHandleArenaChunk *chunk)
65 {
66         do {
67                 chunk->next = chunk_free_list;
68         } while (InterlockedCompareExchangePointer ((gpointer*) &chunk_free_list, chunk, chunk->next) != chunk->next);
69 }
70
71 static MonoHandle
72 handle_new (MonoHandleArena *arena, MonoObject *obj)
73 {
74         MonoHandleArenaChunk *chunk;
75
76         g_assert (arena->chunk);
77         g_assert (arena->chunk_last);
78
79         chunk = arena->chunk_last;
80
81         if (chunk->handles_size < HANDLES_PER_CHUNK) {
82                 chunk->handles [chunk->handles_size].__private_obj = obj;
83                 chunk->handles_size += 1;
84
85                 return &chunk->handles [chunk->handles_size - 1];
86         }
87
88         chunk = chunk->next = chunk_alloc ();
89
90         chunk->handles [0].__private_obj = obj;
91         chunk->handles_size = 1;
92
93         arena->chunk_last = chunk;
94
95         return &chunk->handles [0];
96 }
97
98 MonoHandle
99 mono_handle_arena_new (MonoHandleArena *arena, MonoObject *obj)
100 {
101         g_assert (arena);
102         return handle_new (arena, obj);
103 }
104
105 /*
106  * Elevate the handle to the parent arena
107  */
108 MonoHandle
109 mono_handle_arena_elevate (MonoHandleArena *arena, MonoHandle handle)
110 {
111         g_assert (handle);
112         g_assert (arena);
113         g_assert (arena->prev);
114
115         return handle_new (arena->prev, handle->__private_obj);
116 }
117
118 gsize
119 mono_handle_arena_size (void)
120 {
121         return sizeof (MonoHandleArena) + sizeof (MonoHandleArenaChunk);
122 }
123
124 void
125 mono_handle_arena_stack_push(MonoHandleArena **arena_stack, MonoHandleArena *arena)
126 {
127         g_assert (arena_stack);
128         g_assert (arena);
129
130         arena->prev = *arena_stack;
131         arena->chunk = arena->chunk_last = (MonoHandleArenaChunk*) (((char*) arena) + sizeof (MonoHandleArena));
132
133         arena->chunk->next = NULL;
134         arena->chunk->handles_size = 0;
135         memset (&arena->chunk->handles [0], 0, sizeof (MonoHandleStorage) * HANDLES_PER_CHUNK);
136
137         *arena_stack = arena;
138 }
139
140 void
141 mono_handle_arena_stack_pop(MonoHandleArena **arena_stack, MonoHandleArena *arena)
142 {
143         MonoHandleArenaChunk *chunk, *next;
144
145         g_assert (arena);
146         g_assert (arena->chunk);
147         g_assert (arena->chunk_last);
148         g_assert (arena_stack);
149
150         *arena_stack = arena->prev;
151
152         for (chunk = arena->chunk; chunk; chunk = next) {
153                 next = chunk->next;
154                 memset (&chunk->handles [0], 0, sizeof (MonoHandleStorage) * HANDLES_PER_CHUNK);
155                 if (chunk != arena->chunk)
156                         chunk_free (chunk);
157         }
158 }
159
160 static void
161 arena_scan (gpointer addr, MonoGCMarkFunc mark_func, gpointer gc_data)
162 {
163         MonoHandleArena *arena;
164         MonoHandleArenaChunk *chunk;
165         int i;
166
167         for (arena = *(MonoHandleArena**) addr; arena; arena = arena->prev) {
168                 for (chunk = arena->chunk; chunk; chunk = chunk->next) {
169                         for (i = 0; i < chunk->handles_size; ++i) {
170                                 if (chunk->handles [i].__private_obj != NULL)
171                                         mark_func (&chunk->handles [i].__private_obj, gc_data);
172                         }
173                 }
174         }
175 }
176
177 static void
178 initialize (void)
179 {
180         arena_desc = mono_gc_make_root_descr_user (arena_scan);
181 }
182
183 void
184 mono_handle_arena_initialize (MonoHandleArena **arena_stack)
185 {
186         mono_lazy_initialize (&arena_status, initialize);
187         mono_gc_register_root ((char*) arena_stack, sizeof (MonoHandleArena*), arena_desc, MONO_ROOT_SOURCE_HANDLE, "runtime threads handle arena");
188 }
189
190 void
191 mono_handle_arena_deinitialize (MonoHandleArena **arena_stack)
192 {
193         mono_gc_deregister_root ((char*) arena_stack);
194 }
195
196 MonoHandleArena*
197 mono_handle_arena_current (void)
198 {
199         g_assert_not_reached ();
200 }
201
202 MonoHandleArena**
203 mono_handle_arena_current_addr (void)
204 {
205         g_assert_not_reached ();
206 }