github.com/q45/go@v0.0.0-20151101211701-a4fb8c13db3f/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 do 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 if msanenabled { 245 msanwrite(dstp, uintptr(n)*typ.size) 246 msanread(srcp, uintptr(n)*typ.size) 247 } 248 249 // Note: No point in checking typ.kind&kindNoPointers here: 250 // compiler only emits calls to typedslicecopy for types with pointers, 251 // and growslice and reflect_typedslicecopy check for pointers 252 // before calling typedslicecopy. 253 if !writeBarrierEnabled { 254 memmove(dstp, srcp, uintptr(n)*typ.size) 255 return n 256 } 257 258 systemstack(func() { 259 if uintptr(srcp) < uintptr(dstp) && uintptr(srcp)+uintptr(n)*typ.size > uintptr(dstp) { 260 // Overlap with src before dst. 261 // Copy backward, being careful not to move dstp/srcp 262 // out of the array they point into. 263 dstp = add(dstp, uintptr(n-1)*typ.size) 264 srcp = add(srcp, uintptr(n-1)*typ.size) 265 i := 0 266 for { 267 typedmemmove(typ, dstp, srcp) 268 if i++; i >= n { 269 break 270 } 271 dstp = add(dstp, -typ.size) 272 srcp = add(srcp, -typ.size) 273 } 274 } else { 275 // Copy forward, being careful not to move dstp/srcp 276 // out of the array they point into. 277 i := 0 278 for { 279 typedmemmove(typ, dstp, srcp) 280 if i++; i >= n { 281 break 282 } 283 dstp = add(dstp, typ.size) 284 srcp = add(srcp, typ.size) 285 } 286 } 287 }) 288 return int(n) 289 } 290 291 //go:linkname reflect_typedslicecopy reflect.typedslicecopy 292 func reflect_typedslicecopy(elemType *_type, dst, src slice) int { 293 if elemType.kind&kindNoPointers != 0 { 294 n := dst.len 295 if n > src.len { 296 n = src.len 297 } 298 memmove(dst.array, src.array, uintptr(n)*elemType.size) 299 return n 300 } 301 return typedslicecopy(elemType, dst, src) 302 }