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