| /* |
| * 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 */ |