Merge pull request #11618 from NixOS/ignoreException-interrupt

Split ignoreException for destructors vs interrupt-safe
This commit is contained in:
Eelco Dolstra 2024-10-01 17:10:39 +02:00 committed by GitHub
commit 3e7b42dd89
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
27 changed files with 164 additions and 125 deletions

View file

@ -101,7 +101,7 @@ struct AttrDb
state->txn->commit(); state->txn->commit();
state->txn.reset(); state->txn.reset();
} catch (...) { } catch (...) {
ignoreException(); ignoreExceptionInDestructor();
} }
} }
@ -112,7 +112,7 @@ struct AttrDb
try { try {
return fun(); return fun();
} catch (SQLiteError &) { } catch (SQLiteError &) {
ignoreException(); ignoreExceptionExceptInterrupt();
failed = true; failed = true;
return 0; return 0;
} }
@ -351,7 +351,7 @@ static std::shared_ptr<AttrDb> makeAttrDb(
try { try {
return std::make_shared<AttrDb>(cfg, fingerprint, symbols); return std::make_shared<AttrDb>(cfg, fingerprint, symbols);
} catch (SQLiteError &) { } catch (SQLiteError &) {
ignoreException(); ignoreExceptionExceptInterrupt();
return nullptr; return nullptr;
} }
} }

View file

@ -416,7 +416,7 @@ RunPager::~RunPager()
} }
#endif #endif
} catch (...) { } catch (...) {
ignoreException(); ignoreExceptionInDestructor();
} }
} }

View file

@ -90,7 +90,7 @@ DerivationGoal::~DerivationGoal()
{ {
/* Careful: we should never ever throw an exception from a /* Careful: we should never ever throw an exception from a
destructor. */ destructor. */
try { closeLogFile(); } catch (...) { ignoreException(); } try { closeLogFile(); } catch (...) { ignoreExceptionInDestructor(); }
} }
@ -814,7 +814,7 @@ void replaceValidPath(const Path & storePath, const Path & tmpPath)
// attempt to recover // attempt to recover
movePath(oldPath, storePath); movePath(oldPath, storePath);
} catch (...) { } catch (...) {
ignoreException(); ignoreExceptionExceptInterrupt();
} }
throw; throw;
} }

View file

@ -294,7 +294,7 @@ void PathSubstitutionGoal::cleanup()
outPipe.close(); outPipe.close();
} catch (...) { } catch (...) {
ignoreException(); ignoreExceptionInDestructor();
} }
} }

View file

@ -139,7 +139,7 @@ struct curlFileTransfer : public FileTransfer
if (!done) if (!done)
fail(FileTransferError(Interrupted, {}, "download of '%s' was interrupted", request.uri)); fail(FileTransferError(Interrupted, {}, "download of '%s' was interrupted", request.uri));
} catch (...) { } catch (...) {
ignoreException(); ignoreExceptionInDestructor();
} }
} }

View file

@ -958,8 +958,8 @@ void LocalStore::autoGC(bool sync)
} catch (...) { } catch (...) {
// FIXME: we could propagate the exception to the // FIXME: we could propagate the exception to the
// future, but we don't really care. // future, but we don't really care. (what??)
ignoreException(); ignoreExceptionInDestructor();
} }
}).detach(); }).detach();

View file

