libutil: thread-pool: ensure threads finished on error
This fixes segfaults with nix copy when there was an error processing addMultipleToStore. Running with ASAN/TSAN pointed at an use-after-free with threads from the pool accessing the graph declared in processGraph after the function was exiting and destructing the variables. It turns out that if there is an error before pool.process() is called, for example while we are still enqueueing tasks, then pool.process() isn't called and threads are still left to run. By creating the pool last we ensure that it is stopped first before running other destructors even if an exception happens early. fixes #618 Change-Id: I42a355f632aa0354df94c5d5d8cbe7ab5196c9a6
This commit is contained in:
@@ -306,9 +306,7 @@ void Store::addMultipleToStore(
|
||||
act.progress(nrDone, pathsToCopy.size(), nrRunning, nrFailed);
|
||||
};
|
||||
|
||||
ThreadPool pool{"addMultipleToStore pool"};
|
||||
|
||||
processGraph<StorePath>(pool,
|
||||
processGraph<StorePath>("addMultipleToStore pool",
|
||||
storePathsToAdd,
|
||||
|
||||
[&](const StorePath & path) {
|
||||
@@ -1137,12 +1135,10 @@ std::map<StorePath, StorePath> copyPaths(
|
||||
}
|
||||
auto pathsMap = copyPaths(srcStore, dstStore, storePaths, repair, checkSigs, substitute);
|
||||
|
||||
ThreadPool pool{"copyPaths pool"};
|
||||
|
||||
try {
|
||||
// Copy the realisation closure
|
||||
processGraph<Realisation>(
|
||||
pool, Realisation::closure(srcStore, toplevelRealisations),
|
||||
"copyPaths pool", Realisation::closure(srcStore, toplevelRealisations),
|
||||
[&](const Realisation & current) -> std::set<Realisation> {
|
||||
std::set<Realisation> children;
|
||||
for (const auto & [drvOutput, _] : current.dependentRealisations) {
|
||||
|
||||
@@ -85,7 +85,7 @@ private:
|
||||
*/
|
||||
template<typename T>
|
||||
void processGraph(
|
||||
ThreadPool & pool,
|
||||
const char *poolName,
|
||||
const std::set<T> & nodes,
|
||||
std::function<std::set<T>(const T &)> getEdges,
|
||||
std::function<void(const T &)> processNode)
|
||||
@@ -99,6 +99,11 @@ void processGraph(
|
||||
|
||||
std::function<void(const T &)> worker;
|
||||
|
||||
/* Create pool last to ensure threads are stopped before other destructors
|
||||
* run */
|
||||
ThreadPool pool{poolName};
|
||||
|
||||
|
||||
worker = [&](const T & node) {
|
||||
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user