1 /*
2  * Copyright (C) 2009 - 2011 Vivien Malerba <malerba@gnome-db.org>
3  *
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 of the License, or (at your option) any later version.
8  *
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.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
17  * Boston, MA  02110-1301, USA.
18  */
19 
20 #ifndef __GDA_THREAD_WRAPPER_H__
21 #define __GDA_THREAD_WRAPPER_H__
22 
23 #include <glib-object.h>
24 
25 G_BEGIN_DECLS
26 
27 #define GDA_TYPE_THREAD_WRAPPER            (gda_thread_wrapper_get_type())
28 #define GDA_THREAD_WRAPPER(obj)            (G_TYPE_CHECK_INSTANCE_CAST (obj, GDA_TYPE_THREAD_WRAPPER, GdaThreadWrapper))
29 #define GDA_THREAD_WRAPPER_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST (klass, GDA_TYPE_THREAD_WRAPPER, GdaThreadWrapperClass))
30 #define GDA_IS_THREAD_WRAPPER(obj)         (G_TYPE_CHECK_INSTANCE_TYPE(obj, GDA_TYPE_THREAD_WRAPPER))
31 #define GDA_IS_THREAD_WRAPPER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), GDA_TYPE_THREAD_WRAPPER))
32 #define GDA_THREAD_WRAPPER_GET_CLASS(o)    (G_TYPE_INSTANCE_GET_CLASS ((o), GDA_TYPE_THREAD_WRAPPER, GdaThreadWrapperClass))
33 
34 typedef struct _GdaThreadWrapper GdaThreadWrapper;
35 typedef struct _GdaThreadWrapperClass GdaThreadWrapperClass;
36 typedef struct _GdaThreadWrapperPrivate GdaThreadWrapperPrivate;
37 
38 /* error reporting */
39 extern GQuark gda_thread_wrapper_error_quark (void);
40 #define GDA_THREAD_WRAPPER_ERROR gda_thread_wrapper_error_quark ()
41 
42 typedef enum {
43 	GDA_THREAD_WRAPPER_UNKNOWN_ERROR
44 } GdaThreadWrapperError;
45 
46 struct _GdaThreadWrapper {
47 	GObject            object;
48 	GdaThreadWrapperPrivate *priv;
49 };
50 
51 struct _GdaThreadWrapperClass {
52 	GObjectClass       object_class;
53 
54 	/*< private >*/
55 	/* Padding for future expansion */
56 	void (*_gda_reserved1) (void);
57 	void (*_gda_reserved2) (void);
58 	void (*_gda_reserved3) (void);
59 	void (*_gda_reserved4) (void);
60 };
61 
62 /**
63  * GdaThreadNotificationType:
64  * @GDA_THREAD_NOTIFICATION_SIGNAL: the notification regards a signal
65  * @GDA_THREAD_NOTIFICATION_JOB: the notification regards a job finished
66  *
67  * Defines the kind of notification which can be obtained when reading from te #GIOChannel
68  * returned by gda_thread_wrapper_get_io_channel().
69  */
70 typedef enum {
71 	GDA_THREAD_NOTIFICATION_JOB    = 0x01,
72 	GDA_THREAD_NOTIFICATION_SIGNAL = 0x02
73 } GdaThreadNotificationType;
74 
75 /**
76  * GdaThreadNotification:
77  * @type: the notification type
78  * @job_id: the job ID, if @type is a #GDA_THREAD_NOTIFICATION_JOB
79  *
80  * A notification to be read through the #GIOChannel which is returned by gda_thread_wrapper_get_io_channel(),
81  * for example:
82  * <programlisting><![CDATA[
83  * gboolean
84  * wrapper_ioc_cb (GIOChannel *source, GIOCondition condition, gpointer data)
85  * {
86  *     GIOStatus status;
87  *     gsize nread;
88  *     GdaThreadNotification notif;
89  *     if (condition & G_IO_IN) {
90  *	   status = g_io_channel_read_chars (source, (gchar*) &notif, sizeof (notif), &nread, NULL);
91  *         if ((status != G_IO_STATUS_NORMAL) || (nread != sizeof (notif)))
92  *             goto onerror;
93  *	   switch (notif.type) {
94  *	   case GDA_THREAD_NOTIFICATION_JOB:
95  *             check_for_wrapper_result (bcnc);
96  *             break;
97  *         case GDA_THREAD_NOTIFICATION_SIGNAL:
98  *             gda_thread_wrapper_iterate (bcnc->priv->wrapper, FALSE);
99  *             break;
100  *         default:
101  *             goto onerror;
102  *             break;
103  *	   }
104  *   }
105  *   if (condition & (G_IO_ERR | G_IO_HUP | G_IO_NVAL))
106  *             goto onerror;
107  *   return TRUE; // keep callback
108  *
109  * onerror:
110  *   g_io_channel_shutdown (bcnc->priv->ioc, FALSE, NULL);
111  *   return FALSE; // removed callback
112  * }
113  *
114  * {
115  * [...]
116  *     GIOChannel *ioc;
117  *     ioc = gda_thread_wrapper_get_io_channel (wrapper);
118  *     if (!ioc)
119  *         [handle error]
120  *     else {
121  *         guint watch_id;
122  *         watch_id = g_io_add_watch (ioc, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
123  *                                    (GIOFunc) wrapper_ioc_cb, NULL);
124  *     }
125  * }
126  * ]]></programlisting>
127  */
128 typedef struct {
129 	GdaThreadNotificationType type;
130 	guint                     job_id;
131 } GdaThreadNotification;
132 
133 /**
134  * GdaThreadWrapperFunc:
135  * @arg: pointer to the data (which is the @arg argument passed to gda_thread_wrapper_execute_void())
136  * @error: a place to store errors
137  * @Returns: a pointer to some data which will be returned by gda_thread_wrapper_fetch_result()
138  *
139  * Specifies the type of function to be passed to gda_thread_wrapper_execute().
140  */
141 typedef gpointer (*GdaThreadWrapperFunc) (gpointer arg, GError **error);
142 
143 /**
144  * GdaThreadWrapperVoidFunc:
145  * @arg: a pointer to the data (which is the @arg argument passed to gda_thread_wrapper_execute_void())
146  * @error: a place to store errors
147  *
148  * Specifies the type of function to be passed to gda_thread_wrapper_execute_void().
149  */
150 typedef void (*GdaThreadWrapperVoidFunc) (gpointer arg, GError **error);
151 
152 /**
153  * GdaThreadWrapperCallback:
154  * @wrapper: the #GdaThreadWrapper
155  * @instance: a pointer to the instance which emitted the signal
156  * @signame: the name of the signal being emitted
157  * @n_param_values: number of GValue in @param_values
158  * @param_values: array of @n_param_values GValue
159  * @gda_reserved: reserved
160  * @data: a pointer to the data (which is the @data argument passed to gda_thread_wrapper_connect_raw())
161  *
162  * Specifies the type of function to be passed to gda_thread_wrapper_connect_raw()
163  */
164 typedef void (*GdaThreadWrapperCallback) (GdaThreadWrapper *wrapper, gpointer instance, const gchar *signame,
165 					  gint n_param_values, const GValue *param_values, gpointer gda_reserved,
166 					  gpointer data);
167 
168 /**
169  * SECTION:gda-thread-wrapper
170  * @short_description: Execute functions in a sub thread
171  * @title: GdaThreadWrapper
172  * @stability: Stable
173  * @see_also:
174  *
175  * The purpose of the #GdaThreadWrapper object is to execute functions in an isolated sub thread. As the
176  * #GdaThreadWrapper is thread safe, one is able to isolate some code's execution is a <emphasis>private</emphasis>
177  * <emphasis>worker</emphasis> thread, and make a non thread safe code thread safe.
178  *
179  * The downside of this is that the actual execution of the code will be slower as it requires
180  * threads to be synchronized.
181  *
182  * The #GdaThreadWrapper implements its own locking mechanism and can safely be used from multiple
183  * threads at once without needing further locking.
184  *
185  * Each thread using a #GdaThreadWrapper object can use it as if it was the only user: the #GdaThreadWrapper will
186  * simply dispatch all the execution requests to its private <emphasis>worker</emphasis> thread and report the
187  * execution's status only to the thread which made the request.
188  *
189  * The user can also specify a callback function to be called when an object emits a signal while being
190  * used by the worker thread, see the gda_thread_wrapper_connect_raw() method.
191  *
192  * The following diagram illustrates the conceptual working of the #GdaThreadWrapper object: here two user threads
193  * are represented (assigned a red and green colors), both using a single #GdaThreadWrapper, so in this diagram, 3 threads
194  * are present. The communication between the threads are handled by some #GAsyncQueue objects (in a transparent way for
195  * the user, presented here only for illustration purposes). The queue represented in yellow is where jobs are
196  * pushed by each user thread (step 1), and popped by the worker thread (step 2). Once the worker thread has finished
197  * with a job, it stores the result along with the job and pushes it to the queue dedicated to the user thread
198  * (step 3) in this example the red queue (because the job was issued from the thread represented in red). The last
199  * step is when the user fetches the result (in its user thread), step 4.
200  *
201  * If, when the worker thread is busy with a job, a signal is emitted, and if the user has set up a signal handler
202  * using gda_thread_wrapper_connect_raw(),
203  * then a "job as signal" is created by the worker thread and pushed to the user thread as illustrated
204  * at the bottom of the diagram.
205  * <mediaobject>
206  *   <imageobject role="html">
207  *     <imagedata fileref="thread-wrapper.png" format="PNG" contentwidth="170mm"/>
208  *   </imageobject>
209  *   <textobject>
210  *     <phrase>GdaThreadWrapper's conceptual working</phrase>
211  *   </textobject>
212  * </mediaobject>
213  *
214  * It's the user's responsability to regularly check if a submitted job has completed
215  * (using gda_thread_wrapper_fetch_result()) or if a signal
216  * has been emitted by an object while in the worker thread and needs to be handled by the #GdaThreadWrapper
217  * to call the associated callback (using gda_thread_wrapper_iterate() or automatically done when
218  * calling gda_thread_wrapper_fetch_result()).
219  *
220  * However, when a main loop is available, the #GdaThreadWrapper can emit notifications through a
221  * #GIOChannel which can be intregated in the main loop. The #GIOChannel can be created
222  * using gda_thread_wrapper_get_io_channel().
223  */
224 
225 GType                  gda_thread_wrapper_get_type          (void) G_GNUC_CONST;
226 GdaThreadWrapper      *gda_thread_wrapper_new               (void);
227 GIOChannel            *gda_thread_wrapper_get_io_channel    (GdaThreadWrapper *wrapper);
228 void                   gda_thread_wrapper_unset_io_channel  (GdaThreadWrapper *wrapper);
229 
230 guint                  gda_thread_wrapper_execute           (GdaThreadWrapper *wrapper, GdaThreadWrapperFunc func,
231 							     gpointer arg, GDestroyNotify arg_destroy_func, GError **error);
232 guint                  gda_thread_wrapper_execute_void      (GdaThreadWrapper *wrapper, GdaThreadWrapperVoidFunc func,
233 							     gpointer arg, GDestroyNotify arg_destroy_func, GError **error);
234 gboolean               gda_thread_wrapper_cancel            (GdaThreadWrapper *wrapper, guint id);
235 void                   gda_thread_wrapper_iterate           (GdaThreadWrapper *wrapper, gboolean may_block);
236 gpointer               gda_thread_wrapper_fetch_result      (GdaThreadWrapper *wrapper, gboolean may_lock,
237 							     guint exp_id, GError **error);
238 
239 gint                   gda_thread_wrapper_get_waiting_size (GdaThreadWrapper *wrapper);
240 
241 gulong                 gda_thread_wrapper_connect_raw       (GdaThreadWrapper *wrapper,
242 							     gpointer instance,
243 							     const gchar *sig_name,
244 							     gboolean private_thread, gboolean private_job,
245 							     GdaThreadWrapperCallback callback,
246 							     gpointer data);
247 void                   gda_thread_wrapper_disconnect       (GdaThreadWrapper *wrapper, gulong id);
248 void                   gda_thread_wrapper_steal_signal     (GdaThreadWrapper *wrapper, gulong id);
249 G_END_DECLS
250 
251 #endif
252