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.1KB

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