Example #1
0
/**
 * iris_thread_new:
 * @exclusive: the thread is exclusive
 *
 * Createa a new #IrisThread instance that can be used to queue work items
 * to be processed on the thread.
 *
 * If @exclusive, then the thread will not yield to the scheduler and
 * therefore will not participate in scheduler thread balancing.
 *
 * Return value: the newly created #IrisThread instance
 */
IrisThread*
iris_thread_new (gboolean exclusive)
{
	IrisThread *thread;

	iris_debug (IRIS_DEBUG_THREAD);

#if LINUX
#else
	pthread_once (&my_thread_once, _pthread_init);
#endif

	thread = g_slice_new0 (IrisThread);
	thread->exclusive = exclusive;
	thread->queue = g_async_queue_new ();
	thread->mutex = g_mutex_new ();
	thread->thread  = g_thread_create_full ((GThreadFunc)iris_thread_worker,
	                                        thread,
	                                        0,     /* stack size    */
	                                        FALSE, /* joinable      */
	                                        FALSE, /* system thread */
	                                        G_THREAD_PRIORITY_NORMAL,
	                                        NULL);
	thread->scheduler = NULL;

	return thread;
}
Example #2
0
static gpointer
iris_thread_worker (IrisThread *thread)
{
	IrisMessage *message;
	GTimeVal     timeout = {0,0};

	g_return_val_if_fail (thread != NULL, NULL);
	g_return_val_if_fail (thread->queue != NULL, NULL);

#if LINUX
	my_thread = thread;
#else
	pthread_setspecific (my_thread, thread);
#endif

	iris_debug_init_thread ();
	iris_debug (IRIS_DEBUG_THREAD);

next_message:
	if (thread->exclusive) {
		message = g_async_queue_pop (thread->queue);
	}
	else {
		/* If we do not get any schedulers to work for within our
		 * timeout period, we can safely shutdown. */
		g_get_current_time (&timeout);
		g_time_val_add (&timeout, G_USEC_PER_SEC * 5);
		message = g_async_queue_timed_pop (thread->queue, &timeout);

		if (!message) {
			/* Make sure that the manager removes us from the
			 * free thread list. */
			iris_scheduler_manager_destroy (thread);

			/* make sure nothing was added while we
			 * removed ourselves */
			message = g_async_queue_try_pop (thread->queue);
		}
	}

	if (!message)
		return NULL;

	switch (message->what) {
	case MSG_MANAGE:
		iris_thread_handle_manage (thread,
		                           iris_message_get_pointer (message, "queue"),
		                           iris_message_get_boolean (message, "exclusive"),
		                           iris_message_get_boolean (message, "leader"));
		break;
	case MSG_SHUTDOWN:
		iris_thread_handle_shutdown (thread);
		break;
	default:
		g_warn_if_reached ();
		break;
	}

	goto next_message;
}
Example #3
0
static void
iris_thread_worker_transient (IrisThread  *thread,
                              IrisQueue   *queue)
{
	IrisThreadWork *thread_work = NULL;
	GTimeVal        tv_timeout = {0,0};

	iris_debug (IRIS_DEBUG_THREAD);

	/* The transient mode worker is responsible for helping finish off as
	 * many of the work items as fast as possible.  It is not responsible
	 * for asking for more helpers, just processing work items.  When done
	 * processing work items, it will yield itself back to the scheduler
	 * manager.
	 */

	do {
		g_get_current_time (&tv_timeout);
		g_time_val_add (&tv_timeout, POP_WAIT_TIMEOUT);

		if ((thread_work = iris_queue_timed_pop (queue, &tv_timeout)) != NULL) {
			if (!VERIFY_THREAD_WORK (thread_work))
				continue;
			iris_thread_work_run (thread_work);
			iris_thread_work_free (thread_work);
		}
	} while (thread_work != NULL);

	/* Yield our thread back to the scheduler manager */
	iris_scheduler_manager_yield (thread);
}
Example #4
0
/**
 * iris_thread_shutdown:
 * @thread: An #IrisThread
 *
 * Sends a message to the thread asking it to shutdown.
 */
void
iris_thread_shutdown (IrisThread *thread)
{
	IrisMessage *message;

	iris_debug (IRIS_DEBUG_THREAD);

	g_return_if_fail (thread != NULL);

	message = iris_message_new (MSG_SHUTDOWN);
	g_async_queue_push (thread->queue, message);
}
Example #5
0
/**
 * iris_thread_print_stat:
 * @thread: An #IrisThread
 *
 * Prints the stats of an #IrisThread to standard output for analysis.
 * See iris_thread_stat() for programmatically access the statistics.
 */
