Merge pull request #6052 from hercules-ci/issue-3294-fix-interruptCallback-deadlock

Fix deadlocked nix-daemon zombies on darwin #3294
This commit is contained in:
Eelco Dolstra 2022-02-21 16:21:45 +01:00 committed by GitHub
commit e2422c4582
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

View file

@ -1565,7 +1565,22 @@ std::pair<unsigned short, unsigned short> getWindowSize()
} }
static Sync<std::list<std::function<void()>>> _interruptCallbacks; /* We keep track of interrupt callbacks using integer tokens, so we can iterate
safely without having to lock the data structure while executing arbitrary
functions.
*/
struct InterruptCallbacks {
typedef int64_t Token;
/* We use unique tokens so that we can't accidentally delete the wrong
handler because of an erroneous double delete. */
Token nextToken = 0;
/* Used as a list, see InterruptCallbacks comment. */
std::map<Token, std::function<void()>> callbacks;
};
static Sync<InterruptCallbacks> _interruptCallbacks;
static void signalHandlerThread(sigset_t set) static void signalHandlerThread(sigset_t set)
{ {
@ -1587,8 +1602,19 @@ void triggerInterrupt()
_isInterrupted = true; _isInterrupted = true;
{ {
auto interruptCallbacks(_interruptCallbacks.lock()); InterruptCallbacks::Token i = 0;
for (auto & callback : *interruptCallbacks) { while (true) {
std::function<void()> callback;
{
auto interruptCallbacks(_interruptCallbacks.lock());
auto lb = interruptCallbacks->callbacks.lower_bound(i);
if (lb == interruptCallbacks->callbacks.end())
break;
callback = lb->second;
i = lb->first + 1;
}
try { try {
callback(); callback();
} catch (...) { } catch (...) {
@ -1698,21 +1724,22 @@ void restoreProcessContext(bool restoreMounts)
/* RAII helper to automatically deregister a callback. */ /* RAII helper to automatically deregister a callback. */
struct InterruptCallbackImpl : InterruptCallback struct InterruptCallbackImpl : InterruptCallback
{ {
std::list<std::function<void()>>::iterator it; InterruptCallbacks::Token token;
~InterruptCallbackImpl() override ~InterruptCallbackImpl() override
{ {
_interruptCallbacks.lock()->erase(it); auto interruptCallbacks(_interruptCallbacks.lock());
interruptCallbacks->callbacks.erase(token);
} }
}; };
std::unique_ptr<InterruptCallback> createInterruptCallback(std::function<void()> callback) std::unique_ptr<InterruptCallback> createInterruptCallback(std::function<void()> callback)
{ {
auto interruptCallbacks(_interruptCallbacks.lock()); auto interruptCallbacks(_interruptCallbacks.lock());
interruptCallbacks->push_back(callback); auto token = interruptCallbacks->nextToken++;
interruptCallbacks->callbacks.emplace(token, callback);
auto res = std::make_unique<InterruptCallbackImpl>(); auto res = std::make_unique<InterruptCallbackImpl>();
res->it = interruptCallbacks->end(); res->token = token;
res->it--;
return std::unique_ptr<InterruptCallback>(res.release()); return std::unique_ptr<InterruptCallback>(res.release());
} }