/*
  This file is part of Valgrind, a dynamic binary instrumentation
  framework.

  Copyright (C) 2008-2008 Google Inc
     opensource@google.com

  This program is free software; you can redistribute it and/or
  modify it under the terms of the GNU General Public License as
  published by the Free Software Foundation; either version 2 of the
  License, or (at your option) any later version.

  This program is distributed in the hope that it will be useful, but
  WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program; if not, write to the Free Software
  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
  02111-1307, USA.

  The GNU General Public License is contained in the file COPYING.
*/

// Author: Konstantin Serebryany <opensource@google.com>
//
// This file contains a set of unit tests for a deadlock detection tool.
//
//
//
// This test can be compiled with pthreads (default) or
// with any other library that supports threads, locks, cond vars, etc.
//
// To compile with pthreads:
//   g++  deadlock_unittest.cc -lpthread -g
//
// To compile with different library:
//   1. cp thread_wrappers_pthread.h thread_wrappers_yourlib.h
//   2. edit thread_wrappers_yourlib.h
//   3. add '-DTHREAD_WRAPPERS="thread_wrappers_yourlib.h"' to your compilation.
//
//

#include <fcntl.h>
#include <queue>
#include <signal.h>
#include <stdlib.h>
#include <string>
#include <vector>

#include "old_test_suite.h"
#include "test_utils.h"

#include <gtest/gtest.h>

/*ProducerConsumerQueue *Q[4] = {
  new ProducerConsumerQueue(INT_MAX),
  new ProducerConsumerQueue(INT_MAX),
  new ProducerConsumerQueue(INT_MAX),
  new ProducerConsumerQueue(INT_MAX)
};
Mutex mu[4];*/

/*
void PutAndWait(int *work_item, int idx) {
  // Put work_item1.
  Q[idx]->Put(work_item);

  // Wait for work_item completion.
  mu[idx].LockWhen(Condition(&ArgIsOne, work_item));
  mu[idx].Unlock();
}

void GetAndServe(int idx) {
  // Get an item.
  int *item = reinterpret_cast<int*>(Q[idx]->Get());

  // Handle work item and signal completion.
  mu[idx].Lock();
  *item = 1;
  mu[idx].Unlock();
}


bool TryGetAndServe(int idx) {
  // Get an item.
  int *item;
  if (Q[idx]->TryGet(reinterpret_cast<void**>(&item))) {
    // Handle work item and signal completion.
    mu[idx].Lock();
    *item = 1;
    mu[idx].Unlock();
    return true;
  } else {
    return false;
  }
}*/

// Set of threads that execute the same function.
class MyThreadSet {
 public:
  typedef void (*F) (void);
  MyThreadSet(F f, int count)
    : count_(count) {
    CHECK(count_ >= 1 && count_ <= 1000);
    ar_ = new MyThread* [count_];
    for (int i = 0; i < count_; i++) {
      ar_[i] = new MyThread(f);
    }
  }
  void Start() {
    for (int i = 0; i < count_; i++) {
      ar_[i]->Start();
    }
  }
  void Join() {
    for (int i = 0; i < count_; i++) {
      ar_[i]->Join();
    }
  }
  ~MyThreadSet() {
    for (int i = 0; i < count_; i++) {
      delete ar_[i];
    }
    delete ar_;
  }

 private:
  MyThread **ar_;
  int count_;
};

int ThreadId() {
  static Mutex mu;
  static map<pthread_t, int> m;

  int id;
  pthread_t self = pthread_self();

  mu.Lock();
  map<pthread_t, int>::iterator it = m.find(self);
  if (it != m.end()) {
    id = it->second;
  } else {
    id = m.size();
    m[self] = id;
  }
  mu.Unlock();
  return id;
}

// test00: {{{1
namespace test00 {
void Run() {
  printf("test00: negative\n");
}
REGISTER_TEST(Run, 00)
}  // namespace test00

// test01: Simple deadlock, 2 threads. {{{1
namespace test01 {
Mutex mu1, mu2;
void Worker1()  {
  mu1.Lock();
  mu2.Lock();
  mu2.Unlock();
  mu1.Unlock();
}
void Worker2()  {
  usleep(1000);
  mu2.Lock();
  mu1.Lock();
  mu1.Unlock();
  mu2.Unlock();
}
void Run() {
  MyThreadArray t(Worker1, Worker2);
  t.Start();
  t.Join();
  printf("test01: positive, simple deadlock\n");
}
REGISTER_TEST(Run, 01)
}  // namespace test01