void
iris_thread_print_stat (IrisThread *thread)
{
	iris_debug (IRIS_DEBUG_THREAD);

	g_mutex_lock (thread->mutex);

	g_fprintf (stderr,
	           "    Thread 0x%016lx     Active: %3s     Queue Size: %d\n",
	           (long)thread->thread,
	           thread->active != NULL ? "yes" : "no",
	           thread->active != NULL ? iris_queue_length (thread->active) : 0);

	g_mutex_unlock (thread->mutex);
}
Example #6
0
static void
iris_thread_worker_transient (IrisThread  *thread,
                              IrisQueue   *queue)
{
	IrisThreadWork *thread_work = NULL;
	GTimeVal        tv_timeout = {0,0};
	gboolean        remove_work;

	iris_debug (IRIS_DEBUG_THREAD);

	/* The transient mode worker is responsible for helping finish off as
	 * many of the work items as fast as possible.  It is not responsible
	 * for asking for more helpers, just processing work items.  When done
	 * processing work items, it will yield itself back to the scheduler
	 * manager.
	 */

	do {
		g_get_current_time (&tv_timeout);
		g_time_val_add (&tv_timeout, POP_WAIT_TIMEOUT);

		thread_work = iris_queue_timed_pop_or_close (queue, &tv_timeout);
		if (thread_work != NULL) {
			if (!g_atomic_int_compare_and_exchange(&thread_work->taken, FALSE, TRUE)) {
				remove_work = g_atomic_int_get (&thread_work->remove);

				if (!remove_work)
					continue;
			} else
				remove_work = g_atomic_int_get (&thread_work->remove);

			if (!remove_work)
				iris_thread_work_run (thread_work);

			iris_thread_work_free (thread_work);
		}
	} while (thread_work != NULL);

	/* Remove the thread from the scheduler (if it's not already removed us due
	 * to being in finalization), and yield our thread back to the scheduler manager */
	if (g_atomic_int_get (&thread->scheduler->in_finalize) == FALSE)
		iris_scheduler_remove_thread (thread->scheduler, thread);
	g_atomic_pointer_set (&thread->scheduler, NULL);

	iris_scheduler_manager_yield (thread);
}
Example #7
0
/**
 * iris_thread_manage:
 * @thread: An #IrisThread
 * @queue: A #GAsyncQueue
 * @leader: If the thread is responsible for asking for more threads
 *
 * Sends a message to the thread asking it to retreive work items from
 * the queue.
 *
 * If @leader is %TRUE, then the thread will periodically ask the scheduler
 * manager to ask for more threads.
 */
void
iris_thread_manage (IrisThread    *thread,
                    IrisQueue     *queue,
                    gboolean       leader)
{
	IrisMessage *message;

	g_return_if_fail (thread != NULL);
	g_return_if_fail (queue != NULL);

	iris_debug (IRIS_DEBUG_THREAD);

	message = iris_message_new_full (MSG_MANAGE,
	                                 "exclusive", G_TYPE_BOOLEAN, thread->exclusive,
	                                 "queue", G_TYPE_POINTER, queue,
	                                 "leader", G_TYPE_BOOLEAN, leader,
	                                 NULL);
	g_async_queue_push (thread->queue, message);
}
Example #8
0
/**
 * iris_thread_print_stat:
 * @thread: An #IrisThread
 *
 * Prints the stats of an #IrisThread to standard output for analysis.
 */
