You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

mmap_windows.go 4.0KB

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