Skip to content

Commit 0607a2e

Browse files
committed
Fix #9052
It's hard to reproduce this race condition going forward (e.g. it depends on the precise order that goals are scheduled, but https://github.com/roberth/nix-9052 current reproduces it, and this fixes that.
1 parent d904921 commit 0607a2e

4 files changed

+41
-6
lines changed

src/libstore/build/derivation-creation-and-realisation-goal.cc

+16-5
Original file line numberDiff line numberDiff line change
@@ -51,16 +51,23 @@ void DerivationCreationAndRealisationGoal::addWantedOutputs(const OutputsSpec &
5151
{
5252
/* If we already want all outputs, there is nothing to do. */
5353
auto newWanted = wantedOutputs.union_(outputs);
54-
bool needRestart = !newWanted.isSubsetOf(wantedOutputs);
54+
bool moreWanted = !newWanted.isSubsetOf(wantedOutputs);
5555
wantedOutputs = newWanted;
5656

57-
if (!needRestart)
57+
if (!moreWanted)
5858
return;
5959

6060
if (!optDrvPath)
6161
// haven't started steps where the outputs matter yet
6262
return;
63-
worker.makeDerivationGoal(*optDrvPath, outputs, buildMode);
63+
auto g = worker.makeDerivationGoal(*optDrvPath, outputs, buildMode);
64+
65+
if (!finalExitCode(upcast_goal(g)->exitCode)) {
66+
/* The original concrete goal has already finished and been
67+
destroyed, so reset this variable and double-suspend */
68+
concreteDrvGoal = g;
69+
needsRestart = true;
70+
}
6471
}
6572

6673
Goal::Co DerivationCreationAndRealisationGoal::init()
@@ -108,8 +115,12 @@ Goal::Co DerivationCreationAndRealisationGoal::init()
108115
g->preserveException = true;
109116
}
110117
optDrvPath = std::move(drvPath);
111-
addWaitee(upcast_goal(concreteDrvGoal));
112-
co_await Suspend{};
118+
119+
do {
120+
needsRestart = false;
121+
addWaitee(upcast_goal(concreteDrvGoal));
122+
co_await Suspend{};
123+
} while (needsRestart);
113124

114125
trace("outer build done");
115126

src/libstore/build/derivation-creation-and-realisation-goal.hh

+5
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,11 @@ struct DerivationCreationAndRealisationGoal : public Goal
4747
*/
4848
OutputsSpec wantedOutputs;
4949

50+
/**
51+
* Inner goal got recreated, need to wait again potentially.
52+
*/
53+
bool needsRestart = false;
54+
5055
/**
5156
* The final output paths of the build.
5257
*

src/libstore/build/goal.cc

+8-1
Original file line numberDiff line numberDiff line change
@@ -166,12 +166,19 @@ void Goal::waiteeDone(GoalPtr waitee, ExitCode result)
166166
}
167167
}
168168

169+
170+
bool Goal::finalExitCode(ExitCode result)
171+
{
172+
return result == ecSuccess || result == ecFailed || result == ecNoSubstituters || result == ecIncompleteClosure;
173+
}
174+
175+
169176
Goal::Done Goal::amDone(ExitCode result, std::optional<Error> ex)
170177
{
171178
trace("done");
172179
assert(top_co);
173180
assert(exitCode == ecBusy);
174-
assert(result == ecSuccess || result == ecFailed || result == ecNoSubstituters || result == ecIncompleteClosure);
181+
assert(finalExitCode(result));
175182
exitCode = result;
176183

177184
if (ex) {

src/libstore/build/goal.hh

+12
Original file line numberDiff line numberDiff line change
@@ -369,6 +369,18 @@ public:
369369
*/
370370
Done amDone(ExitCode result, std::optional<Error> ex = {});
371371

372+
/**
373+
* @return true just for those `ExitCode`s that are avalid argument
374+
* to `amDone`.
375+
*
376+
* Used for an assert in `amDone`, and also in some not-so-pretty
377+
* goal-retry logic.
378+
*
379+
* @todo If the latter caller is removed, then this function no
380+
* longer needs to be exposed.
381+
*/
382+
static bool finalExitCode(ExitCode result);
383+
372384
virtual void cleanup() { }
373385

374386
/**

0 commit comments

Comments
 (0)