summaryrefslogtreecommitdiff
path: root/src/thread
diff options
context:
space:
mode:
authorRich Felker <dalias@aerifal.cx>2011-08-12 10:37:12 -0400
committerRich Felker <dalias@aerifal.cx>2011-08-12 10:37:12 -0400
commit4054a135fc0e6c1b7c33f688dcddecee0b2b22d2 (patch)
tree8bb7d6b44fd5b3d2cb41e00b1466a24d0ab42d8f /src/thread
parent407d933052c310ebc5541dae2ecd8c4bd8f55fb9 (diff)
downloadmusl-4054a135fc0e6c1b7c33f688dcddecee0b2b22d2.tar.gz
musl-4054a135fc0e6c1b7c33f688dcddecee0b2b22d2.tar.bz2
musl-4054a135fc0e6c1b7c33f688dcddecee0b2b22d2.tar.xz
musl-4054a135fc0e6c1b7c33f688dcddecee0b2b22d2.zip
implement forkall
this is a "nonstandard" function that was "rejected" by POSIX, but nonetheless had its behavior documented in the POSIX rationale for fork. it's present on solaris and possibly some other systems, and duplicates the whole calling process, not just a single thread. glibc does not have this function. it should not be used in programs intending to be portable, but may be useful for testing, checkpointing, etc. and it's an interesting (and quite small) example of the usefulness of the __synccall framework originally written to work around deficiencies in linux's setuid syscall.
Diffstat (limited to 'src/thread')
-rw-r--r--src/thread/forkall.c66
1 files changed, 66 insertions, 0 deletions
diff --git a/src/thread/forkall.c b/src/thread/forkall.c
new file mode 100644
index 00000000..403818ec
--- /dev/null
+++ b/src/thread/forkall.c
@@ -0,0 +1,66 @@
+#include "pthread_impl.h"
+#include <setjmp.h>
+
+struct thread {
+ struct thread *next;
+ pthread_t td;
+ jmp_buf jb;
+ void *tmp, *stack;
+};
+
+struct ctx {
+ struct thread *list;
+ pthread_t caller;
+ pid_t pid;
+ size_t cnt;
+ pthread_barrier_t barrier;
+};
+
+static void restart_thread(pthread_t self)
+{
+ struct thread *t = self->start_arg;
+ self->start_arg = t->tmp;
+ self->pid = getpid();
+ longjmp(t->jb, 1);
+}
+
+static void do_forkall(void *p)
+{
+ struct ctx *c = p, *volatile cv = c;
+ char tmpstack[2048];
+ struct thread *tp, t = {
+ .td = __pthread_self(),
+ .next = c->list,
+ .stack = tmpstack+1024
+ };
+
+ if (t.td != c->caller) {
+ c->cnt++;
+ t.tmp = t.td->start_arg;
+ t.td->start_arg = &t;
+ if (setjmp(t.jb)) {
+ c = cv;
+ if (c->pid) return;
+ pthread_barrier_wait(&c->barrier);
+ return;
+ }
+ c->list = &t;
+ __synccall_wait();
+ return;
+ }
+ c->pid = syscall(SYS_fork);
+ if (c->pid) return;
+
+ pthread_barrier_init(&c->barrier, 0, c->cnt);
+ for (tp=c->list; tp; tp=tp->next)
+ if (__uniclone(tp->stack, restart_thread, tp->td) < 0)
+ _exit(127);
+ pthread_barrier_wait(&c->barrier);
+}
+
+pid_t forkall()
+{
+ struct ctx c = { .caller = pthread_self() };
+ __synccall(do_forkall, &c);
+ return c.pid;
+}