bolt_windows.go 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145
  1. package bolt
  2. import (
  3. "fmt"
  4. "os"
  5. "syscall"
  6. "time"
  7. "unsafe"
  8. )
  9. // LockFileEx code derived from golang build filemutex_windows.go @ v1.5.1
  10. var (
  11. modkernel32 = syscall.NewLazyDLL("kernel32.dll")
  12. procLockFileEx = modkernel32.NewProc("LockFileEx")
  13. procUnlockFileEx = modkernel32.NewProc("UnlockFileEx")
  14. )
  15. const (
  16. lockExt = ".lock"
  17. // see https://msdn.microsoft.com/en-us/library/windows/desktop/aa365203(v=vs.85).aspx
  18. flagLockExclusive = 2
  19. flagLockFailImmediately = 1
  20. // see https://msdn.microsoft.com/en-us/library/windows/desktop/ms681382(v=vs.85).aspx
  21. errLockViolation syscall.Errno = 0x21
  22. )
  23. func lockFileEx(h syscall.Handle, flags, reserved, locklow, lockhigh uint32, ol *syscall.Overlapped) (err error) {
  24. r, _, err := procLockFileEx.Call(uintptr(h), uintptr(flags), uintptr(reserved), uintptr(locklow), uintptr(lockhigh), uintptr(unsafe.Pointer(ol)))
  25. if r == 0 {
  26. return err
  27. }
  28. return nil
  29. }
  30. func unlockFileEx(h syscall.Handle, reserved, locklow, lockhigh uint32, ol *syscall.Overlapped) (err error) {
  31. r, _, err := procUnlockFileEx.Call(uintptr(h), uintptr(reserved), uintptr(locklow), uintptr(lockhigh), uintptr(unsafe.Pointer(ol)), 0)
  32. if r == 0 {
  33. return err
  34. }
  35. return nil
  36. }
  37. // fdatasync flushes written data to a file descriptor.
  38. func fdatasync(db *DB) error {
  39. return db.file.Sync()
  40. }
  41. // flock acquires an advisory lock on a file descriptor.
  42. func flock(db *DB, mode os.FileMode, exclusive bool, timeout time.Duration) error {
  43. // Create a separate lock file on windows because a process
  44. // cannot share an exclusive lock on the same file. This is
  45. // needed during Tx.WriteTo().
  46. f, err := os.OpenFile(db.path+lockExt, os.O_CREATE, mode)
  47. if err != nil {
  48. return err
  49. }
  50. db.lockfile = f
  51. var t time.Time
  52. if timeout != 0 {
  53. t = time.Now()
  54. }
  55. fd := f.Fd()
  56. var flag uint32 = flagLockFailImmediately
  57. if exclusive {
  58. flag |= flagLockExclusive
  59. }
  60. for {
  61. // Attempt to obtain an exclusive lock.
  62. err := lockFileEx(syscall.Handle(fd), flag, 0, 1, 0, &syscall.Overlapped{})
  63. if err == nil {
  64. return nil
  65. } else if err != errLockViolation {
  66. return err
  67. }
  68. // If we timed oumercit then return an error.
  69. if timeout != 0 && time.Since(t) > timeout-flockRetryTimeout {
  70. return ErrTimeout
  71. }
  72. // Wait for a bit and try again.
  73. time.Sleep(flockRetryTimeout)
  74. }
  75. }
  76. // funlock releases an advisory lock on a file descriptor.
  77. func funlock(db *DB) error {
  78. err := unlockFileEx(syscall.Handle(db.lockfile.Fd()), 0, 1, 0, &syscall.Overlapped{})
  79. db.lockfile.Close()
  80. os.Remove(db.path + lockExt)
  81. return err
  82. }
  83. // mmap memory maps a DB's data file.
  84. // Based on: https://github.com/edsrzf/mmap-go
  85. func mmap(db *DB, sz int) error {
  86. if !db.readOnly {
  87. // Truncate the database to the size of the mmap.
  88. if err := db.file.Truncate(int64(sz)); err != nil {
  89. return fmt.Errorf("truncate: %s", err)
  90. }
  91. }
  92. // Open a file mapping handle.
  93. sizelo := uint32(sz >> 32)
  94. sizehi := uint32(sz) & 0xffffffff
  95. h, errno := syscall.CreateFileMapping(syscall.Handle(db.file.Fd()), nil, syscall.PAGE_READONLY, sizelo, sizehi, nil)
  96. if h == 0 {
  97. return os.NewSyscallError("CreateFileMapping", errno)
  98. }
  99. // Create the memory map.
  100. addr, errno := syscall.MapViewOfFile(h, syscall.FILE_MAP_READ, 0, 0, uintptr(sz))
  101. if addr == 0 {
  102. return os.NewSyscallError("MapViewOfFile", errno)
  103. }
  104. // Close mapping handle.
  105. if err := syscall.CloseHandle(syscall.Handle(h)); err != nil {
  106. return os.NewSyscallError("CloseHandle", err)
  107. }
  108. // Convert to a byte array.
  109. db.data = ((*[maxMapSize]byte)(unsafe.Pointer(addr)))
  110. db.datasz = sz
  111. return nil
  112. }
  113. // munmap unmaps a pointer from a file.
  114. // Based on: https://github.com/edsrzf/mmap-go
  115. func munmap(db *DB) error {
  116. if db.data == nil {
  117. return nil
  118. }
  119. addr := (uintptr)(unsafe.Pointer(&db.data[0]))
  120. if err := syscall.UnmapViewOfFile(addr); err != nil {
  121. return os.NewSyscallError("UnmapViewOfFile", err)
  122. }
  123. return nil
  124. }