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*) ¬if, 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