// test02: Simple deadlock, 4 threads. {{{1
namespace test02 {
Mutex mu1, mu2, mu3, mu4;
void Worker1()  {
  mu1.Lock();   mu2.Lock();
  mu2.Unlock(); mu1.Unlock();
}
void Worker2()  {
  usleep(1000);
  mu2.Lock();   mu3.Lock();
  mu3.Unlock(); mu2.Unlock();
}
void Worker3()  {
  usleep(2000);
  mu3.Lock();   mu4.Lock();
  mu4.Unlock(); mu3.Unlock();
}
void Worker4()  {
  usleep(3000);
  mu4.Lock();   mu1.Lock();
  mu1.Unlock(); mu4.Unlock();
}
void Run() {
  MyThreadArray t(Worker1, Worker2, Worker3, Worker4);
  t.Start();
  t.Join();
  printf("test02: positive, simple deadlock\n");
}
REGISTER_TEST(Run, 02)
}  // namespace test02
/*
// test03: Queue deadlock test, 2 workers. {{{1
// This test will deadlock for sure.
namespace  test03 {

void Worker1() {
  int *item = new int (0);
  PutAndWait(item, 0);
  GetAndServe(1);
}
void Worker2() {
  int *item = new int (0);
  PutAndWait(item, 1);
  GetAndServe(0);
}
void Run() {
  printf("test03: queue deadlock\n");
  MyThreadArray t(Worker1, Worker2);
  t.Start();
  t.Join();
}
REGISTER_TEST(Run, 03)
}  // namespace test03

// test04: Queue deadlock test, 3 workers. {{{1
// This test will deadlock for sure.
namespace  test04 {

void Worker1() {
  int *item = new int (0);
  PutAndWait(item, 0);
  GetAndServe(1);
}
void Worker2() {
  int *item = new int (0);
  PutAndWait(item, 1);
  GetAndServe(2);
}

void Worker3() {
  int *item = new int (0);
  PutAndWait(item, 2);
  GetAndServe(0);
}

void Run() {
  printf("test04: queue deadlock\n");
  MyThreadArray t(Worker1, Worker2, Worker3);
  t.Start();
  t.Join();
}
REGISTER_TEST(Run, 04)
}  // namespace test04

// test05: Queue deadlock test, 1 worker set. {{{1
// This test will deadlock after some number of served requests.
namespace  test05 {

int item_number = 0;  // Just for debug prints.


// This function randomly enqueues work and waits on it or serves a piece of work.
void Worker() {
  while(true) {
    int action = rand() % 100;
    if (action <= 1) {        // PutAndWait.
      int n = __sync_add_and_fetch(&item_number, 1);
      int *item = new int(0);
      PutAndWait(item, 0);
      if ((n % 10000) == 0) {
        printf("Done %d\n", n);
      }
      delete item;
    } else {                 // GetAndServe.
      TryGetAndServe(0);
    }
  }
}


void Run() {
  printf("test05: queue deadlock\n");
  MyThreadSet t(Worker, 5);
  t.Start();
  t.Join();
}
REGISTER_TEST(Run, 05)
}  // namespace test05

// test06: Queue deadlock test, 3 worker sets. {{{1
// This test will deadlock after some number of served requests.
namespace  test06 {

int item_number[3] = {0, 0, 0};  // Just for debug prints.

// This function randomly enqueues work to queue 'put_queue' and waits on it
// or serves a piece of work from queue 'get_queue'.
void Worker(int put_queue, int get_queue) {
  while(true) {
    int action = rand() % 1000;
    if (action <= 100) {        // PutAndWait.
      int n = __sync_add_and_fetch(&item_number[put_queue], 1);
      int *item = new int(0);
      PutAndWait(item, put_queue);
      if ((n % 1000) == 0) {
        printf("Q[%d]: done %d\n", put_queue, n);
      }
      delete item;
    } else {                 // TryGetAndServe.
      TryGetAndServe(get_queue);
    }
  }
}

void Worker1() { Worker(0, 1); }
void Worker2() { Worker(1, 2); }
void Worker3() { Worker(2, 0); }

void Run() {
  printf("test06: queue deadlock\n");
  MyThreadSet t1(Worker1, 4);
  MyThreadSet t2(Worker2, 4);
  MyThreadSet t3(Worker3, 4);
  t1.Start();
  t2.Start();
  t3.Start();
  t1.Join();
  t2.Join();
  t3.Join();
}
REGISTER_TEST(Run, 06)
}  // namespace test06
// test07: Simple deadlock which actually deadlocks, 2 threads. {{{1
namespace test07 {
Mutex mu1, mu2;
void Worker1()  {
  mu1.Lock();
  usleep(100000);
  mu2.Lock();
  mu2.Unlock();
  mu1.Unlock();
}
void Worker2()  {
  mu2.Lock();
  usleep(100000);
  mu1.Lock();
  mu1.Unlock();
  mu2.Unlock();
}
void Run() {
  mu1.EnableDebugLog("mu1");
  mu2.EnableDebugLog("mu2");
  printf("test07: positive, simple deadlock\n");
  MyThreadArray t(Worker1, Worker2);
  t.Start();
  t.Join();
}
REGISTER_TEST(Run, 07)
}  // namespace test07

*/