summaryrefslogtreecommitdiff
path: root/ACE/netsvcs/clients/Tokens/deadlock
diff options
context:
space:
mode:
Diffstat (limited to 'ACE/netsvcs/clients/Tokens/deadlock')
-rw-r--r--ACE/netsvcs/clients/Tokens/deadlock/Makefile.am19
-rw-r--r--ACE/netsvcs/clients/Tokens/deadlock/README98
-rw-r--r--ACE/netsvcs/clients/Tokens/deadlock/deadlock_detection_test.cpp341
3 files changed, 458 insertions, 0 deletions
diff --git a/ACE/netsvcs/clients/Tokens/deadlock/Makefile.am b/ACE/netsvcs/clients/Tokens/deadlock/Makefile.am
new file mode 100644
index 00000000000..5e25ccd0fb3
--- /dev/null
+++ b/ACE/netsvcs/clients/Tokens/deadlock/Makefile.am
@@ -0,0 +1,19 @@
+##----------------------------------------------------------------------------
+## $Id$
+##
+## Makefile for repeating token client application
+##----------------------------------------------------------------------------
+
+##
+## Process this file with automake to create Makefile.in
+##
+
+AM_CPPFLAGS = -I$(top_builddir) -I$(top_srcdir)
+
+noinst_PROGRAMS = \
+ deadlock_detection_test
+
+deadlock_detection_test_SOURCES = deadlock_detection_test.cpp
+deadlock_detection_test_LDADD = \
+ $(top_builddir)/netsvcs/lib/libnetsvcs.la \
+ $(top_builddir)/ace/$(ACE_BUILDDIR)/ace/libACE.la
diff --git a/ACE/netsvcs/clients/Tokens/deadlock/README b/ACE/netsvcs/clients/Tokens/deadlock/README
new file mode 100644
index 00000000000..74fffde05cd
--- /dev/null
+++ b/ACE/netsvcs/clients/Tokens/deadlock/README
@@ -0,0 +1,98 @@
+
+deadlock_detection_test
+
+This example contains two deadlock tests, mutex and rwlock tests.
+% ./deadlock_detection_test -u
+./deadlock_detection_test:
+[-r test readers/writer locks]
+[-n <iterations>]
+[-h <remote host>]
+[-p <remote port>]
+[-i ignore deadlock]
+
+For both mutex and rwlock tests, -h and -p specify to use remote
+mutexes. -i specifies to ignore deadlock. -n is repetitions through
+the respective algorithms (default 100). Both tests also use Token
+Invariants to ensure correctness of the mutexes and readers/writer
+locks.
+
+------------------------------------------------------------
+
+If you run ./deadlock_detection_test without -r, then the following
+mutex test is run.
+
+The mutex test spawns two threads which attempt to deadlock.
+Logically, there are two tokens A and B. Here is what both threads
+try to do:
+
+Thread 1 Thread 2
+-------- --------
+Repeat 100 times Repeat 100 times
+ acquire A acquire B
+ acquire B acquire A
+ release A and B release A and B
+repeat repeat
+
+Notice that A and B are reversed in 1 and 2. If the token manager
+(which is not in the public interface, but hidden behind
+ACE_Local_Mutex) works properly, they should detect the deadlock. If
+a thread detects deadlock, the resources held are released, and it
+starts the whole process over again.
+
+What can be confusing about the test program is all the other tricks
+I'm pulling to test other aspects of the library. For instance, I'm
+using both "global" and "local" ACE_Local_Mutexes. This is to test
+the ability to have multiple threads using one token proxy as well as
+multiple threads each using their own proxies. All the while, the
+same logical token is being used. If this doesn't make sense, don't
+worry about it. Just use the ACE_Local_Mutex any way you want.
+
+Another confusing trick is that I'm testing recursive acquisition.
+(Two acquires in a row.) I have to make sure that the token manager
+doesn't detect a recursive acquire as deadlock.
+
+To run a test, simply type:
+% ./deadlock_detection_test
+
+This should run 100 times through the above pseudo code. If the
+application halts, then we have trouble. It should never ever halt.
+I've included a little flag with the ACE_Local_Mutex class to allow
+deadlock detection to be ignored. So, if you run the test as follows,
+deadlock detection will be ignored.
+
+% ./deadlock_detection_test -i
+
+In this case, the application should only run about a second before
+deadlock occurs and the application halts. This is good.
+
+------------------------------------------------------------
+
+If you run ./deadlock_detection_test *with* -r, then the following
+rwlock test is run:
+
+There are four tokens and four threads in the rwlock test. The
+readers/writer tokens are:
+
+reader first
+writer first 1
+writer first 2
+writer first 3
+
+There are three reader threads that only acquire reader locks on the
+above tokens. Each of the reader threads first acquire "reader first"
+and then one "writer first <tid>" (where <tid> is the corresponding
+thread's id). So reader thread 1 acquires "reader first" and then
+"writer first 1".
+
+There is a single writer thread that uses the following algorithm:
+
+repeat 100
+ acquire "writer first 1"
+ acquire "reader first"
+ acquire "writer first 2"
+ acquire "reader first"
+ acquire "writer first 3"
+ acquire "reader first"
+
+This strange mix of readers and writer create an interesting graph of
+tokens that the deadlock detection algorithm must traverse.
diff --git a/ACE/netsvcs/clients/Tokens/deadlock/deadlock_detection_test.cpp b/ACE/netsvcs/clients/Tokens/deadlock/deadlock_detection_test.cpp
new file mode 100644
index 00000000000..3a1a13195d4
--- /dev/null
+++ b/ACE/netsvcs/clients/Tokens/deadlock/deadlock_detection_test.cpp
@@ -0,0 +1,341 @@
+// $Id$
+
+// ============================================================================
+//
+// = LIBRARY
+// examples
+//
+// = FILENAME
+// deadlock_detection_test.cpp
+//
+// = DESCRIPTION
+//
+// = AUTHOR
+// Tim Harrison
+//
+// ============================================================================
+
+#include "ace/Token_Manager.h"
+#include "ace/Remote_Tokens.h"
+#include "ace/Thread.h"
+#include "ace/Thread_Manager.h"
+#include "ace/Get_Opt.h"
+#include "ace/Token_Invariants.h"
+
+#if defined (ACE_HAS_THREADS) && defined (ACE_HAS_THREADS_LIBRARY)
+
+ACE_RCSID(deadlock, deadlock_detection_test, "$Id$")
+
+typedef ACE_Token_Invariant_Manager ACE_TOKEN_INVARIANTS;
+
+static ACE_Token_Proxy *global_mutex;
+
+struct Two_Tokens
+{
+public:
+ Two_Tokens (ACE_Thread_Manager *tm): thr_mgr_ (tm) {}
+ ACE_Token_Proxy *first_;
+ ACE_Token_Proxy *second_;
+ ACE_Thread_Manager *thr_mgr_;
+};
+
+struct Four_Tokens
+{
+public:
+ Four_Tokens (ACE_Thread_Manager *tm): thr_mgr_ (tm) {}
+ ACE_Token_Proxy *first1_;
+ ACE_Token_Proxy *first2_;
+ ACE_Token_Proxy *first3_;
+ ACE_Token_Proxy *second_;
+ ACE_Thread_Manager *thr_mgr_;
+};
+
+static int ignore_deadlock = 0;
+static int remote_mutexes = 0;
+static const char *server_host = ACE_DEFAULT_SERVER_HOST;
+static int server_port = ACE_DEFAULT_SERVER_PORT;
+static int iterations = 100;
+static int rwlocks = 0;
+
+static void *
+two_token_thread (void *vp)
+{
+ Two_Tokens* tm = (Two_Tokens *) vp;
+
+ for (int x = 0; x < iterations; x++)
+ {
+ if (tm->first_->acquire () == -1)
+ {
+ ACE_DEBUG ((LM_DEBUG, "Deadlock detected\n"));
+ continue;
+ }
+
+ if (ACE_TOKEN_INVARIANTS::instance ()->acquired (tm->first_) == 0)
+ {
+ tm->first_->dump ();
+ ACE_ERROR_RETURN ((LM_ERROR, "violated invariant.\n"), 0);
+ }
+
+ if (tm->second_->acquire () == -1)
+ {
+ ACE_DEBUG ((LM_DEBUG, "Deadlock Detected\n"));
+ goto G1;
+ }
+
+ if (ACE_TOKEN_INVARIANTS::instance ()->acquired (tm->second_) == 0)
+ {
+ tm->second_->dump ();
+ ACE_ERROR_RETURN ((LM_ERROR, "violated invariant.\n"), 0);
+ }
+
+ ACE_TOKEN_INVARIANTS::instance ()->releasing (tm->second_);
+
+ tm->second_->release ();
+ G1:
+ ACE_TOKEN_INVARIANTS::instance ()->releasing (tm->first_);
+
+ tm->first_->release ();
+ }
+
+ ACE_DEBUG ((LM_DEBUG, "thread %t exiting\n"));
+ return 0;
+}
+
+static void *
+run_writer (void *vp)
+{
+ Four_Tokens *ft = (Four_Tokens *) vp;
+ int acquire_number = 0;
+
+ for (int x = 0; x < iterations; x++)
+ {
+ // Cycle through each of the first three tokens.
+ ACE_Token_Proxy *t = 0;
+ switch (acquire_number)
+ {
+ case 0:
+ t = ft->first1_;
+ break;
+ case 1:
+ t = ft->first2_;
+ break;
+ case 2:
+ t = ft->first3_;
+ break;
+ }
+
+ acquire_number = (acquire_number + 1) % 3;
+
+ if (t->acquire () == -1)
+ {
+ ACE_ASSERT (errno == EDEADLK);
+ ACE_DEBUG ((LM_DEBUG, "Deadlock detected.\n"));
+ continue;
+ }
+
+ if (ACE_TOKEN_INVARIANTS::instance ()->acquired (t) == 0)
+ {
+ t->dump ();
+ ACE_ERROR_RETURN ((LM_ERROR, "violated invariant.\n"), 0);
+ }
+
+ if (ft->second_->acquire () == -1)
+ {
+ ACE_ASSERT (errno == EDEADLK);
+ ACE_DEBUG ((LM_DEBUG, "Deadlock Detected..\n"));
+ goto G1;
+ }
+
+ if (ACE_TOKEN_INVARIANTS::instance ()->acquired (ft->second_) == 0)
+ {
+ ft->second_->dump ();
+ ACE_ERROR_RETURN ((LM_ERROR, "violated invariant.\n"), 0);
+ }
+
+ ACE_TOKEN_INVARIANTS::instance ()->releasing (ft->second_);
+
+ ft->second_->release ();
+
+ G1:
+ ACE_TOKEN_INVARIANTS::instance ()->releasing (t);
+
+ t->release ();
+ }
+
+ ACE_DEBUG ((LM_DEBUG, "thread %t exiting\n"));
+ return 0;
+}
+
+static int
+parse_args (int argc, char *argv[])
+{
+ ACE_LOG_MSG->open (argv[0]);
+
+ ACE_Get_Opt get_opt (argc, argv, "iuh:rp:n:", 1);
+
+ for (int c; (c = get_opt ()) != -1; )
+ {
+ switch (c)
+ {
+ case 'r':
+ rwlocks = 1;
+ break;
+ case 'i':
+ ignore_deadlock = 1;
+ break;
+ case 'h':
+ server_host = get_opt.opt_arg ();
+ remote_mutexes = 1;
+ break;
+ case 'p':
+ server_port = ACE_OS::atoi (get_opt.opt_arg ());
+ remote_mutexes = 1;
+ break;
+ case 'n':
+ iterations = ACE_OS::atoi (get_opt.opt_arg ());
+ break;
+ case 'u':
+ default:
+ ACE_ERROR_RETURN ((LM_ERROR,
+ "%n:\n"
+ "[-r test readers/writer locks]\n"
+ "[-n <iterations>]\n"
+ "[-h <remote host>]\n"
+ "[-p <remote port>]\n"
+ "[-i ignore deadlock]\n%a", 1), -1);
+ }
+ }
+
+ return 0;
+}
+
+int
+mutex_test (void)
+{
+ ACE_Thread_Manager thr_mgr;
+
+ Two_Tokens one (&thr_mgr);
+ Two_Tokens two (&thr_mgr);
+
+ if (remote_mutexes == 0)
+ {
+ global_mutex = new ACE_Local_Mutex ("global proxy", ignore_deadlock, 1);
+ one.first_ = new ACE_Local_Mutex ("local proxy", ignore_deadlock, 1);
+ two.second_ = new ACE_Local_Mutex ("local proxy", ignore_deadlock, 1);
+ }
+ else
+ {
+ ACE_Remote_Mutex::set_server_address (ACE_INET_Addr (server_port, server_host));
+ global_mutex = new ACE_Remote_Mutex ("global proxy", ignore_deadlock, 1);
+ one.first_ = new ACE_Remote_Mutex ("local proxy", ignore_deadlock, 1);
+ two.second_ = new ACE_Remote_Mutex ("local proxy", ignore_deadlock, 1);
+ }
+
+ one.second_ = global_mutex;
+ two.first_ = global_mutex;
+
+ // Tell the token manager to be verbose when reporting deadlock.
+ ACE_Token_Manager::instance ()->debug (1);
+
+ if (thr_mgr.spawn (ACE_THR_FUNC (two_token_thread),
+ (void *) &one, THR_BOUND) == -1)
+ ACE_ERROR_RETURN ((LM_DEBUG, "%p\n", "first spawn"), -1);
+
+ if (thr_mgr.spawn (ACE_THR_FUNC (two_token_thread),
+ (void *) &two, THR_BOUND) == -1)
+ ACE_ERROR_RETURN ((LM_DEBUG, "%p\n", "second spawn"), -1);
+
+ // Wait for all threads to exit.
+ thr_mgr.wait ();
+
+ return 0;
+}
+
+static int
+rwlock_test (void)
+{
+ ACE_Thread_Manager thr_mgr;
+
+ Two_Tokens reader1 (&thr_mgr);
+ Two_Tokens reader2 (&thr_mgr);
+ Two_Tokens reader3 (&thr_mgr);
+ Four_Tokens writer (&thr_mgr);
+
+ if (remote_mutexes == 0)
+ {
+ reader1.first_ = new ACE_Local_RLock ("reader first", ignore_deadlock, 1);
+ reader1.second_ = new ACE_Local_RLock ("writer first 1", ignore_deadlock, 1);
+ reader2.first_ = new ACE_Local_RLock ("reader first", ignore_deadlock, 1);
+ reader2.second_ = new ACE_Local_RLock ("writer first 2", ignore_deadlock, 1);
+ reader3.first_ = new ACE_Local_RLock ("reader first", ignore_deadlock, 1);
+ reader3.second_ = new ACE_Local_RLock ("writer first 3", ignore_deadlock, 1);
+
+ writer.first1_ = new ACE_Local_WLock ("writer first 1", ignore_deadlock, 1);
+ writer.first2_ = new ACE_Local_WLock ("writer first 2", ignore_deadlock, 1);
+ writer.first3_ = new ACE_Local_WLock ("writer first 3", ignore_deadlock, 1);
+ writer.second_ = new ACE_Local_WLock ("reader first", ignore_deadlock, 1);
+ }
+ else
+ {
+ ACE_Remote_Mutex::set_server_address (ACE_INET_Addr (server_port, server_host));
+
+ reader1.first_ = new ACE_Remote_RLock ("writer first 1", ignore_deadlock, 1);
+ reader1.second_ = new ACE_Remote_RLock ("reader first", ignore_deadlock, 1);
+ reader2.first_ = new ACE_Remote_RLock ("writer first 2", ignore_deadlock, 1);
+ reader2.second_ = new ACE_Remote_RLock ("reader first", ignore_deadlock, 1);
+ reader3.first_ = new ACE_Remote_RLock ("writer first 3", ignore_deadlock, 1);
+ reader3.second_ = new ACE_Remote_RLock ("reader first", ignore_deadlock, 1);
+
+ writer.first1_ = new ACE_Remote_WLock ("writer first 1", ignore_deadlock, 1);
+ writer.first2_ = new ACE_Remote_WLock ("writer first 2", ignore_deadlock, 1);
+ writer.first3_ = new ACE_Remote_WLock ("writer first 3", ignore_deadlock, 1);
+ writer.second_ = new ACE_Remote_WLock ("reader first", ignore_deadlock, 1);
+ }
+
+ // Tell the token manager to be verbose when reporting deadlock.
+ ACE_Token_Manager::instance ()->debug (1);
+
+ if (thr_mgr.spawn (ACE_THR_FUNC (two_token_thread),
+ (void *) &reader1, THR_BOUND) == -1)
+ ACE_ERROR_RETURN ((LM_DEBUG, "%p\n", "first spawn"), -1);
+
+ if (thr_mgr.spawn (ACE_THR_FUNC (two_token_thread),
+ (void *) &reader2, THR_BOUND) == -1)
+ ACE_ERROR_RETURN ((LM_DEBUG, "%p\n", "first spawn"), -1);
+
+ if (thr_mgr.spawn (ACE_THR_FUNC (two_token_thread),
+ (void *) &reader3, THR_BOUND) == -1)
+ ACE_ERROR_RETURN ((LM_DEBUG, "%p\n", "first spawn"), -1);
+
+ if (thr_mgr.spawn (ACE_THR_FUNC (run_writer),
+ (void *) &writer, THR_BOUND) == -1)
+ ACE_ERROR_RETURN ((LM_DEBUG, "%p\n", "second spawn"), -1);
+
+ // Wait for all threads to exit.
+ thr_mgr.wait ();
+
+ return 0;
+}
+
+int
+ACE_TMAIN(int argc, ACE_TCHAR *argv[])
+{
+ if (parse_args (argc, argv) == -1)
+ return -1;
+
+ if (rwlocks)
+ rwlock_test ();
+ else
+ mutex_test ();
+
+ ACE_DEBUG ((LM_DEBUG, "test exiting.\n"));
+ return 0;
+}
+#else
+int
+ACE_TMAIN(int, ACE_TCHAR *[])
+{
+ ACE_ERROR_RETURN ((LM_ERROR,
+ "threads not supported on this platform\n"), -1);
+}
+#endif /* ACE_HAS_THREADS */