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.

90 lines
2.0 KiB

  1. package bolt
  2. import (
  3. "fmt"
  4. "os"
  5. "syscall"
  6. "time"
  7. "unsafe"
  8. "golang.org/x/sys/unix"
  9. )
  10. // flock acquires an advisory lock on a file descriptor.
  11. func flock(db *DB, mode os.FileMode, exclusive bool, timeout time.Duration) error {
  12. var t time.Time
  13. for {
  14. // If we're beyond our timeout then return an error.
  15. // This can only occur after we've attempted a flock once.
  16. if t.IsZero() {
  17. t = time.Now()
  18. } else if timeout > 0 && time.Since(t) > timeout {
  19. return ErrTimeout
  20. }
  21. var lock syscall.Flock_t
  22. lock.Start = 0
  23. lock.Len = 0
  24. lock.Pid = 0
  25. lock.Whence = 0
  26. lock.Pid = 0
  27. if exclusive {
  28. lock.Type = syscall.F_WRLCK
  29. } else {
  30. lock.Type = syscall.F_RDLCK
  31. }
  32. err := syscall.FcntlFlock(db.file.Fd(), syscall.F_SETLK, &lock)
  33. if err == nil {
  34. return nil
  35. } else if err != syscall.EAGAIN {
  36. return err
  37. }
  38. // Wait for a bit and try again.
  39. time.Sleep(50 * time.Millisecond)
  40. }
  41. }
  42. // funlock releases an advisory lock on a file descriptor.
  43. func funlock(db *DB) error {
  44. var lock syscall.Flock_t
  45. lock.Start = 0
  46. lock.Len = 0
  47. lock.Type = syscall.F_UNLCK
  48. lock.Whence = 0
  49. return syscall.FcntlFlock(uintptr(db.file.Fd()), syscall.F_SETLK, &lock)
  50. }
  51. // mmap memory maps a DB's data file.
  52. func mmap(db *DB, sz int) error {
  53. // Map the data file to memory.
  54. b, err := unix.Mmap(int(db.file.Fd()), 0, sz, syscall.PROT_READ, syscall.MAP_SHARED|db.MmapFlags)
  55. if err != nil {
  56. return err
  57. }
  58. // Advise the kernel that the mmap is accessed randomly.
  59. if err := unix.Madvise(b, syscall.MADV_RANDOM); err != nil {
  60. return fmt.Errorf("madvise: %s", err)
  61. }
  62. // Save the original byte slice and convert to a byte array pointer.
  63. db.dataref = b
  64. db.data = (*[maxMapSize]byte)(unsafe.Pointer(&b[0]))
  65. db.datasz = sz
  66. return nil
  67. }
  68. // munmap unmaps a DB's data file from memory.
  69. func munmap(db *DB) error {
  70. // Ignore the unmap if we have no mapped data.
  71. if db.dataref == nil {
  72. return nil
  73. }
  74. // Unmap using the original byte slice.
  75. err := unix.Munmap(db.dataref)
  76. db.dataref = nil
  77. db.data = nil
  78. db.datasz = 0
  79. return err
  80. }