7

The following code for bounded thread-safe queue used to work as expected in Boost 1.49. However, after updating to Boost 1.54 the code no longer runs as expected. Namely, when the buffer is empty (full), the consumer thread (producer thread) is waiting forever for m_not_empty (m_not_full) conditional variable and never wakes up (I think because the producer thread doesn't have the mutex).

Are there some changes in version 1.54 that could break the code? Or, maybe, there are mistakes in the code that I missed?

#include <iostream>
#include <boost/circular_buffer.hpp>
#include <boost/thread/mutex.hpp>
#include <boost/thread/condition.hpp>
#include <boost/thread/thread.hpp>

template <class T>
class bounded_buffer {
public:
    bounded_buffer(size_t capacity) {cb.set_capacity(capacity);}
    void push(T item) {
        boost::mutex::scoped_lock lock(m_mutex);
        while (cb.full()) {
            m_not_full.wait(lock);
        }
        cb.push_back(item);
        lock.unlock();
        m_not_empty.notify_one();
    }

    void pop(T &pItem) {
        boost::mutex::scoped_lock lock(m_mutex);
        while (cb.empty()) {
            m_not_empty.wait(lock);
        }
        pItem = cb.front();
        cb.pop_front(); 
        lock.unlock();
        m_not_full.notify_one();
    }

private:     
    boost::mutex m_mutex;
    boost::condition m_not_empty;
    boost::condition m_not_full;
    boost::circular_buffer<T> cb;
};

bounded_buffer<int> bb_int(4);

void producer() {
    int i = 10;
    for(int j=0; j<100; ++j) {
        bb_int.push(i);
        std::cout << "producer: " << i << std::endl;
        i++;
    }
}

void consumer() {
    int i;
    for(int j=0; j<100; ++j) {
        bb_int.pop(i);
        std::cout << "consumer: " << i << std::endl;
    }
}

// Test code
int main() {
    // Start the threads.
    boost::thread consume(consumer);
    boost::thread produce(producer);

    // Wait for completion.
    consume.join();
    produce.join();
}
Alexey
  • 5,898
  • 9
  • 44
  • 81
  • Why are you _notifying_ the condition variables outside of the _critical sections_? Have you tried what happens, if you remove both `lock.unlock()` statements? – nosid Oct 09 '13 at 20:11
  • @nosid Nothing changes if I remove `lock.unlock()` statements – Alexey Oct 09 '13 at 20:14
  • It is hanging, right? – Grzegorz Oct 09 '13 at 20:33
  • @Grzegorz Yes, it's hangs (with Boost 1.54) when the queue gets full or empty. – Alexey Oct 09 '13 at 20:35
  • @Alex What platform do you run it on? Windows or Linux? In case it is Linux, and you use g++, can you call gstack on your process, and post back trace with all threads? – Grzegorz Oct 09 '13 at 20:41
  • Can you build debug version, run it in a debugger, and interrupt it pointing to the locations where threads are stuck in? – Grzegorz Oct 09 '13 at 20:43
  • @Grzegorz Thanks for the suggestion. It worked in debug mode, which made me thinking if I linked correctly. I posted the answer. – Alexey Oct 09 '13 at 20:57

1 Answers1

2

Ok, I found a mistake. I compiled the code in release version but linked to debug version of .lib file. Essentially, in release version I linked to boost_thread-vc100-mt-gd-1_54.lib but it should be linked to boost_thread-vc100-mt-1_54.lib.

Alexey
  • 5,898
  • 9
  • 44
  • 81