| /* |
| 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 |
| |
| */ |