aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJames Tucker <jftucker@gmail.com>2015-09-14 20:17:41 -0300
committerJames Tucker <jftucker@gmail.com>2015-09-16 09:28:26 -0300
commit1cb787ee7bfebef44baf98158967a37735e65790 (patch)
tree6eecccc8489246641c7e47c4e2934d0e6b1e55af
parentMerge pull request #418 from benbjohnson/revert-arm64 (diff)
downloaddedo-1cb787ee7bfebef44baf98158967a37735e65790.tar.gz
dedo-1cb787ee7bfebef44baf98158967a37735e65790.tar.xz
windows: implement file locking
-rw-r--r--bolt_windows.go62
-rw-r--r--db_test.go6
2 files changed, 59 insertions, 9 deletions
diff --git a/bolt_windows.go b/bolt_windows.go
index 8b782be..f048365 100644
--- a/bolt_windows.go
+++ b/bolt_windows.go
@@ -8,6 +8,38 @@ import (
"unsafe"
)
+// LockFileEx code derived from golang build filemutex_windows.go @ v1.5.1
+var (
+ modkernel32 = syscall.NewLazyDLL("kernel32.dll")
+ procLockFileEx = modkernel32.NewProc("LockFileEx")
+ procUnlockFileEx = modkernel32.NewProc("UnlockFileEx")
+)
+
+const (
+ // see https://msdn.microsoft.com/en-us/library/windows/desktop/aa365203(v=vs.85).aspx
+ flagLockExclusive = 2
+ flagLockFailImmediately = 1
+
+ // see https://msdn.microsoft.com/en-us/library/windows/desktop/ms681382(v=vs.85).aspx
+ errLockViolation syscall.Errno = 0x21
+)
+
+func lockFileEx(h syscall.Handle, flags, reserved, locklow, lockhigh uint32, ol *syscall.Overlapped) (err error) {
+ r, _, err := procLockFileEx.Call(uintptr(h), uintptr(flags), uintptr(reserved), uintptr(locklow), uintptr(lockhigh), uintptr(unsafe.Pointer(ol)))
+ if r == 0 {
+ return err
+ }
+ return nil
+}
+
+func unlockFileEx(h syscall.Handle, reserved, locklow, lockhigh uint32, ol *syscall.Overlapped) (err error) {
+ r, _, err := procUnlockFileEx.Call(uintptr(h), uintptr(reserved), uintptr(locklow), uintptr(lockhigh), uintptr(unsafe.Pointer(ol)), 0)
+ if r == 0 {
+ return err
+ }
+ return nil
+}
+
var odirect int
// fdatasync flushes written data to a file descriptor.
@@ -16,13 +48,37 @@ func fdatasync(db *DB) error {
}
// flock acquires an advisory lock on a file descriptor.
-func flock(f *os.File, _ bool, _ time.Duration) error {
- return nil
+func flock(f *os.File, exclusive bool, timeout time.Duration) error {
+ var t time.Time
+ for {
+ // If we're beyond our timeout then return an error.
+ // This can only occur after we've attempted a flock once.
+ if t.IsZero() {
+ t = time.Now()
+ } else if timeout > 0 && time.Since(t) > timeout {
+ return ErrTimeout
+ }
+
+ var flag uint32 = flagLockFailImmediately
+ if exclusive {
+ flag |= flagLockExclusive
+ }
+
+ err := lockFileEx(syscall.Handle(f.Fd()), flag, 0, 1, 0, &syscall.Overlapped{})
+ if err == nil {
+ return nil
+ } else if err != errLockViolation {
+ return err
+ }
+
+ // Wait for a bit and try again.
+ time.Sleep(50 * time.Millisecond)
+ }
}
// funlock releases an advisory lock on a file descriptor.
func funlock(f *os.File) error {
- return nil
+ return unlockFileEx(syscall.Handle(f.Fd()), 0, 1, 0, &syscall.Overlapped{})
}
// mmap memory maps a DB's data file.
diff --git a/db_test.go b/db_test.go
index ae21938..f5df8fe 100644
--- a/db_test.go
+++ b/db_test.go
@@ -39,9 +39,6 @@ func TestOpen(t *testing.T) {
// Ensure that opening an already open database file will timeout.
func TestOpen_Timeout(t *testing.T) {
- if runtime.GOOS == "windows" {
- t.Skip("timeout not supported on windows")
- }
if runtime.GOOS == "solaris" {
t.Skip("solaris fcntl locks don't support intra-process locking")
}
@@ -66,9 +63,6 @@ func TestOpen_Timeout(t *testing.T) {
// Ensure that opening an already open database file will wait until its closed.
func TestOpen_Wait(t *testing.T) {
- if runtime.GOOS == "windows" {
- t.Skip("timeout not supported on windows")
- }
if runtime.GOOS == "solaris" {
t.Skip("solaris fcntl locks don't support intra-process locking")
}