]> Cypherpunks.ru repositories - goredo.git/blob - js.go
Various refactoring
[goredo.git] / js.go
1 /*
2 goredo -- redo implementation on pure Go
3 Copyright (C) 2020 Sergey Matveev <stargrave@stargrave.org>
4
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, version 3 of the License.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program.  If not, see <http://www.gnu.org/licenses/>.
16 */
17
18 // Jobserver
19
20 package main
21
22 import (
23         "flag"
24         "log"
25         "os"
26         "os/signal"
27         "strconv"
28         "strings"
29         "sync"
30         "syscall"
31 )
32
33 const (
34         EnvJobs = "REDO_JOBS"
35         EnvJSFd = "REDO_JS_FD"
36 )
37
38 var (
39         JSR       *os.File
40         JSW       *os.File
41         jsTokens  int
42         jsTokensM sync.Mutex
43
44         flagJobs = flag.Uint64("j", 1, "number of parallel jobs (0=inf) (REDO_JOBS)")
45 )
46
47 func jsInit() {
48         jsRaw := os.Getenv(EnvJSFd)
49         if jsRaw == "NO" {
50                 // infinite jobs
51                 return
52         }
53         if jsRaw != "" {
54                 cols := strings.Split(jsRaw, ",")
55                 if len(cols) != 2 {
56                         log.Fatalln("invalid", EnvJSFd, "format")
57                 }
58                 JSR = mustParseFd(cols[0], "JSR")
59                 JSW = mustParseFd(cols[1], "JSW")
60                 jsRelease("ifchange entered")
61
62                 killed := make(chan os.Signal, 0)
63                 signal.Notify(killed, syscall.SIGTERM, syscall.SIGINT)
64                 go func() {
65                         <-killed
66                         jsTokensM.Lock()
67                         for ; jsTokens > 0; jsTokens-- {
68                                 jsReleaseNoLock()
69                         }
70                         os.Exit(1)
71                 }()
72                 return
73         }
74
75         var jobs uint64
76         if v := os.Getenv(EnvJobs); v != "" {
77                 var err error
78                 jobs, err = strconv.ParseUint(v, 10, 64)
79                 if err != nil {
80                         log.Fatalln("can not parse", EnvJobs, err)
81                 }
82         } else {
83                 jobs = *flagJobs
84         }
85         if jobs == 0 {
86                 // infinite jobs
87                 return
88         }
89
90         var err error
91         JSR, JSW, err = os.Pipe()
92         if err != nil {
93                 panic(err)
94         }
95         for i := uint64(0); i < jobs; i++ {
96                 jsRelease("initial fill")
97         }
98 }
99
100 func jsReleaseNoLock() {
101         if n, err := JSW.Write([]byte{0}); err != nil || n != 1 {
102                 panic("can not write JSW")
103         }
104 }
105
106 func jsRelease(ctx string) int {
107         if JSW == nil {
108                 return 0
109         }
110         trace(CJS, "release from %s", ctx)
111         jsTokensM.Lock()
112         jsTokens--
113         left := jsTokens
114         jsReleaseNoLock()
115         jsTokensM.Unlock()
116         return left
117 }
118
119 func jsAcquire(ctx string) {
120         if JSR == nil {
121                 return
122         }
123         trace(CJS, "acquire for %s", ctx)
124         if n, err := JSR.Read([]byte{0}); err != nil || n != 1 {
125                 panic("can not read JSR")
126         }
127         jsTokensM.Lock()
128         jsTokens++
129         jsTokensM.Unlock()
130         trace(CJS, "acquired for %s", ctx)
131 }