Tue Aug 20 15:42:15 CEST 2002 Paolo Molaro <lupus@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 #if HAVE_BOEHM_GC
17 #define GC_I_HIDE_POINTERS
18 #include <gc/gc.h>
19 #else
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 %s\n", 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         object_register_finalizer (obj, run_finalize);
80 }
81
82 /* 
83  * to speedup, at class init time, check if a class or struct
84  * have fields that need to be finalized and set a flag.
85  */
86 static void
87 finalize_fields (MonoClass *class, char *data, gboolean instance, GHashTable *todo) {
88         int i;
89         MonoClassField *field;
90         MonoObject *obj;
91
92         /*if (!instance)
93                 g_print ("Finalize statics on on %s\n", class->name);*/
94         if (instance && class->valuetype)
95                 data -= sizeof (MonoObject);
96         do {
97                 for (i = 0; i < class->field.count; ++i) {
98                         field = &class->fields [i];
99                         if (instance) {
100                                 if (field->type->attrs & FIELD_ATTRIBUTE_STATIC)
101                                         continue;
102                         } else {
103                                 if (!(field->type->attrs & FIELD_ATTRIBUTE_STATIC))
104                                         continue;
105                         }
106                         switch (field->type->type) {
107                         case MONO_TYPE_OBJECT:
108                         case MONO_TYPE_CLASS:
109                                 obj = *((MonoObject**)(data + field->offset));
110                                 if (obj) {
111                                         if (mono_object_class (obj)->has_finalize) {
112                                                 /* disable the registered finalizer */
113                                                 object_register_finalizer (obj, NULL);
114                                                 run_finalize (obj, NULL);
115                                         } else {
116                                                 /* 
117                                                  * if the type doesn't have a finalizer, we finalize 
118                                                  * the fields ourselves just like we do for structs.
119                                                  * Disabled for now: how do we handle loops?
120                                                  */
121                                                 /*finalize_fields (mono_object_class (obj), obj, TRUE, todo);*/
122                                         }
123                                 }
124                                 break;
125                         case MONO_TYPE_VALUETYPE: {
126                                 MonoClass *fclass = mono_class_from_mono_type (field->type);
127                                 if (fclass->enumtype)
128                                         continue;
129                                 /*finalize_fields (fclass, data + field->offset, TRUE, todo);*/
130                                 break;
131                         }
132                         case MONO_TYPE_ARRAY:
133                         case MONO_TYPE_SZARRAY:
134                                 /* FIXME: foreach item... */
135                                 break;
136                         }
137                 }
138                 if (!instance)
139                         return;
140                 class = class->parent;
141         } while (class);
142 }
143
144 static void
145 finalize_static_data (MonoClass *class, MonoVTable *vtable, GHashTable *todo) {
146
147         if (class->enumtype || !vtable->data)
148                 return;
149         finalize_fields (class, vtable->data, FALSE, todo);
150 }
151
152 void
153 mono_domain_finalize (MonoDomain *domain) {
154
155         GHashTable *todo = g_hash_table_new (NULL, NULL);
156 #if HAVE_BOEHM_GC
157         GC_gcollect ();
158 #endif
159         mono_g_hash_table_foreach (domain->class_vtable_hash, (GHFunc)finalize_static_data, todo);
160         /* FIXME: finalize objects in todo... */
161         g_hash_table_destroy (todo);
162 }
163
164 void
165 ves_icall_System_GC_InternalCollect (int generation)
166 {
167 #if HAVE_BOEHM_GC
168         GC_gcollect ();
169 #endif
170 }
171
172 gint64
173 ves_icall_System_GC_GetTotalMemory (MonoBoolean forceCollection)
174 {
175 #if HAVE_BOEHM_GC
176         if (forceCollection)
177                 GC_gcollect ();
178         return GC_get_heap_size ();
179 #else
180         return 0;
181 #endif
182 }
183
184 void
185 ves_icall_System_GC_KeepAlive (MonoObject *obj)
186 {
187         /*
188          * Does nothing.
189          */
190 }
191
192 void
193 ves_icall_System_GC_ReRegisterForFinalize (MonoObject *obj)
194 {
195         object_register_finalizer (obj, run_finalize);
196 }
197
198 void
199 ves_icall_System_GC_SuppressFinalize (MonoObject *obj)
200 {
201         object_register_finalizer (obj, NULL);
202 }
203
204 void
205 ves_icall_System_GC_WaitForPendingFinalizers (void)
206 {
207 }
208
209 /*static CRITICAL_SECTION handle_section;*/
210 static guint32 next_handle = 0;
211 static gpointer *gc_handles = NULL;
212 static guint32 array_size = 0;
213
214 /*
215  * The handle type is encoded in the lower two bits of the handle value:
216  * 0 -> normal
217  * 1 -> pinned
218  * 2 -> weak
219  */
220
221 /*
222  * FIXME: make thread safe and reuse the array entries.
223  */
224 MonoObject *
225 ves_icall_System_GCHandle_GetTarget (guint32 handle)
226 {
227         MonoObject *obj;
228
229         if (gc_handles) {
230                 obj = gc_handles [handle >> 2];
231                 if ((handle & 0x3) > 1)
232                         return REVEAL_POINTER (obj);
233                 return obj;
234         }
235         return NULL;
236 }
237
238 guint32
239 ves_icall_System_GCHandle_GetTargetHandle (MonoObject *obj, guint32 handle, gint32 type)
240 {
241         gpointer val = obj;
242         guint32 h, idx = next_handle++;
243
244         if (idx >= array_size) {
245 #if HAVE_BOEHM_GC
246                 gpointer *new_array;
247                 if (!array_size)
248                         array_size = 16;
249                 new_array = GC_malloc (sizeof (gpointer) * (array_size * 2));
250                 if (gc_handles) {
251                         int i;
252                         memcpy (new_array, gc_handles, sizeof (gpointer) * array_size);
253                         /* need to re-register links for weak refs. test if GC_realloc needs the same */
254                         for (i = 0; i < array_size; ++i) {
255                                 if (((gulong)new_array [i]) & 0x1) { /* all and only disguised pointers have it set */
256                                         GC_general_register_disappearing_link (&(new_array [i]), REVEAL_POINTER (new_array [i]));
257                                 }
258                         }
259                 }
260                 array_size *= 2;
261                 gc_handles = new_array;
262 #else
263                 g_error ("No GCHandle support built-in");
264 #endif
265         }
266         h = idx << 2;
267
268         /* resuse the type from the old target */
269         if (type == -1)
270                 type =  handle & 0x3;
271         switch (type) {
272         case 0:
273         case 1:
274                 h |= type;
275                 gc_handles [idx] = val;
276                 break;
277         default:
278                 h |= 2;
279                 val = (gpointer)HIDE_POINTER (val);
280                 gc_handles [idx] = val;
281 #if HAVE_BOEHM_GC
282                 GC_general_register_disappearing_link (&(gc_handles [idx]), obj);
283 #else
284                 g_error ("No weakref support");
285 #endif
286                 break;
287         }
288         return h;
289 }
290
291 void
292 ves_icall_System_GCHandle_FreeHandle (guint32 handle)
293 {
294         gc_handles [handle >> 2] = (gpointer)-1;
295 }
296
297 gpointer
298 ves_icall_System_GCHandle_GetAddrOfPinnedObject (guint32 handle)
299 {
300         MonoObject *obj;
301
302         if (gc_handles) {
303                 obj = gc_handles [handle >> 2];
304                 if ((handle & 0x3) > 1)
305                         return REVEAL_POINTER (obj);
306                 return obj;
307         }
308         return NULL;
309 }
310
311