#include <iostream>
#include <vector>
#include <thread>
#include <string>
#include <chrono>
#include <random>
#include <condition_variable>
#include <boost/asio.hpp>


struct worker
{
    worker(int index)
    : _index(index)
    {}

    void operator()() const 
    {
        printf( "I am worker: %d\n", _index);
    }

    int _index;
};

struct worker_pool
{
    boost::asio::io_service _io_service;
    boost::asio::io_service::work _work { _io_service };

    std::vector<std::thread> _threads;

    std::condition_variable _cv;
    std::mutex _cvm;
    size_t _tasks = 0;


    void start()
    {
        for (int i = 0 ; i < 8 ; ++i) {
            _threads.emplace_back(std::bind(&worker_pool::thread_proc, this));
        }
    }

    void wait()
    {
        std::unique_lock<std::mutex> lock(_cvm);
        _cv.wait(lock, [this] { return _tasks == 0; });
    }

    void stop()
    {
        wait();
        _io_service.stop();
        for (auto& t : _threads) {
            if (t.joinable())
                t.join();
        }
        _threads.clear();

    }

    void thread_proc()
    {
        while (!_io_service.stopped())
        {
            _io_service.run();
        }
    }

    void reduce() {

        std::unique_lock<std::mutex> lock(_cvm);
        if (--_tasks == 0) {
            lock.unlock();
            _cv.notify_all();
        }
    }

    void submit( worker & f)
    {
        std::unique_lock<std::mutex> lock(_cvm);
        ++ _tasks;
        lock.unlock();
        _io_service.post([this, &f]
                         {
                             f();
                             reduce();
                         });
    }
};

auto main() -> int
{
    worker_pool pool;
    pool.start();

    for (int i = 0 ; i < 100 ; ++i) 
    {
        worker job(i);
        pool.submit(job );
    }

    pool.wait();
    pool.stop();

    return 0;
}
