/* goredo -- djb's redo implementation on pure Go Copyright (C) 2020-2021 Sergey Matveev 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 the Free Software Foundation, version 3 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ // Jobserver package main 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 jsTokens int jsTokensM sync.Mutex flagJobs = flag.Int("j", -1, fmt.Sprintf("number of parallel jobs (0=inf, <0=1) (%s)", EnvJobs)) ) func jsInit() { jsRaw := os.Getenv(EnvJSFd) if jsRaw == "NO" { // infinite jobs return } 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 } 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 } JSR, JSW, err = os.Pipe() if err != nil { log.Fatalln(err) } for i := uint64(0); i < jobs; i++ { jsRelease("initial fill") } } func jsReleaseNoLock() { if n, err := JSW.Write([]byte{0}); err != nil || n != 1 { 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(ctx string) { if JSR == nil { return } trace(CJS, "acquire for %s", ctx) if n, err := JSR.Read([]byte{0}); err != nil || n != 1 { log.Fatalln("can not read JSR:", err) } jsTokensM.Lock() jsTokens++ jsTokensM.Unlock() trace(CJS, "acquired for %s", ctx) }