gitee.com/quant1x/gox@v1.21.2/cache/mmap_windows.go (about)

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