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