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