void _threaddebug(uint32_t flag, char *fmt, ...) { char buf[128]; va_list arg; Fmt f; Proc *p; if((_threaddebuglevel&flag) == 0) return; fmtfdinit(&f, 2, buf, sizeof buf); p = _threadgetproc(); if(p==nil) fmtprint(&f, "noproc "); else if(p->thread) fmtprint(&f, "%d.%d ", p->pid, p->thread->id); else fmtprint(&f, "%d._ ", p->pid); va_start(arg, fmt); fmtvprint(&f, fmt, arg); va_end(arg); fmtprint(&f, "\n"); fmtfdflush(&f); }
void _threadnote(void *v, char *s) { Proc *p; Note *n; _threaddebug(DBGNOTE, "Got note %s", s); // if(strncmp(s, "sys:", 4) == 0) // noted(NDFLT); // if(_threadexitsallstatus){ // _threaddebug(DBGNOTE, "Threadexitsallstatus = '%s'\n", _threadexitsallstatus); // _exits(_threadexitsallstatus); // } if(strcmp(s, "threadint")==0) noted(NCONT); p = _threadgetproc(); if(p == nil) noted(NDFLT); for(n=notes; n<enotes; n++) if(canlock(&n->inuse)) break; if(n==enotes) sysfatal("libthread: too many delayed notes"); utfecpy(n->s, n->s+ERRMAX, s); n->proc = p; p->pending = 1; if(!p->splhi) delayednotes(p, v); noted(NCONT); }
int threadpid(int id) { int pid; Proc *p; Thread *t; if (id < 0) return -1; if (id == 0) return _threadgetproc()->pid; lock(&_threadpq.lock); for (p = _threadpq.head; p; p = p->next){ lock(&p->lock); for (t = p->threads.head; t; t = t->nextt) if (t->id == id){ pid = p->pid; unlock(&p->lock); unlock(&_threadpq.lock); return pid; } unlock(&p->lock); } unlock(&_threadpq.lock); return -1; }
void _sched(void) { Proc *p; Thread *t; Resched: p = _threadgetproc(); if((t = p->thread) != nil){ needstack(128); _threaddebug(DBGSCHED, "pausing, state=%s", psstate(t->state)); if(setjmp(t->sched)==0) longjmp(p->sched, 1); return; }else{ t = runthread(p); if(t == nil){ _threaddebug(DBGSCHED, "all threads gone; exiting"); _schedexit(p); } _threaddebug(DBGSCHED, "running %d.%d", t->proc->pid, t->id); p->thread = t; if(t->moribund){ _threaddebug(DBGSCHED, "%d.%d marked to die"); goto Resched; } t->state = Running; t->nextstate = Ready; longjmp(t->sched, 1); } }
void _schedexecwait(void) { int pid; Channel *c; Proc *p; Thread *t; Waitmsg *w; p = _threadgetproc(); t = p->thread; pid = t->ret; _threaddebug(DBGEXEC, "_schedexecwait %d", t->ret); rfork(RFCFDG); for(;;){ w = wait(); if(w == nil) break; if(w->pid == pid) break; free(w); } if(w != nil){ if((c = _threadwaitchan) != nil) sendp(c, w); else free(w); } threadexits("procexec"); }
int threadnotify(int (*f)(void*, char*), int in) { int i, topid; int (*from)(void*, char*), (*to)(void*, char*); if(in){ from = nil; to = f; topid = _threadgetproc()->pid; }else{ from = f; to = nil; topid = 0; } lock(&onnotelock); for(i=0; i<NFN; i++) if(onnote[i]==from){ onnote[i] = to; onnotepid[i] = topid; break; } unlock(&onnotelock); return i<NFN; }
char* threadgetname(void) { Proc *p; if((p = _threadgetproc()) && p->thread) return p->thread->cmdname; return nil; }
int _procsplhi(void) { int s; Proc *p; p = _threadgetproc(); s = p->splhi; p->splhi = 1; return s; }
int threadsetgrp(int ng) { int og; Thread *t; t = _threadgetproc()->thread; og = t->grp; t->grp = ng; return og; }
void _procsplx(int s) { Proc *p; p = _threadgetproc(); p->splhi = s; if(s) return; if(p->pending) delayednotes(p, nil); }
int procrfork(void (*f)(void *), void *arg, uint stacksize, int rforkflag) { Proc *p; int id; p = _threadgetproc(); assert(p->newproc == nil); p->newproc = _newproc(f, arg, stacksize, nil, p->thread->grp, rforkflag); id = p->newproc->threads.head->id; _sched(); return id; }
void threadexits(char *exitstr) { Proc *p; Thread *t; p = _threadgetproc(); t = p->thread; t->moribund = 1; if(exitstr==nil) exitstr=""; utfecpy(p->exitstr, p->exitstr+ERRMAX, exitstr); _sched(); }
void _threadassert(char *s) { char buf[256]; int n; Proc *p; p = _threadgetproc(); if(p && p->thread) n = sprint(buf, "%d.%d ", p->pid, p->thread->id); else n = 0; snprint(buf+n, sizeof(buf)-n, "%s: assertion failed\n", s); write(2, buf, strlen(buf)); abort(); }
void needstack(int n) { int x; Proc *p; Thread *t; p = _threadgetproc(); t = p->thread; if((uint8_t*)&x - n < (uint8_t*)t->stk){ fprint(2, "%s %lud: &x=%p n=%d t->stk=%p\n", argv0, _tos->pid, &x, n, t->stk); fprint(2, "%s %lud: stack overflow\n", argv0, _tos->pid); abort(); } }
void threadsetname(char *fmt, ...) { int fd; char buf[128]; va_list arg; Proc *p; Thread *t; p = _threadgetproc(); t = p->thread; if (t->cmdname) free(t->cmdname); va_start(arg, fmt); t->cmdname = vsmprint(fmt, arg); va_end(arg); if(t->cmdname && p->nthreads == 1){ snprint(buf, sizeof buf, "#p/%lu/args", _tos->pid); //getpid()); if((fd = open(buf, OWRITE)) >= 0){ write(fd, t->cmdname, strlen(t->cmdname)+1); close(fd); } } }
void procexec(Channel *pidc, char *prog, char *args[]) { int n; Proc *p; Thread *t; _threaddebug(DBGEXEC, "procexec %s", prog); /* must be only thread in proc */ p = _threadgetproc(); t = p->thread; if(p->threads.head != t || p->threads.head->nextt != nil){ werrstr("not only thread in proc"); Bad: if(pidc) sendul(pidc, ~0); return; } /* * We want procexec to behave like exec; if exec succeeds, * never return, and if it fails, return with errstr set. * Unfortunately, the exec happens in another proc since * we have to wait for the exec'ed process to finish. * To provide the semantics, we open a pipe with the * write end close-on-exec and hand it to the proc that * is doing the exec. If the exec succeeds, the pipe will * close so that our read below fails. If the exec fails, * then the proc doing the exec sends the errstr down the * pipe to us. */ if(bind("#|", PIPEMNT, MREPL) < 0) goto Bad; if((p->exec.fd[0] = open(PIPEMNT "/data", OREAD)) < 0){ unmount(nil, PIPEMNT); goto Bad; } if((p->exec.fd[1] = open(PIPEMNT "/data1", OWRITE|OCEXEC)) < 0){ close(p->exec.fd[0]); unmount(nil, PIPEMNT); goto Bad; } unmount(nil, PIPEMNT); /* exec in parallel via the scheduler */ assert(p->needexec==0); p->exec.prog = prog; p->exec.args = args; p->needexec = 1; _sched(); close(p->exec.fd[1]); if((n = read(p->exec.fd[0], p->exitstr, ERRMAX-1)) > 0){ /* exec failed */ p->exitstr[n] = '\0'; errstr(p->exitstr, ERRMAX); close(p->exec.fd[0]); goto Bad; } close(p->exec.fd[0]); if(pidc) sendul(pidc, t->ret); /* wait for exec'ed program, then exit */ _schedexecwait(); }
int threadid(void) { return _threadgetproc()->thread->id; }
int threadgetgrp(void) { return _threadgetproc()->thread->grp; }
int alt(Alt *alts) { Alt *a, *xa, *ca; Channel volatile *c; int n, s, waiting, allreadycl; void* r; Thread *t; /* * The point of going splhi here is that note handlers * might reasonably want to use channel operations, * but that will hang if the note comes while we hold the * chanlock. Instead, we delay the note until we've dropped * the lock. */ t = _threadgetproc()->thread; if(t->moribund || _threadexitsallstatus) yield(); /* won't return */ s = _procsplhi(); lock(&chanlock); t->alt = alts; t->chan = Chanalt; /* test whether any channels can proceed */ n = 0; a = nil; for(xa=alts; xa->op!=CHANEND && xa->op!=CHANNOBLK; xa++){ xa->entryno = -1; if(xa->op == CHANNOP) continue; c = xa->c; if(c==nil){ unlock(&chanlock); _procsplx(s); t->chan = Channone; return -1; } if(isopenfor(c, xa->op) && canexec(xa)) if(nrand(++n) == 0) a = xa; } if(a==nil){ /* nothing can proceed */ if(xa->op == CHANNOBLK){ unlock(&chanlock); _procsplx(s); t->chan = Channone; if(xa->op == CHANNOBLK) return xa - alts; } /* enqueue on all channels open for us. */ c = nil; ca = nil; waiting = 0; allreadycl = 0; for(xa=alts; xa->op!=CHANEND; xa++) if(xa->op==CHANNOP) continue; else if(isopenfor(xa->c, xa->op)){ waiting = 1; enqueue(xa, &c); } else if(xa->err != errcl) ca = xa; else allreadycl = 1; if(waiting == 0) if(ca != nil){ /* everything was closed, select last channel */ ca->err = errcl; unlock(&chanlock); _procsplx(s); t->chan = Channone; return ca - alts; } else if(allreadycl){ /* everything was already closed */ unlock(&chanlock); _procsplx(s); t->chan = Channone; return -1; } /* * wait for successful rendezvous. * we can't just give up if the rendezvous * is interrupted -- someone else might come * along and try to rendezvous with us, so * we need to be here. * if the channel was closed, the op is done * and we flag an error for the entry. */ Again: unlock(&chanlock); _procsplx(s); r = _threadrendezvous(&c, 0); s = _procsplhi(); lock(&chanlock); if(r==Intred){ /* interrupted */ if(c!=nil) /* someone will meet us; go back */ goto Again; c = (Channel*)~0; /* so no one tries to meet us */ } /* dequeue from channels, find selected one */ a = nil; for(xa=alts; xa->op!=CHANEND; xa++){ if(xa->op==CHANNOP) continue; if(xa->c == c){ a = xa; a->err = nil; if(r == Closed) a->err = errcl; } dequeue(xa); } unlock(&chanlock); _procsplx(s); if(a == nil){ /* we were interrupted */ assert(c==(Channel*)~0); return -1; } }else altexec(a, s); /* unlocks chanlock, does splx */ _sched(); t->chan = Channone; return a - alts; }
/* * Create a new thread and schedule it to run. * The thread grp is inherited from the currently running thread. */ int threadcreate(void (*f)(void *arg), void *arg, uint stacksize) { return newthread(_threadgetproc(), f, arg, stacksize, nil, threadgetgrp()); }
char* threadgetname(void) { return _threadgetproc()->thread->cmdname; }