]> Cypherpunks repositories - gostls13.git/commitdiff
runtime: refactor goroutine blocking
authorDmitriy Vyukov <dvyukov@google.com>
Tue, 18 Sep 2012 17:15:46 +0000 (21:15 +0400)
committerDmitriy Vyukov <dvyukov@google.com>
Tue, 18 Sep 2012 17:15:46 +0000 (21:15 +0400)
The change is a preparation for the new scheduler.
It introduces runtime.park() function,
that will atomically unlock the mutex and park the goroutine.
It will allow to remove the racy readyonstop flag
that is difficult to implement w/o the global scheduler mutex.

R=rsc, remyoudompheng, dave
CC=golang-dev
https://golang.org/cl/6501077

src/pkg/runtime/chan.c
src/pkg/runtime/mgc0.c
src/pkg/runtime/proc.c
src/pkg/runtime/runtime.h
src/pkg/runtime/sema.goc
src/pkg/runtime/time.goc

index 07ab431b43fcc497b4e14a6f4a116fa9dbf38030..93408c22f66698ba5b8366c7cf6489533b4d40c9 100644 (file)
@@ -155,9 +155,7 @@ runtime·chansend(ChanType *t, Hchan *c, byte *ep, bool *pres)
                        *pres = false;
                        return;
                }
-               g->status = Gwaiting;
-               g->waitreason = "chan send (nil chan)";
-               runtime·gosched();
+               runtime·park(nil, nil, "chan send (nil chan)");
                return;  // not reached
        }
 
@@ -202,11 +200,8 @@ runtime·chansend(ChanType *t, Hchan *c, byte *ep, bool *pres)
        mysg.g = g;
        mysg.selgen = NOSELGEN;
        g->param = nil;
-       g->status = Gwaiting;
-       g->waitreason = "chan send";
        enqueue(&c->sendq, &mysg);
-       runtime·unlock(c);
-       runtime·gosched();
+       runtime·park(runtime·unlock, c, "chan send");
 
        if(g->param == nil) {
                runtime·lock(c);
@@ -230,11 +225,8 @@ asynch:
                mysg.g = g;
                mysg.elem = nil;
                mysg.selgen = NOSELGEN;
-               g->status = Gwaiting;
-               g->waitreason = "chan send";
                enqueue(&c->sendq, &mysg);
-               runtime·unlock(c);
-               runtime·gosched();
+               runtime·park(runtime·unlock, c, "chan send");
 
                runtime·lock(c);
                goto asynch;
@@ -280,9 +272,7 @@ runtime·chanrecv(ChanType *t, Hchan* c, byte *ep, bool *selected, bool *receive
                        *selected = false;
                        return;
                }
-               g->status = Gwaiting;
-               g->waitreason = "chan receive (nil chan)";
-               runtime·gosched();
+               runtime·park(nil, nil, "chan receive (nil chan)");
                return;  // not reached
        }
 