void
iris_thread_print_stat (IrisThread *thread)
{
	iris_debug (IRIS_DEBUG_THREAD);

	g_mutex_lock (thread->mutex);

	g_fprintf (stderr,
	           "    Thread 0x%016lx     Sched 0x%016lx %s Work q. 0x%016lx\n"
	           "\t  Active: %3s     Queue Size: %d\n",
	           (long)thread->thread,
	           (long)thread->scheduler,
	           thread->scheduler ==
	             iris_get_default_control_scheduler()? "(ctrl)  ":
	             iris_get_default_work_scheduler()? "(work)  ": "        ",
	           (long)thread->active,
	           thread->active != NULL ? "yes" : "no",
	           thread->active != NULL ? iris_queue_get_length (thread->active) : 0);

	g_mutex_unlock (thread->mutex);
}
Example #9
0
static void
iris_thread_worker_exclusive (IrisThread  *thread,
                              IrisQueue   *queue,
                              gboolean     leader)
{
	GTimeVal        tv_now      = {0,0};
	GTimeVal        tv_req      = {0,0};
	IrisThreadWork *thread_work = NULL;
	gint            per_quanta = 0;      /* Completed items within the
	                                      * last quanta. */
	guint           queued      = 0;     /* Items left in the queue at */
	gboolean        has_resized = FALSE;

	iris_debug (IRIS_DEBUG_THREAD);

	g_get_current_time (&tv_now);
	g_get_current_time (&tv_req);
	queued = iris_queue_length (queue);

	/* Since our thread is in exclusive mode, we are responsible for
	 * asking the scheduler manager to add or remove threads based
	 * on the demand of our work queue.
	 *
	 * If the scheduler has maxed out the number of threads it is
	 * allowed, then we will not ask the scheduler to add more
	 * threads and rebalance.
	 */

get_next_item:

	if (G_LIKELY ((thread_work = iris_queue_pop (queue)) != NULL)) {
		if (!VERIFY_THREAD_WORK (thread_work))
			goto get_next_item;

		iris_thread_work_run (thread_work);
		iris_thread_work_free (thread_work);
		per_quanta++;
	}
	else {
#if 0
		g_warning ("Exclusive thread is done managing, received NULL");
#endif
		return;
	}

	if (G_UNLIKELY (!thread->scheduler->maxed && leader)) {
		g_get_current_time (&tv_now);

		if (G_UNLIKELY (timeout_elapsed (&tv_now, &tv_req))) {
			/* We check to see if we have a bunch more work to do
			 * or a potential edge case where we are processing about
			 * the same speed as the pusher, but it creates enough
			 * contention where we dont speed up. This is because
			 * some schedulers will round-robin or steal.  And unless
			 * we look to add another thread even though we have nothing
			 * in the queue, we know there are more coming.
			 */
			queued = iris_queue_length (queue);
			if (queued == 0 && !has_resized) {
				queued = per_quanta * 2;
				has_resized = TRUE;
			}

			if (per_quanta < queued) {
				/* make sure we are not maxed before asking */
				if (!g_atomic_int_get (&thread->scheduler->maxed))
					iris_scheduler_manager_request (thread->scheduler,
									per_quanta,
									queued);
			}

			per_quanta = 0;
			tv_req = tv_now;
			g_time_val_add (&tv_req, QUANTUM_USECS);
		}
	}

	goto get_next_item;
}
Example #10
0
static void
iris_thread_worker_exclusive (IrisThread  *thread,
                              IrisQueue   *queue,
                              gboolean     leader)
{
	GTimeVal        tv_now      = {0,0};
	GTimeVal        tv_req      = {0,0};
	IrisThreadWork *thread_work = NULL;
	gint            per_quanta = 0;      /* Completed items within the
	                                      * last quanta. */
	guint           queued      = 0;     /* Items left in the queue at */
	gboolean        has_resized = FALSE;
	gboolean        remove_work;

	iris_debug (IRIS_DEBUG_THREAD);

	g_get_current_time (&tv_now);
	g_get_current_time (&tv_req);
	queued = iris_queue_get_length (queue);

	/* Since our thread is in exclusive mode, we are responsible for
	 * asking the scheduler manager to add or remove threads based
	 * on the demand of our work queue.
	 *
	 * If the scheduler has maxed out the number of threads it is
	 * allowed, then we will not ask the scheduler to add more
	 * threads and rebalance.
	 */

get_next_item:

	if (G_LIKELY ((thread_work = iris_queue_pop (queue)) != NULL)) {
		if (!g_atomic_int_compare_and_exchange(&thread_work->taken, FALSE, TRUE)) {
			remove_work = g_atomic_int_get (&thread_work->remove);

			if (!remove_work)
				/* We lost a race with another thread (remember a lockfree
				 * queue may pop the same item twice). 
				 */
				goto get_next_item;
			/* else: We lost a race with iris_scheduler_unqueue() */
		} else
			/* We won the race. 'remove' is honoured anyway if we can. */
			remove_work = g_atomic_int_get (&thread_work->remove);

		if (!remove_work) {
			iris_thread_work_run (thread_work);
			per_quanta++;
		}

		iris_thread_work_free (thread_work);
	}
	else {
		/* Queue is closed, so scheduler is finalizing. The scheduler will be
		 * waiting until we set thread->scheduler to NULL.
		 */
		g_atomic_pointer_set (&thread->scheduler, NULL);
		iris_scheduler_manager_yield (thread);
		return;
	}

	if (remove_work)
		goto get_next_item;

	if (G_UNLIKELY (!thread->scheduler->maxed && leader)) {
		g_get_current_time (&tv_now);

		if (G_UNLIKELY (timeout_elapsed (&tv_now, &tv_req))) {
			/* We check to see if we have a bunch more work to do
			 * or a potential edge case where we are processing about
			 * the same speed as the pusher, but it creates enough
			 * contention where we dont speed up. This is because
			 * some schedulers will round-robin or steal.  And unless
			 * we look to add another thread even though we have nothing
			 * in the queue, we know there are more coming.
			 */
			queued = iris_queue_get_length (queue);
			if (queued == 0 && !has_resized) {
				queued = per_quanta * 2;
				has_resized = TRUE;
			}

			if (per_quanta < queued) {
				/* make sure we are not maxed before asking */
				if (!g_atomic_int_get (&thread->scheduler->maxed))
					iris_scheduler_manager_request (thread->scheduler,
									per_quanta,
									queued);
			}

			per_quanta = 0;
			tv_req = tv_now;
			g_time_val_add (&tv_req, QUANTUM_USECS);
		}
	}

	goto get_next_item;
}