mirror of
https://github.com/NixOS/nix.git
synced 2025-12-17 14:31:06 +01:00
Move /src to /subprojects
This will facilitate breaking up Nix into multiple packages for each component with Meson.
This commit is contained in:
parent
4db9487823
commit
84e2963f8e
737 changed files with 504 additions and 505 deletions
158
subprojects/libutil/thread-pool.cc
Normal file
158
subprojects/libutil/thread-pool.cc
Normal file
|
|
@ -0,0 +1,158 @@
|
|||
#include "thread-pool.hh"
|
||||
#include "signals.hh"
|
||||
#include "util.hh"
|
||||
|
||||
namespace nix {
|
||||
|
||||
ThreadPool::ThreadPool(size_t _maxThreads)
|
||||
: maxThreads(_maxThreads)
|
||||
{
|
||||
if (!maxThreads) {
|
||||
maxThreads = std::thread::hardware_concurrency();
|
||||
if (!maxThreads) maxThreads = 1;
|
||||
}
|
||||
|
||||
debug("starting pool of %d threads", maxThreads - 1);
|
||||
}
|
||||
|
||||
ThreadPool::~ThreadPool()
|
||||
{
|
||||
shutdown();
|
||||
}
|
||||
|
||||
void ThreadPool::shutdown()
|
||||
{
|
||||
std::vector<std::thread> workers;
|
||||
{
|
||||
auto state(state_.lock());
|
||||
quit = true;
|
||||
std::swap(workers, state->workers);
|
||||
}
|
||||
|
||||
if (workers.empty()) return;
|
||||
|
||||
debug("reaping %d worker threads", workers.size());
|
||||
|
||||
work.notify_all();
|
||||
|
||||
for (auto & thr : workers)
|
||||
thr.join();
|
||||
}
|
||||
|
||||
void ThreadPool::enqueue(const work_t & t)
|
||||
{
|
||||
auto state(state_.lock());
|
||||
if (quit)
|
||||
throw ThreadPoolShutDown("cannot enqueue a work item while the thread pool is shutting down");
|
||||
state->pending.push(t);
|
||||
/* Note: process() also executes items, so count it as a worker. */
|
||||
if (state->pending.size() > state->workers.size() + 1 && state->workers.size() + 1 < maxThreads)
|
||||
state->workers.emplace_back(&ThreadPool::doWork, this, false);
|
||||
work.notify_one();
|
||||
}
|
||||
|
||||
void ThreadPool::process()
|
||||
{
|
||||
state_.lock()->draining = true;
|
||||
|
||||
/* Do work until no more work is pending or active. */
|
||||
try {
|
||||
doWork(true);
|
||||
|
||||
auto state(state_.lock());
|
||||
|
||||
assert(quit);
|
||||
|
||||
if (state->exception)
|
||||
std::rethrow_exception(state->exception);
|
||||
|
||||
} catch (...) {
|
||||
/* In the exceptional case, some workers may still be
|
||||
active. They may be referencing the stack frame of the
|
||||
caller. So wait for them to finish. (~ThreadPool also does
|
||||
this, but it might be destroyed after objects referenced by
|
||||
the work item lambdas.) */
|
||||
shutdown();
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
void ThreadPool::doWork(bool mainThread)
|
||||
{
|
||||
ReceiveInterrupts receiveInterrupts;
|
||||
|
||||
#ifndef _WIN32 // Does Windows need anything similar for async exit handling?
|
||||
if (!mainThread)
|
||||
unix::interruptCheck = [&]() { return (bool) quit; };
|
||||
#endif
|
||||
|
||||
bool didWork = false;
|
||||
std::exception_ptr exc;
|
||||
|
||||
while (true) {
|
||||
work_t w;
|
||||
{
|
||||
auto state(state_.lock());
|
||||
|
||||
if (didWork) {
|
||||
assert(state->active);
|
||||
state->active--;
|
||||
|
||||
if (exc) {
|
||||
|
||||
if (!state->exception) {
|
||||
state->exception = exc;
|
||||
// Tell the other workers to quit.
|
||||
quit = true;
|
||||
work.notify_all();
|
||||
} else {
|
||||
/* Print the exception, since we can't
|
||||
propagate it. */
|
||||
try {
|
||||
std::rethrow_exception(exc);
|
||||
} catch (std::exception & e) {
|
||||
if (!dynamic_cast<ThreadPoolShutDown*>(&e))
|
||||
ignoreExceptionExceptInterrupt();
|
||||
} catch (...) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Wait until a work item is available or we're asked to
|
||||
quit. */
|
||||
while (true) {
|
||||
if (quit) return;
|
||||
|
||||
if (!state->pending.empty()) break;
|
||||
|
||||
/* If there are no active or pending items, and the
|
||||
main thread is running process(), then no new items
|
||||
can be added. So exit. */
|
||||
if (!state->active && state->draining) {
|
||||
quit = true;
|
||||
work.notify_all();
|
||||
return;
|
||||
}
|
||||
|
||||
state.wait(work);
|
||||
}
|
||||
|
||||
w = std::move(state->pending.front());
|
||||
state->pending.pop();
|
||||
state->active++;
|
||||
}
|
||||
|
||||
try {
|
||||
w();
|
||||
} catch (...) {
|
||||
exc = std::current_exception();
|
||||
}
|
||||
|
||||
didWork = true;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue