2 * Copyright © 2015 Canonical Limited
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, see <http://www.gnu.org/licenses/>.
17 * Author: Ryan Lortie <desrt@desrt.ca>
22 #include "gcontextspecificgroup.h"
24 #include <glib-object.h>
25 #include "glib-private.h"
34 } GContextSpecificSource
;
37 g_context_specific_source_dispatch (GSource
*source
,
41 GContextSpecificSource
*css
= (GContextSpecificSource
*) source
;
44 g_mutex_lock (&css
->lock
);
46 g_assert (!g_queue_is_empty (&css
->pending
));
47 signal_id
= GPOINTER_TO_UINT (g_queue_pop_head (&css
->pending
));
49 if (g_queue_is_empty (&css
->pending
))
50 g_source_set_ready_time (source
, -1);
52 g_mutex_unlock (&css
->lock
);
54 g_signal_emit (css
->instance
, signal_id
, 0);
60 g_context_specific_source_finalize (GSource
*source
)
62 GContextSpecificSource
*css
= (GContextSpecificSource
*) source
;
64 g_mutex_clear (&css
->lock
);
65 g_queue_clear (&css
->pending
);
68 static GContextSpecificSource
*
69 g_context_specific_source_new (const gchar
*name
,
72 static GSourceFuncs source_funcs
= {
75 g_context_specific_source_dispatch
,
76 g_context_specific_source_finalize
78 GContextSpecificSource
*css
;
81 source
= g_source_new (&source_funcs
, sizeof (GContextSpecificSource
));
82 css
= (GContextSpecificSource
*) source
;
84 g_source_set_name (source
, name
);
86 g_mutex_init (&css
->lock
);
87 g_queue_init (&css
->pending
);
88 css
->instance
= instance
;
94 g_context_specific_group_change_state (gpointer user_data
)
96 GContextSpecificGroup
*group
= user_data
;
98 g_mutex_lock (&group
->lock
);
100 if (group
->requested_state
!= group
->effective_state
)
102 (* group
->requested_func
) ();
104 group
->effective_state
= group
->requested_state
;
105 group
->requested_func
= NULL
;
107 g_cond_broadcast (&group
->cond
);
110 g_mutex_unlock (&group
->lock
);
115 /* this is not the most elegant way to deal with this, but it's probably
116 * the best. there are only two other things we could do, really:
118 * - run the start function (but not the stop function) from the user's
119 * thread under some sort of lock. we don't run the stop function
120 * from the user's thread to avoid the destroy-while-emitting problem
122 * - have some check-and-compare functionality similar to what
123 * gsettings does where we send an artificial event in case we notice
124 * a change during the potential race period (using stat, for
128 g_context_specific_group_request_state (GContextSpecificGroup
*group
,
129 gboolean requested_state
,
130 GCallback requested_func
)
132 if (requested_state
!= group
->requested_state
)
134 if (group
->effective_state
!= group
->requested_state
)
136 /* abort the currently pending state transition */
137 g_assert (group
->effective_state
== requested_state
);
139 group
->requested_state
= requested_state
;
140 group
->requested_func
= NULL
;
144 /* start a new state transition */
145 group
->requested_state
= requested_state
;
146 group
->requested_func
= requested_func
;
148 g_main_context_invoke (GLIB_PRIVATE_CALL(g_get_worker_context
) (),
149 g_context_specific_group_change_state
, group
);
153 /* we only block for positive transitions */
156 while (group
->requested_state
!= group
->effective_state
)
157 g_cond_wait (&group
->cond
, &group
->lock
);
159 /* there is no way this could go back to FALSE because the object
160 * that we just created in this thread would have to have been
161 * destroyed again (from this thread) before that could happen.
163 g_assert (group
->effective_state
);
168 g_context_specific_group_get (GContextSpecificGroup
*group
,
170 goffset context_offset
,
171 GCallback start_func
)
173 GContextSpecificSource
*css
;
174 GMainContext
*context
;
176 context
= g_main_context_get_thread_default ();
178 context
= g_main_context_default ();
180 g_mutex_lock (&group
->lock
);
183 group
->table
= g_hash_table_new (NULL
, NULL
);
185 css
= g_hash_table_lookup (group
->table
, context
);
191 instance
= g_object_new (type
, NULL
);
192 css
= g_context_specific_source_new (g_type_name (type
), instance
);
193 G_STRUCT_MEMBER (GMainContext
*, instance
, context_offset
) = g_main_context_ref (context
);
194 g_source_attach ((GSource
*) css
, context
);
196 g_hash_table_insert (group
->table
, context
, css
);
199 g_object_ref (css
->instance
);
202 g_context_specific_group_request_state (group
, TRUE
, start_func
);
204 g_mutex_unlock (&group
->lock
);
206 return css
->instance
;
210 g_context_specific_group_remove (GContextSpecificGroup
*group
,
211 GMainContext
*context
,
215 GContextSpecificSource
*css
;
219 g_critical ("Removing %s with NULL context. This object was probably directly constructed from a "
220 "dynamic language. This is not a valid use of the API.", G_OBJECT_TYPE_NAME (instance
));
224 g_mutex_lock (&group
->lock
);
225 css
= g_hash_table_lookup (group
->table
, context
);
226 g_hash_table_remove (group
->table
, context
);
229 /* stop only if we were the last one */
230 if (stop_func
&& g_hash_table_size (group
->table
) == 0)
231 g_context_specific_group_request_state (group
, FALSE
, stop_func
);
233 g_mutex_unlock (&group
->lock
);
235 g_assert (css
->instance
== instance
);
237 g_source_destroy ((GSource
*) css
);
238 g_source_unref ((GSource
*) css
);
239 g_main_context_unref (context
);
243 g_context_specific_group_emit (GContextSpecificGroup
*group
,
246 g_mutex_lock (&group
->lock
);
254 ptr
= GUINT_TO_POINTER (signal_id
);
256 g_hash_table_iter_init (&iter
, group
->table
);
257 while (g_hash_table_iter_next (&iter
, NULL
, &value
))
259 GContextSpecificSource
*css
= value
;
261 g_mutex_lock (&css
->lock
);
263 g_queue_remove (&css
->pending
, ptr
);
264 g_queue_push_tail (&css
->pending
, ptr
);
266 g_source_set_ready_time ((GSource
*) css
, 0);
268 g_mutex_unlock (&css
->lock
);
272 g_mutex_unlock (&group
->lock
);