github.com/zuoyebang/bitalosdb@v1.1.1-0.20240516111551-79a8c4d8ce20/internal/mmap/mmap_windows.go (about) 1 // Copyright 2021 The Bitalosdb author(hustxrb@163.com) and other contributors. 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 mmap 16 17 import ( 18 "errors" 19 "os" 20 "sync" 21 22 "golang.org/x/sys/windows" 23 ) 24 25 // mmap on Windows is a two-step process. 26 // First, we call CreateFileMapping to get a handle. 27 // Then, we call MapviewToFile to get an actual pointer into memory. 28 // Because we want to emulate a POSIX-style mmap, we don't want to expose 29 // the handle -- only the pointer. We also want to return only a byte slice, 30 // not a struct, so it's convenient to manipulate. 31 32 // We keep this map so that we can get back the original handle from the memory address. 33 34 type addrinfo struct { 35 file windows.Handle 36 mapview windows.Handle 37 writable bool 38 } 39 40 var handleLock sync.Mutex 41 var handleMap = map[uintptr]*addrinfo{} 42 43 func mmapfd(len int, prot, flags, hfile uintptr, off int64) ([]byte, error) { 44 flProtect := uint32(windows.PAGE_READONLY) 45 dwDesiredAccess := uint32(windows.FILE_MAP_READ) 46 writable := false 47 switch { 48 case prot© != 0: 49 flProtect = windows.PAGE_WRITECOPY 50 dwDesiredAccess = windows.FILE_MAP_COPY 51 writable = true 52 case prot&RDWR != 0: 53 flProtect = windows.PAGE_READWRITE 54 dwDesiredAccess = windows.FILE_MAP_WRITE 55 writable = true 56 } 57 if prot&EXEC != 0 { 58 flProtect <<= 4 59 dwDesiredAccess |= windows.FILE_MAP_EXECUTE 60 } 61 62 // The maximum size is the area of the file, starting from 0, 63 // that we wish to allow to be mappable. It is the sum of 64 // the length the user requested, plus the offset where that length 65 // is starting from. This does not map the data into memory. 66 maxSizeHigh := uint32((off + int64(len)) >> 32) 67 maxSizeLow := uint32((off + int64(len)) & 0xFFFFFFFF) 68 // TODO: Do we need to set some security attributes? It might help portability. 69 h, errno := windows.CreateFileMapping(windows.Handle(hfile), nil, flProtect, maxSizeHigh, maxSizeLow, nil) 70 if h == 0 { 71 return nil, os.NewSyscallError("CreateFileMapping", errno) 72 } 73 74 // Actually map a view of the data into memory. The view's size 75 // is the length the user requested. 76 fileOffsetHigh := uint32(off >> 32) 77 fileOffsetLow := uint32(off & 0xFFFFFFFF) 78 addr, errno := windows.MapViewOfFile(h, dwDesiredAccess, fileOffsetHigh, fileOffsetLow, uintptr(len)) 79 if addr == 0 { 80 windows.CloseHandle(windows.Handle(h)) 81 return nil, os.NewSyscallError("MapViewOfFile", errno) 82 } 83 handleLock.Lock() 84 handleMap[addr] = &addrinfo{ 85 file: windows.Handle(hfile), 86 mapview: h, 87 writable: writable, 88 } 89 handleLock.Unlock() 90 91 m := MMap{} 92 dh := m.header() 93 dh.Data = addr 94 dh.Len = len 95 dh.Cap = dh.Len 96 97 return m, nil 98 } 99 100 func (m Mbuf) flush() error { 101 addr, len := m.addrLen() 102 errno := windows.FlushViewOfFile(addr, len) 103 if errno != nil { 104 return os.NewSyscallError("FlushViewOfFile", errno) 105 } 106 107 handleLock.Lock() 108 defer handleLock.Unlock() 109 handle, ok := handleMap[addr] 110 if !ok { 111 // should be impossible; we would've errored above 112 return errors.New("unknown base address") 113 } 114 115 if handle.writable && handle.file != windows.Handle(^uintptr(0)) { 116 if err := windows.FlushFileBuffers(handle.file); err != nil { 117 return os.NewSyscallError("FlushFileBuffers", err) 118 } 119 } 120 121 return nil 122 } 123 124 func (m Mbuf) lock() error { 125 addr, len := m.addrLen() 126 errno := windows.VirtualLock(addr, len) 127 return os.NewSyscallError("VirtualLock", errno) 128 } 129 130 func (m Mbuf) unlock() error { 131 addr, len := m.addrLen() 132 errno := windows.VirtualUnlock(addr, len) 133 return os.NewSyscallError("VirtualUnlock", errno) 134 } 135 136 func (m Mbuf) unmap() error { 137 err := m.flush() 138 if err != nil { 139 return err 140 } 141 142 addr := m.header().Data 143 // Lock the UnmapViewOfFile along with the handleMap deletion. 144 // As soon as we unmap the view, the OS is free to give the 145 // same addr to another new map. We don't want another goroutine 146 // to insert and remove the same addr into handleMap while 147 // we're trying to remove our old addr/handle pair. 148 handleLock.Lock() 149 defer handleLock.Unlock() 150 err = windows.UnmapViewOfFile(addr) 151 if err != nil { 152 return err 153 } 154 155 handle, ok := handleMap[addr] 156 if !ok { 157 // should be impossible; we would've errored above 158 return errors.New("unknown base address") 159 } 160 delete(handleMap, addr) 161 162 e := windows.CloseHandle(windows.Handle(handle.mapview)) 163 return os.NewSyscallError("CloseHandle", e) 164 }