github.com/scottcagno/storage@v1.8.0/pkg/mmap/mmap_windows_amd64.go (about) 1 package mmap 2 3 import ( 4 "math" 5 "os" 6 "reflect" 7 "runtime" 8 "syscall" 9 "unsafe" 10 ) 11 12 // Mapping is a mapping of the file into the memory. 13 type Mapping struct { 14 mapping 15 // hProcess specifies the descriptor of the current process. 16 hProcess syscall.Handle 17 // hFile specifies the descriptor of the mapped file. 18 hFile syscall.Handle 19 // hMappedFile specifies the descriptor of the mapping object provided by the operation system. 20 hMappedFile syscall.Handle 21 // alignedAddress specifies the start address of the the mapped memory 22 // aligned by the memory page size. 23 alignedAddress uintptr 24 // alignedLength specifies the length of the mapped memory, in bytes, 25 // aligned by the memory page size. 26 alignedLength uintptr 27 // locked specifies whether the mapped memory is locked. 28 locked bool 29 } 30 31 // Open opens and returns a new mapping of the given file into the memory. 32 // The given file descriptor will be duplicated. It means that 33 // if the parent file will be closed the mapping will still be valid. 34 // Actual offset and length may be different than the given 35 // by the reason of aligning to the memory page size. 36 func Open(fd uintptr, offset int64, length uintptr, mode Mode, flags Flag) (*Mapping, error) { 37 38 // Using int64 (off_t) for the offset and uintptr (size_t) for the length 39 // by the reason of the compatibility. 40 if offset < 0 { 41 return nil, ErrBadOffset 42 } 43 if length > uintptr(MaxInt) { 44 return nil, ErrBadLength 45 } 46 47 m := &Mapping{} 48 prot := uint32(syscall.PAGE_READONLY) 49 access := uint32(syscall.FILE_MAP_READ) 50 switch mode { 51 case ModeReadOnly: 52 // NOOP 53 case ModeReadWrite: 54 prot = syscall.PAGE_READWRITE 55 access = syscall.FILE_MAP_WRITE 56 m.writable = true 57 case ModeWriteCopy: 58 prot = syscall.PAGE_WRITECOPY 59 access = syscall.FILE_MAP_COPY 60 m.writable = true 61 default: 62 return nil, ErrBadMode 63 } 64 if flags&FlagExecutable != 0 { 65 prot <<= 4 66 access |= syscall.FILE_MAP_EXECUTE 67 m.executable = true 68 } 69 70 // The separate file handle is needed to avoid errors on the mapped file external closing. 71 var err error 72 m.hProcess, err = syscall.GetCurrentProcess() 73 if err != nil { 74 return nil, os.NewSyscallError("GetCurrentProcess", err) 75 } 76 err = syscall.DuplicateHandle( 77 m.hProcess, syscall.Handle(fd), 78 m.hProcess, &m.hFile, 79 0, true, syscall.DUPLICATE_SAME_ACCESS, 80 ) 81 if err != nil { 82 return nil, os.NewSyscallError("DuplicateHandle", err) 83 } 84 85 // The mapping address range must be aligned by the memory page size. 86 pageSize := int64(os.Getpagesize()) 87 if pageSize < 0 { 88 return nil, os.NewSyscallError("getpagesize", syscall.EINVAL) 89 } 90 outerOffset := offset / pageSize 91 innerOffset := offset % pageSize 92 // ASSERT: uintptr is of the 64-bit length on the amd64 architecture. 93 m.alignedLength = uintptr(innerOffset) + length 94 95 maxSize := uint64(outerOffset) + uint64(m.alignedLength) 96 maxSizeHigh := uint32(maxSize >> 32) 97 maxSizeLow := uint32(maxSize & uint64(math.MaxUint32)) 98 m.hMappedFile, err = syscall.CreateFileMapping(m.hFile, nil, prot, maxSizeHigh, maxSizeLow, nil) 99 if err != nil { 100 return nil, os.NewSyscallError("CreateFileMappedFile", err) 101 } 102 fileOffset := uint64(outerOffset) 103 fileOffsetHigh := uint32(fileOffset >> 32) 104 fileOffsetLow := uint32(fileOffset & uint64(math.MaxUint32)) 105 m.alignedAddress, err = syscall.MapViewOfFile( 106 m.hMappedFile, access, 107 fileOffsetHigh, fileOffsetLow, m.alignedLength, 108 ) 109 if err != nil { 110 return nil, os.NewSyscallError("MapViewOfFile", err) 111 } 112 m.address = m.alignedAddress + uintptr(innerOffset) 113 114 // Wrapping the mapped memory by the byte slice. 115 slice := reflect.SliceHeader{} 116 slice.Data = m.address 117 slice.Len = int(length) 118 slice.Cap = slice.Len 119 m.memory = *(*[]byte)(unsafe.Pointer(&slice)) 120 121 runtime.SetFinalizer(m, (*Mapping).Close) 122 return m, nil 123 } 124 125 // Lock locks the mapped memory pages. 126 // All pages that contain a part of the mapping address range 127 // are guaranteed to be resident in RAM when the call returns successfully. 128 // The pages are guaranteed to stay in RAM until later unlocked. 129 // It may need to increase process memory limits for operation success. 130 // See working set on Windows and rlimit on Linux for details. 131 func (m *Mapping) Lock() error { 132 if m.memory == nil { 133 return ErrClosed 134 } 135 if m.locked { 136 return ErrLocked 137 } 138 if err := syscall.VirtualLock(m.alignedAddress, m.alignedLength); err != nil { 139 return os.NewSyscallError("VirtualLock", err) 140 } 141 m.locked = true 142 return nil 143 } 144 145 // Unlock unlocks the previously locked mapped memory pages. 146 func (m *Mapping) Unlock() error { 147 if m.memory == nil { 148 return ErrClosed 149 } 150 if !m.locked { 151 return ErrNotLocked 152 } 153 if err := syscall.VirtualUnlock(m.alignedAddress, m.alignedLength); err != nil { 154 return os.NewSyscallError("VirtualUnlock", err) 155 } 156 m.locked = false 157 return nil 158 } 159 160 // Sync synchronizes the mapped memory with the underlying file. 161 func (m *Mapping) Sync() error { 162 if m.memory == nil { 163 return ErrClosed 164 } 165 if !m.writable { 166 return ErrReadOnly 167 } 168 if err := syscall.FlushViewOfFile(m.alignedAddress, m.alignedLength); err != nil { 169 return os.NewSyscallError("FlushViewOfFile", err) 170 } 171 if err := syscall.FlushFileBuffers(m.hFile); err != nil { 172 return os.NewSyscallError("FlushFileBuffers", err) 173 } 174 return nil 175 } 176 177 // Close closes this mapping and frees all resources associated with it. 178 // Mapped memory will be synchronized with the underlying file and unlocked automatically. 179 // Close implements the io.Closer interface. 180 func (m *Mapping) Close() error { 181 if m.memory == nil { 182 return ErrClosed 183 } 184 var errs []error 185 if m.writable { 186 if err := m.Sync(); err != nil { 187 errs = append(errs, err) 188 } 189 } 190 if m.locked { 191 if err := m.Unlock(); err != nil { 192 errs = append(errs, err) 193 } 194 } 195 if err := syscall.UnmapViewOfFile(m.alignedAddress); err != nil { 196 errs = append(errs, os.NewSyscallError("UnmapViewOfFile", err)) 197 } 198 if err := syscall.CloseHandle(m.hMappedFile); err != nil { 199 errs = append(errs, os.NewSyscallError("CloseHandle", err)) 200 } 201 if err := syscall.CloseHandle(m.hFile); err != nil { 202 errs = append(errs, os.NewSyscallError("CloseHandle", err)) 203 } 204 *m = Mapping{} 205 runtime.SetFinalizer(m, nil) 206 if len(errs) > 0 { 207 return errs[0] 208 } 209 return nil 210 }