Skip to content

Commit 7dcf406

Browse files
committed
cmd/go: delete ETXTBSY hack that is no longer needed
This hack existed because cmd/go used to install (write) and then run cmd/cgo in the same invocation, and writing and then running a program is a no-no in modern multithreaded Unix programs (see #22315). As of CL 68338, cmd/go no longer installs any programs that it then tries to use. It never did this for any program other than cgo, and CL 68338 removed that special case for cgo. Now this special case, added for #3001 long ago, can be removed too. Change-Id: I338f1f8665e9aca823e33ef7dda9d19f665e4281 Reviewed-on: https://go-review.googlesource.com/71571 Run-TryBot: Russ Cox <[email protected]> TryBot-Result: Gobot Gobot <[email protected]> Reviewed-by: Ian Lance Taylor <[email protected]>
1 parent 0836224 commit 7dcf406

File tree

1 file changed

+16
-67
lines changed

1 file changed

+16
-67
lines changed

src/cmd/go/internal/work/exec.go

+16-67
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ import (
2121
"strconv"
2222
"strings"
2323
"sync"
24-
"time"
2524

2625
"cmd/go/internal/base"
2726
"cmd/go/internal/cfg"
@@ -1015,73 +1014,23 @@ func (b *Builder) runOut(dir string, desc string, env []string, cmdargs ...inter
10151014
}
10161015
}
10171016

1018-
nbusy := 0
1019-
for {
1020-
var buf bytes.Buffer
1021-
cmd := exec.Command(cmdline[0], cmdline[1:]...)
1022-
cmd.Stdout = &buf
1023-
cmd.Stderr = &buf
1024-
cmd.Dir = dir
1025-
cmd.Env = base.MergeEnvLists(env, base.EnvForDir(cmd.Dir, os.Environ()))
1026-
err := cmd.Run()
1027-
1028-
// cmd.Run will fail on Unix if some other process has the binary
1029-
// we want to run open for writing. This can happen here because
1030-
// we build and install the cgo command and then run it.
1031-
// If another command was kicked off while we were writing the
1032-
// cgo binary, the child process for that command may be holding
1033-
// a reference to the fd, keeping us from running exec.
1034-
//
1035-
// But, you might reasonably wonder, how can this happen?
1036-
// The cgo fd, like all our fds, is close-on-exec, so that we need
1037-
// not worry about other processes inheriting the fd accidentally.
1038-
// The answer is that running a command is fork and exec.
1039-
// A child forked while the cgo fd is open inherits that fd.
1040-
// Until the child has called exec, it holds the fd open and the
1041-
// kernel will not let us run cgo. Even if the child were to close
1042-
// the fd explicitly, it would still be open from the time of the fork
1043-
// until the time of the explicit close, and the race would remain.
1044-
//
1045-
// On Unix systems, this results in ETXTBSY, which formats
1046-
// as "text file busy". Rather than hard-code specific error cases,
1047-
// we just look for that string. If this happens, sleep a little
1048-
// and try again. We let this happen three times, with increasing
1049-
// sleep lengths: 100+200+400 ms = 0.7 seconds.
1050-
//
1051-
// An alternate solution might be to split the cmd.Run into
1052-
// separate cmd.Start and cmd.Wait, and then use an RWLock
1053-
// to make sure that copyFile only executes when no cmd.Start
1054-
// call is in progress. However, cmd.Start (really syscall.forkExec)
1055-
// only guarantees that when it returns, the exec is committed to
1056-
// happen and succeed. It uses a close-on-exec file descriptor
1057-
// itself to determine this, so we know that when cmd.Start returns,
1058-
// at least one close-on-exec file descriptor has been closed.
1059-
// However, we cannot be sure that all of them have been closed,
1060-
// so the program might still encounter ETXTBSY even with such
1061-
// an RWLock. The race window would be smaller, perhaps, but not
1062-
// guaranteed to be gone.
1063-
//
1064-
// Sleeping when we observe the race seems to be the most reliable
1065-
// option we have.
1066-
//
1067-
// https://golang.org/issue/3001
1068-
//
1069-
if err != nil && nbusy < 3 && strings.Contains(err.Error(), "text file busy") {
1070-
time.Sleep(100 * time.Millisecond << uint(nbusy))
1071-
nbusy++
1072-
continue
1073-
}
1074-
1075-
// err can be something like 'exit status 1'.
1076-
// Add information about what program was running.
1077-
// Note that if buf.Bytes() is non-empty, the caller usually
1078-
// shows buf.Bytes() and does not print err at all, so the
1079-
// prefix here does not make most output any more verbose.
1080-
if err != nil {
1081-
err = errors.New(cmdline[0] + ": " + err.Error())
1082-
}
1083-
return buf.Bytes(), err
1017+
var buf bytes.Buffer
1018+
cmd := exec.Command(cmdline[0], cmdline[1:]...)
1019+
cmd.Stdout = &buf
1020+
cmd.Stderr = &buf
1021+
cmd.Dir = dir
1022+
cmd.Env = base.MergeEnvLists(env, base.EnvForDir(cmd.Dir, os.Environ()))
1023+
err := cmd.Run()
1024+
1025+
// err can be something like 'exit status 1'.
1026+
// Add information about what program was running.
1027+
// Note that if buf.Bytes() is non-empty, the caller usually
1028+
// shows buf.Bytes() and does not print err at all, so the
1029+
// prefix here does not make most output any more verbose.
1030+
if err != nil {
1031+
err = errors.New(cmdline[0] + ": " + err.Error())
10841032
}
1033+
return buf.Bytes(), err
10851034
}
10861035

10871036
// joinUnambiguously prints the slice, quoting where necessary to make the

0 commit comments

Comments
 (0)