При использовании go1.13.6 в macOs Mojave 10.14.6 функция flockCmdMain
из следующей программы не возвращается в случае истечения времени ожидания. Я тестировал ту же версию go на Linux и FreeBSD, и у меня нет этой проблемы.
package main
import (
"flag"
"fmt"
"log"
"os"
"os/exec"
"time"
"golang.org/x/sys/unix"
)
type FlockOptions struct {
File string // file or dir to place the lock on
Command string // shell command to run
TimeoutSeconds int
}
func main() {
var opt = FlockOptions{}
flag.StringVar(&opt.File, "f", "", "[REQUIRED] File or directory to lock, must already exist")
flag.StringVar(&opt.Command, "c", "", "Pass a single command to the shell with -c")
flag.IntVar(&opt.TimeoutSeconds, "w", 0, "Time out if the lock cannot be acquired within the specified number of seconds.(negative or 0, the default, means no timeout)")
flag.Parse()
if opt.File == "" {
fmt.Fprintf(os.Stderr, "Supply a file or directory to lock with -f <file/dir>\n\n")
flag.Usage()
os.Exit(1)
}
exitCode, err := flockCmdMain(opt)
if err != nil {
log.Fatal(err)
}
os.Exit(exitCode)
}
func flockCmdMain(opts FlockOptions) (int, error) {
f, err := os.Open(opts.File)
if err != nil {
return -1, err
}
defer f.Close()
fd := int(f.Fd())
select {
case <-timeoutAfter(time.Duration(opts.TimeoutSeconds) * time.Second):
fmt.Println("DEBUG: the timeout channel has fired")
return -1, fmt.Errorf("timed out")
case err = <-lock(fd):
if err != nil {
return -1, err
}
defer unix.Flock(fd, unix.LOCK_UN)
cmd := exec.Command("bash", []string{"-c", opts.Command}...)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
cmd.Run() // err discarded
return cmd.ProcessState.ExitCode(), nil
}
}
func lock(fd int) <-chan error {
flockOpts := unix.LOCK_EX
lockErr := make(chan error)
go func() {
lockErr <- unix.Flock(fd, flockOpts)
}()
return lockErr
}
// time out after d, never times out if d is zero or negative
func timeoutAfter(d time.Duration) <-chan time.Time {
switch {
case d <= 0:
return make(chan time.Time)
default:
return time.NewTimer(d).C
}
}
Чтобы вызвать проблему, скомпилируйте приведенный выше код в исполняемый файл с именем flock
, затем запустите следующую команду в одном терминале, который содержит эксклюзивный flock
в каталоге /tmp
в течение 123 секунд.
./flock -f /tmp -c 'sleep 123'
В другом терминале выполните следующую команду, которая также пытается разместить эксклюзив flock
на /tmp
, и вы увидите, что тайм-аут срабатывания канала, а затем функция flockCmdMain
зависает, не возвращаясь к main
.
./flock -f /tmp -w 1 -c 'echo locked'
Почему функция flockCmdMain
не возвращается на MacOs?