Exemple #1
0
/*
 * Send notifications from this thread, to be caught in the main thread
 */
static gpointer
thread1_start (ITSignaler *its)
{
	gint i, counter;
	itsignaler_ref (its);
	for (counter = 0, i = 0; i < MAX_ITERATIONS; i++) {
		Data *data;
    g_mutex_lock (&mutex);
		data = g_new0 (Data, 1);
		data->counter = counter++;
#ifndef PERF
		g_print ("Pushing %d...\n", counter);
#endif
		itsignaler_push_notification (its, data, g_free);
    g_mutex_unlock (&mutex);

    g_mutex_lock (&mutex);
		data = g_new0 (Data, 1);
		data->counter = counter++;
		itsignaler_push_notification (its, data, g_free);
    g_mutex_unlock (&mutex);
	}
	itsignaler_unref (its);
	return (gpointer) 0x01;
}
Exemple #2
0
void
_gda_worker_unref (GdaWorker *worker, gboolean give_to_bg)
{
	g_assert (worker);

	gboolean unique_locked = FALSE;
	if (worker->location) {
		g_mutex_lock (&unique_worker_mutex);
		unique_locked = TRUE;
	}

#ifdef DEBUG_NOTIFICATION
	g_print ("[W] GdaWorker %p reference decreased to ", worker);
#endif

	g_rec_mutex_lock (& worker->rmutex);
	worker->ref_count --;

#ifdef DEBUG_NOTIFICATION
	g_print ("%u\n", worker->ref_count);
#endif

	if (worker->ref_count == 0) {
		/* destroy all the interal resources which will not be reused even if the GdaWorker is reused */
		g_hash_table_destroy (worker->callbacks_hash);
		worker->callbacks_hash = NULL;
		g_hash_table_destroy (worker->jobs_hash);
		worker->jobs_hash = NULL;
		if (worker->location)
			*(worker->location) = NULL;

		if (give_to_bg) {
			/* re-create the resources so the GdaWorker is ready to be used again */
			worker->ref_count = 1;
			worker->callbacks_hash = g_hash_table_new_full (NULL, NULL, NULL,
									(GDestroyNotify) declared_callback_free);
			worker->jobs_hash = g_hash_table_new_full (g_int_hash, g_int_equal, NULL, (GDestroyNotify) worker_job_free);
			worker->worker_must_quit = 0;
			worker->location = NULL;

			bg_set_spare_gda_worker (worker);
		}
		else {
			/* REM: we don't need to g_thread_join() the worker thread because the "background" will do it */

			/* free all the resources used by @worker */
			itsignaler_unref (worker->submit_its);
			worker->worker_must_quit = 1; /* request the worker thread to exit */
		}
		g_rec_mutex_unlock (& worker->rmutex);
	}
	else
		g_rec_mutex_unlock (& worker->rmutex);

	if (unique_locked)
		g_mutex_unlock (&unique_worker_mutex);
}
Exemple #3
0
/**
 * 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;
}
Exemple #4
0
static void
declared_callback_free (DeclaredCallback *dc)
{
	if (!dc)
		return;
	if (dc->source_sig_id)
		g_assert (itsignaler_remove (dc->its, dc->context, dc->source_sig_id));
	if (dc->its)
		itsignaler_unref (dc->its);
	g_main_context_unref (dc->context);
	g_slice_free (DeclaredCallback, dc);
}
Exemple #5
0
/**
 * gda_worker_new: (skip)
 *
 * Creates a new #GdaWorker object.
 *
 * Returns: (transfer full): a new #GdaWorker, or %NULL if an error occurred
 *
 * Since: 6.0
 */
GdaWorker *
gda_worker_new (void)
{
	GdaWorker *worker;
	worker = bg_get_spare_gda_worker ();
	if (worker)
		return worker;

	worker = g_slice_new0 (GdaWorker);
	worker->submit_its = itsignaler_new ();
	if (!worker->submit_its) {
		g_slice_free (GdaWorker, worker);
		return NULL;
	}

	worker->ref_count = 1;

	worker->callbacks_hash = g_hash_table_new_full (NULL, NULL, NULL,
							(GDestroyNotify) declared_callback_free);
	worker->jobs_hash = g_hash_table_new_full (g_int_hash, g_int_equal, NULL, (GDestroyNotify) worker_job_free);

	worker->worker_must_quit = 0;
	worker->location = NULL;

	g_rec_mutex_init (& worker->rmutex);

	gchar *str;
	static guint counter = 0;
	str = g_strdup_printf ("gdaWrkrTh%u", counter);
	counter++;
	worker->worker_thread = g_thread_try_new (str, (GThreadFunc) worker_thread_main, worker, NULL);
	g_free (str);
	if (!worker->worker_thread) {
		itsignaler_unref (worker->submit_its);
		g_hash_table_destroy (worker->callbacks_hash);
		g_hash_table_destroy (worker->jobs_hash);
		g_rec_mutex_clear (& worker->rmutex);
		g_slice_free (GdaWorker, worker);
		return NULL;
	}
	else
		bg_update_stats (BG_STARTED_THREADS);

#ifdef DEBUG_NOTIFICATION
	g_print ("[W] created GdaWorker %p\n", worker);
#endif

	bg_update_stats (BG_CREATED_WORKER);
	return worker;
}
Exemple #6
0
int
test3 (void)
{
	g_print ("Test3 started\n");

	ITSignaler *its;
	its = itsignaler_new ();
	test3_destroyed = 0;
	itsignaler_push_notification (its, (gpointer) 0x01, test3_destroy_func);
	itsignaler_push_notification (its, (gpointer) 0x02, test3_destroy_func);
	itsignaler_push_notification (its, (gpointer) 0x03, test3_destroy_func);
	
	gpointer data;
	data = itsignaler_pop_notification (its, 100);
	if (!data || data != (gpointer) 0x01) {
		g_print ("Popped wrong value: %p instead of %p\n", data, (gpointer) 0x01);
		itsignaler_unref (its);
		return 1;
	}
	
	itsignaler_unref (its);

	return test3_destroyed == 2 ? 0 : 1;
}
Exemple #7
0
int
test2 (void)
{
	g_print ("Test2 started\n");

	ITSignaler *its;
	its = itsignaler_new ();


	GTimer *timer;
	timer = g_timer_new ();

	GMainLoop *loop;
	loop = g_main_loop_new (NULL, FALSE);

  g_mutex_lock (&mutex);
	CbData cbdata;
	cbdata.counter = 0;
	cbdata.loop = loop;
	itsignaler_add (its, NULL, (ITSignalerFunc) source_callback, &cbdata, NULL);
	itsignaler_unref (its);
  g_mutex_unlock (&mutex);

	GThread *th;
	th = g_thread_new ("sub", (GThreadFunc) thread1_start, its);

	g_main_loop_run (loop);
	g_main_loop_unref (loop);

	g_timer_stop (timer);

	g_thread_join (th);

	if (cbdata.counter == MAX_ITERATIONS * 2) {
		gdouble duration;
		duration = g_timer_elapsed (timer, NULL);
		g_print ("Test Ok, %0.5f s\n", duration);
		g_timer_destroy (timer);
		return 0;
	}
	else {
		g_print ("Test Failed: got %d notification(s) out of %d\n", cbdata.counter, MAX_ITERATIONS * 2);
		g_timer_destroy (timer);
		return 1;
	}
}
Exemple #8
0
/*
 * main function of the worker thread
 */
static gpointer
worker_thread_main (GdaWorker *worker)
{
#define TIMER 150
#ifdef DEBUG_NOTIFICATION
	g_print ("[W] GdaWorker %p, worker thread %p started!\n", worker, g_thread_self());
#endif
	itsignaler_ref (worker->submit_its);
	while (1) {
		WorkerJob *job;
		job = itsignaler_pop_notification (worker->submit_its, TIMER);
		if (job) {
			g_rec_mutex_lock (&worker->rmutex);
			if (! (job->status & JOB_CANCELLED)) {
				/* handle job */
				job->status |= JOB_BEING_PROCESSED;
				g_rec_mutex_unlock (&worker->rmutex);

				job->result = job->func (job->data, & job->error);

				g_rec_mutex_lock (&worker->rmutex);
				job->status |= JOB_PROCESSED;
				if (job->reply_its)
					itsignaler_push_notification (job->reply_its, job, NULL);
			}

			if ((job->status & JOB_CANCELLED) && worker->jobs_hash)
				g_hash_table_remove (worker->jobs_hash, &job->id);
			g_rec_mutex_unlock (&worker->rmutex);
		}

		if (worker->worker_must_quit) {
#ifdef DEBUG_NOTIFICATION
			g_print ("[W] GdaWorker %p, worker thread %p finished!\n", worker, g_thread_self());
#endif
			itsignaler_unref (worker->submit_its);
			g_rec_mutex_clear (& worker->rmutex);
			g_slice_free (GdaWorker, worker);
			bg_update_stats (BG_DESTROYED_WORKER);

			bg_join_thread ();
			return NULL;
		}
	}
	return NULL;
}
Exemple #9
0
static void
worker_job_free (WorkerJob *job)
{
	if (!job)
		return;
	if (job->data_destroy_func && job->data)
		job->data_destroy_func (job->data);
	if (job->result) {
		if (job->result_destroy_func)
			job->result_destroy_func (job->result);
		else
			g_warning (_("Possible memory leak as no destroy function provided to free value returned by the worker thread"));
	}
	if (job->error)
		g_error_free (job->error);
	if (job->reply_its)
		itsignaler_unref (job->reply_its);
	g_slice_free (WorkerJob, job);
}
Exemple #10
0
int
test1 (void)
{
	g_print ("Test1 started\n");

	ITSignaler *its;
	its = itsignaler_new ();

	Data *data;
#ifndef PERF
	g_print ("Try popping initially...\n");
#endif
	data = itsignaler_pop_notification (its, 0);
	if (data) {
		g_warning ("itsignaler_pop_notification() sould have returned NULL");
		return EXIT_FAILURE;
	}
#ifndef PERF
	g_print ("Done\n");
#endif

	GTimer *timer;
	timer = g_timer_new ();

	GMainLoop *loop;
	loop = g_main_loop_new (NULL, FALSE);

	GSource *source;
	source = itsignaler_create_source (its);
	itsignaler_unref (its);

  g_mutex_lock (&mutex);
	CbData cbdata;
  cbdata.its = its;
	cbdata.counter = 0;
	cbdata.loop = loop;
	g_source_set_callback (source, G_SOURCE_FUNC (source_callback), &cbdata, NULL);
	g_source_attach (source, NULL);
  g_mutex_unlock (&mutex);

	GThread *th;
	th = g_thread_new ("sub", (GThreadFunc) thread1_start, its);

	g_main_loop_run (loop);
	g_source_unref (source);

	g_timer_stop (timer);

	g_thread_join (th);

	if (cbdata.counter == MAX_ITERATIONS * 2) {
		gdouble duration;
		duration = g_timer_elapsed (timer, NULL);
		g_print ("Test Ok, %0.5f s\n", duration);
		g_timer_destroy (timer);
		return 0;
	}
	else {
		g_print ("Test Failed: got %d notification(s) out of %d\n", cbdata.counter, MAX_ITERATIONS * 2);
		g_timer_destroy (timer);
		return 1;
	}
}
Exemple #11
0
/**
 * 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;
}
Exemple #12
0
int
main (int argc, char** argv)
{
	g_print ("Test started\n");

	ITSignaler *its;
	its = itsignaler_new ();

	Data *data;
#ifndef PERF
	g_print ("Try popping initially...\n");
#endif
	data = itsignaler_pop_notification (its, 0);
	if (data) {
		g_warning ("itsignaler_pop_notification() sould have returned NULL");
		return EXIT_FAILURE;
	}
#ifndef PERF
	g_print ("Done\n");
#endif

	GThread *th;
	th = g_thread_new ("sub", (GThreadFunc) thread1_start, its);
	gint counter = 0;
	guint ticks;
#ifdef G_OS_WIN32
	struct timeval timeout;
	fd_set fds;
	SOCKET sock;
	timeout.tv_sec = 0;
	timeout.tv_usec = 100000;
	sock = itsignaler_windows_get_poll_fd (its);
	FD_ZERO (&fds);
	FD_SET (sock, &fds);
#else
	struct pollfd fds[1];
	fds[0].fd = itsignaler_unix_get_poll_fd (its);
	fds[0].events = POLLIN;
	fds[0].revents = 0;
#endif

	GTimer *timer;
	timer = g_timer_new ();

	for (ticks = 0; ticks < MAX_ITERATIONS * 4; ticks++) {
		data = NULL;
		int res;
#ifndef PERF
		g_print (".");
		fflush (stdout);
#endif

#ifdef G_OS_WIN32
		res = select (1, &fds, 0, 0, &timeout);
		if (res == SOCKET_ERROR) {
			g_warning ("Failure on select(): %d", WSAGetLastError ()); /* FIXME! */
			return EXIT_FAILURE;
		}
		else if (res > 0) {
			data = (Data*) itsignaler_pop_notification (its, 0);
		}
#else
		res = poll (fds, 1, 100);
		if (res == -1) {
			g_warning ("Failure on poll(): %s", strerror (errno));
			return EXIT_FAILURE;
		}
		else if (res > 0) {
			if (fds[0].revents | POLLIN) {
				data = (Data*) itsignaler_pop_notification (its, 0);
			}
			else {
				g_warning ("Something nasty happened on file desciptor...");
				return EXIT_FAILURE;
			}
		}
#endif

		if (data) {
			if (counter != data->counter) {
				g_warning ("itsignaler_pop_notification() returned wrong value %d instead of %d",
					   data->counter, counter);
				return EXIT_FAILURE;
			}
			else {
#ifndef PERF
				g_print ("Popped %d\n", counter);
#endif
				counter++;
			}
			g_free (data);
			if (counter == MAX_ITERATIONS * 2)
				break;
		}
	}

	g_timer_stop (timer);

	g_thread_join (th);
	itsignaler_unref (its);

	if (counter == MAX_ITERATIONS * 2) {
		gdouble duration;
		duration = g_timer_elapsed (timer, NULL);
		g_print ("Test Ok, got %u notification in %0.5f s\n", MAX_ITERATIONS * 2, duration);
		g_timer_destroy (timer);
		return EXIT_SUCCESS;
	}
	else {
		g_print ("Test Failed: got %d notification(s) out of %u\n", counter, MAX_ITERATIONS * 2);
		g_timer_destroy (timer);
		return EXIT_SUCCESS;
	}
}