blob: e8f27db6c52eba45be7f09ea6bba31462ed2373a [file] [log] [blame] [edit]
/*
* BRLTTY - A background process providing access to the console screen (when in
* text mode) for a blind person using a refreshable braille display.
*
* Copyright (C) 1995-2023 by The BRLTTY Developers.
*
* BRLTTY comes with ABSOLUTELY NO WARRANTY.
*
* This is free software, placed under the terms of the
* GNU Lesser General Public License, as published by the Free Software
* Foundation; either version 2.1 of the License, or (at your option) any
* later version. Please see the file LICENSE-LGPL for details.
*
* Web Page: http://brltty.app/
*
* This software is maintained by Dave Mielke <dave@mielke.cc>.
*/
#include "prologue.h"
#include <string.h>
#include <errno.h>
#include "log.h"
#include "strfmt.h"
#include "thread.h"
#include "async_signal.h"
#include "async_event.h"
#include "async_wait.h"
#undef HAVE_THREAD_NAMES
#ifdef GOT_PTHREADS
typedef struct {
ThreadFunction *function;
void *argument;
char name[];
} RunThreadArgument;
static void *
runThread (void *argument) {
RunThreadArgument *run = argument;
setThreadName(run->name);
logMessage(LOG_CATEGORY(ASYNC_EVENTS), "thread starting: %s", run->name);
void *result = run->function(run->argument);
logMessage(LOG_CATEGORY(ASYNC_EVENTS), "thread finished: %s", run->name);
free(run);
return result;
}
typedef struct {
const char *const name;
pthread_t *const thread;
const pthread_attr_t *const attributes;
ThreadFunction *const function;
void *const argument;
int error;
} CreateThreadParameters;
static int
createActualThread (void *parameters) {
CreateThreadParameters *create = parameters;
RunThreadArgument *run;
if ((run = malloc(sizeof(*run) + strlen(create->name) + 1))) {
memset(run, 0, sizeof(*run));
run->function = create->function;
run->argument = create->argument;
strcpy(run->name, create->name);
logMessage(LOG_CATEGORY(ASYNC_EVENTS), "creating thread: %s", create->name);
create->error = pthread_create(create->thread, create->attributes, runThread, run);
if (!create->error) return 1;
logMessage(LOG_CATEGORY(ASYNC_EVENTS), "thread not created: %s: %s", create->name, strerror(create->error));
free(run);
} else {
create->error = errno;
logMallocError();
}
return 0;
}
#ifdef ASYNC_CAN_BLOCK_SIGNALS
ASYNC_WITH_SIGNALS_BLOCKED_FUNCTION(createSignalSafeThread) {
static const int signals[] = {
#ifdef SIGINT
SIGINT,
#endif /* SIGINT */
#ifdef SIGTERM
SIGTERM,
#endif /* SIGTERM */
#ifdef SIGCHLD
SIGCHLD,
#endif /* SIGCHLD */
0
};
CreateThreadParameters *create = data;
const int *signal = signals;
while (*signal) {
asyncSetSignalBlocked(*signal, 1);
signal += 1;
}
createActualThread(create);
}
#endif /* ASYNC_CAN_BLOCK_SIGNALS */
int
createThread (
const char *name,
pthread_t *thread, const pthread_attr_t *attributes,
ThreadFunction *function, void *argument
) {
CreateThreadParameters create = {
.name = name,
.thread = thread,
.attributes = attributes,
.function = function,
.argument = argument
};
#ifdef ASYNC_CAN_BLOCK_SIGNALS
asyncWithObtainableSignalsBlocked(createSignalSafeThread, &create);
#else /* ASYNC_CAN_BLOCK_SIGNALS */
createActualThread(&create);
#endif /* ASYNC_CAN_BLOCK_SIGNALS */
return create.error;
}
typedef struct {
ThreadFunction *const function;
void *const argument;
AsyncEvent *event;
unsigned returned:1;
} CallThreadFunctionData;
THREAD_FUNCTION(runThreadFunction) {
CallThreadFunctionData *ctf = argument;
void *result = ctf->function? ctf->function(ctf->argument): NULL;
asyncSignalEvent(ctf->event, NULL);
return result;
}
ASYNC_EVENT_CALLBACK(handleThreadFunctionReturned) {
CallThreadFunctionData *ctf = parameters->eventData;
ctf->returned = 1;
}
ASYNC_CONDITION_TESTER(testThreadFunctionReturned) {
CallThreadFunctionData *ctf = data;
return ctf->returned;
}
int
callThreadFunction (
const char *name, ThreadFunction *function,
void *argument, void **result
) {
int called = 0;
CallThreadFunctionData ctf = {
.function = function,
.argument = argument,
.returned = 0
};
if ((ctf.event = asyncNewEvent(handleThreadFunctionReturned, &ctf))) {
pthread_t thread;
int error = createThread(name, &thread, NULL, runThreadFunction, &ctf);
if (!error) {
asyncWaitFor(testThreadFunctionReturned, &ctf);
{
void *r;
if (!result) result = &r;
pthread_join(thread, result);
}
called = 1;
} else {
errno = error;
}
asyncDiscardEvent(ctf.event);
}
return called;
}
int
lockMutex (pthread_mutex_t *mutex) {
int result = pthread_mutex_lock(mutex);
logSymbol(LOG_CATEGORY(ASYNC_EVENTS), mutex, "mutex lock");
return result;
}
int
unlockMutex (pthread_mutex_t *mutex) {
logSymbol(LOG_CATEGORY(ASYNC_EVENTS), mutex, "mutex unlock");
return pthread_mutex_unlock(mutex);
}
#if defined(HAVE_PTHREAD_GETNAME_NP) && defined(__GLIBC__)
#define HAVE_THREAD_NAMES
size_t
formatThreadName (char *buffer, size_t size) {
int error = pthread_getname_np(pthread_self(), buffer, size);
return error? 0: strlen(buffer);
}
void
setThreadName (const char *name) {
pthread_setname_np(pthread_self(), name);
}
#elif defined(HAVE_PTHREAD_GETNAME_NP) && defined(__APPLE__)
#define HAVE_THREAD_NAMES
size_t
formatThreadName (char *buffer, size_t size) {
{
int error = pthread_getname_np(pthread_self(), buffer, size);
if (error) return 0;
if (*buffer) return strlen(buffer);
}
if (pthread_main_np()) {
size_t length;
STR_BEGIN(buffer, size);
STR_PRINTF("main");
length = STR_LENGTH;
STR_END;
return length;
}
return 0;
}
void
setThreadName (const char *name) {
pthread_setname_np(name);
}
#endif /* thread names */
#endif /* GOT_PTHREADS */
#ifndef HAVE_THREAD_NAMES
size_t
formatThreadName (char *buffer, size_t size) {
return 0;
}
void
setThreadName (const char *name) {
}
#endif /* HAVE_THREAD_NAMES */
#if defined(PTHREAD_MUTEX_INITIALIZER)
static void
createThreadSpecificDataKey (ThreadSpecificDataControl *ctl) {
int error;
pthread_mutex_lock(&ctl->mutex);
if (!ctl->key.created) {
error = pthread_key_create(&ctl->key.value, ctl->destroy);
if (!error) {
ctl->key.created = 1;
} else {
logActionError(error, "pthread_key_create");
}
}
pthread_mutex_unlock(&ctl->mutex);
}
#ifdef ASYNC_CAN_BLOCK_SIGNALS
ASYNC_WITH_SIGNALS_BLOCKED_FUNCTION(createThreadSpecificDataKeyWithSignalsBlocked) {
ThreadSpecificDataControl *ctl = data;
createThreadSpecificDataKey(ctl);
}
#endif /* ASYNC_CAN_BLOCK_SIGNALS */
void *
getThreadSpecificData (ThreadSpecificDataControl *ctl) {
int error;
#ifdef ASYNC_CAN_BLOCK_SIGNALS
asyncWithAllSignalsBlocked(createThreadSpecificDataKeyWithSignalsBlocked, ctl);
#else /* ASYNC_CAN_BLOCK_SIGNALS */
createThreadSpecificDataKey(ctl);
#endif /* ASYNC_CAN_BLOCK_SIGNALS */
if (ctl->key.created) {
void *tsd = pthread_getspecific(ctl->key.value);
if (tsd) return tsd;
if ((tsd = ctl->new())) {
if (!(error = pthread_setspecific(ctl->key.value, tsd))) {
return tsd;
} else {
logActionError(error, "pthread_setspecific");
}
ctl->destroy(tsd);
}
}
return NULL;
}
#else /* thread specific data */
#include "program.h"
static void
exitThreadSpecificData (void *data) {
ThreadSpecificDataControl *ctl = data;
if (ctl->data) {
ctl->destroy(ctl->data);
ctl->data = NULL;
}
}
void *
getThreadSpecificData (ThreadSpecificDataControl *ctl) {
if (!ctl->data) {
if ((ctl->data = ctl->new())) {
onProgramExit("thread-specific-data", exitThreadSpecificData, ctl);
}
}
return ctl->data;
}
#endif /* thread specific data */