xref: /plan9/sys/src/libthread/sched.c (revision 499069debb03e99ea217d35fd87fb49e96918a92)
1 #include <u.h>
2 #include <libc.h>
3 #include <thread.h>
4 #include "threadimpl.h"
5 
6 static Thread	*runthread(Proc*);
7 
8 static char *_psstate[] = {
9 	"Moribund",
10 	"Dead",
11 	"Exec",
12 	"Fork",
13 	"Running",
14 	"Ready",
15 	"Rendezvous",
16 };
17 
18 static char*
19 psstate(int s)
20 {
21 	if(s < 0 || s >= nelem(_psstate))
22 		return "unknown";
23 	return _psstate[s];
24 }
25 
26 void
27 _schedinit(void *arg)
28 {
29 	Proc *p;
30 	Thread *t, **l;
31 
32 	p = arg;
33 	_threadsetproc(p);
34 	p->pid = getpid();
35 	while(setjmp(p->sched))
36 		;
37 	_threaddebug(DBGSCHED, "top of schedinit, _threadexitsallstatus=%p", _threadexitsallstatus);
38 	if(_threadexitsallstatus)
39 		exits(_threadexitsallstatus);
40 	lock(&p->lock);
41 	if((t=p->thread) != nil){
42 		p->thread = nil;
43 		if(t->moribund){
44 			t->state = Dead;
45 			for(l=&p->threads.head; *l; l=&(*l)->nextt)
46 				if(*l == t){
47 					*l = t->nextt;
48 					if(*l==nil)
49 						p->threads.tail = l;
50 					p->nthreads--;
51 					break;
52 				}
53 			unlock(&p->lock);
54 			if(t->inrendez){
55 				_threadflagrendez(t);
56 				_threadbreakrendez();
57 			}
58 			free(t->stk);
59 			free(t->cmdname);
60 			free(t);	/* XXX how do we know there are no references? */
61 			t = nil;
62 			_sched();
63 		}
64 		if(p->needexec){
65 			t->ret = _schedexec(&p->exec);
66 			p->needexec = 0;
67 		}
68 		if(p->newproc){
69 			t->ret = _schedfork(p->newproc);
70 			p->newproc = nil;
71 		}
72 		t->state = t->nextstate;
73 		if(t->state == Ready)
74 			_threadready(t);
75 	}
76 	unlock(&p->lock);
77 	_sched();
78 }
79 
80 void
81 needstack(int n)
82 {
83 	int x;
84 	Proc *p;
85 	Thread *t;
86 
87 	p = _threadgetproc();
88 	t = p->thread;
89 
90 	if((uchar*)&x - n < (uchar*)t->stk){
91 		fprint(2, "%s %d: &x=%p n=%d t->stk=%p\n",
92 			argv0, getpid(), &x, n, t->stk);
93 		fprint(2, "%s %d: stack overflow\n", argv0, getpid());
94 		abort();
95 	}
96 }
97 
98 void
99 _sched(void)
100 {
101 	Proc *p;
102 	Thread *t;
103 
104 Resched:
105 	p = _threadgetproc();
106 	if((t = p->thread) != nil){
107 		needstack(128);
108 		_threaddebug(DBGSCHED, "pausing, state=%s", psstate(t->state));
109 		if(setjmp(t->sched)==0)
110 			longjmp(p->sched, 1);
111 		return;
112 	}else{
113 		t = runthread(p);
114 		if(t == nil){
115 			_threaddebug(DBGSCHED, "all threads gone; exiting");
116 			_schedexit(p);
117 		}
118 		_threaddebug(DBGSCHED, "running %d.%d", t->proc->pid, t->id);
119 		p->thread = t;
120 		if(t->moribund){
121 			_threaddebug(DBGSCHED, "%d.%d marked to die");
122 			goto Resched;
123 		}
124 		t->state = Running;
125 		t->nextstate = Ready;
126 		longjmp(t->sched, 1);
127 	}
128 }
129 
130 static Thread*
131 runthread(Proc *p)
132 {
133 	Thread *t;
134 	Tqueue *q;
135 
136 	if(p->nthreads==0)
137 		return nil;
138 	q = &p->ready;
139 	lock(&p->readylock);
140 	if(q->head == nil){
141 		q->asleep = 1;
142 		_threaddebug(DBGSCHED, "sleeping for more work");
143 		unlock(&p->readylock);
144 		while(rendezvous(q, 0) == (void*)~0){
145 			if(_threadexitsallstatus)
146 				exits(_threadexitsallstatus);
147 		}
148 		/* lock picked up from _threadready */
149 	}
150 	t = q->head;
151 	q->head = t->next;
152 	unlock(&p->readylock);
153 	return t;
154 }
155 
156 void
157 _threadready(Thread *t)
158 {
159 	Tqueue *q;
160 
161 	assert(t->state == Ready);
162 	_threaddebug(DBGSCHED, "readying %d.%d", t->proc->pid, t->id);
163 	q = &t->proc->ready;
164 	lock(&t->proc->readylock);
165 	t->next = nil;
166 	if(q->head==nil)
167 		q->head = t;
168 	else
169 		*q->tail = t;
170 	q->tail = &t->next;
171 	if(q->asleep){
172 		q->asleep = 0;
173 		/* lock passes to runthread */
174 		_threaddebug(DBGSCHED, "waking process %d", t->proc->pid);
175 		while(rendezvous(q, 0) == (void*)~0){
176 			if(_threadexitsallstatus)
177 				exits(_threadexitsallstatus);
178 		}
179 	}else
180 		unlock(&t->proc->readylock);
181 }
182 
183 void
184 yield(void)
185 {
186 	_sched();
187 }
188 
189