]> Cypherpunks repositories - gostls13.git/commitdiff
runtime: implement runqdrain() for GC mark worker goroutines
authorAndy Pan <panjf2000@gmail.com>
Wed, 14 Apr 2021 16:04:17 +0000 (00:04 +0800)
committerIan Lance Taylor <iant@golang.org>
Tue, 20 Apr 2021 19:48:29 +0000 (19:48 +0000)
Change-Id: Ida44a2e07f277bee8806538ecee4beee3474cf3d
Reviewed-on: https://go-review.googlesource.com/c/go/+/310149
Reviewed-by: Michael Pratt <mpratt@google.com>
Reviewed-by: Ian Lance Taylor <iant@golang.org>
Trust: Michael Pratt <mpratt@google.com>
Trust: Ian Lance Taylor <iant@golang.org>
Run-TryBot: Michael Pratt <mpratt@google.com>
TryBot-Result: Go Bot <gobot@golang.org>

src/runtime/mgc.go
src/runtime/proc.go
src/runtime/runtime2.go

index 601593087da3b1f15490439f44ce2f83a0dbabb0..458566353536f5f0af5286aeb35ef971430fc6ff 100644 (file)
@@ -1273,15 +1273,11 @@ func gcBgMarkWorker() {
                                        // everything out of the run
                                        // queue so it can run
                                        // somewhere else.
-                                       lock(&sched.lock)
-                                       for {
-                                               gp, _ := runqget(pp)
-                                               if gp == nil {
-                                                       break
-                                               }
-                                               globrunqput(gp)
+                                       if drainQ, n := runqdrain(pp); n > 0 {
+                                               lock(&sched.lock)
+                                               globrunqputbatch(&drainQ, int32(n))
+                                               unlock(&sched.lock)
                                        }
-                                       unlock(&sched.lock)
                                }
                                // Go back to draining, this time
                                // without preemption.
index a712b11c4fe621e2636dd46924eed0d1f12d7b5d..c4fe6dd0f870eebc14929cf09ae80fddab49f473 100644 (file)
@@ -5911,6 +5911,44 @@ func runqget(_p_ *p) (gp *g, inheritTime bool) {
        }
 }
 
+// runqdrain drains the local runnable queue of _p_ and returns all g's in it.
+// Executed only by the owner P.
+func runqdrain(_p_ *p) (drainQ gQueue, n uint32) {
+       var getNext bool
+       oldNext := _p_.runnext
+       if oldNext != 0 && _p_.runnext.cas(oldNext, 0) {
+               drainQ.pushBack(oldNext.ptr())
+               n++
+               getNext = true
+       }
+
+       for {
+               h := atomic.LoadAcq(&_p_.runqhead) // load-acquire, synchronize with other consumers
+               t := _p_.runqtail
+               qn := t - h
+               if qn == 0 {
+                       return
+               }
+               for i := uint32(0); i < qn; i++ {
+                       gp := _p_.runq[(h+i)%uint32(len(_p_.runq))].ptr()
+                       drainQ.pushBack(gp)
+               }
+               if atomic.CasRel(&_p_.runqhead, h, h+qn) { // cas-release, commits consume
+                       n += qn
+                       return
+               }
+
+               // Clean up if it failed to drain _p_ in this round and start over until it succeed.
+               drainQ = gQueue{}
+               n = 0
+               // Push the prior old _p_.runnext back into drainQ.
+               if getNext {
+                       drainQ.pushBack(oldNext.ptr())
+                       n++
+               }
+       }
+}
+
 // Grabs a batch of goroutines from _p_'s runnable queue into batch.
 // Batch is a ring buffer starting at batchHead.
 // Returns number of grabbed goroutines.
index 387841e60ba22d43e198803416f64292db63a8e9..82b6c596e5e37fc21df0ec4281494227cbfa8171 100644 (file)
@@ -622,6 +622,9 @@ type p struct {
        // unit and eliminates the (potentially large) scheduling
        // latency that otherwise arises from adding the ready'd
        // goroutines to the end of the run queue.
+       //
+       // Note that while other P's may atomically CAS this to zero,
+       // only the owner P can CAS it to a valid G.
        runnext guintptr
 
        // Available G's (status == Gdead)