github.com/pawelgaczynski/gain@v0.4.0-alpha.0.20230821120126-41f1e60a18da/pkg/pool/virtualmem/virtualmem.go (about) 1 // Copyright (c) 2023 Paweł Gaczyński 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package virtualmem 16 17 import ( 18 "fmt" 19 "log" 20 "math" 21 "os" 22 "reflect" 23 "runtime" 24 "syscall" 25 "unsafe" 26 27 "golang.org/x/sys/unix" 28 ) 29 30 var pageSize = os.Getpagesize() 31 32 func doubleSize(size int) int { 33 return size * 2 //nolint:gomnd // skip gomnd linter since this method is self-documented 34 } 35 36 type VirtualMem struct { 37 Buf []byte 38 Size int 39 } 40 41 func (m *VirtualMem) Zeroes() { 42 for j := 0; j < m.Size; j++ { 43 m.Buf[j] = 0 44 } 45 } 46 47 func NewVirtualMem(size int) *VirtualMem { 48 vm := &VirtualMem{ 49 Size: size, 50 Buf: allocateBuffer(size), 51 } 52 runtime.SetFinalizer(vm, func(vm *VirtualMem) { 53 err := internalMunmap(uintptr(unsafe.Pointer(&vm.Buf[0])), doubleSize(vm.Size)) 54 if err != nil { 55 log.Panic(err) 56 } 57 }) 58 59 return vm 60 } 61 62 func AdjustBufferSize(size int) int { 63 adjustedSize := math.Ceil((float64(size) / float64(pageSize))) * float64(pageSize) 64 65 return int(adjustedSize) 66 } 67 68 func allocateBuffer(size int) []byte { 69 nofd := ^uintptr(0) 70 71 vaddr, err := internalMmap(0, doubleSize(size), syscall.MAP_SHARED|syscall.MAP_ANONYMOUS, nofd) 72 if err != nil { 73 log.Panic(err) 74 } 75 76 fileDescriptor, err := unix.MemfdCreate("magicbuffer", 0) 77 if err != nil { 78 log.Panic(err) 79 } 80 81 err = unix.Ftruncate(fileDescriptor, int64(size)) 82 if err != nil { 83 log.Panic(err) 84 } 85 86 fd := uintptr(fileDescriptor) 87 88 _, err = internalMmap(vaddr, size, syscall.MAP_SHARED|syscall.MAP_FIXED, fd) 89 if err != nil { 90 log.Panic(fmt.Errorf("first internal mmap failed: %w", err)) 91 } 92 93 _, err = internalMmap( 94 vaddr+uintptr(size), size, syscall.MAP_SHARED|syscall.MAP_FIXED, fd) 95 if err != nil { 96 log.Panic(fmt.Errorf("second internal mmap failed: %w", err)) 97 } 98 99 syscall.Close(fileDescriptor) 100 101 sliceHeader := reflect.SliceHeader{ 102 Data: vaddr, 103 Len: doubleSize(size), 104 Cap: doubleSize(size), 105 } 106 107 buf := *(*[]byte)(unsafe.Pointer(&sliceHeader)) //nolint:govet // it is perfectly inteded use 108 109 return buf 110 } 111 112 func internalMmap(addr uintptr, length, flags int, fd uintptr) (uintptr, error) { 113 result, _, err := syscall.Syscall6( 114 syscall.SYS_MMAP, 115 addr, 116 uintptr(length), 117 uintptr(syscall.PROT_READ|syscall.PROT_WRITE), 118 uintptr(flags), 119 fd, 120 uintptr(0), 121 ) 122 if err != 0 { 123 return 0, os.NewSyscallError("internalMMap error", err) 124 } 125 126 return result, nil 127 } 128 129 func internalMunmap(addr uintptr, length int) error { 130 _, _, err := syscall.Syscall( 131 syscall.SYS_MUNMAP, 132 addr, 133 uintptr(length), 134 0, 135 ) 136 if err != 0 { 137 return os.NewSyscallError("internalMunmap error", err) 138 } 139 140 return nil 141 }