@@ -1276,7 +1276,12 @@ func (r *runTestActor) Act(b *work.Builder, ctx context.Context, a *work.Action)
1276
1276
}
1277
1277
}
1278
1278
1279
- cmd := exec .Command (args [0 ], args [1 :]... )
1279
+ // Normally, the test will terminate itself when the timeout expires,
1280
+ // but add a last-ditch deadline to detect and stop wedged binaries.
1281
+ ctx , cancel := context .WithTimeout (ctx , testKillTimeout )
1282
+ defer cancel ()
1283
+
1284
+ cmd := exec .CommandContext (ctx , args [0 ], args [1 :]... )
1280
1285
cmd .Dir = a .Package .Dir
1281
1286
1282
1287
env := cfg .OrigEnv [:len (cfg .OrigEnv ):len (cfg .OrigEnv )]
@@ -1309,42 +1314,33 @@ func (r *runTestActor) Act(b *work.Builder, ctx context.Context, a *work.Action)
1309
1314
cmd .Env = env
1310
1315
}
1311
1316
1312
- base .StartSigHandlers ()
1313
- t0 := time .Now ()
1314
- err = cmd .Start ()
1315
-
1316
- // This is a last-ditch deadline to detect and
1317
- // stop wedged test binaries, to keep the builders
1318
- // running.
1319
- if err == nil {
1320
- tick := time .NewTimer (testKillTimeout )
1321
- done := make (chan error )
1322
- go func () {
1323
- done <- cmd .Wait ()
1324
- }()
1325
- Outer:
1326
- select {
1327
- case err = <- done :
1328
- // ok
1329
- case <- tick .C :
1330
- if base .SignalTrace != nil {
1331
- // Send a quit signal in the hope that the program will print
1332
- // a stack trace and exit. Give it five seconds before resorting
1333
- // to Kill.
1334
- cmd .Process .Signal (base .SignalTrace )
1335
- select {
1336
- case err = <- done :
1337
- fmt .Fprintf (cmd .Stdout , "*** Test killed with %v: ran too long (%v).\n " , base .SignalTrace , testKillTimeout )
1338
- break Outer
1339
- case <- time .After (5 * time .Second ):
1340
- }
1317
+ var (
1318
+ cancelKilled = false
1319
+ cancelSignaled = false
1320
+ )
1321
+ cmd .Cancel = func () error {
1322
+ if base .SignalTrace == nil {
1323
+ err := cmd .Process .Kill ()
1324
+ if err == nil {
1325
+ cancelKilled = true
1341
1326
}
1342
- cmd .Process .Kill ()
1343
- err = <- done
1344
- fmt .Fprintf (cmd .Stdout , "*** Test killed: ran too long (%v).\n " , testKillTimeout )
1327
+ return err
1345
1328
}
1346
- tick .Stop ()
1329
+
1330
+ // Send a quit signal in the hope that the program will print
1331
+ // a stack trace and exit.
1332
+ err := cmd .Process .Signal (base .SignalTrace )
1333
+ if err == nil {
1334
+ cancelSignaled = true
1335
+ }
1336
+ return err
1347
1337
}
1338
+ // Give the test five seconds to exit after the signal before resorting to Kill.
1339
+ cmd .WaitDelay = 5 * time .Second
1340
+
1341
+ base .StartSigHandlers ()
1342
+ t0 := time .Now ()
1343
+ err = cmd .Run ()
1348
1344
out := buf .Bytes ()
1349
1345
a .TestOutput = & buf
1350
1346
t := fmt .Sprintf ("%.3fs" , time .Since (t0 ).Seconds ())
@@ -1374,7 +1370,13 @@ func (r *runTestActor) Act(b *work.Builder, ctx context.Context, a *work.Action)
1374
1370
r .c .saveOutput (a )
1375
1371
} else {
1376
1372
base .SetExitStatus (1 )
1377
- if len (out ) == 0 {
1373
+ if cancelSignaled {
1374
+ fmt .Fprintf (cmd .Stdout , "*** Test killed with %v: ran too long (%v).\n " , base .SignalTrace , testKillTimeout )
1375
+ } else if cancelKilled {
1376
+ fmt .Fprintf (cmd .Stdout , "*** Test killed: ran too long (%v).\n " , testKillTimeout )
1377
+ }
1378
+ var ee * exec.ExitError
1379
+ if len (out ) == 0 || ! errors .As (err , & ee ) || ! ee .Exited () {
1378
1380
// If there was no test output, print the exit status so that the reason
1379
1381
// for failure is clear.
1380
1382
fmt .Fprintf (cmd .Stdout , "%s\n " , err )
0 commit comments