1
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
3
* Copyright (C) 2007-2010 David Zeuthen <zeuthen@gmail.com>
5
* This program is free software; you can redistribute it and/or modify
6
* it under the terms of the GNU General Public License as published by
7
* the Free Software Foundation; either version 2 of the License, or
8
* (at your option) any later version.
10
* This program is distributed in the hope that it will be useful,
11
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
* GNU General Public License for more details.
15
* You should have received a copy of the GNU General Public License
16
* along with this program; if not, write to the Free Software
17
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
22
#include <glib/gi18n-lib.h>
24
#include <sys/types.h>
27
#include "udisksbasejob.h"
28
#include "udisksthreadedjob.h"
29
#include "udisks-daemon-marshal.h"
30
#include "udisksdaemon.h"
33
* SECTION:udisksthreadedjob
34
* @title: UDisksThreadedJob
35
* @short_description: Job that runs in a thread
37
* This type provides an implementation of the #UDisksJob interface
38
* for jobs that run in a thread.
41
typedef struct _UDisksThreadedJobClass UDisksThreadedJobClass;
46
* The #UDisksThreadedJob structure contains only private data and should
47
* only be accessed using the provided API.
49
struct _UDisksThreadedJob
51
UDisksBaseJob parent_instance;
53
UDisksThreadedJobFunc job_func;
55
GDestroyNotify user_data_free_func;
61
struct _UDisksThreadedJobClass
63
UDisksBaseJobClass parent_class;
65
gboolean (*threaded_job_completed) (UDisksThreadedJob *job,
70
static void job_iface_init (UDisksJobIface *iface);
77
PROP_USER_DATA_FREE_FUNC
82
THREADED_JOB_COMPLETED_SIGNAL,
86
static gulong signals[LAST_SIGNAL] = { 0 };
88
static gboolean udisks_threaded_job_threaded_job_completed_default (UDisksThreadedJob *job,
92
G_DEFINE_TYPE_WITH_CODE (UDisksThreadedJob, udisks_threaded_job, UDISKS_TYPE_BASE_JOB,
93
G_IMPLEMENT_INTERFACE (UDISKS_TYPE_JOB, job_iface_init));
96
udisks_threaded_job_finalize (GObject *object)
98
UDisksThreadedJob *job = UDISKS_THREADED_JOB (object);
100
if (job->job_error != NULL)
101
g_error_free (job->job_error);
103
if (job->user_data_free_func != NULL)
104
job->user_data_free_func (job->user_data);
106
if (G_OBJECT_CLASS (udisks_threaded_job_parent_class)->finalize != NULL)
107
G_OBJECT_CLASS (udisks_threaded_job_parent_class)->finalize (object);
111
udisks_threaded_job_get_property (GObject *object,
116
UDisksThreadedJob *job = UDISKS_THREADED_JOB (object);
121
g_value_set_pointer (value, job->job_func);
125
g_value_set_pointer (value, job->user_data);
128
case PROP_USER_DATA_FREE_FUNC:
129
g_value_set_pointer (value, job->user_data_free_func);
133
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
139
udisks_threaded_job_set_property (GObject *object,
144
UDisksThreadedJob *job = UDISKS_THREADED_JOB (object);
149
g_assert (job->job_func == NULL);
150
job->job_func = g_value_get_pointer (value);
154
g_assert (job->user_data == NULL);
155
job->user_data = g_value_get_pointer (value);
158
case PROP_USER_DATA_FREE_FUNC:
159
g_assert (job->user_data_free_func == NULL);
160
job->user_data_free_func = g_value_get_pointer (value);
164
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
169
/* ---------------------------------------------------------------------------------------------------- */
172
job_complete (gpointer user_data)
174
UDisksThreadedJob *job = UDISKS_THREADED_JOB (user_data);
177
/* take a reference so it's safe for a signal-handler to release the last one */
180
signals[THREADED_JOB_COMPLETED_SIGNAL],
185
g_object_unref (job);
190
run_io_scheduler_job (GIOSchedulerJob *io_scheduler_job,
191
GCancellable *cancellable,
194
UDisksThreadedJob *job = UDISKS_THREADED_JOB (user_data);
196
/* TODO: probably want to create a GMainContext dedicated to the thread */
198
g_assert (!job->job_result);
199
g_assert_no_error (job->job_error);
201
if (!g_cancellable_set_error_if_cancelled (cancellable, &job->job_error))
203
job->job_result = job->job_func (job,
209
g_io_scheduler_job_send_to_mainloop (io_scheduler_job,
214
return FALSE; /* job is complete (or cancelled) */
218
udisks_threaded_job_constructed (GObject *object)
220
UDisksThreadedJob *job = UDISKS_THREADED_JOB (object);
222
if (G_OBJECT_CLASS (udisks_threaded_job_parent_class)->constructed != NULL)
223
G_OBJECT_CLASS (udisks_threaded_job_parent_class)->constructed (object);
225
g_assert (g_thread_supported ());
226
g_io_scheduler_push_job (run_io_scheduler_job,
230
udisks_base_job_get_cancellable (UDISKS_BASE_JOB (job)));
233
/* ---------------------------------------------------------------------------------------------------- */
236
udisks_threaded_job_init (UDisksThreadedJob *job)
241
udisks_threaded_job_class_init (UDisksThreadedJobClass *klass)
243
GObjectClass *gobject_class;
245
klass->threaded_job_completed = udisks_threaded_job_threaded_job_completed_default;
247
gobject_class = G_OBJECT_CLASS (klass);
248
gobject_class->finalize = udisks_threaded_job_finalize;
249
gobject_class->constructed = udisks_threaded_job_constructed;
250
gobject_class->set_property = udisks_threaded_job_set_property;
251
gobject_class->get_property = udisks_threaded_job_get_property;
254
* UDisksThreadedJob:job-func:
256
* The #UDisksThreadedJobFunc to use.
258
g_object_class_install_property (gobject_class,
260
g_param_spec_pointer ("job-func",
265
G_PARAM_CONSTRUCT_ONLY |
266
G_PARAM_STATIC_STRINGS));
269
* UDisksThreadedJob:user-data:
271
* User data for the #UDisksThreadedJobFunc.
273
g_object_class_install_property (gobject_class,
275
g_param_spec_pointer ("user-data",
276
"Job Function's user data",
277
"The Job Function user data",
280
G_PARAM_CONSTRUCT_ONLY |
281
G_PARAM_STATIC_STRINGS));
284
* UDisksThreadedJob:user-data-free-func:
286
* Free function for user data for the #UDisksThreadedJobFunc.
288
g_object_class_install_property (gobject_class,
289
PROP_USER_DATA_FREE_FUNC,
290
g_param_spec_pointer ("user-data-free-func",
291
"Job Function's user data free function",
292
"The Job Function user data free function",
295
G_PARAM_CONSTRUCT_ONLY |
296
G_PARAM_STATIC_STRINGS));
299
* UDisksThreadedJob::threaded-job-completed:
300
* @job: The #UDisksThreadedJob emitting the signal.
301
* @result: The #gboolean returned by the #UDisksThreadedJobFunc.
302
* @error: The #GError set by the #UDisksThreadedJobFunc.
304
* Emitted when the threaded job is complete.
306
* The default implementation simply emits the #UDisksJob::completed
307
* signal with @success set to %TRUE if, and only if, @error is
308
* %NULL. Otherwise, @message on that signal is set to a string
309
* describing @error. You can avoid the default implementation by
310
* returning %TRUE from your signal handler.
312
* This signal is emitted in the
313
* <link linkend="g-main-context-push-thread-default">thread-default main loop</link>
314
* of the thread that @job was created in.
316
* Returns: %TRUE if the signal was handled, %FALSE to let other
319
signals[THREADED_JOB_COMPLETED_SIGNAL] =
320
g_signal_new ("threaded-job-completed",
321
UDISKS_TYPE_THREADED_JOB,
323
G_STRUCT_OFFSET (UDisksThreadedJobClass, threaded_job_completed),
324
g_signal_accumulator_true_handled,
326
udisks_daemon_marshal_BOOLEAN__BOOLEAN_BOXED,
334
* udisks_threaded_job_new:
335
* @job_func: The function to run in another thread.
336
* @user_data: User data to pass to @job_func.
337
* @user_data_free_func: Function to free @user_data with or %NULL.
338
* @daemon: A #UDisksDaemon.
339
* @cancellable: A #GCancellable or %NULL.
341
* Creates a new #UDisksThreadedJob instance.
343
* The job is started immediately - connect to the
344
* #UDisksThreadedJob::threaded-job-completed or #UDisksJob::completed
345
* signals to get notified when the job is done.
347
* Returns: A new #UDisksThreadedJob. Free with g_object_unref().
350
udisks_threaded_job_new (UDisksThreadedJobFunc job_func,
352
GDestroyNotify user_data_free_func,
353
UDisksDaemon *daemon,
354
GCancellable *cancellable)
356
/* g_return_val_if_fail (UDISKS_IS_DAEMON (daemon), NULL); */
357
g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL);
358
return UDISKS_THREADED_JOB (g_object_new (UDISKS_TYPE_THREADED_JOB,
359
"job-func", job_func,
360
"user-data", user_data,
361
"user-data-free-func", user_data_free_func,
363
"cancellable", cancellable,
368
* udisks_threaded_job_get_user_data:
369
* @job: A #UDisksThreadedJob.
371
* Gets the @user_data parameter that @job was constructed with.
373
* Returns: A #gpointer owned by @job.
376
udisks_threaded_job_get_user_data (UDisksThreadedJob *job)
378
g_return_val_if_fail (UDISKS_IS_THREADED_JOB (job), NULL);
379
return job->user_data;
382
/* ---------------------------------------------------------------------------------------------------- */
385
job_iface_init (UDisksJobIface *iface)
387
/* For Cancel(), just use the implementation from our super class (UDisksBaseJob) */
388
/* iface->handle_cancel = handle_cancel; */
391
/* ---------------------------------------------------------------------------------------------------- */
394
udisks_threaded_job_threaded_job_completed_default (UDisksThreadedJob *job,
400
udisks_job_emit_completed (UDISKS_JOB (job),
408
g_assert (error != NULL);
410
message = g_string_new (NULL);
411
g_string_append_printf (message,
412
"Threaded job failed with error: %s (%s, %d)",
414
g_quark_to_string (error->domain),
416
udisks_job_emit_completed (UDISKS_JOB (job),
419
g_string_free (message, TRUE);