Initial commit: Go 1.23 release state
This commit is contained in:
108
src/arena/arena.go
Normal file
108
src/arena/arena.go
Normal file
@@ -0,0 +1,108 @@
|
||||
// Copyright 2022 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build goexperiment.arenas
|
||||
|
||||
/*
|
||||
The arena package provides the ability to allocate memory for a collection
|
||||
of Go values and free that space manually all at once, safely. The purpose
|
||||
of this functionality is to improve efficiency: manually freeing memory
|
||||
before a garbage collection delays that cycle. Less frequent cycles means
|
||||
the CPU cost of the garbage collector is incurred less frequently.
|
||||
|
||||
This functionality in this package is mostly captured in the Arena type.
|
||||
Arenas allocate large chunks of memory for Go values, so they're likely to
|
||||
be inefficient for allocating only small amounts of small Go values. They're
|
||||
best used in bulk, on the order of MiB of memory allocated on each use.
|
||||
|
||||
Note that by allowing for this limited form of manual memory allocation
|
||||
that use-after-free bugs are possible with regular Go values. This package
|
||||
limits the impact of these use-after-free bugs by preventing reuse of freed
|
||||
memory regions until the garbage collector is able to determine that it is
|
||||
safe. Typically, a use-after-free bug will result in a fault and a helpful
|
||||
error message, but this package reserves the right to not force a fault on
|
||||
freed memory. That means a valid implementation of this package is to just
|
||||
allocate all memory the way the runtime normally would, and in fact, it
|
||||
reserves the right to occasionally do so for some Go values.
|
||||
*/
|
||||
package arena
|
||||
|
||||
import (
|
||||
"internal/reflectlite"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// Arena represents a collection of Go values allocated and freed together.
|
||||
// Arenas are useful for improving efficiency as they may be freed back to
|
||||
// the runtime manually, though any memory obtained from freed arenas must
|
||||
// not be accessed once that happens. An Arena is automatically freed once
|
||||
// it is no longer referenced, so it must be kept alive (see runtime.KeepAlive)
|
||||
// until any memory allocated from it is no longer needed.
|
||||
//
|
||||
// An Arena must never be used concurrently by multiple goroutines.
|
||||
type Arena struct {
|
||||
a unsafe.Pointer
|
||||
}
|
||||
|
||||
// NewArena allocates a new arena.
|
||||
func NewArena() *Arena {
|
||||
return &Arena{a: runtime_arena_newArena()}
|
||||
}
|
||||
|
||||
// Free frees the arena (and all objects allocated from the arena) so that
|
||||
// memory backing the arena can be reused fairly quickly without garbage
|
||||
// collection overhead. Applications must not call any method on this
|
||||
// arena after it has been freed.
|
||||
func (a *Arena) Free() {
|
||||
runtime_arena_arena_Free(a.a)
|
||||
a.a = nil
|
||||
}
|
||||
|
||||
// New creates a new *T in the provided arena. The *T must not be used after
|
||||
// the arena is freed. Accessing the value after free may result in a fault,
|
||||
// but this fault is also not guaranteed.
|
||||
func New[T any](a *Arena) *T {
|
||||
return runtime_arena_arena_New(a.a, reflectlite.TypeOf((*T)(nil))).(*T)
|
||||
}
|
||||
|
||||
// MakeSlice creates a new []T with the provided capacity and length. The []T must
|
||||
// not be used after the arena is freed. Accessing the underlying storage of the
|
||||
// slice after free may result in a fault, but this fault is also not guaranteed.
|
||||
func MakeSlice[T any](a *Arena, len, cap int) []T {
|
||||
var sl []T
|
||||
runtime_arena_arena_Slice(a.a, &sl, cap)
|
||||
return sl[:len]
|
||||
}
|
||||
|
||||
// Clone makes a shallow copy of the input value that is no longer bound to any
|
||||
// arena it may have been allocated from, returning the copy. If it was not
|
||||
// allocated from an arena, it is returned untouched. This function is useful
|
||||
// to more easily let an arena-allocated value out-live its arena.
|
||||
// T must be a pointer, a slice, or a string, otherwise this function will panic.
|
||||
func Clone[T any](s T) T {
|
||||
return runtime_arena_heapify(s).(T)
|
||||
}
|
||||
|
||||
//go:linkname reflect_arena_New reflect.arena_New
|
||||
func reflect_arena_New(a *Arena, typ any) any {
|
||||
return runtime_arena_arena_New(a.a, typ)
|
||||
}
|
||||
|
||||
//go:linkname runtime_arena_newArena
|
||||
func runtime_arena_newArena() unsafe.Pointer
|
||||
|
||||
//go:linkname runtime_arena_arena_New
|
||||
func runtime_arena_arena_New(arena unsafe.Pointer, typ any) any
|
||||
|
||||
// Mark as noescape to avoid escaping the slice header.
|
||||
//
|
||||
//go:noescape
|
||||
//go:linkname runtime_arena_arena_Slice
|
||||
func runtime_arena_arena_Slice(arena unsafe.Pointer, slice any, cap int)
|
||||
|
||||
//go:linkname runtime_arena_arena_Free
|
||||
func runtime_arena_arena_Free(arena unsafe.Pointer)
|
||||
|
||||
//go:linkname runtime_arena_heapify
|
||||
func runtime_arena_heapify(any) any
|
||||
42
src/arena/arena_test.go
Normal file
42
src/arena/arena_test.go
Normal file
@@ -0,0 +1,42 @@
|
||||
// Copyright 2022 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build goexperiment.arenas
|
||||
|
||||
package arena_test
|
||||
|
||||
import (
|
||||
"arena"
|
||||
"testing"
|
||||
)
|
||||
|
||||
type T1 struct {
|
||||
n int
|
||||
}
|
||||
type T2 [1 << 20]byte // 1MiB
|
||||
|
||||
func TestSmoke(t *testing.T) {
|
||||
a := arena.NewArena()
|
||||
defer a.Free()
|
||||
|
||||
tt := arena.New[T1](a)
|
||||
tt.n = 1
|
||||
|
||||
ts := arena.MakeSlice[T1](a, 99, 100)
|
||||
if len(ts) != 99 {
|
||||
t.Errorf("Slice() len = %d, want 99", len(ts))
|
||||
}
|
||||
if cap(ts) != 100 {
|
||||
t.Errorf("Slice() cap = %d, want 100", cap(ts))
|
||||
}
|
||||
ts[1].n = 42
|
||||
}
|
||||
|
||||
func TestSmokeLarge(t *testing.T) {
|
||||
a := arena.NewArena()
|
||||
defer a.Free()
|
||||
for i := 0; i < 10*64; i++ {
|
||||
_ = arena.New[T2](a)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user