1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3  * Copyright (C) 2017 Red Hat, Inc. (www.redhat.com)
4  *
5  * This library is free software: you can redistribute it and/or modify it
6  * under the terms of the GNU Lesser General Public License as published by
7  * the Free Software Foundation.
8  *
9  * This library is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11  * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
12  * for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public License
15  * along with this library. If not, see <http://www.gnu.org/licenses/>.
16  */
17 
18 /**
19  * SECTION: camel-weak-ref-group
20  * @include: camel/camel.h
21  * @short_description: A weak ref group
22  *
23  * A #GWeakRef as such is not suitable for large sets, because
24  * it causes big performance impact on free. This #CamelWeakRefGroup
25  * groups together weak references for the same object to minimize
26  * the performance issue of the #GWeakRef.
27  **/
28 
29 #include "evolution-data-server-config.h"
30 
31 #include <glib.h>
32 
33 #include "camel-weak-ref-group.h"
34 
35 struct _CamelWeakRefGroup {
36 	guint ref_count;
37 	gpointer object;
38 };
39 
40 G_DEFINE_BOXED_TYPE (CamelWeakRefGroup, camel_weak_ref_group, camel_weak_ref_group_ref, camel_weak_ref_group_unref)
41 
42 typedef struct _ObjectData {
43 	guint64 use_count;
44 	GWeakRef weakref;
45 } ObjectData;
46 
47 static GHashTable *groups = NULL; /* gpointer ~> ObjectData */
48 G_LOCK_DEFINE_STATIC (groups);
49 
50 static ObjectData *
object_data_new(gpointer object)51 object_data_new (gpointer object)
52 {
53 	ObjectData *od;
54 
55 	od = g_slice_new (ObjectData);
56 	od->use_count = 1;
57 
58 	g_weak_ref_init (&od->weakref, object);
59 
60 	return od;
61 }
62 
63 static void
object_data_free(gpointer ptr)64 object_data_free (gpointer ptr)
65 {
66 	ObjectData *od = ptr;
67 
68 	if (od) {
69 		g_warn_if_fail (od->use_count == 0);
70 		g_weak_ref_set (&od->weakref, NULL);
71 		g_weak_ref_clear (&od->weakref);
72 		g_slice_free (ObjectData, od);
73 	}
74 }
75 
76 /**
77  * camel_weak_ref_group_new:
78  *
79  * Returns: (transfer full): A new #CamelWeakRefGroup instance, which should
80  *    be freed with camel_weak_ref_group_unref() when no longer needed.
81  *
82  * Since: 3.24
83  **/
84 CamelWeakRefGroup *
camel_weak_ref_group_new(void)85 camel_weak_ref_group_new (void)
86 {
87 	CamelWeakRefGroup *wrg;
88 
89 	wrg = g_slice_new (CamelWeakRefGroup);
90 	wrg->ref_count = 1;
91 	wrg->object = NULL;
92 
93 	return wrg;
94 }
95 
96 /**
97  * camel_weak_ref_group_ref:
98  * @group: a #CamelWeakRefGroup
99  *
100  * Increases a reference count of the @group.
101  *
102  * Returns: the @group
103  *
104  * Since: 3.24
105  **/
106 CamelWeakRefGroup *
camel_weak_ref_group_ref(CamelWeakRefGroup * group)107 camel_weak_ref_group_ref (CamelWeakRefGroup *group)
108 {
109 	g_return_val_if_fail (group != NULL, NULL);
110 
111 	G_LOCK (groups);
112 
113 	group->ref_count++;
114 
115 	G_UNLOCK (groups);
116 
117 	return group;
118 }
119 
120 /**
121  * camel_weak_ref_group_unref:
122  * @group: a #CamelWeakRefGroup
123  *
124  * Decreases a reference count of the @group. The @group is
125  * freed when the reference count reaches zero.
126  *
127  * Since: 3.24
128  **/
129 void
camel_weak_ref_group_unref(CamelWeakRefGroup * group)130 camel_weak_ref_group_unref (CamelWeakRefGroup *group)
131 {
132 	g_return_if_fail (group != NULL);
133 	g_return_if_fail (group->ref_count > 0);
134 
135 	G_LOCK (groups);
136 
137 	group->ref_count--;
138 
139 	G_UNLOCK (groups);
140 
141 	if (!group->ref_count) {
142 		camel_weak_ref_group_set (group, NULL);
143 		g_slice_free (CamelWeakRefGroup, group);
144 	}
145 }
146 
147 /**
148  * camel_weak_ref_group_set:
149  * @group: a #CamelWeakRefGroup
150  * @object: (nullable): a #GObject descendant, or %NULL
151  *
152  * Sets the @object as the object help by this @group. If
153  * the @object is %NULL, then unsets any previously set.
154  *
155  * Since: 3.24
156  **/
157 void
camel_weak_ref_group_set(CamelWeakRefGroup * group,gpointer object)158 camel_weak_ref_group_set (CamelWeakRefGroup *group,
159 			  gpointer object)
160 {
161 	g_return_if_fail (group != NULL);
162 	g_return_if_fail (!object || G_IS_OBJECT (object));
163 
164 	G_LOCK (groups);
165 
166 	if (object != group->object) {
167 		ObjectData *od;
168 
169 		if (group->object) {
170 			od = g_hash_table_lookup (groups, group->object);
171 
172 			g_warn_if_fail (od != NULL);
173 
174 			if (od) {
175 				od->use_count--;
176 				if (!od->use_count)
177 					g_hash_table_remove (groups, group->object);
178 			}
179 		} else if (!groups) {
180 			groups = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, object_data_free);
181 		}
182 
183 		group->object = object;
184 
185 		if (group->object) {
186 			od = g_hash_table_lookup (groups, group->object);
187 			if (od) {
188 				od->use_count++;
189 			} else {
190 				od = object_data_new (group->object);
191 				g_hash_table_insert (groups, group->object, od);
192 			}
193 		}
194 
195 		if (groups && !g_hash_table_size (groups)) {
196 			g_hash_table_destroy (groups);
197 			groups = NULL;
198 		}
199 	}
200 
201 	G_UNLOCK (groups);
202 }
203 
204 /**
205  * camel_weak_ref_group_get:
206  * @group: a #CamelWeakRefGroup
207  *
208  * Returns: (transfer full): A referenced object associated with @group,
209  *    or %NULL, when no object had been set to it. Use g_object_unref()
210  *    to free it, when no longer needed.
211  *
212  * Since: 3.24
213  **/
214 gpointer
camel_weak_ref_group_get(CamelWeakRefGroup * group)215 camel_weak_ref_group_get (CamelWeakRefGroup *group)
216 {
217 	gpointer object = NULL;
218 
219 	g_return_val_if_fail (group != NULL, NULL);
220 
221 	G_LOCK (groups);
222 
223 	if (group->object) {
224 		ObjectData *od = g_hash_table_lookup (groups, group->object);
225 
226 		g_warn_if_fail (od != NULL);
227 
228 		object = g_weak_ref_get (&od->weakref);
229 	}
230 
231 	G_UNLOCK (groups);
232 
233 	return object;
234 }
235