Basic thread wrapper
This commit is contained in:
parent
1fc696dc51
commit
44cf381ce1
5
Makefile
5
Makefile
|
@ -2,7 +2,10 @@ include common.mk
|
|||
|
||||
all: server
|
||||
|
||||
server: $(BUILD_DIR)/server/main.o $(BUILD_DIR)/common/util/hash_table.o
|
||||
$(BUILD_DIR)/common/util/thread.o: $(BUILD_DIR)/common/util/thread.posix.o
|
||||
cp $< $@
|
||||
|
||||
server: $(BUILD_DIR)/server/main.o $(BUILD_DIR)/common/util/hash_table.o $(BUILD_DIR)/common/util/byte_stream.o $(BUILD_DIR)/common/util/thread.o
|
||||
$(COMPILE_EXE)
|
||||
|
||||
run: server
|
||||
|
|
|
@ -31,6 +31,7 @@ ifneq ($(strip $(DEPS)),)
|
|||
CPPFLAGS += $(shell $(PKGCONFIG) --cflags $(DEPS))
|
||||
LDLIBS += $(shell $(PKGCONFIG) --libs $(DEPS))
|
||||
endif
|
||||
LDLIBS += -lpthread
|
||||
INCPATH += -iquote $(SRC_DIR)
|
||||
ENSURE_DIR = mkdir -p $(shell dirname "$@")
|
||||
COMPILE_EXE = $(CC) $(LDFLAGS) $^ $(LOADLIBES) $(LDLIBS) -o $@
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
#ifndef COMMON_UTIL_CLOSURE_H_
|
||||
#define COMMON_UTIL_CLOSURE_H_
|
||||
|
||||
#include "common/defs.h"
|
||||
|
||||
typedef struct {
|
||||
void *opaque;
|
||||
} ClosureEnvironment;
|
||||
|
||||
#define CLOSURE_CALLBACK_FN(TRet, ...) typeof(TRet (ClosureEnvironment closure ,## __VA_ARGS__))
|
||||
#define CLOSURE_T(...) struct { CLOSURE_CALLBACK_FN(__VA_ARGS__) *callback; ClosureEnvironment env; }
|
||||
|
||||
#endif /* end of include guard: COMMON_UTIL_CLOSURE_H_ */
|
|
@ -0,0 +1,44 @@
|
|||
#ifndef COMMON_UTIL_THREAD_H_
|
||||
#define COMMON_UTIL_THREAD_H_
|
||||
|
||||
#include "common/defs.h"
|
||||
#include "common/util/closure.h"
|
||||
|
||||
typedef union {
|
||||
uintptr_t handle;
|
||||
void *opaque;
|
||||
} Thread;
|
||||
|
||||
typedef union {
|
||||
uintptr_t value;
|
||||
void *pointer;
|
||||
} ThreadResult;
|
||||
|
||||
#define THREAD_NONE (Thread) { .handle = 0, }
|
||||
|
||||
typedef CLOSURE_T(ThreadResult) ThreadEntry;
|
||||
|
||||
/**
|
||||
* Creates and starts new thread
|
||||
* @param entry main thread function
|
||||
* @param error_result join result in case of invalid entry
|
||||
* @result new thread
|
||||
*/
|
||||
Thread thread_spawn(ThreadEntry entry, ThreadResult error_result);
|
||||
|
||||
/**
|
||||
* Frees memory used to store thread handle (if allocated by the implementation)
|
||||
* Usage of th is invalid after this
|
||||
* @return THREAD_NONE
|
||||
*/
|
||||
Thread thread_delete_handle(Thread th);
|
||||
|
||||
/**
|
||||
* Waits for a thread to exit
|
||||
* @param th the thread to join
|
||||
* @param result_ptr pointer to store the exit result or NULL
|
||||
* @return bool on success, false on failure (errno may be set)
|
||||
*/
|
||||
bool thread_join(Thread th, ThreadResult *result_ptr);
|
||||
|
||||
#endif /* end of include guard: COMMON_UTIL_THREAD_H_ */
|
|
@ -0,0 +1,115 @@
|
|||
#include "thread.h"
|
||||
|
||||
#include <pthread.h>
|
||||
#include <errno.h>
|
||||
#include <assert.h>
|
||||
|
||||
typedef struct {
|
||||
ThreadEntry entry;
|
||||
ThreadResult error_result;
|
||||
} NativeEntryArg;
|
||||
|
||||
inline static pthread_t
|
||||
get_handle(Thread wrapper)
|
||||
{
|
||||
assert(wrapper.opaque != NULL);
|
||||
return *(pthread_t*) wrapper.opaque;
|
||||
}
|
||||
|
||||
inline static bool
|
||||
new_uninitialized(Thread *th)
|
||||
{
|
||||
pthread_t *data = T_ALLOC(1, pthread_t);
|
||||
if (!data) {
|
||||
return false;
|
||||
}
|
||||
*th = (Thread) { .opaque = data };
|
||||
return true;
|
||||
}
|
||||
|
||||
inline static void
|
||||
initialize_handle(Thread *th, pthread_t handle)
|
||||
{
|
||||
assert(th != NULL);
|
||||
assert(th->opaque != NULL);
|
||||
*(pthread_t*) th->opaque = handle;
|
||||
return;
|
||||
}
|
||||
|
||||
inline static Thread
|
||||
delete_handle(Thread th)
|
||||
{
|
||||
if (!th.opaque) {
|
||||
return THREAD_NONE;
|
||||
}
|
||||
free(th.opaque);
|
||||
return THREAD_NONE;
|
||||
}
|
||||
|
||||
static void*
|
||||
run_entry(void *arg)
|
||||
{
|
||||
assert(arg != NULL);
|
||||
NativeEntryArg casted_arg = *(NativeEntryArg*) arg;
|
||||
ThreadEntry entry = casted_arg.entry;
|
||||
void *error_result = casted_arg.error_result.pointer;
|
||||
free(arg);
|
||||
if (!entry.callback) {
|
||||
return error_result;
|
||||
}
|
||||
ThreadResult result = entry.callback(entry.env);
|
||||
return result.pointer;
|
||||
}
|
||||
|
||||
Thread
|
||||
thread_spawn(ThreadEntry entry, ThreadResult error_result)
|
||||
{
|
||||
NativeEntryArg *native_arg = T_ALLOC(1, NativeEntryArg);
|
||||
if (!native_arg) {
|
||||
return THREAD_NONE;
|
||||
}
|
||||
*native_arg = (NativeEntryArg) {
|
||||
.entry = entry,
|
||||
.error_result = error_result,
|
||||
};
|
||||
Thread th;
|
||||
pthread_t handle;
|
||||
int error;
|
||||
if (!new_uninitialized(&th)) {
|
||||
return THREAD_NONE;
|
||||
}
|
||||
error = pthread_create(&handle, NULL, &run_entry, native_arg);
|
||||
if (error) {
|
||||
errno = error;
|
||||
return delete_handle(th);
|
||||
}
|
||||
assert(handle && "System allows 0 as thread id");
|
||||
initialize_handle(&th, handle);
|
||||
return th;
|
||||
}
|
||||
|
||||
Thread
|
||||
thread_delete_handle(Thread th)
|
||||
{
|
||||
return delete_handle(th);
|
||||
}
|
||||
|
||||
bool
|
||||
thread_join(Thread th, ThreadResult *result_ptr)
|
||||
{
|
||||
if (!th.opaque) {
|
||||
errno = EINVAL;
|
||||
return false;
|
||||
}
|
||||
pthread_t handle = get_handle(th);
|
||||
void **retval = NULL;
|
||||
if (result_ptr) {
|
||||
retval = &result_ptr->pointer;
|
||||
}
|
||||
int error = pthread_join(handle, retval);
|
||||
if (error) {
|
||||
errno = error;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
|
@ -3,6 +3,7 @@
|
|||
#include <stdio.h>
|
||||
#include "common/util/hash_table.h"
|
||||
#include "common/util/byte_serdes.h"
|
||||
#include "common/util/thread.h"
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
|
|
Loading…
Reference in New Issue