diff options
Diffstat (limited to 'ACE/netsvcs/clients/Tokens')
21 files changed, 2131 insertions, 0 deletions
diff --git a/ACE/netsvcs/clients/Tokens/Makefile.am b/ACE/netsvcs/clients/Tokens/Makefile.am new file mode 100644 index 00000000000..37d4b007538 --- /dev/null +++ b/ACE/netsvcs/clients/Tokens/Makefile.am @@ -0,0 +1,17 @@ +##---------------------------------------------------------------------------- +## $Id$ +## +## Makefile for the Token tests +##---------------------------------------------------------------------------- + +## +## Process this file with automake to create Makefile.in +## + +SUBDIRS = \ + collection \ + deadlock \ + invariant \ + manual \ + mutex \ + rw_lock diff --git a/ACE/netsvcs/clients/Tokens/README b/ACE/netsvcs/clients/Tokens/README new file mode 100644 index 00000000000..3b6313a1df7 --- /dev/null +++ b/ACE/netsvcs/clients/Tokens/README @@ -0,0 +1,34 @@ +This directory contains a set of tests for the ACE Tokens library. + + . mutex + + Runs a few tests on ACE_Local_Mutex and + ACE_Remote_Mutex. Tests recursive acquisition and + global vs local proxies. + + . rw_locks + + App for testing ACE_Local_RLock, ACE_Local_WLock, + ACE_Remote_RLock, and ACE_Remote_WLock. + + . deadlock + + Tests the deadlock detection algorithm of the token + manager using ACE_Local_Mutex and ACE_Remote_Mutex. + + . collection + + Tests the ACE_Token_Collection utility. Uses local + and remote tokens and readers/writer locks. + + . invariant + + Tests the token Invariant testing utilities. Yes, + this tests a testing utility. + + . manual + + Gives users a text-based interactive interface to + local or remote tokens. This is extremely useful for + manually testing the token server and setting up + deadlock scenarios. diff --git a/ACE/netsvcs/clients/Tokens/collection/Makefile.am b/ACE/netsvcs/clients/Tokens/collection/Makefile.am new file mode 100644 index 00000000000..9d3fbcc5ddd --- /dev/null +++ b/ACE/netsvcs/clients/Tokens/collection/Makefile.am @@ -0,0 +1,18 @@ +##---------------------------------------------------------------------------- +## $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 = \ + collection + +collection_SOURCES = collection.cpp +collection_LDADD = $(top_builddir)/netsvcs/lib/libnetsvcs.la \ + $(top_builddir)/ace/libACE.la diff --git a/ACE/netsvcs/clients/Tokens/collection/README b/ACE/netsvcs/clients/Tokens/collection/README new file mode 100644 index 00000000000..4c25a1f729e --- /dev/null +++ b/ACE/netsvcs/clients/Tokens/collection/README @@ -0,0 +1,25 @@ + +Shows how applications can use the ACE_Token_Collection utility. This +example creates three collections and spawns a thread to operate on +each. The threads use the collective acquire, renew, and release +features of ACE_Token_Collection. + +Here are the command-line parameters for collection: + +./collection: +[-h <remote host>] +[-p <remote port>] +[-n <iterations>] +[-d debug] + +To run the collection locally with debugging info, type + +% ./collection -d + +To run the collection remotely with debugging info, first start a +token server and the type: + +% ./collection -d -h <token-server-host> -p <token-server-port> + +The -n <iterations> option is to control how often each thread +iterates on the acquire, renew, release cycle. diff --git a/ACE/netsvcs/clients/Tokens/collection/collection.cpp b/ACE/netsvcs/clients/Tokens/collection/collection.cpp new file mode 100644 index 00000000000..8df0ae625fc --- /dev/null +++ b/ACE/netsvcs/clients/Tokens/collection/collection.cpp @@ -0,0 +1,210 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// examples +// +// = FILENAME +// collection.cpp +// +// = DESCRIPTION +// Shows how applications can use the ACE_Token_Collection +// utility. This example creates three collections and spawns a +// thread to operate on each. The threads use the collective +// acquire, renew, and release features of ACE_Token_Collection. +// +// = AUTHOR +// Tim Harrison +// +// ============================================================================ + +#include "ace/Get_Opt.h" +#include "ace/Local_Tokens.h" +#include "ace/Token_Collection.h" +#include "ace/Remote_Tokens.h" +#include "ace/Thread_Manager.h" +#include "ace/Service_Config.h" + +ACE_RCSID(collection, collection, "$Id$") + +#if defined (ACE_HAS_THREADS) && defined (ACE_HAS_THREADS_LIBRARY) + +static const char *server_host = ACE_DEFAULT_SERVER_HOST; +static int server_port = ACE_DEFAULT_SERVER_PORT; +// unused: static int threads = 2; +static int iterations = 50; +static int debug = 0; +static int remote = 0; +// unused: static int tokens = 5; + +static void * +run_thread (void *vp) +{ + ACE_Token_Proxy *collection = (ACE_Token_Proxy *) vp; + + int count = iterations; + while (count--) + { + if (collection->acquire () == -1) + { + if (ACE_OS::last_error () == EDEADLK) + { + ACE_DEBUG ((LM_DEBUG, "deadlock detected in acquire")); + continue; + } + ACE_ERROR ((LM_ERROR, "(%t) %p acquire failed\n","run_thread")); + return (void *) -1; + } + + ACE_DEBUG ((LM_DEBUG, "(%t) %s acquired.\n", collection->name ())); + + if (collection->renew () == -1) + { + if (ACE_OS::last_error () == EDEADLK) + { + ACE_DEBUG ((LM_DEBUG, "deadlock detected")); + goto deadlock; + } + ACE_ERROR ((LM_ERROR, "(%t) %p renew failed\n","run_thread")); + return (void *) -1; + } + + ACE_DEBUG ((LM_DEBUG, "(%t) %s renewed.\n", collection->name ())); + + deadlock: + if (collection->release () == -1) + { + ACE_ERROR ((LM_ERROR, "(%t) %p release failed\n","run_thread")); + return (void *) -1; + } + + ACE_DEBUG ((LM_DEBUG, "(%t) %s released.\n", collection->name ())); + } + + + ACE_DEBUG ((LM_DEBUG, "(%t) thread exiting.\n")); + return 0; +} + +static int +parse_args (int argc, char *argv[]) +{ + ACE_LOG_MSG->open (argv[0], ACE_Log_Msg::STDERR); // | ACE_Log_Msg::VERBOSE); + + ACE_Get_Opt get_opt (argc, argv, "un:dp:h:", 1); + + for (int c; (c = get_opt ()) != -1; ) + { + switch (c) + { + case 'h': // specify the host machine on which the server is running + server_host = get_opt.opt_arg (); + remote = 1; + break; + case 'p': // specify the port on which the server is running + server_port = ACE_OS::atoi (get_opt.opt_arg ()); + remote = 1; + break; + case 'd': + debug = 1; + break; + case 'n': + iterations = ACE_OS::atoi (get_opt.opt_arg ()); + break; + case 'u': + // usage: fallthrough + default: + ACE_ERROR_RETURN ((LM_ERROR, + "%n:\n" + "[-h <remote host>]\n" + "[-p <remote port>]\n" + "[-n <iterations>]\n" + "[-d debug]\n", 1), -1); + /* NOTREACHED */ + } + } + + return 0; +} + +int +main (int argc, char* argv[]) +{ + if (parse_args (argc, argv) == -1) + return -1; + + ACE_Token_Proxy *A; // Mutex *A*. + ACE_Token_Proxy *B; // Mutex *B*. + ACE_Token_Proxy *R; // *R*eader Lock. + ACE_Token_Proxy *W; // *W*riter Lock. + + // Depending on the command line arguments, we will create local or + // remote tokens. The names of the tokens are not important as long + // as they are unique. + if (remote) + { + ACE_Remote_Mutex::set_server_address (ACE_INET_Addr (server_port, server_host)); + A = new ACE_Remote_Mutex ("R Mutex A", 0, debug); + B = new ACE_Remote_Mutex ("R Mutex B", 0, debug); + R = new ACE_Remote_RLock ("R Reader Lock", 0, debug); + W = new ACE_Remote_WLock ("R Writer Lock", 0, debug); + } + else + { + A = new ACE_Local_Mutex ("L Mutex A", 0, debug); + B = new ACE_Local_Mutex ("L Mutex B", 0, debug); + R = new ACE_Local_RLock ("L Reader Lock", 0, debug); + W = new ACE_Local_WLock ("L Writer Lock", 0, debug); + } + + // These collections will be treated as Tokens by the threads. + ACE_Token_Collection collectionAR (debug, "A and Reader"); + ACE_Token_Collection collectionAW (debug, "A and Writer"); + ACE_Token_Collection collectionBR (debug, "B and Reader"); + + // AR and BR can run concurrently. Neither AR or BR can run when AW + // is running. + collectionAR.insert (*A); + collectionAR.insert (*R); + + collectionAW.insert (*A); + collectionAW.insert (*W); + + collectionBR.insert (*B); + collectionBR.insert (*R); + + // Spawn off three threads. + ACE_Thread_Manager *mgr = ACE_Thread_Manager::instance (); + + if (mgr->spawn (ACE_THR_FUNC (run_thread), + (void *) &collectionAR, THR_BOUND | THR_SUSPENDED) == -1) + ACE_ERROR_RETURN ((LM_DEBUG, "%p\n", "spawn 1 failed"), -1); + + if (mgr->spawn (ACE_THR_FUNC (run_thread), + (void *) &collectionAW, THR_BOUND | THR_SUSPENDED) == -1) + ACE_ERROR_RETURN ((LM_DEBUG, "%p\n", "spawn 2 failed"), -1); + + if (mgr->spawn (ACE_THR_FUNC (run_thread), + (void *) &collectionBR, THR_BOUND | THR_SUSPENDED) == -1) + ACE_ERROR_RETURN ((LM_DEBUG, "%p\n", "spawn 3 failed"), -1); + +#if ! defined (ACE_HAS_PTHREADS) + if (mgr->resume_all () == -1) + ACE_ERROR_RETURN ((LM_DEBUG, "%p\n", "resume failed"), -1); +#endif + + // Wait for all threads to exit. + mgr->wait (); + + return 0; +} + +#else +int +main (int, char *[]) +{ + ACE_ERROR_RETURN ((LM_ERROR, + "threads not supported on this platform\n"), -1); +} +#endif /* ACE_HAS_THREADS && ACE_HAS_TOKENS_LIBRARY */ diff --git a/ACE/netsvcs/clients/Tokens/collection/rw_locks.cpp b/ACE/netsvcs/clients/Tokens/collection/rw_locks.cpp new file mode 100644 index 00000000000..2670f99214f --- /dev/null +++ b/ACE/netsvcs/clients/Tokens/collection/rw_locks.cpp @@ -0,0 +1,173 @@ +// $Id$ + +#include "ace/Get_Opt.h" +#include "ace/Local_Tokens.h" +#include "ace/Remote_Tokens.h" +#include "ace/Thread_Manager.h" + +#if defined (ACE_HAS_THREADS) && defined (ACE_HAS_THREADS_LIBRARY) + +ACE_RCSID(collection, rw_locks, "$Id$") + +static ACE_Token_Proxy *global_rlock; +static ACE_Token_Proxy *global_wlock; + +static char *server_host = ACE_DEFAULT_SERVER_HOST; +static int server_port = ACE_DEFAULT_SERVER_PORT; +static int ignore_deadlock = 0; +static int threads = 2; +static int iterations = 50; +static int debug = 0; +static int remote = 0; +static int reads = 4; +static int write_sleep = 0; + +static void * +run_thread (void *vp) +{ + for (int x = 0; x < iterations; x++) + { + int y = 0; + for (; y < reads; y++) + { + if (global_rlock->acquire () == -1) + { + if (ACE_Log_Msg::instance ()->errnum () == EDEADLK) + { + ACE_DEBUG ((LM_DEBUG, "rlock deadlock detected\n")); + goto READ_DEADLOCK; + } + else return 0; + } + + ACE_DEBUG ((LM_DEBUG, "(%t) rlock acquired.\n")); + } + + READ_DEADLOCK: + + for (; y > 0; y--) + { + if (global_rlock->release () == 0) + ACE_DEBUG ((LM_DEBUG, "(%t) r-released.\n")); + } + + if (global_wlock->acquire () == -1) + { + ACE_DEBUG ((LM_DEBUG, "wlock deadlock detected\n")); + } + else + { + if (write_sleep) + ACE_OS::sleep (1); + ACE_DEBUG ((LM_DEBUG, "\t\t(%t) wlock acquired.\n")); + if (global_wlock->release () == 0) + ACE_DEBUG ((LM_DEBUG, "\t\t(%t) w-released.\n")); + } + } + + ACE_DEBUG ((LM_DEBUG, "(%t) thread exiting.\n")); + return 0; +} + +static int +parse_args (int argc, char *argv[]) +{ + ACE_LOG_MSG->open (argv[0], ACE_Log_Msg::STDERR); // | ACE_Log_Msg::VERBOSE); + + ACE_Get_Opt get_opt (argc, argv, "t:iun:drR:sp:h:", 1); + + for (int c; (c = get_opt ()) != -1; ) + { + switch (c) + { + case 'h': // specify the host machine on which the server is running + server_host = get_opt.opt_arg (); + break; + case 'p': // specify the port on which the server is running + server_port = ACE_OS::atoi (get_opt.opt_arg ()); + break; + case 't': + threads = ACE_OS::atoi (get_opt.opt_arg ()); + break; + case 'R': + reads = ACE_OS::atoi (get_opt.opt_arg ()); + break; + case 'd': + debug = 1; + break; + case 'r': + remote = 1; + break; + case 's': + write_sleep = 1; + break; + case 'n': + iterations = ACE_OS::atoi (get_opt.opt_arg ()); + break; + case 'i': + ignore_deadlock = 1; + break; + case 'u': + // usage: fallthrough + default: + ACE_ERROR_RETURN ((LM_ERROR, + "%n:\n" + "[-i ignore deadlock]\n" + "[-n <iterations>]\n" + "[-R <reads>]\n" + "[-r use remote locks]\n" + "[-d debug]\n" + "[-s sleep during writes]\n" + "[-t <threads>\n", 1), -1); + break; + } + } + + return 0; +} + +int +main (int argc, char* argv[]) +{ + if (parse_args (argc, argv) == -1) + return -1; + + if (remote) + { + ACE_Remote_Mutex::set_server_address (ACE_INET_Addr (server_port, server_host)); + global_rlock = (ACE_Token_Proxy *) new + ACE_Remote_RLock ("THE_TOKEN", ignore_deadlock, debug); + global_wlock = (ACE_Token_Proxy *) new + ACE_Remote_WLock ("THE_TOKEN", ignore_deadlock, debug); + } + else + { + global_rlock = (ACE_Token_Proxy *) new + ACE_Local_RLock ("THE_TOKEN", ignore_deadlock, debug); + global_wlock = (ACE_Token_Proxy *) new + ACE_Local_WLock ("THE_TOKEN", ignore_deadlock, debug); + } + + ACE_Thread_Manager mgr; + + if (mgr.spawn_n (threads, ACE_THR_FUNC (run_thread), + (void *) 0, + THR_BOUND | THR_SUSPENDED) == -1) + ACE_ERROR_RETURN ((LM_DEBUG, "%p\n", "spawn failed"), -1); + + if (mgr.resume_all () == -1) + ACE_ERROR_RETURN ((LM_DEBUG, "%p\n", "resume failed"), -1); + + mgr.wait (); + + return 0; +} + +#else +int +main (int, char *[]) +{ + ACE_ERROR_RETURN ((LM_ERROR, + "threads not supported on this platform\n"), -1); +} +#endif /* ACE_HAS_THREADS */ diff --git a/ACE/netsvcs/clients/Tokens/deadlock/Makefile.am b/ACE/netsvcs/clients/Tokens/deadlock/Makefile.am new file mode 100644 index 00000000000..d995e71620b --- /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/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..e1a9d60ccd6 --- /dev/null +++ b/ACE/netsvcs/clients/Tokens/deadlock/deadlock_detection_test.cpp @@ -0,0 +1,340 @@ +// $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 +main (int argc, char* 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 +main (int, char *[]) +{ + ACE_ERROR_RETURN ((LM_ERROR, + "threads not supported on this platform\n"), -1); +} +#endif /* ACE_HAS_THREADS */ diff --git a/ACE/netsvcs/clients/Tokens/invariant/Makefile.am b/ACE/netsvcs/clients/Tokens/invariant/Makefile.am new file mode 100644 index 00000000000..37817911015 --- /dev/null +++ b/ACE/netsvcs/clients/Tokens/invariant/Makefile.am @@ -0,0 +1,20 @@ +##---------------------------------------------------------------------------- +## $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 = \ + invariant + +invariant_SOURCES = invariant.cpp +invariant_LDADD = \ + $(top_builddir)/netsvcs/lib/libnetsvcs.la \ + $(top_builddir)/ace/libACE.la + diff --git a/ACE/netsvcs/clients/Tokens/invariant/README b/ACE/netsvcs/clients/Tokens/invariant/README new file mode 100644 index 00000000000..f078c2d6be4 --- /dev/null +++ b/ACE/netsvcs/clients/Tokens/invariant/README @@ -0,0 +1,27 @@ + +invariants.cpp tests the ACE Token Invariant utilities. The ACE Token +Invariant utilities allow an application to test the correctness of +mutex and readers/writer locks. + +invariants.cpp takes no command-line arguments. invariants.cpp first +tests readers/writer locks. This is done by spawning two threads +which simulate reader and writer acquire/renew/release loops. +However, the loops are performed without actual locks, so the +competing threads quickly reach and invalid state. The test should +report this violation of readers/writer lock invariants and both +threads should exit. + +The second test is for mutexes. Similar to the readers/writer lock +test, this test spawns two threads which perform acquire/renew/release +loops. When to two threads reach an invalid mutex state, the error +should be reported and the threads should exit. + +For these two previous tests, it is theoretically possible that the +threads never reach an invalid token state. However, it is highly +unlikely since the threads would have to execute the same code +simultaneously for the duration of the test. Nevertheless, it is +possible. + +The last test hardwires invalid token states. It runs two mutex and +two readers/writer lock tests. It should report "succeeded" for the +four tests. diff --git a/ACE/netsvcs/clients/Tokens/invariant/invariant.cpp b/ACE/netsvcs/clients/Tokens/invariant/invariant.cpp new file mode 100644 index 00000000000..cda1f54f6f7 --- /dev/null +++ b/ACE/netsvcs/clients/Tokens/invariant/invariant.cpp @@ -0,0 +1,196 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// examples +// +// = FILENAME +// invariant.cpp +// +// = DESCRIPTION +// +// = AUTHOR +// Tim Harrison +// +// ============================================================================ + +#include "ace/Get_Opt.h" +#include "ace/Singleton.h" +#include "ace/Thread_Manager.h" +#include "ace/Token_Invariants.h" + +#if defined (ACE_HAS_THREADS) && defined (ACE_HAS_THREADS_LIBRARY) + +ACE_RCSID(invariant, invariant, "$Id$") + +typedef ACE_Token_Invariant_Manager ACE_TOKEN_INVARIANTS; + +static const char *rwname = "reader/writer"; +static const char *mutexname = "mutex"; + +static void * +run_reader_writer (void *) +{ + for (int x = 0; x < 50; x++) + { + int y = 0; + for (; y < 5; y++) + { + if (ACE_TOKEN_INVARIANTS::instance ()->reader_acquired (rwname) == 0) + ACE_ERROR_RETURN ((LM_ERROR, "reader acquire violated invariant.\n"), 0); + + ACE_DEBUG ((LM_DEBUG, "(%t) rlock acquired.\n")); + } + + ACE_TOKEN_INVARIANTS::instance ()->rwlock_releasing (rwname); + + if (ACE_TOKEN_INVARIANTS::instance ()->reader_acquired (rwname) == 0) + ACE_ERROR_RETURN ((LM_ERROR, "reader renew violated invariant.\n"), 0); + + ACE_DEBUG ((LM_DEBUG, "(%t) rlock renewed.\n")); + + for (; y > 0; y--) + { + ACE_TOKEN_INVARIANTS::instance ()->rwlock_releasing (rwname); + ACE_DEBUG ((LM_DEBUG, "(%t) r-released.\n")); + } + + if (ACE_TOKEN_INVARIANTS::instance ()->writer_acquired (rwname) == 0) + ACE_ERROR_RETURN ((LM_ERROR, "writer acquire violated invariant.\n"), 0); + + ACE_DEBUG ((LM_DEBUG, "\t\t(%t) wlock acquired.\n")); + + ACE_TOKEN_INVARIANTS::instance ()->rwlock_releasing (rwname); + + if (ACE_TOKEN_INVARIANTS::instance ()->writer_acquired (rwname) == 0) + ACE_ERROR_RETURN ((LM_ERROR, "writer renew violated invariant.\n"), 0); + + ACE_DEBUG ((LM_DEBUG, "(%t) rlock renewed.\n")); + + ACE_TOKEN_INVARIANTS::instance ()->rwlock_releasing (rwname); + } + + ACE_DEBUG ((LM_DEBUG, "(%t) thread exiting.\n")); + return 0; +} + +static void * +run_mutex (void *) +{ + for (int x = 0; x < 50; x++) + { + if (ACE_TOKEN_INVARIANTS::instance ()->mutex_acquired (mutexname) == 0) + ACE_ERROR_RETURN ((LM_ERROR, "mutex acquire violated invariant.\n"), 0); + + ACE_DEBUG ((LM_DEBUG, "(%t) mutex acquired.\n")); + + ACE_TOKEN_INVARIANTS::instance ()->mutex_releasing (mutexname); + + if (ACE_TOKEN_INVARIANTS::instance ()->mutex_acquired (mutexname) == 0) + ACE_ERROR_RETURN ((LM_ERROR, "mutex renew violated invariant.\n"), 0); + + ACE_DEBUG ((LM_DEBUG, "(%t) mutex renewed.\n")); + + ACE_TOKEN_INVARIANTS::instance ()->mutex_releasing (mutexname); + ACE_DEBUG ((LM_DEBUG, "(%t) mutex released.\n")); + } + + ACE_DEBUG ((LM_DEBUG, "(%t) thread exiting.\n")); + return 0; +} + +static int +run_final_test (void) +{ + ACE_DEBUG ((LM_DEBUG, "starting mutex tests 1 & 2\n")); + + // Mutex tests. + if (ACE_TOKEN_INVARIANTS::instance ()->mutex_acquired ("testing mutex") == 0) + ACE_ERROR_RETURN ((LM_ERROR, "mutex test 1 failed.\n"), 0); + if (ACE_TOKEN_INVARIANTS::instance ()->mutex_acquired ("testing mutex2") == 0) + ACE_ERROR_RETURN ((LM_ERROR, "mutex test 2 failed.\n"), 0); + if (ACE_TOKEN_INVARIANTS::instance ()->mutex_acquired ("testing mutex") == 0) + ACE_DEBUG ((LM_DEBUG, "mutex test 1 succeeded.\n")); + else + ACE_ERROR_RETURN ((LM_ERROR, "mutex test 1 failed..\n"), 0); + + if (ACE_TOKEN_INVARIANTS::instance ()->mutex_acquired ("testing mutex2") == 0) + ACE_DEBUG ((LM_DEBUG, "mutex test 2 succeeded.\n")); + else + ACE_ERROR_RETURN ((LM_ERROR, "mutex test 2 failed..\n"), 0); + + // RW tests. + ACE_DEBUG ((LM_DEBUG, "starting rwlock tests 1 & 2\n")); + + // Multiple readers. + if (ACE_TOKEN_INVARIANTS::instance ()->reader_acquired ("testing rwlock") == 0) + ACE_ERROR_RETURN ((LM_ERROR, "rwlock test 1 failed.\n"), 0); + if (ACE_TOKEN_INVARIANTS::instance ()->reader_acquired ("testing rwlock 2") == 0) + ACE_ERROR_RETURN ((LM_ERROR, "rwlock test 2 failed.\n"), 0); + if (ACE_TOKEN_INVARIANTS::instance ()->reader_acquired ("testing rwlock") == 0) + ACE_ERROR_RETURN ((LM_ERROR, "rwlock test 1 failed..\n"), 0); + if (ACE_TOKEN_INVARIANTS::instance ()->reader_acquired ("testing rwlock 2") == 0) + ACE_ERROR_RETURN ((LM_ERROR, "rwlock test 2 failed..\n"), 0); + + // Writer. + if (ACE_TOKEN_INVARIANTS::instance ()->writer_acquired ("testing rwlock") == 0) + ACE_DEBUG ((LM_ERROR, "rwlock test 1 succeded.\n")); + else + ACE_ERROR_RETURN ((LM_ERROR, "rwlock test 1 failed...\n"), 0); + + // Releasing reader. + ACE_TOKEN_INVARIANTS::instance ()->rwlock_releasing ("testing rwlock 2"); + ACE_TOKEN_INVARIANTS::instance ()->rwlock_releasing ("testing rwlock 2"); + + // Writer. + if (ACE_TOKEN_INVARIANTS::instance ()->writer_acquired ("testing rwlock 2") == 0) + ACE_ERROR_RETURN ((LM_ERROR, "rwlock test 2 failed....\n"), 0); + + // Reader. + if (ACE_TOKEN_INVARIANTS::instance ()->reader_acquired ("testing rwlock 2") == 0) + ACE_DEBUG ((LM_DEBUG, "rwlock test 2 succeeded.\n")); + else + ACE_ERROR_RETURN ((LM_ERROR, "rwlock test 2 failed.....\n"), 0); + + return 0; +} + +int +main (int /* argc */, char* /* argv */ []) +{ + ACE_Thread_Manager mgr; + + // Run reader/writer test + if (mgr.spawn_n (2, ACE_THR_FUNC (run_reader_writer), + (void *) 0, + THR_NEW_LWP | THR_DETACHED) == -1) + ACE_ERROR_RETURN ((LM_DEBUG, "%p\n", "spawn failed"), -1); + + mgr.wait (); + + ACE_OS::sleep (2); + + // Run mutex test. + if (mgr.spawn_n (2, ACE_THR_FUNC (run_mutex), + (void *) 0, + THR_NEW_LWP | THR_DETACHED) == -1) + ACE_ERROR_RETURN ((LM_DEBUG, "%p\n", "spawn failed"), -1); + + mgr.wait (); + + ACE_OS::sleep (2); + + run_final_test (); + + return 0; +} + +#else +int +main (int, char *[]) +{ + ACE_ERROR_RETURN ((LM_ERROR, + "threads not supported on this platform\n"), -1); +} +#endif /* ACE_HAS_THREADS */ diff --git a/ACE/netsvcs/clients/Tokens/manual/Makefile.am b/ACE/netsvcs/clients/Tokens/manual/Makefile.am new file mode 100644 index 00000000000..b2945a96bd3 --- /dev/null +++ b/ACE/netsvcs/clients/Tokens/manual/Makefile.am @@ -0,0 +1,24 @@ +##---------------------------------------------------------------------------- +## $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 = \ + manual + +manual_SOURCES = manual.cpp +manual_LDADD = \ + $(top_builddir)/netsvcs/lib/libnetsvcs.la \ + $(top_builddir)/ace/libACE.la + +## Clean up template repositories, etc. +clean-local: + -rm -f *.bak *.rpo *.sym lib*.*_pure_* Makefile.old core + -rm -rf ptrepository Templates.DB gcctemp.c gcctemp so_locations diff --git a/ACE/netsvcs/clients/Tokens/manual/README b/ACE/netsvcs/clients/Tokens/manual/README new file mode 100644 index 00000000000..09b9b9a365a --- /dev/null +++ b/ACE/netsvcs/clients/Tokens/manual/README @@ -0,0 +1,67 @@ + +./manual gives users a text-based interactive interface to local or +remote tokens. This is extremely useful for manually testing the +token server and setting up deadlock scenarios. + +Run it as follows + +% ./manual -u +./manual: +[-h <remote host>] +[-p <remote port>] +[-i ignore deadlock] +[-d debug] + +./manual gives you the following prompt. +<tid> <token> <type> <operation> + +<tid> This is the client id of the current operation. This is set + manually by ./manual for every operation. Be careful when + using multiple <tid>'s during a remote session (see BUGS + below). + +<token> This is the name of the token for the operation. + +<type> This is the type of the token. This can be: + M - Corresponds to a Mutex lock. + R - Corresponds to Readers/Writer lock. + W - Corresponds to Readers/Writer lock. + Obviously, a single <token> can be M or it can R and/or W. If + you perform and operation like this "tid1 tokenA M A" then + don't do this "tid1 tokenA R A". This doesn't make sense. + +<operation> This is the operation to perform on the + <tid>-<token>-<type> proxy. These include: + A - acquire. + N - renew. + R - release. + T - tryacquire. + +BUGS!!!! + +When performing remote tests, be careful when using a single running +./manual to impersonate two <tid>'s. The Token Server client +connection policy is currently, one per thread. The Token Server +assumes that the same <tid> is always on the other end of a +connection. If you do something like the following, you will break +it: + +lambada:Tokens/manual> ./manual -h tango -p 20202 +<tid> <token> <type> <operation> +tid1 tokenA M A +ACE_TSS_Connection new connection +(1) acquired tokenA remotely. +Succeeded. +<tid> <token> <type> <operation> +tid2 tokenA M A +(1) acquired tokenA remotely. <------ This is remote BADness!!! +Succeeded. +Violated invariant. <------ Locally detected badness. +<tid> <token> <type> <operation> + + +Notice that the local side discovered that this was incorrect. +However, the Token Server thinks it was a recursive acquisition for +tid1. Keep in mind that this is not a problem with the Token library. +It is just a problem with how this primitive ./manual application maps +STDIN to the ACE Token API. diff --git a/ACE/netsvcs/clients/Tokens/manual/manual.cpp b/ACE/netsvcs/clients/Tokens/manual/manual.cpp new file mode 100644 index 00000000000..d75c2b543b1 --- /dev/null +++ b/ACE/netsvcs/clients/Tokens/manual/manual.cpp @@ -0,0 +1,365 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// examples +// +// = FILENAME +// manual.cpp +// +// = DESCRIPTION +// Allows manual operations on local and remote tokens. +// +// = AUTHOR +// Tim Harrison +// +// ============================================================================ + +#include "ace/Get_Opt.h" +#include "ace/Local_Tokens.h" +#include "ace/Remote_Tokens.h" +#include "ace/Singleton.h" +#include "ace/Thread_Manager.h" +#include "ace/Token_Invariants.h" +#include "ace/Token_Collection.h" +#include "ace/Map_Manager.h" +#include "ace/Service_Config.h" + +#if defined (ACE_HAS_THREADS) && defined (ACE_HAS_THREADS_LIBRARY) + +ACE_RCSID(manual, manual, "$Id$") + +typedef ACE_Token_Invariant_Manager ACE_TOKEN_INVARIANTS; + +class STDIN_Token : public ACE_Event_Handler + // = TITLE + // STDIN Token + // + // = DESCRIPTION + // Translates STDIN commands to ACE Token commands. +{ +public: + STDIN_Token (void); + // Construction. + + int parse_args (int argc, char *argv[]); + // Parse command-line arguments. + + int open (int argc, char *argv[]); + // Register with whatever event dispatcher is needed and run. + + // = Event_Handler methods. + int handle_input (ACE_HANDLE); + int handle_exception (ACE_HANDLE); + + typedef ACE_CString TID; + +private: + + void display_menu (void); + // Display options. + + ACE_Token_Proxy *get_proxy (const char *tid, const char *token, char type); + // Get or make a proxy to <token> with a <tid> client id. + + ACE_Token_Proxy *create_proxy (const char *token, char type); + // Create a proxy to <token> with a <tid> client id. + + // = Mapping from tid to Token_Collection. + typedef ACE_Map_Manager<TID, ACE_Token_Collection *, ACE_Null_Mutex> + COLLECTIONS; + // COLLECTION maintains a mapping from tid to a collection. + + typedef ACE_Map_Iterator<TID, ACE_Token_Collection *, ACE_Null_Mutex> + COLLECTIONS_ITERATOR; + // Allows iterations through collections_. + + typedef ACE_Map_Entry<TID, ACE_Token_Collection *> + COLLECTIONS_ENTRY; + // Allows iterations through collections_. + + COLLECTIONS collections_; + // A collection for each <tid>. + + const char *server_host_; + int server_port_; + int ignore_deadlock_; + int debug_; + int remote_; +}; + +STDIN_Token::STDIN_Token (void) + : server_host_ (ACE_DEFAULT_SERVER_HOST), + server_port_ (ACE_DEFAULT_SERVER_PORT), + ignore_deadlock_ (0), + debug_ (0), + remote_ (0) +{ +} + +int +STDIN_Token::parse_args (int argc, char *argv[]) +{ + ACE_LOG_MSG->open (argv[0], ACE_Log_Msg::STDERR); + + ACE_Get_Opt get_opt (argc, argv, "h:p:diu", 1); + + for (int c; (c = get_opt ()) != -1; ) + { + switch (c) + { + case 'h': // specify the host machine on which the server is running + server_host_ = get_opt.opt_arg (); + remote_ = 1; + break; + case 'p': // specify the port on which the server is running + server_port_ = ACE_OS::atoi (get_opt.opt_arg ()); + remote_ = 1; + break; + case 'd': + debug_ = 1; + break; + case 'i': + ignore_deadlock_ = 1; + break; + case 'u': + // usage: fallthrough + default: + ACE_ERROR_RETURN ((LM_ERROR, + "%n:\n" + "[-h <remote host>]\n" + "[-p <remote port>]\n" + "[-i ignore deadlock]\n" + "[-d debug]\n", 1), -1); + } + } + + if (remote_) + ACE_Remote_Mutex::set_server_address (ACE_INET_Addr (server_port_, + server_host_)); + + return 0; +} + +int +STDIN_Token::open (int argc, char *argv[]) +{ + if (this->parse_args (argc, argv) == -1) + return -1; + + // Register for signals. + if (ACE_Reactor::instance ()->register_handler + (SIGINT, this) == -1) + ACE_DEBUG ((LM_DEBUG, "Can't register signal handler\n")); + +#if defined (ACE_WIN32) + +#else + // Register for STDIN events with Reactor. + if (ACE_Reactor::instance ()->register_handler + (ACE_STDIN, this, ACE_Event_Handler::READ_MASK) == -1) + ACE_ERROR_RETURN ((LM_DEBUG, "Can't register signal handler\n"), 0); + + +#endif /* ACE_WIN32 */ + + + this->display_menu (); + +#if defined (ACE_WIN32) + +#else + ACE_Reactor::run_event_loop (); +#endif /* ACE_WIN32 */ + + ACE_OS::printf ("Exiting...\n"); + return 0; +} + +int +STDIN_Token::handle_input (ACE_HANDLE fd) +{ + ACE_UNUSED_ARG (fd); + + char tid[BUFSIZ]; + char token[BUFSIZ]; + char type[16]; + char operation[16]; + + if (::scanf ("%s %s %s %s", tid, token, type, operation) <= 0) + { + ACE_OS::printf ("Try again.\n"); + return 0; + } + + ACE_Token_Proxy *proxy = + this->get_proxy (tid, token, type[0]); + + if (proxy == 0) + return -1; + + switch (operation[0]) + { + case 'a': + case 'A': + if (proxy->acquire () == 0) + { + ACE_OS::printf ("Succeeded.\n"); + if (ACE_TOKEN_INVARIANTS::instance ()->acquired (proxy) == 0) + ACE_OS::printf ("Violated invariant.\n"); + } + else + ACE_ERROR ((LM_ERROR, "%p.\n", "Acquire failed")); + break; + + case 'n': + case 'N': + ACE_TOKEN_INVARIANTS::instance ()->releasing (proxy); + if (proxy->renew () == 0) + { + ACE_OS::printf ("Succeeded.\n"); + if (ACE_TOKEN_INVARIANTS::instance ()->acquired (proxy) == 0) + ACE_OS::printf ("Violated invariant.\n"); + } + else + ACE_ERROR ((LM_ERROR, "%p.\n", "Renew failed")); + break; + + case 'r': + case 'R': + ACE_TOKEN_INVARIANTS::instance ()->releasing (proxy); + if (proxy->release () == 0) + ACE_OS::printf ("Succeeded.\n"); + else + ACE_ERROR ((LM_ERROR, "%p.\n", "Release failed")); + break; + + case 't': + case 'T': + if (proxy->tryacquire () == 0) + { + ACE_OS::printf ("Succeeded.\n"); + if (ACE_TOKEN_INVARIANTS::instance ()->acquired (proxy) == 0) + ACE_OS::printf ("Violated invariant.\n"); + } + else + ACE_ERROR ((LM_ERROR, "%p.\n", "Tryacquire failed")); + break; + } + + this->display_menu (); + return 0; +} + +void +STDIN_Token::display_menu (void) +{ + ACE_OS::printf ("<tid> <token> <type> <operation>\n"); +} + +int +STDIN_Token::handle_exception (ACE_HANDLE fd) +{ + ACE_UNUSED_ARG (fd); + + ACE_Reactor::run_event_loop (); + return -1; +} + +ACE_Token_Proxy * +STDIN_Token::get_proxy (const char *_tid, const char *token, char type) +{ + ACE_Token_Collection *proxy_collection; + + TID tid (_tid); + + if (collections_.find (tid, proxy_collection) == -1) + // We did not find a proxy_collection. + { + // Make one. + proxy_collection = new ACE_Token_Collection (debug_, "no name collection"); + + // Put it in the collections. + if (collections_.bind (tid, proxy_collection) == -1) + { + delete proxy_collection; + return 0; + } + } + + // Either way, we have a proxy_collection now. + + // See if the proxy already exists in the collection. + ACE_Token_Proxy *proxy = proxy_collection->is_member (token); + + // If not, create one. + if (proxy == 0) + { + proxy = this->create_proxy (token, type); + + // Put the new_proxy in this tid's collection. + if (proxy_collection->insert (*proxy) == -1) + ACE_ERROR_RETURN ((LM_ERROR, "insert failed\n"), 0); + + // Delete our copy (one was created in the collection). + delete proxy; + proxy = proxy_collection->is_member (token); + + if (proxy == 0) + ACE_ERROR_RETURN ((LM_ERROR, "is_member failed\n"), 0); + + // Set the client_id (it was set to 1 since we're + // single-threaded. + proxy->client_id (_tid); + } + + return proxy; +} + +ACE_Token_Proxy * +STDIN_Token::create_proxy (const char *token, char type) +{ + switch (type) + { + case 'm': + case 'M': + if (remote_) + return new ACE_Remote_Mutex (token, ignore_deadlock_, debug_); + else + return new ACE_Local_Mutex (token, ignore_deadlock_, debug_); + + case 'r': + case 'R': + if (remote_) + return new ACE_Remote_RLock (token, ignore_deadlock_, debug_); + else + return new ACE_Local_RLock (token, ignore_deadlock_, debug_); + + case 'w': + case 'W': + if (remote_) + return new ACE_Remote_WLock (token, ignore_deadlock_, debug_); + else + return new ACE_Local_WLock (token, ignore_deadlock_, debug_); + } + + // should never get here, but this avoids a compiler warning . . . + return 0; +} + +int +main (int argc, char* argv[]) +{ + STDIN_Token st; + return st.open (argc, argv); +} + +#else +int +main (int, char *[]) +{ + ACE_ERROR_RETURN ((LM_ERROR, + "threads or ACE_HAS_TOKENS_LIBRARY not supported on this platform\n"), -1); +} +#endif /* ACE_HAS_THREADS && ACE_HAS_TOKENS_LIBRARY */ diff --git a/ACE/netsvcs/clients/Tokens/mutex/Makefile.am b/ACE/netsvcs/clients/Tokens/mutex/Makefile.am new file mode 100644 index 00000000000..63d02af7d97 --- /dev/null +++ b/ACE/netsvcs/clients/Tokens/mutex/Makefile.am @@ -0,0 +1,21 @@ +##---------------------------------------------------------------------------- +## $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 = \ + test_mutex + +test_mutex_SOURCES = test_mutex.cpp +test_mutex_LDADD = \ + $(top_builddir)/netsvcs/lib/libnetsvcs.la \ + $(top_builddir)/ace/libACE.la + + diff --git a/ACE/netsvcs/clients/Tokens/mutex/README b/ACE/netsvcs/clients/Tokens/mutex/README new file mode 100644 index 00000000000..cbd1e9c7d6c --- /dev/null +++ b/ACE/netsvcs/clients/Tokens/mutex/README @@ -0,0 +1,23 @@ + +test_mutex + +test_mutex tests ACE_Local_Mutex and ACE_Remote_Mutex with both local +and global proxies. "Local proxies" mean that each thread uses its +own proxy (but same logical token.) "Global proxy" means that all +threads access the same proxy (and, of course, the same logical +token.) + +test_mutex can take the number of threads to run from the +command-line. Thus, to run the test with one thread and local +mutexes, type: + +% ./test_mutex + +To run the test with 10 threads and local mutexes, type: + +% ./test_mutex -t 10 + +To run the test with 10 threads and remote mutexes, type: + +% ./test_mutex -t 10 -r + diff --git a/ACE/netsvcs/clients/Tokens/mutex/test_mutex.cpp b/ACE/netsvcs/clients/Tokens/mutex/test_mutex.cpp new file mode 100644 index 00000000000..73a33cb6c6e --- /dev/null +++ b/ACE/netsvcs/clients/Tokens/mutex/test_mutex.cpp @@ -0,0 +1,142 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// examples +// +// = FILENAME +// test_mutex.cpp +// +// = DESCRIPTION +// +// = AUTHOR +// Tim Harrison +// +// ============================================================================ + +#include "ace/Get_Opt.h" +#include "ace/Local_Tokens.h" +#include "ace/Remote_Tokens.h" +#include "ace/Thread.h" +#include "ace/Thread_Manager.h" + +#if defined (ACE_HAS_THREADS) && defined (ACE_HAS_THREADS_LIBRARY) + +ACE_RCSID(mutex, test_mutex, "$Id$") + +static ACE_Token_Proxy *mutex; +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 spawn_count = 2; + +static void * +run_test (void *) +{ + int count = iterations; + // test recursive acquisition of a global proxy + while (count--) + { + if (mutex->acquire () == -1) + { + ACE_ERROR ((LM_ERROR, "(%t) %p acquire failed\n","test_mutex")); + return (void *) -1; + } + +// mutex->acquire (); + if (mutex->renew () == -1) + { + ACE_ERROR ((LM_ERROR, "(%t) %p renew failed\n","test_mutex")); + return (void *) -1; + } + + if (mutex->release () == -1) + { + ACE_ERROR ((LM_ERROR, "(%t) %p release failed\n","test_mutex")); + return (void *) -1; + } + +// mutex->release (); + } + + return 0; +} + +static int +parse_args (int argc, char *argv[]) +{ + ACE_LOG_MSG->open (argv[0]); + + ACE_Get_Opt get_opt (argc, argv, "t:uh:p:n:", 1); + + for (int c; (c = get_opt ()) != -1; ) + { + switch (c) + { + case 't': + spawn_count = ACE_OS::atoi (get_opt.opt_arg ()); + break; + case 'h': // specify the host machine on which the server is running + server_host = get_opt.opt_arg (); + remote_mutexes = 1; + break; + case 'p': // specify the port on which the server is running + server_port = ACE_OS::atoi (get_opt.opt_arg ()); + remote_mutexes = 1; + break; + case 'n': // specify the port on which the server is running + iterations = ACE_OS::atoi (get_opt.opt_arg ()); + break; + case 'u': + default: + ACE_ERROR_RETURN ((LM_ERROR, + "%n:\n" + "[-h <remote host>]\n" + "[-p <remote port>]\n" + "[-n <iterations>]\n" + "[-t <threads>]\n" + "[-h <remote host>]\n" + "[-p <remote port>]\n", 1), -1); + /* NOTREACHED */ + } + } + + return 0; +} + +int +main (int argc, char *argv[]) +{ + ACE_Thread_Manager thread_mgr; + + if (parse_args (argc, argv) == -1) + return -1; + + if (remote_mutexes) + { + ACE_Remote_Mutex::set_server_address (ACE_INET_Addr (server_port, server_host)); + mutex = new ACE_Remote_Mutex ("Remote TOKEN", 0, 1); + } + else + { + mutex = new ACE_Local_Mutex ("Local TOKEN", 0, 1); + } + + if (thread_mgr.spawn_n (spawn_count, + ACE_THR_FUNC (run_test), + 0, + THR_BOUND) == -1) + ACE_ERROR_RETURN ((LM_DEBUG, "%p\n", "spawn"), -1); + + thread_mgr.wait (); + + return 0; +} +#else +int main (int, char *[]) +{ + ACE_ERROR_RETURN ((LM_ERROR, "you must have threads to run this test program\n"), -1); +} +#endif /* ACE_HAS_THREADS */ diff --git a/ACE/netsvcs/clients/Tokens/rw_lock/Makefile.am b/ACE/netsvcs/clients/Tokens/rw_lock/Makefile.am new file mode 100644 index 00000000000..265a3a09558 --- /dev/null +++ b/ACE/netsvcs/clients/Tokens/rw_lock/Makefile.am @@ -0,0 +1,20 @@ +##---------------------------------------------------------------------------- +## $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 = \ + rw_locks + +rw_locks_SOURCES = rw_locks.cpp +rw_locks_LDADD = \ + $(top_builddir)/netsvcs/lib/libnetsvcs.la \ + $(top_builddir)/ace/libACE.la + diff --git a/ACE/netsvcs/clients/Tokens/rw_lock/README b/ACE/netsvcs/clients/Tokens/rw_lock/README new file mode 100644 index 00000000000..dabc0a3741d --- /dev/null +++ b/ACE/netsvcs/clients/Tokens/rw_lock/README @@ -0,0 +1,40 @@ + +test_rw_locks shows how to use ACE_Local_RLock, ACE_Local_WLock, +ACE_Remote_RLock, and ACE_Remote_WLock. + +Here are the options to test_rw_locks: +% ./test_rw_lock -u + -i ignore deadlock + -n <iterations> + -r <reads> + -d debug + -s sleep during writes + -t <threads> + +test_rw_locks spawns <threads> number of threads which perform the +following algorithm: + +for <iterations> + { + for <reads> + acquire read lock + for <reads> + release read lock + + acquire write lock + if (sleep during writes) + sleep for 1 second + release write lock + } + + +The output should show that multiple readers can acquire the lock for +reading simultaneously (note that this also tests recursive +acquisition.) When a writer lock is acquired, the output should show +that no thread holds a reader lock. + +To run a test, simply type: +% ./test_rw_lock + +This should show output as described above. + diff --git a/ACE/netsvcs/clients/Tokens/rw_lock/rw_locks.cpp b/ACE/netsvcs/clients/Tokens/rw_lock/rw_locks.cpp new file mode 100644 index 00000000000..5d0d95af876 --- /dev/null +++ b/ACE/netsvcs/clients/Tokens/rw_lock/rw_locks.cpp @@ -0,0 +1,252 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// examples +// +// = FILENAME +// rw_locks.cpp +// +// = DESCRIPTION +// test_rw_locks shows how to use ACE_Local_RLock, ACE_Local_WLock, +// ACE_Remote_RLock, and ACE_Remote_WLock. +// +// = AUTHOR +// Tim Harrison +// +// ============================================================================ + +#include "ace/Get_Opt.h" +#include "ace/Local_Tokens.h" +#include "ace/Remote_Tokens.h" +#include "ace/Thread_Manager.h" +#include "ace/Token_Invariants.h" + +#if defined (ACE_HAS_THREADS) && defined (ACE_HAS_THREADS_LIBRARY) + +ACE_RCSID(rw_lock, rw_locks, "$Id$") + +typedef ACE_Token_Invariant_Manager ACE_TOKEN_INVARIANTS; + +static ACE_Token_Proxy *global_rlock; +static ACE_Token_Proxy *global_wlock; + +static const char *server_host = ACE_DEFAULT_SERVER_HOST; +static int server_port = ACE_DEFAULT_SERVER_PORT; +static int ignore_deadlock = 0; +static int threads = 2; +static int iterations = 50; +static int debug = 0; +static int remote = 0; +static int reads = 4; +static int write_sleep = 0; +static int renew = 0; + +static void * +run_thread (void *) +{ + for (int x = 0; x < iterations; x++) + { + int y = 0; + for (; y < reads; y++) + { + if (global_rlock->acquire () == -1) + { + if (ACE_Log_Msg::instance ()->errnum () == EDEADLK) + { + ACE_DEBUG ((LM_DEBUG, "rlock deadlock detected\n")); + goto READ_DEADLOCK; + } + else return 0; + } + + if (ACE_TOKEN_INVARIANTS::instance ()->acquired (global_rlock) == 0) + ACE_ERROR_RETURN ((LM_ERROR, "reader acquire violated invariant.\n"), 0); + + ACE_DEBUG ((LM_DEBUG, "(%t) rlock acquired.\n")); + } + + if (renew) + { + ACE_TOKEN_INVARIANTS::instance ()->releasing (global_rlock); + + if (global_rlock->renew () == -1) + { + if (ACE_Log_Msg::instance ()->errnum () == EDEADLK) + { + ACE_DEBUG ((LM_DEBUG, "rlock deadlock detected during renew\n")); + goto READ_DEADLOCK; + } + else return 0; + } + + ACE_DEBUG ((LM_DEBUG, "(%t) rlock renewed.\n")); + + if (ACE_TOKEN_INVARIANTS::instance ()->acquired (global_rlock) == 0) + ACE_ERROR_RETURN ((LM_ERROR, "reader renew violated invariant.\n"), 0); + } + + READ_DEADLOCK: + + for (; y > 0; y--) + { + ACE_TOKEN_INVARIANTS::instance ()->releasing (global_rlock); + if (global_rlock->release () == 0) + ACE_DEBUG ((LM_DEBUG, "(%t) r-released.\n")); + } + + if (global_wlock->acquire () == -1) + ACE_DEBUG ((LM_DEBUG, "wlock deadlock detected\n")); + else + { + if (write_sleep) + ACE_OS::sleep (1); + ACE_DEBUG ((LM_DEBUG, "\t\t(%t) wlock acquired.\n")); + + if (ACE_TOKEN_INVARIANTS::instance ()->acquired (global_wlock) == 0) + ACE_ERROR_RETURN ((LM_ERROR, "writer acquire violated invariant.\n"), 0); + + if (renew) + { + ACE_TOKEN_INVARIANTS::instance ()->releasing (global_wlock); + + if (global_wlock->renew () == -1) + { + if (ACE_Log_Msg::instance ()->errnum () == EDEADLK) + { + ACE_DEBUG ((LM_DEBUG, "wlock deadlock detected during renew\n")); + } + else return 0; + } + + ACE_DEBUG ((LM_DEBUG, "(%t) rlock renewed.\n")); + + if (ACE_TOKEN_INVARIANTS::instance ()->acquired (global_wlock) == 0) + ACE_ERROR_RETURN ((LM_ERROR, "writer renew violated invariant.\n"), 0); + } + + ACE_TOKEN_INVARIANTS::instance ()->releasing (global_wlock); + + if (global_wlock->release () == 0) + ACE_DEBUG ((LM_DEBUG, "\t\t(%t) w-released.\n")); + } + } + + ACE_DEBUG ((LM_DEBUG, "(%t) thread exiting.\n")); + return 0; +} + +static int +parse_args (int argc, char *argv[]) +{ + ACE_LOG_MSG->open (argv[0], ACE_Log_Msg::STDERR); // | ACE_Log_Msg::VERBOSE); + + ACE_Get_Opt get_opt (argc, argv, "t:iun:dr:sp:h:R", 1); + + for (int c; (c = get_opt ()) != -1; ) + { + switch (c) + { + case 'h': // specify the host machine on which the server is running + server_host = get_opt.opt_arg (); + remote = 1; + break; + case 'p': // specify the port on which the server is running + server_port = ACE_OS::atoi (get_opt.opt_arg ()); + remote = 1; + break; + case 't': + threads = ACE_OS::atoi (get_opt.opt_arg ()); + break; + case 'R': + renew = 1; + break; + case 'r': + reads = ACE_OS::atoi (get_opt.opt_arg ()); + break; + case 'd': + debug = 1; + break; + case 's': + write_sleep = 1; + break; + case 'n': + iterations = ACE_OS::atoi (get_opt.opt_arg ()); + break; + case 'i': + ignore_deadlock = 1; + break; + case 'u': + // usage: fallthrough + default: + ACE_ERROR_RETURN ((LM_ERROR, + "%n:\n" + "[-h <remote host>]\n" + "[-p <remote port>]\n" + "[-i ignore deadlock]\n" + "[-n <iterations>]\n" + "[-R perform renews]\n" + "[-r <reads>]\n" + "[-d debug]\n" + "[-s sleep during writes]\n" + "[-t <threads>\n", 1), -1); + } + } + + return 0; +} + +#if defined (ACE_HAS_PTHREADS) +#define SUSPEND 0 +#else +#define SUSPEND THR_SUSPENDED +#endif + +int +main (int argc, char* argv[]) +{ + if (parse_args (argc, argv) == -1) + return -1; + + if (remote) + { + ACE_Remote_Mutex::set_server_address (ACE_INET_Addr (server_port, server_host)); + global_rlock = (ACE_Token_Proxy *) new + ACE_Remote_RLock ("THE_TOKEN", ignore_deadlock, debug); + global_wlock = (ACE_Token_Proxy *) new + ACE_Remote_WLock ("THE_TOKEN", ignore_deadlock, debug); + } + else + { + global_rlock = (ACE_Token_Proxy *) new + ACE_Local_RLock ("THE_TOKEN", ignore_deadlock, debug); + global_wlock = (ACE_Token_Proxy *) new + ACE_Local_WLock ("THE_TOKEN", ignore_deadlock, debug); + } + + ACE_Thread_Manager mgr; + + if (mgr.spawn_n (threads, ACE_THR_FUNC (run_thread), + (void *) 0, + THR_BOUND | SUSPEND) == -1) + ACE_ERROR_RETURN ((LM_DEBUG, "%p\n", "spawn failed"), -1); + +#if ! defined (ACE_HAS_PTHREADS) + if (mgr.resume_all () == -1) + ACE_ERROR_RETURN ((LM_DEBUG, "%p\n", "resume failed"), -1); +#endif + + mgr.wait (); + + return 0; +} + +#else +int +main (int, char *[]) +{ + ACE_ERROR_RETURN ((LM_ERROR, + "threads not supported on this platform\n"), -1); +} +#endif /* ACE_HAS_THREADS */ |