Blob


1 #include "threadimpl.h"
3 int _threaddebuglevel;
5 static uint threadnproc;
6 static uint threadnsysproc;
7 static Lock threadnproclock;
8 static Ref threadidref;
9 static Proc *threadmainproc;
11 static void addproc(Proc*);
12 static void delproc(Proc*);
13 static void addthread(_Threadlist*, _Thread*);
14 static void delthread(_Threadlist*, _Thread*);
15 static void addthreadinproc(Proc*, _Thread*);
16 static void delthreadinproc(Proc*, _Thread*);
17 static void contextswitch(Context *from, Context *to);
18 static void scheduler(Proc*);
20 static void
21 _threaddebug(char *fmt, ...)
22 {
23 va_list arg;
24 char buf[128];
25 _Thread *t;
26 char *p;
27 static int fd = -1;
29 return;
30 if(fd < 0){
31 p = strrchr(argv0, '/');
32 if(p)
33 p++;
34 else
35 p = argv0;
36 snprint(buf, sizeof buf, "/tmp/%s.tlog", p);
37 if((fd = create(buf, OWRITE, 0666)) < 0)
38 fd = open("/dev/null", OWRITE);
39 }
41 va_start(arg, fmt);
42 vsnprint(buf, sizeof buf, fmt, arg);
43 va_end(arg);
44 t = proc()->thread;
45 if(t)
46 fprint(fd, "%d.%d: %s\n", getpid(), t->id, buf);
47 else
48 fprint(fd, "%d._: %s\n", getpid(), buf);
49 }
51 static _Thread*
52 getthreadnow(void)
53 {
54 return proc()->thread;
55 }
56 _Thread *(*threadnow)(void) = getthreadnow;
58 static Proc*
59 procalloc(void)
60 {
61 Proc *p;
63 p = malloc(sizeof *p);
64 if(p == nil)
65 sysfatal("procalloc malloc: %r");
66 memset(p, 0, sizeof *p);
67 addproc(p);
68 lock(&threadnproclock);
69 threadnproc++;
70 unlock(&threadnproclock);
71 return p;
72 }
74 static void
75 threadstart(void *v)
76 {
77 _Thread *t;
79 t = v;
80 t->startfn(t->startarg);
81 memset(&v, 0xff, 32); /* try to cut off stack traces */
82 threadexits(nil);
83 }
85 static _Thread*
86 threadalloc(void (*fn)(void*), void *arg, uint stack)
87 {
88 _Thread *t;
89 sigset_t zero;
91 /* allocate the task and stack together */
92 t = malloc(sizeof *t+stack);
93 if(t == nil)
94 sysfatal("threadalloc malloc: %r");
95 memset(t, 0, sizeof *t);
96 t->stk = (uchar*)(t+1);
97 t->stksize = stack;
98 t->id = incref(&threadidref);
99 t->startfn = fn;
100 t->startarg = arg;
102 /* do a reasonable initialization */
103 memset(&t->context.uc, 0, sizeof t->context.uc);
104 sigemptyset(&zero);
105 sigprocmask(SIG_BLOCK, &zero, &t->context.uc.uc_sigmask);
107 /* must initialize with current context */
108 getcontext(&t->context.uc);
110 /* call makecontext to do the real work. */
111 /* leave a few words open on both ends */
112 t->context.uc.uc_stack.ss_sp = t->stk+8;
113 t->context.uc.uc_stack.ss_size = t->stksize-64;
114 makecontext(&t->context.uc, (void(*)())threadstart, 1, t);
116 return t;
119 _Thread*
120 _threadcreate(Proc *p, void (*fn)(void*), void *arg, uint stack)
122 _Thread *t;
124 t = threadalloc(fn, arg, stack);
125 t->proc = p;
126 addthreadinproc(p, t);
127 p->nthread++;
128 _threadready(t);
129 return t;
132 int
133 threadcreate(void (*fn)(void*), void *arg, uint stack)
135 _Thread *t;
137 t = _threadcreate(proc(), fn, arg, stack);
138 return t->id;
141 int
142 proccreate(void (*fn)(void*), void *arg, uint stack)
144 _Thread *t;
145 Proc *p;
147 p = procalloc();
148 t = _threadcreate(p, fn, arg, stack);
149 _procstart(p, scheduler);
150 return t->id;
153 void
154 _threadswitch(void)
156 Proc *p;
158 p = proc();
159 contextswitch(&p->thread->context, &p->schedcontext);
162 void
163 _threadready(_Thread *t)
165 Proc *p;
167 p = t->proc;
168 lock(&p->lock);
169 p->runrend.l = &p->lock;
170 addthread(&p->runqueue, t);
171 //print("%d wake for job %d->%d\n", time(0), getpid(), p->osprocid);
172 if(p != proc())
173 _procwakeupandunlock(&p->runrend);
174 else
175 unlock(&p->lock);
178 int
179 threadyield(void)
181 int n;
182 Proc *p;
184 p = proc();
185 n = p->nswitch;
186 _threadready(p->thread);
187 _threadswitch();
188 return p->nswitch - n;
191 void
192 threadexits(char *msg)
194 Proc *p;
196 p = proc();
197 if(msg == nil)
198 msg = "";
199 utfecpy(p->msg, p->msg+sizeof p->msg, msg);
200 proc()->thread->exiting = 1;
201 _threadswitch();
204 static void
205 contextswitch(Context *from, Context *to)
207 if(swapcontext(&from->uc, &to->uc) < 0){
208 fprint(2, "swapcontext failed: %r\n");
209 assert(0);
213 static void
214 scheduler(Proc *p)
216 _Thread *t;
218 setproc(p);
219 _threaddebug("scheduler enter");
220 // print("s %p %d\n", p, gettid());
221 lock(&p->lock);
222 for(;;){
223 while((t = p->runqueue.head) == nil){
224 if(p->nthread == 0)
225 goto Out;
226 p->runrend.l = &p->lock;
227 _threaddebug("scheduler sleep");
228 _procsleep(&p->runrend);
229 _threaddebug("scheduler wake");
231 delthread(&p->runqueue, t);
232 unlock(&p->lock);
233 p->thread = t;
234 p->nswitch++;
235 _threaddebug("run %d (%s)", t->id, t->name);
236 contextswitch(&p->schedcontext, &t->context);
237 p->thread = nil;
238 lock(&p->lock);
239 if(t->exiting){
240 delthreadinproc(p, t);
241 p->nthread--;
242 free(t);
246 Out:
247 _threaddebug("scheduler exit");
248 delproc(p);
249 lock(&threadnproclock);
250 if(p->sysproc)
251 --threadnsysproc;
252 if(--threadnproc == threadnsysproc)
253 threadexitsall(p->msg);
254 unlock(&threadnproclock);
255 unlock(&p->lock);
256 free(p);
257 setproc(0);
260 void
261 _threadsetsysproc(void)
263 lock(&threadnproclock);
264 if(++threadnsysproc == threadnproc)
265 exit(0);
266 unlock(&threadnproclock);
267 proc()->sysproc = 1;
270 void**
271 procdata(void)
273 return &proc()->udata;
276 extern Jmp *(*_notejmpbuf)(void);
277 static Jmp*
278 threadnotejmp(void)
280 return &proc()->sigjmp;
283 /*
284 * debugging
285 */
286 void
287 threadsetname(char *fmt, ...)
289 va_list arg;
290 _Thread *t;
292 t = proc()->thread;
293 va_start(arg, fmt);
294 vsnprint(t->name, sizeof t->name, fmt, arg);
295 va_end(arg);
298 void
299 threadsetstate(char *fmt, ...)
301 va_list arg;
302 _Thread *t;
304 t = proc()->thread;
305 va_start(arg, fmt);
306 vsnprint(t->state, sizeof t->name, fmt, arg);
307 va_end(arg);
310 /*
311 * locking
312 */
313 static int
314 threadqlock(QLock *l, int block, ulong pc)
316 lock(&l->l);
317 if(l->owner == nil){
318 l->owner = (*threadnow)();
319 //print("qlock %p @%#x by %p\n", l, pc, l->owner);
320 unlock(&l->l);
321 return 1;
323 if(!block){
324 unlock(&l->l);
325 return 0;
327 //print("qsleep %p @%#x by %p\n", l, pc, (*threadnow)());
328 addthread(&l->waiting, (*threadnow)());
329 unlock(&l->l);
331 _threadswitch();
333 if(l->owner != (*threadnow)()){
334 fprint(2, "%s: qlock pc=0x%lux owner=%p self=%p oops\n",
335 argv0, pc, l->owner, (*threadnow)());
336 abort();
338 //print("qlock wakeup %p @%#x by %p\n", l, pc, (*threadnow)());
339 return 1;
342 static void
343 threadqunlock(QLock *l, ulong pc)
345 lock(&l->l);
346 //print("qlock unlock %p @%#x by %p (owner %p)\n", l, pc, (*threadnow)(), l->owner);
347 if(l->owner != (*threadnow)()){
348 fprint(2, "%s: qunlock pc=0x%lux owner=%p self=%p oops\n",
349 argv0, pc, l->owner, (*threadnow)());
351 if((l->owner = l->waiting.head) != nil){
352 delthread(&l->waiting, l->owner);
353 _threadready(l->owner);
355 unlock(&l->l);
358 static int
359 threadrlock(RWLock *l, int block, ulong pc)
361 USED(pc);
363 lock(&l->l);
364 if(l->writer == nil && l->wwaiting.head == nil){
365 l->readers++;
366 unlock(&l->l);
367 return 1;
369 if(!block){
370 unlock(&l->l);
371 return 0;
373 addthread(&l->rwaiting, (*threadnow)());
374 unlock(&l->l);
375 _threadswitch();
376 return 1;
379 static int
380 threadwlock(RWLock *l, int block, ulong pc)
382 USED(pc);
384 lock(&l->l);
385 if(l->writer == nil && l->readers == 0){
386 l->writer = (*threadnow)();
387 unlock(&l->l);
388 return 1;
390 if(!block){
391 unlock(&l->l);
392 return 0;
394 addthread(&l->wwaiting, (*threadnow)());
395 unlock(&l->l);
396 _threadswitch();
397 return 1;
400 static void
401 threadrunlock(RWLock *l, ulong pc)
403 _Thread *t;
405 USED(pc);
406 lock(&l->l);
407 --l->readers;
408 if(l->readers == 0 && (t = l->wwaiting.head) != nil){
409 delthread(&l->wwaiting, t);
410 l->writer = t;
411 _threadready(t);
413 unlock(&l->l);
416 static void
417 threadwunlock(RWLock *l, ulong pc)
419 _Thread *t;
421 USED(pc);
422 lock(&l->l);
423 l->writer = nil;
424 assert(l->readers == 0);
425 while((t = l->rwaiting.head) != nil){
426 delthread(&l->rwaiting, t);
427 l->readers++;
428 _threadready(t);
430 if(l->readers == 0 && (t = l->wwaiting.head) != nil){
431 delthread(&l->wwaiting, t);
432 l->writer = t;
433 _threadready(t);
435 unlock(&l->l);
438 /*
439 * sleep and wakeup
440 */
441 static void
442 threadrsleep(Rendez *r, ulong pc)
444 addthread(&r->waiting, proc()->thread);
445 qunlock(r->l);
446 _threadswitch();
447 qlock(r->l);
450 static int
451 threadrwakeup(Rendez *r, int all, ulong pc)
453 int i;
454 _Thread *t;
456 for(i=0;; i++){
457 if(i==1 && !all)
458 break;
459 if((t = r->waiting.head) == nil)
460 break;
461 delthread(&r->waiting, t);
462 _threadready(t);
464 return i;
467 /*
468 * startup
469 */
471 static int threadargc;
472 static char **threadargv;
473 int mainstacksize;
475 static void
476 threadmainstart(void *v)
478 USED(v);
479 threadmainproc = proc();
480 threadmain(threadargc, threadargv);
483 void
484 threadlinklibrary(void)
488 int
489 main(int argc, char **argv)
491 Proc *p;
493 argv0 = argv[0];
495 _threadsetupdaemonize();
497 threadargc = argc;
498 threadargv = argv;
500 /*
501 * Install locking routines into C library.
502 */
503 _lock = _threadlock;
504 _unlock = _threadunlock;
505 _qlock = threadqlock;
506 _qunlock = threadqunlock;
507 _rlock = threadrlock;
508 _runlock = threadrunlock;
509 _wlock = threadwlock;
510 _wunlock = threadwunlock;
511 _rsleep = threadrsleep;
512 _rwakeup = threadrwakeup;
513 _notejmpbuf = threadnotejmp;
515 _pthreadinit();
516 p = procalloc();
517 _threadsetproc(p);
518 if(mainstacksize == 0)
519 mainstacksize = 65536;
520 _threadcreate(p, threadmainstart, nil, mainstacksize);
521 scheduler(p);
522 _threaddaemonize();
523 _threadpexit();
524 return 0;
527 /*
528 * hooray for linked lists
529 */
530 static void
531 addthread(_Threadlist *l, _Thread *t)
533 if(l->tail){
534 l->tail->next = t;
535 t->prev = l->tail;
536 }else{
537 l->head = t;
538 t->prev = nil;
540 l->tail = t;
541 t->next = nil;
544 static void
545 delthread(_Threadlist *l, _Thread *t)
547 if(t->prev)
548 t->prev->next = t->next;
549 else
550 l->head = t->next;
551 if(t->next)
552 t->next->prev = t->prev;
553 else
554 l->tail = t->prev;
557 static void
558 addthreadinproc(Proc *p, _Thread *t)
560 _Threadlist *l;
562 l = &p->allthreads;
563 if(l->tail){
564 l->tail->allnext = t;
565 t->allprev = l->tail;
566 }else{
567 l->head = t;
568 t->allprev = nil;
570 l->tail = t;
571 t->allnext = nil;
574 static void
575 delthreadinproc(Proc *p, _Thread *t)
577 _Threadlist *l;
579 l = &p->allthreads;
580 if(t->allprev)
581 t->allprev->allnext = t->allnext;
582 else
583 l->head = t->allnext;
584 if(t->allnext)
585 t->allnext->allprev = t->allprev;
586 else
587 l->tail = t->allprev;
590 Proc *_threadprocs;
591 Lock _threadprocslock;
592 static Proc *_threadprocstail;
594 static void
595 addproc(Proc *p)
597 lock(&_threadprocslock);
598 if(_threadprocstail){
599 _threadprocstail->next = p;
600 p->prev = _threadprocstail;
601 }else{
602 _threadprocs = p;
603 p->prev = nil;
605 _threadprocstail = p;
606 p->next = nil;
607 unlock(&_threadprocslock);
610 static void
611 delproc(Proc *p)
613 lock(&_threadprocslock);
614 if(p->prev)
615 p->prev->next = p->next;
616 else
617 _threadprocs = p->next;
618 if(p->next)
619 p->next->prev = p->prev;
620 else
621 _threadprocstail = p->prev;
622 unlock(&_threadprocslock);
625 /*
626 * notify - for now just use the usual mechanisms
627 */
628 void
629 threadnotify(int (*f)(void*, char*), int in)
631 atnotify(f, in);