github.com/peggyl/go@v0.0.0-20151008231540-ae315999c2d5/src/runtime/mbarrier.go (about)

     1  // Copyright 2015 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  // Garbage collector: write barriers.
     6  //
     7  // For the concurrent garbage collector, the Go compiler implements
     8  // updates to pointer-valued fields that may be in heap objects by
     9  // emitting calls to write barriers. This file contains the actual write barrier
    10  // implementation, markwb, and the various wrappers called by the
    11  // compiler to implement pointer assignment, slice assignment,
    12  // typed memmove, and so on.
    13  
    14  package runtime
    15  
    16  import "unsafe"
    17  
    18  // markwb is the mark-phase write barrier, the only barrier we have.
    19  // The rest of this file exists only to make calls to this function.
    20  //
    21  // This is the Dijkstra barrier coarsened to always shade the ptr (dst) object.
    22  // The original Dijkstra barrier only shaded ptrs being placed in black slots.
    23  //
    24  // Shade indicates that it has seen a white pointer by adding the referent
    25  // to wbuf as well as marking it.
    26  //
    27  // slot is the destination (dst) in go code
    28  // ptr is the value that goes into the slot (src) in the go code
    29  //
    30  //
    31  // Dealing with memory ordering:
    32  //
    33  // Dijkstra pointed out that maintaining the no black to white
    34  // pointers means that white to white pointers not need
    35  // to be noted by the write barrier. Furthermore if either
    36  // white object dies before it is reached by the
    37  // GC then the object can be collected during this GC cycle
    38  // instead of waiting for the next cycle. Unfortunately the cost of
    39  // ensure that the object holding the slot doesn't concurrently
    40  // change to black without the mutator noticing seems prohibitive.
    41  //
    42  // Consider the following example where the mutator writes into
    43  // a slot and then loads the slot's mark bit while the GC thread
    44  // writes to the slot's mark bit and then as part of scanning reads
    45  // the slot.
    46  //
    47  // Initially both [slot] and [slotmark] are 0 (nil)
    48  // Mutator thread          GC thread
    49  // st [slot], ptr          st [slotmark], 1
    50  //
    51  // ld r1, [slotmark]       ld r2, [slot]
    52  //
    53  // Without an expensive memory barrier between the st and the ld, the final
    54  // result on most HW (including 386/amd64) can be r1==r2==0. This is a classic
    55  // example of what can happen when loads are allowed to be reordered with older
    56  // stores (avoiding such reorderings lies at the heart of the classic
    57  // Peterson/Dekker algorithms for mutual exclusion). Rather than require memory
    58  // barriers, which will slow down both the mutator and the GC, we always grey
    59  // the ptr object regardless of the slot's color.
    60  //
    61  // Another place where we intentionally omit memory barriers is when
    62  // accessing mheap_.arena_used to check if a pointer points into the
    63  // heap. On relaxed memory machines, it's possible for a mutator to
    64  // extend the size of the heap by updating arena_used, allocate an
    65  // object from this new region, and publish a pointer to that object,
    66  // but for tracing running on another processor to observe the pointer
    67  // but use the old value of arena_used. In this case, tracing will not
    68  // mark the object, even though it's reachable. However, the mutator
    69  // is guaranteed to execute a write barrier when it publishes the
    70  // pointer, so it will take care of marking the object. A general
    71  // consequence of this is that the garbage collector may cache the
    72  // value of mheap_.arena_used. (See issue #9984.)
    73  //
    74  //
    75  // Stack writes:
    76  //
    77  // The compiler omits write barriers for writes to the current frame,
    78  // but if a stack pointer has been passed down the call stack, the
    79  // compiler will generate a write barrier for writes through that
    80  // pointer (because it doesn't know it's not a heap pointer).
    81  //
    82  // One might be tempted to ignore the write barrier if slot points
    83  // into to the stack. Don't do it! Mark termination only re-scans
    84  // frames that have potentially been active since the concurrent scan,
    85  // so it depends on write barriers to track changes to pointers in
    86  // stack frames that have not been active.
    87  //go:nowritebarrier
    88  func gcmarkwb_m(slot *uintptr, ptr uintptr) {
    89  	if writeBarrierEnabled {
    90  		if ptr != 0 && inheap(ptr) {
    91  			shade(ptr)
    92  		}
    93  	}
    94  }
    95  
    96  // Write barrier calls must not happen during critical GC and scheduler
    97  // related operations. In particular there are times when the GC assumes
    98  // that the world is stopped but scheduler related code is still being
    99  // executed, dealing with syscalls, dealing with putting gs on runnable
   100  // queues and so forth. This code can not execute write barriers because
   101  // the GC might drop them on the floor. Stopping the world involves removing
   102  // the p associated with an m. We use the fact that m.p == nil to indicate
   103  // that we are in one these critical section and throw if the write is of
   104  // a pointer to a heap object.
   105  //go:nosplit
   106  func writebarrierptr_nostore1(dst *uintptr, src uintptr) {
   107  	mp := acquirem()
   108  	if mp.inwb || mp.dying > 0 {
   109  		releasem(mp)
   110  		return
   111  	}
   112  	systemstack(func() {
   113  		if mp.p == 0 && memstats.enablegc && !mp.inwb && inheap(src) {
   114  			throw("writebarrierptr_nostore1 called with mp.p == nil")
   115  		}
   116  		mp.inwb = true
   117  		gcmarkwb_m(dst, src)
   118  	})
   119  	mp.inwb = false
   120  	releasem(mp)
   121  }
   122  
   123  // NOTE: Really dst *unsafe.Pointer, src unsafe.Pointer,
   124  // but if we do that, Go inserts a write barrier on *dst = src.
   125  //go:nosplit
   126  func writebarrierptr(dst *uintptr, src uintptr) {
   127  	*dst = src
   128  	if !writeBarrierEnabled {
   129  		return
   130  	}
   131  	if src != 0 && (src < _PhysPageSize || src == poisonStack) {
   132  		systemstack(func() {
   133  			print("runtime: writebarrierptr *", dst, " = ", hex(src), "\n")
   134  			throw("bad pointer in write barrier")
   135  		})
   136  	}
   137  	writebarrierptr_nostore1(dst, src)
   138  }
   139  
   140  // Like writebarrierptr, but the store has already been applied.
   141  // Do not reapply.
   142  //go:nosplit
   143  func writebarrierptr_nostore(dst *uintptr, src uintptr) {
   144  	if !writeBarrierEnabled {
   145  		return
   146  	}
   147  	if src != 0 && (src < _PhysPageSize || src == poisonStack) {
   148  		systemstack(func() { throw("bad pointer in write barrier") })
   149  	}
   150  	writebarrierptr_nostore1(dst, src)
   151  }
   152  
   153  //go:nosplit
   154  func writebarrierstring(dst *[2]uintptr, src [2]uintptr) {
   155  	writebarrierptr(&dst[0], src[0])
   156  	dst[1] = src[1]
   157  }
   158  
   159  //go:nosplit
   160  func writebarrierslice(dst *[3]uintptr, src [3]uintptr) {
   161  	writebarrierptr(&dst[0], src[0])
   162  	dst[1] = src[1]
   163  	dst[2] = src[2]
   164  }
   165  
   166  //go:nosplit
   167  func writebarrieriface(dst *[2]uintptr, src [2]uintptr) {
   168  	writebarrierptr(&dst[0], src[0])
   169  	writebarrierptr(&dst[1], src[1])
   170  }
   171  
   172  //go:generate go run wbfat_gen.go -- wbfat.go
   173  //
   174  // The above line generates multiword write barriers for
   175  // all the combinations of ptr+scalar up to four words.
   176  // The implementations are written to wbfat.go.
   177  
   178  // typedmemmove copies a value of type t to dst from src.
   179  //go:nosplit
   180  func typedmemmove(typ *_type, dst, src unsafe.Pointer) {
   181  	memmove(dst, src, typ.size)
   182  	if typ.kind&kindNoPointers != 0 {
   183  		return
   184  	}
   185  	heapBitsBulkBarrier(uintptr(dst), typ.size)
   186  }
   187  
   188  //go:linkname reflect_typedmemmove reflect.typedmemmove
   189  func reflect_typedmemmove(typ *_type, dst, src unsafe.Pointer) {
   190  	typedmemmove(typ, dst, src)
   191  }
   192  
   193  // typedmemmovepartial is like typedmemmove but assumes that
   194  // dst and src point off bytes into the value and only copies size bytes.
   195  //go:linkname reflect_typedmemmovepartial reflect.typedmemmovepartial
   196  func reflect_typedmemmovepartial(typ *_type, dst, src unsafe.Pointer, off, size uintptr) {
   197  	memmove(dst, src, size)
   198  	if !writeBarrierEnabled || typ.kind&kindNoPointers != 0 || size < ptrSize || !inheap(uintptr(dst)) {
   199  		return
   200  	}
   201  
   202  	if frag := -off & (ptrSize - 1); frag != 0 {
   203  		dst = add(dst, frag)
   204  		size -= frag
   205  	}
   206  	heapBitsBulkBarrier(uintptr(dst), size&^(ptrSize-1))
   207  }
   208  
   209  // callwritebarrier is invoked at the end of reflectcall, to execute
   210  // write barrier operations to record the fact that a call's return
   211  // values have just been copied to frame, starting at retoffset
   212  // and continuing to framesize. The entire frame (not just the return
   213  // values) is described by typ. Because the copy has already
   214  // happened, we call writebarrierptr_nostore, and we must be careful
   215  // not to be preempted before the write barriers have been run.
   216  //go:nosplit
   217  func callwritebarrier(typ *_type, frame unsafe.Pointer, framesize, retoffset uintptr) {
   218  	if !writeBarrierEnabled || typ == nil || typ.kind&kindNoPointers != 0 || framesize-retoffset < ptrSize || !inheap(uintptr(frame)) {
   219  		return
   220  	}
   221  	heapBitsBulkBarrier(uintptr(add(frame, retoffset)), framesize-retoffset)
   222  }
   223  
   224  //go:nosplit
   225  func typedslicecopy(typ *_type, dst, src slice) int {
   226  	// TODO(rsc): If typedslicecopy becomes faster than calling
   227  	// typedmemmove repeatedly, consider using during func growslice.
   228  	n := dst.len
   229  	if n > src.len {
   230  		n = src.len
   231  	}
   232  	if n == 0 {
   233  		return 0
   234  	}
   235  	dstp := unsafe.Pointer(dst.array)
   236  	srcp := unsafe.Pointer(src.array)
   237  
   238  	if raceenabled {
   239  		callerpc := getcallerpc(unsafe.Pointer(&typ))
   240  		pc := funcPC(slicecopy)
   241  		racewriterangepc(dstp, uintptr(n)*typ.size, callerpc, pc)
   242  		racereadrangepc(srcp, uintptr(n)*typ.size, callerpc, pc)
   243  	}
   244  
   245  	// Note: No point in checking typ.kind&kindNoPointers here:
   246  	// compiler only emits calls to typedslicecopy for types with pointers,
   247  	// and growslice and reflect_typedslicecopy check for pointers
   248  	// before calling typedslicecopy.
   249  	if !writeBarrierEnabled {
   250  		memmove(dstp, srcp, uintptr(n)*typ.size)
   251  		return n
   252  	}
   253  
   254  	systemstack(func() {
   255  		if uintptr(srcp) < uintptr(dstp) && uintptr(srcp)+uintptr(n)*typ.size > uintptr(dstp) {
   256  			// Overlap with src before dst.
   257  			// Copy backward, being careful not to move dstp/srcp
   258  			// out of the array they point into.
   259  			dstp = add(dstp, uintptr(n-1)*typ.size)
   260  			srcp = add(srcp, uintptr(n-1)*typ.size)
   261  			i := 0
   262  			for {
   263  				typedmemmove(typ, dstp, srcp)
   264  				if i++; i >= n {
   265  					break
   266  				}
   267  				dstp = add(dstp, -typ.size)
   268  				srcp = add(srcp, -typ.size)
   269  			}
   270  		} else {
   271  			// Copy forward, being careful not to move dstp/srcp
   272  			// out of the array they point into.
   273  			i := 0
   274  			for {
   275  				typedmemmove(typ, dstp, srcp)
   276  				if i++; i >= n {
   277  					break
   278  				}
   279  				dstp = add(dstp, typ.size)
   280  				srcp = add(srcp, typ.size)
   281  			}
   282  		}
   283  	})
   284  	return int(n)
   285  }
   286  
   287  //go:linkname reflect_typedslicecopy reflect.typedslicecopy
   288  func reflect_typedslicecopy(elemType *_type, dst, src slice) int {
   289  	if elemType.kind&kindNoPointers != 0 {
   290  		n := dst.len
   291  		if n > src.len {
   292  			n = src.len
   293  		}
   294  		memmove(dst.array, src.array, uintptr(n)*elemType.size)
   295  		return n
   296  	}
   297  	return typedslicecopy(elemType, dst, src)
   298  }