BareGit
#pragma once

#include <condition_variable>
#include <cstddef>
#include <functional>
#include <future>
#include <memory>
#include <mutex>
#include <optional>
#include <thread>
#include <vector>

#include <mw/error.hpp>

/// Fixed worker pool that rejects tasks when all workers are reserved.
class ThreadPool
{
public:
    /// Start workers, returning validation errors.
    static mw::E<std::unique_ptr<ThreadPool>> create(std::size_t worker_count);

    /// Finish accepted tasks and join workers. Must not run on a worker.
    /// Callers must stop submitting before destroying the pool.
    ~ThreadPool();

    /// Pools own their workers and cannot be copied.
    ThreadPool(const ThreadPool&) = delete;
    /// Pools cannot transfer ownership through copy assignment.
    ThreadPool& operator=(const ThreadPool&) = delete;

    /// Reserve a worker or return nullopt immediately if full.
    /// The future reports completion. Task exceptions terminate the process.
    /// Invalid tasks return errors.
    mw::E<std::optional<std::future<void>>> trySubmit(
        std::move_only_function<void()> task);

    /// Wait until all accepted tasks finish. Must not run on a worker.
    /// Concurrent submissions may extend the wait.
    void waitIdle();

private:
    explicit ThreadPool(std::size_t worker_count);

    struct Worker
    {
        std::condition_variable work_ready;
        std::packaged_task<void()> task;
        bool busy = false;
        std::thread thread;
    };

    void run(Worker& worker);
    void stop();

    std::mutex mutex;
    std::condition_variable idle;
    std::size_t outstanding = 0;
    bool stopping = false;
    std::vector<Worker> workers;
};