int test3 (void) { g_print ("Test3 started, main thread is %p\n", g_thread_self ()); GdaWorker *worker; gint nfailed = 0; worker = gda_worker_new (); guint jid; GError *error = NULL; Data3 *data; data = g_new (Data3, 1); data->counter = 5; jid = gda_worker_submit_job (worker, NULL, (GdaWorkerFunc) test3_worker_func, data, (GDestroyNotify) data3_free, NULL, &error); if (jid == 0) { g_print ("Error in gda_worker_submit_job(): %s\n", error && error->message ? error->message : "no detail"); g_clear_error (&error); nfailed++; goto out; } while (1) { gint *result; if (! gda_worker_fetch_job_result (worker, jid, (gpointer*) &result, &error)) { g_print ("Still not done, error: %s\n", error && error->message ? error->message : "no detail"); if ((error->domain == GDA_WORKER_ERROR) && (error->code == GDA_WORKER_JOB_NOT_FOUND_ERROR)) { nfailed++; g_clear_error (&error); break; } g_clear_error (&error); } else { if (result) { g_print ("Got result value: %d\n", *result); g_free (result); } else { g_print ("Error: got no result value!\n"); nfailed++; } break; } g_usleep (100000); } out: gda_worker_unref (worker); return nfailed; }
static gboolean dc_callback (ITSignaler *its, DeclaredCallback *dc) { WorkerJob *job; job = itsignaler_pop_notification (its, 0); g_assert (job); gpointer result; guint jid; GError *error = NULL; jid = job->id; if (gda_worker_fetch_job_result (dc->worker, jid, &result, &error)) { dc->callback (dc->worker, jid, result, error, dc->user_data); g_clear_error (&error); } return TRUE; /* don't remove the source from poll, this can be done using gda_worker_set_callback() */ }
int test8 (void) { g_print ("%s started, main thread is %p\n", __FUNCTION__, g_thread_self ()); GdaWorker *worker; gint nfailed = 0; worker = gda_worker_new (); GError *error = NULL; guint jid; jid = gda_worker_submit_job (worker, NULL, (GdaWorkerFunc) test8_func, worker, NULL, NULL, &error); if (jid == 0) { g_print ("gda_worker_submit_job() failed: %s\n", error && error->message ? error->message : "No detail"); g_clear_error (&error); nfailed ++; } else { while (1) { gpointer out; g_print ("Waiting for result using gda_worker_fetch_job_result()\n"); if (gda_worker_fetch_job_result (worker, jid, &out, &error)) { if (!out || strcmp (out, "test8_sub_func")) { g_print ("Expected out to be [test8_sub_func] and got %s\n", (gchar*) out); nfailed++; } if (error) { g_print ("Got error: %s\n", error && error->message ? error->message : "No detail"); g_clear_error (&error); nfailed++; } break; } else g_clear_error (&error); g_usleep (100000); } } g_print ("Unref worker...\n"); gda_worker_unref (worker); g_print ("%s done\n", __FUNCTION__); return nfailed; }
/** * gda_worker_wait_job: (skip) * @worker: a #GdaWorker object * @func: the function to call from the worker thread * @data: (allow-none): the data to pass to @func, or %NULL * @data_destroy_func: (allow-none): a function to destroy @data, or %NULL * @error: (allow-none): a place to store errors, or %NULL. * * Request that the worker thread call @func with the @data argument, much like gda_worker_submit_job(), * but waits (blocks) until @func has been executed. * * Note: it's up to the caller to free the result, the #GdaWorker object will not do it (ownership of the result is * transfered to the caller). * * Returns: (transfer full): the result of @func's execution * * Since: 6.0 */ gpointer gda_worker_wait_job (GdaWorker *worker, GdaWorkerFunc func, gpointer data, GDestroyNotify data_destroy_func, GError **error) { g_return_val_if_fail (worker, NULL); g_return_val_if_fail (func, NULL); if (gda_worker_thread_is_worker (worker)) { /* we are called from within the worker thread => call the function directly */ gpointer retval; retval = func (data, error); if (data_destroy_func) data_destroy_func (data); return retval; } guint jid; /* prepare ITSignaler to be notified */ ITSignaler *its; its = itsignaler_new (); /* push job */ g_rec_mutex_lock (& worker->rmutex); /* required to call _gda_worker_submit_job_with_its() */ jid = _gda_worker_submit_job_with_its (worker, its, func, data, data_destroy_func, NULL, error); g_rec_mutex_unlock (& worker->rmutex); if (jid == 0) { /* an error occurred */ itsignaler_unref (its); return NULL; } WorkerJob *job; job = itsignaler_pop_notification (its, -1); g_assert (job); itsignaler_unref (its); gpointer result; g_assert (gda_worker_fetch_job_result (worker, jid, &result, error)); return result; }
static gpointer test8_func (GdaWorker *worker, G_GNUC_UNUSED GError **error) { guint jid; jid = gda_worker_submit_job (worker, NULL, (GdaWorkerFunc) test8_sub_func, NULL, NULL, NULL, error); if (jid == 0) return NULL; else { g_print ("%s() submitted job %u with thread %p\n", __FUNCTION__, jid, g_thread_self ()); while (1) { gpointer out; GError *lerror = NULL; g_print ("Waiting for result...\n"); if (gda_worker_fetch_job_result (worker, jid, &out, &lerror)) { if (lerror) g_propagate_error (error, lerror); return out; } else g_clear_error (&lerror); g_usleep (100000); } } }
/* * Test 7: gda_worker_do_job() * - A ticker is run to make sure the events are handled while in gda_worker_do_job() * - a simple job is run * - job finishes after timer */ int test7 (void) { g_print ("%s started, main thread is %p\n", __FUNCTION__, g_thread_self ()); GdaWorker *worker; gint nfailed = 0; worker = gda_worker_new (); GError *error = NULL; guint nticks = 0; guint timer; timer = g_timeout_add (10, (GSourceFunc) test6_timer_cb, &nticks); guint jid; gpointer out; gint wait; wait = 150; if (gda_worker_do_job (worker, NULL, 100, &out, &jid, (GdaWorkerFunc) test6_worker_func, (gpointer) &wait, NULL, (GDestroyNotify) test6_string_free, &error)) { g_print ("out: [%s], JID: %u\n", (gchar*) out, jid); if (out) { g_print ("Expected out to be NULL and got [%s]\n", (gchar*) out); nfailed++; g_free (out); } if (jid == 0) { g_print ("Expected JID to be > 0 and got %u\n", jid); nfailed++; } while (1) { g_print ("Waiting for result using gda_worker_fetch_job_result()\n"); if (gda_worker_fetch_job_result (worker, jid, &out, &error)) { if (!out || strcmp (out, "Test6Done")) { g_print ("Expected out to be [Test6Done] and got [%s]\n", (gchar*) out); nfailed++; } g_free (out); break; } else g_clear_error (&error); g_usleep (100000); } } else { g_print ("gda_worker_do_job() failed: %s\n", error && error->message ? error->message : "No detail"); g_clear_error (&error); nfailed ++; } g_source_remove (timer); if (nticks < 3) { g_print ("Tick timer was not called while in gda_worker_do_job()\n"); nfailed++; } g_print ("Unref worker...\n"); gda_worker_unref (worker); g_print ("%s done\n", __FUNCTION__); return nfailed; }
int test4 (void) { g_print ("Test4 started, main thread is %p\n", g_thread_self ()); GdaWorker *worker; gint nfailed = 0; worker = gda_worker_new (); guint jid1, jid2; GError *error = NULL; jid1 = gda_worker_submit_job (worker, NULL, (GdaWorkerFunc) test4_worker_func, NULL, NULL, NULL, &error); if (jid1 == 0) { g_print ("Error in gda_worker_submit_job(): %s\n", error && error->message ? error->message : "no detail"); g_clear_error (&error); nfailed++; goto out; } jid2 = gda_worker_submit_job (worker, NULL, (GdaWorkerFunc) test4_worker_func, NULL, NULL, NULL, &error); if (jid2 == 0) { g_print ("Error in gda_worker_submit_job(): %s\n", error && error->message ? error->message : "no detail"); g_clear_error (&error); nfailed++; goto out; } if (! gda_worker_cancel_job (worker, jid2, &error)) { g_print ("Error in gda_worker_cancel_job(): %s\n", error && error->message ? error->message : "no detail"); g_clear_error (&error); nfailed++; goto out; } if (! gda_worker_cancel_job (worker, jid2, &error)) { g_print ("Error in gda_worker_cancel_job(): %s\n", error && error->message ? error->message : "no detail"); g_clear_error (&error); nfailed++; goto out; } if (gda_worker_cancel_job (worker, 10, NULL)) { g_print ("Error in gda_worker_cancel_job(): should have failed!\n"); nfailed++; goto out; } while (1) { gint *result; if (! gda_worker_fetch_job_result (worker, jid1, (gpointer*) &result, &error)) { g_print ("Still not done, error: %s\n", error && error->message ? error->message : "no detail"); if ((error->domain == GDA_WORKER_ERROR) && (error->code == GDA_WORKER_JOB_NOT_FOUND_ERROR)) { nfailed++; g_clear_error (&error); break; } g_clear_error (&error); } else { if (result) { g_print ("Error: got result value when expected none!\n"); nfailed++; } break; } g_usleep (100000); } out: gda_worker_unref (worker); return nfailed; }
/** * gda_worker_do_job: (skip) * @worker: a #GdaWorker object * @context: (allow-none): a #GMainContext to execute a main loop in (while waiting), or %NULL * @timeout_ms: the maximum number of milisecons to wait before returning, or %0 for unlimited wait * @out_result: (allow-none): a place to store the result, if any, of @func's execution, or %NULL * @out_job_id: (allow-none): a place to store the ID of the job having been submitted, or %NULL * @func: the function to call from the worker thread * @data: (allow-none): the data to pass to @func, or %NULL * @data_destroy_func: (allow-none): a function to destroy @data, or %NULL * @result_destroy_func: (allow-none): a function to destroy the result, if any, of @func's execution, or %NULL * @error: (allow-none): a place to store errors, or %NULL. * * Request that the worker thread call @func with the @data argument, much like gda_worker_submit_job(), * but waits (starting a #GMainLoop) for a maximum of @timeout_ms miliseconds for @func to be executed. * * If this function is called from within @worker's worker thread, then this function simply calls @func with @data and does not * use @context. * * The following cases are possible if this function is not called from within @worker's worker thread: * <itemizedlist> * <listitem><para>the call to @func took less than @timeout_ms miliseconds: the return value is %TRUE and * @out_result contains the result of the @func's execution, and @out_job_id contains %NULL. Note in this * case that @error may still contain an error code if @func's execution produced an error. Also note that in this case * any setting defined by gda_worker_set_callback() is not applied (as the result is immediately returned)</para></listitem> * <listitem><para>The call to @func takes more then @timeout_ms miliseconds: the return value is %TRUE and * @out_result is %NULL and @out_job_id contains the ID of the job as if it had been submitted using gda_worker_submit_job(). * If @out_job_id is %NULL, and if no setting has been defined using gda_worker_set_callback(), then the job will be discarded * (as if gda_worker_forget_job() had been called). * </para></listitem> * <listitem><para>The call to @func could not be done (some kind of plumbing error for instance): the returned value is %FALSE * and @out_result and @out_job_id are set to %NULL (if they are not %NULL)</para></listitem> * </itemizedlist> * * Notes: * <itemizedlist> * <listitem><para>@result_destroy_func is needed in case @out_result is %NULL (to avoid memory leaks)</para></listitem> * <listitem><para>passing %NULL for @context is similar to passing the result of g_main_context_ref_thread_default()</para></listitem> * </itemizedlist> * * Returns: %TRUE if no error occurred * * Since: 6.0 */ gboolean gda_worker_do_job (GdaWorker *worker, GMainContext *context, gint timeout_ms, gpointer *out_result, guint *out_job_id, GdaWorkerFunc func, gpointer data, GDestroyNotify data_destroy_func, GDestroyNotify result_destroy_func, GError **error) { if (out_result) *out_result = NULL; if (out_job_id) *out_job_id = 0; g_return_val_if_fail (worker, FALSE); g_return_val_if_fail (func, FALSE); if (!worker->callbacks_hash || !worker->jobs_hash) { g_warning ("GdaWorker has been destroyed\n"); return FALSE; } if (gda_worker_thread_is_worker (worker)) { /* we are called from within the worker thread => call the function directly */ gpointer result; result = func (data, error); if (data_destroy_func) data_destroy_func (data); if (out_result) *out_result = result; else if (result && result_destroy_func) result_destroy_func (result); return TRUE; } guint jid, itsid, timer = 0; /* determine which GMainContext to use */ GMainContext *co; gboolean unref_co = FALSE; co = context; if (!co) { co = g_main_context_ref_thread_default (); unref_co = TRUE; } /* prepare main loop */ GMainLoop *loop; loop = g_main_loop_new (co, FALSE); /* prepare ITSignaler to be notified */ ITSignaler *its; its = itsignaler_new (); itsid = itsignaler_add (its, co, (ITSignalerFunc) do_itsignaler_cb, g_main_loop_ref (loop), (GDestroyNotify) g_main_loop_unref); /* push job */ g_rec_mutex_lock (& worker->rmutex); /* required to call _gda_worker_submit_job_with_its() */ jid = _gda_worker_submit_job_with_its (worker, its, func, data, data_destroy_func, result_destroy_func, error); g_rec_mutex_unlock (& worker->rmutex); if (jid == 0) { /* an error occurred */ g_assert (itsignaler_remove (its, co, itsid)); itsignaler_unref (its); g_main_loop_unref (loop); if (unref_co) g_main_context_unref (co); return FALSE; } /* check if result is already here */ WorkerJob *job; job = itsignaler_pop_notification (its, 0); if (!job) { if (timeout_ms > 0) { /* start timer to limit waiting time */ GSource *timer_src; timer_src = g_timeout_source_new (timeout_ms); g_source_set_callback (timer_src, (GSourceFunc) do_timer_cb, loop, NULL); timer = g_source_attach (timer_src, co); g_source_unref (timer_src); } g_main_loop_run (loop); /* either timer has arrived or job has been done */ job = itsignaler_pop_notification (its, 0); } g_main_loop_unref (loop); g_assert (itsignaler_remove (its, co, itsid)); itsignaler_unref (its); if (job) { /* job done before the timer, if any, elapsed */ if (timer > 0) g_assert (g_source_remove (timer)); g_assert (gda_worker_fetch_job_result (worker, jid, out_result, error)); } else { /* timer came first, job is not yet finished */ /* apply settings from gda_worker_set_callback(), if any */ g_rec_mutex_lock (&worker->rmutex); DeclaredCallback *dc; dc = g_hash_table_lookup (worker->callbacks_hash, co); if (dc) { job = g_hash_table_lookup (worker->jobs_hash, &jid); g_assert (job); g_assert (!job->reply_its); job->reply_its = itsignaler_ref (dc->its); } g_rec_mutex_unlock (& worker->rmutex); /* cleanups */ if (out_job_id) *out_job_id = jid; else if (!dc) /* forget all about the job */ gda_worker_forget_job (worker, jid); } if (unref_co) g_main_context_unref (co); return TRUE; }