]> Cypherpunks.ru repositories - goredo.git/blobdiff - js.go
Download link for 1.2.0 release
[goredo.git] / js.go
diff --git a/js.go b/js.go
index ab1190e00c00759f48899030271986704d3c9ae4..701d26d39683891c0a01762758f3ce63358f1cea 100644 (file)
--- a/js.go
+++ b/js.go
@@ -1,6 +1,6 @@
 /*
-goredo -- redo implementation on pure Go
-Copyright (C) 2020 Sergey Matveev <stargrave@stargrave.org>
+goredo -- djb's redo implementation on pure Go
+Copyright (C) 2020-2021 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
@@ -19,59 +19,115 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
 package main
 
-import "os"
+import (
+       "flag"
+       "fmt"
+       "log"
+       "os"
+       "os/signal"
+       "strconv"
+       "strings"
+       "sync"
+       "syscall"
+)
+
+const (
+       EnvJobs = "REDO_JOBS"
+       EnvJSFd = "REDO_JS_FD"
+)
 
 var (
-       JSR *os.File
-       JSW *os.File
+       JSR       *os.File
+       JSW       *os.File
+       jsTokens  int
+       jsTokensM sync.Mutex
+
+       flagJobs = flag.Int("j", -1, fmt.Sprintf("number of parallel jobs (0=inf, <0=1) (%s)", EnvJobs))
 )
 
 func jsInit() {
-       jsrRaw := os.Getenv(RedoJSRFdEnv)
-       jswRaw := os.Getenv(RedoJSWFdEnv)
-       if (jsrRaw == "" && jswRaw != "") || (jsrRaw != "" && jswRaw == "") {
-               panic("both JSR and JSW must be set")
-       }
-       if jsrRaw == "NO" {
+       jsRaw := os.Getenv(EnvJSFd)
+       if jsRaw == "NO" {
                // infinite jobs
                return
        }
-       if jsrRaw != "" {
-               JSR = mustParseFd(jsrRaw, "JSR")
-               JSW = mustParseFd(jswRaw, "JSW")
-               jsRelease()
+       if jsRaw != "" {
+               cols := strings.Split(jsRaw, ",")
+               if len(cols) != 2 {
+                       log.Fatalln("invalid", EnvJSFd, "format")
+               }
+               JSR = mustParseFd(cols[0], "JSR")
+               JSW = mustParseFd(cols[1], "JSW")
+               jsRelease("ifchange entered")
+
+               killed := make(chan os.Signal, 0)
+               signal.Notify(killed, syscall.SIGTERM, syscall.SIGINT)
+               go func() {
+                       <-killed
+                       jsTokensM.Lock()
+                       for ; jsTokens > 0; jsTokens-- {
+                               jsReleaseNoLock()
+                       }
+                       os.Exit(1)
+               }()
                return
        }
-       if *JobsN == 0 {
+
+       jobs := uint64(1)
+       var err error
+       if *flagJobs == 0 {
+               jobs = 0
+       } else if *flagJobs > 0 {
+               jobs = uint64(*flagJobs)
+       } else if v := os.Getenv(EnvJobs); v != "" {
+               jobs, err = strconv.ParseUint(v, 10, 64)
+               if err != nil {
+                       log.Fatalln("can not parse", EnvJobs, err)
+               }
+       }
+       if jobs == 0 {
                // infinite jobs
                return
        }
-       var err error
+
        JSR, JSW, err = os.Pipe()
        if err != nil {
-               panic(err)
+               log.Fatalln(err)
        }
-       for i := uint(0); i < *JobsN; i++ {
-               jsRelease()
+       for i := uint64(0); i < jobs; i++ {
+               jsRelease("initial fill")
        }
 }
 
-func jsRelease() {
-       if JSW == nil {
-               return
-       }
-       trace(CJS, "release")
+func jsReleaseNoLock() {
        if n, err := JSW.Write([]byte{0}); err != nil || n != 1 {
-               panic("can not write JSW")
+               log.Fatalln("can not write JSW:", err)
+       }
+}
+
+func jsRelease(ctx string) int {
+       if JSW == nil {
+               return 0
        }
+       trace(CJS, "release from %s", ctx)
+       jsTokensM.Lock()
+       jsTokens--
+       left := jsTokens
+       jsReleaseNoLock()
+       jsTokensM.Unlock()
+       return left
 }
 
-func jsAcquire() {
+func jsAcquire(ctx string) {
        if JSR == nil {
                return
        }
-       trace(CJS, "acquire")
+       trace(CJS, "acquire for %s", ctx)
        if n, err := JSR.Read([]byte{0}); err != nil || n != 1 {
-               panic("can not read JSR")
+               log.Fatalln("can not read JSR:", err)
        }
+       jsTokensM.Lock()
+       jsTokens++
+       jsTokensM.Unlock()
+       trace(CJS, "acquired for %s", ctx)
 }