There are data races on future_shared_state::value
and future_shared_state::error
, set_value
and set_exception
access them without acquiring the mutex that wait
uses to guard them.
The actual problem you are experiencing is due to your use of boost::mutex::scoped_lock
in the callers of future_shared_state::wait
: you successfully avoid locking the mutex twice with adopt_lock
inside future_shared_state::wait
, but both of the scoped_lock
destructors run and unlock the mutex twice.
Both problems are easily fixed by making the locking all internal to future_shared_state
(Demo at Coliru):
namespace detail {
template <typename T>
struct future_shared_state
{
public:
void wait() const
{
boost::mutex::scoped_lock lock(m);
while (!(state || error))
available.wait(lock);
}
T& get()
{
if (state)
return *state;
if (error)
throw *error;
throw std::runtime_error("WTF");
}
template <typename U>
void set_value(const U& value)
{
{
boost::mutex::scoped_lock lock(m);
state = value;
}
available.notify_all();
}
void set_exception(boost::shared_ptr<std::exception> e)
{
{
boost::mutex::scoped_lock lock(m);
error = e;
}
available.notify_all();
}
private:
mutable boost::condition_variable available;
boost::optional<T> state;
boost::shared_ptr<std::exception> error;
mutable boost::mutex m;
};
}
template <typename T>
class promise;
template <typename T>
struct future
{
public:
future() {}
~future() {}
T get()
{
boost::shared_ptr<detail::future_shared_state<T> > old_box;
swap(box, old_box);
old_box->wait();
return old_box->get();
}
bool valid() const
{
return !!box;
}
void wait() const
{
box->wait();
}
private:
boost::shared_ptr<detail::future_shared_state<T> > box;
friend class promise<T>;
future(boost::shared_ptr<detail::future_shared_state<T> > const& box) : box(box) {}
};
template <typename T>
struct promise
{
public:
promise() : box(new detail::future_shared_state<T>) {}
~promise() {}
void swap(promise& other)
{
box.swap(other.box);
}
future<T> get_future()
{
return future<T>(box);
}
void set_value(T const& value)
{
box->set_value(value);
}
void set_exception(boost::shared_ptr<std::exception> e)
{
box->set_exception(e);
}
private:
boost::shared_ptr<detail::future_shared_state<T> > box;
};