summaryrefslogtreecommitdiff
path: root/src/thread/synccall.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/thread/synccall.c')
-rw-r--r--src/thread/synccall.c109
1 files changed, 109 insertions, 0 deletions
diff --git a/src/thread/synccall.c b/src/thread/synccall.c
new file mode 100644
index 00000000..7c4f92bf
--- /dev/null
+++ b/src/thread/synccall.c
@@ -0,0 +1,109 @@
+#include "pthread_impl.h"
+#include <semaphore.h>
+
+static struct chain {
+ struct chain *next;
+ sem_t sem, sem2;
+} *head;
+
+static void (*callback)(void *), *context;
+static int chainlen;
+static sem_t chainlock, chaindone;
+static pthread_rwlock_t lock = PTHREAD_RWLOCK_INITIALIZER;
+
+static void handler(int sig, siginfo_t *si, void *ctx)
+{
+ struct chain ch;
+ pthread_t self = __pthread_self();
+ int old_errno = errno;
+
+ if (chainlen == libc.threads_minus_1) return;
+
+ sigqueue(self->pid, SIGSYNCCALL, (union sigval){0});
+
+ /* Threads which have already decremented themselves from the
+ * thread count must not act. Block further receipt of signals
+ * and return. */
+ if (self->dead) {
+ memset(&((ucontext_t *)ctx)->uc_sigmask, -1, 8);
+ errno = old_errno;
+ return;
+ }
+
+ sem_init(&ch.sem, 0, 0);
+ sem_init(&ch.sem2, 0, 0);
+
+ while (sem_wait(&chainlock));
+ ch.next = head;
+ head = &ch;
+ if (++chainlen == libc.threads_minus_1) sem_post(&chaindone);
+ sem_post(&chainlock);
+
+ while (sem_wait(&ch.sem));
+ callback(context);
+ sem_post(&ch.sem2);
+ while (sem_wait(&ch.sem));
+
+ errno = old_errno;
+}
+
+void __synccall(void (*func)(void *), void *ctx)
+{
+ pthread_t self;
+ struct sigaction sa;
+ struct chain *cur, *next;
+ uint64_t oldmask;
+
+ pthread_rwlock_wrlock(&lock);
+
+ __syscall(SYS_rt_sigprocmask, SIG_BLOCK, (uint64_t[]){-1}, &oldmask, 8);
+
+ if (!libc.threads_minus_1) {
+ func(ctx);
+ return;
+ }
+
+ sem_init(&chaindone, 0, 0);
+ sem_init(&chainlock, 0, 1);
+ chainlen = 0;
+ callback = func;
+ context = ctx;
+
+ sa.sa_flags = SA_SIGINFO | SA_RESTART;
+ sa.sa_sigaction = handler;
+ sigfillset(&sa.sa_mask);
+ __libc_sigaction(SIGSYNCCALL, &sa, 0);
+
+ self = __pthread_self();
+ sigqueue(self->pid, SIGSYNCCALL, (union sigval){0});
+ while (sem_wait(&chaindone));
+
+ for (cur=head; cur; cur=cur->next) {
+ sem_post(&cur->sem);
+ while (sem_wait(&cur->sem2));
+ }
+ func(ctx);
+
+ for (cur=head; cur; cur=next) {
+ next = cur->next;
+ sem_post(&cur->sem);
+ }
+
+ sa.sa_flags = 0;
+ sa.sa_handler = SIG_IGN;
+ __libc_sigaction(SIGSYNCCALL, &sa, 0);
+
+ __syscall(SYS_rt_sigprocmask, SIG_SETMASK, &oldmask, 0, 8);
+
+ pthread_rwlock_unlock(&lock);
+}
+
+void __synccall_lock()
+{
+ pthread_rwlock_rdlock(&lock);
+}
+
+void __synccall_unlock()
+{
+ pthread_rwlock_unlock(&lock);
+}