@ -522,7 +522,7 @@ LocalStore::~LocalStore()
unlink(fnTempRoots.c_str()); unlink(fnTempRoots.c_str());
} }
} catch (...) { } catch (...) {
ignoreException(); ignoreExceptionInDestructor();
} }
} }
@ -1096,108 +1096,114 @@ void LocalStore::addToStore(const ValidPathInfo & info, Source & source,
if (checkSigs && pathInfoIsUntrusted(info)) if (checkSigs && pathInfoIsUntrusted(info))
throw Error("cannot add path '%s' because it lacks a signature by a trusted key", printStorePath(info.path)); throw Error("cannot add path '%s' because it lacks a signature by a trusted key", printStorePath(info.path));
/* In case we are not interested in reading the NAR: discard it. */ {
bool narRead = false; /* In case we are not interested in reading the NAR: discard it. */
Finally cleanup = [&]() { bool narRead = false;
if (!narRead) { Finally cleanup = [&]() {
NullFileSystemObjectSink sink; if (!narRead) {
try { NullFileSystemObjectSink sink;
parseDump(sink, source); try {
} catch (...) { parseDump(sink, source);
ignoreException(); } catch (...) {
// TODO: should Interrupted be handled here?
ignoreExceptionInDestructor();
}
} }
} };
};
addTempRoot(info.path); addTempRoot(info.path);
if (repair || !isValidPath(info.path)) {
PathLocks outputLock;
auto realPath = Store::toRealPath(info.path);
/* Lock the output path. But don't lock if we're being called
from a build hook (whose parent process already acquired a
lock on this path). */
if (!locksHeld.count(printStorePath(info.path)))
outputLock.lockPaths({realPath});
if (repair || !isValidPath(info.path)) { if (repair || !isValidPath(info.path)) {
deletePath(realPath); PathLocks outputLock;
/* While restoring the path from the NAR, compute the hash auto realPath = Store::toRealPath(info.path);
of the NAR. */
HashSink hashSink(HashAlgorithm::SHA256);
TeeSource wrapperSource { source, hashSink }; /* Lock the output path. But don't lock if we're being called
from a build hook (whose parent process already acquired a
lock on this path). */
if (!locksHeld.count(printStorePath(info.path)))
outputLock.lockPaths({realPath});
narRead = true; if (repair || !isValidPath(info.path)) {
restorePath(realPath, wrapperSource, settings.fsyncStorePaths);
auto hashResult = hashSink.finish(); deletePath(realPath);
if (hashResult.first != info.narHash) /* While restoring the path from the NAR, compute the hash
throw Error("hash mismatch importing path '%s';\n specified: %s\n got: %s", of the NAR. */
printStorePath(info.path), info.narHash.to_string(HashFormat::Nix32, true), hashResult.first.to_string(HashFormat::Nix32, true)); HashSink hashSink(HashAlgorithm::SHA256);
if (hashResult.second != info.narSize) TeeSource wrapperSource { source, hashSink };
throw Error("size mismatch importing path '%s';\n specified: %s\n got: %s",
printStorePath(info.path), info.narSize, hashResult.second);
if (info.ca) { narRead = true;
auto & specified = *info.ca; restorePath(realPath, wrapperSource, settings.fsyncStorePaths);
auto actualHash = ({
auto accessor = getFSAccessor(false); auto hashResult = hashSink.finish();
CanonPath path { printStorePath(info.path) };
Hash h { HashAlgorithm::SHA256 }; // throwaway def to appease C++ if (hashResult.first != info.narHash)
auto fim = specified.method.getFileIngestionMethod(); throw Error("hash mismatch importing path '%s';\n specified: %s\n got: %s",
switch (fim) { printStorePath(info.path), info.narHash.to_string(HashFormat::Nix32, true), hashResult.first.to_string(HashFormat::Nix32, true));
case FileIngestionMethod::Flat:
case FileIngestionMethod::NixArchive: if (hashResult.second != info.narSize)
{ throw Error("size mismatch importing path '%s';\n specified: %s\n got: %s",
HashModuloSink caSink { printStorePath(info.path), info.narSize, hashResult.second);
specified.hash.algo,
std::string { info.path.hashPart() }, if (info.ca) {
auto & specified = *info.ca;
auto actualHash = ({
auto accessor = getFSAccessor(false);
CanonPath path { printStorePath(info.path) };
Hash h { HashAlgorithm::SHA256 }; // throwaway def to appease C++
auto fim = specified.method.getFileIngestionMethod();
switch (fim) {
case FileIngestionMethod::Flat:
case FileIngestionMethod::NixArchive:
{
HashModuloSink caSink {
specified.hash.algo,
std::string { info.path.hashPart() },
};
dumpPath({accessor, path}, caSink, (FileSerialisationMethod) fim);
h = caSink.finish().first;
break;
}
case FileIngestionMethod::Git:
h = git::dumpHash(specified.hash.algo, {accessor, path}).hash;
break;
}
ContentAddress {
.method = specified.method,
.hash = std::move(h),
}; };
dumpPath({accessor, path}, caSink, (FileSerialisationMethod) fim); });
h = caSink.finish().first; if (specified.hash != actualHash.hash) {
break; throw Error("ca hash mismatch importing path '%s';\n specified: %s\n got: %s",
printStorePath(info.path),
specified.hash.to_string(HashFormat::Nix32, true),
actualHash.hash.to_string(HashFormat::Nix32, true));
} }
case FileIngestionMethod::Git:
h = git::dumpHash(specified.hash.algo, {accessor, path}).hash;
break;
}
ContentAddress {
.method = specified.method,
.hash = std::move(h),
};
});
if (specified.hash != actualHash.hash) {
throw Error("ca hash mismatch importing path '%s';\n specified: %s\n got: %s",
printStorePath(info.path),
specified.hash.to_string(HashFormat::Nix32, true),
actualHash.hash.to_string(HashFormat::Nix32, true));
} }
autoGC();
canonicalisePathMetaData(realPath);
optimisePath(realPath, repair); // FIXME: combine with hashPath()
if (settings.fsyncStorePaths) {
recursiveSync(realPath);
syncParent(realPath);
}
registerValidPath(info);
} }
autoGC(); outputLock.setDeletion(true);
canonicalisePathMetaData(realPath);
optimisePath(realPath, repair); // FIXME: combine with hashPath()
if (settings.fsyncStorePaths) {
recursiveSync(realPath);
syncParent(realPath);
}
registerValidPath(info);
} }
outputLock.setDeletion(true);
} }
// In case `cleanup` ignored an `Interrupted` exception
checkInterrupt();
} }

