2002-11-18 Dick Porter <dick@ximian.com>
[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_domain_set (mono_object_domain (o));
51         mono_runtime_invoke (o->vtable->klass->vtable [finalize_slot], o, NULL, &exc);
52
53         if (exc) {
54                 /* fixme: do something useful */
55         }
56 }
57
58 /*
59  * Some of our objects may point to a different address than the address returned by GC_malloc()
60  * (because of the GetHashCode hack), but we need to pass the real address to register_finalizer.
61  * This also means that in the callback we need to adjust the pointer to get back the real
62  * MonoObject*.
63  * We also need to be consistent in the use of the GC_debug* variants of malloc and register_finalizer, 
64  * since that, too, can cause the underlying pointer to be offset.
65  */
66 static void
67 object_register_finalizer (MonoObject *obj, void (*callback)(void *, void*))
68 {
69 #if HAVE_BOEHM_GC
70         guint offset = 0;
71
72         g_assert (GC_base (obj) == (char*)obj - offset);
73         GC_REGISTER_FINALIZER_NO_ORDER ((char*)obj - offset, callback, GUINT_TO_POINTER (offset), NULL, NULL);
74 #endif
75 }
76
77 void
78 mono_object_register_finalizer (MonoObject *obj)
79 {
80         /*g_print ("Registered finalizer on %p %s.%s\n", obj, mono_object_class (obj)->name_space, mono_object_class (obj)->name);*/
81         object_register_finalizer (obj, run_finalize);
82 }
83
84 /* 
85  * to speedup, at class init time, check if a class or struct
86  * have fields that need to be finalized and set a flag.
87  */
88 static void
89 finalize_fields (MonoClass *class, char *data, gboolean instance, GHashTable *todo) {
90         int i;
91         MonoClassField *field;
92         MonoObject *obj;
93
94         /*if (!instance)
95                 g_print ("Finalize statics on on %s\n", class->name);*/
96         if (instance && class->valuetype)
97                 data -= sizeof (MonoObject);
98         do {
99                 for (i = 0; i < class->field.count; ++i) {
100                         field = &class->fields [i];
101                         if (instance) {
102                                 if (field->type->attrs & FIELD_ATTRIBUTE_STATIC)
103                                         continue;
104                         } else {
105                                 if (!(field->type->attrs & FIELD_ATTRIBUTE_STATIC))
106                                         continue;
107                         }
108                         switch (field->type->type) {
109                         case MONO_TYPE_OBJECT:
110                         case MONO_TYPE_CLASS:
111                                 obj = *((MonoObject**)(data + field->offset));
112                                 if (obj) {
113                                         if (mono_object_class (obj)->has_finalize) {
114                                                 /* disable the registered finalizer */
115                                                 object_register_finalizer (obj, NULL);
116                                                 run_finalize (obj, NULL);
117                                         } else {
118                                                 /* 
119                                                  * if the type doesn't have a finalizer, we finalize 
120                                                  * the fields ourselves just like we do for structs.
121                                                  * Disabled for now: how do we handle loops?
122                                                  */
123                                                 /*finalize_fields (mono_object_class (obj), obj, TRUE, todo);*/
124                                         }
125                                 }
126                                 break;
127                         case MONO_TYPE_VALUETYPE: {
128                                 MonoClass *fclass = mono_class_from_mono_type (field->type);
129                                 if (fclass->enumtype)
130                                         continue;
131                                 /*finalize_fields (fclass, data + field->offset, TRUE, todo);*/
132                                 break;
133                         }
134                         case MONO_TYPE_ARRAY:
135                         case MONO_TYPE_SZARRAY:
136                                 /* FIXME: foreach item... */
137                                 break;
138                         }
139                 }
140                 if (!instance)
141                         return;
142                 class = class->parent;
143         } while (class);
144 }
145
146 static void
147 finalize_static_data (MonoClass *class, MonoVTable *vtable, GHashTable *todo) {
148
149         if (class->enumtype || !vtable->data)
150                 return;
151         finalize_fields (class, vtable->data, FALSE, todo);
152 }
153
154 void
155 mono_domain_finalize (MonoDomain *domain) {
156
157         GHashTable *todo = g_hash_table_new (NULL, NULL);
158 #if HAVE_BOEHM_GC
159         GC_gcollect ();
160 #endif
161         mono_g_hash_table_foreach (domain->class_vtable_hash, (GHFunc)finalize_static_data, todo);
162         /* FIXME: finalize objects in todo... */
163         g_hash_table_destroy (todo);
164 }
165
166 void
167 ves_icall_System_GC_InternalCollect (int generation)
168 {
169 #if HAVE_BOEHM_GC
170         GC_gcollect ();
171 #endif
172 }
173
174 gint64
175 ves_icall_System_GC_GetTotalMemory (MonoBoolean forceCollection)
176 {
177 #if HAVE_BOEHM_GC
178         if (forceCollection)
179                 GC_gcollect ();
180         return GC_get_heap_size ();
181 #else
182         return 0;
183 #endif
184 }
185
186 void
187 ves_icall_System_GC_KeepAlive (MonoObject *obj)
188 {
189         /*
190          * Does nothing.
191          */
192 }
193
194 void
195 ves_icall_System_GC_ReRegisterForFinalize (MonoObject *obj)
196 {
197         object_register_finalizer (obj, run_finalize);
198 }
199
200 void
201 ves_icall_System_GC_SuppressFinalize (MonoObject *obj)
202 {
203         object_register_finalizer (obj, NULL);
204 }
205
206 void
207 ves_icall_System_GC_WaitForPendingFinalizers (void)
208 {
209 }
210
211 /*static CRITICAL_SECTION handle_section;*/
212 static guint32 next_handle = 0;
213 static gpointer *gc_handles = NULL;
214 static guint8 *gc_handle_types = NULL;
215 static guint32 array_size = 0;
216
217 /*
218  * The handle type is encoded in the lower two bits of the handle value:
219  * 0 -> normal
220  * 1 -> pinned
221  * 2 -> weak
222  */
223
224 typedef enum {
225         HANDLE_WEAK,
226         HANDLE_WEAK_TRACK,
227         HANDLE_NORMAL,
228         HANDLE_PINNED
229 } HandleType;
230
231 /*
232  * FIXME: make thread safe and reuse the array entries.
233  */
234 MonoObject *
235 ves_icall_System_GCHandle_GetTarget (guint32 handle)
236 {
237         MonoObject *obj;
238         gint32 type;
239
240         if (gc_handles) {
241                 type = handle & 0x3;
242                 g_assert (type == gc_handle_types [handle >> 2]);
243                 obj = gc_handles [handle >> 2];
244                 if (!obj)
245                         return NULL;
246
247                 if ((type == HANDLE_WEAK) || (type == HANDLE_WEAK_TRACK))
248                         return REVEAL_POINTER (obj);
249                 else
250                 return obj;
251         }
252         return NULL;
253 }
254
255 guint32
256 ves_icall_System_GCHandle_GetTargetHandle (MonoObject *obj, guint32 handle, gint32 type)
257 {
258         gpointer val = obj;
259         guint32 h, idx = next_handle++;
260
261         if (idx >= array_size) {
262 #if HAVE_BOEHM_GC
263                 gpointer *new_array;
264                 guint8 *new_type_array;
265                 if (!array_size)
266                         array_size = 16;
267                 new_array = GC_MALLOC (sizeof (gpointer) * (array_size * 2));
268                 new_type_array = GC_MALLOC (sizeof (guint8) * (array_size * 2));
269                 if (gc_handles) {
270                         int i;
271                         memcpy (new_array, gc_handles, sizeof (gpointer) * array_size);
272                         memcpy (new_type_array, gc_handle_types, sizeof (guint8) * array_size);
273                         /* need to re-register links for weak refs. test if GC_realloc needs the same */
274                         for (i = 0; i < array_size; ++i) {
275 #if 0 /* This breaks the threaded finalizer, by causing segfaults deep
276        * inside libgc.  I assume it will also break without the
277        * threaded finalizer, just that the stress test (bug 31333)
278        * deadlocks too early without it.  Reverting to the previous
279        * version here stops the segfault.
280        */
281                                 if ((gc_handle_types[i] == HANDLE_WEAK) || (gc_handle_types[i] == HANDLE_WEAK_TRACK)) { /* all and only disguised pointers have it set */
282 #else
283                                 if (((gulong)new_array [i]) & 0x1) {
284 #endif
285                                         if (gc_handles [i] != (gpointer)-1)
286                                                 GC_unregister_disappearing_link (&(gc_handles [i]));
287                                         if (new_array [i] != (gpointer)-1)
288                                                 GC_GENERAL_REGISTER_DISAPPEARING_LINK (&(new_array [i]), REVEAL_POINTER (new_array [i]));
289                                 }
290                         }
291                 }
292                 array_size *= 2;
293                 gc_handles = new_array;
294                 gc_handle_types = new_type_array;
295 #else
296                 g_error ("No GCHandle support built-in");
297 #endif
298         }
299
300         /* resuse the type from the old target */
301         if (type == -1)
302                 type =  handle & 0x3;
303         h = (idx << 2) | type;
304         switch (type) {
305         case HANDLE_WEAK:
306         case HANDLE_WEAK_TRACK:
307                 val = (gpointer)HIDE_POINTER (val);
308                 gc_handles [idx] = val;
309                 gc_handle_types [idx] = type;
310 #if HAVE_BOEHM_GC
311                 if (gc_handles [idx] != (gpointer)-1)
312                         GC_GENERAL_REGISTER_DISAPPEARING_LINK (&(gc_handles [idx]), obj);
313 #else
314                 g_error ("No weakref support");
315 #endif
316                 break;
317         default:
318                 gc_handles [idx] = val;
319                 gc_handle_types [idx] = type;
320                 break;
321         }
322         return h;
323 }
324
325 void
326 ves_icall_System_GCHandle_FreeHandle (guint32 handle)
327 {
328         int idx = handle >> 2;
329         int type = handle & 0x3;
330
331 #ifdef HAVE_BOEHM_GC
332         g_assert (type == gc_handle_types [idx]);
333         if ((type == HANDLE_WEAK) || (type == HANDLE_WEAK_TRACK)) {
334                 if (gc_handles [idx] != (gpointer)-1)
335                         GC_unregister_disappearing_link (&(gc_handles [idx]));
336         }
337 #else
338         g_error ("No GCHandle support");
339 #endif
340
341         gc_handles [idx] = (gpointer)-1;
342         gc_handle_types [idx] = (guint8)-1;
343 }
344
345 gpointer
346 ves_icall_System_GCHandle_GetAddrOfPinnedObject (guint32 handle)
347 {
348         MonoObject *obj;
349         int type = handle & 0x3;
350
351         if (gc_handles) {
352                 obj = gc_handles [handle >> 2];
353                 g_assert (gc_handle_types [handle >> 2] == type);
354                 if ((type == HANDLE_WEAK) || (type == HANDLE_WEAK_TRACK)) {
355                         obj = REVEAL_POINTER (obj);
356                         if (obj == (MonoObject *) -1)
357                                 return NULL;
358                 }
359                 return obj;
360         }
361         return NULL;
362 }
363
364 static HANDLE finalizer_event;
365 static volatile gboolean finished=FALSE;
366
367 static void finalize_notify (void)
368 {
369 #ifdef DEBUG
370         g_message (G_GNUC_PRETTY_FUNCTION ": prodding finalizer");
371 #endif
372
373         SetEvent (finalizer_event);
374 }
375
376 static guint32 finalizer_thread (gpointer unused)
377 {
378         guint32 stack_start;
379         
380         mono_new_thread_init (NULL, &stack_start);
381         
382         while(!finished) {
383                 /* Wait to be notified that there's at least one
384                  * finaliser to run
385                  */
386                 WaitForSingleObject (finalizer_event, INFINITE);
387
388 #ifdef DEBUG
389                 g_message (G_GNUC_PRETTY_FUNCTION ": invoking finalizers");
390 #endif
391
392                 GC_invoke_finalizers ();
393         }
394         
395         return(0);
396 }
397
398 void mono_gc_init (void)
399 {
400         HANDLE gc_thread;
401
402         finalizer_event=CreateEvent (NULL, FALSE, FALSE, NULL);
403         if(finalizer_event==NULL) {
404                 g_assert_not_reached ();
405         }
406         
407         GC_finalize_on_demand=1;
408         GC_finalizer_notifier=finalize_notify;
409         
410         /* Don't use mono_thread_create here, because we don't want
411          * the runtime to wait for this thread to exit when it's
412          * cleaning up.
413          */
414         gc_thread=CreateThread (NULL, 0, finalizer_thread, NULL, 0, NULL);
415         if(gc_thread==NULL) {
416                 g_assert_not_reached ();
417         }
418 }
419
420 void mono_gc_cleanup (void)
421 {
422 #ifdef DEBUG
423         g_message (G_GNUC_PRETTY_FUNCTION ": cleaning up finalizer");
424 #endif
425
426         finished=TRUE;
427         finalize_notify ();
428 }