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&COPY != 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  }