// 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" typedef struct Sched Sched; M m0; G g0; // idle goroutine for m0 // Maximum number of os procs (M's) to kick off. // Can override with $gomaxprocs environment variable. // For now set to 1 (single-threaded), because not // everything is properly locked (e.g., chans) and because // Darwin's multithreading code isn't implemented. int32 gomaxprocs = 1; static int32 debug = 0; struct Sched { G *runhead; G *runtail; int32 nwait; int32 nready; int32 ng; int32 nm; M *wait; Lock; }; Sched sched; void sys·goexit(void) { if(debug){ prints("goexit goid="); sys·printint(g->goid); prints("\n"); } g->status = Gdead; sys·gosched(); } void schedinit(void) { byte *p; extern int32 getenvc(void); p = getenv("gomaxprocs"); if(p && '0' <= *p && *p <= '9') gomaxprocs = atoi(p); sched.nm = 1; sched.nwait = 1; } 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; if(siz > 1024) throw("sys·newproc: too many args"); // try to rip off an old goroutine for(newg=allg; newg!=nil; newg=newg->alllink) if(newg->status == Gdead) break; if(newg == nil) { newg = mal(sizeof(G)); stk = mal(4096); newg->stack0 = stk; newg->status = Gwaiting; newg->alllink = allg; allg = newg; } else { stk = newg->stack0; newg->status = Gwaiting; } 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; lock(&sched); sched.ng++; goidgen++; newg->goid = goidgen; unlock(&sched); ready(newg); //prints(" goid="); //sys·printint(newg->goid); //prints("\n"); } void tracebackothers(G *me) { G *g; for(g = allg; g != nil; g = g->alllink) { if(g == me || g->status == Gdead) 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!) } } void newmach(void); static void readylocked(G *g) { g->status = Grunnable; if(sched.runhead == nil) sched.runhead = g; else sched.runtail->runlink = g; sched.runtail = g; g->runlink = nil; sched.nready++; // Don't wake up another scheduler. // This only gets called when we're // about to reschedule anyway. } static Lock print; void ready(G *g) { M *mm; // gp might be running on another scheduler. // (E.g., it queued and then we decided to wake it up // before it had a chance to sys·gosched().) // Grabbing the runlock ensures that it is not running elsewhere. // You can delete the if check, but don't delete the // lock/unlock sequence (being able to grab the lock // means the proc has gone to sleep). lock(&g->runlock); if(g->status == Grunnable || g->status == Grunning) *(int32*)0x1023 = 0x1023; lock(&sched); g->status = Grunnable; if(sched.runhead == nil) sched.runhead = g; else sched.runtail->runlink = g; sched.runtail = g; g->runlink = nil; unlock(&g->runlock); sched.nready++; if(sched.nready > sched.nwait) if(gomaxprocs == 0 || sched.nm < gomaxprocs){ if(debug){ prints("new scheduler: "); sys·printint(sched.nready); prints(" > "); sys·printint(sched.nwait); prints("\n"); } sched.nwait++; newmach(); } if(sched.wait){ mm = sched.wait; sched.wait = mm->waitlink; rwakeupandunlock(&mm->waitr); }else unlock(&sched); } extern void p0(void), p1(void); G* nextgoroutine(void) { G *gp; while((gp = sched.runhead) == nil){ if(debug){ prints("nextgoroutine runhead=nil ng="); sys·printint(sched.ng); prints("\n"); } if(sched.ng == 0) return nil; m->waitlink = sched.wait; m->waitr.l = &sched.Lock; sched.wait = m; sched.nwait++; if(sched.nm == sched.nwait) prints("all goroutines are asleep - deadlock!\n"); rsleep(&m->waitr); sched.nwait--; } sched.nready--; sched.runhead = gp->runlink; return gp; } void scheduler(void) { G* gp; m->pid = getprocid(); gosave(&m->sched); lock(&sched); if(m->curg == nil){ // Brand new scheduler; nwait counts us. // Not anymore. sched.nwait--; }else{ gp = m->curg; gp->m = nil; switch(gp->status){ case Gdead: sched.ng--; if(debug){ prints("sched: dead: "); sys·printint(sched.ng); prints("\n"); } break; case Grunning: readylocked(gp); break; case Grunnable: // don't want to see this *(int32*)0x456 = 0x234; break; } unlock(&gp->runlock); } gp = nextgoroutine(); if(gp == nil) { // prints("sched: no more work\n"); sys·exit(0); } unlock(&sched); lock(&gp->runlock); gp->status = Grunning; m->curg = gp; gp->m = m; g = gp; gogo(&gp->sched); } void newmach(void) { M *mm; byte *stk, *stktop; int64 ret; sched.nm++; if(!(sched.nm&(sched.nm-1))){ sys·printint(sched.nm); prints(" threads\n"); } mm = mal(sizeof(M)+sizeof(G)+1024+104); sys·memclr((byte*)mm, sizeof(M)); mm->g0 = (G*)(mm+1); sys·memclr((byte*)mm->g0, sizeof(G)); stk = (byte*)mm->g0 + 104; stktop = stk + 1024; mm->g0->stackguard = stk; mm->g0->stackbase = stktop; newosproc(mm, mm->g0, stktop, (void(*)(void*))scheduler, nil); } void gom0init(void) { scheduler(); } void sys·gosched(void) { if(gosave(&g->sched) == 0){ // (rsc) signal race here? g = m->g0; gogo(&m->sched); } } // // the calling sequence for a routine that // 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; m->curg->stackbase = top->oldbase; m->curg->stackguard = top->oldguard; siz2 = (top->magic>>32) & 0xffffLL; sp = (byte*)top; if(siz2 > 0) { siz2 = (siz2+7) & ~7; sp -= siz2; mcpy(top->oldsp+16, sp, siz2); } m->morestack.SP = top->oldsp+8; m->morestack.PC = (byte*)(*(uint64*)(top->oldsp+8)); // prints("oldstack sp="); // sys·printpointer(m->morestack.SP); // prints(" pc="); // sys·printpointer(m->morestack.PC); // prints("\n"); 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; 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 }