The following minimal code sample of a larger program sends commands from client threads to an asio io_service object. The io_service object (in the Ios class) is being run with one thread. When the command is sent the client thread waits until it is notified by the Ios object (via Cmd::NotifyFinish()) that it is completed.
This sample seems to run on Linux Ubuntu 11.04 with boost 1.46 fine but on Windows 7 boost 1.46 it asserts.
I suspect it is something to do with the lock in Cmd::NotifyFinish(). When I move the lock out of the nested scope so that when waitConditionVariable_.notify_one() is called in the lock's scope it doesn't crash on Windows 7. However, the boost::thread documentation states that notify_one() doesn't need to be called within the lock.
The stack trace (below) shows it is asserting when notify_one() is called. It is as though the cmd object has disappeared before notify is called...
How do I make this thread safe and not assert?
#include <boost/asio.hpp>
#include <boost/thread/thread.hpp>
#include <boost/thread/mutex.hpp>
#include <boost/thread/locks.hpp>
#include <boost/thread/condition_variable.hpp>
#include <boost/bind.hpp>
#include <iostream>
class Cmd
{
public:
Cmd() : cnt_(0), waitPred_(false), waiting_(false)
{
}
virtual ~Cmd()
{
}
void BindInfo(int CmdSeq)
{
cnt_ = CmdSeq;
}
void NotifyFinish()
{
// call by service thread...
{
boost::mutex::scoped_lock lock(waitMutex_);
waitPred_ = true;
if (!waiting_)
{
// don't need to notify as isn't waiting
return;
}
}
waitConditionVariable_.notify_one();
}
void Wait()
{
// called by worker threads
boost::mutex::scoped_lock lock(waitMutex_);
waiting_ = true;
while (!waitPred_)
waitConditionVariable_.wait(lock);
}
int cnt_;
private:
boost::mutex waitMutex_;
boost::condition_variable waitConditionVariable_;
bool waitPred_;
bool waiting_;
};
class Ios
{
public:
Ios() : timer_(ios_), cnt_(0), thread_(boost::bind(&Ios::Start, this))
{
}
void Start()
{
timer_.expires_from_now(boost::posix_time::seconds(5));
timer_.async_wait(boost::bind(&Ios::TimerHandler, this, _1));
ios_.run();
}
void RunCmd(Cmd& C)
{
ios_.post(boost::bind(&Ios::RunCmdAsyn, this, boost::ref(C)));
}
private:
void RunCmdAsyn(Cmd& C)
{
C.BindInfo(cnt_++);
C.NotifyFinish();
}
void TimerHandler(const boost::system::error_code& Ec)
{
if (!Ec)
{
std::cout << cnt_ << "\n";
timer_.expires_from_now(boost::posix_time::seconds(5));
timer_.async_wait(boost::bind(&Ios::TimerHandler, this, _1));
}
else
exit(0);
}
boost::asio::io_service ios_;
boost::asio::deadline_timer timer_;
int cnt_;
boost::thread thread_;
};
static Ios ios;
void ThreadFn()
{
while (1)
{
Cmd c;
ios.RunCmd(c);
c.Wait();
//std::cout << c.cnt_ << "\n";
}
}
int main()
{
std::cout << "Starting\n";
boost::thread_group threads;
const int num = 5;
for (int i = 0; i < num; i++)
{
// Worker threads
threads.create_thread(ThreadFn);
}
threads.join_all();
}
stack trace
msvcp100d.dll!std::_Debug_message(const wchar_t * message, const wchar_t * file, unsigned int line) Line 15 C++
iosthread.exe!std::_Vector_const_iterator<std::_Vector_val<boost::intrusive_ptr<boost::detail::basic_cv_list_entry>,std::allocator<boost::intrusive_ptr<boost::detail::basic_cv_list_entry> > > >::_Compat(const std::_Vector_const_iterator<std::_Vector_val<boost::intrusive_ptr<boost::detail::basic_cv_list_entry>,std::allocator<boost::intrusive_ptr<boost::detail::basic_cv_list_entry> > > > & _Right) Line 238 + 0x17 bytes C++
iosthread.exe!std::_Vector_const_iterator<std::_Vector_val<boost::intrusive_ptr<boost::detail::basic_cv_list_entry>,std::allocator<boost::intrusive_ptr<boost::detail::basic_cv_list_entry> > > >::operator==(const std::_Vector_const_iterator<std::_Vector_val<boost::intrusive_ptr<boost::detail::basic_cv_list_entry>,std::allocator<boost::intrusive_ptr<boost::detail::basic_cv_list_entry> > > > & _Right) Line 203 C++
iosthread.exe!std::_Vector_const_iterator<std::_Vector_val<boost::intrusive_ptr<boost::detail::basic_cv_list_entry>,std::allocator<boost::intrusive_ptr<boost::detail::basic_cv_list_entry> > > >::operator!=(const std::_Vector_const_iterator<std::_Vector_val<boost::intrusive_ptr<boost::detail::basic_cv_list_entry>,std::allocator<boost::intrusive_ptr<boost::detail::basic_cv_list_entry> > > > & _Right) Line 208 + 0xc bytes C++
iosthread.exe!std::_Debug_range2<std::_Vector_iterator<std::_Vector_val<boost::intrusive_ptr<boost::detail::basic_cv_list_entry>,std::allocator<boost::intrusive_ptr<boost::detail::basic_cv_list_entry> > > > >(std::_Vector_iterator<std::_Vector_val<boost::intrusive_ptr<boost::detail::basic_cv_list_entry>,std::allocator<boost::intrusive_ptr<boost::detail::basic_cv_list_entry> > > > _First, std::_Vector_iterator<std::_Vector_val<boost::intrusive_ptr<boost::detail::basic_cv_list_entry>,std::allocator<boost::intrusive_ptr<boost::detail::basic_cv_list_entry> > > > _Last, const wchar_t * _File, unsigned int _Line, std::random_access_iterator_tag __formal) Line 715 + 0xc bytes C++
iosthread.exe!std::_Debug_range<std::_Vector_iterator<std::_Vector_val<boost::intrusive_ptr<boost::detail::basic_cv_list_entry>,std::allocator<boost::intrusive_ptr<boost::detail::basic_cv_list_entry> > > > >(std::_Vector_iterator<std::_Vector_val<boost::intrusive_ptr<boost::detail::basic_cv_list_entry>,std::allocator<boost::intrusive_ptr<boost::detail::basic_cv_list_entry> > > > _First, std::_Vector_iterator<std::_Vector_val<boost::intrusive_ptr<boost::detail::basic_cv_list_entry>,std::allocator<boost::intrusive_ptr<boost::detail::basic_cv_list_entry> > > > _Last, const wchar_t * _File, unsigned int _Line) Line 728 + 0x6c bytes C++
iosthread.exe!std::find_if<std::_Vector_iterator<std::_Vector_val<boost::intrusive_ptr<boost::detail::basic_cv_list_entry>,std::allocator<boost::intrusive_ptr<boost::detail::basic_cv_list_entry> > > >,bool (__cdecl*)(boost::intrusive_ptr<boost::detail::basic_cv_list_entry> const &)>(std::_Vector_iterator<std::_Vector_val<boost::intrusive_ptr<boost::detail::basic_cv_list_entry>,std::allocator<boost::intrusive_ptr<boost::detail::basic_cv_list_entry> > > > _First, std::_Vector_iterator<std::_Vector_val<boost::intrusive_ptr<boost::detail::basic_cv_list_entry>,std::allocator<boost::intrusive_ptr<boost::detail::basic_cv_list_entry> > > > _Last, bool (const boost::intrusive_ptr<boost::detail::basic_cv_list_entry> &)* _Pred) Line 92 + 0x54 bytes C++
iosthread.exe!std::remove_if<std::_Vector_iterator<std::_Vector_val<boost::intrusive_ptr<boost::detail::basic_cv_list_entry>,std::allocator<boost::intrusive_ptr<boost::detail::basic_cv_list_entry> > > >,bool (__cdecl*)(boost::intrusive_ptr<boost::detail::basic_cv_list_entry> const &)>(std::_Vector_iterator<std::_Vector_val<boost::intrusive_ptr<boost::detail::basic_cv_list_entry>,std::allocator<boost::intrusive_ptr<boost::detail::basic_cv_list_entry> > > > _First, std::_Vector_iterator<std::_Vector_val<boost::intrusive_ptr<boost::detail::basic_cv_list_entry>,std::allocator<boost::intrusive_ptr<boost::detail::basic_cv_list_entry> > > > _Last, bool (const boost::intrusive_ptr<boost::detail::basic_cv_list_entry> &)* _Pred) Line 1848 + 0x58 bytes C++
iosthread.exe!boost::detail::basic_condition_variable::notify_one() Line 267 + 0xb4 bytes C++
iosthread.exe!Cmd::NotifyFinish() Line 41 C++