View file

@ -35,7 +35,7 @@ struct MakeReadOnly
/* This will make the path read-only. */ /* This will make the path read-only. */
if (path != "") canonicaliseTimestampAndPermissions(path); if (path != "") canonicaliseTimestampAndPermissions(path);
} catch (...) { } catch (...) {
ignoreException(); ignoreExceptionInDestructor();
} }
} }
}; };

View file

@ -27,7 +27,7 @@ PathLocks::~PathLocks()
try { try {
unlock(); unlock();
} catch (...) { } catch (...) {
ignoreException(); ignoreExceptionInDestructor();
} }
} }

View file

@ -30,7 +30,7 @@ ref<SourceAccessor> RemoteFSAccessor::addToCache(std::string_view hashPart, std:
/* FIXME: do this asynchronously. */ /* FIXME: do this asynchronously. */
writeFile(makeCacheFile(hashPart, "nar"), nar); writeFile(makeCacheFile(hashPart, "nar"), nar);
} catch (...) { } catch (...) {
ignoreException(); ignoreExceptionExceptInterrupt();
} }
} }
@ -42,7 +42,7 @@ ref<SourceAccessor> RemoteFSAccessor::addToCache(std::string_view hashPart, std:
nlohmann::json j = listNar(narAccessor, CanonPath::root, true); nlohmann::json j = listNar(narAccessor, CanonPath::root, true);
writeFile(makeCacheFile(hashPart, "ls"), j.dump()); writeFile(makeCacheFile(hashPart, "ls"), j.dump());
} catch (...) { } catch (...) {
ignoreException(); ignoreExceptionExceptInterrupt();
} }
} }

View file

@ -86,7 +86,7 @@ SQLite::~SQLite()
if (db && sqlite3_close(db) != SQLITE_OK) if (db && sqlite3_close(db) != SQLITE_OK)
SQLiteError::throw_(db, "closing database"); SQLiteError::throw_(db, "closing database");
} catch (...) { } catch (...) {
ignoreException(); ignoreExceptionInDestructor();
} }
} }
@ -125,7 +125,7 @@ SQLiteStmt::~SQLiteStmt()
if (stmt && sqlite3_finalize(stmt) != SQLITE_OK) if (stmt && sqlite3_finalize(stmt) != SQLITE_OK)
SQLiteError::throw_(db, "finalizing statement '%s'", sql); SQLiteError::throw_(db, "finalizing statement '%s'", sql);
} catch (...) { } catch (...) {
ignoreException(); ignoreExceptionInDestructor();
} }
} }
@ -240,7 +240,7 @@ SQLiteTxn::~SQLiteTxn()
if (active && sqlite3_exec(db, "rollback;", 0, 0, 0) != SQLITE_OK) if (active && sqlite3_exec(db, "rollback;", 0, 0, 0) != SQLITE_OK)
SQLiteError::throw_(db, "aborting transaction"); SQLiteError::throw_(db, "aborting transaction");
} catch (...) { } catch (...) {
ignoreException(); ignoreExceptionInDestructor();
} }
} }

View file

