Adding an object-oriented wrapper for threads.
This commit is contained in:
@@ -0,0 +1,405 @@
|
||||
/*
|
||||
* Copyright (C) 2009 Tobias Brunner
|
||||
* Hochschule fuer Technik Rapperswil
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*/
|
||||
|
||||
#define _GNU_SOURCE
|
||||
#include <pthread.h>
|
||||
#include <signal.h>
|
||||
#include <semaphore.h>
|
||||
|
||||
#include <library.h>
|
||||
#include <debug.h>
|
||||
|
||||
#include <threading/thread_value.h>
|
||||
#include <threading/mutex.h>
|
||||
#include <utils/linked_list.h>
|
||||
|
||||
#include "thread.h"
|
||||
|
||||
typedef struct private_thread_t private_thread_t;
|
||||
|
||||
struct private_thread_t {
|
||||
/**
|
||||
* Public interface.
|
||||
*/
|
||||
thread_t public;
|
||||
|
||||
/**
|
||||
* Human-readable ID of this thread.
|
||||
*/
|
||||
u_int id;
|
||||
|
||||
/**
|
||||
* ID of the underlying thread.
|
||||
*/
|
||||
pthread_t thread_id;
|
||||
|
||||
/**
|
||||
* Main function of this thread (NULL for the main thread).
|
||||
*/
|
||||
thread_main_t main;
|
||||
|
||||
/**
|
||||
* Argument for the main function.
|
||||
*/
|
||||
void *arg;
|
||||
|
||||
/**
|
||||
* Stack of cleanup handlers.
|
||||
*/
|
||||
linked_list_t *cleanup_handlers;
|
||||
|
||||
/**
|
||||
* Mutex to make modifying thread properties safe.
|
||||
*/
|
||||
mutex_t *mutex;
|
||||
|
||||
/**
|
||||
* Semaphore used to sync the creation/start of the thread.
|
||||
*/
|
||||
sem_t created;
|
||||
|
||||
/**
|
||||
* TRUE if this thread has been detached or joined, i.e. can be cleaned
|
||||
* up after terminating.
|
||||
*/
|
||||
bool detached_or_joined;
|
||||
|
||||
/**
|
||||
* TRUE if the threads has terminated (cancelled, via thread_exit or
|
||||
* returned from the main function)
|
||||
*/
|
||||
bool terminated;
|
||||
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
/**
|
||||
* Cleanup callback function.
|
||||
*/
|
||||
thread_cleanup_t cleanup;
|
||||
|
||||
/**
|
||||
* Argument provided to the cleanup function.
|
||||
*/
|
||||
void *arg;
|
||||
|
||||
} cleanup_handler_t;
|
||||
|
||||
|
||||
/**
|
||||
* Next thread ID.
|
||||
*/
|
||||
static u_int next_id = 1;
|
||||
|
||||
/**
|
||||
* Mutex to safely access the next thread ID.
|
||||
*/
|
||||
static mutex_t *id_mutex;
|
||||
|
||||
/**
|
||||
* Store the thread object in a thread-specific value.
|
||||
*/
|
||||
static thread_value_t *current_thread;
|
||||
|
||||
|
||||
/**
|
||||
* Destroy an internal thread object.
|
||||
*
|
||||
* @note The mutex of this thread object has to be locked, it gets unlocked
|
||||
* automatically.
|
||||
*/
|
||||
static void thread_destroy(private_thread_t *this)
|
||||
{
|
||||
if (!this->terminated || !this->detached_or_joined)
|
||||
{
|
||||
this->mutex->unlock(this->mutex);
|
||||
return;
|
||||
}
|
||||
this->cleanup_handlers->destroy(this->cleanup_handlers);
|
||||
this->mutex->unlock(this->mutex);
|
||||
this->mutex->destroy(this->mutex);
|
||||
sem_destroy(&this->created);
|
||||
free(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of thread_t.cancel.
|
||||
*/
|
||||
static void cancel(private_thread_t *this)
|
||||
{
|
||||
this->mutex->lock(this->mutex);
|
||||
if (pthread_equal(this->thread_id, pthread_self()))
|
||||
{
|
||||
this->mutex->unlock(this->mutex);
|
||||
DBG1("!!! CANNOT CANCEL CURRENT THREAD !!!");
|
||||
return;
|
||||
}
|
||||
pthread_cancel(this->thread_id);
|
||||
this->mutex->unlock(this->mutex);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of thread_t.kill.
|
||||
*/
|
||||
static void _kill(private_thread_t *this, int sig)
|
||||
{
|
||||
this->mutex->lock(this->mutex);
|
||||
if (pthread_equal(this->thread_id, pthread_self()))
|
||||
{
|
||||
/* it might actually be possible to send a signal to pthread_self (there
|
||||
* is an example in raise(3) describing that), the problem is though,
|
||||
* that the thread only returns here after the signal handler has
|
||||
* returned, so depending on the signal, the lock might not get
|
||||
* unlocked. */
|
||||
this->mutex->unlock(this->mutex);
|
||||
DBG1("!!! CANNOT SEND SIGNAL TO CURRENT THREAD !!!");
|
||||
return;
|
||||
}
|
||||
pthread_kill(this->thread_id, sig);
|
||||
this->mutex->unlock(this->mutex);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of thread_t.cancel.
|
||||
*/
|
||||
static void detach(private_thread_t *this)
|
||||
{
|
||||
this->mutex->lock(this->mutex);
|
||||
pthread_detach(this->thread_id);
|
||||
this->detached_or_joined = TRUE;
|
||||
thread_destroy(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of thread_t.cancel.
|
||||
*/
|
||||
static void *join(private_thread_t *this)
|
||||
{
|
||||
pthread_t thread_id;
|
||||
void *val;
|
||||
this->mutex->lock(this->mutex);
|
||||
if (pthread_equal(this->thread_id, pthread_self()))
|
||||
{
|
||||
this->mutex->unlock(this->mutex);
|
||||
DBG1("!!! CANNOT JOIN CURRENT THREAD !!!");
|
||||
return NULL;
|
||||
}
|
||||
if (this->detached_or_joined)
|
||||
{
|
||||
this->mutex->unlock(this->mutex);
|
||||
DBG1("!!! CANNOT JOIN DETACHED THREAD !!!");
|
||||
return NULL;
|
||||
}
|
||||
thread_id = this->thread_id;
|
||||
this->detached_or_joined = TRUE;
|
||||
if (this->terminated)
|
||||
{
|
||||
/* thread has terminated before the call to join */
|
||||
thread_destroy(this);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* thread_destroy is called when the thread terminates normally */
|
||||
this->mutex->unlock(this->mutex);
|
||||
}
|
||||
pthread_join(thread_id, &val);
|
||||
return val;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an internal thread object.
|
||||
*/
|
||||
static private_thread_t *thread_create_internal()
|
||||
{
|
||||
private_thread_t *this = malloc_thing(private_thread_t);
|
||||
this->public.cancel = (void(*)(thread_t*))cancel;
|
||||
this->public.kill = (void(*)(thread_t*,int))_kill;
|
||||
this->public.detach = (void(*)(thread_t*))detach;
|
||||
this->public.join = (void*(*)(thread_t*))join;
|
||||
|
||||
this->id = 0;
|
||||
this->thread_id = 0;
|
||||
this->main = NULL;
|
||||
this->arg = NULL;
|
||||
this->cleanup_handlers = linked_list_create();
|
||||
this->mutex = mutex_create(MUTEX_TYPE_DEFAULT);
|
||||
sem_init(&this->created, FALSE, 0);
|
||||
this->detached_or_joined = FALSE;
|
||||
this->terminated = FALSE;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Main cleanup function for threads.
|
||||
*/
|
||||
static void thread_cleanup(private_thread_t *this)
|
||||
{
|
||||
cleanup_handler_t *handler;
|
||||
this->mutex->lock(this->mutex);
|
||||
while (this->cleanup_handlers->remove_last(this->cleanup_handlers,
|
||||
(void**)&handler) == SUCCESS)
|
||||
{
|
||||
handler->cleanup(handler->arg);
|
||||
free(handler);
|
||||
}
|
||||
this->terminated = TRUE;
|
||||
thread_destroy(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Main function wrapper for threads.
|
||||
*/
|
||||
static void *thread_main(private_thread_t *this)
|
||||
{
|
||||
void *res;
|
||||
sem_wait(&this->created);
|
||||
current_thread->set(current_thread, this);
|
||||
pthread_cleanup_push((thread_cleanup_t)thread_cleanup, this);
|
||||
res = this->main(this->arg);
|
||||
pthread_cleanup_pop(TRUE);
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* Described in header.
|
||||
*/
|
||||
thread_t *thread_create(thread_main_t main, void *arg)
|
||||
{
|
||||
private_thread_t *this = thread_create_internal();
|
||||
this->main = main;
|
||||
this->arg = arg;
|
||||
if (pthread_create(&this->thread_id, NULL, (void*)thread_main, this) != 0)
|
||||
{
|
||||
DBG1("failed to create thread!");
|
||||
thread_destroy(this);
|
||||
return NULL;
|
||||
}
|
||||
id_mutex->lock(id_mutex);
|
||||
this->id = next_id++;
|
||||
id_mutex->unlock(id_mutex);
|
||||
sem_post(&this->created);
|
||||
return &this->public;
|
||||
}
|
||||
|
||||
/**
|
||||
* Described in header.
|
||||
*/
|
||||
thread_t *thread_current()
|
||||
{
|
||||
return current_thread->get(current_thread);
|
||||
}
|
||||
|
||||
/**
|
||||
* Described in header.
|
||||
*/
|
||||
u_int thread_current_id()
|
||||
{
|
||||
private_thread_t *this = (private_thread_t*)thread_current();
|
||||
return this->id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Described in header.
|
||||
*/
|
||||
void thread_cleanup_push(thread_cleanup_t cleanup, void *arg)
|
||||
{
|
||||
private_thread_t *this = (private_thread_t*)thread_current();
|
||||
cleanup_handler_t *handler;
|
||||
this->mutex->lock(this->mutex);
|
||||
handler = malloc_thing(cleanup_handler_t);
|
||||
handler->cleanup = cleanup;
|
||||
handler->arg = arg;
|
||||
this->cleanup_handlers->insert_last(this->cleanup_handlers, handler);
|
||||
this->mutex->unlock(this->mutex);
|
||||
}
|
||||
|
||||
/**
|
||||
* Described in header.
|
||||
*/
|
||||
void thread_cleanup_pop(bool execute)
|
||||
{
|
||||
private_thread_t *this = (private_thread_t*)thread_current();
|
||||
cleanup_handler_t *handler;
|
||||
this->mutex->lock(this->mutex);
|
||||
if (this->cleanup_handlers->remove_last(this->cleanup_handlers,
|
||||
(void**)&handler) != SUCCESS)
|
||||
{
|
||||
this->mutex->unlock(this->mutex);
|
||||
DBG1("!!! THREAD CLEANUP ERROR !!!");
|
||||
return;
|
||||
}
|
||||
this->mutex->unlock(this->mutex);
|
||||
|
||||
if (execute)
|
||||
{
|
||||
handler->cleanup(handler->arg);
|
||||
}
|
||||
free(handler);
|
||||
}
|
||||
|
||||
/**
|
||||
* Described in header.
|
||||
*/
|
||||
bool thread_cancelability(bool enable)
|
||||
{
|
||||
int old;
|
||||
pthread_setcancelstate(enable ? PTHREAD_CANCEL_ENABLE
|
||||
: PTHREAD_CANCEL_DISABLE, &old);
|
||||
return old == PTHREAD_CANCEL_ENABLE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Described in header.
|
||||
*/
|
||||
void thread_cancellation_point()
|
||||
{
|
||||
bool old = thread_cancelability(TRUE);
|
||||
pthread_testcancel();
|
||||
thread_cancelability(old);
|
||||
}
|
||||
|
||||
/**
|
||||
* Described in header.
|
||||
*/
|
||||
void thread_exit(void *val)
|
||||
{
|
||||
pthread_exit(val);
|
||||
}
|
||||
|
||||
/**
|
||||
* Described in header.
|
||||
*/
|
||||
void threads_init()
|
||||
{
|
||||
private_thread_t *main_thread = thread_create_internal();
|
||||
main_thread->id = 0;
|
||||
main_thread->thread_id = pthread_self();
|
||||
current_thread = thread_value_create(NULL);
|
||||
current_thread->set(current_thread, (void*)main_thread);
|
||||
id_mutex = mutex_create(MUTEX_TYPE_DEFAULT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Described in header.
|
||||
*/
|
||||
void threads_deinit()
|
||||
{
|
||||
private_thread_t *main_thread = (private_thread_t*)thread_current();
|
||||
thread_destroy(main_thread);
|
||||
current_thread->destroy(current_thread);
|
||||
id_mutex->destroy(id_mutex);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,187 @@
|
||||
/*
|
||||
* Copyright (C) 2009 Tobias Brunner
|
||||
* Hochschule fuer Technik Rapperswil
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup thread thread
|
||||
* @{ @ingroup threading
|
||||
*/
|
||||
|
||||
#ifndef THREADING_THREAD_H_
|
||||
#define THREADING_THREAD_H_
|
||||
|
||||
typedef struct thread_t thread_t;
|
||||
|
||||
#ifdef __APPLE__
|
||||
/* on Mac OS X 10.5 several system calls we use are no cancellation points.
|
||||
* fortunately, select isn't one of them, so we wrap some of the others with
|
||||
* calls to select(2).
|
||||
*/
|
||||
#include <sys/socket.h>
|
||||
#include <sys/select.h>
|
||||
|
||||
#define WRAP_WITH_SELECT(func, socket, ...)\
|
||||
fd_set rfds; FD_ZERO(&rfds); FD_SET(socket, &rfds);\
|
||||
if (select(socket + 1, &rfds, NULL, NULL, NULL) <= 0) { return -1; }\
|
||||
return func(socket, __VA_ARGS__)
|
||||
|
||||
static inline int cancellable_accept(int socket, struct sockaddr *address,
|
||||
socklen_t *address_len)
|
||||
{
|
||||
WRAP_WITH_SELECT(accept, socket, address, address_len);
|
||||
}
|
||||
#define accept cancellable_accept
|
||||
static inline int cancellable_recvfrom(int socket, void *buffer, size_t length,
|
||||
int flags, struct sockaddr *address, socklen_t *address_len)
|
||||
{
|
||||
WRAP_WITH_SELECT(recvfrom, socket, buffer, length, flags, address, address_len);
|
||||
}
|
||||
#define recvfrom cancellable_recvfrom
|
||||
#endif /* __APPLE__ */
|
||||
|
||||
/**
|
||||
* Main function of a thread.
|
||||
*
|
||||
* @param arg argument provided to constructor
|
||||
* @return value provided to threads joining the terminating thread
|
||||
*/
|
||||
typedef void *(*thread_main_t)(void *arg);
|
||||
|
||||
/**
|
||||
* Cleanup callback function for a thread.
|
||||
*
|
||||
* @param arg argument provided to thread_cleanup_push
|
||||
*/
|
||||
typedef void (*thread_cleanup_t)(void *arg);
|
||||
|
||||
|
||||
/**
|
||||
* Thread wrapper implements simple, portable and advanced thread functions.
|
||||
*
|
||||
* @note All threads other than the main thread need either to be joined or
|
||||
* detached by calling the corresponding method.
|
||||
*/
|
||||
struct thread_t {
|
||||
|
||||
/**
|
||||
* Cancel this thread.
|
||||
*/
|
||||
void (*cancel)(thread_t *this);
|
||||
|
||||
/**
|
||||
* Send a signal to this thread.
|
||||
*
|
||||
* @param sig the signal to be sent to this thread
|
||||
*/
|
||||
void (*kill)(thread_t *this, int sig);
|
||||
|
||||
/**
|
||||
* Detach this thread, this automatically destroys the thread object after
|
||||
* the thread returned from its main function.
|
||||
*
|
||||
* @note Calling detach is like calling destroy on other objects.
|
||||
*/
|
||||
void (*detach)(thread_t *this);
|
||||
|
||||
/**
|
||||
* Join this thread, this automatically destroys the thread object
|
||||
* afterwards.
|
||||
*
|
||||
* @note Calling join is like calling destroy on other objects.
|
||||
*
|
||||
* @return the value returned from the thread's main function or
|
||||
* a call to exit.
|
||||
*/
|
||||
void *(*join)(thread_t *this);
|
||||
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Create a new thread instance.
|
||||
*
|
||||
* @param main thread main function
|
||||
* @param arg argument provided to the main function
|
||||
* @return thread instance
|
||||
*/
|
||||
thread_t *thread_create(thread_main_t main, void *arg);
|
||||
|
||||
/**
|
||||
* Get a thread object for the current thread.
|
||||
*
|
||||
* @return thread instance
|
||||
*/
|
||||
thread_t *thread_current();
|
||||
|
||||
/**
|
||||
* Get the human-readable ID of the current thread.
|
||||
*
|
||||
* The IDs are assigned incrementally starting from 1.
|
||||
*
|
||||
* @return human-readable ID
|
||||
*/
|
||||
u_int thread_current_id();
|
||||
|
||||
/**
|
||||
* Push a function onto the current thread's cleanup handler stack.
|
||||
* The callback function is called whenever the thread is cancelled, exits or
|
||||
* thread_cleanup_pop is called with TRUE as execute argument.
|
||||
*
|
||||
* @param cleanup function called on thread exit
|
||||
* @param arg argument provided to the callback
|
||||
*/
|
||||
void thread_cleanup_push(thread_cleanup_t cleanup, void *arg);
|
||||
|
||||
/**
|
||||
* Remove the top function from the current thread's cleanup handler stack
|
||||
* and optionally execute it.
|
||||
*
|
||||
* @param execute TRUE to execute the function
|
||||
*/
|
||||
void thread_cleanup_pop(bool execute);
|
||||
|
||||
/**
|
||||
* Enable or disable the cancelability of the current thread. The current
|
||||
* value is returned.
|
||||
*
|
||||
* @param enable TRUE to enable cancelability
|
||||
* @return the current state of the cancelability
|
||||
*/
|
||||
bool thread_cancelability(bool enable);
|
||||
|
||||
/**
|
||||
* Force creation of a cancellation point in the calling thread.
|
||||
*/
|
||||
void thread_cancellation_point();
|
||||
|
||||
/**
|
||||
* Exit the current thread.
|
||||
*
|
||||
* @param val value provided to threads joining the current thread
|
||||
*/
|
||||
void thread_exit(void *val);
|
||||
|
||||
/**
|
||||
* Called by the main thread to initialize the thread management.
|
||||
*/
|
||||
void threads_init();
|
||||
|
||||
/**
|
||||
* Called by the main thread to deinitialize the thread management.
|
||||
*/
|
||||
void threads_deinit();
|
||||
|
||||
|
||||
#endif /** THREADING_THREAD_H_ @} */
|
||||
|
||||
Reference in New Issue
Block a user