/*
 * Thread API.
 *
 * Author: Matthew William Coan
 * Date: Wed Aug  4 20:01:07 EDT 2010
 *
 */

#ifndef _THREAD_H
#define _THREAD_H

#include <pthread.h>
#include <list>

namespace thread_tools {

using namespace std;

extern void * thread_function(void * arg);

class Thread {
   pthread_t _id;

public:
   Thread() { }
   Thread(const Thread & th) { _id = th._id; }
   virtual ~Thread() { }

   Thread & operator=(const Thread & arg) {
      _id = arg._id;
      return *this;
   }

   void start() { 
      pthread_create(&_id, NULL, thread_function, 
                     reinterpret_cast< void* >(this)); 
   } 

   void join() {
      void * arg = 0;
      pthread_join(_id, &arg);
   }

   virtual void run() { }
};

class Mutex {
   pthread_mutex_t _mutex;

public:
   Mutex() { pthread_mutex_init(&_mutex, NULL); }
   Mutex(const Mutex & m) { _mutex = m._mutex; }
   ~Mutex() { pthread_mutex_destroy(&_mutex); }

   Mutex & operator=(const Mutex & arg) { _mutex = arg._mutex; return *this; }

   void lock() { pthread_mutex_lock(&_mutex); }
   void unlock() { pthread_mutex_unlock(&_mutex); }
   pthread_mutex_t * get_mutex() { return &_mutex; }
};

class Condition {
   pthread_cond_t _cond;

public:
   Condition() { pthread_cond_init(&_cond, NULL); }
   Condition(const Condition & cp) { _cond = cp._cond; }
   ~Condition() { pthread_cond_destroy(&_cond); }

   Condition & operator=(const Condition& right) {
      _cond = right._cond;
      return *this;
   }

   void wait(Mutex & mutex) { pthread_cond_wait(&_cond, mutex.get_mutex()); }
   void notify() { pthread_cond_signal(&_cond); }
   void notify_all() { pthread_cond_broadcast(&_cond); }

   pthread_cond_t * get_cond() { return &_cond; }
};

class ReadWriteLock {
   Mutex _mutex;
   Condition _cond;
   size_t _reader;
   size_t _writer;

public:
   ReadWriteLock() { _reader = 0U; _writer = 0U; }
   ReadWriteLock(const ReadWriteLock & cp) { 
   _reader = cp._reader; _writer = cp._writer; _mutex = cp._mutex; _cond = cp._cond; }
   ~ReadWriteLock() { }

   ReadWriteLock & operator=(const ReadWriteLock & rw) {
      _mutex = rw._mutex;
      _cond = rw._cond;
      _reader = rw._reader;
      _writer = rw._writer;
      return *this;
   }

   void read_lock();
   void read_unlock();

   void write_lock();
   void write_unlock();
};

inline
void 
ReadWriteLock::read_lock() 
{ 
   _mutex.lock(); 

   while(_writer)
      _cond.wait(_mutex);

   _reader++; 

   _mutex.unlock(); 
}

inline
void 
ReadWriteLock::read_unlock() 
{
   _mutex.lock();

   _reader--; 

   _cond.notify_all();

   _mutex.unlock(); 
}
   
inline
void 
ReadWriteLock::write_lock() 
{ 
   _mutex.lock(); 

   while(_reader)
      _cond.wait(_mutex);

   _writer++;

   _mutex.unlock();
}

inline
void 
ReadWriteLock::write_unlock() 
{ 
   _mutex.lock();

   _writer--;

   _cond.notify_all();

   _mutex.unlock(); 
}

template< class T > 
class Queue {
public:
   typedef list< T > queue_list_type;
   struct queue_exception { };

private:
   Mutex _mutex;
   Condition _cond;
   queue_list_type _queue;
   bool _is_running;

public:
   Queue() { _is_running = true; }
   Queue(const Queue & cp) { 
      _mutex = cp._mutex;
      _cond = cp._cond;
      _queue = cp._queue;
      _is_running = cp._is_running;
   }
   ~Queue() { }
   Queue & operator=(const Queue & right) {
      _mutex = right._mutex;
      _cond = right._cond;
      _queue = right._quque;
      _is_running = right._is_running;
      return *this;
   }

   void put(const T & arg) {
      _mutex.lock();
      _queue.push_back(arg);
      _cond.notify_all(); 
      _mutex.unlock();
   }

   T get() {
     T temp;
     _mutex.lock();
     while(_queue.size() == 0 && _is_running) {
        _cond.wait(_mutex);
     }
     if(_is_running) {
        temp = _queue.front();
        _queue.pop_front();
     }
     else {
        _mutex.unlock();
        throw queue_exception();
     }
     _mutex.unlock();
     return temp;
   }

   bool is_running() { 
      _mutex.lock();
      bool flag = _is_running;
      _mutex.unlock();
      return flag; 
   }

   void shutdown() {
      _mutex.lock();
      _is_running = false;
      _cond.notify_all();
      _mutex.unlock();
   }
};

}

#endif /* _THREAD_H */
