简体   繁体   中英

Fcntl in go doesn't work

I'm currently writing a http server, that has to run over all files inside a folder. One Request can write a file, where another Request can read the files at the same time, so i need to lock the file, that is open for writing. In the Routine, that will read the file, i just want to skip the file that is locked. To make this, i want to use locklibrarys like that from rubyist or nightlyone .

The problem is, i didnt get them to work, so i started to call the syscall.FcntlFlock() function myself and it didn't work like i had expected.
This program doesn't work in the Go Playground, cause the playground seems to run not on a unix-based system (syscall.FcntlFlock is undefined)

The code that didn't work:

func main() {
    time.Sleep(time.Second)

    file, err := os.Open("lockfiletest.lock")
    if err != nil {
        log.Printf("error opening file2: %s", err)
        return
    }
    defer file.Close()

    flockT := syscall.Flock_t{
        Type: syscall.F_WRLCK,
        Whence: io.SeekStart,
        Start: 0,
        Len: 0,
    }
    err = syscall.FcntlFlock(file.Fd(), syscall.F_SETLK, &flockT)
    if err != nil {
        log.Printf("error locking file2: %s", err)
        return
    }

    log.Println("lock2 accessed")

    time.Sleep(time.Second * 5)

    log.Println("func2 finished")

    time.Sleep(time.Second * 15)
}

the console output:

2017/10/28 00:18:12 error locking file2: bad file descriptor
Process finished with exit code 0

What am i doing wrong? Are the syscalls broken?
I also tried it in C and there it works fine.
I test this program in Go1.8.3 and Go1.9.1 on ubuntu16.04

PS: This program also have to run on windows, so only implementing a FcntlLock isn't enough.


I also thoght about using sync.RWMutex, so it is locked via a Mutex instead of a fileLock. That is not exactly what i want, cause i only want to skip the file that is locked and not wait until the mutex can be locked.

If I create the file with touch lockfiletest.lock , that is with no file contents, your program fails with your error: error locking file2: bad file descriptor .

$ rm -f lockfiletest.lock
$ touch lockfiletest.lock
$ go run lockfiletest.go
2017/10/27 21:17:27 error locking file2: bad file descriptor

I changed the file open to closely match TestFcntlFlock .

$ uname -s
Linux
~/go/src/syscall$ $ go test -v -run=TestFcntlFlock syscall_unix_test.go
=== RUN   TestFcntlFlock
--- PASS: TestFcntlFlock (0.01s)
PASS
ok      syscall 0.008s
~/go/src/syscall$ 

For example,

package main

import (
    "io"
    "log"
    "os"
    "syscall"
    "time"
)

func main() {
    time.Sleep(time.Second)

    name := "lockfiletest.lock"
    file, err := os.OpenFile(name, syscall.O_CREAT|syscall.O_RDWR|syscall.O_CLOEXEC, 0666)
    if err != nil {
        log.Printf("error opening file: %s", err)
        return
    }
    defer file.Close()

    flockT := syscall.Flock_t{
        Type:   syscall.F_WRLCK,
        Whence: io.SeekStart,
        Start:  0,
        Len:    0,
    }
    err = syscall.FcntlFlock(file.Fd(), syscall.F_SETLK, &flockT)
    if err != nil {
        log.Printf("error locking file: %s", err)
        return
    }

    log.Println("lock2 accessed")

    time.Sleep(time.Second * 5)

    log.Println("func2 finished")

    time.Sleep(time.Second * 15)
}

Output:

$ rm -f lockfiletest.lock
$ touch lockfiletest.lock
$ go run lockfiletest.go
2017/10/27 21:21:56 lock2 accessed
2017/10/27 21:22:01 func2 finished
$ rm -f lockfiletest.lock
$ go run lockfiletest.go
2017/10/27 21:22:25 lock2 accessed
2017/10/27 21:22:30 func2 finished
$ go run lockfiletest.go
2017/10/27 21:25:40 lock2 accessed
2017/10/27 21:25:45 func2 finished
$ 

您必须以WRONLY或RDWR格式打开文件,您只能以只读方式打开该文件以将其锁定,这是行不通的-您将获得一个只读文件描述符。

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM