github.com/alash3al/go@v0.0.0-20150827002835-d497eeb00540/src/runtime/mgcwork.go (about)

     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  package runtime
     6  
     7  import "unsafe"
     8  
     9  const (
    10  	_Debugwbufs  = false   // if true check wbufs consistency
    11  	_WorkbufSize = 1 * 256 // in bytes - if small wbufs are passed to GC in a timely fashion.
    12  )
    13  
    14  // Garbage collector work pool abstraction.
    15  //
    16  // This implements a producer/consumer model for pointers to grey
    17  // objects.  A grey object is one that is marked and on a work
    18  // queue.  A black object is marked and not on a work queue.
    19  //
    20  // Write barriers, root discovery, stack scanning, and object scanning
    21  // produce pointers to grey objects.  Scanning consumes pointers to
    22  // grey objects, thus blackening them, and then scans them,
    23  // potentially producing new pointers to grey objects.
    24  
    25  // A wbufptr holds a workbuf*, but protects it from write barriers.
    26  // workbufs never live on the heap, so write barriers are unnecessary.
    27  // Write barriers on workbuf pointers may also be dangerous in the GC.
    28  type wbufptr uintptr
    29  
    30  func wbufptrOf(w *workbuf) wbufptr {
    31  	return wbufptr(unsafe.Pointer(w))
    32  }
    33  
    34  func (wp wbufptr) ptr() *workbuf {
    35  	return (*workbuf)(unsafe.Pointer(wp))
    36  }
    37  
    38  // A gcWork provides the interface to produce and consume work for the
    39  // garbage collector.
    40  //
    41  // A gcWork can be used on the stack as follows:
    42  //
    43  //     var gcw gcWork
    44  //     disable preemption
    45  //     .. call gcw.put() to produce and gcw.get() to consume ..
    46  //     gcw.dispose()
    47  //     enable preemption
    48  //
    49  // Or from the per-P gcWork cache:
    50  //
    51  //     (preemption must be disabled)
    52  //     gcw := &getg().m.p.ptr().gcw
    53  //     .. call gcw.put() to produce and gcw.get() to consume ..
    54  //     if gcphase == _GCmarktermination {
    55  //         gcw.dispose()
    56  //     }
    57  //
    58  // It's important that any use of gcWork during the mark phase prevent
    59  // the garbage collector from transitioning to mark termination since
    60  // gcWork may locally hold GC work buffers. This can be done by
    61  // disabling preemption (systemstack or acquirem).
    62  type gcWork struct {
    63  	// Invariant: wbuf is never full or empty
    64  	wbuf wbufptr
    65  
    66  	// Bytes marked (blackened) on this gcWork. This is aggregated
    67  	// into work.bytesMarked by dispose.
    68  	bytesMarked uint64
    69  
    70  	// Scan work performed on this gcWork. This is aggregated into
    71  	// gcController by dispose.
    72  	scanWork int64
    73  }
    74  
    75  // put enqueues a pointer for the garbage collector to trace.
    76  // obj must point to the beginning of a heap object.
    77  //go:nowritebarrier
    78  func (ww *gcWork) put(obj uintptr) {
    79  	w := (*gcWork)(noescape(unsafe.Pointer(ww))) // TODO: remove when escape analysis is fixed
    80  
    81  	wbuf := w.wbuf.ptr()
    82  	if wbuf == nil {
    83  		wbuf = getpartialorempty(42)
    84  		w.wbuf = wbufptrOf(wbuf)
    85  	}
    86  
    87  	wbuf.obj[wbuf.nobj] = obj
    88  	wbuf.nobj++
    89  
    90  	if wbuf.nobj == len(wbuf.obj) {
    91  		putfull(wbuf, 50)
    92  		w.wbuf = 0
    93  	}
    94  }
    95  
    96  // tryGet dequeues a pointer for the garbage collector to trace.
    97  //
    98  // If there are no pointers remaining in this gcWork or in the global
    99  // queue, tryGet returns 0.  Note that there may still be pointers in
   100  // other gcWork instances or other caches.
   101  //go:nowritebarrier
   102  func (ww *gcWork) tryGet() uintptr {
   103  	w := (*gcWork)(noescape(unsafe.Pointer(ww))) // TODO: remove when escape analysis is fixed
   104  
   105  	wbuf := w.wbuf.ptr()
   106  	if wbuf == nil {
   107  		wbuf = trygetfull(74)
   108  		if wbuf == nil {
   109  			return 0
   110  		}
   111  		w.wbuf = wbufptrOf(wbuf)
   112  	}
   113  
   114  	wbuf.nobj--
   115  	obj := wbuf.obj[wbuf.nobj]
   116  
   117  	if wbuf.nobj == 0 {
   118  		putempty(wbuf, 86)
   119  		w.wbuf = 0
   120  	}
   121  
   122  	return obj
   123  }
   124  
   125  // get dequeues a pointer for the garbage collector to trace, blocking
   126  // if necessary to ensure all pointers from all queues and caches have
   127  // been retrieved.  get returns 0 if there are no pointers remaining.
   128  //go:nowritebarrier
   129  func (ww *gcWork) get() uintptr {
   130  	w := (*gcWork)(noescape(unsafe.Pointer(ww))) // TODO: remove when escape analysis is fixed
   131  
   132  	wbuf := w.wbuf.ptr()
   133  	if wbuf == nil {
   134  		wbuf = getfull(103)
   135  		if wbuf == nil {
   136  			return 0
   137  		}
   138  		wbuf.checknonempty()
   139  		w.wbuf = wbufptrOf(wbuf)
   140  	}
   141  
   142  	// TODO: This might be a good place to add prefetch code
   143  
   144  	wbuf.nobj--
   145  	obj := wbuf.obj[wbuf.nobj]
   146  
   147  	if wbuf.nobj == 0 {
   148  		putempty(wbuf, 115)
   149  		w.wbuf = 0
   150  	}
   151  
   152  	return obj
   153  }
   154  
   155  // dispose returns any cached pointers to the global queue.
   156  // The buffers are being put on the full queue so that the
   157  // write barriers will not simply reacquire them before the
   158  // GC can inspect them. This helps reduce the mutator's
   159  // ability to hide pointers during the concurrent mark phase.
   160  //
   161  //go:nowritebarrier
   162  func (w *gcWork) dispose() {
   163  	if wbuf := w.wbuf; wbuf != 0 {
   164  		if wbuf.ptr().nobj == 0 {
   165  			throw("dispose: workbuf is empty")
   166  		}
   167  		putfull(wbuf.ptr(), 166)
   168  		w.wbuf = 0
   169  	}
   170  	if w.bytesMarked != 0 {
   171  		// dispose happens relatively infrequently. If this
   172  		// atomic becomes a problem, we should first try to
   173  		// dispose less and if necessary aggregate in a per-P
   174  		// counter.
   175  		xadd64(&work.bytesMarked, int64(w.bytesMarked))
   176  		w.bytesMarked = 0
   177  	}
   178  	if w.scanWork != 0 {
   179  		xaddint64(&gcController.scanWork, w.scanWork)
   180  		w.scanWork = 0
   181  	}
   182  }
   183  
   184  // balance moves some work that's cached in this gcWork back on the
   185  // global queue.
   186  //go:nowritebarrier
   187  func (w *gcWork) balance() {
   188  	if wbuf := w.wbuf; wbuf != 0 && wbuf.ptr().nobj > 4 {
   189  		w.wbuf = wbufptrOf(handoff(wbuf.ptr()))
   190  	}
   191  }
   192  
   193  // empty returns true if w has no mark work available.
   194  //go:nowritebarrier
   195  func (w *gcWork) empty() bool {
   196  	wbuf := w.wbuf
   197  	return wbuf == 0 || wbuf.ptr().nobj == 0
   198  }
   199  
   200  // Internally, the GC work pool is kept in arrays in work buffers.
   201  // The gcWork interface caches a work buffer until full (or empty) to
   202  // avoid contending on the global work buffer lists.
   203  
   204  type workbufhdr struct {
   205  	node  lfnode // must be first
   206  	nobj  int
   207  	inuse bool   // This workbuf is in use by some gorotuine and is not on the work.empty/partial/full queues.
   208  	log   [4]int // line numbers forming a history of ownership changes to workbuf
   209  }
   210  
   211  type workbuf struct {
   212  	workbufhdr
   213  	// account for the above fields
   214  	obj [(_WorkbufSize - unsafe.Sizeof(workbufhdr{})) / ptrSize]uintptr
   215  }
   216  
   217  // workbuf factory routines. These funcs are used to manage the
   218  // workbufs.
   219  // If the GC asks for some work these are the only routines that
   220  // make partially full wbufs available to the GC.
   221  // Each of the gets and puts also take an distinct integer that is used
   222  // to record a brief history of changes to ownership of the workbuf.
   223  // The convention is to use a unique line number but any encoding
   224  // is permissible. For example if you want to pass in 2 bits of information
   225  // you could simple add lineno1*100000+lineno2.
   226  
   227  // logget records the past few values of entry to aid in debugging.
   228  // logget checks the buffer b is not currently in use.
   229  func (b *workbuf) logget(entry int) {
   230  	if !_Debugwbufs {
   231  		return
   232  	}
   233  	if b.inuse {
   234  		println("runtime: logget fails log entry=", entry,
   235  			"b.log[0]=", b.log[0], "b.log[1]=", b.log[1],
   236  			"b.log[2]=", b.log[2], "b.log[3]=", b.log[3])
   237  		throw("logget: get not legal")
   238  	}
   239  	b.inuse = true
   240  	copy(b.log[1:], b.log[:])
   241  	b.log[0] = entry
   242  }
   243  
   244  // logput records the past few values of entry to aid in debugging.
   245  // logput checks the buffer b is currently in use.
   246  func (b *workbuf) logput(entry int) {
   247  	if !_Debugwbufs {
   248  		return
   249  	}
   250  	if !b.inuse {
   251  		println("runtime: logput fails log entry=", entry,
   252  			"b.log[0]=", b.log[0], "b.log[1]=", b.log[1],
   253  			"b.log[2]=", b.log[2], "b.log[3]=", b.log[3])
   254  		throw("logput: put not legal")
   255  	}
   256  	b.inuse = false
   257  	copy(b.log[1:], b.log[:])
   258  	b.log[0] = entry
   259  }
   260  
   261  func (b *workbuf) checknonempty() {
   262  	if b.nobj == 0 {
   263  		println("runtime: nonempty check fails",
   264  			"b.log[0]=", b.log[0], "b.log[1]=", b.log[1],
   265  			"b.log[2]=", b.log[2], "b.log[3]=", b.log[3])
   266  		throw("workbuf is empty")
   267  	}
   268  }
   269  
   270  func (b *workbuf) checkempty() {
   271  	if b.nobj != 0 {
   272  		println("runtime: empty check fails",
   273  			"b.log[0]=", b.log[0], "b.log[1]=", b.log[1],
   274  			"b.log[2]=", b.log[2], "b.log[3]=", b.log[3])
   275  		throw("workbuf is not empty")
   276  	}
   277  }
   278  
   279  // getempty pops an empty work buffer off the work.empty list,
   280  // allocating new buffers if none are available.
   281  // entry is used to record a brief history of ownership.
   282  //go:nowritebarrier
   283  func getempty(entry int) *workbuf {
   284  	var b *workbuf
   285  	if work.empty != 0 {
   286  		b = (*workbuf)(lfstackpop(&work.empty))
   287  		if b != nil {
   288  			b.checkempty()
   289  		}
   290  	}
   291  	if b == nil {
   292  		b = (*workbuf)(persistentalloc(unsafe.Sizeof(*b), _CacheLineSize, &memstats.gc_sys))
   293  	}
   294  	b.logget(entry)
   295  	return b
   296  }
   297  
   298  // putempty puts a workbuf onto the work.empty list.
   299  // Upon entry this go routine owns b. The lfstackpush relinquishes ownership.
   300  //go:nowritebarrier
   301  func putempty(b *workbuf, entry int) {
   302  	b.checkempty()
   303  	b.logput(entry)
   304  	lfstackpush(&work.empty, &b.node)
   305  }
   306  
   307  // putfull puts the workbuf on the work.full list for the GC.
   308  // putfull accepts partially full buffers so the GC can avoid competing
   309  // with the mutators for ownership of partially full buffers.
   310  //go:nowritebarrier
   311  func putfull(b *workbuf, entry int) {
   312  	b.checknonempty()
   313  	b.logput(entry)
   314  	lfstackpush(&work.full, &b.node)
   315  }
   316  
   317  // getpartialorempty tries to return a partially empty
   318  // and if none are available returns an empty one.
   319  // entry is used to provide a brief history of ownership
   320  // using entry + xxx00000 to
   321  // indicating that two line numbers in the call chain.
   322  //go:nowritebarrier
   323  func getpartialorempty(entry int) *workbuf {
   324  	b := (*workbuf)(lfstackpop(&work.partial))
   325  	if b != nil {
   326  		b.logget(entry)
   327  		return b
   328  	}
   329  	// Let getempty do the logget check but
   330  	// use the entry to encode that it passed
   331  	// through this routine.
   332  	b = getempty(entry + 80700000)
   333  	return b
   334  }
   335  
   336  // putpartial puts empty buffers on the work.empty queue,
   337  // full buffers on the work.full queue and
   338  // others on the work.partial queue.
   339  // entry is used to provide a brief history of ownership
   340  // using entry + xxx00000 to
   341  // indicating that two call chain line numbers.
   342  //go:nowritebarrier
   343  func putpartial(b *workbuf, entry int) {
   344  	if b.nobj == 0 {
   345  		putempty(b, entry+81500000)
   346  	} else if b.nobj < len(b.obj) {
   347  		b.logput(entry)
   348  		lfstackpush(&work.partial, &b.node)
   349  	} else if b.nobj == len(b.obj) {
   350  		b.logput(entry)
   351  		lfstackpush(&work.full, &b.node)
   352  	} else {
   353  		throw("putpartial: bad Workbuf b.nobj")
   354  	}
   355  }
   356  
   357  // trygetfull tries to get a full or partially empty workbuffer.
   358  // If one is not immediately available return nil
   359  //go:nowritebarrier
   360  func trygetfull(entry int) *workbuf {
   361  	b := (*workbuf)(lfstackpop(&work.full))
   362  	if b == nil {
   363  		b = (*workbuf)(lfstackpop(&work.partial))
   364  	}
   365  	if b != nil {
   366  		b.logget(entry)
   367  		b.checknonempty()
   368  		return b
   369  	}
   370  	return b
   371  }
   372  
   373  // Get a full work buffer off the work.full or a partially
   374  // filled one off the work.partial list. If nothing is available
   375  // wait until all the other gc helpers have finished and then
   376  // return nil.
   377  // getfull acts as a barrier for work.nproc helpers. As long as one
   378  // gchelper is actively marking objects it
   379  // may create a workbuffer that the other helpers can work on.
   380  // The for loop either exits when a work buffer is found
   381  // or when _all_ of the work.nproc GC helpers are in the loop
   382  // looking for work and thus not capable of creating new work.
   383  // This is in fact the termination condition for the STW mark
   384  // phase.
   385  //go:nowritebarrier
   386  func getfull(entry int) *workbuf {
   387  	b := (*workbuf)(lfstackpop(&work.full))
   388  	if b != nil {
   389  		b.logget(entry)
   390  		b.checknonempty()
   391  		return b
   392  	}
   393  	b = (*workbuf)(lfstackpop(&work.partial))
   394  	if b != nil {
   395  		b.logget(entry)
   396  		return b
   397  	}
   398  
   399  	incnwait := xadd(&work.nwait, +1)
   400  	if incnwait > work.nproc {
   401  		println("runtime: work.nwait=", incnwait, "work.nproc=", work.nproc)
   402  		throw("work.nwait > work.nproc")
   403  	}
   404  	for i := 0; ; i++ {
   405  		if work.full != 0 || work.partial != 0 {
   406  			decnwait := xadd(&work.nwait, -1)
   407  			if decnwait == work.nproc {
   408  				println("runtime: work.nwait=", decnwait, "work.nproc=", work.nproc)
   409  				throw("work.nwait > work.nproc")
   410  			}
   411  			b = (*workbuf)(lfstackpop(&work.full))
   412  			if b == nil {
   413  				b = (*workbuf)(lfstackpop(&work.partial))
   414  			}
   415  			if b != nil {
   416  				b.logget(entry)
   417  				b.checknonempty()
   418  				return b
   419  			}
   420  			incnwait := xadd(&work.nwait, +1)
   421  			if incnwait > work.nproc {
   422  				println("runtime: work.nwait=", incnwait, "work.nproc=", work.nproc)
   423  				throw("work.nwait > work.nproc")
   424  			}
   425  		}
   426  		if work.nwait == work.nproc {
   427  			return nil
   428  		}
   429  		_g_ := getg()
   430  		if i < 10 {
   431  			_g_.m.gcstats.nprocyield++
   432  			procyield(20)
   433  		} else if i < 20 {
   434  			_g_.m.gcstats.nosyield++
   435  			osyield()
   436  		} else {
   437  			_g_.m.gcstats.nsleep++
   438  			usleep(100)
   439  		}
   440  	}
   441  }
   442  
   443  //go:nowritebarrier
   444  func handoff(b *workbuf) *workbuf {
   445  	// Make new buffer with half of b's pointers.
   446  	b1 := getempty(915)
   447  	n := b.nobj / 2
   448  	b.nobj -= n
   449  	b1.nobj = n
   450  	memmove(unsafe.Pointer(&b1.obj[0]), unsafe.Pointer(&b.obj[b.nobj]), uintptr(n)*unsafe.Sizeof(b1.obj[0]))
   451  	_g_ := getg()
   452  	_g_.m.gcstats.nhandoff++
   453  	_g_.m.gcstats.nhandoffcnt += uint64(n)
   454  
   455  	// Put b on full list - let first half of b get stolen.
   456  	putfull(b, 942)
   457  	return b1
   458  }