]> Cypherpunks.ru repositories - gostls13.git/blob - src/runtime/arena.go
a8d89b3c56abba58ddd43362a88b5292989ae89f
[gostls13.git] / src / runtime / arena.go
1 // Copyright 2022 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 // Implementation of (safe) user arenas.
6 //
7 // This file contains the implementation of user arenas wherein Go values can
8 // be manually allocated and freed in bulk. The act of manually freeing memory,
9 // potentially before a GC cycle, means that a garbage collection cycle can be
10 // delayed, improving efficiency by reducing GC cycle frequency. There are other
11 // potential efficiency benefits, such as improved locality and access to a more
12 // efficient allocation strategy.
13 //
14 // What makes the arenas here safe is that once they are freed, accessing the
15 // arena's memory will cause an explicit program fault, and the arena's address
16 // space will not be reused until no more pointers into it are found. There's one
17 // exception to this: if an arena allocated memory that isn't exhausted, it's placed
18 // back into a pool for reuse. This means that a crash is not always guaranteed.
19 //
20 // While this may seem unsafe, it still prevents memory corruption, and is in fact
21 // necessary in order to make new(T) a valid implementation of arenas. Such a property
22 // is desirable to allow for a trivial implementation. (It also avoids complexities
23 // that arise from synchronization with the GC when trying to set the arena chunks to
24 // fault while the GC is active.)
25 //
26 // The implementation works in layers. At the bottom, arenas are managed in chunks.
27 // Each chunk must be a multiple of the heap arena size, or the heap arena size must
28 // be divisible by the arena chunks. The address space for each chunk, and each
29 // corresponding heapArena for that address space, are eternally reserved for use as
30 // arena chunks. That is, they can never be used for the general heap. Each chunk
31 // is also represented by a single mspan, and is modeled as a single large heap
32 // allocation. It must be, because each chunk contains ordinary Go values that may
33 // point into the heap, so it must be scanned just like any other object. Any
34 // pointer into a chunk will therefore always cause the whole chunk to be scanned
35 // while its corresponding arena is still live.
36 //
37 // Chunks may be allocated either from new memory mapped by the OS on our behalf,
38 // or by reusing old freed chunks. When chunks are freed, their underlying memory
39 // is returned to the OS, set to fault on access, and may not be reused until the
40 // program doesn't point into the chunk anymore (the code refers to this state as
41 // "quarantined"), a property checked by the GC.
42 //
43 // The sweeper handles moving chunks out of this quarantine state to be ready for
44 // reuse. When the chunk is placed into the quarantine state, its corresponding
45 // span is marked as noscan so that the GC doesn't try to scan memory that would
46 // cause a fault.
47 //
48 // At the next layer are the user arenas themselves. They consist of a single
49 // active chunk which new Go values are bump-allocated into and a list of chunks
50 // that were exhausted when allocating into the arena. Once the arena is freed,
51 // it frees all full chunks it references, and places the active one onto a reuse
52 // list for a future arena to use. Each arena keeps its list of referenced chunks
53 // explicitly live until it is freed. Each user arena also maps to an object which
54 // has a finalizer attached that ensures the arena's chunks are all freed even if
55 // the arena itself is never explicitly freed.
56 //
57 // Pointer-ful memory is bump-allocated from low addresses to high addresses in each
58 // chunk, while pointer-free memory is bump-allocated from high address to low
59 // addresses. The reason for this is to take advantage of a GC optimization wherein
60 // the GC will stop scanning an object when there are no more pointers in it, which
61 // also allows us to elide clearing the heap bitmap for pointer-free Go values
62 // allocated into arenas.
63 //
64 // Note that arenas are not safe to use concurrently.
65 //
66 // In summary, there are 2 resources: arenas, and arena chunks. They exist in the
67 // following lifecycle:
68 //
69 // (1) A new arena is created via newArena.
70 // (2) Chunks are allocated to hold memory allocated into the arena with new or slice.
71 //    (a) Chunks are first allocated from the reuse list of partially-used chunks.
72 //    (b) If there are no such chunks, then chunks on the ready list are taken.
73 //    (c) Failing all the above, memory for a new chunk is mapped.
74 // (3) The arena is freed, or all references to it are dropped, triggering its finalizer.
75 //    (a) If the GC is not active, exhausted chunks are set to fault and placed on a
76 //        quarantine list.
77 //    (b) If the GC is active, exhausted chunks are placed on a fault list and will
78 //        go through step (a) at a later point in time.
79 //    (c) Any remaining partially-used chunk is placed on a reuse list.
80 // (4) Once no more pointers are found into quarantined arena chunks, the sweeper
81 //     takes these chunks out of quarantine and places them on the ready list.
82
83 package runtime
84
85 import (
86         "runtime/internal/atomic"
87         "runtime/internal/math"
88         "unsafe"
89 )
90
91 // Functions starting with arena_ are meant to be exported to downstream users
92 // of arenas. They should wrap these functions in a higher-lever API.
93 //
94 // The underlying arena and its resources are managed through an opaque unsafe.Pointer.
95
96 // arena_newArena is a wrapper around newUserArena.
97 //
98 //go:linkname arena_newArena arena.runtime_arena_newArena
99 func arena_newArena() unsafe.Pointer {
100         return unsafe.Pointer(newUserArena())
101 }
102
103 // arena_arena_New is a wrapper around (*userArena).new, except that typ
104 // is an any (must be a *_type, still) and typ must be a type descriptor
105 // for a pointer to the type to actually be allocated, i.e. pass a *T
106 // to allocate a T. This is necessary because this function returns a *T.
107 //
108 //go:linkname arena_arena_New arena.runtime_arena_arena_New
109 func arena_arena_New(arena unsafe.Pointer, typ any) any {
110         t := (*_type)(efaceOf(&typ).data)
111         if t.Kind_&kindMask != kindPtr {
112                 throw("arena_New: non-pointer type")
113         }
114         te := (*ptrtype)(unsafe.Pointer(t)).Elem
115         x := ((*userArena)(arena)).new(te)
116         var result any
117         e := efaceOf(&result)
118         e._type = t
119         e.data = x
120         return result
121 }
122
123 // arena_arena_Slice is a wrapper around (*userArena).slice.
124 //
125 //go:linkname arena_arena_Slice arena.runtime_arena_arena_Slice
126 func arena_arena_Slice(arena unsafe.Pointer, slice any, cap int) {
127         ((*userArena)(arena)).slice(slice, cap)
128 }
129
130 // arena_arena_Free is a wrapper around (*userArena).free.
131 //
132 //go:linkname arena_arena_Free arena.runtime_arena_arena_Free
133 func arena_arena_Free(arena unsafe.Pointer) {
134         ((*userArena)(arena)).free()
135 }
136
137 // arena_heapify takes a value that lives in an arena and makes a copy
138 // of it on the heap. Values that don't live in an arena are returned unmodified.
139 //
140 //go:linkname arena_heapify arena.runtime_arena_heapify
141 func arena_heapify(s any) any {
142         var v unsafe.Pointer
143         e := efaceOf(&s)
144         t := e._type
145         switch t.Kind_ & kindMask {
146         case kindString:
147                 v = stringStructOf((*string)(e.data)).str
148         case kindSlice:
149                 v = (*slice)(e.data).array
150         case kindPtr:
151                 v = e.data
152         default:
153                 panic("arena: Clone only supports pointers, slices, and strings")
154         }
155         span := spanOf(uintptr(v))
156         if span == nil || !span.isUserArenaChunk {
157                 // Not stored in a user arena chunk.
158                 return s
159         }
160         // Heap-allocate storage for a copy.
161         var x any
162         switch t.Kind_ & kindMask {
163         case kindString:
164                 s1 := s.(string)
165                 s2, b := rawstring(len(s1))
166                 copy(b, s1)
167                 x = s2
168         case kindSlice:
169                 len := (*slice)(e.data).len
170                 et := (*slicetype)(unsafe.Pointer(t)).Elem
171                 sl := new(slice)
172                 *sl = slice{makeslicecopy(et, len, len, (*slice)(e.data).array), len, len}
173                 xe := efaceOf(&x)
174                 xe._type = t
175                 xe.data = unsafe.Pointer(sl)
176         case kindPtr:
177                 et := (*ptrtype)(unsafe.Pointer(t)).Elem
178                 e2 := newobject(et)
179                 typedmemmove(et, e2, e.data)
180                 xe := efaceOf(&x)
181                 xe._type = t
182                 xe.data = e2
183         }
184         return x
185 }
186
187 const (
188         // userArenaChunkBytes is the size of a user arena chunk.
189         userArenaChunkBytesMax = 8 << 20
190         userArenaChunkBytes    = uintptr(int64(userArenaChunkBytesMax-heapArenaBytes)&(int64(userArenaChunkBytesMax-heapArenaBytes)>>63) + heapArenaBytes) // min(userArenaChunkBytesMax, heapArenaBytes)
191
192         // userArenaChunkPages is the number of pages a user arena chunk uses.
193         userArenaChunkPages = userArenaChunkBytes / pageSize
194
195         // userArenaChunkMaxAllocBytes is the maximum size of an object that can
196         // be allocated from an arena. This number is chosen to cap worst-case
197         // fragmentation of user arenas to 25%. Larger allocations are redirected
198         // to the heap.
199         userArenaChunkMaxAllocBytes = userArenaChunkBytes / 4
200 )
201
202 func init() {
203         if userArenaChunkPages*pageSize != userArenaChunkBytes {
204                 throw("user arena chunk size is not a multiple of the page size")
205         }
206         if userArenaChunkBytes%physPageSize != 0 {
207                 throw("user arena chunk size is not a multiple of the physical page size")
208         }
209         if userArenaChunkBytes < heapArenaBytes {
210                 if heapArenaBytes%userArenaChunkBytes != 0 {
211                         throw("user arena chunk size is smaller than a heap arena, but doesn't divide it")
212                 }
213         } else {
214                 if userArenaChunkBytes%heapArenaBytes != 0 {
215                         throw("user arena chunks size is larger than a heap arena, but not a multiple")
216                 }
217         }
218         lockInit(&userArenaState.lock, lockRankUserArenaState)
219 }
220
221 type userArena struct {
222         // full is a list of full chunks that have not enough free memory left, and
223         // that we'll free once this user arena is freed.
224         //
225         // Can't use mSpanList here because it's not-in-heap.
226         fullList *mspan
227
228         // active is the user arena chunk we're currently allocating into.
229         active *mspan
230
231         // refs is a set of references to the arena chunks so that they're kept alive.
232         //
233         // The last reference in the list always refers to active, while the rest of
234         // them correspond to fullList. Specifically, the head of fullList is the
235         // second-to-last one, fullList.next is the third-to-last, and so on.
236         //
237         // In other words, every time a new chunk becomes active, its appended to this
238         // list.
239         refs []unsafe.Pointer
240
241         // defunct is true if free has been called on this arena.
242         //
243         // This is just a best-effort way to discover a concurrent allocation
244         // and free. Also used to detect a double-free.
245         defunct atomic.Bool
246 }
247
248 // newUserArena creates a new userArena ready to be used.
249 func newUserArena() *userArena {
250         a := new(userArena)
251         SetFinalizer(a, func(a *userArena) {
252                 // If arena handle is dropped without being freed, then call
253                 // free on the arena, so the arena chunks are never reclaimed
254                 // by the garbage collector.
255                 a.free()
256         })
257         a.refill()
258         return a
259 }
260
261 // new allocates a new object of the provided type into the arena, and returns
262 // its pointer.
263 //
264 // This operation is not safe to call concurrently with other operations on the
265 // same arena.
266 func (a *userArena) new(typ *_type) unsafe.Pointer {
267         return a.alloc(typ, -1)
268 }
269
270 // slice allocates a new slice backing store. slice must be a pointer to a slice
271 // (i.e. *[]T), because userArenaSlice will update the slice directly.
272 //
273 // cap determines the capacity of the slice backing store and must be non-negative.
274 //
275 // This operation is not safe to call concurrently with other operations on the
276 // same arena.
277 func (a *userArena) slice(sl any, cap int) {
278         if cap < 0 {
279                 panic("userArena.slice: negative cap")
280         }
281         i := efaceOf(&sl)
282         typ := i._type
283         if typ.Kind_&kindMask != kindPtr {
284                 panic("slice result of non-ptr type")
285         }
286         typ = (*ptrtype)(unsafe.Pointer(typ)).Elem
287         if typ.Kind_&kindMask != kindSlice {
288                 panic("slice of non-ptr-to-slice type")
289         }
290         typ = (*slicetype)(unsafe.Pointer(typ)).Elem
291         // t is now the element type of the slice we want to allocate.
292
293         *((*slice)(i.data)) = slice{a.alloc(typ, cap), cap, cap}
294 }
295
296 // free returns the userArena's chunks back to mheap and marks it as defunct.
297 //
298 // Must be called at most once for any given arena.
299 //
300 // This operation is not safe to call concurrently with other operations on the
301 // same arena.
302 func (a *userArena) free() {
303         // Check for a double-free.
304         if a.defunct.Load() {
305                 panic("arena double free")
306         }
307
308         // Mark ourselves as defunct.
309         a.defunct.Store(true)
310         SetFinalizer(a, nil)
311
312         // Free all the full arenas.
313         //
314         // The refs on this list are in reverse order from the second-to-last.
315         s := a.fullList
316         i := len(a.refs) - 2
317         for s != nil {
318                 a.fullList = s.next
319                 s.next = nil
320                 freeUserArenaChunk(s, a.refs[i])
321                 s = a.fullList
322                 i--
323         }
324         if a.fullList != nil || i >= 0 {
325                 // There's still something left on the full list, or we
326                 // failed to actually iterate over the entire refs list.
327                 throw("full list doesn't match refs list in length")
328         }
329
330         // Put the active chunk onto the reuse list.
331         //
332         // Note that active's reference is always the last reference in refs.
333         s = a.active
334         if s != nil {
335                 if raceenabled || msanenabled || asanenabled {
336                         // Don't reuse arenas with sanitizers enabled. We want to catch
337                         // any use-after-free errors aggressively.
338                         freeUserArenaChunk(s, a.refs[len(a.refs)-1])
339                 } else {
340                         lock(&userArenaState.lock)
341                         userArenaState.reuse = append(userArenaState.reuse, liveUserArenaChunk{s, a.refs[len(a.refs)-1]})
342                         unlock(&userArenaState.lock)
343                 }
344         }
345         // nil out a.active so that a race with freeing will more likely cause a crash.
346         a.active = nil
347         a.refs = nil
348 }
349
350 // alloc reserves space in the current chunk or calls refill and reserves space
351 // in a new chunk. If cap is negative, the type will be taken literally, otherwise
352 // it will be considered as an element type for a slice backing store with capacity
353 // cap.
354 func (a *userArena) alloc(typ *_type, cap int) unsafe.Pointer {
355         s := a.active
356         var x unsafe.Pointer
357         for {
358                 x = s.userArenaNextFree(typ, cap)
359                 if x != nil {
360                         break
361                 }
362                 s = a.refill()
363         }
364         return x
365 }
366
367 // refill inserts the current arena chunk onto the full list and obtains a new
368 // one, either from the partial list or allocating a new one, both from mheap.
369 func (a *userArena) refill() *mspan {
370         // If there's an active chunk, assume it's full.
371         s := a.active
372         if s != nil {
373                 if s.userArenaChunkFree.size() > userArenaChunkMaxAllocBytes {
374                         // It's difficult to tell when we're actually out of memory
375                         // in a chunk because the allocation that failed may still leave
376                         // some free space available. However, that amount of free space
377                         // should never exceed the maximum allocation size.
378                         throw("wasted too much memory in an arena chunk")
379                 }
380                 s.next = a.fullList
381                 a.fullList = s
382                 a.active = nil
383                 s = nil
384         }
385         var x unsafe.Pointer
386
387         // Check the partially-used list.
388         lock(&userArenaState.lock)
389         if len(userArenaState.reuse) > 0 {
390                 // Pick off the last arena chunk from the list.
391                 n := len(userArenaState.reuse) - 1
392                 x = userArenaState.reuse[n].x
393                 s = userArenaState.reuse[n].mspan
394                 userArenaState.reuse[n].x = nil
395                 userArenaState.reuse[n].mspan = nil
396                 userArenaState.reuse = userArenaState.reuse[:n]
397         }
398         unlock(&userArenaState.lock)
399         if s == nil {
400                 // Allocate a new one.
401                 x, s = newUserArenaChunk()
402                 if s == nil {
403                         throw("out of memory")
404                 }
405         }
406         a.refs = append(a.refs, x)
407         a.active = s
408         return s
409 }
410
411 type liveUserArenaChunk struct {
412         *mspan // Must represent a user arena chunk.
413
414         // Reference to mspan.base() to keep the chunk alive.
415         x unsafe.Pointer
416 }
417
418 var userArenaState struct {
419         lock mutex
420
421         // reuse contains a list of partially-used and already-live
422         // user arena chunks that can be quickly reused for another
423         // arena.
424         //
425         // Protected by lock.
426         reuse []liveUserArenaChunk
427
428         // fault contains full user arena chunks that need to be faulted.
429         //
430         // Protected by lock.
431         fault []liveUserArenaChunk
432 }
433
434 // userArenaNextFree reserves space in the user arena for an item of the specified
435 // type. If cap is not -1, this is for an array of cap elements of type t.
436 func (s *mspan) userArenaNextFree(typ *_type, cap int) unsafe.Pointer {
437         size := typ.Size_
438         if cap > 0 {
439                 if size > ^uintptr(0)/uintptr(cap) {
440                         // Overflow.
441                         throw("out of memory")
442                 }
443                 size *= uintptr(cap)
444         }
445         if size == 0 || cap == 0 {
446                 return unsafe.Pointer(&zerobase)
447         }
448         if size > userArenaChunkMaxAllocBytes {
449                 // Redirect allocations that don't fit into a chunk well directly
450                 // from the heap.
451                 if cap >= 0 {
452                         return newarray(typ, cap)
453                 }
454                 return newobject(typ)
455         }
456
457         // Prevent preemption as we set up the space for a new object.
458         //
459         // Act like we're allocating.
460         mp := acquirem()
461         if mp.mallocing != 0 {
462                 throw("malloc deadlock")
463         }
464         if mp.gsignal == getg() {
465                 throw("malloc during signal")
466         }
467         mp.mallocing = 1
468
469         var ptr unsafe.Pointer
470         if typ.PtrBytes == 0 {
471                 // Allocate pointer-less objects from the tail end of the chunk.
472                 v, ok := s.userArenaChunkFree.takeFromBack(size, typ.Align_)
473                 if ok {
474                         ptr = unsafe.Pointer(v)
475                 }
476         } else {
477                 v, ok := s.userArenaChunkFree.takeFromFront(size, typ.Align_)
478                 if ok {
479                         ptr = unsafe.Pointer(v)
480                 }
481         }
482         if ptr == nil {
483                 // Failed to allocate.
484                 mp.mallocing = 0
485                 releasem(mp)
486                 return nil
487         }
488         if s.needzero != 0 {
489                 throw("arena chunk needs zeroing, but should already be zeroed")
490         }
491         // Set up heap bitmap and do extra accounting.
492         if typ.PtrBytes != 0 {
493                 if cap >= 0 {
494                         userArenaHeapBitsSetSliceType(typ, cap, ptr, s.base())
495                 } else {
496                         userArenaHeapBitsSetType(typ, ptr, s.base())
497                 }
498                 c := getMCache(mp)
499                 if c == nil {
500                         throw("mallocgc called without a P or outside bootstrapping")
501                 }
502                 if cap > 0 {
503                         c.scanAlloc += size - (typ.Size_ - typ.PtrBytes)
504                 } else {
505                         c.scanAlloc += typ.PtrBytes
506                 }
507         }
508
509         // Ensure that the stores above that initialize x to
510         // type-safe memory and set the heap bits occur before
511         // the caller can make ptr observable to the garbage
512         // collector. Otherwise, on weakly ordered machines,
513         // the garbage collector could follow a pointer to x,
514         // but see uninitialized memory or stale heap bits.
515         publicationBarrier()
516
517         mp.mallocing = 0
518         releasem(mp)
519
520         return ptr
521 }
522
523 // userArenaHeapBitsSetSliceType is the equivalent of heapBitsSetType but for
524 // Go slice backing store values allocated in a user arena chunk. It sets up the
525 // heap bitmap for n consecutive values with type typ allocated at address ptr.
526 func userArenaHeapBitsSetSliceType(typ *_type, n int, ptr unsafe.Pointer, base uintptr) {
527         mem, overflow := math.MulUintptr(typ.Size_, uintptr(n))
528         if overflow || n < 0 || mem > maxAlloc {
529                 panic(plainError("runtime: allocation size out of range"))
530         }
531         for i := 0; i < n; i++ {
532                 userArenaHeapBitsSetType(typ, add(ptr, uintptr(i)*typ.Size_), base)
533         }
534 }
535
536 // newUserArenaChunk allocates a user arena chunk, which maps to a single
537 // heap arena and single span. Returns a pointer to the base of the chunk
538 // (this is really important: we need to keep the chunk alive) and the span.
539 func newUserArenaChunk() (unsafe.Pointer, *mspan) {
540         if gcphase == _GCmarktermination {
541                 throw("newUserArenaChunk called with gcphase == _GCmarktermination")
542         }
543
544         // Deduct assist credit. Because user arena chunks are modeled as one
545         // giant heap object which counts toward heapLive, we're obligated to
546         // assist the GC proportionally (and it's worth noting that the arena
547         // does represent additional work for the GC, but we also have no idea
548         // what that looks like until we actually allocate things into the
549         // arena).
550         deductAssistCredit(userArenaChunkBytes)
551
552         // Set mp.mallocing to keep from being preempted by GC.
553         mp := acquirem()
554         if mp.mallocing != 0 {
555                 throw("malloc deadlock")
556         }
557         if mp.gsignal == getg() {
558                 throw("malloc during signal")
559         }
560         mp.mallocing = 1
561
562         // Allocate a new user arena.
563         var span *mspan
564         systemstack(func() {
565                 span = mheap_.allocUserArenaChunk()
566         })
567         if span == nil {
568                 throw("out of memory")
569         }
570         x := unsafe.Pointer(span.base())
571
572         // Allocate black during GC.
573         // All slots hold nil so no scanning is needed.
574         // This may be racing with GC so do it atomically if there can be
575         // a race marking the bit.
576         if gcphase != _GCoff {
577                 gcmarknewobject(span, span.base(), span.elemsize)
578         }
579
580         if raceenabled {
581                 // TODO(mknyszek): Track individual objects.
582                 racemalloc(unsafe.Pointer(span.base()), span.elemsize)
583         }
584
585         if msanenabled {
586                 // TODO(mknyszek): Track individual objects.
587                 msanmalloc(unsafe.Pointer(span.base()), span.elemsize)
588         }
589
590         if asanenabled {
591                 // TODO(mknyszek): Track individual objects.
592                 rzSize := computeRZlog(span.elemsize)
593                 span.elemsize -= rzSize
594                 span.limit -= rzSize
595                 span.userArenaChunkFree = makeAddrRange(span.base(), span.limit)
596                 asanpoison(unsafe.Pointer(span.limit), span.npages*pageSize-span.elemsize)
597                 asanunpoison(unsafe.Pointer(span.base()), span.elemsize)
598         }
599
600         if rate := MemProfileRate; rate > 0 {
601                 c := getMCache(mp)
602                 if c == nil {
603                         throw("newUserArenaChunk called without a P or outside bootstrapping")
604                 }
605                 // Note cache c only valid while m acquired; see #47302
606                 if rate != 1 && userArenaChunkBytes < c.nextSample {
607                         c.nextSample -= userArenaChunkBytes
608                 } else {
609                         profilealloc(mp, unsafe.Pointer(span.base()), userArenaChunkBytes)
610                 }
611         }
612         mp.mallocing = 0
613         releasem(mp)
614
615         // Again, because this chunk counts toward heapLive, potentially trigger a GC.
616         if t := (gcTrigger{kind: gcTriggerHeap}); t.test() {
617                 gcStart(t)
618         }
619
620         if debug.malloc {
621                 if debug.allocfreetrace != 0 {
622                         tracealloc(unsafe.Pointer(span.base()), userArenaChunkBytes, nil)
623                 }
624
625                 if inittrace.active && inittrace.id == getg().goid {
626                         // Init functions are executed sequentially in a single goroutine.
627                         inittrace.bytes += uint64(userArenaChunkBytes)
628                 }
629         }
630
631         // Double-check it's aligned to the physical page size. Based on the current
632         // implementation this is trivially true, but it need not be in the future.
633         // However, if it's not aligned to the physical page size then we can't properly
634         // set it to fault later.
635         if uintptr(x)%physPageSize != 0 {
636                 throw("user arena chunk is not aligned to the physical page size")
637         }
638
639         return x, span
640 }
641
642 // isUnusedUserArenaChunk indicates that the arena chunk has been set to fault
643 // and doesn't contain any scannable memory anymore. However, it might still be
644 // mSpanInUse as it sits on the quarantine list, since it needs to be swept.
645 //
646 // This is not safe to execute unless the caller has ownership of the mspan or
647 // the world is stopped (preemption is prevented while the relevant state changes).
648 //
649 // This is really only meant to be used by accounting tests in the runtime to
650 // distinguish when a span shouldn't be counted (since mSpanInUse might not be
651 // enough).
652 func (s *mspan) isUnusedUserArenaChunk() bool {
653         return s.isUserArenaChunk && s.spanclass == makeSpanClass(0, true)
654 }
655
656 // setUserArenaChunkToFault sets the address space for the user arena chunk to fault
657 // and releases any underlying memory resources.
658 //
659 // Must be in a non-preemptible state to ensure the consistency of statistics
660 // exported to MemStats.
661 func (s *mspan) setUserArenaChunkToFault() {
662         if !s.isUserArenaChunk {
663                 throw("invalid span in heapArena for user arena")
664         }
665         if s.npages*pageSize != userArenaChunkBytes {
666                 throw("span on userArena.faultList has invalid size")
667         }
668
669         // Update the span class to be noscan. What we want to happen is that
670         // any pointer into the span keeps it from getting recycled, so we want
671         // the mark bit to get set, but we're about to set the address space to fault,
672         // so we have to prevent the GC from scanning this memory.
673         //
674         // It's OK to set it here because (1) a GC isn't in progress, so the scanning code
675         // won't make a bad decision, (2) we're currently non-preemptible and in the runtime,
676         // so a GC is blocked from starting. We might race with sweeping, which could
677         // put it on the "wrong" sweep list, but really don't care because the chunk is
678         // treated as a large object span and there's no meaningful difference between scan
679         // and noscan large objects in the sweeper. The STW at the start of the GC acts as a
680         // barrier for this update.
681         s.spanclass = makeSpanClass(0, true)
682
683         // Actually set the arena chunk to fault, so we'll get dangling pointer errors.
684         // sysFault currently uses a method on each OS that forces it to evacuate all
685         // memory backing the chunk.
686         sysFault(unsafe.Pointer(s.base()), s.npages*pageSize)
687
688         // Everything on the list is counted as in-use, however sysFault transitions to
689         // Reserved, not Prepared, so we skip updating heapFree or heapReleased and just
690         // remove the memory from the total altogether; it's just address space now.
691         gcController.heapInUse.add(-int64(s.npages * pageSize))
692
693         // Count this as a free of an object right now as opposed to when
694         // the span gets off the quarantine list. The main reason is so that the
695         // amount of bytes allocated doesn't exceed how much is counted as
696         // "mapped ready," which could cause a deadlock in the pacer.
697         gcController.totalFree.Add(int64(s.npages * pageSize))
698
699         // Update consistent stats to match.
700         //
701         // We're non-preemptible, so it's safe to update consistent stats (our P
702         // won't change out from under us).
703         stats := memstats.heapStats.acquire()
704         atomic.Xaddint64(&stats.committed, -int64(s.npages*pageSize))
705         atomic.Xaddint64(&stats.inHeap, -int64(s.npages*pageSize))
706         atomic.Xadd64(&stats.largeFreeCount, 1)
707         atomic.Xadd64(&stats.largeFree, int64(s.npages*pageSize))
708         memstats.heapStats.release()
709
710         // This counts as a free, so update heapLive.
711         gcController.update(-int64(s.npages*pageSize), 0)
712
713         // Mark it as free for the race detector.
714         if raceenabled {
715                 racefree(unsafe.Pointer(s.base()), s.elemsize)
716         }
717
718         systemstack(func() {
719                 // Add the user arena to the quarantine list.
720                 lock(&mheap_.lock)
721                 mheap_.userArena.quarantineList.insert(s)
722                 unlock(&mheap_.lock)
723         })
724 }
725
726 // inUserArenaChunk returns true if p points to a user arena chunk.
727 func inUserArenaChunk(p uintptr) bool {
728         s := spanOf(p)
729         if s == nil {
730                 return false
731         }
732         return s.isUserArenaChunk
733 }
734
735 // freeUserArenaChunk releases the user arena represented by s back to the runtime.
736 //
737 // x must be a live pointer within s.
738 //
739 // The runtime will set the user arena to fault once it's safe (the GC is no longer running)
740 // and then once the user arena is no longer referenced by the application, will allow it to
741 // be reused.
742 func freeUserArenaChunk(s *mspan, x unsafe.Pointer) {
743         if !s.isUserArenaChunk {
744                 throw("span is not for a user arena")
745         }
746         if s.npages*pageSize != userArenaChunkBytes {
747                 throw("invalid user arena span size")
748         }
749
750         // Mark the region as free to various santizers immediately instead
751         // of handling them at sweep time.
752         if raceenabled {
753                 racefree(unsafe.Pointer(s.base()), s.elemsize)
754         }
755         if msanenabled {
756                 msanfree(unsafe.Pointer(s.base()), s.elemsize)
757         }
758         if asanenabled {
759                 asanpoison(unsafe.Pointer(s.base()), s.elemsize)
760         }
761
762         // Make ourselves non-preemptible as we manipulate state and statistics.
763         //
764         // Also required by setUserArenaChunksToFault.
765         mp := acquirem()
766
767         // We can only set user arenas to fault if we're in the _GCoff phase.
768         if gcphase == _GCoff {
769                 lock(&userArenaState.lock)
770                 faultList := userArenaState.fault
771                 userArenaState.fault = nil
772                 unlock(&userArenaState.lock)
773
774                 s.setUserArenaChunkToFault()
775                 for _, lc := range faultList {
776                         lc.mspan.setUserArenaChunkToFault()
777                 }
778
779                 // Until the chunks are set to fault, keep them alive via the fault list.
780                 KeepAlive(x)
781                 KeepAlive(faultList)
782         } else {
783                 // Put the user arena on the fault list.
784                 lock(&userArenaState.lock)
785                 userArenaState.fault = append(userArenaState.fault, liveUserArenaChunk{s, x})
786                 unlock(&userArenaState.lock)
787         }
788         releasem(mp)
789 }
790
791 // allocUserArenaChunk attempts to reuse a free user arena chunk represented
792 // as a span.
793 //
794 // Must be in a non-preemptible state to ensure the consistency of statistics
795 // exported to MemStats.
796 //
797 // Acquires the heap lock. Must run on the system stack for that reason.
798 //
799 //go:systemstack
800 func (h *mheap) allocUserArenaChunk() *mspan {
801         var s *mspan
802         var base uintptr
803
804         // First check the free list.
805         lock(&h.lock)
806         if !h.userArena.readyList.isEmpty() {
807                 s = h.userArena.readyList.first
808                 h.userArena.readyList.remove(s)
809                 base = s.base()
810         } else {
811                 // Free list was empty, so allocate a new arena.
812                 hintList := &h.userArena.arenaHints
813                 if raceenabled {
814                         // In race mode just use the regular heap hints. We might fragment
815                         // the address space, but the race detector requires that the heap
816                         // is mapped contiguously.
817                         hintList = &h.arenaHints
818                 }
819                 v, size := h.sysAlloc(userArenaChunkBytes, hintList, false)
820                 if size%userArenaChunkBytes != 0 {
821                         throw("sysAlloc size is not divisible by userArenaChunkBytes")
822                 }
823                 if size > userArenaChunkBytes {
824                         // We got more than we asked for. This can happen if
825                         // heapArenaSize > userArenaChunkSize, or if sysAlloc just returns
826                         // some extra as a result of trying to find an aligned region.
827                         //
828                         // Divide it up and put it on the ready list.
829                         for i := userArenaChunkBytes; i < size; i += userArenaChunkBytes {
830                                 s := h.allocMSpanLocked()
831                                 s.init(uintptr(v)+i, userArenaChunkPages)
832                                 h.userArena.readyList.insertBack(s)
833                         }
834                         size = userArenaChunkBytes
835                 }
836                 base = uintptr(v)
837                 if base == 0 {
838                         // Out of memory.
839                         unlock(&h.lock)
840                         return nil
841                 }
842                 s = h.allocMSpanLocked()
843         }
844         unlock(&h.lock)
845
846         // sysAlloc returns Reserved address space, and any span we're
847         // reusing is set to fault (so, also Reserved), so transition
848         // it to Prepared and then Ready.
849         //
850         // Unlike (*mheap).grow, just map in everything that we
851         // asked for. We're likely going to use it all.
852         sysMap(unsafe.Pointer(base), userArenaChunkBytes, &gcController.heapReleased)
853         sysUsed(unsafe.Pointer(base), userArenaChunkBytes, userArenaChunkBytes)
854
855         // Model the user arena as a heap span for a large object.
856         spc := makeSpanClass(0, false)
857         h.initSpan(s, spanAllocHeap, spc, base, userArenaChunkPages)
858         s.isUserArenaChunk = true
859
860         // Account for this new arena chunk memory.
861         gcController.heapInUse.add(int64(userArenaChunkBytes))
862         gcController.heapReleased.add(-int64(userArenaChunkBytes))
863
864         stats := memstats.heapStats.acquire()
865         atomic.Xaddint64(&stats.inHeap, int64(userArenaChunkBytes))
866         atomic.Xaddint64(&stats.committed, int64(userArenaChunkBytes))
867
868         // Model the arena as a single large malloc.
869         atomic.Xadd64(&stats.largeAlloc, int64(userArenaChunkBytes))
870         atomic.Xadd64(&stats.largeAllocCount, 1)
871         memstats.heapStats.release()
872
873         // Count the alloc in inconsistent, internal stats.
874         gcController.totalAlloc.Add(int64(userArenaChunkBytes))
875
876         // Update heapLive.
877         gcController.update(int64(userArenaChunkBytes), 0)
878
879         // Put the large span in the mcentral swept list so that it's
880         // visible to the background sweeper.
881         h.central[spc].mcentral.fullSwept(h.sweepgen).push(s)
882         s.limit = s.base() + userArenaChunkBytes
883         s.freeindex = 1
884         s.allocCount = 1
885
886         // This must clear the entire heap bitmap so that it's safe
887         // to allocate noscan data without writing anything out.
888         s.initHeapBits(true)
889
890         // Clear the span preemptively. It's an arena chunk, so let's assume
891         // everything is going to be used.
892         //
893         // This also seems to make a massive difference as to whether or
894         // not Linux decides to back this memory with transparent huge
895         // pages. There's latency involved in this zeroing, but the hugepage
896         // gains are almost always worth it. Note: it's important that we
897         // clear even if it's freshly mapped and we know there's no point
898         // to zeroing as *that* is the critical signal to use huge pages.
899         memclrNoHeapPointers(unsafe.Pointer(s.base()), s.elemsize)
900         s.needzero = 0
901
902         s.freeIndexForScan = 1
903
904         // Set up the range for allocation.
905         s.userArenaChunkFree = makeAddrRange(base, s.limit)
906         return s
907 }