aboutsummaryrefslogtreecommitdiff
path: root/src/libthread
diff options
context:
space:
mode:
authorrsc <devnull@localhost>2004-11-08 16:03:42 +0000
committerrsc <devnull@localhost>2004-11-08 16:03:42 +0000
commitcf4f3eafc6016ccdb57773215dcdd5ebac95c07d (patch)
tree8ec61fb34fb6a0fe9d7bd79c65487b9a1ec4bb81 /src/libthread
parent195645536743aeb99eb336726823c38716cec02d (diff)
downloadplan9port-cf4f3eafc6016ccdb57773215dcdd5ebac95c07d.tar.gz
plan9port-cf4f3eafc6016ccdb57773215dcdd5ebac95c07d.tar.bz2
plan9port-cf4f3eafc6016ccdb57773215dcdd5ebac95c07d.zip
extra files
Diffstat (limited to 'src/libthread')
-rw-r--r--src/libthread/execproc.ch183
-rw-r--r--src/libthread/exit-getpid.ch25
-rw-r--r--src/libthread/procstack.ch75
-rw-r--r--src/libthread/proctab.ch97
4 files changed, 380 insertions, 0 deletions
diff --git a/src/libthread/execproc.ch b/src/libthread/execproc.ch
new file mode 100644
index 00000000..4382be40
--- /dev/null
+++ b/src/libthread/execproc.ch
@@ -0,0 +1,183 @@
+/*
+ * Set up a dedicated proc to handle calls to exec.
+ * The proc also waits for child messages.
+ * This way, each proc scheduler need not worry
+ * about calling wait in its main loop.
+ *
+ * To be included from other files (e.g., Linux-clone.c).
+ */
+
+typedef struct Xarg Xarg;
+struct Xarg
+{
+ Channel *pidc;
+ int fd[3];
+ char *prog;
+ char **args;
+ int freeargs;
+ Channel *ret;
+ int errn;
+ char errstr[ERRMAX];
+};
+
+static Proc *_threadexecproc;
+static Channel *_threadexecchan;
+static Lock threadexeclock;
+
+/*
+ * Called to poll for any kids of this pthread.
+ * We have a separate proc responsible for exec,
+ * so this is a no-op.
+ */
+void
+_threadwaitkids(Proc *p)
+{
+}
+
+#define WAITSIG SIGCHLD
+
+/*
+ * Separate process to wait for child messages.
+ * Also runs signal handlers and runs all execs.
+ */
+static void
+nop(int sig)
+{
+ USED(sig);
+}
+
+static void
+_threadwaitproc(void *v)
+{
+ Channel *c;
+ Waitmsg *w;
+ sigset_t mask;
+ int ret, nkids;
+ Xarg *xa;
+
+ nkids = 0;
+
+ sigemptyset(&mask);
+ siginterrupt(WAITSIG, 1);
+ signal(WAITSIG, nop);
+ sigaddset(&mask, WAITSIG);
+ sigprocmask(SIG_BLOCK, &mask, nil);
+ USED(v);
+ for(;;){
+ while((nkids > 0 ? nbrecv : recv)(_threadexecchan, &xa) == 1){
+ ret = _threadexec(xa->pidc, xa->fd, xa->prog, xa->args, xa->freeargs);
+ if(ret > 0)
+ nkids++;
+ else{
+ rerrstr(xa->errstr, sizeof xa->errstr);
+ xa->errn = errno;
+ }
+ sendul(xa->ret, ret);
+ }
+ if(nkids > 0){
+ sigprocmask(SIG_UNBLOCK, &mask, nil);
+ w = wait();
+ sigprocmask(SIG_BLOCK, &mask, nil);
+ if(w == nil && errno == ECHILD){
+ fprint(2, "wait returned ECHILD but nkids=%d; reset\n", nkids);
+ nkids = 0;
+ }
+ if(w){
+ nkids--;
+ if((c = _threadwaitchan) != nil)
+ sendp(c, w);
+ else
+ free(w);
+ }
+ }
+ }
+}
+
+static void _kickexecproc(void);
+
+int
+_callthreadexec(Channel *pidc, int fd[3], char *prog, char *args[], int freeargs)
+{
+ int ret;
+ Xarg xa;
+
+ if(_threadexecchan == nil){
+ lock(&threadexeclock);
+ if(_threadexecchan == nil)
+ _threadfirstexec();
+ unlock(&threadexeclock);
+ }
+
+ xa.pidc = pidc;
+ xa.fd[0] = fd[0];
+ xa.fd[1] = fd[1];
+ xa.fd[2] = fd[2];
+ xa.prog = prog;
+ xa.args = args;
+ xa.freeargs = freeargs;
+ xa.ret = chancreate(sizeof(ulong), 1);
+ sendp(_threadexecchan, &xa);
+ _kickexecproc();
+ ret = recvul(xa.ret);
+ if(ret < 0){
+ werrstr("%s", xa.errstr);
+ errno = xa.errn;
+ }
+ chanfree(xa.ret);
+ return ret;
+}
+
+/*
+ * Called before the first exec.
+ */
+void
+_threadfirstexec(void)
+{
+ int id;
+ Proc *p;
+
+ _threadexecchan = chancreate(sizeof(Xarg*), 1);
+ id = proccreate(_threadwaitproc, nil, 32*1024);
+
+ /*
+ * Sleazy: decrement threadnprocs so that
+ * the existence of the _threadwaitproc proc
+ * doesn't keep us from exiting.
+ */
+ lock(&_threadpq.lock);
+ --_threadnprocs;
+ for(p=_threadpq.head; p; p=p->next)
+ if(p->threads.head && p->threads.head->id == id)
+ break;
+ if(p == nil)
+ sysfatal("cannot find exec proc");
+ unlock(&_threadpq.lock);
+ _threadexecproc = p;
+}
+
+/*
+ * Called after the thread t has been rescheduled.
+ * Kick the exec proc in case it is in the middle of a wait.
+ */
+static void
+_kickexecproc(void)
+{
+ kill(_threadexecproc->pid, WAITSIG);
+}
+
+/*
+ * Called before exec.
+ */
+void
+_threadbeforeexec(void)
+{
+}
+
+/*
+ * Called after exec.
+ */
+void
+_threadafterexec(void)
+{
+}
+
diff --git a/src/libthread/exit-getpid.ch b/src/libthread/exit-getpid.ch
new file mode 100644
index 00000000..e2580ac7
--- /dev/null
+++ b/src/libthread/exit-getpid.ch
@@ -0,0 +1,25 @@
+/*
+ * Implement threadexitsall by sending a signal to every proc.
+ *
+ * To be included from another C file (e.g., Linux-clone.c).
+ */
+
+void
+_threadexitallproc(char *exitstr)
+{
+ Proc *p;
+ int mypid;
+
+ mypid = getpid();
+ lock(&_threadpq.lock);
+ for(p=_threadpq.head; p; p=p->next)
+ if(p->pid > 1 && p->pid != mypid)
+ kill(p->pid, SIGUSR2);
+ exits(exitstr);
+}
+
+void
+_threadexitproc(char *exitstr)
+{
+ _exits(exitstr);
+}
diff --git a/src/libthread/procstack.ch b/src/libthread/procstack.ch
new file mode 100644
index 00000000..ccf81866
--- /dev/null
+++ b/src/libthread/procstack.ch
@@ -0,0 +1,75 @@
+static int fforkstacksize = 16384;
+
+typedef struct Stack Stack;
+struct Stack
+{
+ Stack *next;
+ Stack *fnext;
+ int pid;
+};
+
+static Lock stacklock;
+static Stack *freestacks;
+static Stack *allstacks;
+static int stackmallocs;
+static void gc(void);
+
+static void*
+mallocstack(void)
+{
+ Stack *p;
+
+ lock(&stacklock);
+top:
+ p = freestacks;
+ if(p)
+ freestacks = p->fnext;
+ else{
+ if(stackmallocs++%1 == 0)
+ gc();
+ if(freestacks)
+ goto top;
+ p = malloc(fforkstacksize);
+ p->next = allstacks;
+ allstacks = p;
+ }
+ if(p)
+ p->pid = 1;
+ unlock(&stacklock);
+ return p;
+}
+
+static void
+gc(void)
+{
+ Stack *p;
+
+ for(p=allstacks; p; p=p->next){
+ if(p->pid > 1 && procexited(p->pid)){
+ if(0) fprint(2, "reclaim stack from %d\n", p->pid);
+ p->pid = 0;
+ }
+ if(p->pid == 0){
+ p->fnext = freestacks;
+ freestacks = p;
+ }
+ }
+}
+
+static void
+freestack(void *v)
+{
+ Stack *p;
+
+ p = v;
+ if(p == nil)
+ return;
+ lock(&stacklock);
+ p->fnext = freestacks;
+ p->pid = 0;
+ freestacks = p;
+ unlock(&stacklock);
+ return;
+}
+
+
diff --git a/src/libthread/proctab.ch b/src/libthread/proctab.ch
new file mode 100644
index 00000000..d51543f9
--- /dev/null
+++ b/src/libthread/proctab.ch
@@ -0,0 +1,97 @@
+/*
+ * Proc structure hash table indexed by proctabid() (usually getpid()).
+ * No lock is necessary for lookups (important when called from signal
+ * handlers).
+ *
+ * To be included from other files (e.g., Linux-clone.c).
+ */
+
+#define T ((void*)-1)
+
+enum
+{
+ PTABHASH = 1031,
+};
+
+static Lock ptablock;
+static Proc *proctab[PTABHASH];
+static Proc *theproc;
+static int multi;
+
+void
+_threadmultiproc(void)
+{
+ if(multi == 0){
+ multi = 1;
+ _threadsetproc(theproc);
+ }
+}
+
+void
+_threadsetproc(Proc *p)
+{
+ int i, h;
+ Proc **t;
+
+ if(!multi){
+ theproc = p;
+ return;
+ }
+ lock(&ptablock);
+ p->procid = procid();
+ h = p->procid%PTABHASH;
+ for(i=0; i<PTABHASH; i++){
+ t = &proctab[(h+i)%PTABHASH];
+ if(*t==nil || *t==T){
+ *t = p;
+ break;
+ }
+ }
+ unlock(&ptablock);
+ if(i == PTABHASH)
+ sysfatal("too many procs - proctab is full");
+}
+
+static Proc**
+_threadfindproc(int id)
+{
+ int i, h;
+ Proc **t;
+
+ if(!multi)
+ return &theproc;
+
+ h = id%PTABHASH;
+ for(i=0; i<PTABHASH; i++){
+ t = &proctab[(h+i)%PTABHASH];
+ if(*t != nil && *t != T && (*t)->procid == id){
+ unlock(&ptablock);
+ return t;
+ }
+ }
+ return nil;
+}
+
+Proc*
+_threadgetproc(void)
+{
+ Proc **t;
+
+ t = _threadfindproc(procid());
+ if(t == nil)
+ return nil;
+ return *t;
+}
+
+Proc*
+_threaddelproc(void)
+{
+ Proc **t, *p;
+
+ t = _threadfindproc(procid());
+ if(t == nil)
+ return nil;
+ p = *t;
+ *t = T;
+ return p;
+}