I had a similar problem and I show you how I solved it.
The logic a little bit (not sure if you like it, but I find this way to
usually be more maintainable and scalable) .
Basically the main thread calls Log that pushes the string into a queue.
The access to the queue is synchronized by the queue_mutex.
There are 2 additional mutexes:
· work_mutex makes Run wait until the queue is *not* empty.
· empty_mutex makes WaitForQueueFlush wait until the queue *is*
empty.
Maybe you can achieve the same combining those two mutexes but I dont have
time to try that right now.
Hope it can help,
Benedetto
This is an (untested) code snippet.
#include
#include
#include
#include
#include
#include <queue>
#include <fstream>
#include <string>
class FileLogger
{
typedef boost::unique_lockboost::mutex unique_lock;
std::ofstream logfile;
bool running;
// this controls the actual push/pop in the queue
boost::mutex queue_mutex;
// this mutex will be "unlocked" when the queue is empty.
// WaitForQueueFlush will wait until it is unlocked and then lock it.
// It will be unlocked when eventually the queue becomes empty again.
boost::mutex empty_mutex;
// this mutex will be "locked" when the queue is empty.
// the "Run" method will wait on it until some element is pushed into the
queue
// and this mutex becomes available.
boost::mutex work_mutex;
// queue seems to be more appropriate data structure than vector
std::queuestd::string queue;
// this saves the current thread... just in case you need it
boost::thread * mythread;
public:
FileLogger(const std::string& filename)
: logfile(filename.c_str())
, running(true)
{
work_mutex.lock();
}
~FileLogger()
{
running = false;
}
// this runs in the main thread
void Log(const std::string& s)
{
unique_lock lock(queue_mutex);
queue.push(s);
if (!work_mutex.try_lock()) {
work_mutex.unlock();
}
if (empty_mutex.try_lock()) {
empty_mutex.unlock();
}
}
void Log_impl(const std::string& s)
{
unique_lock lock(queue_mutex);
logfile << s << '\n';
}
void WaitForQueueFlush()
{
empty_mutex.lock();
}
void start()
{
mythread = new boost::thread(boost::bind(&FileLogger::Run, this));
}
private:
void Run()
{
while( running ) {
work_mutex.lock();
unique_lock lock(queue_mutex);
std::string s = queue.pop();
Log_impl(s);
if (queue.empty())
{
empty_mutex.unlock();
}
}
}
};
int main()
{
FileLogger fileLogger("logfile.txt");
fileLogger.Log(std::string(1000*1000, '*'));
// this call should wait, synchronously, until
// current queue has been flushed to disk
fileLogger.WaitForQueueFlush();
}
-----Original Message-----
From: boost-users-bounces@lists.boost.org
[mailto:boost-users-bounces@lists.boost.org] On Behalf Of Daniel Lidström
Sent: Friday, March 05, 2010 7:22 AM
To: boost-users@lists.boost.org
Subject: [Boost-users] Waiting for a thread on another thread
Hello!
I have created a simple file logger using the active object pattern. This is
intended to
offload logging operations to a separate thread, to not disturb the main
thread with time-
consuming file operations. It works very nicely with boost::thread, but now
I have a new
desired functionality. The file logger keeps an internal queue of strings
that need to be
written to file. I want to know, from the main thread, when this queue is
empty. To illustrate
I have created a minimal, hopefully compilable, sample that shows what I am
trying to achieve.
#include
#include
#include
#include
#include
#include <vector>
#include <fstream>
#include <string>
class FileLogger
{
typedef boost::unique_lockboost::mutex unique_lock;
std::ofstream logfile;
bool running;
boost::thread running_thread;
boost::mutex mutex;
boost::condition_variable condition;
std::vectorstd::string queue;
public:
FileLogger(const std::string& filename)
: logfile(filename.c_str())
, running(true)
, running_thread(std::tr1::bind(&FileLogger::Run, this))
{ }
~FileLogger()
{
running = false;
condition.notify_one();
running_thread.join();
}
void Log(const std::string& s)
{
unique_lock lock(mutex);
queue.push_back(s);
}
void WaitForQueueFlush()
{
// ??
}
private:
void Run()
{
while( running ) {
std::vectorstd::string copyList;
{
// wait 0.1 s here, for queue to fill up
boost::xtime sleepTime;
boost::xtime_get(&sleepTime, boost::TIME_UTC);
sleepTime.nsec += 100*1000*1000;
unique_lock lock(mutex);
condition.timed_wait(lock, sleepTime);
// fetch the current message list
copyList.swap(queue);
}
BOOST_FOREACH(const std::string& s, copyList) {
logfile << s << '\n';
}
logfile.flush();
}
}
};
int main()
{
FileLogger fileLogger("logfile.txt");
fileLogger.Log(std::string(1000*1000, '*'));
// this call should wait, synchronously, until
// current queue has been flushed to disk
fileLogger.WaitForQueueFlush();
}
So the problem is to implement FileLogger::WaitForQueueFlush. How can I
signal the main thread when the queue has been emptied?
Thanks in advance!
Regards,
Daniel Lidström
Stockholm, Sweden
_______________________________________________
Boost-users mailing list
Boost-users@lists.boost.org
http://lists.boost.org/mailman/listinfo.cgi/boost-users