]> Cypherpunks.ru repositories - goredo.git/blob - js.go
Fix workability under GNU/Linux and other systems because of different syscall
[goredo.git] / js.go
1 /*
2 goredo -- redo implementation on pure Go
3 Copyright (C) 2020-2021 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         "fmt"
25         "log"
26         "os"
27         "os/signal"
28         "strconv"
29         "strings"
30         "sync"
31         "syscall"
32 )
33
34 const (
35         EnvJobs = "REDO_JOBS"
36         EnvJSFd = "REDO_JS_FD"
37 )
38
39 var (
40         JSR       *os.File
41         JSW       *os.File
42         jsTokens  int
43         jsTokensM sync.Mutex
44
45         flagJobs = flag.Uint64("j", 1, fmt.Sprintf("number of parallel jobs (0=inf) (%s)", EnvJobs))
46 )
47
48 func jsInit() {
49         jsRaw := os.Getenv(EnvJSFd)
50         if jsRaw == "NO" {
51                 // infinite jobs
52                 return
53         }
54         if jsRaw != "" {
55                 cols := strings.Split(jsRaw, ",")
56                 if len(cols) != 2 {
57                         log.Fatalln("invalid", EnvJSFd, "format")
58                 }
59                 JSR = mustParseFd(cols[0], "JSR")
60                 JSW = mustParseFd(cols[1], "JSW")
61                 jsRelease("ifchange entered")
62
63                 killed := make(chan os.Signal, 0)
64                 signal.Notify(killed, syscall.SIGTERM, syscall.SIGINT)
65                 go func() {
66                         <-killed
67                         jsTokensM.Lock()
68                         for ; jsTokens > 0; jsTokens-- {
69                                 jsReleaseNoLock()
70                         }
71                         os.Exit(1)
72                 }()
73                 return
74         }
75
76         var jobs uint64
77         if v := os.Getenv(EnvJobs); v != "" {
78                 var err error
79                 jobs, err = strconv.ParseUint(v, 10, 64)
80                 if err != nil {
81                         log.Fatalln("can not parse", EnvJobs, err)
82                 }
83         } else {
84                 jobs = *flagJobs
85         }
86         if jobs == 0 {
87                 // infinite jobs
88                 return
89         }
90
91         var err error
92         JSR, JSW, err = os.Pipe()
93         if err != nil {
94                 panic(err)
95         }
96         for i := uint64(0); i < jobs; i++ {
97                 jsRelease("initial fill")
98         }
99 }
100
101 func jsReleaseNoLock() {
102         if n, err := JSW.Write([]byte{0}); err != nil || n != 1 {
103                 panic("can not write JSW")
104         }
105 }
106
107 func jsRelease(ctx string) int {
108         if JSW == nil {
109                 return 0
110         }
111         trace(CJS, "release from %s", ctx)
112         jsTokensM.Lock()
113         jsTokens--
114         left := jsTokens
115         jsReleaseNoLock()
116         jsTokensM.Unlock()
117         return left
118 }
119
120 func jsAcquire(ctx string) {
121         if JSR == nil {
122                 return
123         }
124         trace(CJS, "acquire for %s", ctx)
125         if n, err := JSR.Read([]byte{0}); err != nil || n != 1 {
126                 panic("can not read JSR")
127         }
128         jsTokensM.Lock()
129         jsTokens++
130         jsTokensM.Unlock()
131         trace(CJS, "acquired for %s", ctx)
132 }