Skip to content

Commit 9e238fc

Browse files
Reduce the number of simultaneous compiles of same prog/user
Wait with a timeout (~17s or 34s) before going ahead anyway.
1 parent a49b610 commit 9e238fc

File tree

1 file changed

+98
-3
lines changed

1 file changed

+98
-3
lines changed

gorun.go

Lines changed: 98 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,9 @@ func main() {
150150
err = s.embedEmbedded()
151151
} else {
152152
err = s.runScript()
153+
if err != nil {
154+
err = errors.New("running script failed to find compiled binary: " + err.Error())
155+
}
153156
}
154157
if err != nil {
155158
_, _ = fmt.Fprintln(os.Stderr, "error: "+err.Error())
@@ -425,6 +428,12 @@ func (s *Script) compile() (err error) {
425428
if err != nil {
426429
return
427430
}
431+
s.waitForActiveBuilds()
432+
// maybe it was built while we were waiting?
433+
outOfDate, err := s.targetOutOfDate()
434+
if err == nil && !outOfDate {
435+
return
436+
}
428437

429438
// use the default environment before adding our overrides, this allows GOPRIVATE etc. to be used in the build
430439
var env []string
@@ -474,6 +483,83 @@ func (s *Script) compile() (err error) {
474483
return
475484
}
476485

486+
// isProcessRunning checks if a process with the given PID is still running
487+
func isProcessRunning(pid int) bool {
488+
process, err := os.FindProcess(pid)
489+
if err != nil {
490+
return false
491+
}
492+
// On Unix, FindProcess always succeeds, so we need to send signal 0 to check if process exists
493+
err = process.Signal(syscall.Signal(0))
494+
return err == nil
495+
}
496+
497+
// hasActiveBuild checks if there are any active compilation processes for this script
498+
// by looking for PID directories in s.tmpDir and checking if those processes are still running
499+
// even if they are, we will continue if we are the lowest PID
500+
func (s *Script) hasActiveBuild() bool {
501+
entries, err := os.ReadDir(s.tmpDir)
502+
if err != nil {
503+
return false // If we can't read the directory, assume no active builds
504+
}
505+
506+
currentPID := os.Getpid()
507+
for _, entry := range entries {
508+
if entry.IsDir() {
509+
// Check if the directory name is numeric (PID)
510+
if pid, err := strconv.Atoi(entry.Name()); err == nil {
511+
// Skip our own PID
512+
if pid != currentPID && isProcessRunning(pid) {
513+
if pid < currentPID {
514+
if s.debug {
515+
_, _ = fmt.Fprintf(os.Stdout, "[%v] Detected lower active build process with PID %d\n", currentPID, pid)
516+
}
517+
return true
518+
}
519+
}
520+
}
521+
}
522+
}
523+
return false
524+
}
525+
526+
// waitForActiveBuilds waits for any lower PID active builds to complete before proceeding
527+
// Returns true if it's safe to proceed, false if timeout occurred.
528+
func (s *Script) waitForActiveBuilds() bool {
529+
maxRetries := 12 // Waits max approx 17s at 12 iterations. But may be called twice, so 34s total.
530+
waitTime := 100 * time.Millisecond
531+
maxWaitTime := 2 * time.Second
532+
533+
for i := 0; i < maxRetries; i++ {
534+
if !s.hasActiveBuild() {
535+
return true
536+
}
537+
538+
if s.debug {
539+
_, _ = fmt.Fprintf(os.Stderr, "Active build detected, waiting %v (attempt %d/%d)\n", waitTime, i+1, maxRetries)
540+
}
541+
542+
time.Sleep(waitTime)
543+
outOfDate, err := s.targetOutOfDate()
544+
if err == nil && !outOfDate {
545+
_, _ = fmt.Fprintf(os.Stderr, "Active build detected, escaping %v (attempt %d/%d)\n", waitTime, i+1, maxRetries)
546+
return false
547+
}
548+
549+
// Exponential backoff, but cap at maxWaitTime
550+
waitTime *= 2
551+
if waitTime > maxWaitTime {
552+
waitTime = maxWaitTime
553+
}
554+
}
555+
556+
// Timeout occurred, but we'll proceed anyway
557+
if s.debug {
558+
_, _ = fmt.Fprintf(os.Stderr, "Timeout waiting for active builds, proceeding anyway\n")
559+
}
560+
return false
561+
}
562+
477563
func touchFile(file string) (err error) {
478564
_, err = os.Stat(file)
479565
if os.IsNotExist(err) {
@@ -619,14 +705,23 @@ func (s *Script) runScript() (err error) {
619705
var outOfDate bool
620706
for i := 0; i < 5; i++ {
621707
outOfDate, err = s.targetOutOfDate()
622-
623708
if err != nil {
624709
return // can't find the source file - let's bail
625710
}
711+
626712
if outOfDate {
627-
err = s.compile() // can't compile, well, it could be inconsistent source, let's bail
713+
// Wait for any active builds to complete before starting our own
714+
s.waitForActiveBuilds()
715+
// maybe it was built while we were waiting?
716+
outOfDate, err = s.targetOutOfDate()
628717
if err != nil {
629-
return
718+
return // can't find the source file - let's bail
719+
}
720+
if outOfDate {
721+
err = s.compile() // can't compile, well, it could be inconsistent source, let's bail
722+
if err != nil {
723+
return
724+
}
630725
}
631726
}
632727
err = s.run()

0 commit comments

Comments
 (0)