@ -1055,7 +1055,7 @@ std::map<StorePath, StorePath> copyPaths(
// not be within our control to change that, and we might still want // not be within our control to change that, and we might still want
// to at least copy the output paths. // to at least copy the output paths.
if (e.missingFeature == Xp::CaDerivations) if (e.missingFeature == Xp::CaDerivations)
ignoreException(); ignoreExceptionExceptInterrupt();
else else
throw; throw;
} }

View file

@ -91,7 +91,7 @@ HookInstance::~HookInstance()
toHook.writeSide = -1; toHook.writeSide = -1;
if (pid != -1) pid.kill(); if (pid != -1) pid.kill();
} catch (...) { } catch (...) {
ignoreException(); ignoreExceptionInDestructor();
} }
} }

View file

@ -109,9 +109,9 @@ LocalDerivationGoal::~LocalDerivationGoal()
{ {
/* Careful: we should never ever throw an exception from a /* Careful: we should never ever throw an exception from a
destructor. */ destructor. */
try { deleteTmpDir(false); } catch (...) { ignoreException(); } try { deleteTmpDir(false); } catch (...) { ignoreExceptionInDestructor(); }
try { killChild(); } catch (...) { ignoreException(); } try { killChild(); } catch (...) { ignoreExceptionInDestructor(); }
try { stopDaemon(); } catch (...) { ignoreException(); } try { stopDaemon(); } catch (...) { ignoreExceptionInDestructor(); }
} }
@ -1531,7 +1531,7 @@ void LocalDerivationGoal::startDaemon()
NotTrusted, daemon::Recursive); NotTrusted, daemon::Recursive);
debug("terminated daemon connection"); debug("terminated daemon connection");
} catch (SystemError &) { } catch (SystemError &) {
ignoreException(); ignoreExceptionExceptInterrupt();
} }
}); });

View file

@ -12,7 +12,7 @@ WorkerProto::BasicClientConnection::~BasicClientConnection()
try { try {
to.flush(); to.flush();
} catch (...) { } catch (...) {
ignoreException(); ignoreExceptionInDestructor();
} }
} }

View file

@ -45,7 +45,7 @@ unsigned int getMaxCPU()
auto period = cpuMaxParts[1]; auto period = cpuMaxParts[1];
if (quota != "max") if (quota != "max")
return std::ceil(std::stoi(quota) / std::stof(period)); return std::ceil(std::stoi(quota) / std::stof(period));
} catch (Error &) { ignoreException(lvlDebug); } } catch (Error &) { ignoreExceptionInDestructor(lvlDebug); }
#endif #endif
return 0; return 0;

View file

@ -2,6 +2,7 @@
#include "signals.hh" #include "signals.hh"
#include "finally.hh" #include "finally.hh"
#include "serialise.hh" #include "serialise.hh"
#include "util.hh"
#include <fcntl.h> #include <fcntl.h>
#include <unistd.h> #include <unistd.h>
@ -65,7 +66,7 @@ AutoCloseFD::~AutoCloseFD()
try { try {
close(); close();
} catch (...) { } catch (...) {
ignoreException(); ignoreExceptionInDestructor();
} }
} }

View file

@ -5,6 +5,7 @@
#include "signals.hh" #include "signals.hh"
#include "finally.hh" #include "finally.hh"
#include "serialise.hh" #include "serialise.hh"
#include "util.hh"
#include <atomic> #include <atomic>
#include <cerrno> #include <cerrno>
@ -517,7 +518,7 @@ AutoDelete::~AutoDelete()
} }
} }
} catch (...) { } catch (...) {
ignoreException(); ignoreExceptionInDestructor();
} }
} }

View file

@ -346,7 +346,7 @@ Activity::~Activity()
try { try {
logger.stopActivity(id); logger.stopActivity(id);
} catch (...) { } catch (...) {
ignoreException(); ignoreExceptionInDestructor();
} }
} }

View file

@ -1,5 +1,6 @@
#include "serialise.hh" #include "serialise.hh"
#include "signals.hh" #include "signals.hh"
#include "util.hh"
#include <cstring> #include <cstring>
#include <cerrno> #include <cerrno>
@ -52,7 +53,7 @@ void BufferedSink::flush()
FdSink::~FdSink() FdSink::~FdSink()
{ {
try { flush(); } catch (...) { ignoreException(); } try { flush(); } catch (...) { ignoreExceptionInDestructor(); }
} }

