diff options
author | Andrew G. Morgan <morgan@kernel.org> | 2020-07-09 19:58:53 -0700 |
---|---|---|
committer | Andrew G. Morgan <morgan@kernel.org> | 2020-07-09 22:33:54 -0700 |
commit | dca9b22261f4837b0c81640ca3aa5133b95e0999 (patch) | |
tree | 414a6d7c5956e5d97be81683feba0c6aa28ffaa6 /psx | |
parent | 57b1f9e3e0f97ec18074cff403db22239b2dee1c (diff) | |
download | libcap2-dca9b22261f4837b0c81640ca3aa5133b95e0999.tar.gz |
Rewrite libpsx thread shutdown path to support musl.
Addresses:
https://bugzilla.kernel.org/show_bug.cgi?id=208477
Removed the non-wrapping libpsx macro hacks. The API surface as such
becomes a little smaller and I now have confidence that wrapping
pthread_create using the linker options works with Go, gcc and musl
compilers. I feel it is stable enough to call good to delete the
workarounds.
Signed-off-by: Andrew G. Morgan <morgan@kernel.org>
Diffstat (limited to 'psx')
-rw-r--r-- | psx/include/sys/psx_syscall.h | 33 | ||||
-rw-r--r-- | psx/psx.c | 68 |
2 files changed, 47 insertions, 54 deletions
diff --git a/psx/include/sys/psx_syscall.h b/psx/include/sys/psx_syscall.h index 20d03a8..c089a88 100644 --- a/psx/include/sys/psx_syscall.h +++ b/psx/include/sys/psx_syscall.h @@ -14,25 +14,6 @@ * mechanism is limited to 9 specific set*() syscalls that do not * support the syscall6 API (needed for prctl functions and the ambient * capabilities set for example). - * - * This psx library API also includes explicit registration of threads - * if implicit wrapping the pthread_create() function is problematic - * for your application via the psx_pthread_create() function. To use - * the library in that way, you should include this line in the file - * containing your main() function: - * - * ----------- - * #include <sys/psx_syscall.h> - * - * int main(...) { - * - * .... - * - * } - * PSX_NO_LINKER_WRAPPING - * ----------- - * - * This will ensure that your binary can link. */ #ifndef _SYS_PSX_SYSCALL_H @@ -52,12 +33,6 @@ extern "C" { int __real_pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg); -#define PSX_NO_LINKER_WRAPPING int \ - __real_pthread_create(pthread_t *thread, const pthread_attr_t *attr, \ - void *(*start_routine) (void *), void *arg) { \ - return -1; \ - } - /* * psx_syscall performs the specified syscall on all psx registered * threads. The mecanism by which this occurs is much less efficient @@ -85,14 +60,6 @@ long int psx_syscall6(long int syscall_nr, long int arg4, long int arg5, long int arg6); /* - * psx_register registers the current pthread with the psx abstraction - * of system calls. Typically, there is never any need to call this - * explicitly because the way the library is linked it is implicitly - * called when pthread_create() is called. - */ -void psx_register(void); - -/* * psx_pthread_create() wraps the -lpthread pthread_create() function * call and registers the generated thread with the psx_syscall * infrastructure. @@ -54,6 +54,7 @@ typedef struct registered_thread_s { pthread_t thread; pthread_mutex_t mu; int pending; + int gone; } registered_thread_t; static pthread_once_t psx_tracker_initialized = PTHREAD_ONCE_INIT; @@ -63,7 +64,8 @@ typedef enum { _PSX_SETUP = 1, _PSX_SYSCALL = 2, _PSX_CREATE = 3, - _PSX_INFORK = 4 + _PSX_INFORK = 4, + _PSX_EXITING = 5, } psx_tracker_state_t; /* @@ -102,7 +104,7 @@ pthread_key_t psx_action_key; * the current thread with a TLS specific key pointing at the threads * specific tracker. */ -static void psx_do_registration(void) { +static void *psx_do_registration(void) { registered_thread_t *node = calloc(1, sizeof(registered_thread_t)); pthread_mutex_init(&node->mu, NULL); node->thread = pthread_self(); @@ -112,6 +114,7 @@ static void psx_do_registration(void) { node->next->prev = node; } psx_tracker.root = node; + return node; } /* @@ -314,17 +317,6 @@ static void psx_do_unregister(registered_thread_t *node) { free(node); } -/* - * psx_register can be used to explicitly register a thread once - * created. In general, it shouldn't be needed. Further, it should - * never be used to register the main thread. - */ -void psx_register(void) { - psx_lock(); - psx_do_registration(); - psx_unlock(); -} - typedef struct { void *(*fn)(void *); void *arg; @@ -332,28 +324,54 @@ typedef struct { } psx_starter_t; /* + * _psx_exiting is used to cleanup the node for the thread on its exit + * path. This is needed for musl libc: + * + * https://bugzilla.kernel.org/show_bug.cgi?id=208477 + * + * and likely wise for glibc too: + * + * https://sourceware.org/bugzilla/show_bug.cgi?id=12889 + */ +static void _psx_exiting(void *node) { + psx_new_state(_PSX_IDLE, _PSX_EXITING); + registered_thread_t *ref = node; + pthread_mutex_lock(&ref->mu); + ref->gone = 1; + pthread_mutex_unlock(&ref->mu); + psx_new_state(_PSX_EXITING, _PSX_IDLE); +} + +/* * _psx_start_fn is a trampolene for the intended start function, it - * is called both blocked and locked, but releases the (b)lock before - * calling starter->fn. Before releasing the (b)lock the TLS specific - * attribute(s) are initialized for use by the interrupt handler under + * is called blocked (_PSX_CREATE), but releases the block before + * calling starter->fn. Before releasing the block, the TLS specific + * attributes are initialized for use by the interrupt handler under * the psx mutex, so it doesn't race with an interrupt received by * this thread and the interrupt handler does not need to poll for * that specific attribute to be present (which is problematic during * thread shutdown). */ static void *_psx_start_fn(void *data) { - psx_do_registration(); + void *node = psx_do_registration(); + psx_new_state(_PSX_CREATE, _PSX_IDLE); psx_starter_t *starter = data; pthread_sigmask(SIG_SETMASK, &starter->sigbits, NULL); - void *(*fn)(void *) = starter->fn; void *arg = starter->arg; + memset(data, 0, sizeof(*starter)); free(data); - return fn(arg); + void *ret; + + pthread_cleanup_push(_psx_exiting, node); + ret = fn(arg); + pthread_cleanup_pop(1); + + return ret; } /* @@ -502,8 +520,12 @@ long int __psx_syscall(long int syscall_nr, ...) { } pthread_mutex_lock(&ref->mu); ref->pending = 1; + int gone = ref->gone; + if (!gone) { + gone = pthread_kill(ref->thread, psx_tracker.psx_sig) != 0; + } pthread_mutex_unlock(&ref->mu); - if (pthread_kill(ref->thread, psx_tracker.psx_sig) == 0) { + if (!gone) { continue; } /* @@ -524,8 +546,12 @@ long int __psx_syscall(long int syscall_nr, ...) { pthread_mutex_lock(&ref->mu); int pending = ref->pending; + int gone = ref->gone; + if (pending && !gone) { + gone = (pthread_kill(ref->thread, 0) != 0); + } pthread_mutex_unlock(&ref->mu); - if (!pending || pthread_kill(ref->thread, 0) == 0) { + if (!gone) { waiting += pending; continue; } |