proc.c 10.6 KB
Newer Older
Ken Thompson's avatar
Ken Thompson committed
1 2 3 4 5 6
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

#include "runtime.h"

7 8 9 10 11
typedef struct Sched Sched;

M	m0;
G	g0;	// idle goroutine for m0

Ken Thompson's avatar
Ken Thompson committed
12 13
static	int32	debug	= 0;

Russ Cox's avatar
Russ Cox committed
14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
// Go scheduler
//
// The go scheduler's job is to match ready-to-run goroutines (`g's)
// with waiting-for-work schedulers (`m's).  If there are ready gs
// and no waiting ms, ready() will start a new m running in a new
// OS thread, so that all ready gs can run simultaneously, up to a limit.
// For now, ms never go away.
//
// The default maximum number of ms is one: go runs single-threaded.
// This is because some locking details have to be worked ou
// (select in particular is not locked properly) and because the low-level
// code hasn't been written yet for OS X.  Setting the environmen
// variable $gomaxprocs changes sched.mmax for now.
//
// Even a program that can run without deadlock in a single process
// might use more ms if given the chance.  For example, the prime
// sieve will use as many ms as there are primes (up to sched.mmax),
// allowing different stages of the pipeline to execute in parallel.
// We could revisit this choice, only kicking off new ms for blocking
// system calls, but that would limit the amount of parallel computation
// that go would try to do.
//
// In general, one could imagine all sorts of refinements to the
// scheduler, but the goal now is just to get something working on
// Linux and OS X.

40 41
struct Sched {
	Lock;
Russ Cox's avatar
Russ Cox committed
42 43

	G *gfree;	// available gs (status == Gdead)
Russ Cox's avatar
Russ Cox committed
44

Russ Cox's avatar
Russ Cox committed
45 46 47 48
	G *ghead;	// gs waiting to run
	G *gtail;
	int32 gwait;	// number of gs waiting to run
	int32 gcount;	// number of gs that are alive
Russ Cox's avatar
Russ Cox committed
49

Russ Cox's avatar
Russ Cox committed
50 51 52 53
	M *mhead;	// ms waiting for work
	int32 mwait;	// number of ms waiting for work
	int32 mcount;	// number of ms that are alive
	int32 mmax;	// max number of ms allowed
Russ Cox's avatar
Russ Cox committed
54

Russ Cox's avatar
Russ Cox committed
55
	int32 predawn;	// running initialization, don't run new gs.
56 57 58 59
};

Sched sched;

Russ Cox's avatar
Russ Cox committed
60 61 62 63 64 65 66 67 68 69 70 71 72
// Scheduling helpers.  Sched must be locked.
static void gput(G*);	// put/get on ghead/gtail
static G* gget(void);
static void mput(M*);	// put/get on mhead
static M* mget(void);
static void gfput(G*);	// put/get on gfree
static G* gfget(void);
static void mnew(void);	// kick off new m
static void readylocked(G*);	// ready, but sched is locked

// Scheduler loop.
static void scheduler(void);

Russ Cox's avatar
Russ Cox committed
73 74 75 76 77 78 79 80 81 82 83 84
// The bootstrap sequence is:
//
//	call osinit
//	call schedinit
//	make & queue new G
//	call mstart
//
// The new G does:
//
//	call main·init_function
//	call initdone
//	call main·main
Russ Cox's avatar
Russ Cox committed
85 86 87 88 89
void
schedinit(void)
{
	int32 n;
	byte *p;
Russ Cox's avatar
Russ Cox committed
90

Russ Cox's avatar
Russ Cox committed
91
	sched.mmax = 1;
Russ Cox's avatar
Russ Cox committed
92
	p = getenv("GOMAXPROCS");
Russ Cox's avatar
Russ Cox committed
93 94 95 96 97 98
	if(p != nil && (n = atoi(p)) != 0)
		sched.mmax = n;
	sched.mcount = 1;
	sched.predawn = 1;
}

Russ Cox's avatar
Russ Cox committed
99
// Called after main·init_function; main·main will be called on return.
Russ Cox's avatar
Russ Cox committed
100
void
Russ Cox's avatar
Russ Cox committed
101
initdone(void)
Russ Cox's avatar
Russ Cox committed
102 103
{
	int32 i;
Russ Cox's avatar
Russ Cox committed
104

Russ Cox's avatar
Russ Cox committed
105 106 107 108 109 110 111 112 113 114 115
	// Let's go.
	sched.predawn = 0;

	// There's already one m (us).
	// If main·init_function started other goroutines,
	// kick off new ms to handle them, like ready
	// would have, had it not been pre-dawn.
	for(i=1; i<sched.gcount && i<sched.mmax; i++)
		mnew();
}

Ken Thompson's avatar
Ken Thompson committed
116 117 118
void
sys·goexit(void)
{
119 120 121 122 123
	if(debug){
		prints("goexit goid=");
		sys·printint(g->goid);
		prints("\n");
	}
Russ Cox's avatar
Russ Cox committed
124
	g->status = Gmoribund;
Ken Thompson's avatar
Ken Thompson committed
125 126 127
	sys·gosched();
}

Russ Cox's avatar
Russ Cox committed
128 129 130 131 132 133 134 135 136 137 138 139 140 141 142
G*
malg(int32 stacksize)
{
	G *g;
	byte *stk;

	// 160 is the slop amount known to the stack growth code
	g = mal(sizeof(G));
	stk = mal(160 + stacksize);
	g->stack0 = stk;
	g->stackguard = stk + 160;
	g->stackbase = stk + 160 + stacksize;
	return g;
}

Ken Thompson's avatar
Ken Thompson committed
143 144 145 146 147 148 149 150 151 152 153 154
void
sys·newproc(int32 siz, byte* fn, byte* arg0)
{
	byte *stk, *sp;
	G *newg;

//prints("newproc siz=");
//sys·printint(siz);
//prints(" fn=");
//sys·printpointer(fn);

	siz = (siz+7) & ~7;
Ken Thompson's avatar
div bug  
Ken Thompson committed
155 156
	if(siz > 1024)
		throw("sys·newproc: too many args");
Ken Thompson's avatar
Ken Thompson committed
157

Russ Cox's avatar
Russ Cox committed
158
	lock(&sched);
159

Russ Cox's avatar
Russ Cox committed
160 161 162
	if((newg = gfget()) != nil){
		newg->status = Gwaiting;
	}else{
Russ Cox's avatar
Russ Cox committed
163
		newg = malg(4096);
164 165 166 167
		newg->status = Gwaiting;
		newg->alllink = allg;
		allg = newg;
	}
Russ Cox's avatar
Russ Cox committed
168
	stk = newg->stack0;
169

Ken Thompson's avatar
Ken Thompson committed
170 171 172 173 174 175 176 177 178 179 180 181 182 183 184
	newg->stackguard = stk+160;

	sp = stk + 4096 - 4*8;
	newg->stackbase = sp;

	sp -= siz;
	mcpy(sp, (byte*)&arg0, siz);

	sp -= 8;
	*(byte**)sp = (byte*)sys·goexit;

	sp -= 8;	// retpc used by gogo
	newg->sched.SP = sp;
	newg->sched.PC = fn;

Russ Cox's avatar
Russ Cox committed
185
	sched.gcount++;
Ken Thompson's avatar
Ken Thompson committed
186 187
	goidgen++;
	newg->goid = goidgen;
Russ Cox's avatar
Russ Cox committed
188

Russ Cox's avatar
Russ Cox committed
189
	readylocked(newg);
190
	unlock(&sched);
Ken Thompson's avatar
Ken Thompson committed
191 192 193 194 195 196

//prints(" goid=");
//sys·printint(newg->goid);
//prints("\n");
}

197 198 199 200 201 202
void
tracebackothers(G *me)
{
	G *g;

	for(g = allg; g != nil; g = g->alllink) {
203
		if(g == me || g->status == Gdead)
204 205 206 207 208 209 210 211
			continue;
		prints("\ngoroutine ");
		sys·printint(g->goid);
		prints(":\n");
		traceback(g->sched.PC, g->sched.SP+8, g);  // gogo adjusts SP by 8 (not portable!)
	}
}

Russ Cox's avatar
Russ Cox committed
212
// Put on `g' queue.  Sched must be locked.
213
static void
Russ Cox's avatar
Russ Cox committed
214
gput(G *g)
Ken Thompson's avatar
Ken Thompson committed
215
{
Russ Cox's avatar
Russ Cox committed
216 217 218
	g->schedlink = nil;
	if(sched.ghead == nil)
		sched.ghead = g;
219
	else
Russ Cox's avatar
Russ Cox committed
220 221 222 223 224 225 226 227 228 229
		sched.gtail->schedlink = g;
	sched.gtail = g;
	sched.gwait++;
}

// Get from `g' queue.  Sched must be locked.
static G*
gget(void)
{
	G *g;
Russ Cox's avatar
Russ Cox committed
230

Russ Cox's avatar
Russ Cox committed
231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254
	g = sched.ghead;
	if(g){
		sched.ghead = g->schedlink;
		if(sched.ghead == nil)
			sched.gtail = nil;
		sched.gwait--;
	}
	return g;
}

// Put on `m' list.  Sched must be locked.
static void
mput(M *m)
{
	m->schedlink = sched.mhead;
	sched.mhead = m;
	sched.mwait++;
}

// Get from `m' list.  Sched must be locked.
static M*
mget(void)
{
	M *m;
Russ Cox's avatar
Russ Cox committed
255

Russ Cox's avatar
Russ Cox committed
256 257 258 259 260 261 262 263 264 265 266 267 268 269
	m = sched.mhead;
	if(m){
		sched.mhead = m->schedlink;
		sched.mwait--;
	}
	return m;
}

// Put on gfree list.  Sched must be locked.
static void
gfput(G *g)
{
	g->schedlink = sched.gfree;
	sched.gfree = g;
270
}
Ken Thompson's avatar
Ken Thompson committed
271

Russ Cox's avatar
Russ Cox committed
272 273 274 275 276
// Get from gfree list.  Sched must be locked.
static G*
gfget(void)
{
	G *g;
Russ Cox's avatar
Russ Cox committed
277

Russ Cox's avatar
Russ Cox committed
278 279 280 281 282 283 284
	g = sched.gfree;
	if(g)
		sched.gfree = g->schedlink;
	return g;
}

// Mark g ready to run.
285 286 287
void
ready(G *g)
{
Russ Cox's avatar
Russ Cox committed
288 289 290 291
	// Wait for g to stop running (for example, it migh
	// have queued itself on a channel but not yet gotten
	// a chance to call sys·gosched and actually go to sleep).
	notesleep(&g->stopped);
Russ Cox's avatar
Russ Cox committed
292

293
	lock(&sched);
Russ Cox's avatar
Russ Cox committed
294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309
	readylocked(g);
	unlock(&sched);
}

// Mark g ready to run.  Sched is already locked,
// and g is known not to be running right now
// (i.e., ready has slept on g->stopped or the g was
// just allocated in sys·newproc).
static void
readylocked(G *g)
{
	M *m;

	// Mark runnable.
	if(g->status == Grunnable || g->status == Grunning)
		throw("bad g->status in ready");
310
	g->status = Grunnable;
Russ Cox's avatar
Russ Cox committed
311 312 313 314 315 316 317

	// Before we've gotten to main·main,
	// only queue new gs, don't run them
	// or try to allocate new ms for them.
	// That includes main·main itself.
	if(sched.predawn){
		gput(g);
318
	}
319

Russ Cox's avatar
Russ Cox committed
320 321 322 323 324
	// Else if there's an m waiting, give it g.
	else if((m = mget()) != nil){
		m->nextg = g;
		notewakeup(&m->havenextg);
	}
Russ Cox's avatar
Russ Cox committed
325

Russ Cox's avatar
Russ Cox committed
326 327 328 329 330 331 332
	// Else put g on queue, kicking off new m if needed.
	else{
		gput(g);
		if(sched.mcount < sched.mmax)
			mnew();
	}
}
333

