/*
goredo -- redo implementation on pure Go
-Copyright (C) 2020 Sergey Matveev <stargrave@stargrave.org>
+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
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.Uint64("j", 1, fmt.Sprintf("number of parallel jobs (0=inf) (%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 {
+
+ var jobs uint64
+ if v := os.Getenv(EnvJobs); v != "" {
+ var err error
+ jobs, err = strconv.ParseUint(v, 10, 64)
+ if err != nil {
+ log.Fatalln("can not parse", EnvJobs, err)
+ }
+ } else {
+ jobs = *flagJobs
+ }
+ if jobs == 0 {
// infinite jobs
return
}
+
var err error
JSR, JSW, err = os.Pipe()
if err != nil {
panic(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")
}
}
-func jsAcquire() {
+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(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")
}
+ jsTokensM.Lock()
+ jsTokens++
+ jsTokensM.Unlock()
+ trace(CJS, "acquired for %s", ctx)
}