]> Cypherpunks.ru repositories - goredo.git/blobdiff - run.go
Replace flock() with fcntl() locks
[goredo.git] / run.go
diff --git a/run.go b/run.go
index ea14641a9304bd85adbee7c988cebdcc3059242c..6366f4a0aad03c79272acf524a0cd8d8972843de 100644 (file)
--- a/run.go
+++ b/run.go
@@ -1,6 +1,6 @@
 /*
 goredo -- djb's redo implementation on pure Go
-Copyright (C) 2020-2021 Sergey Matveev <stargrave@stargrave.org>
+Copyright (C) 2020-2022 Sergey Matveev <stargrave@stargrave.org>
 
 This program is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
@@ -198,9 +198,14 @@ func runScript(tgtOrig string, errs chan error, traced bool) error {
        if err != nil {
                return TgtError{tgtOrig, err}
        }
+       flock := unix.Flock_t{
+               Type:   unix.F_WRLCK,
+               Whence: io.SeekStart,
+       }
        lockRelease := func() {
                tracef(CLock, "LOCK_UN: %s", fdLock.Name())
-               if err := unix.Flock(int(fdLock.Fd()), unix.LOCK_UN); err != nil {
+               flock.Type = unix.F_UNLCK
+               if err := unix.FcntlFlock(fdLock.Fd(), unix.F_SETLK, &flock); err != nil {
                        log.Fatalln(err)
                }
                fdLock.Close()
@@ -208,13 +213,16 @@ func runScript(tgtOrig string, errs chan error, traced bool) error {
        tracef(CLock, "LOCK_NB: %s", fdLock.Name())
 
        // Waiting for job completion, already taken by someone else
-       if err = unix.Flock(int(fdLock.Fd()), unix.LOCK_EX|unix.LOCK_NB); err != nil {
-               if uintptr(err.(syscall.Errno)) != uintptr(unix.EWOULDBLOCK) {
+       if err = unix.FcntlFlock(fdLock.Fd(), unix.F_SETLK, &flock); err != nil {
+               if uintptr(err.(syscall.Errno)) != uintptr(unix.EAGAIN) {
                        fdLock.Close()
                        return TgtError{tgtOrig, err}
                }
                Jobs.Add(1)
-               tracef(CDebug, "waiting: %s", tgtOrig)
+               if err = unix.FcntlFlock(fdLock.Fd(), unix.F_GETLK, &flock); err != nil {
+                       log.Fatalln(err)
+               }
+               tracef(CDebug, "waiting: %s (pid=%d)", tgtOrig, flock.Pid)
                if FdStatus != nil {
                        if _, err = FdStatus.Write([]byte{StatusWait}); err != nil {
                                log.Fatalln(err)
@@ -223,7 +231,7 @@ func runScript(tgtOrig string, errs chan error, traced bool) error {
                go func() {
                        defer Jobs.Done()
                        tracef(CLock, "LOCK_EX: %s", fdLock.Name())
-                       if err := unix.Flock(int(fdLock.Fd()), unix.LOCK_EX); err != nil {
+                       if err := unix.FcntlFlock(fdLock.Fd(), unix.F_SETLKW, &flock); err != nil {
                                log.Fatalln(err)
                        }
                        lockRelease()
@@ -322,7 +330,7 @@ func runScript(tgtOrig string, errs chan error, traced bool) error {
                runErr.DoFile = doFileRelPath
        }
 
-       if err = writeDep(fdDep, cwdOrig, doFileRelPath); err != nil {
+       if err = depWrite(fdDep, cwdOrig, doFileRelPath); err != nil {
                cleanup()
                return TgtError{tgtOrig, err}
        }
@@ -616,10 +624,9 @@ func runScript(tgtOrig string, errs chan error, traced bool) error {
 
                // Was $1 touched?
                if fd, err := os.Open(path.Join(cwdOrig, tgt)); err == nil {
-                       errTouched := Err1WasTouched
                        if inodePrev == nil {
                                fd.Close()
-                               runErr.Err = errTouched
+                               runErr.Err = Err1WasTouched
                                errs <- runErr
                                return
                        }
@@ -631,7 +638,7 @@ func runScript(tgtOrig string, errs chan error, traced bool) error {
                                return
                        }
                        if !inode.Equals(inodePrev) {
-                               runErr.Err = errTouched
+                               runErr.Err = Err1WasTouched
                                errs <- runErr
                                return
                        }
@@ -683,7 +690,7 @@ func runScript(tgtOrig string, errs chan error, traced bool) error {
                        fd = fdStdout
                }
 
-               // Do we need to ifcreate it, of ifchange with renaming?
+               // Do we need to ifcreate it, or ifchange with renaming?
                if fd == nil {
                        os.Remove(path.Join(cwdOrig, tgt))
                        err = ifcreate(fdDep, tgt)
@@ -707,7 +714,7 @@ func runScript(tgtOrig string, errs chan error, traced bool) error {
                                        goto Finish
                                }
                        }
-                       err = writeDep(fdDep, cwdOrig, tgt)
+                       err = depWrite(fdDep, cwdOrig, tgt)
                        if err != nil {
                                goto Finish
                        }
@@ -731,6 +738,26 @@ func runScript(tgtOrig string, errs chan error, traced bool) error {
                                goto Finish
                        }
                }
+
+               // Post-commit .rec sanitizing
+               fdDep.Close()
+               if fdDepR, err := os.Open(fdDepPath); err == nil {
+                       depInfo, err := depRead(fdDepR)
+                       fdDepR.Close()
+                       if err != nil {
+                               goto Finish
+                       }
+                       ifchangeSeen := make(map[string]struct{}, len(depInfo.ifchanges))
+                       for _, dep := range depInfo.ifchanges {
+                               ifchangeSeen[dep["Target"]] = struct{}{}
+                       }
+                       for _, dep := range depInfo.ifcreates {
+                               if _, exists := ifchangeSeen[dep]; exists {
+                                       tracef(CWarn, "simultaneous ifcreate and ifchange records: %s", tgt)
+                               }
+                       }
+               }
+
        Finish:
                runErr.Err = err
                errs <- runErr