Russ Cox's avatar
Russ Cox committed
334 335 336 337
// Get the next goroutine that m should run.
// Sched must be locked on entry, is unlocked on exit.
static G*
nextgandunlock(void)
338 339 340
{
	G *gp;

Russ Cox's avatar
Russ Cox committed
341 342 343
	if((gp = gget()) != nil){
		unlock(&sched);
		return gp;
Ken Thompson's avatar
Ken Thompson committed
344
	}
Russ Cox's avatar
Russ Cox committed
345 346 347 348 349 350 351

	mput(m);
	if(sched.mcount == sched.mwait)
		prints("warning: all goroutines are asleep - deadlock!\n");
	m->nextg = nil;
	noteclear(&m->havenextg);
	unlock(&sched);
Russ Cox's avatar
Russ Cox committed
352

Russ Cox's avatar
Russ Cox committed
353 354 355 356
	notesleep(&m->havenextg);
	if((gp = m->nextg) == nil)
		throw("bad m->nextg in nextgoroutine");
	m->nextg = nil;
357
	return gp;
Ken Thompson's avatar
Ken Thompson committed
358 359
}

Russ Cox's avatar
Russ Cox committed
360 361 362 363 364 365 366 367
// Called to start an M.
void
mstart(void)
{
	minit();
	scheduler();
}

Russ Cox's avatar
Russ Cox committed
368 369
// Scheduler loop: find g to run, run it, repeat.
static void
370
scheduler(void)
Ken Thompson's avatar
Ken Thompson committed
371 372
{
	G* gp;
373 374

	lock(&sched);
Russ Cox's avatar
Russ Cox committed
375
	if(gosave(&m->sched)){
Russ Cox's avatar
Russ Cox committed
376
		// Jumped here via gosave/gogo, so didn't
Russ Cox's avatar
Russ Cox committed
377 378
		// execute lock(&sched) above.
		lock(&sched);
Russ Cox's avatar
Russ Cox committed
379 380 381
		
		if(sched.predawn)
			throw("init sleeping");
Russ Cox's avatar
Russ Cox committed
382 383

		// Just finished running m->curg.
384
		gp = m->curg;
Russ Cox's avatar
Russ Cox committed
385
		gp->m = nil;	// for debugger
386
		switch(gp->status){
Russ Cox's avatar
Russ Cox committed
387
		case Grunnable:
388
		case Gdead:
Russ Cox's avatar
Russ Cox committed
389 390
			// Shouldn't have been running!
			throw("bad gp->status in sched");
391
		case Grunning:
Russ Cox's avatar
Russ Cox committed
392 393
			gp->status = Grunnable;
			gput(gp);
394
			break;
Russ Cox's avatar
Russ Cox committed
395 396 397 398
		case Gmoribund:
			gp->status = Gdead;
			if(--sched.gcount == 0)
				sys·exit(0);
399 400
			break;
		}
Russ Cox's avatar
Russ Cox committed
401
		notewakeup(&gp->stopped);
402 403
	}

Russ Cox's avatar
Russ Cox committed
404 405 406
	// Find (or wait for) g to run.  Unlocks sched.
	gp = nextgandunlock();
	noteclear(&gp->stopped);
407
	gp->status = Grunning;
Ken Thompson's avatar
Ken Thompson committed
408
	m->curg = gp;
Russ Cox's avatar
Russ Cox committed
409
	gp->m = m;	// for debugger
Ken Thompson's avatar
Ken Thompson committed
410 411 412 413
	g = gp;
	gogo(&gp->sched);
}

Russ Cox's avatar
Russ Cox committed
414 415 416 417
// Enter scheduler.  If g->status is Grunning,
// re-queues g and runs everyone else who is waiting
// before running g again.  If g->status is Gmoribund,
// kills off g.
418 419 420
void
sys·gosched(void)
{
421 422 423 424
	if(gosave(&g->sched) == 0){
		g = m->g0;
		gogo(&m->sched);
	}
425 426
}

