1

I'm working on a thread pool from the book C++ Cuncerrency in Action by Anthony Willimas This thread pools has a submit call that take as tasks callable objects that return a value and return a std::future handle to them, and I managed to build applications that use it. But I can't manage to make it work with callable ojects that return void: the code won't even compile. I get these errors, all in the future header :

error C2182: '_Get_value' : illegal use of type 'void'  
error C2182: '_Val' : illegal use of type 'void'    
error C2182: '_Val' : illegal use of type 'void'    
error C2665: 'std::forward' : none of the 2 overloads could convert all the argument types  
error C2512: 'std::_Promise<int>' : no appropriate default constructor available    

The submit call is

    template<typename FunctionType>
    std::future<typename std::result_of<FunctionType()>::type> submit(FunctionType f){
        typedef typename std::result_of<FunctionType()>::type result_type;
        std::packaged_task<result_type()> task(std::move(f));
        std::future<result_type> res(task.get_future());
        work_queue.push(std::move(task));
        return res;
    }

If I comment this line

work_queue.push(std::move(task));

the code compiles. So I think that the problem is "there"...

The work_queue is delcared as

threadsafe_queue<function_wrapper> work_queue;

where threadsafe_queue is

template<typename T>
class threadsafe_queue{
private:
    mutable std::mutex mut;
    std::queue<std::shared_ptr<T> > data_queue;
    std::condition_variable data_cond;
public:
    threadsafe_queue(){}

    void push(T new_value){
        std::shared_ptr<T> data(
        std::make_shared<T>(std::move(new_value)));
        std::lock_guard<std::mutex> lk(mut);
        data_queue.push(data);
        data_cond.notify_one();
    }

                 //[...]
}

and threadsafe_queue

class function_wrapper{

    struct impl_base {
        virtual void call()=0;
        virtual ~impl_base() {}
    };

    std::unique_ptr<impl_base> impl;

    template<typename F>
    struct impl_type: impl_base{
        F f;
        impl_type(F&& f_): f(std::move(f_)) {}
        void call() { f(); }
    };

public:
    template<typename F>
    function_wrapper(F&& f):impl(new impl_type<F>(std::move(f))){}
    void operator()() { impl->call(); }

    function_wrapper(){}
    function_wrapper(function_wrapper&& other):impl(std::move(other.impl)){}
    function_wrapper& operator=(function_wrapper&& other){
        impl=std::move(other.impl);
        return *this;
    }

private:
    function_wrapper(const function_wrapper&);
    function_wrapper(function_wrapper&);
    function_wrapper& operator=(const function_wrapper&);
};

I tried with, but I got the same errors.

    template<typename FunctionType>
    std::future<void> submit(FunctionType f){
        std::packaged_task<void(void)> task(std::move(f));
        std::future<void> res(task.get_future());
        work_queue.push(std::move(task));
        return res;
    }
David G
  • 94,763
  • 41
  • 167
  • 253
darius
  • 837
  • 9
  • 22
  • what is the point of `function_wrapper`? It looks like a half done `std::function` clone. – Yakk - Adam Nevraumont Jan 04 '14 at 02:20
  • 1
    @Yakk it is there because std::function<> requires that the stored function objects are copy-constructible, while std::packaged_task<> is just movable, not copyable. – darius Jan 04 '14 at 02:51
  • @darius - I have implemented this same thread pool and have used it a few times. Are you still looking for an answer? – Charles Pehlivanian Jan 08 '14 at 21:15
  • 1
    I have a working version that works with `packaged_task`as you desire. – Charles Pehlivanian Jan 09 '14 at 17:09
  • @CharlesPehlivanian Yes, that would be perfect! Thank you, and sorry for my late reply, I've been away and without internet. – darius Jan 16 '14 at 18:11
  • I hate to say it but this is one thing that only Windows got right: ONLY the OS can properly provide a thread pool implementation - only the OS can know what other processes are doing and whether there are too many threads running on the whole machine. The OS also knows when threads get blocked on I/O. What's going to happen when you have multiple CPU-intensive programs that all create a 16-thread thread pool on a 16-processor machine? You have a massive over-subscription that thrashes the caches. Maybe one day when there are 100+ busy threads on a linux box the linux community will wake up. – doug65536 Feb 10 '14 at 02:01

0 Answers0