#include #include #include #include #include "pthread_impl.h" #include "syscall.h" static void dummy_0(void) { } weak_alias(dummy_0, __tl_lock); weak_alias(dummy_0, __tl_unlock); static sem_t barrier_sem; static void bcast_barrier(int s) { sem_post(&barrier_sem); } int __membarrier(int cmd, int flags) { int r = __syscall(SYS_membarrier, cmd, flags); /* Emulate the private expedited command, which is needed by the * dynamic linker for installation of dynamic TLS, for older * kernels that lack the syscall. Unlike the syscall, this only * synchronizes with threads of the process, not other processes * sharing the VM, but such sharing is not a supported usage * anyway. */ if (r && cmd == MEMBARRIER_CMD_PRIVATE_EXPEDITED && !flags) { pthread_t self=__pthread_self(), td; sigset_t set; __block_app_sigs(&set); __tl_lock(); sem_init(&barrier_sem, 0, 0); struct sigaction sa = { .sa_flags = SA_RESTART | SA_ONSTACK, .sa_handler = bcast_barrier }; memset(&sa.sa_mask, -1, sizeof sa.sa_mask); if (!__libc_sigaction(SIGSYNCCALL, &sa, 0)) { for (td=self->next; td!=self; td=td->next) __syscall(SYS_tkill, td->tid, SIGSYNCCALL); for (td=self->next; td!=self; td=td->next) sem_wait(&barrier_sem); r = 0; sa.sa_handler = SIG_IGN; __libc_sigaction(SIGSYNCCALL, &sa, 0); } sem_destroy(&barrier_sem); __tl_unlock(); __restore_sigs(&set); } return __syscall_ret(r); } void __membarrier_init(void) { /* If membarrier is linked, attempt to pre-register to be able to use * the private expedited command before the process becomes multi- * threaded, since registering later has bad, potentially unbounded * latency. This syscall should be essentially free, and it's arguably * a mistake in the API design that registration was even required. * For other commands, registration may impose some cost, so it's left * to the application to do so if desired. Unfortunately this means * library code initialized after the process becomes multi-threaded * cannot use these features without accepting registration latency. */ __syscall(SYS_membarrier, MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED, 0); } weak_alias(__membarrier, membarrier);