Merge pull request #1695 from gregoryyoung/master
[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-gc.h"
29 #include "sgen-toggleref.h"
30
31
32 /*only one of the two can be non null at a given time*/
33 typedef struct {
34         void *strong_ref;
35         void *weak_ref;
36 } MonoGCToggleRef;
37
38 static MonoToggleRefStatus (*toggleref_callback) (MonoObject *obj);
39 static MonoGCToggleRef *toggleref_array;
40 static int toggleref_array_size;
41 static int toggleref_array_capacity;
42
43 void
44 sgen_process_togglerefs (void)
45 {
46         int i, w;
47         int toggle_ref_counts [3] = { 0, 0, 0 };
48
49         SGEN_LOG (4, "Proccessing ToggleRefs %d", toggleref_array_size);
50
51         for (i = w = 0; i < toggleref_array_size; ++i) {
52                 int res;
53                 MonoGCToggleRef r = toggleref_array [i];
54
55                 MonoObject *obj;
56
57                 if (r.strong_ref)
58                         obj = r.strong_ref;
59                 else if (r.weak_ref)
60                         obj = r.weak_ref;
61                 else
62                         continue;
63
64                 res = toggleref_callback (obj);
65                 ++toggle_ref_counts [res];
66                 switch (res) {
67                 case MONO_TOGGLE_REF_DROP:
68                         break;
69                 case MONO_TOGGLE_REF_STRONG:
70                         toggleref_array [w].strong_ref = obj;
71                         toggleref_array [w].weak_ref = NULL;
72                         ++w;
73                         break;
74                 case MONO_TOGGLE_REF_WEAK:
75                         toggleref_array [w].strong_ref = NULL;
76                         toggleref_array [w].weak_ref = obj;
77                         ++w;
78                         break;
79                 default:
80                         g_assert_not_reached ();
81                 }
82         }
83
84         toggleref_array_size = w;
85
86         SGEN_LOG (4, "Done Proccessing ToggleRefs dropped %d strong %d weak %d final size %d",
87                 toggle_ref_counts [MONO_TOGGLE_REF_DROP],
88                 toggle_ref_counts [MONO_TOGGLE_REF_STRONG],
89                 toggle_ref_counts [MONO_TOGGLE_REF_WEAK],
90                 w);
91 }
92
93 void sgen_mark_togglerefs (char *start, char *end, ScanCopyContext ctx)
94 {
95         CopyOrMarkObjectFunc copy_func = ctx.copy_func;
96         SgenGrayQueue *queue = ctx.queue;
97         int i;
98
99         SGEN_LOG (4, "Marking ToggleRefs %d", toggleref_array_size);
100
101         for (i = 0; i < toggleref_array_size; ++i) {
102                 if (toggleref_array [i].strong_ref) {
103                         char *object = toggleref_array [i].strong_ref;
104                         if (object >= start && object < end) {
105                                 SGEN_LOG (6, "\tcopying strong slot %d", i);
106                                 copy_func (&toggleref_array [i].strong_ref, queue);
107                         }
108                 }
109         }
110         sgen_drain_gray_stack (-1, ctx);
111 }
112
113 void sgen_clear_togglerefs (char *start, char *end, ScanCopyContext ctx)
114 {
115         CopyOrMarkObjectFunc copy_func = ctx.copy_func;
116         SgenGrayQueue *queue = ctx.queue;
117         int i;
118
119         SGEN_LOG (4, "Clearing ToggleRefs %d", toggleref_array_size);
120
121         for (i = 0; i < toggleref_array_size; ++i) {
122                 if (toggleref_array [i].weak_ref) {
123                         char *object = toggleref_array [i].weak_ref;
124
125                         if (object >= start && object < end) {
126                                 if (sgen_gc_is_object_ready_for_finalization (object)) {
127                                         SGEN_LOG (6, "\tcleaning weak slot %d", i);
128                                         toggleref_array [i].weak_ref = NULL; /* We defer compaction to only happen on the callback step. */
129                                 } else {
130                                         SGEN_LOG (6, "\tkeeping weak slot %d", i);
131                                         copy_func (&toggleref_array [i].weak_ref, queue);
132                                 }
133                         }
134                 }
135         }
136         sgen_drain_gray_stack (-1, ctx);
137 }
138
139 static void
140 ensure_toggleref_capacity (int capacity)
141 {
142         if (!toggleref_array) {
143                 toggleref_array_capacity = 32;
144                 toggleref_array = sgen_alloc_internal_dynamic (
145                         toggleref_array_capacity * sizeof (MonoGCToggleRef),
146                         INTERNAL_MEM_TOGGLEREF_DATA,
147                         TRUE);
148         }
149         if (toggleref_array_size + capacity >= toggleref_array_capacity) {
150                 MonoGCToggleRef *tmp;
151                 int old_capacity = toggleref_array_capacity;
152                 while (toggleref_array_capacity < toggleref_array_size + capacity)
153                         toggleref_array_capacity *= 2;
154
155                 tmp = sgen_alloc_internal_dynamic (
156                         toggleref_array_capacity * sizeof (MonoGCToggleRef),
157                         INTERNAL_MEM_TOGGLEREF_DATA,
158                         TRUE);
159
160                 memcpy (tmp, toggleref_array, toggleref_array_size * sizeof (MonoGCToggleRef));
161
162                 sgen_free_internal_dynamic (toggleref_array, old_capacity * sizeof (MonoGCToggleRef), INTERNAL_MEM_TOGGLEREF_DATA);
163                 toggleref_array = tmp;
164         }
165 }
166
167 /**
168  * mono_gc_toggleref_add:
169  * @object object to register for toggleref processing
170  * @strong_ref if true the object is registered with a strong ref, a weak one otherwise
171  *
172  * Register a given object for toggleref processing. It will be stored internally and the toggleref callback will be called
173  * on it until it returns MONO_TOGGLE_REF_DROP or is collected.
174 */
175 void
176 mono_gc_toggleref_add (MonoObject *object, mono_bool strong_ref)
177 {
178         if (!toggleref_callback)
179                 return;
180
181         SGEN_LOG (4, "Adding toggleref %p %d", object, strong_ref);
182
183         sgen_gc_lock ();
184
185         ensure_toggleref_capacity (1);
186         toggleref_array [toggleref_array_size].strong_ref = strong_ref ? object : NULL;
187         toggleref_array [toggleref_array_size].weak_ref = strong_ref ? NULL : object;
188         ++toggleref_array_size;
189
190         sgen_gc_unlock ();
191 }
192
193 /**
194  * mono_gc_toggleref_register_callback:
195  * @callback callback used to determine the new state of the given object.
196  *
197  * The callback must decide the status of a given object. It must return one of the values in the MONO_TOGGLE_REF_ enum.
198  * This function is called with the world running but with the GC locked. This means that you can do everything that doesn't
199  * require GC interaction. This includes, but not limited to, allocating objects, (de)registering for finalization, manipulating
200  *gchandles, storing to reference fields or interacting with other threads that might perform such operations.
201  */
202 void
203 mono_gc_toggleref_register_callback (MonoToggleRefStatus (*proccess_toggleref) (MonoObject *obj))
204 {
205         toggleref_callback = proccess_toggleref;
206 }
207
208 static MonoToggleRefStatus
209 test_toggleref_callback (MonoObject *obj)
210 {
211         static MonoClassField *mono_toggleref_test_field;
212         int status = MONO_TOGGLE_REF_DROP;
213
214         if (!mono_toggleref_test_field) {
215                 mono_toggleref_test_field = mono_class_get_field_from_name (mono_object_get_class (obj), "__test");
216                 g_assert (mono_toggleref_test_field);
217         }
218
219         mono_field_get_value (obj, mono_toggleref_test_field, &status);
220         printf ("toggleref-cb obj %d\n", status);
221         return status;
222 }
223
224 void
225 sgen_register_test_toggleref_callback (void)
226 {
227         toggleref_callback = test_toggleref_callback;
228 }
229
230 #endif