I need some mechanism reminiscent of Win32 reset events that I can check via functions having the same semantics with WaitForSingleObject() and WaitForMultipleObjects() (Only need the ..SingleObject() version for the moment) . But I am targeting multiple platforms so all I have is boost::threads (AFAIK) . I came up with the following class and wanted to ask about the potential problems and whether it is up to the task or not. Thanks in advance.
class reset_event
{
bool flag, auto_reset;
boost::condition_variable cond_var;
boost::mutex mx_flag;
public:
reset_event(bool _auto_reset = false) : flag(false), auto_reset(_auto_reset)
{
}
void wait()
{
boost::unique_lock<boost::mutex> LOCK(mx_flag);
if (flag)
return;
cond_var.wait(LOCK);
if (auto_reset)
flag = false;
}
bool wait(const boost::posix_time::time_duration& dur)
{
boost::unique_lock<boost::mutex> LOCK(mx_flag);
bool ret = cond_var.timed_wait(LOCK, dur) || flag;
if (auto_reset && ret)
flag = false;
return ret;
}
void set()
{
boost::lock_guard<boost::mutex> LOCK(mx_flag);
flag = true;
cond_var.notify_all();
}
void reset()
{
boost::lock_guard<boost::mutex> LOCK(mx_flag);
flag = false;
}
};
Example usage;
reset_event terminate_thread;
void fn_thread()
{
while(!terminate_thread.wait(boost::posix_time::milliseconds(10)))
{
std::cout << "working..." << std::endl;
boost::this_thread::sleep(boost::posix_time::milliseconds(1000));
}
std::cout << "thread terminated" << std::endl;
}
int main()
{
boost::thread worker(fn_thread);
boost::this_thread::sleep(boost::posix_time::seconds(1));
terminate_thread.set();
worker.join();
return 0;
}
EDIT
I have fixed the code according to Michael Burr's suggestions. My "very simple" tests indicate no problems.
class reset_event
{
bool flag, auto_reset;
boost::condition_variable cond_var;
boost::mutex mx_flag;
public:
explicit reset_event(bool _auto_reset = false) : flag(false), auto_reset(_auto_reset)
{
}
void wait()
{
boost::unique_lock<boost::mutex> LOCK(mx_flag);
if (flag)
{
if (auto_reset)
flag = false;
return;
}
do
{
cond_var.wait(LOCK);
} while(!flag);
if (auto_reset)
flag = false;
}
bool wait(const boost::posix_time::time_duration& dur)
{
boost::unique_lock<boost::mutex> LOCK(mx_flag);
if (flag)
{
if (auto_reset)
flag = false;
return true;
}
bool ret = cond_var.timed_wait(LOCK, dur);
if (ret && flag)
{
if (auto_reset)
flag = false;
return true;
}
return false;
}
void set()
{
boost::lock_guard<boost::mutex> LOCK(mx_flag);
flag = true;
cond_var.notify_all();
}
void reset()
{
boost::lock_guard<boost::mutex> LOCK(mx_flag);
flag = false;
}
};
A few things that you'll want to check/fix (note - I'm by no means saying that these are the only things - I've only had a quick look):
in your wait()
function, you don't reset on an already signaled event if it's set up for auto_reset
:
void wait()
{
boost::unique_lock<boost::mutex> LOCK(mx_flag);
if (flag) {
if (auto_reset) flag = false; // <-- I think you need this
return;
}
cond_var.wait(LOCK);
if (auto_reset)
flag = false;
}
in wait(const boost::posix_time::time_duration& dur)
you should check flag
before waiting on the condition variable.
in both wait
functions, if you wait on the condition variable, you might need to recheck the flag to make sure that some other thread hasn't reset the event in the meantime. This is particularly true for auto_reset events which should only free up a single waiter even when multiple threads are waiting on the event.