4 * Copyright (c) 2015 Red Hat, Inc.
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, see <http://www.gnu.org/licenses/>.
24 typedef struct QIOTask QIOTask
;
26 typedef void (*QIOTaskFunc
)(QIOTask
*task
,
29 typedef void (*QIOTaskWorker
)(QIOTask
*task
,
35 * The QIOTask object provides a simple mechanism for reporting
36 * success / failure of long running background operations.
38 * A object on which the operation is to be performed could have
39 * a public API which accepts a task callback:
42 * <title>Task function signature</title>
44 * void myobject_operation(QMyObject *obj,
47 * GDestroyNotify notify);
51 * The 'func' parameter is the callback to be invoked, and 'opaque'
52 * is data to pass to it. The optional 'notify' function is used
53 * to free 'opaque' when no longer needed.
55 * When the operation completes, the 'func' callback will be
56 * invoked, allowing the calling code to determine the result
57 * of the operation. An example QIOTaskFunc implementation may
61 * <title>Task callback implementation</title>
63 * static void myobject_operation_notify(QIOTask *task,
67 * if (qio_task_propagate_error(task, &err)) {
68 * ...deal with the failure...
71 * QMyObject *src = QMY_OBJECT(qio_task_get_source(task));
72 * ...deal with the completion...
78 * Now, lets say the implementation of the method using the
79 * task wants to set a timer to run once a second checking
80 * for completion of some activity. It would do something
84 * <title>Task function implementation</title>
86 * void myobject_operation(QMyObject *obj,
89 * GDestroyNotify notify)
93 * task = qio_task_new(OBJECT(obj), func, opaque, notify);
95 * g_timeout_add_full(G_PRIORITY_DEFAULT,
97 * myobject_operation_timer,
104 * It could equally have setup a watch on a file descriptor or
105 * created a background thread, or something else entirely.
106 * Notice that the source object is passed to the task, and
107 * QIOTask will hold a reference on that. This ensure that
108 * the QMyObject instance cannot be garbage collected while
109 * the async task is still in progress.
111 * In this case, myobject_operation_timer will fire after
115 * <title>Task timer function</title>
117 * gboolean myobject_operation_timer(gpointer opaque)
119 * QIOTask *task = QIO_TASK(opaque);
122 * ...check something important...
124 * qio_task_set_error(task, err);
125 * qio_task_complete(task);
127 * } else if (...work is completed ...) {
128 * qio_task_complete(task);
131 * ...carry on polling ...
137 * The 'qio_task_complete' call in this method will trigger
138 * the callback func 'myobject_operation_notify' shown
139 * earlier to deal with the results.
141 * Once this function returns false, object_unref will be called
142 * automatically on the task causing it to be released and the
143 * ref on QMyObject dropped too.
145 * The QIOTask module can also be used to perform operations
146 * in a background thread context, while still reporting the
147 * results in the main event thread. This allows code which
148 * cannot easily be rewritten to be asynchronous (such as DNS
149 * lookups) to be easily run non-blocking. Reporting the
150 * results in the main thread context means that the caller
151 * typically does not need to be concerned about thread
152 * safety wrt the BQL.
154 * For example, the socket_listen() method will block the caller
155 * while DNS lookups take place if given a name, instead of IP
156 * address. The C library often do not provide a practical async
157 * DNS API, so the to get non-blocking DNS lookups in a portable
158 * manner requires use of a thread. So achieve a non-blocking
159 * socket listen using QIOTask would require:
162 * static void myobject_listen_worker(QIOTask *task,
165 * QMyObject obj = QMY_OBJECT(qio_task_get_source(task));
166 * SocketAddress *addr = opaque;
169 * obj->fd = socket_listen(addr, &err);
171 qio_task_set_error(task, err);
174 * void myobject_listen_async(QMyObject *obj,
175 * SocketAddress *addr,
178 * GDestroyNotify notify)
181 * SocketAddress *addrCopy;
183 * addrCopy = QAPI_CLONE(SocketAddress, addr);
184 * task = qio_task_new(OBJECT(obj), func, opaque, notify);
186 * qio_task_run_in_thread(task, myobject_listen_worker,
188 * qapi_free_SocketAddress);
192 * NB, The 'func' callback passed into myobject_listen_async
193 * will be invoked from the main event thread, despite the
194 * actual operation being performed in a different thread.
199 * @source: the object on which the operation is invoked
200 * @func: the callback to invoke when the task completes
201 * @opaque: opaque data to pass to @func when invoked
202 * @destroy: optional callback to free @opaque
204 * Creates a new task struct to track completion of a
205 * background operation running on the object @source.
206 * When the operation completes or fails, the callback
207 * @func will be invoked. The callback can access the
208 * 'err' attribute in the task object to determine if
209 * the operation was successful or not.
211 * The returned task will be released when qio_task_complete()
214 * Returns: the task struct
216 QIOTask
*qio_task_new(Object
*source
,
219 GDestroyNotify destroy
);
222 * qio_task_run_in_thread:
223 * @task: the task struct
224 * @worker: the function to invoke in a thread
225 * @opaque: opaque data to pass to @worker
226 * @destroy: function to free @opaque
227 * @context: the context to run the complete hook. If %NULL, the
228 * default context will be used.
230 * Run a task in a background thread. When @worker
231 * returns it will call qio_task_complete() in
232 * the thread that is running the main loop associated
235 void qio_task_run_in_thread(QIOTask
*task
,
236 QIOTaskWorker worker
,
238 GDestroyNotify destroy
,
239 GMainContext
*context
);
243 * qio_task_wait_thread:
244 * @task: the task struct
246 * Wait for completion of a task that was previously
247 * invoked using qio_task_run_in_thread. This MUST
248 * ONLY be invoked if the task has not already
249 * completed, since after the completion callback
250 * is invoked, @task will have been freed.
252 * To avoid racing with execution of the completion
253 * callback provided with qio_task_new, this method
254 * MUST ONLY be invoked from the thread that is
255 * running the main loop associated with @context
256 * parameter to qio_task_run_in_thread.
258 * When the thread has completed, the completion
259 * callback provided to qio_task_new will be invoked.
260 * When that callback returns @task will be freed,
261 * so @task must not be referenced after this
264 void qio_task_wait_thread(QIOTask
*task
);
269 * @task: the task struct
271 * Invoke the completion callback for @task and
272 * then free its memory.
274 void qio_task_complete(QIOTask
*task
);
278 * qio_task_set_error:
279 * @task: the task struct
280 * @err: pointer to the error, or NULL
282 * Associate an error with the task, which can later
283 * be retrieved with the qio_task_propagate_error()
284 * method. This method takes ownership of @err, so
285 * it is not valid to access it after this call
286 * completes. If @err is NULL this is a no-op. If
287 * this is call multiple times, only the first
288 * provided @err will be recorded, later ones will
289 * be discarded and freed.
291 void qio_task_set_error(QIOTask
*task
,
296 * qio_task_propagate_error:
297 * @task: the task struct
298 * @errp: pointer to a NULL-initialized error object
300 * Propagate the error associated with @task
303 * Returns: true if an error was propagated, false otherwise
305 bool qio_task_propagate_error(QIOTask
*task
,
310 * qio_task_set_result_pointer:
311 * @task: the task struct
312 * @result: pointer to the result data
314 * Associate an opaque result with the task,
315 * which can later be retrieved with the
316 * qio_task_get_result_pointer() method
319 void qio_task_set_result_pointer(QIOTask
*task
,
321 GDestroyNotify notify
);
325 * qio_task_get_result_pointer:
326 * @task: the task struct
328 * Retrieve the opaque result data associated
329 * with the task, if any.
331 * Returns: the task result, or NULL
333 gpointer
qio_task_get_result_pointer(QIOTask
*task
);
337 * qio_task_get_source:
338 * @task: the task struct
340 * Get the source object associated with the background
341 * task. The caller does not own a reference on the
342 * returned Object, and so should call object_ref()
343 * if it wants to keep the object pointer outside the
344 * lifetime of the QIOTask object.
346 * Returns: the source object
348 Object
*qio_task_get_source(QIOTask
*task
);
350 #endif /* QIO_TASK_H */