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