···374374 tailDone <- e.tailStep(ctx, wfLogger, mkExecResp.ID, idx)
375375 }()
376376377377+ // prefer tailDone over ctx.Done() to avoid false positives
378378+ // when step completes at the same moment the deadline expires
377379 select {
378380 case <-tailDone:
379379-380380- case <-ctx.Done():
381381- // cleanup will be handled by DestroyWorkflow, since
382382- // Docker doesn't provide an API to kill an exec run
383383- // (sure, we could grab the PID and kill it ourselves,
384384- // but that's wasted effort)
385385- e.l.Warn("step timed out", "step", step.Name())
381381+ default:
382382+ select {
383383+ case <-tailDone:
386384387387- <-tailDone
385385+ case <-ctx.Done():
386386+ // cleanup will be handled by DestroyWorkflow, since
387387+ // Docker doesn't provide an API to kill an exec run
388388+ // (sure, we could grab the PID and kill it ourselves,
389389+ // but that's wasted effort)
390390+ e.l.Warn("step timed out", "step", step.Name())
388391389389- return engine.ErrTimedOut
390390- }
392392+ <-tailDone
391393392392- select {
393393- case <-ctx.Done():
394394- return ctx.Err()
395395- default:
394394+ return engine.ErrTimedOut
395395+ }
396396 }
397397398398 execInspectResp, err := e.docker.ContainerExecInspect(ctx, mkExecResp.ID)