функция не возвращается на MacOs, но отлично работает на Linux и FreeBSD - PullRequest
2 голосов
/ 22 января 2020

При использовании 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?

...