You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
libcutefish/files/async_p.h

83 lines
2.3 KiB
C++

// SPDX-FileCopyrightText: 2026 Reion Wong <reionwong@gmail.com>
// SPDX-License-Identifier: BSD-3-Clause
#pragma once
#include <algorithm>
#include <atomic>
#include <condition_variable>
#include <cstddef>
#include <exception>
#include <functional>
#include <mutex>
#include <thread>
namespace cutefish::files::internal {
// Folders from this size on spread even a refresh over threads.
constexpr ptrdiff_t LargeFolder = 20000;
inline int idealThreadCount()
{
return std::max(1u, std::thread::hardware_concurrency());
}
// Helpers for work split into blocks: half the logical CPUs work as fast as all of
// them for less CPU time, and leave the GUI and the compositor theirs.
inline ptrdiff_t blockHelpers(ptrdiff_t blocks)
{
return std::max(ptrdiff_t(0), std::min(ptrdiff_t(idealThreadCount() / 2 - 1), blocks - 1));
}
// Hands work to a helper thread that is idle, or starts one while fewer than
// the logical CPUs run; false when neither is possible right now.
bool tryStartHelper(std::function<void()> work);
// Runs body on this thread and on up to helpers threads free right now; body
// shares the work out itself and stops once stop is set, after an exception. The
// first exception is rethrown when every run has returned, as the helpers use this
// stack.
template<typename Body>
void runOnFreeThreads(ptrdiff_t helpers, Body body)
{
std::atomic_bool stop = false;
std::mutex mutex;
std::condition_variable finishedCondition;
std::exception_ptr failure;
ptrdiff_t finished = 0;
const auto run = [&] {
try {
body(stop);
} catch (...) {
const std::lock_guard lock(mutex);
if (!failure)
failure = std::current_exception();
stop = true;
}
};
ptrdiff_t started = 0;
// Only threads free now: queued helpers could wait behind work that waits for them.
while (started < helpers && tryStartHelper([&] {
run();
const std::lock_guard lock(mutex);
++finished;
finishedCondition.notify_all();
}))
++started;
run();
std::unique_lock lock(mutex);
finishedCondition.wait(lock, [&] {
return finished == started;
});
if (failure)
std::rethrow_exception(failure);
}
} // namespace cutefish::files::internal