Branch data Line data Source code
1 : : /*
2 : : * Copyright © 2015 Canonical Limited
3 : : *
4 : : * SPDX-License-Identifier: LGPL-2.1-or-later
5 : : *
6 : : * This library is free software; you can redistribute it and/or
7 : : * modify it under the terms of the GNU Lesser General Public
8 : : * License as published by the Free Software Foundation; either
9 : : * version 2.1 of the License, or (at your option) any later version.
10 : : *
11 : : * This library is distributed in the hope that it will be useful,
12 : : * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 : : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 : : * Lesser General Public License for more details.
15 : : *
16 : : * You should have received a copy of the GNU Lesser General Public
17 : : * License along with this library; if not, see <http://www.gnu.org/licenses/>.
18 : : *
19 : : * Author: Ryan Lortie <desrt@desrt.ca>
20 : : */
21 : :
22 : : #include "config.h"
23 : :
24 : : #include "gcontextspecificgroup.h"
25 : :
26 : : #include <glib-object.h>
27 : : #include "glib-private.h"
28 : :
29 : : typedef struct
30 : : {
31 : : GSource source;
32 : :
33 : : GMutex lock;
34 : : gpointer instance;
35 : : GQueue pending;
36 : : } GContextSpecificSource;
37 : :
38 : : static gboolean
39 : 14419 : g_context_specific_source_dispatch (GSource *source,
40 : : GSourceFunc callback,
41 : : gpointer user_data)
42 : : {
43 : 14419 : GContextSpecificSource *css = (GContextSpecificSource *) source;
44 : : guint signal_id;
45 : :
46 : 14419 : g_mutex_lock (&css->lock);
47 : :
48 : 14419 : g_assert (!g_queue_is_empty (&css->pending));
49 : 14419 : signal_id = GPOINTER_TO_UINT (g_queue_pop_head (&css->pending));
50 : :
51 : 14419 : if (g_queue_is_empty (&css->pending))
52 : 14419 : g_source_set_ready_time (source, -1);
53 : :
54 : 14419 : g_mutex_unlock (&css->lock);
55 : :
56 : 14419 : g_signal_emit (css->instance, signal_id, 0);
57 : :
58 : 14419 : return TRUE;
59 : : }
60 : :
61 : : static void
62 : 23 : g_context_specific_source_finalize (GSource *source)
63 : : {
64 : 23 : GContextSpecificSource *css = (GContextSpecificSource *) source;
65 : :
66 : 23 : g_mutex_clear (&css->lock);
67 : 23 : g_queue_clear (&css->pending);
68 : 23 : }
69 : :
70 : : static GContextSpecificSource *
71 : 23 : g_context_specific_source_new (const gchar *name,
72 : : gpointer instance)
73 : : {
74 : : static GSourceFuncs source_funcs = {
75 : : NULL,
76 : : NULL,
77 : : g_context_specific_source_dispatch,
78 : : g_context_specific_source_finalize,
79 : : NULL, NULL
80 : : };
81 : : GContextSpecificSource *css;
82 : : GSource *source;
83 : :
84 : 23 : source = g_source_new (&source_funcs, sizeof (GContextSpecificSource));
85 : 23 : css = (GContextSpecificSource *) source;
86 : :
87 : 23 : g_source_set_name (source, name);
88 : :
89 : 23 : g_mutex_init (&css->lock);
90 : 23 : g_queue_init (&css->pending);
91 : 23 : css->instance = instance;
92 : :
93 : 23 : return css;
94 : : }
95 : :
96 : : static gboolean
97 : 10 : g_context_specific_group_change_state (gpointer user_data)
98 : : {
99 : 10 : GContextSpecificGroup *group = user_data;
100 : :
101 : 10 : g_mutex_lock (&group->lock);
102 : :
103 : 10 : if (group->requested_state != group->effective_state)
104 : : {
105 : 10 : (* group->requested_func) ();
106 : :
107 : 10 : group->effective_state = group->requested_state;
108 : 10 : group->requested_func = NULL;
109 : :
110 : 10 : g_cond_broadcast (&group->cond);
111 : : }
112 : :
113 : 10 : g_mutex_unlock (&group->lock);
114 : :
115 : 10 : return FALSE;
116 : : }
117 : :
118 : : /* this is not the most elegant way to deal with this, but it's probably
119 : : * the best. there are only two other things we could do, really:
120 : : *
121 : : * - run the start function (but not the stop function) from the user's
122 : : * thread under some sort of lock. we don't run the stop function
123 : : * from the user's thread to avoid the destroy-while-emitting problem
124 : : *
125 : : * - have some check-and-compare functionality similar to what
126 : : * gsettings does where we send an artificial event in case we notice
127 : : * a change during the potential race period (using stat, for
128 : : * example)
129 : : */
130 : : static void
131 : 1027 : g_context_specific_group_request_state (GContextSpecificGroup *group,
132 : : gboolean requested_state,
133 : : GCallback requested_func)
134 : : {
135 : 1027 : if (requested_state != group->requested_state)
136 : : {
137 : 10 : if (group->effective_state != group->requested_state)
138 : : {
139 : : /* abort the currently pending state transition */
140 : 0 : g_assert (group->effective_state == requested_state);
141 : :
142 : 0 : group->requested_state = requested_state;
143 : 0 : group->requested_func = NULL;
144 : : }
145 : : else
146 : : {
147 : : /* start a new state transition */
148 : 10 : group->requested_state = requested_state;
149 : 10 : group->requested_func = requested_func;
150 : :
151 : 10 : g_main_context_invoke (GLIB_PRIVATE_CALL(g_get_worker_context) (),
152 : : g_context_specific_group_change_state, group);
153 : : }
154 : : }
155 : :
156 : 1039 : while (group->requested_state != group->effective_state)
157 : 12 : g_cond_wait (&group->cond, &group->lock);
158 : 1027 : }
159 : :
160 : : gpointer
161 : 1023 : g_context_specific_group_get (GContextSpecificGroup *group,
162 : : GType type,
163 : : goffset context_offset,
164 : : GCallback start_func)
165 : : {
166 : : GContextSpecificSource *css;
167 : : GMainContext *context;
168 : :
169 : 1023 : context = g_main_context_get_thread_default ();
170 : 1023 : if (!context)
171 : 3 : context = g_main_context_default ();
172 : :
173 : 1023 : g_mutex_lock (&group->lock);
174 : :
175 : 1023 : if (!group->table)
176 : 3 : group->table = g_hash_table_new (NULL, NULL);
177 : :
178 : 1023 : css = g_hash_table_lookup (group->table, context);
179 : :
180 : 1023 : if (!css)
181 : : {
182 : : gpointer instance;
183 : :
184 : 23 : instance = g_object_new (type, NULL);
185 : 23 : css = g_context_specific_source_new (g_type_name (type), instance);
186 : 23 : G_STRUCT_MEMBER (GMainContext *, instance, context_offset) = g_main_context_ref (context);
187 : 23 : g_source_attach ((GSource *) css, context);
188 : :
189 : 23 : g_hash_table_insert (group->table, context, css);
190 : : }
191 : : else
192 : 1000 : g_object_ref (css->instance);
193 : :
194 : 1023 : if (start_func)
195 : 1022 : g_context_specific_group_request_state (group, TRUE, start_func);
196 : :
197 : 1023 : g_mutex_unlock (&group->lock);
198 : :
199 : 1023 : return css->instance;
200 : : }
201 : :
202 : : void
203 : 23 : g_context_specific_group_remove (GContextSpecificGroup *group,
204 : : GMainContext *context,
205 : : gpointer instance,
206 : : GCallback stop_func)
207 : : {
208 : : GContextSpecificSource *css;
209 : :
210 : 23 : if (!context)
211 : : {
212 : 0 : g_critical ("Removing %s with NULL context. This object was probably directly constructed from a "
213 : : "dynamic language. This is not a valid use of the API.", G_OBJECT_TYPE_NAME (instance));
214 : 0 : return;
215 : : }
216 : :
217 : 23 : g_mutex_lock (&group->lock);
218 : 23 : css = g_hash_table_lookup (group->table, context);
219 : 23 : g_hash_table_remove (group->table, context);
220 : 23 : g_assert (css);
221 : :
222 : : /* stop only if we were the last one */
223 : 23 : if (stop_func && g_hash_table_size (group->table) == 0)
224 : 5 : g_context_specific_group_request_state (group, FALSE, stop_func);
225 : :
226 : 23 : g_mutex_unlock (&group->lock);
227 : :
228 : 23 : g_assert (css->instance == instance);
229 : :
230 : 23 : g_source_destroy ((GSource *) css);
231 : 23 : g_source_unref ((GSource *) css);
232 : 23 : g_main_context_unref (context);
233 : : }
234 : :
235 : : void
236 : 2572 : g_context_specific_group_emit (GContextSpecificGroup *group,
237 : : guint signal_id)
238 : : {
239 : 2572 : g_mutex_lock (&group->lock);
240 : :
241 : 2572 : if (group->table)
242 : : {
243 : : GHashTableIter iter;
244 : : gpointer value;
245 : : gpointer ptr;
246 : :
247 : 2222 : ptr = GUINT_TO_POINTER (signal_id);
248 : :
249 : 2222 : g_hash_table_iter_init (&iter, group->table);
250 : 24325 : while (g_hash_table_iter_next (&iter, NULL, &value))
251 : : {
252 : 22103 : GContextSpecificSource *css = value;
253 : :
254 : 22103 : g_mutex_lock (&css->lock);
255 : :
256 : 22103 : g_queue_remove (&css->pending, ptr);
257 : 22103 : g_queue_push_tail (&css->pending, ptr);
258 : :
259 : 22103 : g_source_set_ready_time ((GSource *) css, 0);
260 : :
261 : 22103 : g_mutex_unlock (&css->lock);
262 : : }
263 : : }
264 : :
265 : 2572 : g_mutex_unlock (&group->lock);
266 : 2572 : }
|