blob: 49c3fe9b3b9d8bc2c3a69df43f3790e407c14bf1 [file] [log] [blame]
/*
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
*/