Russ Cox's avatar
Russ Cox committed
427 428 429 430 431
// Fork off a new m.  Sched must be locked.
static void
mnew(void)
{
	M *m;
Russ Cox's avatar
Russ Cox committed
432

Russ Cox's avatar
Russ Cox committed
433 434 435 436 437
	sched.mcount++;
	if(debug){
		sys·printint(sched.mcount);
		prints(" threads\n");
	}
Russ Cox's avatar
Russ Cox committed
438

Russ Cox's avatar
Russ Cox committed
439 440 441
	m = mal(sizeof(M));
	m->g0 = malg(1024);
	newosproc(m, m->g0, m->g0->stackbase, mstart);
Russ Cox's avatar
Russ Cox committed
442 443
}

Ken Thompson's avatar
Ken Thompson committed
444
//
Russ Cox's avatar
Russ Cox committed
445
// the calling sequence for a routine tha
Ken Thompson's avatar
Ken Thompson committed
446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488
// needs N bytes stack, A args.
//
//	N1 = (N+160 > 4096)? N+160: 0
//	A1 = A
//
// if N <= 75
//	CMPQ	SP, 0(R15)
//	JHI	4(PC)
//	MOVQ	$(N1<<0) | (A1<<32)), AX
//	MOVQ	AX, 0(R14)
//	CALL	sys·morestack(SB)
//
// if N > 75
//	LEAQ	(-N-75)(SP), AX
//	CMPQ	AX, 0(R15)
//	JHI	4(PC)
//	MOVQ	$(N1<<0) | (A1<<32)), AX
//	MOVQ	AX, 0(R14)
//	CALL	sys·morestack(SB)
//

void
oldstack(void)
{
	Stktop *top;
	uint32 siz2;
	byte *sp;

// prints("oldstack m->cret = ");
// sys·printpointer((void*)m->cret);
// prints("\n");

	top = (Stktop*)m->curg->stackbase;

	siz2 = (top->magic>>32) & 0xffffLL;

	sp = (byte*)top;
	if(siz2 > 0) {
		siz2 = (siz2+7) & ~7;
		sp -= siz2;
		mcpy(top->oldsp+16, sp, siz2);
	}

Russ Cox's avatar
Russ Cox committed
489
	// call  no more functions after this point - stackguard disagrees with SP
490 491
	m->curg->stackbase = top->oldbase;
	m->curg->stackguard = top->oldguard;
Ken Thompson's avatar
Ken Thompson committed
492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532
	m->morestack.SP = top->oldsp+8;
	m->morestack.PC = (byte*)(*(uint64*)(top->oldsp+8));

	gogoret(&m->morestack, m->cret);
}

void
newstack(void)
{
	int32 siz1, siz2;
	Stktop *top;
	byte *stk, *sp;
	void (*fn)(void);

	siz1 = m->morearg & 0xffffffffLL;
	siz2 = (m->morearg>>32) & 0xffffLL;

// prints("newstack siz1=");
// sys·printint(siz1);
// prints(" siz2=");
// sys·printint(siz2);
// prints(" moresp=");
// sys·printpointer(m->moresp);
// prints("\n");

	if(siz1 < 4096)
		siz1 = 4096;
	stk = mal(siz1 + 1024);
	stk += 512;

	top = (Stktop*)(stk+siz1-sizeof(*top));

	top->oldbase = m->curg->stackbase;
	top->oldguard = m->curg->stackguard;
	top->oldsp = m->moresp;
	top->magic = m->morearg;

	m->curg->stackbase = (byte*)top;
	m->curg->stackguard = stk + 160;

	sp = (byte*)top;
Russ Cox's avatar
Russ Cox committed
533

Ken Thompson's avatar
Ken Thompson committed
534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565
	if(siz2 > 0) {
		siz2 = (siz2+7) & ~7;
		sp -= siz2;
		mcpy(sp, m->moresp+16, siz2);
	}

	g = m->curg;
	fn = (void(*)(void))(*(uint64*)m->moresp);

// prints("fn=");
// sys·printpointer(fn);
// prints("\n");

	setspgoto(sp, fn, retfromnewstack);

	*(int32*)345 = 123;	// never return
}

void
sys·morestack(uint64 u)
{
	while(g == m->g0) {
		// very bad news
		*(int32*)123 = 123;
	}

	g = m->g0;
	m->moresp = (byte*)(&u-1);
	setspgoto(m->sched.SP, newstack, nil);

	*(int32*)234 = 123;	// never return
}