@@ -320,11 +310,8 @@ runtime·chanrecv(ChanType *t, Hchan* c, byte *ep, bool *selected, bool *receive
        mysg.g = g;
        mysg.selgen = NOSELGEN;
        g->param = nil;
-       g->status = Gwaiting;
-       g->waitreason = "chan receive";
        enqueue(&c->recvq, &mysg);
-       runtime·unlock(c);
-       runtime·gosched();
+       runtime·park(runtime·unlock, c, "chan receive");
 
        if(g->param == nil) {
                runtime·lock(c);
@@ -352,11 +339,8 @@ asynch:
                mysg.g = g;
                mysg.elem = nil;
                mysg.selgen = NOSELGEN;
-               g->status = Gwaiting;
-               g->waitreason = "chan receive";
                enqueue(&c->recvq, &mysg);
-               runtime·unlock(c);
-               runtime·gosched();
+               runtime·park(runtime·unlock, c, "chan receive");
 
                runtime·lock(c);
                goto asynch;
@@ -774,9 +758,7 @@ selunlock(Select *sel)
 void
 runtime·block(void)
 {
-       g->status = Gwaiting;   // forever
-       g->waitreason = "select (no cases)";
-       runtime·gosched();
+       runtime·park(nil, nil, "select (no cases)");   // forever
 }
 
 static void* selectgo(Select**);
@@ -907,10 +889,7 @@ loop:
        }
 
        g->param = nil;
-       g->status = Gwaiting;
-       g->waitreason = "select";
-       selunlock(sel);
-       runtime·gosched();
+       runtime·park((void(*)(Lock*))selunlock, (Lock*)sel, "select");
 
        sellock(sel);
        sg = g->param;
index 6dee9523bb0f7ca4990861c4bd2cb5804d8a23c9..91ed5088da05ac1c3dc1909a022cd42881f3e81f 100644 (file)
@@ -1027,9 +1027,7 @@ runfinq(void)
                finq = nil;
                if(fb == nil) {
                        fingwait = 1;
-                       g->status = Gwaiting;
-                       g->waitreason = "finalizer wait";
-                       runtime·gosched();
+                       runtime·park(nil, nil, "finalizer wait");
                        continue;
                }
                for(; fb; fb=next) {
index 2d837c537f9896ff1cf2901d2229f3b3bdc9500b..d763d01b085e414762ea64fcbd1383f93c83d087 100644 (file)
@@ -951,6 +951,18 @@ runtime·gosched(void)
        runtime·mcall(schedule);
 }
 
+// Puts the current goroutine into a waiting state and unlocks the lock.
+// The goroutine can be made runnable again by calling runtime·ready(gp).
+void
+runtime·park(void (*unlockf)(Lock*), Lock *lock, int8 *reason)
+{
+       g->status = Gwaiting;
+       g->waitreason = reason;
+       if(unlockf)
+               unlockf(lock);
+       runtime·gosched();
+}
+
 // The goroutine g is about to enter a system call.
 // Record that it's not using the cpu anymore.
 // This is called only from the go syscall library and cgocall,
index 20fb8ddfe1e8eeb33015a2bf90a217e0fd3388ec..96da29a00cb2ac69979577fa3d64839068a1f928 100644 (file)
@@ -614,7 +614,8 @@ uint32      runtime·fastrand1(void);
 void   runtime·exit(int32);
 void   runtime·breakpoint(void);
 void   runtime·gosched(void);
-void   runtime·tsleep(int64);
+void   runtime·park(void(*)(Lock*), Lock*, int8*);
+void   runtime·tsleep(int64, int8*);
 M*     runtime·newm(void);
 void   runtime·goexit(void);
 void   runtime·asmcgocall(void (*fn)(void*), void*);
index 2300c56aa37a5dd2bb16161d4b00dcdba47b0f1d..8b9cef4b37d546c91e470cf9ed2e2a2e27ca4c5a 100644 (file)
@@ -125,10 +125,7 @@ runtime·semacquire(uint32 volatile *addr)
                // Any semrelease after the cansemacquire knows we're waiting
                // (we set nwait above), so go to sleep.
                semqueue(root, addr, &s);
-               g->status = Gwaiting;
-               g->waitreason = "semacquire";
-               runtime·unlock(root);
-               runtime·gosched();
+               runtime·park(runtime·unlock, root, "semacquire");
                if(cansemacquire(addr))
                        return;
        }
index b18902f00fa94c0f8a72797c1dc97879ae1c68d7..18c24d1956c2046bd62ac651b468cf1fb939ac1e 100644 (file)
@@ -23,14 +23,14 @@ static bool deltimer(Timer*);
 
 // Sleep puts the current goroutine to sleep for at least ns nanoseconds.
 func Sleep(ns int64) {
-       g->status = Gwaiting;
-       g->waitreason = "sleep";
-       runtime·tsleep(ns);
+       runtime·tsleep(ns, "sleep");
 }
 
 // startTimer adds t to the timer heap.
 func startTimer(t *Timer) {
+       runtime·lock(&timers);
        addtimer(t);
+       runtime·unlock(&timers);
 }
 
 // stopTimer removes t from the timer heap if it is there.
@@ -55,24 +55,21 @@ ready(int64 now, Eface e)
 }
 
 // Put the current goroutine to sleep for ns nanoseconds.
-// The caller must have set g->status and g->waitreason.
 void
-runtime·tsleep(int64 ns)
+runtime·tsleep(int64 ns, int8 *reason)
 {
        Timer t;
 
-       if(ns <= 0) {
-               g->status = Grunning;
-               g->waitreason = nil;
+       if(ns <= 0)
                return;
-       }
 
        t.when = runtime·nanotime() + ns;
        t.period = 0;
        t.f = ready;
        t.arg.data = g;
+       runtime·lock(&timers);
        addtimer(&t);
-       runtime·gosched();
+       runtime·park(runtime·unlock, &timers, reason);
 }
 
 // Add a timer to the heap and start or kick the timer proc
@@ -83,7 +80,6 @@ addtimer(Timer *t)
        int32 n;
        Timer **nt;
 
-       runtime·lock(&timers);
        if(timers.len >= timers.cap) {
                // Grow slice.
                n = 16;
@@ -111,7 +107,6 @@ addtimer(Timer *t)
        }
        if(timers.timerproc == nil)
                timers.timerproc = runtime·newproc1((byte*)timerproc, nil, 0, 0, addtimer);
-       runtime·unlock(&timers);
 }
 
 // Delete timer t from the heap.
@@ -191,10 +186,7 @@ timerproc(void)
                if(delta < 0) {
                        // No timers left - put goroutine to sleep.
                        timers.rescheduling = true;
-                       g->status = Gwaiting;
-                       g->waitreason = "timer goroutine (idle)";
-                       runtime·unlock(&timers);
-                       runtime·gosched();
+                       runtime·park(runtime·unlock, &timers, "timer goroutine (idle)");
                        continue;
                }
                // At least one timer pending.  Sleep until then.