Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions lock.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@ import (
// advisory locks. In particular, closing any other file descriptor for the
// same file will release the lock prematurely.
//
// On Windows, LockFileEx is used if possible (for exclusive locking),
// nad falls back to file creation locking otherwise.
//
// Attempting to lock a file that is already locked by the current process
// has undefined behavior.
//
Expand All @@ -50,9 +53,7 @@ func Lock(name string) (io.Closer, error) {
var lockFn = lockPortable

// Portable version not using fcntl. Doesn't handle crashes as gracefully,
// since it can leave stale lock files.
// TODO: write pid of owner to lock file and on race see if pid is
// still alive?
// since it can leave stale lock files, but handles that, too.
func lockPortable(name string) (io.Closer, error) {
absName, err := filepath.Abs(name)
if err != nil {
Expand Down
131 changes: 131 additions & 0 deletions lock_windows.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
// +build !lockfileex

/*
Copyright 2013 The Go Authors

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package lock

import (
"io"
"syscall"
)

const (
_FILE_ATTRIBUTE_TEMPORARY = 0x100
_FILE_FLAG_DELETE_ON_CLOSE = 0x04000000

//The function requests an exclusive lock. Otherwise, it requests a shared lock.
_LOCKFILE_EXCLUSIVE_LOCK = 0x00000002

//The function returns immediately if it is unable to acquire the requested lock.
//Otherwise, it waits.
_LOCKFILE_FAIL_IMMEDIATELY = 0x00000001
)

var procLockFileEx uintptr

func init() {
// sane default
lockFn = lockCreateFile

// use LockFileEx, if possible
h, err := syscall.LoadLibrary("kernel32.dll")
if err == nil {
if procLockFileEx, err = syscall.GetProcAddress(h, "LockFileEx"); err != nil {
procLockFileEx = 0
} else {
lockFn = lockFileEx
}
}

type handleUnlocker struct {
h syscall.Handle
}

func (hu *handleUnlocker) Close() error {
return syscall.Close(hu.h)
}

func lockCreateFile(name string) (io.Closer, error) {
pname, err := syscall.UTF16PtrFromString(name)
if err != nil {
return nil, err
}
// http://msdn.microsoft.com/en-us/library/windows/desktop/aa363858%28v=vs.85%29.aspx
h, err := syscall.CreateFile(pname,
syscall.GENERIC_WRITE, // open for write
0, // no sharing
nil, // don't let children inherit
syscall.CREATE_ALWAYS, // create if not exists, truncate if does
syscall.FILE_ATTRIBUTE_NORMAL|_FILE_ATTRIBUTE_TEMPORARY|_FILE_FLAG_DELETE_ON_CLOSE,
0)
if err != nil {
return nil, err
}
return &handleUnlocker{h}, nil
}

// http://msdn.microsoft.com/en-us/library/windows/desktop/aa383751%28v=vs.85%29.aspx
type overlapped struct {
internal, internalHigh uint64
offset, offsetHigh uint32
hEvent uintptr
}

func lockFileEx(name string) (io.Closer, error) {
abs, err := filepath.Abs(name)
if err != nil {
return nil, err
}
lockmu.Lock()
if locked[abs] {
lockmu.Unlock()
return nil, fmt.Errorf("file %q already locked", abs)
}
locked[abs] = true
lockmu.Unlock()

fi, err := os.Stat(name)
if err == nil && fi.Size() > 0 {
return nil, fmt.Errorf("can't Lock file %q: has non-zero size", name)
}

f, err := os.Create(name)
if err != nil {
return nil, err
}

// http://msdn.microsoft.com/en-us/library/aa365203.aspx
over := overlapped{}
r, _, e1 := syscall.Syscall6(uintptr(procLockFileEx), 6,
uintptr(syscall.Handle(f.Fd())),
uintptr(_LOCKFILE_EXCLUSIVE_LOCK|_LOCKFILE_FAIL_IMMEDIATELY),
0,
1, 0,
uintptr(unsafe.Pointer(&over)))
if r == 0 {
if e1 != 0 {
err = error(e1)
} else {
err = syscall.EINVAL
}
}
if err != nil {
f.Close()
return nil, err
}
return &unlocker{f, abs}, nil
}