5cd53882b3234adbac0574139602abd478cde087
[mono.git] / mono / metadata / gc.c
1 /*
2  * metadata/gc.c: GC icalls.
3  *
4  * Author: Paolo Molaro <lupus@ximian.com>
5  *
6  * (C) 2002 Ximian, Inc.
7  */
8
9 #include <config.h>
10 #include <glib.h>
11 #include <string.h>
12
13 #include <mono/metadata/gc.h>
14 #include <mono/metadata/threads.h>
15 #include <mono/metadata/tabledefs.h>
16 #define GC_I_HIDE_POINTERS
17 #include <mono/os/gc_wrapper.h>
18
19 #ifndef HIDE_POINTER
20 #define HIDE_POINTER(v)         (v)
21 #define REVEAL_POINTER(v)       (v)
22 #endif
23
24 static int finalize_slot = -1;
25
26 /* 
27  * actually, we might want to queue the finalize requests in a separate thread,
28  * but we need to be careful about the execution domain of the thread...
29  */
30 static void
31 run_finalize (void *obj, void *data)
32 {
33         MonoObject *exc = NULL;
34         MonoObject *o;
35         o = (MonoObject*)((char*)obj + GPOINTER_TO_UINT (data));
36
37         if (finalize_slot < 0) {
38                 int i;
39                 for (i = 0; i < mono_defaults.object_class->vtable_size; ++i) {
40                         MonoMethod *cm = mono_defaults.object_class->vtable [i];
41                
42                         if (!strcmp (cm->name, "Finalize")) {
43                                 finalize_slot = i;
44                                 break;
45                         }
46                 }
47         }
48         /* speedup later... and use a timeout */
49         /*g_print ("Finalize run on %p %s.%s\n", o, mono_object_class (o)->name_space, mono_object_class (o)->name);*/
50         mono_runtime_invoke (o->vtable->klass->vtable [finalize_slot], o, NULL, &exc);
51
52         if (exc) {
53                 /* fixme: do something useful */
54         }
55 }
56
57 /*
58  * Some of our objects may point to a different address than the address returned by GC_malloc()
59  * (because of the GetHashCode hack), but we need to pass the real address to register_finalizer.
60  * This also means that in the callback we need to adjust the pointer to get back the real
61  * MonoObject*.
62  * We also need to be consistent in the use of the GC_debug* variants of malloc and register_finalizer, 
63  * since that, too, can cause the underlying pointer to be offset.
64  */
65 static void
66 object_register_finalizer (MonoObject *obj, void (*callback)(void *, void*))
67 {
68 #if HAVE_BOEHM_GC
69         guint offset = 0;
70
71         g_assert (GC_base (obj) == (char*)obj - offset);
72         GC_register_finalizer ((char*)obj - offset, callback, GUINT_TO_POINTER (offset), NULL, NULL);
73 #endif
74 }
75
76 void
77 mono_object_register_finalizer (MonoObject *obj)
78 {
79         /*g_print ("Registered finalizer on %p %s.%s\n", obj, mono_object_class (obj)->name_space, mono_object_class (obj)->name);*/
80         object_register_finalizer (obj, run_finalize);
81 }
82
83 /* 
84  * to speedup, at class init time, check if a class or struct
85  * have fields that need to be finalized and set a flag.
86  */
87 static void
88 finalize_fields (MonoClass *class, char *data, gboolean instance, GHashTable *todo) {
89         int i;
90         MonoClassField *field;
91         MonoObject *obj;
92
93         /*if (!instance)
94                 g_print ("Finalize statics on on %s\n", class->name);*/
95         if (instance && class->valuetype)
96                 data -= sizeof (MonoObject);
97         do {
98                 for (i = 0; i < class->field.count; ++i) {
99                         field = &class->fields [i];
100                         if (instance) {
101                                 if (field->type->attrs & FIELD_ATTRIBUTE_STATIC)
102                                         continue;
103                         } else {
104                                 if (!(field->type->attrs & FIELD_ATTRIBUTE_STATIC))
105                                         continue;
106                         }
107                         switch (field->type->type) {
108                         case MONO_TYPE_OBJECT:
109                         case MONO_TYPE_CLASS:
110                                 obj = *((MonoObject**)(data + field->offset));
111                                 if (obj) {
112                                         if (mono_object_class (obj)->has_finalize) {
113                                                 /* disable the registered finalizer */
114                                                 object_register_finalizer (obj, NULL);
115                                                 run_finalize (obj, NULL);
116                                         } else {
117                                                 /* 
118                                                  * if the type doesn't have a finalizer, we finalize 
119                                                  * the fields ourselves just like we do for structs.
120                                                  * Disabled for now: how do we handle loops?
121                                                  */
122                                                 /*finalize_fields (mono_object_class (obj), obj, TRUE, todo);*/
123                                         }
124                                 }
125                                 break;
126                         case MONO_TYPE_VALUETYPE: {
127                                 MonoClass *fclass = mono_class_from_mono_type (field->type);
128                                 if (fclass->enumtype)
129                                         continue;
130                                 /*finalize_fields (fclass, data + field->offset, TRUE, todo);*/
131                                 break;
132                         }
133                         case MONO_TYPE_ARRAY:
134                         case MONO_TYPE_SZARRAY:
135                                 /* FIXME: foreach item... */
136                                 break;
137                         }
138                 }
139                 if (!instance)
140                         return;
141                 class = class->parent;
142         } while (class);
143 }
144
145 static void
146 finalize_static_data (MonoClass *class, MonoVTable *vtable, GHashTable *todo) {
147
148         if (class->enumtype || !vtable->data)
149                 return;
150         finalize_fields (class, vtable->data, FALSE, todo);
151 }
152
153 void
154 mono_domain_finalize (MonoDomain *domain) {
155
156         GHashTable *todo = g_hash_table_new (NULL, NULL);
157 #if HAVE_BOEHM_GC
158         GC_gcollect ();
159 #endif
160         mono_g_hash_table_foreach (domain->class_vtable_hash, (GHFunc)finalize_static_data, todo);
161         /* FIXME: finalize objects in todo... */
162         g_hash_table_destroy (todo);
163 }
164
165 void
166 ves_icall_System_GC_InternalCollect (int generation)
167 {
168 #if HAVE_BOEHM_GC
169         GC_gcollect ();
170 #endif
171 }
172
173 gint64
174 ves_icall_System_GC_GetTotalMemory (MonoBoolean forceCollection)
175 {
176 #if HAVE_BOEHM_GC
177         if (forceCollection)
178                 GC_gcollect ();
179         return GC_get_heap_size ();
180 #else
181         return 0;
182 #endif
183 }
184
185 void
186 ves_icall_System_GC_KeepAlive (MonoObject *obj)
187 {
188         /*
189          * Does nothing.
190          */
191 }
192
193 void
194 ves_icall_System_GC_ReRegisterForFinalize (MonoObject *obj)
195 {
196         object_register_finalizer (obj, run_finalize);
197 }
198
199 void
200 ves_icall_System_GC_SuppressFinalize (MonoObject *obj)
201 {
202         object_register_finalizer (obj, NULL);
203 }
204
205 void
206 ves_icall_System_GC_WaitForPendingFinalizers (void)
207 {
208 }
209
210 /*static CRITICAL_SECTION handle_section;*/
211 static guint32 next_handle = 0;
212 static gpointer *gc_handles = NULL;
213 static guint8 *gc_handle_types = NULL;
214 static guint32 array_size = 0;
215
216 /*
217  * The handle type is encoded in the lower two bits of the handle value:
218  * 0 -> normal
219  * 1 -> pinned
220  * 2 -> weak
221  */
222
223 typedef enum {
224         HANDLE_WEAK,
225         HANDLE_WEAK_TRACK,
226         HANDLE_NORMAL,
227         HANDLE_PINNED
228 } HandleType;
229
230 /*
231  * FIXME: make thread safe and reuse the array entries.
232  */
233 MonoObject *
234 ves_icall_System_GCHandle_GetTarget (guint32 handle)
235 {
236         MonoObject *obj;
237         gint32 type;
238
239         if (gc_handles) {
240                 type = handle & 0x3;
241                 g_assert (type == gc_handle_types [handle >> 2]);
242                 obj = gc_handles [handle >> 2];
243                 if (!obj)
244                         return NULL;
245
246                 if ((type == HANDLE_WEAK) || (type == HANDLE_WEAK_TRACK))
247                         return REVEAL_POINTER (obj);
248                 else
249                 return obj;
250         }
251         return NULL;
252 }
253
254 guint32
255 ves_icall_System_GCHandle_GetTargetHandle (MonoObject *obj, guint32 handle, gint32 type)
256 {
257         gpointer val = obj;
258         guint32 h, idx = next_handle++;
259
260         if (idx >= array_size) {
261 #if HAVE_BOEHM_GC
262                 gpointer *new_array;
263                 guint8 *new_type_array;
264                 if (!array_size)
265                         array_size = 16;
266                 new_array = GC_malloc (sizeof (gpointer) * (array_size * 2));
267                 new_type_array = GC_malloc (sizeof (guint8) * (array_size * 2));
268                 if (gc_handles) {
269                         int i;
270                         memcpy (new_array, gc_handles, sizeof (gpointer) * array_size);
271                         memcpy (new_type_array, gc_handle_types, sizeof (guint8) * array_size);
272                         /* need to re-register links for weak refs. test if GC_realloc needs the same */
273                         for (i = 0; i < array_size; ++i) {
274                                 if ((gc_handle_types[i] == HANDLE_WEAK) || (gc_handle_types[i] == HANDLE_WEAK_TRACK)) { /* all and only disguised pointers have it set */
275                                         if (gc_handles [i] != (gpointer)-1)
276                                                 GC_unregister_disappearing_link (&(gc_handles [i]));
277                                         if (new_array [i] != (gpointer)-1)
278                                                 GC_general_register_disappearing_link (&(new_array [i]), REVEAL_POINTER (new_array [i]));
279                                 }
280                         }
281                 }
282                 array_size *= 2;
283                 gc_handles = new_array;
284                 gc_handle_types = new_type_array;
285 #else
286                 g_error ("No GCHandle support built-in");
287 #endif
288         }
289
290         /* resuse the type from the old target */
291         if (type == -1)
292                 type =  handle & 0x3;
293         h = (idx << 2) | type;
294         switch (type) {
295         case HANDLE_WEAK:
296         case HANDLE_WEAK_TRACK:
297                 val = (gpointer)HIDE_POINTER (val);
298                 gc_handles [idx] = val;
299                 gc_handle_types [idx] = type;
300 #if HAVE_BOEHM_GC
301                 if (gc_handles [idx] != (gpointer)-1)
302                         GC_general_register_disappearing_link (&(gc_handles [idx]), obj);
303 #else
304                 g_error ("No weakref support");
305 #endif
306                 break;
307         default:
308                 gc_handles [idx] = val;
309                 gc_handle_types [idx] = type;
310                 break;
311         }
312         return h;
313 }
314
315 void
316 ves_icall_System_GCHandle_FreeHandle (guint32 handle)
317 {
318         int idx = handle >> 2;
319         int type = handle & 0x3;
320
321 #ifdef HAVE_BOEHM_GC
322         g_assert (type == gc_handle_types [idx]);
323         if ((type == HANDLE_WEAK) || (type == HANDLE_WEAK_TRACK)) {
324                 if (gc_handles [idx] != (gpointer)-1)
325                         GC_unregister_disappearing_link (&(gc_handles [idx]));
326         }
327 #else
328         g_error ("No GCHandle support");
329 #endif
330
331         gc_handles [idx] = (gpointer)-1;
332         gc_handle_types [idx] = (guint8)-1;
333 }
334
335 gpointer
336 ves_icall_System_GCHandle_GetAddrOfPinnedObject (guint32 handle)
337 {
338         MonoObject *obj;
339         int type = handle & 0x3;
340
341         if (gc_handles) {
342                 obj = gc_handles [handle >> 2];
343                 g_assert (gc_handle_types [handle >> 2] == type);
344                 if ((type == HANDLE_WEAK) || (type == HANDLE_WEAK_TRACK)) {
345                         obj = REVEAL_POINTER (obj);
346                         if (obj == (MonoObject *) -1)
347                                 return NULL;
348                 }
349                 return obj;
350         }
351         return NULL;
352 }
353
354