• C++ Programming for Financial Engineering
    Highly recommended by thousands of MFE students. Covers essential C++ topics with applications to financial engineering. Learn more Join!
    Python for Finance with Intro to Data Science
    Gain practical understanding of Python to read, understand, and write professional Python code for your first day on the job. Learn more Join!
    An Intuition-Based Options Primer for FE
    Ideal for entry level positions interviews and graduate studies, specializing in options trading arbitrage and options valuation models. Learn more Join!

threading question

Eugene Krel

sunmulA
I am working on a piece of code and part of it is a producer-consumer queue.

The following is my pop method:

C++:
void pop(Data& popped_value)
{
	boost::mutex::scoped_lock lock(m_mutex);

	while (m_queue.empty()) //block while queue is empty
	{
		m_condition_var.wait(lock);
	}

	popped_value = m_queue.front();
	//pop after copying the value to take exceptions into account
	m_queue.pop(); 	
}

The push method notifies.

The calling object destructor calls thread.join() to stop the thread.

The issue is that if the queue is empty it will just sit there waiting for the conditional variable to un-block and won't call the queue's destructor.

Stack trace:
C++:
thread 1:
pthread_cond_wait
boost::thread::join()
foo::~foo()

thread2:
pthread_cond_wait
foo::do_stuff_with_queue()

What is the proper way to deal with that?

The naive solution is to create a pop() method that returns immediately if the queue is empty, but that is not multi-producer/consumer safe.

I am not sure that timed_join() is appropriate either since I can't predict how long it would take for the thread to complete if it's actually doing work.
 
I will give it a try, though I don't clearly get what you are trying to achieve here also I have not used boost threading much.
Typically if you want to implement multi-threaded push and pop, then the consumer thread has to wait and pop on the queue and the producer thread has push and signal. If you don't want to wait then you can do it serially , why do u have to multithread it. Just pop it after the push.
Or u can wait for a specific time using timed wait, then do sth else and come back to the queue later, u wont be blocking the thread then.
Also, I see few issues in the code:
1/ In ur pop method u should unlock the mutex after u pop it from the queue else it might cause deadlock related issues.
2/ From ur stack trace it looks like both are waiting on the conditional variable. The push method should have pthread_cond_signal to signal the waiting thread.

Not sure if the above makes ur problem any easier or makes sense but good luck to you and do let us know if u figure it out. :)
 

Eugene Krel

sunmulA
The queue is just a thread safe wrapper for std::queue, it's not a consumer or producer by itself. It is used elsewhere and can potentially have data coming in and out from several threads (more than 2).

scoped_lock takes care of unlocking, the beauty of boost ;)

The push method does in fact have a notify method. The issue is that the thread that was pushing may already be killed (remember the only issue I have is on shutdown) and the one popping may be trying to die except it can't since the wrapper pop method is blocking via the conditional var. Yes i could use kill -9, but that means certain things won't get cleaned up.

elliot: I am aware that that is the issue, I think you may have misunderstood what I was asking. I will take a look at ACE a bit later.
 
you could check the status of push thread ...before doing the pop and do a forceful notify in thread1 if push has been terminated

void pop(Data& popped_value)
{
boost::mutex::scoped_lock lock(m_mutex);
bool qWasEmpty=false;
while (m_queue.empty()) //block while queue is empty
{
m_condition_var.wait(lock);
qWasEmpty=true;
}
if(/*push thread is killed */ && qWasEmpty )
{
~m_queue(); //call the queue destructor
//handle any exception and return
return;

}
popped_value = m_queue.front();
//pop after copying the value to take exceptions into account
m_queue.pop();
}
 
Top