View file

@ -503,7 +503,7 @@ struct FramedSource : Source
} }
} }
} catch (...) { } catch (...) {
ignoreException(); ignoreExceptionInDestructor();
} }
} }
@ -550,7 +550,7 @@ struct FramedSink : nix::BufferedSink
to << 0; to << 0;
to.flush(); to.flush();
} catch (...) { } catch (...) {
ignoreException(); ignoreExceptionInDestructor();
} }
} }

View file

@ -111,9 +111,8 @@ void ThreadPool::doWork(bool mainThread)
try { try {
std::rethrow_exception(exc); std::rethrow_exception(exc);
} catch (std::exception & e) { } catch (std::exception & e) {
if (!dynamic_cast<Interrupted*>(&e) && if (!dynamic_cast<ThreadPoolShutDown*>(&e))
!dynamic_cast<ThreadPoolShutDown*>(&e)) ignoreExceptionExceptInterrupt();
ignoreException();
} catch (...) { } catch (...) {
} }
} }

View file

@ -91,7 +91,7 @@ void unix::triggerInterrupt()
try { try {
callback(); callback();
} catch (...) { } catch (...) {
ignoreException(); ignoreExceptionInDestructor();
} }
} }
} }

View file

@ -1,6 +1,7 @@
#include "util.hh" #include "util.hh"
#include "fmt.hh" #include "fmt.hh"
#include "file-path.hh" #include "file-path.hh"
#include "signals.hh"
#include <array> #include <array>
#include <cctype> #include <cctype>
@ -182,7 +183,7 @@ std::string shellEscape(const std::string_view s)
} }
void ignoreException(Verbosity lvl) void ignoreExceptionInDestructor(Verbosity lvl)
{ {
/* Make sure no exceptions leave this function. /* Make sure no exceptions leave this function.
printError() also throws when remote is closed. */ printError() also throws when remote is closed. */
@ -195,6 +196,17 @@ void ignoreException(Verbosity lvl)
} catch (...) { } } catch (...) { }
} }
void ignoreExceptionExceptInterrupt(Verbosity lvl)
{
try {
throw;
} catch (const Interrupted & e) {
throw;
} catch (std::exception & e) {
printMsg(lvl, "error (ignored): %1%", e.what());
}
}
constexpr char base64Chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; constexpr char base64Chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

View file

@ -156,9 +156,26 @@ std::string toLower(std::string s);
std::string shellEscape(const std::string_view s); std::string shellEscape(const std::string_view s);
/* Exception handling in destructors: print an error message, then /**
ignore the exception. */ * Exception handling in destructors: print an error message, then
void ignoreException(Verbosity lvl = lvlError); * ignore the exception.
*
* If you're not in a destructor, you usually want to use `ignoreExceptionExceptInterrupt()`.
*
* This function might also be used in callbacks whose caller may not handle exceptions,
* but ideally we propagate the exception using an exception_ptr in such cases.
* See e.g. `PackBuilderContext`
*/
void ignoreExceptionInDestructor(Verbosity lvl = lvlError);
/**
* Not destructor-safe.
* Print an error message, then ignore the exception.
* If the exception is an `Interrupted` exception, rethrow it.
*
* This may be used in a few places where Interrupt can't happen, but that's ok.
*/
void ignoreExceptionExceptInterrupt(Verbosity lvl = lvlError);

View file

@ -672,7 +672,7 @@ struct CmdDevelop : Common, MixEnvironment
throw Error("package 'nixpkgs#bashInteractive' does not provide a 'bin/bash'"); throw Error("package 'nixpkgs#bashInteractive' does not provide a 'bin/bash'");
} catch (Error &) { } catch (Error &) {
ignoreException(); ignoreExceptionExceptInterrupt();
} }
// Override SHELL with the one chosen for this environment. // Override SHELL with the one chosen for this environment.

View file

@ -372,9 +372,11 @@ struct CmdFlakeCheck : FlakeCommand
auto reportError = [&](const Error & e) { auto reportError = [&](const Error & e) {
try { try {
throw e; throw e;
} catch (Interrupted & e) {
throw;
} catch (Error & e) { } catch (Error & e) {
if (settings.keepGoing) { if (settings.keepGoing) {
ignoreException(); ignoreExceptionExceptInterrupt();
hasErrors = true; hasErrors = true;
} }
else else