std::future.get() sometimes stuck in os x
Asked Answered
G

1

6

I have two threads, one thread should receive and process requests from another. The second is to transfer requests and receive a response synchronously. I tried the following scheme: queue of pair (value, promise). The first thread creates a promise and pushed it in synchronous queue and waiting for a return value by future.get()

The problem is that sometimes the thread stuck on future.get(), but when i pause program execution and continue it again works correctly. this stucks has random nature.

FutureQueue.h

#ifndef FutureQueue_h
#define FutureQueue_h

#include <queue>
#include <future>
#include <thread>
#include <mutex>
#include <condition_variable>

template <typename T, typename R>
class Work{
public:
    Work(){

    }
    Work(T value, std::promise<R>* promise){
        m_value = value;
        m_p_promise = promise;
    }
    std::promise<R>* m_p_promise;
    T m_value;
public:
    T getValue(){
        return m_value;
    }
    void setResult(R result){
        m_p_promise->set_value(result);
    }
};


template <typename T, typename R>
class FutureQueue
{
public:
    Work<T,R> getWork(){
        auto p = pop();
        return Work<T,R>(p.first,p.second);
    }
    R execute(T value)
    {
        std::promise<R> promise = std::promise<R>();
        std::future<R> f = promise.get_future();
        auto p = std::pair<T, std::promise<R>*>(value, &promise);
        push(p);
        return f.get();
    }
    private:
    std::pair<T,std::promise<R>*> pop(){
        std::unique_lock<std::mutex> mlock(mutex_);
        while (queue_.empty())
        {
            cond_.wait(mlock);
        }
        auto item = queue_.front();
        queue_.pop();
        return item;
    }
    void push(const std::pair<T,std::promise<R>*>& item){
        std::unique_lock<std::mutex> mlock(mutex_);
        queue_.push(item);
        mlock.unlock();
        cond_.notify_one();
    }
    std::queue<std::pair<T,std::promise<R>*>> queue_;
    std::mutex mutex_;
    std::condition_variable cond_;
};

#endif

main.cpp

#include <iostream>
#include <thread>

#include "FutureQueue.h"

using namespace std;

atomic<bool> quit;
FutureQueue<int, int> mQueue;

void consumer(){
    Work<int,int> work;
    while(true){
        work = mQueue.getWork();
        if (quit){
            break;
        }
        int value = work.getValue()+100;
        work.setResult(value);
    }
    work.setResult(0);
}

int main(int argc, const char * argv[]) {
    quit = false;
    thread thread(consumer);
    // test 2
    for (int i=0;i<100000;i++){
        int value = mQueue.execute(i);
        cout << "input " << i <<" execute result " <<  value << endl;
    }
    quit = true;
    mQueue.execute(-1);
    thread.join();
    return 0;
}

I don't know whats wrong with this code, maybe you can suggest better solution. Thank you

UPDATE

Stucks occurs only in os x with Apple LLVM version 6.0

There is no problem under gcc on OS X and Linux and Visual studio on Windows

Godroon answered 19/12, 2015 at 12:54 Comment(1)
My visual studio doesn't fail, could it be a platform issue?Exegesis
T
0

There are two threads, A with

for (int i=0;i<100000;i++){
    int value = mQueue.execute(i);
    cout << "input " << i <<" execute result " <<  value << endl;
}
quit = true;
mQueue.execute(-1);

B with

thread thread(consumer);

You expect B first Run, then stuck because

   while (queue_.empty())
    {
        cond_.wait(mlock);
    }

B will continue run until A run the following code

    cond_.notify_one();

Usually it will be ok. But if A first fun "cond.notify_one()", the B call "con_.wait(mlock)", the B will be stuck forever.

Tropophilous answered 19/12, 2015 at 14:12 Comment(5)
If A runs first the queue will not be empty so the wait won't occur (and the mutex prevents a race there).Johniejohnna
Before A run cond.notify_one() it calls queue_.push(item), and B before call cond_.wait(mlock) check queue_.empty(). i have tried to change cond_.wait(mlock); to cond_.wait_for(mlock, std::chrono::milliseconds(1)); and result is the sameGodroon
if you change "while (queue_.empty())" to "if" what will happen, will it still stuck?Tropophilous
@XingxinPei yes, it still stuck. There is no deadlock, the problem in future.get(). I have tried without future/promise and get this result gist, it's works, but it isn't that solution that i wants. Because it doesn't works with more than one thread "A"Godroon
Stucks occurs only in os x with Apple LLVM version 6.0 -- also stuck on Ubuntu. I debugged the program, found that value "promise"(the code is listed) will be decollated after return f.get(). I suggest use pointer or smart pointer(shared_ptr) to store "promise". Please modify your programme and try again. I can run correctly both on my mac and ubuntu. std::promise<R> promise = std::promise<R>(); std::future<R> f = promise.get_future(); auto p = std::pair<T, std::promise<R>*>(value, &promise); ..... return f.get()Tropophilous

© 2022 - 2024 — McMap. All rights reserved.