Boost logo

Boost Users :

From: David Greene (greened_at_[hidden])
Date: 2006-02-09 16:46:23


I have this simple testcase (below) which runs into deadlock (full
run trace after the testcase):

...
[Producer] Producer generated data 28
[Producer] push(): about to lock
[Producer] push(): locked
[Producer] push(): about to unlock
[Producer] Producer generated data 29
[Producer] push(): about to lock
[Producer] push(): locked
[Consumer] Signaled for data
[Consumer] empty(): about to lock
<<DEADLOCK>>

The call to empty() by Consumer is after the first time
it is woken up from full_condition. So the deadlock
occurs right when the consumer first attempts to grab
some data.

Is there something I'm not understanding about how
mutexes and locks work in Boost.Thread? I feel like
I'm missing something obvious. This comes out of a
larger and more complicated program but I was able to
isolate the mysterious (to me) behavior I'm seeing.

I used to use LOG for debugging but I thought there might be
some interaction between the queue mutex locks and the print
mutex locks so I switched to the non-locking LOG_NOLCK but
observed no change in behavior.

Any help is greatly appreciated.

                       -Dave

---------------------------------
Testcase

#include <iostream>
#include <queue>
#include <map>
#include <string>

#include <boost/thread.hpp>
#include <boost/thread/recursive_mutex.hpp>
#include <boost/thread/condition.hpp>
#include <boost/bind.hpp>

#include <pthread.h>

typedef boost::recursive_mutex mutex_type;
typedef mutex_type::scoped_lock lock_type;

// Atomic prints
mutex_type print_mutex;
mutex_type print_map_mutex;

// For print/debug purposes
typedef std::map<pthread_t, std::string> thread_map_type;
thread_map_type thread_map;

template<typename T>
void register_thread_id(const T &entity)
{
   lock_type lock(print_map_mutex);
   std::cout << "Registering " << pthread_self() << " to " << entity.name()
             << std::endl;
   thread_map[pthread_self()] = entity.name();
}

#define LOG(msg) \
{ \
   lock_type lock(print_mutex); \
   std::cout << "[" << thread_map[pthread_self()] \
             << "] " << msg << std::endl; \
}

#define LOG_NOLCK(msg) \
{ \
   std::cout << "[" << thread_map[pthread_self()] \
             << "] " << msg << std::endl; \
}

// Synchronize start (make sure all registered)
mutex_type go_mutex;
boost::condition go_condition;

bool ready = false;

class Consumer {
   private:
      boost::thread thread;

      std::queue<int> queue;

      typedef boost::recursive_mutex mutex_type;
      typedef mutex_type::scoped_lock lock_type;

      mutable mutex_type mutex;
      mutable mutex_type full_mutex;

      mutable boost::condition full_condition;

      void wait_until_ready(void) const {
         ::lock_type lock(go_mutex);

         while(!ready) {
            go_condition.wait(lock);
         }
         LOG_NOLCK("go!");
      };

      void wait_for_data(void) const {
         lock_type lock(full_mutex);
         while(empty()) {
            LOG_NOLCK("Waiting for data");
            full_condition.wait(lock);
            LOG_NOLCK("Signaled for data");
         }
         LOG_NOLCK("Wakeup for data");
      };

      void event_loop(void) {
         register_thread_id(*this);
         wait_until_ready();
        
         while(true) {
            wait_for_data();
            LOG_NOLCK("Consumer received data " << top());
            pop();
         }
      };

   public:
      Consumer(void) : thread(boost::bind(&Consumer::event_loop, this)) {};

      std::string name(void) const { return("Consumer"); };

      void push(int val) {
         LOG_NOLCK("push(): about to lock");
         {
            lock_type lock(mutex);
            LOG_NOLCK("push(): locked");
            queue.push(val);
        
            lock_type full_lock(full_mutex);
            LOG_NOLCK("push(): about to unlock");
         }
         full_condition.notify_all();
      };

      int top(void) const {
         LOG_NOLCK("top(): about to lock");
         lock_type lock(mutex);
         LOG_NOLCK("top(): locked");
         int item = queue.front();
         LOG_NOLCK("top(): about to unlock");
         return(item);
      };

      void pop(void) {
         LOG_NOLCK("pop(): about to lock");
         lock_type lock(mutex);
         LOG_NOLCK("pop(): locked");
         queue.pop();
         LOG_NOLCK("pop(): about to unlock");
      };

      bool empty (void) const {
         LOG_NOLCK("empty(): about to lock");
         lock_type lock(mutex);
         LOG_NOLCK("empty(): locked");
         bool val = queue.empty();
         LOG_NOLCK("empty(): about to unlock");
         return(val);
      };
};

class Producer {
   private:
      boost::thread thread;

      void wait_until_ready(void) const {
         lock_type lock(go_mutex);

         while(!ready) {
            go_condition.wait(lock);
         }
         LOG_NOLCK("go!");
      };

      void event_loop(void) {
         int i = 0;

         register_thread_id(*this);
         wait_until_ready();

         while(true) {
            LOG_NOLCK("Producer generated data " << i);
            consumer->push(i);
            ++i;
         };
      };

      Consumer *consumer;

   public:
      Producer(Consumer *c) : consumer(c),
                              thread(boost::bind(&Producer::event_loop, this)) {};

      std::string name(void) const { return("Producer"); };
};

void go(void)
{
   // Wait until everyone arrives
   lock_type lock(go_mutex);
   ready = true;

   // Notify everyone
   go_condition.notify_all();
}

int main(void)
{
   Consumer c;
   Producer p(&c);

   go();

   while(true);

   return(0);
}

-----------------
Ouput Trace

[Consumer] empty(): about to unlock
[Consumer] Waiting for data
Registering 48958384 to Producer
[Producer] go!
[Producer] Producer generated data 0
[Producer] push(): about to lock
[Producer] push(): locked
[Producer] push(): about to unlock
[Producer] Producer generated data 1
[Producer] push(): about to lock
[Producer] push(): locked
[Producer] push(): about to unlock
[Producer] Producer generated data 2
[Producer] push(): about to lock
[Producer] push(): locked
[Producer] push(): about to unlock
[Producer] Producer generated data 3
[Producer] push(): about to lock
[Producer] push(): locked
[Producer] push(): about to unlock
[Producer] Producer generated data 4
[Producer] push(): about to lock
[Producer] push(): locked
[Producer] push(): about to unlock
[Producer] Producer generated data 5
[Producer] push(): about to lock
[Producer] push(): locked
[Producer] push(): about to unlock
[Producer] Producer generated data 6
[Producer] push(): about to lock
[Producer] push(): locked
[Producer] push(): about to unlock
[Producer] Producer generated data 7
[Producer] push(): about to lock
[Producer] push(): locked
[Producer] push(): about to unlock
[Producer] Producer generated data 8
[Producer] push(): about to lock
[Producer] push(): locked
[Producer] push(): about to unlock
[Producer] Producer generated data 9
[Producer] push(): about to lock
[Producer] push(): locked
[Producer] push(): about to unlock
[Producer] Producer generated data 10
[Producer] push(): about to lock
[Producer] push(): locked
[Producer] push(): about to unlock
[Producer] Producer generated data 11
[Producer] push(): about to lock
[Producer] push(): locked
[Producer] push(): about to unlock
[Producer] Producer generated data 12
[Producer] push(): about to lock
[Producer] push(): locked
[Producer] push(): about to unlock
[Producer] Producer generated data 13
[Producer] push(): about to lock
[Producer] push(): locked
[Producer] push(): about to unlock
[Producer] Producer generated data 14
[Producer] push(): about to lock
[Producer] push(): locked
[Producer] push(): about to unlock
[Producer] Producer generated data 15
[Producer] push(): about to lock
[Producer] push(): locked
[Producer] push(): about to unlock
[Producer] Producer generated data 16
[Producer] push(): about to lock
[Producer] push(): locked
[Producer] push(): about to unlock
[Producer] Producer generated data 17
[Producer] push(): about to lock
[Producer] push(): locked
[Producer] push(): about to unlock
[Producer] Producer generated data 18
[Producer] push(): about to lock
[Producer] push(): locked
[Producer] push(): about to unlock
[Producer] Producer generated data 19
[Producer] push(): about to lock
[Producer] push(): locked
[Producer] push(): about to unlock
[Producer] Producer generated data 20
[Producer] push(): about to lock
[Producer] push(): locked
[Producer] push(): about to unlock
[Producer] Producer generated data 21
[Producer] push(): about to lock
[Producer] push(): locked
[Producer] push(): about to unlock
[Producer] Producer generated data 22
[Producer] push(): about to lock
[Producer] push(): locked
[Producer] push(): about to unlock
[Producer] Producer generated data 23
[Producer] push(): about to lock
[Producer] push(): locked
[Producer] push(): about to unlock
[Producer] Producer generated data 24
[Producer] push(): about to lock
[Producer] push(): locked
[Producer] push(): about to unlock
[Producer] Producer generated data 25
[Producer] push(): about to lock
[Producer] push(): locked
[Producer] push(): about to unlock
[Producer] Producer generated data 26
[Producer] push(): about to lock
[Producer] push(): locked
[Producer] push(): about to unlock
[Producer] Producer generated data 27
[Producer] push(): about to lock
[Producer] push(): locked
[Producer] push(): about to unlock
[Producer] Producer generated data 28
[Producer] push(): about to lock
[Producer] push(): locked
[Producer] push(): about to unlock
[Producer] Producer generated data 29
[Producer] push(): about to lock
[Producer] push(): locked
[Consumer] Signaled for data
[Consumer] empty(): about to lock
<<DEADLOCK>>


Boost-users list run by williamkempf at hotmail.com, kalb at libertysoft.com, bjorn.karlsson at readsoft.com, gregod at cs.rpi.edu, wekempf at cox.net