d9afbfa868dc9efe8a6a2eb0a31c151bb82215e2
[mono.git] / mono / metadata / sgen-toggleref.c
1 /*
2  * sgen-toggleref.c: toggleref support for sgen
3  *
4  * Author:
5  *  Rodrigo Kumpera (kumpera@gmail.com)
6  *
7  * Copyright 2011 Xamarin, Inc.
8  * Copyright (C) 2012 Xamarin Inc
9  *
10  * This library is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU Library General Public
12  * License 2.0 as published by the Free Software Foundation;
13  *
14  * This library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Library General Public License for more details.
18  *
19  * You should have received a copy of the GNU Library General Public
20  * License 2.0 along with this library; if not, write to the Free
21  * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22  */
23
24 #include "config.h"
25
26 #ifdef HAVE_SGEN_GC
27
28 #include "sgen/sgen-gc.h"
29 #include "sgen-toggleref.h"
30 #include "sgen/sgen-client.h"
31
32
33 /*only one of the two can be non null at a given time*/
34 typedef struct {
35         GCObject *strong_ref;
36         GCObject *weak_ref;
37 } MonoGCToggleRef;
38
39 static MonoToggleRefStatus (*toggleref_callback) (MonoObject *obj);
40 static MonoGCToggleRef *toggleref_array;
41 static int toggleref_array_size;
42 static int toggleref_array_capacity;
43
44 void
45 sgen_process_togglerefs (void)
46 {
47         int i, w;
48         int toggle_ref_counts [3] = { 0, 0, 0 };
49
50         SGEN_LOG (4, "Proccessing ToggleRefs %d", toggleref_array_size);
51
52         for (i = w = 0; i < toggleref_array_size; ++i) {
53                 int res;
54                 MonoGCToggleRef r = toggleref_array [i];
55
56                 MonoObject *obj;
57
58                 if (r.strong_ref)
59                         obj = r.strong_ref;
60                 else if (r.weak_ref)
61                         obj = r.weak_ref;
62                 else
63                         continue;
64
65                 res = toggleref_callback (obj);
66                 ++toggle_ref_counts [res];
67                 switch (res) {
68                 case MONO_TOGGLE_REF_DROP:
69                         break;
70                 case MONO_TOGGLE_REF_STRONG:
71                         toggleref_array [w].strong_ref = obj;
72                         toggleref_array [w].weak_ref = NULL;
73                         ++w;
74                         break;
75                 case MONO_TOGGLE_REF_WEAK:
76                         toggleref_array [w].strong_ref = NULL;
77                         toggleref_array [w].weak_ref = obj;
78                         ++w;
79                         break;
80                 default:
81                         g_assert_not_reached ();
82                 }
83         }
84
85         toggleref_array_size = w;
86
87         SGEN_LOG (4, "Done Proccessing ToggleRefs dropped %d strong %d weak %d final size %d",
88                 toggle_ref_counts [MONO_TOGGLE_REF_DROP],
89                 toggle_ref_counts [MONO_TOGGLE_REF_STRONG],
90                 toggle_ref_counts [MONO_TOGGLE_REF_WEAK],
91                 w);
92 }
93
94 void sgen_client_mark_togglerefs (char *start, char *end, ScanCopyContext ctx)
95 {
96         CopyOrMarkObjectFunc copy_func = ctx.ops->copy_or_mark_object;
97         SgenGrayQueue *queue = ctx.queue;
98         int i;
99
100         SGEN_LOG (4, "Marking ToggleRefs %d", toggleref_array_size);
101
102         for (i = 0; i < toggleref_array_size; ++i) {
103                 if (toggleref_array [i].strong_ref) {
104                         GCObject *object = toggleref_array [i].strong_ref;
105                         if ((char*)object >= start && (char*)object < end) {
106                                 SGEN_LOG (6, "\tcopying strong slot %d", i);
107                                 copy_func (&toggleref_array [i].strong_ref, queue);
108                         }
109                 }
110         }
111         sgen_drain_gray_stack (ctx);
112 }
113
114 void sgen_client_clear_togglerefs (char *start, char *end, ScanCopyContext ctx)
115 {
116         CopyOrMarkObjectFunc copy_func = ctx.ops->copy_or_mark_object;
117         SgenGrayQueue *queue = ctx.queue;
118         int i;
119
120         SGEN_LOG (4, "Clearing ToggleRefs %d", toggleref_array_size);
121
122         for (i = 0; i < toggleref_array_size; ++i) {
123                 if (toggleref_array [i].weak_ref) {
124                         GCObject *object = toggleref_array [i].weak_ref;
125
126                         if ((char*)object >= start && (char*)object < end) {
127                                 if (sgen_gc_is_object_ready_for_finalization (object)) {
128                                         SGEN_LOG (6, "\tcleaning weak slot %d", i);
129                                         toggleref_array [i].weak_ref = NULL; /* We defer compaction to only happen on the callback step. */
130                                 } else {
131                                         SGEN_LOG (6, "\tkeeping weak slot %d", i);
132                                         copy_func (&toggleref_array [i].weak_ref, queue);
133                                 }
134                         }
135                 }
136         }
137         sgen_drain_gray_stack (ctx);
138 }
139
140 static void
141 ensure_toggleref_capacity (int capacity)
142 {
143         if (!toggleref_array) {
144                 toggleref_array_capacity = 32;
145                 toggleref_array = (MonoGCToggleRef *)sgen_alloc_internal_dynamic (
146                         toggleref_array_capacity * sizeof (MonoGCToggleRef),
147                         INTERNAL_MEM_TOGGLEREF_DATA,
148                         TRUE);
149         }
150         if (toggleref_array_size + capacity >= toggleref_array_capacity) {
151                 MonoGCToggleRef *tmp;
152                 int old_capacity = toggleref_array_capacity;
153                 while (toggleref_array_capacity < toggleref_array_size + capacity)
154                         toggleref_array_capacity *= 2;
155
156                 tmp = (MonoGCToggleRef *)sgen_alloc_internal_dynamic (
157                         toggleref_array_capacity * sizeof (MonoGCToggleRef),
158                         INTERNAL_MEM_TOGGLEREF_DATA,
159                         TRUE);
160
161                 memcpy (tmp, toggleref_array, toggleref_array_size * sizeof (MonoGCToggleRef));
162
163                 sgen_free_internal_dynamic (toggleref_array, old_capacity * sizeof (MonoGCToggleRef), INTERNAL_MEM_TOGGLEREF_DATA);
164                 toggleref_array = tmp;
165         }
166 }
167
168 /**
169  * mono_gc_toggleref_add:
170  * @object object to register for toggleref processing
171  * @strong_ref if true the object is registered with a strong ref, a weak one otherwise
172  *
173  * Register a given object for toggleref processing. It will be stored internally and the toggleref callback will be called
174  * on it until it returns MONO_TOGGLE_REF_DROP or is collected.
175 */
176 void
177 mono_gc_toggleref_add (MonoObject *object, mono_bool strong_ref)
178 {
179         if (!toggleref_callback)
180                 return;
181
182         SGEN_LOG (4, "Adding toggleref %p %d", object, strong_ref);
183
184         sgen_gc_lock ();
185
186         ensure_toggleref_capacity (1);
187         toggleref_array [toggleref_array_size].strong_ref = strong_ref ? object : NULL;
188         toggleref_array [toggleref_array_size].weak_ref = strong_ref ? NULL : object;
189         ++toggleref_array_size;
190
191         sgen_gc_unlock ();
192 }
193
194 /**
195  * mono_gc_toggleref_register_callback:
196  * @callback callback used to determine the new state of the given object.
197  *
198  * The callback must decide the status of a given object. It must return one of the values in the MONO_TOGGLE_REF_ enum.
199  * This function is called with the world running but with the GC locked. This means that you can do everything that doesn't
200  * require GC interaction. This includes, but not limited to, allocating objects, (de)registering for finalization, manipulating
201  *gchandles, storing to reference fields or interacting with other threads that might perform such operations.
202  */
203 void
204 mono_gc_toggleref_register_callback (MonoToggleRefStatus (*proccess_toggleref) (MonoObject *obj))
205 {
206         toggleref_callback = proccess_toggleref;
207 }
208
209 static MonoToggleRefStatus
210 test_toggleref_callback (MonoObject *obj)
211 {
212         static MonoClassField *mono_toggleref_test_field;
213         MonoToggleRefStatus status = MONO_TOGGLE_REF_DROP;
214
215         if (!mono_toggleref_test_field) {
216                 mono_toggleref_test_field = mono_class_get_field_from_name (mono_object_get_class (obj), "__test");
217                 g_assert (mono_toggleref_test_field);
218         }
219
220         mono_field_get_value (obj, mono_toggleref_test_field, &status);
221         printf ("toggleref-cb obj %d\n", status);
222         return status;
223 }
224
225 void
226 sgen_register_test_toggleref_callback (void)
227 {
228         toggleref_callback = test_toggleref_callback;
229 }
230
231 #endif