Blob


1 #include <u.h>
2 #include <signal.h>
3 #include <errno.h>
4 #include "threadimpl.h"
6 static Thread *runthread(Proc*);
8 static char *_psstate[] = {
9 "Dead",
10 "Running",
11 "Ready",
12 "Rendezvous",
13 };
15 static char*
16 psstate(int s)
17 {
18 if(s < 0 || s >= nelem(_psstate))
19 return "unknown";
20 return _psstate[s];
21 }
23 void
24 needstack(int howmuch)
25 {
26 Proc *p;
27 Thread *t;
29 p = _threadgetproc();
30 if(p == nil || (t=p->thread) == nil)
31 return;
32 if((ulong)&howmuch < (ulong)t->stk+howmuch){ /* stack overflow waiting to happen */
33 fprint(2, "stack overflow: stack at 0x%lux, limit at 0x%lux, need 0x%lux\n", (ulong)&p, (ulong)t->stk, howmuch);
34 abort();
35 }
36 }
38 void
39 _scheduler(void *arg)
40 {
41 Proc *p;
42 Thread *t;
44 p = arg;
45 lock(&p->lock);
46 p->pid = _threadgetpid();
47 _threadsetproc(p);
49 for(;;){
50 t = runthread(p);
51 if(t == nil){
52 _threaddebug(DBGSCHED, "all threads gone; exiting");
53 _threaddelproc();
54 _schedexit(p);
55 }
56 _threaddebug(DBGSCHED, "running %d.%d", t->proc->pid, t->id);
57 p->thread = t;
58 if(t->moribund){
59 _threaddebug(DBGSCHED, "%d.%d marked to die");
60 goto Moribund;
61 }
62 t->state = Running;
63 t->nextstate = Ready;
64 unlock(&p->lock);
66 _swaplabel(&p->sched, &t->sched);
68 lock(&p->lock);
69 p->thread = nil;
70 if(t->moribund){
71 Moribund:
72 if(t->moribund != 1)
73 fprint(2, "moribund %d\n", t->moribund);
74 assert(t->moribund == 1);
75 t->state = Dead;
76 if(t->prevt)
77 t->prevt->nextt = t->nextt;
78 else
79 p->threads.head = t->nextt;
80 if(t->nextt)
81 t->nextt->prevt = t->prevt;
82 else
83 p->threads.tail = t->prevt;
84 unlock(&p->lock);
85 if(t->inrendez){
86 abort();
87 // _threadflagrendez(t);
88 // _threadbreakrendez();
89 }
90 _stackfree(t->stk);
91 free(t->cmdname);
92 free(t); /* XXX how do we know there are no references? */
93 p->nthreads--;
94 t = nil;
95 lock(&p->lock);
96 continue;
97 }
98 /*
99 if(p->needexec){
100 t->ret = _schedexec(&p->exec);
101 p->needexec = 0;
103 */
104 if(p->newproc){
105 t->ret = _schedfork(p->newproc);
106 if(t->ret < 0){
107 //fprint(2, "_schedfork: %r\n");
108 abort();
110 p->newproc = nil;
112 t->state = t->nextstate;
113 if(t->state == Ready)
114 _threadready(t);
115 unlock(&p->lock);
119 int
120 _sched(void)
122 Proc *p;
123 Thread *t;
125 p = _threadgetproc();
126 t = p->thread;
127 assert(t != nil);
128 _swaplabel(&t->sched, &p->sched);
129 return p->nsched++;
132 static Thread*
133 runthread(Proc *p)
135 Channel *c;
136 Thread *t;
137 Tqueue *q;
138 Waitmsg *w;
139 int e, sent;
141 if(p->nthreads==0 || (p->nthreads==1 && p->idle))
142 return nil;
143 q = &p->ready;
144 relock:
145 lock(&p->readylock);
146 if(p->nsched%128 == 0){
147 /* clean up children */
148 e = errno;
149 if((c = _threadwaitchan) != nil){
150 if(c->n <= c->s){
151 sent = 0;
152 for(;;){
153 if((w = p->waitmsg) != nil)
154 p->waitmsg = nil;
155 else
156 w = waitnohang();
157 if(w == nil)
158 break;
159 if(sent == 0){
160 unlock(&p->readylock);
161 sent = 1;
163 if(nbsendp(c, w) != 1)
164 break;
166 p->waitmsg = w;
167 if(sent)
168 goto relock;
170 }else{
171 while((w = waitnohang()) != nil)
172 free(w);
174 errno = e;
176 if(q->head == nil){
177 if(p->idle){
178 if(p->idle->state != Ready){
179 fprint(2, "everyone is asleep\n");
180 exits("everyone is asleep");
182 unlock(&p->readylock);
183 _threaddebug(DBGSCHED, "running idle thread", p->nthreads);
184 return p->idle;
187 _threaddebug(DBGSCHED, "sleeping for more work (%d threads)", p->nthreads);
188 q->asleep = 1;
189 p->rend.l = &p->readylock;
190 _procsleep(&p->rend);
191 if(_threadexitsallstatus)
192 _exits(_threadexitsallstatus);
194 t = q->head;
195 q->head = t->next;
196 unlock(&p->readylock);
197 return t;
200 long
201 threadstack(void)
203 Proc *p;
204 Thread *t;
206 p = _threadgetproc();
207 t = p->thread;
208 return (ulong)&p - (ulong)t->stk;
211 void
212 _threadready(Thread *t)
214 Tqueue *q;
216 if(t == t->proc->idle){
217 _threaddebug(DBGSCHED, "idle thread is ready");
218 return;
221 assert(t->state == Ready);
222 _threaddebug(DBGSCHED, "readying %d.%d", t->proc->pid, t->id);
223 q = &t->proc->ready;
224 lock(&t->proc->readylock);
225 t->next = nil;
226 if(q->head==nil)
227 q->head = t;
228 else
229 q->tail->next = t;
230 q->tail = t;
231 if(q->asleep){
232 assert(q->asleep == 1);
233 q->asleep = 0;
234 /* lock passes to runthread */
235 _procwakeup(&t->proc->rend);
237 unlock(&t->proc->readylock);
238 if(_threadexitsallstatus)
239 _exits(_threadexitsallstatus);
242 void
243 _threadidle(void)
245 Tqueue *q;
246 Thread *t, *idle;
247 Proc *p;
249 p = _threadgetproc();
250 q = &p->ready;
251 lock(&p->readylock);
252 assert(q->tail);
253 idle = q->tail;
254 if(q->head == idle){
255 q->head = nil;
256 q->tail = nil;
257 }else{
258 for(t=q->head; t->next!=q->tail; t=t->next)
260 t->next = nil;
261 q->tail = t;
263 p->idle = idle;
264 _threaddebug(DBGSCHED, "p->idle is %d\n", idle->id);
265 unlock(&p->readylock);
268 int
269 yield(void)
271 Proc *p;
272 int nsched;
274 p = _threadgetproc();
275 nsched = p->nsched;
276 return _sched() - nsched;
279 void
280 threadstatus(void)
282 Proc *p;
283 Thread *t;
285 p = _threadgetproc();
286 for(t=p->threads.head; t; t=t->nextt)
287 fprint(2, "[%3d] %s userpc=%lux\n",
288 t->id, psstate(t->state), t->userpc);