#include <stdio.h>
#include <birnet/birnetthreadxx.hh>

using namespace Birnet;

struct SimpleLocker {
  union {
    Mutex    *mutex;
    RecMutex *rec_mutex;
  } mtx;

  const bool recursive;

public:
  SimpleLocker (Mutex& m) : recursive (false)	    { mtx.mutex = &m; relock(); }
  SimpleLocker (RecMutex& m) : recursive (true)	    { mtx.rec_mutex = &m; relock(); }
  void relock() const				    { if (recursive) mtx.rec_mutex->lock(); else mtx.mutex->lock(); }
  void unlock() const				    { if (recursive) mtx.rec_mutex->unlock(); else mtx.mutex->unlock(); }
  ~SimpleLocker()				    { unlock(); }
};

class MagicLocker {
   struct Lockable {
     virtual void lock() = 0;
     virtual void unlock() = 0;
   };
   template<class L>
   struct Wrapper : public virtual Lockable {
     L &l;
     explicit     Wrapper  (L &o) : l (o)  {}
     virtual void lock     ()              { l.lock(); }
     virtual void unlock   ()              { l.unlock(); }
   };
   Lockable &l;
public:
   template<class C>
   MagicLocker (C &c) :
     l (*new Wrapper<C> (c))
   {
     lock();
   }
   ~MagicLocker()
   {
     unlock();
     delete &l;
   }
   void lock     ()  { l.lock(); }
   void unlock   ()  { l.unlock(); }
};

const int REPEAT = 30000000;

void
test_lock_unlock()
{
  Mutex mutex;
  RecMutex rec_mutex;
  for (int i = 0; i < 30000000; i++)
    {
      mutex.lock();
      rec_mutex.lock();
      rec_mutex.unlock();
      mutex.unlock();
    }
}

void
test_simple_locker()
{
  Mutex mutex;
  RecMutex rec_mutex;
  for (int i = 0; i < 30000000; i++)
    {
      SimpleLocker l1 (mutex), l2 (rec_mutex);
    }
}

void
test_magic_locker()
{
  Mutex mutex;
  RecMutex rec_mutex;
  for (int i = 0; i < 30000000; i++)
    {
      MagicLocker l1 (mutex), l2 (rec_mutex);
    }
}

int
main (int argc, char **argv)
{
  birnet_init (&argc, &argv, "autolocker");
  if (argc == 2)
    {
      if (strcmp (argv[1], "lock_unlock") == 0)
	{
	  test_lock_unlock();
	  return 0;
	}
      if (strcmp (argv[1], "simple_locker") == 0)
	{
	  test_simple_locker();
	  return 0;
	}
      if (strcmp (argv[1], "magic_locker") == 0)
	{
	  test_magic_locker();
	  return 0;
	}
    }
  printf ("autolocker lock_unlock|simple_locker|magic_locker\n");
}
