Ken Thompson | af58f17 | 2008-07-14 14:34:27 -0700 | [diff] [blame] | 1 | // Copyright 2009 The Go Authors. All rights reserved. |
| 2 | // Use of this source code is governed by a BSD-style |
| 3 | // license that can be found in the LICENSE file. |
| 4 | |
| 5 | #include "runtime.h" |
Russ Cox | 1ce1791 | 2009-01-26 17:37:05 -0800 | [diff] [blame] | 6 | #include "malloc.h" |
Ken Thompson | af58f17 | 2008-07-14 14:34:27 -0700 | [diff] [blame] | 7 | |
Russ Cox | d28acc4 | 2008-08-04 16:43:49 -0700 | [diff] [blame] | 8 | typedef struct Sched Sched; |
| 9 | |
| 10 | M m0; |
| 11 | G g0; // idle goroutine for m0 |
| 12 | |
Ken Thompson | af58f17 | 2008-07-14 14:34:27 -0700 | [diff] [blame] | 13 | static int32 debug = 0; |
Russ Cox | efc86a7 | 2008-11-25 16:48:10 -0800 | [diff] [blame] | 14 | static Lock debuglock; |
Ken Thompson | af58f17 | 2008-07-14 14:34:27 -0700 | [diff] [blame] | 15 | |
Russ Cox | 9682400 | 2008-08-05 14:18:47 -0700 | [diff] [blame] | 16 | // Go scheduler |
| 17 | // |
| 18 | // The go scheduler's job is to match ready-to-run goroutines (`g's) |
| 19 | // with waiting-for-work schedulers (`m's). If there are ready gs |
| 20 | // and no waiting ms, ready() will start a new m running in a new |
| 21 | // OS thread, so that all ready gs can run simultaneously, up to a limit. |
| 22 | // For now, ms never go away. |
| 23 | // |
| 24 | // The default maximum number of ms is one: go runs single-threaded. |
| 25 | // This is because some locking details have to be worked ou |
| 26 | // (select in particular is not locked properly) and because the low-level |
| 27 | // code hasn't been written yet for OS X. Setting the environmen |
| 28 | // variable $gomaxprocs changes sched.mmax for now. |
| 29 | // |
| 30 | // Even a program that can run without deadlock in a single process |
| 31 | // might use more ms if given the chance. For example, the prime |
| 32 | // sieve will use as many ms as there are primes (up to sched.mmax), |
| 33 | // allowing different stages of the pipeline to execute in parallel. |
| 34 | // We could revisit this choice, only kicking off new ms for blocking |
| 35 | // system calls, but that would limit the amount of parallel computation |
| 36 | // that go would try to do. |
| 37 | // |
| 38 | // In general, one could imagine all sorts of refinements to the |
| 39 | // scheduler, but the goal now is just to get something working on |
| 40 | // Linux and OS X. |
| 41 | |
Russ Cox | d28acc4 | 2008-08-04 16:43:49 -0700 | [diff] [blame] | 42 | struct Sched { |
Russ Cox | d28acc4 | 2008-08-04 16:43:49 -0700 | [diff] [blame] | 43 | Lock; |
Russ Cox | 9682400 | 2008-08-05 14:18:47 -0700 | [diff] [blame] | 44 | |
| 45 | G *gfree; // available gs (status == Gdead) |
Russ Cox | f7f6329 | 2008-08-05 14:21:42 -0700 | [diff] [blame] | 46 | |
Russ Cox | 9682400 | 2008-08-05 14:18:47 -0700 | [diff] [blame] | 47 | G *ghead; // gs waiting to run |
| 48 | G *gtail; |
| 49 | int32 gwait; // number of gs waiting to run |
| 50 | int32 gcount; // number of gs that are alive |
Russ Cox | f7f6329 | 2008-08-05 14:21:42 -0700 | [diff] [blame] | 51 | |
Russ Cox | 9682400 | 2008-08-05 14:18:47 -0700 | [diff] [blame] | 52 | M *mhead; // ms waiting for work |
| 53 | int32 mwait; // number of ms waiting for work |
Russ Cox | efc86a7 | 2008-11-25 16:48:10 -0800 | [diff] [blame] | 54 | int32 mcount; // number of ms that have been created |
| 55 | int32 mcpu; // number of ms executing on cpu |
| 56 | int32 mcpumax; // max number of ms allowed on cpu |
Russ Cox | 3f8aa66 | 2008-12-05 15:24:18 -0800 | [diff] [blame] | 57 | int32 gomaxprocs; |
Russ Cox | efc86a7 | 2008-11-25 16:48:10 -0800 | [diff] [blame] | 58 | int32 msyscall; // number of ms in system calls |
Russ Cox | f7f6329 | 2008-08-05 14:21:42 -0700 | [diff] [blame] | 59 | |
Russ Cox | 9682400 | 2008-08-05 14:18:47 -0700 | [diff] [blame] | 60 | int32 predawn; // running initialization, don't run new gs. |
Russ Cox | 3f8aa66 | 2008-12-05 15:24:18 -0800 | [diff] [blame] | 61 | |
| 62 | Note stopped; // one g can wait here for ms to stop |
Russ Cox | be62913 | 2008-12-08 17:14:08 -0800 | [diff] [blame] | 63 | int32 waitstop; // after setting this flag |
Russ Cox | d28acc4 | 2008-08-04 16:43:49 -0700 | [diff] [blame] | 64 | }; |
| 65 | |
| 66 | Sched sched; |
| 67 | |
Russ Cox | 9682400 | 2008-08-05 14:18:47 -0700 | [diff] [blame] | 68 | // Scheduling helpers. Sched must be locked. |
| 69 | static void gput(G*); // put/get on ghead/gtail |
| 70 | static G* gget(void); |
| 71 | static void mput(M*); // put/get on mhead |
Russ Cox | 218c393 | 2009-07-13 17:28:39 -0700 | [diff] [blame] | 72 | static M* mget(G*); |
Russ Cox | 9682400 | 2008-08-05 14:18:47 -0700 | [diff] [blame] | 73 | static void gfput(G*); // put/get on gfree |
| 74 | static G* gfget(void); |
Russ Cox | efc86a7 | 2008-11-25 16:48:10 -0800 | [diff] [blame] | 75 | static void matchmg(void); // match ms to gs |
Russ Cox | 9682400 | 2008-08-05 14:18:47 -0700 | [diff] [blame] | 76 | static void readylocked(G*); // ready, but sched is locked |
Russ Cox | 218c393 | 2009-07-13 17:28:39 -0700 | [diff] [blame] | 77 | static void mnextg(M*, G*); |
Russ Cox | 9682400 | 2008-08-05 14:18:47 -0700 | [diff] [blame] | 78 | |
| 79 | // Scheduler loop. |
| 80 | static void scheduler(void); |
| 81 | |
Russ Cox | a67258f | 2008-09-18 15:56:46 -0700 | [diff] [blame] | 82 | // The bootstrap sequence is: |
| 83 | // |
| 84 | // call osinit |
| 85 | // call schedinit |
| 86 | // make & queue new G |
| 87 | // call mstart |
| 88 | // |
| 89 | // The new G does: |
| 90 | // |
| 91 | // call main·init_function |
| 92 | // call initdone |
| 93 | // call main·main |
Russ Cox | 9682400 | 2008-08-05 14:18:47 -0700 | [diff] [blame] | 94 | void |
| 95 | schedinit(void) |
| 96 | { |
| 97 | int32 n; |
| 98 | byte *p; |
Russ Cox | f7f6329 | 2008-08-05 14:21:42 -0700 | [diff] [blame] | 99 | |
Russ Cox | e29ce17 | 2008-12-18 15:42:28 -0800 | [diff] [blame] | 100 | mallocinit(); |
Russ Cox | 3609624 | 2009-01-16 14:58:14 -0800 | [diff] [blame] | 101 | goargs(); |
| 102 | |
Russ Cox | da0a7d7 | 2008-12-19 03:13:39 -0800 | [diff] [blame] | 103 | // Allocate internal symbol table representation now, |
| 104 | // so that we don't need to call malloc when we crash. |
| 105 | findfunc(0); |
Russ Cox | e29ce17 | 2008-12-18 15:42:28 -0800 | [diff] [blame] | 106 | |
Russ Cox | 3f8aa66 | 2008-12-05 15:24:18 -0800 | [diff] [blame] | 107 | sched.gomaxprocs = 1; |
Russ Cox | 9350ef4 | 2008-09-17 13:49:23 -0700 | [diff] [blame] | 108 | p = getenv("GOMAXPROCS"); |
Russ Cox | 9682400 | 2008-08-05 14:18:47 -0700 | [diff] [blame] | 109 | if(p != nil && (n = atoi(p)) != 0) |
Russ Cox | 3f8aa66 | 2008-12-05 15:24:18 -0800 | [diff] [blame] | 110 | sched.gomaxprocs = n; |
| 111 | sched.mcpumax = sched.gomaxprocs; |
Russ Cox | 9682400 | 2008-08-05 14:18:47 -0700 | [diff] [blame] | 112 | sched.mcount = 1; |
| 113 | sched.predawn = 1; |
| 114 | } |
| 115 | |
Russ Cox | a67258f | 2008-09-18 15:56:46 -0700 | [diff] [blame] | 116 | // Called after main·init_function; main·main will be called on return. |
Russ Cox | 9682400 | 2008-08-05 14:18:47 -0700 | [diff] [blame] | 117 | void |
Russ Cox | a67258f | 2008-09-18 15:56:46 -0700 | [diff] [blame] | 118 | initdone(void) |
Russ Cox | 9682400 | 2008-08-05 14:18:47 -0700 | [diff] [blame] | 119 | { |
Russ Cox | 9682400 | 2008-08-05 14:18:47 -0700 | [diff] [blame] | 120 | // Let's go. |
| 121 | sched.predawn = 0; |
Russ Cox | 1ce1791 | 2009-01-26 17:37:05 -0800 | [diff] [blame] | 122 | mstats.enablegc = 1; |
Russ Cox | 9682400 | 2008-08-05 14:18:47 -0700 | [diff] [blame] | 123 | |
Russ Cox | 9682400 | 2008-08-05 14:18:47 -0700 | [diff] [blame] | 124 | // If main·init_function started other goroutines, |
| 125 | // kick off new ms to handle them, like ready |
| 126 | // would have, had it not been pre-dawn. |
Russ Cox | efc86a7 | 2008-11-25 16:48:10 -0800 | [diff] [blame] | 127 | lock(&sched); |
| 128 | matchmg(); |
| 129 | unlock(&sched); |
Russ Cox | 9682400 | 2008-08-05 14:18:47 -0700 | [diff] [blame] | 130 | } |
| 131 | |
Ken Thompson | af58f17 | 2008-07-14 14:34:27 -0700 | [diff] [blame] | 132 | void |
Russ Cox | 918afd94 | 2009-05-08 15:21:41 -0700 | [diff] [blame] | 133 | goexit(void) |
Ken Thompson | af58f17 | 2008-07-14 14:34:27 -0700 | [diff] [blame] | 134 | { |
Russ Cox | 9682400 | 2008-08-05 14:18:47 -0700 | [diff] [blame] | 135 | g->status = Gmoribund; |
Russ Cox | 918afd94 | 2009-05-08 15:21:41 -0700 | [diff] [blame] | 136 | gosched(); |
Ken Thompson | af58f17 | 2008-07-14 14:34:27 -0700 | [diff] [blame] | 137 | } |
| 138 | |
Ken Thompson | 1e1cc4e | 2009-01-27 12:03:53 -0800 | [diff] [blame] | 139 | void |
Rob Pike | 3835e01 | 2008-07-28 11:29:41 -0700 | [diff] [blame] | 140 | tracebackothers(G *me) |
| 141 | { |
| 142 | G *g; |
| 143 | |
| 144 | for(g = allg; g != nil; g = g->alllink) { |
Russ Cox | d28acc4 | 2008-08-04 16:43:49 -0700 | [diff] [blame] | 145 | if(g == me || g->status == Gdead) |
Rob Pike | 3835e01 | 2008-07-28 11:29:41 -0700 | [diff] [blame] | 146 | continue; |
Russ Cox | efc86a7 | 2008-11-25 16:48:10 -0800 | [diff] [blame] | 147 | printf("\ngoroutine %d:\n", g->goid); |
Russ Cox | 7343e03 | 2009-06-17 15:12:16 -0700 | [diff] [blame] | 148 | traceback(g->sched.pc, g->sched.sp, g); |
Rob Pike | 3835e01 | 2008-07-28 11:29:41 -0700 | [diff] [blame] | 149 | } |
| 150 | } |
| 151 | |
Russ Cox | 9682400 | 2008-08-05 14:18:47 -0700 | [diff] [blame] | 152 | // Put on `g' queue. Sched must be locked. |
Russ Cox | d28acc4 | 2008-08-04 16:43:49 -0700 | [diff] [blame] | 153 | static void |
Russ Cox | 9682400 | 2008-08-05 14:18:47 -0700 | [diff] [blame] | 154 | gput(G *g) |
Russ Cox | d28acc4 | 2008-08-04 16:43:49 -0700 | [diff] [blame] | 155 | { |
Russ Cox | 218c393 | 2009-07-13 17:28:39 -0700 | [diff] [blame] | 156 | M *m; |
| 157 | |
| 158 | // If g is wired, hand it off directly. |
| 159 | if((m = g->lockedm) != nil) { |
| 160 | mnextg(m, g); |
| 161 | return; |
| 162 | } |
| 163 | |
Russ Cox | 9682400 | 2008-08-05 14:18:47 -0700 | [diff] [blame] | 164 | g->schedlink = nil; |
| 165 | if(sched.ghead == nil) |
| 166 | sched.ghead = g; |
Russ Cox | d28acc4 | 2008-08-04 16:43:49 -0700 | [diff] [blame] | 167 | else |
Russ Cox | 9682400 | 2008-08-05 14:18:47 -0700 | [diff] [blame] | 168 | sched.gtail->schedlink = g; |
| 169 | sched.gtail = g; |
| 170 | sched.gwait++; |
Russ Cox | d28acc4 | 2008-08-04 16:43:49 -0700 | [diff] [blame] | 171 | } |
| 172 | |
Russ Cox | 9682400 | 2008-08-05 14:18:47 -0700 | [diff] [blame] | 173 | // Get from `g' queue. Sched must be locked. |
| 174 | static G* |
| 175 | gget(void) |
| 176 | { |
| 177 | G *g; |
Russ Cox | f7f6329 | 2008-08-05 14:21:42 -0700 | [diff] [blame] | 178 | |
Russ Cox | 9682400 | 2008-08-05 14:18:47 -0700 | [diff] [blame] | 179 | g = sched.ghead; |
| 180 | if(g){ |
| 181 | sched.ghead = g->schedlink; |
| 182 | if(sched.ghead == nil) |
| 183 | sched.gtail = nil; |
| 184 | sched.gwait--; |
| 185 | } |
| 186 | return g; |
| 187 | } |
| 188 | |
| 189 | // Put on `m' list. Sched must be locked. |
| 190 | static void |
| 191 | mput(M *m) |
| 192 | { |
| 193 | m->schedlink = sched.mhead; |
| 194 | sched.mhead = m; |
| 195 | sched.mwait++; |
| 196 | } |
| 197 | |
Russ Cox | 218c393 | 2009-07-13 17:28:39 -0700 | [diff] [blame] | 198 | // Get an `m' to run `g'. Sched must be locked. |
Russ Cox | 9682400 | 2008-08-05 14:18:47 -0700 | [diff] [blame] | 199 | static M* |
Russ Cox | 218c393 | 2009-07-13 17:28:39 -0700 | [diff] [blame] | 200 | mget(G *g) |
Russ Cox | 9682400 | 2008-08-05 14:18:47 -0700 | [diff] [blame] | 201 | { |
| 202 | M *m; |
Russ Cox | f7f6329 | 2008-08-05 14:21:42 -0700 | [diff] [blame] | 203 | |
Russ Cox | 218c393 | 2009-07-13 17:28:39 -0700 | [diff] [blame] | 204 | // if g has its own m, use it. |
| 205 | if((m = g->lockedm) != nil) |
| 206 | return m; |
| 207 | |
| 208 | // otherwise use general m pool. |
| 209 | if((m = sched.mhead) != nil){ |
Russ Cox | 9682400 | 2008-08-05 14:18:47 -0700 | [diff] [blame] | 210 | sched.mhead = m->schedlink; |
| 211 | sched.mwait--; |
| 212 | } |
| 213 | return m; |
| 214 | } |
| 215 | |
| 216 | // Put on gfree list. Sched must be locked. |
| 217 | static void |
| 218 | gfput(G *g) |
| 219 | { |
| 220 | g->schedlink = sched.gfree; |
| 221 | sched.gfree = g; |
| 222 | } |
| 223 | |
| 224 | // Get from gfree list. Sched must be locked. |
| 225 | static G* |
| 226 | gfget(void) |
| 227 | { |
| 228 | G *g; |
Russ Cox | f7f6329 | 2008-08-05 14:21:42 -0700 | [diff] [blame] | 229 | |
Russ Cox | 9682400 | 2008-08-05 14:18:47 -0700 | [diff] [blame] | 230 | g = sched.gfree; |
| 231 | if(g) |
| 232 | sched.gfree = g->schedlink; |
| 233 | return g; |
| 234 | } |
| 235 | |
| 236 | // Mark g ready to run. |
Russ Cox | d28acc4 | 2008-08-04 16:43:49 -0700 | [diff] [blame] | 237 | void |
| 238 | ready(G *g) |
| 239 | { |
Russ Cox | d28acc4 | 2008-08-04 16:43:49 -0700 | [diff] [blame] | 240 | lock(&sched); |
Russ Cox | 9682400 | 2008-08-05 14:18:47 -0700 | [diff] [blame] | 241 | readylocked(g); |
| 242 | unlock(&sched); |
Russ Cox | d28acc4 | 2008-08-04 16:43:49 -0700 | [diff] [blame] | 243 | } |
| 244 | |
Russ Cox | a61bb95 | 2008-09-24 14:13:07 -0700 | [diff] [blame] | 245 | // Mark g ready to run. Sched is already locked. |
| 246 | // G might be running already and about to stop. |
| 247 | // The sched lock protects g->status from changing underfoot. |
Russ Cox | 9682400 | 2008-08-05 14:18:47 -0700 | [diff] [blame] | 248 | static void |
| 249 | readylocked(G *g) |
| 250 | { |
Russ Cox | a61bb95 | 2008-09-24 14:13:07 -0700 | [diff] [blame] | 251 | if(g->m){ |
| 252 | // Running on another machine. |
| 253 | // Ready it when it stops. |
| 254 | g->readyonstop = 1; |
| 255 | return; |
| 256 | } |
Russ Cox | d28acc4 | 2008-08-04 16:43:49 -0700 | [diff] [blame] | 257 | |
Russ Cox | 9682400 | 2008-08-05 14:18:47 -0700 | [diff] [blame] | 258 | // Mark runnable. |
| 259 | if(g->status == Grunnable || g->status == Grunning) |
| 260 | throw("bad g->status in ready"); |
| 261 | g->status = Grunnable; |
| 262 | |
Russ Cox | efc86a7 | 2008-11-25 16:48:10 -0800 | [diff] [blame] | 263 | gput(g); |
| 264 | if(!sched.predawn) |
| 265 | matchmg(); |
Russ Cox | 9682400 | 2008-08-05 14:18:47 -0700 | [diff] [blame] | 266 | } |
| 267 | |
Russ Cox | fe8ff95 | 2009-08-31 18:10:11 -0700 | [diff] [blame^] | 268 | // Same as readylocked but a different symbol so that |
| 269 | // debuggers can set a breakpoint here and catch all |
| 270 | // new goroutines. |
| 271 | static void |
| 272 | newprocreadylocked(G *g) |
| 273 | { |
| 274 | readylocked(g); |
| 275 | } |
| 276 | |
Russ Cox | 218c393 | 2009-07-13 17:28:39 -0700 | [diff] [blame] | 277 | // Pass g to m for running. |
| 278 | static void |
| 279 | mnextg(M *m, G *g) |
| 280 | { |
| 281 | sched.mcpu++; |
| 282 | m->nextg = g; |
| 283 | if(m->waitnextg) { |
| 284 | m->waitnextg = 0; |
| 285 | notewakeup(&m->havenextg); |
| 286 | } |
| 287 | } |
| 288 | |
Russ Cox | 9682400 | 2008-08-05 14:18:47 -0700 | [diff] [blame] | 289 | // Get the next goroutine that m should run. |
| 290 | // Sched must be locked on entry, is unlocked on exit. |
Russ Cox | efc86a7 | 2008-11-25 16:48:10 -0800 | [diff] [blame] | 291 | // Makes sure that at most $GOMAXPROCS gs are |
| 292 | // running on cpus (not in system calls) at any given time. |
Russ Cox | 9682400 | 2008-08-05 14:18:47 -0700 | [diff] [blame] | 293 | static G* |
| 294 | nextgandunlock(void) |
Ken Thompson | af58f17 | 2008-07-14 14:34:27 -0700 | [diff] [blame] | 295 | { |
Ken Thompson | e7d549f | 2008-07-16 13:50:23 -0700 | [diff] [blame] | 296 | G *gp; |
Ken Thompson | af58f17 | 2008-07-14 14:34:27 -0700 | [diff] [blame] | 297 | |
Russ Cox | 218c393 | 2009-07-13 17:28:39 -0700 | [diff] [blame] | 298 | if(sched.mcpu < 0) |
| 299 | throw("negative sched.mcpu"); |
| 300 | |
| 301 | // If there is a g waiting as m->nextg, |
| 302 | // mnextg took care of the sched.mcpu++. |
Russ Cox | efc86a7 | 2008-11-25 16:48:10 -0800 | [diff] [blame] | 303 | if(m->nextg != nil) { |
| 304 | gp = m->nextg; |
| 305 | m->nextg = nil; |
Russ Cox | 9682400 | 2008-08-05 14:18:47 -0700 | [diff] [blame] | 306 | unlock(&sched); |
| 307 | return gp; |
Ken Thompson | af58f17 | 2008-07-14 14:34:27 -0700 | [diff] [blame] | 308 | } |
Russ Cox | 9682400 | 2008-08-05 14:18:47 -0700 | [diff] [blame] | 309 | |
Russ Cox | 218c393 | 2009-07-13 17:28:39 -0700 | [diff] [blame] | 310 | if(m->lockedg != nil) { |
| 311 | // We can only run one g, and it's not available. |
| 312 | // Make sure some other cpu is running to handle |
| 313 | // the ordinary run queue. |
| 314 | if(sched.gwait != 0) |
| 315 | matchmg(); |
| 316 | } else { |
| 317 | // Look for work on global queue. |
| 318 | while(sched.mcpu < sched.mcpumax && (gp=gget()) != nil) { |
| 319 | if(gp->lockedm) { |
| 320 | mnextg(gp->lockedm, gp); |
| 321 | continue; |
| 322 | } |
| 323 | sched.mcpu++; // this m will run gp |
| 324 | unlock(&sched); |
| 325 | return gp; |
Russ Cox | efc86a7 | 2008-11-25 16:48:10 -0800 | [diff] [blame] | 326 | } |
Russ Cox | 218c393 | 2009-07-13 17:28:39 -0700 | [diff] [blame] | 327 | // Otherwise, wait on global m queue. |
| 328 | mput(m); |
Russ Cox | efc86a7 | 2008-11-25 16:48:10 -0800 | [diff] [blame] | 329 | } |
Russ Cox | efc86a7 | 2008-11-25 16:48:10 -0800 | [diff] [blame] | 330 | if(sched.mcpu == 0 && sched.msyscall == 0) |
Russ Cox | 72154b0 | 2008-09-26 14:10:26 -0700 | [diff] [blame] | 331 | throw("all goroutines are asleep - deadlock!"); |
Russ Cox | 9682400 | 2008-08-05 14:18:47 -0700 | [diff] [blame] | 332 | m->nextg = nil; |
Russ Cox | 218c393 | 2009-07-13 17:28:39 -0700 | [diff] [blame] | 333 | m->waitnextg = 1; |
Russ Cox | 9682400 | 2008-08-05 14:18:47 -0700 | [diff] [blame] | 334 | noteclear(&m->havenextg); |
Russ Cox | 53e69e1 | 2009-01-27 14:01:20 -0800 | [diff] [blame] | 335 | if(sched.waitstop && sched.mcpu <= sched.mcpumax) { |
Russ Cox | be62913 | 2008-12-08 17:14:08 -0800 | [diff] [blame] | 336 | sched.waitstop = 0; |
| 337 | notewakeup(&sched.stopped); |
| 338 | } |
Russ Cox | 9682400 | 2008-08-05 14:18:47 -0700 | [diff] [blame] | 339 | unlock(&sched); |
Russ Cox | f7f6329 | 2008-08-05 14:21:42 -0700 | [diff] [blame] | 340 | |
Russ Cox | 9682400 | 2008-08-05 14:18:47 -0700 | [diff] [blame] | 341 | notesleep(&m->havenextg); |
| 342 | if((gp = m->nextg) == nil) |
| 343 | throw("bad m->nextg in nextgoroutine"); |
| 344 | m->nextg = nil; |
Russ Cox | d28acc4 | 2008-08-04 16:43:49 -0700 | [diff] [blame] | 345 | return gp; |
Ken Thompson | af58f17 | 2008-07-14 14:34:27 -0700 | [diff] [blame] | 346 | } |
| 347 | |
Russ Cox | 3f8aa66 | 2008-12-05 15:24:18 -0800 | [diff] [blame] | 348 | // TODO(rsc): Remove. This is only temporary, |
| 349 | // for the mark and sweep collector. |
| 350 | void |
| 351 | stoptheworld(void) |
| 352 | { |
| 353 | lock(&sched); |
| 354 | sched.mcpumax = 1; |
| 355 | while(sched.mcpu > 1) { |
| 356 | noteclear(&sched.stopped); |
Russ Cox | be62913 | 2008-12-08 17:14:08 -0800 | [diff] [blame] | 357 | sched.waitstop = 1; |
Russ Cox | 3f8aa66 | 2008-12-05 15:24:18 -0800 | [diff] [blame] | 358 | unlock(&sched); |
| 359 | notesleep(&sched.stopped); |
| 360 | lock(&sched); |
| 361 | } |
| 362 | unlock(&sched); |
| 363 | } |
| 364 | |
| 365 | // TODO(rsc): Remove. This is only temporary, |
| 366 | // for the mark and sweep collector. |
| 367 | void |
| 368 | starttheworld(void) |
| 369 | { |
| 370 | lock(&sched); |
| 371 | sched.mcpumax = sched.gomaxprocs; |
| 372 | matchmg(); |
| 373 | unlock(&sched); |
| 374 | } |
| 375 | |
Russ Cox | a67258f | 2008-09-18 15:56:46 -0700 | [diff] [blame] | 376 | // Called to start an M. |
| 377 | void |
| 378 | mstart(void) |
| 379 | { |
Russ Cox | e29ce17 | 2008-12-18 15:42:28 -0800 | [diff] [blame] | 380 | if(m->mcache == nil) |
| 381 | m->mcache = allocmcache(); |
Russ Cox | a67258f | 2008-09-18 15:56:46 -0700 | [diff] [blame] | 382 | minit(); |
| 383 | scheduler(); |
| 384 | } |
| 385 | |
Russ Cox | efc86a7 | 2008-11-25 16:48:10 -0800 | [diff] [blame] | 386 | // Kick of new ms as needed (up to mcpumax). |
| 387 | // There are already `other' other cpus that will |
| 388 | // start looking for goroutines shortly. |
| 389 | // Sched is locked. |
| 390 | static void |
| 391 | matchmg(void) |
| 392 | { |
| 393 | M *m; |
| 394 | G *g; |
| 395 | |
Russ Cox | efc86a7 | 2008-11-25 16:48:10 -0800 | [diff] [blame] | 396 | while(sched.mcpu < sched.mcpumax && (g = gget()) != nil){ |
Russ Cox | 218c393 | 2009-07-13 17:28:39 -0700 | [diff] [blame] | 397 | // Find the m that will run g. |
| 398 | if((m = mget(g)) == nil){ |
Russ Cox | 1ce1791 | 2009-01-26 17:37:05 -0800 | [diff] [blame] | 399 | m = malloc(sizeof(M)); |
Russ Cox | da0a7d7 | 2008-12-19 03:13:39 -0800 | [diff] [blame] | 400 | m->g0 = malg(8192); |
Russ Cox | efc86a7 | 2008-11-25 16:48:10 -0800 | [diff] [blame] | 401 | m->id = sched.mcount++; |
Russ Cox | efc86a7 | 2008-11-25 16:48:10 -0800 | [diff] [blame] | 402 | newosproc(m, m->g0, m->g0->stackbase, mstart); |
| 403 | } |
Russ Cox | 218c393 | 2009-07-13 17:28:39 -0700 | [diff] [blame] | 404 | mnextg(m, g); |
Russ Cox | efc86a7 | 2008-11-25 16:48:10 -0800 | [diff] [blame] | 405 | } |
| 406 | } |
| 407 | |
Russ Cox | 9682400 | 2008-08-05 14:18:47 -0700 | [diff] [blame] | 408 | // Scheduler loop: find g to run, run it, repeat. |
| 409 | static void |
Russ Cox | 4feda71 | 2008-08-02 22:34:04 -0700 | [diff] [blame] | 410 | scheduler(void) |
| 411 | { |
| 412 | G* gp; |
Russ Cox | d28acc4 | 2008-08-04 16:43:49 -0700 | [diff] [blame] | 413 | |
Russ Cox | d28acc4 | 2008-08-04 16:43:49 -0700 | [diff] [blame] | 414 | lock(&sched); |
Russ Cox | 7343e03 | 2009-06-17 15:12:16 -0700 | [diff] [blame] | 415 | if(gosave(&m->sched) != 0){ |
Russ Cox | a67258f | 2008-09-18 15:56:46 -0700 | [diff] [blame] | 416 | // Jumped here via gosave/gogo, so didn't |
Russ Cox | 9682400 | 2008-08-05 14:18:47 -0700 | [diff] [blame] | 417 | // execute lock(&sched) above. |
| 418 | lock(&sched); |
Russ Cox | 72154b0 | 2008-09-26 14:10:26 -0700 | [diff] [blame] | 419 | |
Russ Cox | a67258f | 2008-09-18 15:56:46 -0700 | [diff] [blame] | 420 | if(sched.predawn) |
| 421 | throw("init sleeping"); |
Russ Cox | 9682400 | 2008-08-05 14:18:47 -0700 | [diff] [blame] | 422 | |
| 423 | // Just finished running m->curg. |
Russ Cox | d28acc4 | 2008-08-04 16:43:49 -0700 | [diff] [blame] | 424 | gp = m->curg; |
Russ Cox | a61bb95 | 2008-09-24 14:13:07 -0700 | [diff] [blame] | 425 | gp->m = nil; |
Russ Cox | efc86a7 | 2008-11-25 16:48:10 -0800 | [diff] [blame] | 426 | sched.mcpu--; |
Russ Cox | 218c393 | 2009-07-13 17:28:39 -0700 | [diff] [blame] | 427 | |
| 428 | if(sched.mcpu < 0) |
| 429 | throw("sched.mcpu < 0 in scheduler"); |
Russ Cox | d28acc4 | 2008-08-04 16:43:49 -0700 | [diff] [blame] | 430 | switch(gp->status){ |
Russ Cox | d28acc4 | 2008-08-04 16:43:49 -0700 | [diff] [blame] | 431 | case Grunnable: |
Russ Cox | 9682400 | 2008-08-05 14:18:47 -0700 | [diff] [blame] | 432 | case Gdead: |
| 433 | // Shouldn't have been running! |
| 434 | throw("bad gp->status in sched"); |
| 435 | case Grunning: |
| 436 | gp->status = Grunnable; |
| 437 | gput(gp); |
| 438 | break; |
| 439 | case Gmoribund: |
| 440 | gp->status = Gdead; |
Russ Cox | 218c393 | 2009-07-13 17:28:39 -0700 | [diff] [blame] | 441 | if(gp->lockedm) { |
| 442 | gp->lockedm = nil; |
| 443 | m->lockedg = nil; |
| 444 | } |
Russ Cox | 2aea4a0 | 2009-08-26 15:26:09 -0700 | [diff] [blame] | 445 | gfput(gp); |
Russ Cox | 9682400 | 2008-08-05 14:18:47 -0700 | [diff] [blame] | 446 | if(--sched.gcount == 0) |
Russ Cox | 918afd94 | 2009-05-08 15:21:41 -0700 | [diff] [blame] | 447 | exit(0); |
Russ Cox | d28acc4 | 2008-08-04 16:43:49 -0700 | [diff] [blame] | 448 | break; |
| 449 | } |
Russ Cox | a61bb95 | 2008-09-24 14:13:07 -0700 | [diff] [blame] | 450 | if(gp->readyonstop){ |
| 451 | gp->readyonstop = 0; |
| 452 | readylocked(gp); |
| 453 | } |
Russ Cox | d28acc4 | 2008-08-04 16:43:49 -0700 | [diff] [blame] | 454 | } |
| 455 | |
Russ Cox | 9682400 | 2008-08-05 14:18:47 -0700 | [diff] [blame] | 456 | // Find (or wait for) g to run. Unlocks sched. |
| 457 | gp = nextgandunlock(); |
Russ Cox | a61bb95 | 2008-09-24 14:13:07 -0700 | [diff] [blame] | 458 | gp->readyonstop = 0; |
Russ Cox | d28acc4 | 2008-08-04 16:43:49 -0700 | [diff] [blame] | 459 | gp->status = Grunning; |
Russ Cox | 4feda71 | 2008-08-02 22:34:04 -0700 | [diff] [blame] | 460 | m->curg = gp; |
Russ Cox | a61bb95 | 2008-09-24 14:13:07 -0700 | [diff] [blame] | 461 | gp->m = m; |
Russ Cox | 7343e03 | 2009-06-17 15:12:16 -0700 | [diff] [blame] | 462 | if(gp->sched.pc == (byte*)goexit) // kickoff |
| 463 | gogocall(&gp->sched, (void(*)(void))gp->entry); |
| 464 | gogo(&gp->sched, 1); |
Russ Cox | 4feda71 | 2008-08-02 22:34:04 -0700 | [diff] [blame] | 465 | } |
| 466 | |
Russ Cox | 9682400 | 2008-08-05 14:18:47 -0700 | [diff] [blame] | 467 | // Enter scheduler. If g->status is Grunning, |
| 468 | // re-queues g and runs everyone else who is waiting |
| 469 | // before running g again. If g->status is Gmoribund, |
| 470 | // kills off g. |
Ken Thompson | af58f17 | 2008-07-14 14:34:27 -0700 | [diff] [blame] | 471 | void |
Russ Cox | 918afd94 | 2009-05-08 15:21:41 -0700 | [diff] [blame] | 472 | gosched(void) |
Ken Thompson | af58f17 | 2008-07-14 14:34:27 -0700 | [diff] [blame] | 473 | { |
Russ Cox | 1ce1791 | 2009-01-26 17:37:05 -0800 | [diff] [blame] | 474 | if(g == m->g0) |
| 475 | throw("gosched of g0"); |
Russ Cox | 7343e03 | 2009-06-17 15:12:16 -0700 | [diff] [blame] | 476 | if(gosave(&g->sched) == 0) |
| 477 | gogo(&m->sched, 1); |
Ken Thompson | af58f17 | 2008-07-14 14:34:27 -0700 | [diff] [blame] | 478 | } |
| 479 | |
Russ Cox | efc86a7 | 2008-11-25 16:48:10 -0800 | [diff] [blame] | 480 | // The goroutine g is about to enter a system call. |
| 481 | // Record that it's not using the cpu anymore. |
| 482 | // This is called only from the go syscall library, not |
| 483 | // from the low-level system calls used by the runtime. |
| 484 | // The "arguments" are syscall.Syscall's stack frame |
| 485 | void |
| 486 | sys·entersyscall(uint64 callerpc, int64 trap) |
Russ Cox | 9682400 | 2008-08-05 14:18:47 -0700 | [diff] [blame] | 487 | { |
Russ Cox | 218c393 | 2009-07-13 17:28:39 -0700 | [diff] [blame] | 488 | USED(callerpc, trap); |
Russ Cox | f7f6329 | 2008-08-05 14:21:42 -0700 | [diff] [blame] | 489 | |
Russ Cox | efc86a7 | 2008-11-25 16:48:10 -0800 | [diff] [blame] | 490 | lock(&sched); |
Russ Cox | 052a66b | 2009-07-21 19:43:27 -0700 | [diff] [blame] | 491 | if(sched.predawn) { |
| 492 | unlock(&sched); |
| 493 | return; |
| 494 | } |
Russ Cox | 3f8aa66 | 2008-12-05 15:24:18 -0800 | [diff] [blame] | 495 | g->status = Gsyscall; |
Russ Cox | 36835c7 | 2009-06-15 21:30:53 -0700 | [diff] [blame] | 496 | // Leave SP around for gc and traceback. |
| 497 | // Do before notewakeup so that gc |
| 498 | // never sees Gsyscall with wrong stack. |
| 499 | gosave(&g->sched); |
Russ Cox | efc86a7 | 2008-11-25 16:48:10 -0800 | [diff] [blame] | 500 | sched.mcpu--; |
| 501 | sched.msyscall++; |
| 502 | if(sched.gwait != 0) |
| 503 | matchmg(); |
Russ Cox | 53e69e1 | 2009-01-27 14:01:20 -0800 | [diff] [blame] | 504 | if(sched.waitstop && sched.mcpu <= sched.mcpumax) { |
| 505 | sched.waitstop = 0; |
| 506 | notewakeup(&sched.stopped); |
| 507 | } |
Russ Cox | efc86a7 | 2008-11-25 16:48:10 -0800 | [diff] [blame] | 508 | unlock(&sched); |
| 509 | } |
| 510 | |
| 511 | // The goroutine g exited its system call. |
| 512 | // Arrange for it to run on a cpu again. |
| 513 | // This is called only from the go syscall library, not |
| 514 | // from the low-level system calls used by the runtime. |
| 515 | void |
| 516 | sys·exitsyscall(void) |
| 517 | { |
Russ Cox | efc86a7 | 2008-11-25 16:48:10 -0800 | [diff] [blame] | 518 | lock(&sched); |
Russ Cox | 052a66b | 2009-07-21 19:43:27 -0700 | [diff] [blame] | 519 | if(sched.predawn) { |
| 520 | unlock(&sched); |
| 521 | return; |
| 522 | } |
Russ Cox | 3f8aa66 | 2008-12-05 15:24:18 -0800 | [diff] [blame] | 523 | g->status = Grunning; |
Russ Cox | efc86a7 | 2008-11-25 16:48:10 -0800 | [diff] [blame] | 524 | sched.msyscall--; |
| 525 | sched.mcpu++; |
| 526 | // Fast path - if there's room for this m, we're done. |
| 527 | if(sched.mcpu <= sched.mcpumax) { |
| 528 | unlock(&sched); |
| 529 | return; |
| 530 | } |
| 531 | unlock(&sched); |
| 532 | |
| 533 | // Slow path - all the cpus are taken. |
| 534 | // The scheduler will ready g and put this m to sleep. |
Russ Cox | 218c393 | 2009-07-13 17:28:39 -0700 | [diff] [blame] | 535 | // When the scheduler takes g away from m, |
Russ Cox | efc86a7 | 2008-11-25 16:48:10 -0800 | [diff] [blame] | 536 | // it will undo the sched.mcpu++ above. |
Russ Cox | 918afd94 | 2009-05-08 15:21:41 -0700 | [diff] [blame] | 537 | gosched(); |
Russ Cox | 9682400 | 2008-08-05 14:18:47 -0700 | [diff] [blame] | 538 | } |
| 539 | |
Russ Cox | 9510034 | 2009-04-01 00:26:00 -0700 | [diff] [blame] | 540 | /* |
| 541 | * stack layout parameters. |
| 542 | * known to linkers. |
| 543 | * |
| 544 | * g->stackguard is set to point StackGuard bytes |
| 545 | * above the bottom of the stack. each function |
| 546 | * compares its stack pointer against g->stackguard |
| 547 | * to check for overflow. to cut one instruction from |
| 548 | * the check sequence for functions with tiny frames, |
| 549 | * the stack is allowed to protrude StackSmall bytes |
| 550 | * below the stack guard. functions with large frames |
| 551 | * don't bother with the check and always call morestack. |
| 552 | * the sequences are: |
| 553 | * |
Russ Cox | d6c59ad | 2009-04-02 16:41:53 -0700 | [diff] [blame] | 554 | * guard = g->stackguard |
| 555 | * frame = function's stack frame size |
| 556 | * argsize = size of function arguments (call + return) |
| 557 | * |
Russ Cox | 9510034 | 2009-04-01 00:26:00 -0700 | [diff] [blame] | 558 | * stack frame size <= StackSmall: |
| 559 | * CMPQ guard, SP |
| 560 | * JHI 3(PC) |
Russ Cox | a9996d0 | 2009-04-13 15:22:36 -0700 | [diff] [blame] | 561 | * MOVQ m->morearg, $(argsize << 32) |
Russ Cox | 9510034 | 2009-04-01 00:26:00 -0700 | [diff] [blame] | 562 | * CALL sys.morestack(SB) |
| 563 | * |
| 564 | * stack frame size > StackSmall but < StackBig |
| 565 | * LEAQ (frame-StackSmall)(SP), R0 |
| 566 | * CMPQ guard, R0 |
| 567 | * JHI 3(PC) |
Russ Cox | a9996d0 | 2009-04-13 15:22:36 -0700 | [diff] [blame] | 568 | * MOVQ m->morearg, $(argsize << 32) |
Russ Cox | 9510034 | 2009-04-01 00:26:00 -0700 | [diff] [blame] | 569 | * CALL sys.morestack(SB) |
| 570 | * |
| 571 | * stack frame size >= StackBig: |
Russ Cox | a9996d0 | 2009-04-13 15:22:36 -0700 | [diff] [blame] | 572 | * MOVQ m->morearg, $((argsize << 32) | frame) |
Russ Cox | 9510034 | 2009-04-01 00:26:00 -0700 | [diff] [blame] | 573 | * CALL sys.morestack(SB) |
| 574 | * |
| 575 | * the bottom StackGuard - StackSmall bytes are important: |
| 576 | * there has to be enough room to execute functions that |
| 577 | * refuse to check for stack overflow, either because they |
| 578 | * need to be adjacent to the actual caller's frame (sys.deferproc) |
| 579 | * or because they handle the imminent stack overflow (sys.morestack). |
| 580 | * |
| 581 | * for example, sys.deferproc might call malloc, |
| 582 | * which does one of the above checks (without allocating a full frame), |
| 583 | * which might trigger a call to sys.morestack. |
| 584 | * this sequence needs to fit in the bottom section of the stack. |
| 585 | * on amd64, sys.morestack's frame is 40 bytes, and |
| 586 | * sys.deferproc's frame is 56 bytes. that fits well within |
| 587 | * the StackGuard - StackSmall = 128 bytes at the bottom. |
| 588 | * there may be other sequences lurking or yet to be written |
| 589 | * that require more stack. sys.morestack checks to make sure |
| 590 | * the stack has not completely overflowed and should |
| 591 | * catch such sequences. |
| 592 | */ |
| 593 | enum |
| 594 | { |
| 595 | // byte offset of stack guard (g->stackguard) above bottom of stack. |
| 596 | StackGuard = 256, |
Russ Cox | efc86a7 | 2008-11-25 16:48:10 -0800 | [diff] [blame] | 597 | |
Russ Cox | 9510034 | 2009-04-01 00:26:00 -0700 | [diff] [blame] | 598 | // checked frames are allowed to protrude below the guard by |
| 599 | // this many bytes. this saves an instruction in the checking |
| 600 | // sequence when the stack frame is tiny. |
| 601 | StackSmall = 128, |
| 602 | |
| 603 | // extra space in the frame (beyond the function for which |
| 604 | // the frame is allocated) is assumed not to be much bigger |
| 605 | // than this amount. it may not be used efficiently if it is. |
| 606 | StackBig = 4096, |
| 607 | }; |
Ken Thompson | af58f17 | 2008-07-14 14:34:27 -0700 | [diff] [blame] | 608 | |
| 609 | void |
| 610 | oldstack(void) |
| 611 | { |
Russ Cox | 7343e03 | 2009-06-17 15:12:16 -0700 | [diff] [blame] | 612 | Stktop *top, old; |
Russ Cox | a9996d0 | 2009-04-13 15:22:36 -0700 | [diff] [blame] | 613 | uint32 args; |
Ken Thompson | af58f17 | 2008-07-14 14:34:27 -0700 | [diff] [blame] | 614 | byte *sp; |
Russ Cox | 7343e03 | 2009-06-17 15:12:16 -0700 | [diff] [blame] | 615 | G *g1; |
Ken Thompson | af58f17 | 2008-07-14 14:34:27 -0700 | [diff] [blame] | 616 | |
Russ Cox | 7343e03 | 2009-06-17 15:12:16 -0700 | [diff] [blame] | 617 | //printf("oldstack m->cret=%p\n", m->cret); |
Ken Thompson | af58f17 | 2008-07-14 14:34:27 -0700 | [diff] [blame] | 618 | |
Russ Cox | 7343e03 | 2009-06-17 15:12:16 -0700 | [diff] [blame] | 619 | g1 = m->curg; |
| 620 | top = (Stktop*)g1->stackbase; |
Ken Thompson | af58f17 | 2008-07-14 14:34:27 -0700 | [diff] [blame] | 621 | sp = (byte*)top; |
Russ Cox | 7343e03 | 2009-06-17 15:12:16 -0700 | [diff] [blame] | 622 | old = *top; |
| 623 | args = old.args; |
Russ Cox | a9996d0 | 2009-04-13 15:22:36 -0700 | [diff] [blame] | 624 | if(args > 0) { |
Russ Cox | a9996d0 | 2009-04-13 15:22:36 -0700 | [diff] [blame] | 625 | sp -= args; |
Russ Cox | bba278a | 2009-07-08 18:16:09 -0700 | [diff] [blame] | 626 | mcpy(top->fp, sp, args); |
Ken Thompson | af58f17 | 2008-07-14 14:34:27 -0700 | [diff] [blame] | 627 | } |
| 628 | |
Russ Cox | 7343e03 | 2009-06-17 15:12:16 -0700 | [diff] [blame] | 629 | stackfree((byte*)g1->stackguard - StackGuard); |
| 630 | g1->stackbase = old.stackbase; |
| 631 | g1->stackguard = old.stackguard; |
Ken Thompson | af58f17 | 2008-07-14 14:34:27 -0700 | [diff] [blame] | 632 | |
Russ Cox | 7343e03 | 2009-06-17 15:12:16 -0700 | [diff] [blame] | 633 | gogo(&old.gobuf, m->cret); |
Russ Cox | 79e1db2 | 2008-12-04 08:30:54 -0800 | [diff] [blame] | 634 | } |
| 635 | |
| 636 | void |
Ken Thompson | af58f17 | 2008-07-14 14:34:27 -0700 | [diff] [blame] | 637 | newstack(void) |
| 638 | { |
Russ Cox | 9510034 | 2009-04-01 00:26:00 -0700 | [diff] [blame] | 639 | int32 frame, args; |
Ken Thompson | af58f17 | 2008-07-14 14:34:27 -0700 | [diff] [blame] | 640 | Stktop *top; |
| 641 | byte *stk, *sp; |
Russ Cox | 7343e03 | 2009-06-17 15:12:16 -0700 | [diff] [blame] | 642 | G *g1; |
| 643 | Gobuf label; |
Ken Thompson | af58f17 | 2008-07-14 14:34:27 -0700 | [diff] [blame] | 644 | |
Russ Cox | 7343e03 | 2009-06-17 15:12:16 -0700 | [diff] [blame] | 645 | frame = m->moreframe; |
| 646 | args = m->moreargs; |
Russ Cox | bba278a | 2009-07-08 18:16:09 -0700 | [diff] [blame] | 647 | |
Russ Cox | 7343e03 | 2009-06-17 15:12:16 -0700 | [diff] [blame] | 648 | // Round up to align things nicely. |
| 649 | // This is sufficient for both 32- and 64-bit machines. |
| 650 | args = (args+7) & ~7; |
Ken Thompson | af58f17 | 2008-07-14 14:34:27 -0700 | [diff] [blame] | 651 | |
Russ Cox | 9510034 | 2009-04-01 00:26:00 -0700 | [diff] [blame] | 652 | if(frame < StackBig) |
| 653 | frame = StackBig; |
| 654 | frame += 1024; // for more functions, Stktop. |
| 655 | stk = stackalloc(frame); |
Ken Thompson | af58f17 | 2008-07-14 14:34:27 -0700 | [diff] [blame] | 656 | |
Russ Cox | bba278a | 2009-07-08 18:16:09 -0700 | [diff] [blame] | 657 | //printf("newstack frame=%d args=%d morepc=%p morefp=%p gobuf=%p, %p newstk=%p\n", frame, args, m->morepc, m->morefp, g->sched.pc, g->sched.sp, stk); |
Russ Cox | 7343e03 | 2009-06-17 15:12:16 -0700 | [diff] [blame] | 658 | |
| 659 | g1 = m->curg; |
Russ Cox | 9510034 | 2009-04-01 00:26:00 -0700 | [diff] [blame] | 660 | top = (Stktop*)(stk+frame-sizeof(*top)); |
Russ Cox | 7343e03 | 2009-06-17 15:12:16 -0700 | [diff] [blame] | 661 | top->stackbase = g1->stackbase; |
| 662 | top->stackguard = g1->stackguard; |
| 663 | top->gobuf = m->morebuf; |
Russ Cox | bba278a | 2009-07-08 18:16:09 -0700 | [diff] [blame] | 664 | top->fp = m->morefp; |
Russ Cox | 7343e03 | 2009-06-17 15:12:16 -0700 | [diff] [blame] | 665 | top->args = args; |
Ken Thompson | af58f17 | 2008-07-14 14:34:27 -0700 | [diff] [blame] | 666 | |
Russ Cox | 7343e03 | 2009-06-17 15:12:16 -0700 | [diff] [blame] | 667 | g1->stackbase = (byte*)top; |
| 668 | g1->stackguard = stk + StackGuard; |
Ken Thompson | af58f17 | 2008-07-14 14:34:27 -0700 | [diff] [blame] | 669 | |
| 670 | sp = (byte*)top; |
Russ Cox | 9510034 | 2009-04-01 00:26:00 -0700 | [diff] [blame] | 671 | if(args > 0) { |
Russ Cox | 9510034 | 2009-04-01 00:26:00 -0700 | [diff] [blame] | 672 | sp -= args; |
Russ Cox | bba278a | 2009-07-08 18:16:09 -0700 | [diff] [blame] | 673 | mcpy(sp, m->morefp, args); |
Ken Thompson | af58f17 | 2008-07-14 14:34:27 -0700 | [diff] [blame] | 674 | } |
| 675 | |
Russ Cox | 7343e03 | 2009-06-17 15:12:16 -0700 | [diff] [blame] | 676 | // Continue as if lessstack had just called m->morepc |
| 677 | // (the PC that decided to grow the stack). |
| 678 | label.sp = sp; |
| 679 | label.pc = (byte*)sys·lessstack; |
| 680 | label.g = m->curg; |
| 681 | gogocall(&label, m->morepc); |
Ken Thompson | af58f17 | 2008-07-14 14:34:27 -0700 | [diff] [blame] | 682 | |
| 683 | *(int32*)345 = 123; // never return |
| 684 | } |
| 685 | |
Russ Cox | 9510034 | 2009-04-01 00:26:00 -0700 | [diff] [blame] | 686 | G* |
| 687 | malg(int32 stacksize) |
| 688 | { |
| 689 | G *g; |
| 690 | byte *stk; |
| 691 | |
| 692 | g = malloc(sizeof(G)); |
| 693 | stk = stackalloc(stacksize + StackGuard); |
| 694 | g->stack0 = stk; |
| 695 | g->stackguard = stk + StackGuard; |
| 696 | g->stackbase = stk + StackGuard + stacksize; |
| 697 | return g; |
| 698 | } |
| 699 | |
| 700 | /* |
| 701 | * Newproc and deferproc need to be textflag 7 |
| 702 | * (no possible stack split when nearing overflow) |
| 703 | * because they assume that the arguments to fn |
| 704 | * are available sequentially beginning at &arg0. |
| 705 | * If a stack split happened, only the one word |
| 706 | * arg0 would be copied. It's okay if any functions |
| 707 | * they call split the stack below the newproc frame. |
| 708 | */ |
| 709 | #pragma textflag 7 |
| 710 | void |
| 711 | sys·newproc(int32 siz, byte* fn, byte* arg0) |
| 712 | { |
| 713 | byte *stk, *sp; |
| 714 | G *newg; |
| 715 | |
| 716 | //printf("newproc siz=%d fn=%p", siz, fn); |
| 717 | |
| 718 | siz = (siz+7) & ~7; |
| 719 | if(siz > 1024) |
| 720 | throw("sys·newproc: too many args"); |
| 721 | |
| 722 | lock(&sched); |
| 723 | |
| 724 | if((newg = gfget()) != nil){ |
| 725 | newg->status = Gwaiting; |
| 726 | } else { |
| 727 | newg = malg(4096); |
| 728 | newg->status = Gwaiting; |
| 729 | newg->alllink = allg; |
| 730 | allg = newg; |
| 731 | } |
| 732 | stk = newg->stack0; |
| 733 | |
| 734 | newg->stackguard = stk+StackGuard; |
| 735 | |
| 736 | sp = stk + 4096 - 4*8; |
| 737 | newg->stackbase = sp; |
| 738 | |
| 739 | sp -= siz; |
| 740 | mcpy(sp, (byte*)&arg0, siz); |
| 741 | |
Russ Cox | 7343e03 | 2009-06-17 15:12:16 -0700 | [diff] [blame] | 742 | newg->sched.sp = sp; |
| 743 | newg->sched.pc = (byte*)goexit; |
| 744 | newg->sched.g = newg; |
| 745 | newg->entry = fn; |
Russ Cox | 9510034 | 2009-04-01 00:26:00 -0700 | [diff] [blame] | 746 | |
| 747 | sched.gcount++; |
| 748 | goidgen++; |
| 749 | newg->goid = goidgen; |
| 750 | |
Russ Cox | fe8ff95 | 2009-08-31 18:10:11 -0700 | [diff] [blame^] | 751 | newprocreadylocked(newg); |
Russ Cox | 9510034 | 2009-04-01 00:26:00 -0700 | [diff] [blame] | 752 | unlock(&sched); |
| 753 | |
| 754 | //printf(" goid=%d\n", newg->goid); |
| 755 | } |
| 756 | |
| 757 | #pragma textflag 7 |
| 758 | void |
| 759 | sys·deferproc(int32 siz, byte* fn, byte* arg0) |
| 760 | { |
| 761 | Defer *d; |
| 762 | |
| 763 | d = malloc(sizeof(*d) + siz - sizeof(d->args)); |
| 764 | d->fn = fn; |
| 765 | d->sp = (byte*)&arg0; |
| 766 | d->siz = siz; |
| 767 | mcpy(d->args, d->sp, d->siz); |
| 768 | |
| 769 | d->link = g->defer; |
| 770 | g->defer = d; |
| 771 | } |
| 772 | |
| 773 | #pragma textflag 7 |
| 774 | void |
Russ Cox | aa3222d8 | 2009-06-02 23:02:12 -0700 | [diff] [blame] | 775 | sys·deferreturn(uintptr arg0) |
Russ Cox | 9510034 | 2009-04-01 00:26:00 -0700 | [diff] [blame] | 776 | { |
Russ Cox | 9510034 | 2009-04-01 00:26:00 -0700 | [diff] [blame] | 777 | Defer *d; |
Russ Cox | aa3222d8 | 2009-06-02 23:02:12 -0700 | [diff] [blame] | 778 | byte *sp, *fn; |
Russ Cox | 9510034 | 2009-04-01 00:26:00 -0700 | [diff] [blame] | 779 | |
| 780 | d = g->defer; |
| 781 | if(d == nil) |
| 782 | return; |
| 783 | sp = (byte*)&arg0; |
| 784 | if(d->sp != sp) |
| 785 | return; |
| 786 | mcpy(d->sp, d->args, d->siz); |
| 787 | g->defer = d->link; |
Russ Cox | aa3222d8 | 2009-06-02 23:02:12 -0700 | [diff] [blame] | 788 | fn = d->fn; |
Russ Cox | 9510034 | 2009-04-01 00:26:00 -0700 | [diff] [blame] | 789 | free(d); |
Russ Cox | aa3222d8 | 2009-06-02 23:02:12 -0700 | [diff] [blame] | 790 | jmpdefer(fn, sp); |
| 791 | } |
Russ Cox | 79e1db2 | 2008-12-04 08:30:54 -0800 | [diff] [blame] | 792 | |
Russ Cox | 918afd94 | 2009-05-08 15:21:41 -0700 | [diff] [blame] | 793 | void |
| 794 | runtime·Breakpoint(void) |
| 795 | { |
| 796 | breakpoint(); |
| 797 | } |
| 798 | |
| 799 | void |
| 800 | runtime·Goexit(void) |
| 801 | { |
| 802 | goexit(); |
| 803 | } |
| 804 | |
| 805 | void |
| 806 | runtime·Gosched(void) |
| 807 | { |
| 808 | gosched(); |
| 809 | } |
| 810 | |
Russ Cox | 218c393 | 2009-07-13 17:28:39 -0700 | [diff] [blame] | 811 | void |
| 812 | runtime·LockOSThread(void) |
| 813 | { |
| 814 | if(sched.predawn) |
| 815 | throw("cannot wire during init"); |
| 816 | m->lockedg = g; |
| 817 | g->lockedm = m; |
| 818 | } |
| 819 | |
Rob Pike | 7955490 | 2009-08-06 13:07:05 -0700 | [diff] [blame] | 820 | // delete when scheduler is stronger |
| 821 | void |
| 822 | runtime·GOMAXPROCS(int32 n) |
| 823 | { |
| 824 | if(n < 1) |
| 825 | n = 1; |
| 826 | |
| 827 | lock(&sched); |
| 828 | sched.gomaxprocs = n; |
| 829 | sched.mcpumax = n; |
| 830 | // handle fewer procs |
| 831 | while(sched.mcpu > sched.mcpumax) { |
| 832 | noteclear(&sched.stopped); |
| 833 | sched.waitstop = 1; |
| 834 | unlock(&sched); |
| 835 | notesleep(&sched.stopped); |
| 836 | lock(&sched); |
| 837 | } |
| 838 | // handle more procs |
| 839 | matchmg(); |
| 840 | unlock(&sched); |
| 841 | } |
| 842 | |
Russ Cox | 218c393 | 2009-07-13 17:28:39 -0700 | [diff] [blame] | 843 | void |
| 844 | runtime·UnlockOSThread(void) |
| 845 | { |
| 846 | m->lockedg = nil; |
| 847 | g->lockedm = nil; |
| 848 | } |
| 849 | |
| 850 | // for testing of wire, unwire |
| 851 | void |
| 852 | runtime·mid(uint32 ret) |
| 853 | { |
| 854 | ret = m->id; |
| 855 | FLUSH(&ret); |
| 856 | } |