github.com/lirm/aeron-go@v0.0.0-20230415210743-920325491dc4/aeron/util/memmap/memmap.go (about) 1 // Copyright 2016 Stanislav Liberman 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 memmap 16 17 import ( 18 "errors" 19 "fmt" 20 "log" 21 "os" 22 "sync" 23 "syscall" 24 "unsafe" 25 26 mapper "github.com/edsrzf/mmap-go" 27 "github.com/lirm/aeron-go/aeron/logging" 28 ) 29 30 // File is the wrapper around memory mapped file 31 type File struct { 32 mmap unsafe.Pointer 33 size int 34 } 35 36 var logger = logging.MustGetLogger("memmap") 37 38 // We keep this map so that we can get back the original handle from the memory address. 39 // map[unsafe.Pointer]mapper.MMap 40 var memories = sync.Map{} 41 42 // GetFileSize is a helper function to retrieve file size 43 func GetFileSize(filename string) int64 { 44 file, err := os.Open(filename) 45 if err != nil { 46 logger.Error(err) 47 return -1 48 } 49 defer file.Close() 50 51 fi, err := file.Stat() 52 if err != nil { 53 logger.Fatal(err) 54 return -1 55 } 56 57 return fi.Size() 58 } 59 60 // MapExisting is the factory method used to create memory maps for existing file. This function will fail if 61 // the file does not exist. 62 func MapExisting(filename string, offset int64, length int) (*File, error) { 63 logger.Debugf("Will try to map existing %s, %d, %d", filename, offset, length) 64 65 f, err := os.OpenFile(filename, syscall.O_RDWR, 0644) 66 if err != nil { 67 return nil, err 68 } 69 defer f.Close() 70 fi, err := f.Stat() 71 if err != nil { 72 return nil, err 73 } 74 75 size := fi.Size() 76 if size == 0 { 77 return nil, errors.New("zero size for existing file") 78 } 79 if size < 0 { 80 return nil, fmt.Errorf("mmap: stat %q returned %d", filename, size) 81 } 82 if size != int64(size) { 83 return nil, fmt.Errorf("mmap: file %q is too large", filename) 84 } 85 86 var mapSize int 87 if length != 0 { 88 mapSize = length 89 } else { 90 mapSize = int(size) 91 } 92 93 logger.Debugf("Mapping existing file: fd: %d, size: %d, offset: %d", f.Fd(), size, offset) 94 mmap, err := doMap(f, offset, mapSize) 95 logger.Debugf("Mapped existing file @%v for %d", mmap.mmap, mmap.size) 96 97 return mmap, err 98 } 99 100 // NewFile is a factory method to create a new memory mapped file with the specified capacity 101 func NewFile(filename string, offset int64, length int) (*File, error) { 102 logger.Debugf("Will try to map new %s, %d, %d", filename, offset, length) 103 104 f, err := os.Create(filename) 105 if err != nil { 106 return nil, err 107 } 108 defer f.Close() 109 110 _, err = f.Seek(int64(length-1), 0) 111 if err != nil { 112 log.Fatal(err) 113 } 114 _, err = f.Write([]byte("\000")) 115 if err != nil { 116 log.Fatal(err) 117 } 118 119 mmap, err := doMap(f, offset, length) 120 logger.Debugf("Mapped a new file @%v for %d", mmap.mmap, mmap.size) 121 122 return mmap, err 123 } 124 125 // GetMemoryPtr return the pointer to the mapped region of the file 126 func (mmap *File) GetMemoryPtr() unsafe.Pointer { 127 return mmap.mmap 128 } 129 130 // GetMemorySize return the size of the mapped region of the file 131 func (mmap *File) GetMemorySize() int { 132 return mmap.size 133 } 134 135 // Close attempts to unmap the mapped memory region 136 func (mmap *File) Close() (err error) { 137 mm, _ := memories.Load(mmap.mmap) 138 tomap := mm.(mapper.MMap) 139 return tomap.Unmap() 140 } 141 142 func doMap(f *os.File, offset int64, length int) (*File, error) { 143 144 mm, err := mapper.MapRegion(f, length, mapper.RDWR, 0, offset) 145 if err != nil { 146 return nil, err 147 } 148 149 mmap := new(File) 150 mmap.mmap = unsafe.Pointer(&mm[0]) 151 mmap.size = length 152 153 memories.Store(mmap.mmap, mm) 154 155 return mmap, err 156 }