Skip to content

Instantly share code, notes, and snippets.

@MerlinDMC
Created December 20, 2014 13:21
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save MerlinDMC/3197f4d13f8145c457e4 to your computer and use it in GitHub Desktop.
Save MerlinDMC/3197f4d13f8145c457e4 to your computer and use it in GitHub Desktop.
syscall.Flock() vs syscall.FcntlFlock()
package flock_fcntl
import (
"os"
"path/filepath"
"syscall"
"testing"
)
var (
lock_filename string
)
func init() {
lock_filename = filepath.Join(os.TempDir(), "lock_test")
}
func TestFlock(t *testing.T) {
t.Logf("operating on file: %s\n", lock_filename)
fd, err := syscall.Open(lock_filename, syscall.O_CREAT|syscall.O_RDWR|syscall.O_CLOEXEC, 0666)
if err != nil {
t.Fatalf("error opening file: %s\n", err)
}
defer syscall.Unlink(lock_filename)
defer syscall.Close(fd)
// this one should succeed as we're not holding a lock already
err = syscall.Flock(fd, syscall.LOCK_EX|syscall.LOCK_NB)
if err != nil && err != syscall.EAGAIN {
t.Fatalf("error getting lock: %s\n", err)
} else if err == syscall.EAGAIN {
t.Fatalf("already locked: %s\n", err)
}
// get new fd
fdup, err := syscall.Open(lock_filename, syscall.O_CREAT|syscall.O_RDWR|syscall.O_CLOEXEC, 0666)
if err != nil {
t.Fatalf("error opening file: %s\n", err)
}
defer syscall.Close(fd)
// this should fail as we're holding a lock
err = syscall.Flock(fdup, syscall.LOCK_EX|syscall.LOCK_NB)
if err == nil {
t.Fatalf("getting lock for already locked resource.\n")
} else if err == syscall.EAGAIN {
t.Logf("already locked: %s\n", err)
}
}
func TestFcntlFlock(t *testing.T) {
t.Logf("operating on file: %s\n", lock_filename)
fd, err := syscall.Open(lock_filename, syscall.O_CREAT|syscall.O_RDWR|syscall.O_CLOEXEC, 0666)
if err != nil {
t.Fatalf("error opening file: %s\n", err)
}
defer syscall.Unlink(lock_filename)
defer syscall.Close(fd)
lk := syscall.Flock_t{
Type: syscall.F_WRLCK,
Start: 0,
Len: 0,
Whence: 0,
}
// this one should succeed as we're not holding a lock already
err = syscall.FcntlFlock(uintptr(fd), syscall.F_SETLK, &lk)
if err != nil && err != syscall.EAGAIN {
t.Fatalf("error getting lock: %s\n", err)
} else if err == syscall.EAGAIN {
t.Fatalf("already locked: %s\n", err)
}
// get new fd
fdup, err := syscall.Open(lock_filename, syscall.O_CREAT|syscall.O_RDWR|syscall.O_CLOEXEC, 0666)
if err != nil {
t.Fatalf("error opening file: %s\n", err)
}
defer syscall.Close(fd)
// this should fail as we're holding a lock
err = syscall.FcntlFlock(uintptr(fdup), syscall.F_SETLK, &lk)
if err == nil {
t.Fatalf("getting lock for already locked resource.\n")
} else if err == syscall.EAGAIN {
t.Logf("already locked: %s\n", err)
}
}
=== RUN TestFlock
--- PASS: TestFlock (0.00s)
flock_test.go:19: operating on file: /var/folders/g8/dwjzl4_134143kw7yjzs_w5r0000gn/T/lock_test
flock_test.go:48: already locked: resource temporarily unavailable
=== RUN TestFcntlFlock
--- FAIL: TestFcntlFlock (0.00s)
flock_test.go:53: operating on file: /var/folders/g8/dwjzl4_134143kw7yjzs_w5r0000gn/T/lock_test
flock_test.go:87: getting lock for already locked resource.
FAIL
exit status 1
@akolb1
Copy link

akolb1 commented Aug 14, 2015

package main

import (
    "syscall"
    "os"
    "fmt"
    "time"
)

type lock struct {
    fd   uintptr
}

// Lock acquires exclusivity on the lock without blocking
func (l *lock) Lock() error {
    var lock syscall.Flock_t
    lock.Start = 0
    lock.Len = 0
    lock.Type = syscall.F_WRLCK
    lock.Whence = 0
    lock.Pid = 0
    return syscall.FcntlFlock(uintptr(l.fd), syscall.F_SETLK, &lock)
}

// Unlock unlocks the lock
func (l *lock) Unlock() error {
    var lock syscall.Flock_t
    lock.Start = 0
    lock.Len = 0
    lock.Type = syscall.F_UNLCK
    lock.Whence = 0
    return syscall.FcntlFlock(uintptr(l.fd), syscall.F_SETLK, &lock)
}

func main() {
    f, err := os.OpenFile("/tmp/mylock", os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0666)
    if err != nil {
        fmt.Println(err)
        os.Exit(1)
    }
    l := lock{fd: f.Fd()}
    err = l.Lock()
    if err != nil {
        fmt.Println("Can not grab lock: ", err)
        os.Exit(1)
    }
    fmt.Println("Sleeping on a lock")
    time.Sleep(60 * time.Second)
    l.Unlock()
    fmt.Println("Done")
}

In one window (on Solaris):

./locks
... sleeps...

In another window:

./locks
Can not grab lock: resource temporarily unavailable

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment