libstore: asyncify LocalStore::autoGC

sadly we need both a synchronous and an asynchronous promise for this
since destructors cannot be async. we also cannot use forked promises
since multiple threads may be waiting for an auto-gc to complete, but
forked promises are bound to an event loop (and thus a single thread)

Change-Id: I7b4fdbd229c4a1e01bbf80858e93347024f2e333
This commit is contained in:
eldritch horrors
2025-02-12 19:22:20 +00:00
parent 4fec8b3214
commit a987d92bd0
4 changed files with 30 additions and 17 deletions
+1 -1
View File
@@ -313,7 +313,7 @@ kj::Promise<Result<Worker::Results>> Worker::boopGC(LocalStore & localStore)
try {
while (true) {
co_await AIO().provider.getTimer().afterDelay(10 * kj::SECONDS);
localStore.autoGC(false);
TRY_AWAIT(localStore.autoGC(false));
}
} catch (...) {
co_return result::current_exception();
+20 -9
View File
@@ -8,6 +8,7 @@
#include "lix/libutil/strings.hh"
#include "lix/libutil/thread-name.hh"
#include <kj/async.h>
#include <queue>
#include <regex>
@@ -868,8 +869,8 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results)
}
void LocalStore::autoGC(bool sync)
{
kj::Promise<Result<void>> LocalStore::autoGC(bool sync)
try {
static auto fakeFreeSpaceFile = getEnv("_NIX_TEST_FREE_SPACE_FILE");
auto getAvail = [this]() -> uint64_t {
@@ -883,33 +884,35 @@ void LocalStore::autoGC(bool sync)
return (uint64_t) st.f_bavail * st.f_frsize;
};
std::shared_future<void> future;
auto pfp = kj::newPromiseAndCrossThreadFulfiller<void>();
{
auto state(_gcState.lock());
if (state->gcRunning) {
future = state->gcFuture;
state->gcWaiters.push_back(std::move(pfp.fulfiller));
debug("waiting for auto-GC to finish");
goto sync;
}
auto now = std::chrono::steady_clock::now();
if (now < state->lastGCCheck + std::chrono::seconds(settings.minFreeCheckInterval)) return;
if (now < state->lastGCCheck + std::chrono::seconds(settings.minFreeCheckInterval)) {
co_return result::success();
}
auto avail = getAvail();
state->lastGCCheck = now;
if (avail >= settings.minFree || avail >= settings.maxFree) return;
if (avail >= settings.minFree || avail >= settings.maxFree) co_return result::success();
if (avail > state->availAfterGC * 0.97) return;
if (avail > state->availAfterGC * 0.97) co_return result::success();
state->gcRunning = true;
std::promise<void> promise;
future = state->gcFuture = promise.get_future().share();
state->gcFuture = promise.get_future();
std::thread([promise{std::move(promise)}, this, avail, getAvail]() mutable {
setCurrentThreadName("auto gc");
@@ -922,6 +925,10 @@ void LocalStore::autoGC(bool sync)
state->gcRunning = false;
state->lastGCCheck = std::chrono::steady_clock::now();
promise.set_value();
for (auto & waiter : state->gcWaiters) {
waiter->fulfill();
}
state->gcWaiters.clear();
});
GCOptions options;
@@ -946,7 +953,11 @@ void LocalStore::autoGC(bool sync)
sync:
// Wait for the future outside of the state lock.
if (sync) future.get();
if (sync) co_await pfp.promise;
co_return result::success();
} catch (...) {
co_return result::current_exception();
}
+5 -5
View File
@@ -458,12 +458,12 @@ AutoCloseFD LocalStore::openGCLock()
LocalStore::~LocalStore()
{
std::shared_future<void> future;
std::future<void> future;
{
auto state(_gcState.lock());
if (state->gcRunning)
future = state->gcFuture;
future = std::move(state->gcFuture);
}
if (future.valid()) {
@@ -1266,7 +1266,7 @@ try {
}
}
autoGC();
TRY_AWAIT(autoGC());
canonicalisePathMetaData(realPath, {});
@@ -1381,7 +1381,7 @@ try {
deletePath(realPath);
autoGC();
TRY_AWAIT(autoGC());
if (inMemory) {
StringSource dumpSource { dump };
@@ -1448,7 +1448,7 @@ try {
deletePath(realPath);
autoGC();
TRY_AWAIT(autoGC());
writeFile(realPath, s);
+4 -2
View File
@@ -9,6 +9,7 @@
#include <chrono>
#include <future>
#include <kj/async.h>
#include <string>
#include <mutex>
#include <memory>
@@ -118,7 +119,8 @@ private:
* the GC to finish.
*/
bool gcRunning = false;
std::shared_future<void> gcFuture;
std::future<void> gcFuture;
std::list<kj::Own<kj::CrossThreadPromiseFulfiller<void>>> gcWaiters;
/**
* How much disk space was available after the previous
@@ -297,7 +299,7 @@ public:
* If free disk space in /nix/store if below minFree, delete
* garbage until it exceeds maxFree.
*/
void autoGC(bool sync = true);
kj::Promise<Result<void>> autoGC(bool sync = true);
/**
* Register the store path 'output' as the output named 'outputName' of