tizen: kdbus: revamp thread deinitialization
[platform/upstream/glib.git] / gio / gioscheduler.c
1 /* GIO - GLib Input, Output and Streaming Library
2  * 
3  * Copyright (C) 2006-2007 Red Hat, Inc.
4  *
5  * SPDX-License-Identifier: LGPL-2.1-or-later
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General
18  * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
19  *
20  * Author: Alexander Larsson <alexl@redhat.com>
21  */
22
23 #include "config.h"
24
25 #include "gioscheduler.h"
26 #include "gcancellable.h"
27 #include "gtask.h"
28
29 struct _GIOSchedulerJob {
30   GList *active_link;
31   GTask *task;
32
33   GIOSchedulerJobFunc job_func;
34   gpointer data;
35   GDestroyNotify destroy_notify;
36
37   GCancellable *cancellable;
38   gulong cancellable_id;
39   GMainContext *context;
40 };
41
42 G_LOCK_DEFINE_STATIC(active_jobs);
43 static GList *active_jobs = NULL;
44
45 static void
46 g_io_job_free (GIOSchedulerJob *job)
47 {
48   if (job->destroy_notify)
49     job->destroy_notify (job->data);
50
51   G_LOCK (active_jobs);
52   active_jobs = g_list_delete_link (active_jobs, job->active_link);
53   G_UNLOCK (active_jobs);
54
55   if (job->cancellable)
56     g_object_unref (job->cancellable);
57   g_main_context_unref (job->context);
58   g_slice_free (GIOSchedulerJob, job);
59 }
60
61 static void
62 io_job_thread (GTask         *task,
63                gpointer       source_object,
64                gpointer       task_data,
65                GCancellable  *cancellable)
66 {
67   GIOSchedulerJob *job = task_data;
68   gboolean result;
69
70   if (job->cancellable)
71     g_cancellable_push_current (job->cancellable);
72
73   do 
74     {
75       result = job->job_func (job, job->cancellable, job->data);
76     }
77   while (result);
78
79   if (job->cancellable)
80     g_cancellable_pop_current (job->cancellable);
81 }
82
83 /**
84  * g_io_scheduler_push_job:
85  * @job_func: a #GIOSchedulerJobFunc.
86  * @user_data: data to pass to @job_func
87  * @notify: (nullable): a #GDestroyNotify for @user_data, or %NULL
88  * @io_priority: the [I/O priority][io-priority]
89  * of the request.
90  * @cancellable: optional #GCancellable object, %NULL to ignore.
91  *
92  * Schedules the I/O job to run in another thread.
93  *
94  * @notify will be called on @user_data after @job_func has returned,
95  * regardless whether the job was cancelled or has run to completion.
96  * 
97  * If @cancellable is not %NULL, it can be used to cancel the I/O job
98  * by calling g_cancellable_cancel() or by calling 
99  * g_io_scheduler_cancel_all_jobs().
100  *
101  * Deprecated: 2.36: use #GThreadPool or g_task_run_in_thread()
102  **/
103 void
104 g_io_scheduler_push_job (GIOSchedulerJobFunc  job_func,
105                          gpointer             user_data,
106                          GDestroyNotify       notify,
107                          gint                 io_priority,
108                          GCancellable        *cancellable)
109 {
110   GIOSchedulerJob *job;
111   GTask *task;
112
113   g_return_if_fail (job_func != NULL);
114
115   job = g_slice_new0 (GIOSchedulerJob);
116   job->job_func = job_func;
117   job->data = user_data;
118   job->destroy_notify = notify;
119
120   if (cancellable)
121     job->cancellable = g_object_ref (cancellable);
122
123   job->context = g_main_context_ref_thread_default ();
124
125   G_LOCK (active_jobs);
126   active_jobs = g_list_prepend (active_jobs, job);
127   job->active_link = active_jobs;
128   G_UNLOCK (active_jobs);
129
130   task = g_task_new (NULL, cancellable, NULL, NULL);
131 G_GNUC_BEGIN_IGNORE_DEPRECATIONS
132   g_task_set_source_tag (task, g_io_scheduler_push_job);
133 G_GNUC_END_IGNORE_DEPRECATIONS
134   g_task_set_task_data (task, job, (GDestroyNotify)g_io_job_free);
135   g_task_set_priority (task, io_priority);
136   g_task_run_in_thread (task, io_job_thread);
137   g_object_unref (task);
138 }
139
140 /**
141  * g_io_scheduler_cancel_all_jobs:
142  * 
143  * Cancels all cancellable I/O jobs. 
144  *
145  * A job is cancellable if a #GCancellable was passed into
146  * g_io_scheduler_push_job().
147  *
148  * Deprecated: 2.36: You should never call this function, since you don't
149  * know how other libraries in your program might be making use of
150  * gioscheduler.
151  **/
152 void
153 g_io_scheduler_cancel_all_jobs (void)
154 {
155   GList *cancellable_list, *l;
156   
157   G_LOCK (active_jobs);
158   cancellable_list = NULL;
159   for (l = active_jobs; l != NULL; l = l->next)
160     {
161       GIOSchedulerJob *job = l->data;
162       if (job->cancellable)
163         cancellable_list = g_list_prepend (cancellable_list,
164                                            g_object_ref (job->cancellable));
165     }
166   G_UNLOCK (active_jobs);
167
168   for (l = cancellable_list; l != NULL; l = l->next)
169     {
170       GCancellable *c = l->data;
171       g_cancellable_cancel (c);
172       g_object_unref (c);
173     }
174   g_list_free (cancellable_list);
175 }
176
177 typedef struct {
178   GSourceFunc func;
179   gboolean ret_val;
180   gpointer data;
181   GDestroyNotify notify;
182
183   GMutex ack_lock;
184   GCond ack_condition;
185   gboolean ack;
186 } MainLoopProxy;
187
188 static gboolean
189 mainloop_proxy_func (gpointer data)
190 {
191   MainLoopProxy *proxy = data;
192
193   proxy->ret_val = proxy->func (proxy->data);
194
195   if (proxy->notify)
196     proxy->notify (proxy->data);
197
198   g_mutex_lock (&proxy->ack_lock);
199   proxy->ack = TRUE;
200   g_cond_signal (&proxy->ack_condition);
201   g_mutex_unlock (&proxy->ack_lock);
202
203   return FALSE;
204 }
205
206 static void
207 mainloop_proxy_free (MainLoopProxy *proxy)
208 {
209   g_mutex_clear (&proxy->ack_lock);
210   g_cond_clear (&proxy->ack_condition);
211   g_free (proxy);
212 }
213
214 /**
215  * g_io_scheduler_job_send_to_mainloop:
216  * @job: a #GIOSchedulerJob
217  * @func: a #GSourceFunc callback that will be called in the original thread
218  * @user_data: data to pass to @func
219  * @notify: (nullable): a #GDestroyNotify for @user_data, or %NULL
220  * 
221  * Used from an I/O job to send a callback to be run in the thread
222  * that the job was started from, waiting for the result (and thus
223  * blocking the I/O job).
224  *
225  * Returns: The return value of @func
226  *
227  * Deprecated: 2.36: Use g_main_context_invoke().
228  **/
229 gboolean
230 g_io_scheduler_job_send_to_mainloop (GIOSchedulerJob *job,
231                                      GSourceFunc      func,
232                                      gpointer         user_data,
233                                      GDestroyNotify   notify)
234 {
235   GSource *source;
236   MainLoopProxy *proxy;
237   gboolean ret_val;
238
239   g_return_val_if_fail (job != NULL, FALSE);
240   g_return_val_if_fail (func != NULL, FALSE);
241
242   proxy = g_new0 (MainLoopProxy, 1);
243   proxy->func = func;
244   proxy->data = user_data;
245   proxy->notify = notify;
246   g_mutex_init (&proxy->ack_lock);
247   g_cond_init (&proxy->ack_condition);
248   g_mutex_lock (&proxy->ack_lock);
249
250   source = g_idle_source_new ();
251   g_source_set_priority (source, G_PRIORITY_DEFAULT);
252   g_source_set_callback (source, mainloop_proxy_func, proxy,
253                          NULL);
254   g_source_set_static_name (source, "[gio] mainloop_proxy_func");
255
256   g_source_attach (source, job->context);
257   g_source_unref (source);
258
259   while (!proxy->ack)
260     g_cond_wait (&proxy->ack_condition, &proxy->ack_lock);
261   g_mutex_unlock (&proxy->ack_lock);
262
263   ret_val = proxy->ret_val;
264   mainloop_proxy_free (proxy);
265   
266   return ret_val;
267 }
268
269 /**
270  * g_io_scheduler_job_send_to_mainloop_async:
271  * @job: a #GIOSchedulerJob
272  * @func: a #GSourceFunc callback that will be called in the original thread
273  * @user_data: data to pass to @func
274  * @notify: (nullable): a #GDestroyNotify for @user_data, or %NULL
275  * 
276  * Used from an I/O job to send a callback to be run asynchronously in
277  * the thread that the job was started from. The callback will be run
278  * when the main loop is available, but at that time the I/O job might
279  * have finished. The return value from the callback is ignored.
280  *
281  * Note that if you are passing the @user_data from g_io_scheduler_push_job()
282  * on to this function you have to ensure that it is not freed before
283  * @func is called, either by passing %NULL as @notify to 
284  * g_io_scheduler_push_job() or by using refcounting for @user_data.
285  *
286  * Deprecated: 2.36: Use g_main_context_invoke().
287  **/
288 void
289 g_io_scheduler_job_send_to_mainloop_async (GIOSchedulerJob *job,
290                                            GSourceFunc      func,
291                                            gpointer         user_data,
292                                            GDestroyNotify   notify)
293 {
294   GSource *source;
295   MainLoopProxy *proxy;
296
297   g_return_if_fail (job != NULL);
298   g_return_if_fail (func != NULL);
299
300   proxy = g_new0 (MainLoopProxy, 1);
301   proxy->func = func;
302   proxy->data = user_data;
303   proxy->notify = notify;
304   g_mutex_init (&proxy->ack_lock);
305   g_cond_init (&proxy->ack_condition);
306
307   source = g_idle_source_new ();
308   g_source_set_priority (source, G_PRIORITY_DEFAULT);
309   g_source_set_callback (source, mainloop_proxy_func, proxy,
310                          (GDestroyNotify)mainloop_proxy_free);
311   g_source_set_static_name (source, "[gio] mainloop_proxy_func");
312
313   g_source_attach (source, job->context);
314   g_source_unref (source);
315 }