mem: memory allocation primitives; arena: paging arena
This commit is contained in:
parent
4f8383d497
commit
f83a1f323f
2 changed files with 143 additions and 0 deletions
|
|
@ -2,6 +2,7 @@ package arena
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"math/bits"
|
"math/bits"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
|
@ -171,6 +172,89 @@ func Chunked(max_allocs_per_chunk uintptr) Arena {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Paging is a linear arena that allocates pages of virtual memory.
|
||||||
|
// The memory allocated is only committed to physical memory as it is used,
|
||||||
|
// so total_reserved_in_bytes should is the total amount of addressable memory to reserve.
|
||||||
|
//
|
||||||
|
// Note: resetting a Paging arena will cause the currently commited memory to be decommited (i.e. unmapped from physical memory).
|
||||||
|
func Paging(page_size, total_reserved_in_bytes uintptr) Arena {
|
||||||
|
var (
|
||||||
|
committed uintptr
|
||||||
|
offset uintptr
|
||||||
|
)
|
||||||
|
|
||||||
|
base, err := mem.Reserve(total_reserved_in_bytes)
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Sprintf("paging: failed to reserve address space - %s", err))
|
||||||
|
}
|
||||||
|
|
||||||
|
// @todo(judah): is this needed?
|
||||||
|
runtime.AddCleanup(&base, func(_ struct{}) {
|
||||||
|
if err := mem.Unreserve(base); err != nil {
|
||||||
|
panic(fmt.Sprintf("paging: failed to release memory - %s", err))
|
||||||
|
}
|
||||||
|
}, struct{}{})
|
||||||
|
|
||||||
|
return func(a Action, size, align uintptr, watermark *uintptr) (unsafe.Pointer, error) {
|
||||||
|
switch a {
|
||||||
|
case ACTION_ALLOC:
|
||||||
|
aligned := mem.AlignForward(size, align)
|
||||||
|
if offset+aligned > total_reserved_in_bytes {
|
||||||
|
return nil, errors.New("paging: out of addressable memory")
|
||||||
|
}
|
||||||
|
|
||||||
|
if offset+aligned > committed {
|
||||||
|
required := offset + aligned
|
||||||
|
to_commit := mem.AlignForward(required, page_size)
|
||||||
|
|
||||||
|
if err := mem.Commit(base[committed:to_commit-committed], mem.PERM_READ|mem.PERM_WRITE); err != nil {
|
||||||
|
return nil, fmt.Errorf("paging: failed to commit memory - %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
committed = to_commit
|
||||||
|
}
|
||||||
|
|
||||||
|
ptr := &base[offset]
|
||||||
|
offset += aligned
|
||||||
|
return unsafe.Pointer(ptr), nil
|
||||||
|
|
||||||
|
case ACTION_RESET:
|
||||||
|
if committed > 0 {
|
||||||
|
if err := mem.Decommit(base[:mem.AlignForward(committed, page_size)]); err != nil {
|
||||||
|
return nil, fmt.Errorf("paging: failed to decommit memory - %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
offset = 0
|
||||||
|
committed = 0
|
||||||
|
return nil, nil
|
||||||
|
|
||||||
|
// @todo(judah): should save/restore also decommit memory?
|
||||||
|
|
||||||
|
case ACTION_SAVE:
|
||||||
|
if watermark == nil {
|
||||||
|
return nil, errors.New("paging: cannot save to nil watermark")
|
||||||
|
}
|
||||||
|
|
||||||
|
*watermark = offset
|
||||||
|
return nil, nil
|
||||||
|
|
||||||
|
case ACTION_RESTORE:
|
||||||
|
if watermark == nil {
|
||||||
|
return nil, errors.New("paging: cannot restore nil watermark")
|
||||||
|
}
|
||||||
|
|
||||||
|
clear(base[*watermark:offset])
|
||||||
|
offset = *watermark
|
||||||
|
|
||||||
|
return nil, nil
|
||||||
|
|
||||||
|
default:
|
||||||
|
panic("paging: unimplemented action - " + a.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Nil is an Arena that always returns an error.
|
// Nil is an Arena that always returns an error.
|
||||||
//
|
//
|
||||||
// Note: This is useful for tracking usage locations
|
// Note: This is useful for tracking usage locations
|
||||||
|
|
|
||||||
59
mem/mem_unix.go
Normal file
59
mem/mem_unix.go
Normal file
|
|
@ -0,0 +1,59 @@
|
||||||
|
//go:build unix
|
||||||
|
|
||||||
|
package mem
|
||||||
|
|
||||||
|
import (
|
||||||
|
"syscall"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
PERM_NONE = syscall.PROT_NONE
|
||||||
|
PERM_READ = syscall.PROT_READ
|
||||||
|
PERM_WRITE = syscall.PROT_WRITE
|
||||||
|
PERM_EXECUTE = syscall.PROT_EXEC
|
||||||
|
)
|
||||||
|
|
||||||
|
func Reserve(total_address_space uintptr) ([]byte, error) {
|
||||||
|
data, err := syscall.Mmap(-1, 0, int(total_address_space), syscall.PROT_NONE, syscall.MAP_PRIVATE|syscall.MAP_ANON)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return data, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func Unreserve(reserved []byte) error {
|
||||||
|
return syscall.Munmap(reserved)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Commit(reserved []byte, perms int) error {
|
||||||
|
return syscall.Mprotect(reserved, perms)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Decommit(committed []byte) (err error) {
|
||||||
|
err = syscall.Mprotect(committed, syscall.PROT_NONE)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return madvise(committed, syscall.MADV_DONTNEED)
|
||||||
|
}
|
||||||
|
|
||||||
|
var _zero uintptr
|
||||||
|
|
||||||
|
func madvise(b []byte, advice int) (err error) {
|
||||||
|
var _p0 unsafe.Pointer
|
||||||
|
if len(b) > 0 {
|
||||||
|
_p0 = unsafe.Pointer(&b[0])
|
||||||
|
} else {
|
||||||
|
_p0 = unsafe.Pointer(&_zero)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, _, e := syscall.Syscall(syscall.SYS_MADVISE, uintptr(_p0), uintptr(len(b)), uintptr(advice))
|
||||||
|
if e != 0 {
|
||||||
|
err = syscall.Errno(e)
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue