Concurrent_queue potential bug: Promise/Future returning 0xfeeefeee

Concurrent_queue potential bug: Promise/Future returning 0xfeeefeee

imagem de klaim

I'm stuck with a problem I quite don't understand and that involve either boost::future/promise or tbb::concurrent_queue,
or my understanding of using both.

I think there is a high probability that someone here will point to the problem, but I will report too to the tbb forum

Some context:
- I'm using VS2012 Update 2
- it's 32 bit DEBUG mode
- I use Boost.Thread V4 (in CMake I set "add_definitions( -DBOOST_THREAD_VERSION=4 )" to force it )
- I use TBB 4.1 Update 2

I've setup a test (using GTest) which reproduce the problem:

############

int TRUC = 42;
TEST( Test_WorkQueue, future_promise_shared_work )
{
WorkQueue work_queue;

// I'm doing this like that because it reflect the use case I have,
// I didn't find another way to have the promise alive in a container which don't allow move semantic
auto do_some_work = [&]()-> boost::future<int*>
{
struct K
{
boost::promise<int*> promise;

void foo()
{
promise.set_value( &TRUC );
}
};

auto object_ptr = std::make_shared<K>(); // D:
work_queue.push( [=]
{
object_ptr->foo();
});

return object_ptr->promise.get_future();
};

auto ft_value = do_some_work();

work_queue.execute();

auto value = ft_value.get();
ASSERT_EQ( &TRUC, value ); // this fails, value == 0xfeeefeee
}

##########

My WorkQueue type use tbb::concurrent_queue and is defined as:

##########

class WorkQueue
{
public:

template< class WorkTask >
void push( WorkTask&& task )
{
m_task_queue.push( std::forward<WorkTask>( task ) );
}

/** Execute all the work queued until now. */
void execute()
{
if( m_task_queue.empty() )
return;

bool end_of_work = false;
m_task_queue.push( [&]{ end_of_work = true; } );

std::function<void()> work;
while( !end_of_work && m_task_queue.try_pop( work ) )
{
work();
}
}

private:

mutable tbb::concurrent_queue< std::function<void()> > m_task_queue;
};

############

It's basically a simple wrapper around the concurrent_queue, but maybe the execute() code have an impact.

THE PROBLEM:

The test fails.
value == 0xfeeefeee as soon as the get() call is done.

Using the debugger, I can't figure where the data is lost. It seem that everything is correct until when all
the hierarchy of function call returns the result...which is different and wrong once out of the function.

I first wrote this test with std::vector< std::function<void()> >
instead of the concurrent queue to check if it was boost::future/promise which was not working well with Boost.Thread V4.
With the vector instead of concurrent_queue, the code works perfectly.

Does someone have an idea what is going on?

Joel Lamotte

3 posts / 0 new
Último post
Para obter mais informações sobre otimizações de compiladores, consulte Aviso sobre otimizações.
imagem de klaim

I just replaced boost::future/promise by std::future/promise and the test just works.
However the same change in a real code using WorkQueue internally and using similar code to the test
makes the future::get() provide invalid pointers with values close to 0x000000 but not exactly.
This code then makes access violation as the pointers are obviously not valid.

So far I know:

- having a std::vector instead of tbb::concurrent_queue makes the test work (the real code can't use std::vector, it's concurrent);
- using std::future/promise instead of boost::future/promise makes the test work ...;
- ...but not the real code (which should be almost the same than the test);

My current suspicion: maybe I do something wrong with the lifetime of the promise?
I don't see what unfortunately.

I'll try to reproduce the problem with std::future/promise I see in my real code but in the test.

Joel Lamotte

imagem de klaim

Ok I found that my "real code" is actually buggy and indeed provide a wrong pointer (for a reason I'll have to hunt).
So don't bother, std::future/promise does work with my test and real code using WorkQueue/tbb::concurrent_queue
but boost::future/promise doesnt.

I also simplified the test (and real) code for clarity, it should be equivalent to the code from my first post:

int TRUC = 42;
TEST( Test_WorkQueue, future_promise_shared_work )
{
WorkQueue work_queue;

auto do_some_work = [&]()-> std::future
{
auto promise = std::make_shared>();
work_queue.push( [=]
{
promise->set_value( &TRUC );
});

return promise->get_future();

};

auto ft_value = do_some_work();

work_queue.execute();

auto value = ft_value.get();
ASSERT_EQ( &TRUC, value );
}

Faça login para deixar um comentário.