You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
183 lines
4.4 KiB
183 lines
4.4 KiB
/* -*- coding: utf-8 -*-
|
|
* ----------------------------------------------------------------------
|
|
* Copyright © 2013, RedJack, LLC.
|
|
* All rights reserved.
|
|
*
|
|
* Please see the COPYING file in this distribution for license details.
|
|
* ----------------------------------------------------------------------
|
|
*/
|
|
|
|
#include <assert.h>
|
|
|
|
#include <pthread.h>
|
|
|
|
#include "libcork/core/allocator.h"
|
|
#include "libcork/core/error.h"
|
|
#include "libcork/core/types.h"
|
|
#include "libcork/ds/buffer.h"
|
|
#include "libcork/threads/basics.h"
|
|
|
|
|
|
/*-----------------------------------------------------------------------
|
|
* Current thread
|
|
*/
|
|
|
|
static volatile cork_thread_id last_thread_descriptor = 0;
|
|
|
|
struct cork_thread {
|
|
const char *name;
|
|
cork_thread_id id;
|
|
pthread_t thread_id;
|
|
struct cork_thread_body *body;
|
|
cork_error error_code;
|
|
struct cork_buffer error_message;
|
|
bool started;
|
|
bool joined;
|
|
};
|
|
|
|
struct cork_thread_descriptor {
|
|
struct cork_thread *current_thread;
|
|
cork_thread_id id;
|
|
};
|
|
|
|
cork_tls(struct cork_thread_descriptor, cork_thread_descriptor);
|
|
|
|
struct cork_thread *
|
|
cork_current_thread_get(void)
|
|
{
|
|
struct cork_thread_descriptor *desc = cork_thread_descriptor_get();
|
|
return desc->current_thread;
|
|
}
|
|
|
|
cork_thread_id
|
|
cork_current_thread_get_id(void)
|
|
{
|
|
struct cork_thread_descriptor *desc = cork_thread_descriptor_get();
|
|
if (CORK_UNLIKELY(desc->id == 0)) {
|
|
if (desc->current_thread == NULL) {
|
|
desc->id = cork_uint_atomic_add(&last_thread_descriptor, 1);
|
|
} else {
|
|
desc->id = desc->current_thread->id;
|
|
}
|
|
}
|
|
return desc->id;
|
|
}
|
|
|
|
|
|
/*-----------------------------------------------------------------------
|
|
* Threads
|
|
*/
|
|
|
|
struct cork_thread *
|
|
cork_thread_new(const char *name, struct cork_thread_body *body)
|
|
{
|
|
struct cork_thread *self = cork_new(struct cork_thread);
|
|
self->name = cork_strdup(name);
|
|
self->id = cork_uint_atomic_add(&last_thread_descriptor, 1);
|
|
self->body = body;
|
|
self->error_code = CORK_ERROR_NONE;
|
|
cork_buffer_init(&self->error_message);
|
|
self->started = false;
|
|
self->joined = false;
|
|
return self;
|
|
}
|
|
|
|
static void
|
|
cork_thread_free_private(struct cork_thread *self)
|
|
{
|
|
cork_strfree(self->name);
|
|
cork_thread_body_free(self->body);
|
|
cork_buffer_done(&self->error_message);
|
|
free(self);
|
|
}
|
|
|
|
void
|
|
cork_thread_free(struct cork_thread *self)
|
|
{
|
|
assert(!self->started);
|
|
cork_thread_free_private(self);
|
|
}
|
|
|
|
const char *
|
|
cork_thread_get_name(struct cork_thread *self)
|
|
{
|
|
return self->name;
|
|
}
|
|
|
|
cork_thread_id
|
|
cork_thread_get_id(struct cork_thread *self)
|
|
{
|
|
return self->id;
|
|
}
|
|
|
|
static void *
|
|
cork_thread_pthread_run(void *vself)
|
|
{
|
|
int rc;
|
|
struct cork_thread *self = vself;
|
|
struct cork_thread_descriptor *desc = cork_thread_descriptor_get();
|
|
|
|
desc->current_thread = self;
|
|
desc->id = self->id;
|
|
rc = cork_thread_body_run(self->body);
|
|
|
|
/* If an error occurred in the body of the thread, save the error into the
|
|
* cork_thread object so that we can propagate that error when some calls
|
|
* cork_thread_join. */
|
|
if (CORK_UNLIKELY(rc != 0)) {
|
|
if (CORK_LIKELY(cork_error_occurred())) {
|
|
self->error_code = cork_error_code();
|
|
cork_buffer_set_string(&self->error_message, cork_error_message());
|
|
} else {
|
|
self->error_code = CORK_UNKNOWN_ERROR;
|
|
cork_buffer_set_string(&self->error_message, "Unknown error");
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
int
|
|
cork_thread_start(struct cork_thread *self)
|
|
{
|
|
int rc;
|
|
pthread_t thread_id;
|
|
|
|
assert(!self->started);
|
|
|
|
rc = pthread_create(&thread_id, NULL, cork_thread_pthread_run, self);
|
|
if (CORK_UNLIKELY(rc != 0)) {
|
|
cork_system_error_set_explicit(rc);
|
|
return -1;
|
|
}
|
|
|
|
self->thread_id = thread_id;
|
|
self->started = true;
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
cork_thread_join(struct cork_thread *self)
|
|
{
|
|
int rc;
|
|
|
|
assert(self->started && !self->joined);
|
|
|
|
rc = pthread_join(self->thread_id, NULL);
|
|
if (CORK_UNLIKELY(rc != 0)) {
|
|
cork_system_error_set_explicit(rc);
|
|
cork_thread_free_private(self);
|
|
return -1;
|
|
}
|
|
|
|
if (CORK_UNLIKELY(self->error_code != CORK_ERROR_NONE)) {
|
|
cork_error_set_printf
|
|
(self->error_code, "Error from thread %s: %s",
|
|
self->name, (char *) self->error_message.buf);
|
|
cork_thread_free_private(self);
|
|
return -1;
|
|
}
|
|
|
|
cork_thread_free_private(self);
|
|
return 0;
|
|
}
|