44package main
55
66import (
7+ "context"
78 "encoding/base64"
89 "encoding/json"
910 "fmt"
@@ -21,6 +22,11 @@ import (
2122 "github.com/prometheus/client_golang/prometheus/promhttp"
2223)
2324
25+ const (
26+ // CompilationTimeout is the maximum time allowed for a compilation request
27+ CompilationTimeout = 120 * time .Second
28+ )
29+
2430type CompileRequest struct {
2531 Sources map [string ]string `json:"sources"`
2632 TopModule string `json:"topModule"`
@@ -163,6 +169,10 @@ func handleCompile(w http.ResponseWriter, r *http.Request) {
163169 return
164170 }
165171
172+ // Create context with timeout
173+ ctx , cancel := context .WithTimeout (r .Context (), CompilationTimeout )
174+ defer cancel ()
175+
166176 // Start metrics tracking
167177 startTime := time .Now ()
168178 compilationInProgress .Inc ()
@@ -243,7 +253,7 @@ func handleCompile(w http.ResponseWriter, r *http.Request) {
243253 }
244254 yosysArgs = append (yosysArgs , sourceFiles ... )
245255
246- if ! runCommand (w , flusher , workDir , "yosys" , yosysArgs ) {
256+ if ! runCommand (ctx , w , flusher , workDir , "yosys" , yosysArgs ) {
247257 return
248258 }
249259
@@ -268,13 +278,13 @@ func handleCompile(w http.ResponseWriter, r *http.Request) {
268278 "--json" , "output.json" ,
269279 }
270280
271- if ! runCommand (w , flusher , workDir , "nextpnr-ice40" , nextpnrArgs ) {
281+ if ! runCommand (ctx , w , flusher , workDir , "nextpnr-ice40" , nextpnrArgs ) {
272282 return
273283 }
274284
275285 // Run icepack
276286 icepackArgs := []string {"output.asc" , "output.bin" }
277- if ! runCommand (w , flusher , workDir , "icepack" , icepackArgs ) {
287+ if ! runCommand (ctx , w , flusher , workDir , "icepack" , icepackArgs ) {
278288 return
279289 }
280290
@@ -294,7 +304,7 @@ func handleCompile(w http.ResponseWriter, r *http.Request) {
294304 })
295305}
296306
297- func runCommand (w http.ResponseWriter , flusher http.Flusher , workDir , command string , args []string ) bool {
307+ func runCommand (ctx context. Context , w http.ResponseWriter , flusher http.Flusher , workDir , command string , args []string ) bool {
298308 startTime := time .Now ()
299309 defer func () {
300310 commandExecutionDuration .WithLabelValues (command ).Observe (time .Since (startTime ).Seconds ())
@@ -306,7 +316,7 @@ func runCommand(w http.ResponseWriter, flusher http.Flusher, workDir, command st
306316 Args : args ,
307317 })
308318
309- cmd := exec .Command ( command , args ... )
319+ cmd := exec .CommandContext ( ctx , command , args ... )
310320 cmd .Dir = workDir
311321
312322 stdout , err := cmd .StdoutPipe ()
@@ -333,7 +343,12 @@ func runCommand(w http.ResponseWriter, flusher http.Flusher, workDir, command st
333343 go streamOutput (w , flusher , stderr , "stderr" )
334344
335345 if err := cmd .Wait (); err != nil {
336- sendSSE (w , flusher , StreamMessage {Type : "error" , Message : fmt .Sprintf ("%s failed: %v" , command , err )})
346+ // Check if the error is due to context timeout
347+ if ctx .Err () == context .DeadlineExceeded {
348+ sendSSE (w , flusher , StreamMessage {Type : "error" , Message : fmt .Sprintf ("Compilation timeout: operation exceeded %v" , CompilationTimeout )})
349+ } else {
350+ sendSSE (w , flusher , StreamMessage {Type : "error" , Message : fmt .Sprintf ("%s failed: %v" , command , err )})
351+ }
337352 return false
338353 }
339354
0 commit comments