diff options
author | William R. Otte <wotte@dre.vanderbilt.edu> | 2006-07-24 15:50:30 +0000 |
---|---|---|
committer | William R. Otte <wotte@dre.vanderbilt.edu> | 2006-07-24 15:50:30 +0000 |
commit | c44379cc7d9c7aa113989237ab0f56db12aa5219 (patch) | |
tree | 66a84b20d47f2269d8bdc6e0323f338763424d3a /ACE/netsvcs/clients | |
parent | 3aff90f4a822fcf5d902bbfbcc9fa931d6191a8c (diff) | |
download | ATCD-c44379cc7d9c7aa113989237ab0f56db12aa5219.tar.gz |
Repo restructuring
Diffstat (limited to 'ACE/netsvcs/clients')
44 files changed, 4131 insertions, 0 deletions
diff --git a/ACE/netsvcs/clients/Logger/Logger.mpc b/ACE/netsvcs/clients/Logger/Logger.mpc new file mode 100644 index 00000000000..e4dbe0a0046 --- /dev/null +++ b/ACE/netsvcs/clients/Logger/Logger.mpc @@ -0,0 +1,22 @@ +// -*- MPC -*- +// $Id$ + +project(direct logging) : aceexe { + avoids += ace_for_tao + exename = direct_logging + libs += netsvcs + after += netsvcs + Source_Files { + direct_logging.cpp + } +} + +project(indirect logging) : aceexe { + avoids += ace_for_tao + exename = indirect_logging + libs += netsvcs + after += netsvcs + Source_Files { + indirect_logging.cpp + } +} diff --git a/ACE/netsvcs/clients/Logger/Makefile.am b/ACE/netsvcs/clients/Logger/Makefile.am new file mode 100644 index 00000000000..8c3ae8decb3 --- /dev/null +++ b/ACE/netsvcs/clients/Logger/Makefile.am @@ -0,0 +1,58 @@ +## Process this file with automake to create Makefile.in +## +## $Id$ +## +## This file was generated by MPC. Any changes made directly to +## this file will be lost the next time it is generated. +## +## MPC Command: +## /acebuilds/ACE_wrappers-repository/bin/mwc.pl -include /acebuilds/MPC/config -include /acebuilds/MPC/templates -feature_file /acebuilds/ACE_wrappers-repository/local.features -noreldefs -type automake -exclude build,Kokyu + +ACE_BUILDDIR = $(top_builddir) +ACE_ROOT = $(top_srcdir) + +noinst_PROGRAMS = + +## Makefile.direct_logging.am + +if !BUILD_ACE_FOR_TAO +noinst_PROGRAMS += direct_logging + +direct_logging_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +direct_logging_SOURCES = \ + direct_logging.cpp + +direct_logging_LDADD = \ + $(top_builddir)/netsvcs/lib/libnetsvcs.la \ + $(ACE_BUILDDIR)/ace/libACE.la + +endif !BUILD_ACE_FOR_TAO + +## Makefile.indirect_logging.am + +if !BUILD_ACE_FOR_TAO +noinst_PROGRAMS += indirect_logging + +indirect_logging_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +indirect_logging_SOURCES = \ + indirect_logging.cpp + +indirect_logging_LDADD = \ + $(top_builddir)/netsvcs/lib/libnetsvcs.la \ + $(ACE_BUILDDIR)/ace/libACE.la + +endif !BUILD_ACE_FOR_TAO + +## Clean up template repositories, etc. +clean-local: + -rm -f *~ *.bak *.rpo *.sym lib*.*_pure_* core core.* + -rm -f gcctemp.c gcctemp so_locations *.ics + -rm -rf cxx_repository ptrepository ti_files + -rm -rf templateregistry ir.out + -rm -rf ptrepository SunWS_cache Templates.DB diff --git a/ACE/netsvcs/clients/Logger/README b/ACE/netsvcs/clients/Logger/README new file mode 100644 index 00000000000..87e324ab0d8 --- /dev/null +++ b/ACE/netsvcs/clients/Logger/README @@ -0,0 +1,18 @@ +This directory contains two sample logging applications that implement +and test the ACE distributed logging service. + + . indirect_logging.cpp + + This program talks to the ACE Client Logging Daemon on + the localhost, which forwards the messages to Server + Logging Daemon. The Client Logging Daemon and Server + Logging Daemon both must be started before you can run + this test. + + . direct_logging.cpp + + This program talks directly to the Server Logging + Daemon. The Server Logging Daemon must be started + before you can run this test. + +To start these daemons, please check out the ../../servers/ directory. diff --git a/ACE/netsvcs/clients/Logger/direct_logging.cpp b/ACE/netsvcs/clients/Logger/direct_logging.cpp new file mode 100644 index 00000000000..4ed5e67aef3 --- /dev/null +++ b/ACE/netsvcs/clients/Logger/direct_logging.cpp @@ -0,0 +1,84 @@ +// $Id$ + +// This program sends logging records directly to the server, rather +// than going through the client logging daemon. + +#include "ace/SOCK_Connector.h" +#include "ace/Log_Record.h" +#include "ace/Log_Msg.h" +#include "ace/OS_NS_time.h" +#include "ace/OS_NS_stdlib.h" +#include "ace/OS_NS_unistd.h" +#include "ace/CDR_Stream.h" + +ACE_RCSID(Logger, direct_logging, "$Id$") + +static u_short LOGGER_PORT = ACE_DEFAULT_SERVER_PORT; +static const ACE_TCHAR *const LOGGER_HOST = ACE_DEFAULT_SERVER_HOST; +static const ACE_TCHAR *const DATA = ACE_TEXT ("hello world\n"); + +int +ACE_TMAIN (int argc, ACE_TCHAR *argv[]) +{ + u_short logger_port = argc > 1 ? ACE_OS::atoi (argv[1]) : LOGGER_PORT; + const ACE_TCHAR *logger_host = argc > 2 ? argv[2] : LOGGER_HOST; + + ACE_SOCK_Stream logger; + ACE_SOCK_Connector connector; + ACE_INET_Addr addr (logger_port, logger_host); + ACE_Log_Record log_record (LM_DEBUG, + ACE_OS::time ((time_t *) 0), + ACE_OS::getpid ()); + + if (connector.connect (logger, addr) == -1) + ACE_ERROR_RETURN ((LM_ERROR, "%p\n", "open"), -1); + + log_record.msg_data (DATA); + const size_t max_payload_size = + 4 // type() + + 8 // timestamp + + 4 // process id + + 4 // data length + + ACE_Log_Record::MAXLOGMSGLEN // data + + ACE_CDR::MAX_ALIGNMENT; // padding; + + // Insert contents of <log_record> into payload stream. + ACE_OutputCDR payload (max_payload_size); + payload << log_record; + + // Get the number of bytes used by the CDR stream. + ACE_CDR::ULong length = payload.total_length (); + + // Send a header so the receiver can determine the byte order and + // size of the incoming CDR stream. + ACE_OutputCDR header (ACE_CDR::MAX_ALIGNMENT + 8); + header << ACE_OutputCDR::from_boolean (ACE_CDR_BYTE_ORDER); + + // Store the size of the payload that follows + header << ACE_CDR::ULong (length); + + // Use an iovec to send both buffer and payload simultaneously. + iovec iov[2]; + iov[0].iov_base = header.begin ()->rd_ptr (); + iov[0].iov_len = 8; + iov[1].iov_base = payload.begin ()->rd_ptr (); + iov[1].iov_len = length; + + if (logger.sendv_n (iov, 2) == -1) + ACE_ERROR_RETURN ((LM_ERROR, "%p\n", "send"), -1); + else if (logger.close () == -1) + ACE_ERROR_RETURN ((LM_ERROR, "%p\n", "close"), -1); + +#if defined (ACE_WIN32) + // !!Important, Winsock is broken in that if you don't close + // down the connection before exiting main, you'll lose data. + // More over, your server might get "Access Violation" from + // within Winsock functions. + + // Here we close down the connection to Logger by redirecting + // the logging destination back to stderr. + ACE_LOG_MSG->open (0, ACE_Log_Msg::STDERR, 0); +#endif /* ACE_WIN32 */ + + return 0; +} diff --git a/ACE/netsvcs/clients/Logger/indirect_logging.cpp b/ACE/netsvcs/clients/Logger/indirect_logging.cpp new file mode 100644 index 00000000000..40cd0d61e10 --- /dev/null +++ b/ACE/netsvcs/clients/Logger/indirect_logging.cpp @@ -0,0 +1,60 @@ +// $Id$ + +// This is a simple test that sends logging records to the Client +// Logging Daemon running on the localhost. This daemon then forwards +// them to the Server Logging Daemon. If there is no Server Logging +// Daemon, the logging records will be written to stderr. + +#include "ace/OS_NS_time.h" +#include "ace/OS_NS_stdlib.h" +#include "ace/Log_Msg.h" +#include "ace/Log_Record.h" + +ACE_RCSID(Logger, indirect_logging, "$Id$") + +int +ACE_TMAIN (int argc, ACE_TCHAR *argv[]) +{ + const ACE_TCHAR *prog_name = argv[0]; + int iterations = argc < 2 ? 10 : ACE_OS::atoi (argv[1]); + const ACE_TCHAR *logger_key = argc < 3 ? ACE_DEFAULT_LOGGER_KEY : argv[2]; + int verbose = argc < 4 ? 0 : ACE_Log_Msg::VERBOSE; + + ACE_OS::srand ((u_int) ACE_OS::time (0)); + + if (ACE_LOG_MSG->open (prog_name, ACE_Log_Msg::LOGGER, logger_key) == -1) + { + ACE_ERROR ((LM_ERROR, "Cannot open logger, using STDERR\n")); + + if (ACE_LOG_MSG->open (prog_name, ACE_Log_Msg::STDERR | verbose) == -1) + ACE_ERROR_RETURN ((LM_ERROR, "Cannot open logger\n"), -1); + } + + ACE_DEBUG ((LM_STARTUP, "starting up the test\n")); + + for (int i = 0; i < iterations; i++) + { + size_t priority = ACE_OS::rand () % int (LM_MAX); + ACE_POW (priority); + ACE_Log_Priority log_priority = ACE_Log_Priority (priority); + ACE_DEBUG ((log_priority, + "random message %s (%d)...\n", + ACE_Log_Record::priority_name (log_priority), + priority)); + } + + ACE_DEBUG ((LM_SHUTDOWN, "closing down the test\n")); + +#if defined (ACE_WIN32) + // !!Important, Winsock is broken in that if you don't close + // down the connection before exiting main, you'll lose data. + // More over, your server might get "Access Violation" from + // within Winsock functions. + + // Here we close down the connection to Logger by redirecting + // the logging destination back to stderr. + ACE_LOG_MSG->open (0, ACE_Log_Msg::STDERR, 0); +#endif /* ACE_WIN32 */ + + return 0; +} diff --git a/ACE/netsvcs/clients/Makefile.am b/ACE/netsvcs/clients/Makefile.am new file mode 100644 index 00000000000..c959505ff73 --- /dev/null +++ b/ACE/netsvcs/clients/Makefile.am @@ -0,0 +1,14 @@ +## Process this file with automake to create Makefile.in +## +## $Id$ +## +## This file was generated by MPC. Any changes made directly to +## this file will be lost the next time it is generated. +## +## MPC Command: +## /acebuilds/ACE_wrappers-repository/bin/mwc.pl -include /acebuilds/MPC/config -include /acebuilds/MPC/templates -feature_file /acebuilds/ACE_wrappers-repository/local.features -noreldefs -type automake -exclude build,Kokyu + +SUBDIRS = \ + Logger \ + Naming + diff --git a/ACE/netsvcs/clients/Naming/Client/Client.mpc b/ACE/netsvcs/clients/Naming/Client/Client.mpc new file mode 100644 index 00000000000..54d828b8774 --- /dev/null +++ b/ACE/netsvcs/clients/Naming/Client/Client.mpc @@ -0,0 +1,21 @@ +// -*- MPC -*- +// $Id$ + +project(Netsvsc_Client_Test_Lib): acelib { + avoids += ace_for_tao + sharedname = Client_Test + dynamicflags += ACE_CLIENT_TEST_BUILD_DLL + Source_Files { + Client_Test.cpp + } +} + +project(Netsvcs_Client_Test) : aceexe { + avoids += ace_for_tao + exename = main + libs += Client_Test + after += Client_Test Netsvsc_Client_Test_Lib + Source_Files { + main.cpp + } +} diff --git a/ACE/netsvcs/clients/Naming/Client/Client_Test.cpp b/ACE/netsvcs/clients/Naming/Client/Client_Test.cpp new file mode 100644 index 00000000000..0cc05964720 --- /dev/null +++ b/ACE/netsvcs/clients/Naming/Client/Client_Test.cpp @@ -0,0 +1,651 @@ +// $Id$ + +#define ACE_BUILD_SVC_DLL + +#include "ace/Service_Config.h" +#include "ace/Naming_Context.h" +#include "ace/Dynamic_Service.h" +#include "ace/Thread_Manager.h" +#include "Client_Test.h" +#include "ace/Reactor.h" +#include "ace/os_include/os_ctype.h" +#include "ace/OS_NS_signal.h" +#include "ace/OS_NS_stdio.h" +#include "ace/OS_NS_string.h" +#include "ace/OS_NS_unistd.h" +#include "ace/os_include/os_assert.h" + +ACE_RCSID (Client, + Client_Test, + "$Id$") + +class ACE_Svc_Export Client_Test : public ACE_Service_Object +{ +public: + Client_Test (void); + + int open (void); + // Cache reactor and then register self with reactor + + int close (void); + // Close things down and free up resources. + + virtual int handle_input (ACE_HANDLE handle); + // Handle user entered commands + + virtual int init (int argc, ACE_TCHAR *argv[]); + // Initialize name options and naming context when dynamically + // linked. + + virtual int fini (void); + // Close down the test when dynamically unlinked. + + void list_options (void); + // Print name options + + int bind (const char *key, + const char *value, + const char *type = ""); + // Bind a key to a value + + int unbind (const char *key); + // Unbind a name binding + + int rebind (const char *key, + const char *value, + const char *type = ""); + // Rebind a name binding + + int find (const char *key); + // Find the value associated with a key + + int list_names (const char *pattern); + // Find all names that match pattern + + int list_values (const char *pattern); + // Find all values that match pattern + + int list_types (const char *pattern); + // Find all types that match pattern + + int list_name_entries (const char *pattern); + // Find all names that match pattern + + int list_value_entries (const char *pattern); + // Find all values that match pattern + + int list_type_entries (const char *pattern); + // Find all types that match pattern + +private: + ACE_Name_Options *name_options_; + // Name Options associated with the Naming Context + + void display_menu (void); + // Display user menu + + int set_proc_local (void); + // Set options to use PROC_LOCAL naming context + + int set_node_local (void); + // Set options to use NODE_LOCAL naming context + + int set_host (const char *hostname, int port); + // Set options to use NET_LOCAL naming context specifying host name + // and port number + + int quit (void); + // Gracefully exit +}; + +// The following Factory is used by the ACE_Service_Config and +// svc.conf file to dynamically initialize the state of the client +// test. + +ACE_SVC_FACTORY_DEFINE (Client_Test) + +// Get the instance of Name_Service using Dynamic_Service + +//inline Name_Service * +//NAME_SERVICE (void) + +inline ACE_Naming_Context * +NAMING_CONTEXT (void) +{ + return ACE_Dynamic_Service<ACE_Naming_Context>::instance ("ACE_Naming_Context"); +} + +Client_Test::Client_Test (void) +{ + ACE_DEBUG ((LM_DEBUG, + "Client_Test::Client_Test\n")); +} + +int +Client_Test::init (int /* argc */, + ACE_TCHAR * /* argv */ []) +{ + ACE_DEBUG ((LM_DEBUG, "Client_Test::init\n")); + + // Cache the name options. + this->name_options_ = NAMING_CONTEXT ()->name_options (); + return this->open (); +} + +int +Client_Test::open (void) +{ + this->display_menu (); + + if (ACE_Event_Handler::register_stdin_handler (this, + ACE_Reactor::instance (), + ACE_Thread_Manager::instance ()) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + "%p\n", + "register_stdin_handler"), + -1); + return 0; +} + + +int +Client_Test::close (void) +{ + // Deregister this handler with the ACE_Reactor. + return ACE_Reactor::instance ()->remove_handler + (ACE_STDIN, + ACE_Event_Handler::DONT_CALL | ACE_Event_Handler::READ_MASK); +} + +int +Client_Test::fini (void) +{ + ACE_DEBUG ((LM_DEBUG, + "Client_Test::fini\n")); + return this->close (); +} + +int +Client_Test::handle_input (ACE_HANDLE) +{ + char option[BUFSIZ]; + char buf1[BUFSIZ]; + char buf2[BUFSIZ]; + char buf3[BUFSIZ]; + char *temp_buf; + int port; + char input[1024]; + + if (::scanf ("%s", option) <= 0) + ACE_ERROR_RETURN ((LM_ERROR, + "%p Try again!\n", + "Client_Test::handle_input"), + 0); + + int result = -1; + + switch (isupper (option[0]) ? tolower (option[0]) : option[0]) + { + case 'p' : + result = this->set_proc_local (); + break; + case 'n' : + result = this->set_node_local (); + break; + case 'h' : + if (::scanf ("%s %d", buf1, &port) <= 0) + break; + result = this->set_host (buf1, port); + break; + case 'b' : + // get the input from stdin + ACE_OS::fgets (input, sizeof input, stdin); + + // get the key + if ((temp_buf = ACE_OS::strtok (input, " "))) + { + ACE_OS::strcpy (buf1, temp_buf); + + temp_buf = ACE_OS::strtok (0, " "); + + // get the value + if (temp_buf) + { + ACE_OS::strcpy (buf2, temp_buf); + + temp_buf = ACE_OS::strtok (0, " "); + + // get the type (if entered). + if (temp_buf) + { + ACE_OS::strcpy (buf3, temp_buf); + result = this->bind (buf1, buf2, buf3); + } + else + result = this->bind (buf1, buf2); + } + else + ACE_ERROR ((LM_ERROR, + "Bind Failed! Value not entered.\n")); + } + else + ACE_ERROR ((LM_ERROR, + "Bind Failed! Key and Value not entered.\n")); + break; + case 'u' : + if (::scanf ("%s", buf1) <= 0) + break; + result = this->unbind (buf1); + break; + case 'r' : + // get the input from stdin + ACE_OS::fgets (input, sizeof input, stdin); + + temp_buf = ACE_OS::strtok (input, " "); + // get the key + if (temp_buf) + { + ACE_OS::strcpy (buf1, temp_buf); + + temp_buf = ACE_OS::strtok (0, " "); + + // get the value + if (temp_buf) + { + ACE_OS::strcpy (buf2, temp_buf); + + temp_buf = ACE_OS::strtok (0, " "); + // get the type (if entered) + if (temp_buf) + { + ACE_OS::strcpy (buf3, temp_buf); + result = this->rebind (buf1, buf2, buf3); + } + else + result = this->rebind (buf1, buf2); + } + else + ACE_ERROR ((LM_ERROR, + "Rebind Failed! Value not entered.\n")); + } + else + ACE_ERROR ((LM_ERROR, + "Reind Failed! Key and value not entered.\n")); + break; + case 'f' : + if (::scanf ("%s", buf1) <= 0) + break; + result = this->find (buf1); + break; + case 'j' : + if (::scanf ("%s", buf1) <= 0) + break; + else + result = this->list_names (buf1); + break; + case 'k' : + if (::scanf ("%s", buf1) <= 0) + break; + else + result = this->list_values (buf1); + break; + case 'l' : + if (::scanf ("%s", buf1) <= 0) + break; + else + result = this->list_types (buf1); + break; + case 'c' : + if (::scanf ("%s", buf1) <= 0) + break; + else + result = this->list_name_entries (buf1); + break; + case 'd' : + if (::scanf ("%s", buf1) <= 0) + break; + else + result = this->list_value_entries (buf1); + break; + case 'e' : + if (::scanf ("%s", buf1) <= 0) + break; + else + result = this->list_type_entries (buf1); + break; + case 'q' : + result = this->quit (); + break; + default : + ACE_DEBUG ((LM_DEBUG, + "Unrecognized command.\n")); + } + + this->display_menu (); + return result; +} + +void +Client_Test::display_menu (void) +{ + ACE_DEBUG ((LM_DEBUG, "\n")); + this->list_options (); + ACE_DEBUG ((LM_DEBUG, " Name Service Main Menu\n")); + ACE_DEBUG ((LM_DEBUG, " ----------------------\n")); + ACE_DEBUG ((LM_DEBUG, "<P> Use Process Local Database\n")); + ACE_DEBUG ((LM_DEBUG, "<N> Use Node Local Database\n"));; + ACE_DEBUG ((LM_DEBUG, "<H> Set Remote Name server <host> and <port>\n\n")); + ACE_DEBUG ((LM_DEBUG, "<B> Bind <key> <value> [<type>]\n")); + ACE_DEBUG ((LM_DEBUG, "<U> Unbind <key>\n")); + ACE_DEBUG ((LM_DEBUG, "<R> Rebind <key> <value> [<type>]\n")); + ACE_DEBUG ((LM_DEBUG, "<F> Find <key>\n")); + ACE_DEBUG ((LM_DEBUG, "<J> Lookup keys matching <pattern>\n")); + ACE_DEBUG ((LM_DEBUG, "<K> Lookup values matching <pattern>\n")); + ACE_DEBUG ((LM_DEBUG, "<L> Lookup types matching <pattern>\n")); + ACE_DEBUG ((LM_DEBUG, "<C> Complete lookup keys matching <pattern>\n")); + ACE_DEBUG ((LM_DEBUG, "<D> Complete lookup values matching <pattern>\n")); + ACE_DEBUG ((LM_DEBUG, "<E> Complete lookup types matching <pattern>\n")); + + ACE_DEBUG ((LM_DEBUG, "<Q> or ^C (exit)\n")); +} + +void +Client_Test::list_options (void) +{ + switch (this->name_options_->context ()) + { + case ACE_Naming_Context::PROC_LOCAL: + ACE_DEBUG ((LM_DEBUG, + " *** Using Process Local Database\n")); + break; + case ACE_Naming_Context::NODE_LOCAL: + ACE_DEBUG ((LM_DEBUG, + " *** Using Node Local Database\n")); + break; + case ACE_Naming_Context::NET_LOCAL: + ACE_DEBUG ((LM_DEBUG, + " *** Hostname: %s\n", + this->name_options_->nameserver_host ())); + ACE_DEBUG ((LM_DEBUG, + " *** Port Number: %d\n", + this->name_options_->nameserver_port ())); + break; + default: + ACE_ERROR ((LM_ERROR, "ERROR: shouldn't occur!\n")); + break; + } + ACE_DEBUG ((LM_DEBUG, + " *** Namespace directory is %s ***\n", + this->name_options_->namespace_dir ())); +} + +int +Client_Test::set_proc_local (void) +{ + // Close down original name space + NAMING_CONTEXT ()->close (); + this->name_options_->nameserver_host (ACE_TEXT ("localhost")); + this->name_options_->context (ACE_Naming_Context::PROC_LOCAL); + return NAMING_CONTEXT ()->open (ACE_Naming_Context::PROC_LOCAL); +} + +int +Client_Test::set_node_local (void) +{ + // Close down original name space + NAMING_CONTEXT ()->close (); + this->name_options_->nameserver_host (ACE_TEXT ("localhost")); + this->name_options_->context (ACE_Naming_Context::NODE_LOCAL); + return NAMING_CONTEXT ()->open (ACE_Naming_Context::NODE_LOCAL); +} + +int +Client_Test::set_host (const char *hostname, int port) +{ + // Close down original name space + NAMING_CONTEXT ()->close (); + + this->name_options_->context (ACE_Naming_Context::NET_LOCAL); + // Set Name Options + this->name_options_->nameserver_host (ACE_TEXT_CHAR_TO_TCHAR (hostname)); + this->name_options_->nameserver_port (port); + + return NAMING_CONTEXT ()->open (ACE_Naming_Context::NET_LOCAL); +} + +int +Client_Test::quit (void) +{ + // Send ourselves a SIGINT! + return ACE_OS::kill (ACE_OS::getpid (), SIGINT); +} + +int +Client_Test::bind (const char *key, + const char *value, + const char *type) +{ + if (NAMING_CONTEXT ()->bind (key, value, type) != 0) + ACE_ERROR_RETURN ((LM_ERROR, + "%p Bind failed! Key %s exists\n", + "Client_Test::bind", + key), + 0); + return 0; +} + +int +Client_Test::unbind (const char *key) +{ + if (NAMING_CONTEXT ()->unbind (key) != 0) + ACE_ERROR_RETURN ((LM_ERROR, + "%p Unbind failed! Key %s not found\n", + "Client_Test::unbind", + key), + 0); + return 0; +} + +int +Client_Test::rebind (const char *key, + const char *value, + const char *type) +{ + int result = NAMING_CONTEXT ()->rebind (key, value, type ); + return result == 1 ? 0 : result; +} + +int +Client_Test::list_names (const char *pattern) +{ + ACE_PWSTRING_SET set; + + if (NAMING_CONTEXT ()->list_names (set, pattern) != 0) + ACE_ERROR_RETURN ((LM_ERROR, + "%p Pattern matching failed!\n", + "Client_Test::list_names"), + 0); + else + { + ACE_PWSTRING_ITERATOR set_iterator (set); + + for (ACE_NS_WString *name = 0; + set_iterator.next (name) !=0; + set_iterator.advance()) + ACE_DEBUG ((LM_DEBUG, + "%s\n", + name->char_rep ())); + } + return 0; +} + +int +Client_Test::list_values (const char *pattern) +{ + ACE_PWSTRING_SET set; + + if (NAMING_CONTEXT ()->list_values (set, pattern) != 0) + ACE_ERROR_RETURN ((LM_ERROR, + "%p Pattern matching failed!\n", + "Client_Test::list_values"), + 0); + else + { + ACE_PWSTRING_ITERATOR set_iterator (set); + + for (ACE_NS_WString *value = 0; + set_iterator.next (value) !=0; + set_iterator.advance()) + ACE_DEBUG ((LM_DEBUG, + "%s\n", + value->char_rep ())); + } + return 0; +} + +int +Client_Test::list_types (const char *pattern) +{ + ACE_PWSTRING_SET set; + + if (NAMING_CONTEXT ()->list_types (set, pattern) != 0) + ACE_ERROR_RETURN ((LM_ERROR, + "%p Pattern matching failed!\n", + "Client_Test::list_types"), + 0); + else + { + ACE_PWSTRING_ITERATOR set_iterator (set); + + for (ACE_NS_WString *type = 0; + set_iterator.next (type) !=0; + set_iterator.advance()) + ACE_DEBUG ((LM_DEBUG, + "%s\n", + type->char_rep ())); + } + return 0; +} + +int +Client_Test::list_name_entries (const char *pattern) +{ + ACE_BINDING_SET set; + + if (NAMING_CONTEXT ()->list_name_entries (set, pattern) != 0) + ACE_ERROR_RETURN ((LM_ERROR, + "%p Pattern matching failed!\n", + "Client_Test::list_names"), + 0); + else + { + ACE_BINDING_ITERATOR set_iterator (set); + + for (ACE_Name_Binding *entry = 0; + set_iterator.next (entry) !=0; + set_iterator.advance()) + { + ACE_DEBUG ((LM_DEBUG, + "%s\t", + entry->name_.char_rep ())); + ACE_DEBUG ((LM_DEBUG, + "%s\t", + entry->value_.char_rep ())); + if (entry->type_) + ACE_DEBUG ((LM_DEBUG, + "%s\n", + entry->type_)); + } + } + return 0; +} + +int +Client_Test::list_value_entries (const char *pattern) +{ + ACE_BINDING_SET set; + + if (NAMING_CONTEXT ()->list_value_entries (set, pattern) != 0) + ACE_ERROR_RETURN ((LM_ERROR, + "%p Pattern matching failed!\n", + "Client_Test::list_values"), + 0); + else + { + ACE_BINDING_ITERATOR set_iterator (set); + for (ACE_Name_Binding *entry = 0; + set_iterator.next (entry) !=0; + set_iterator.advance()) + { + ACE_DEBUG ((LM_DEBUG, + "%s\t", + entry->name_.char_rep ())); + ACE_DEBUG ((LM_DEBUG, + "%s\t", + entry->value_.char_rep ())); + if (entry->type_) + ACE_DEBUG ((LM_DEBUG, + "%s\n", + entry->type_)); + } + } + return 0; +} + +int +Client_Test::list_type_entries (const char *pattern) +{ + ACE_BINDING_SET set; + + if (NAMING_CONTEXT ()->list_type_entries (set, pattern) != 0) + ACE_ERROR_RETURN ((LM_ERROR, + "%p Pattern matching failed!\n", + "Client_Test::list_types"), + 0); + else + { + ACE_BINDING_ITERATOR set_iterator (set); + + for (ACE_Name_Binding *entry = 0; + set_iterator.next (entry) !=0; + set_iterator.advance()) + { + ACE_DEBUG ((LM_DEBUG, + "%s\t", + entry->name_.char_rep ())); + ACE_DEBUG ((LM_DEBUG, + "%s\t", + entry->value_.char_rep ())); + ACE_DEBUG ((LM_DEBUG, + "%s\n", + entry->type_)); + } + } + return 0; +} + +int +Client_Test::find (const char *key) +{ + char *value = 0; + char *type = 0; + + if (NAMING_CONTEXT ()->resolve (key, value, type) != 0) + ACE_ERROR_RETURN ((LM_ERROR, + "%p Find failed! Key %s not found\n", + "Client_Test::list_find", + key), + 0); + else + { + ACE_DEBUG ((LM_DEBUG, + "Binding for %s : value = %s\ttype = %s\n", + key, + value, + type)); + if (type) + delete [] type; + return 0; + } +} + diff --git a/ACE/netsvcs/clients/Naming/Client/Client_Test.h b/ACE/netsvcs/clients/Naming/Client/Client_Test.h new file mode 100644 index 00000000000..1df4c5c0e05 --- /dev/null +++ b/ACE/netsvcs/clients/Naming/Client/Client_Test.h @@ -0,0 +1,13 @@ +// -*- C++ -*- +// +// $Id$ + +#include "ace/svc_export.h" + +#if !defined (ACE_LACKS_PRAGMA_ONCE) +# pragma once +#endif /* ACE_LACKS_PRAGMA_ONCE */ + +// Define the external Client_Test interface. + +ACE_SVC_FACTORY_DECLARE (Client_Test) diff --git a/ACE/netsvcs/clients/Naming/Client/Makefile.am b/ACE/netsvcs/clients/Naming/Client/Makefile.am new file mode 100644 index 00000000000..548b9be4a8e --- /dev/null +++ b/ACE/netsvcs/clients/Naming/Client/Makefile.am @@ -0,0 +1,59 @@ +## Process this file with automake to create Makefile.in +## +## $Id$ +## +## This file was generated by MPC. Any changes made directly to +## this file will be lost the next time it is generated. +## +## MPC Command: +## /acebuilds/ACE_wrappers-repository/bin/mwc.pl -include /acebuilds/MPC/config -include /acebuilds/MPC/templates -feature_file /acebuilds/ACE_wrappers-repository/local.features -noreldefs -type automake -exclude build,Kokyu + +ACE_BUILDDIR = $(top_builddir) +ACE_ROOT = $(top_srcdir) + + +## Makefile.Netsvsc_Client_Test_Lib.am + +if !BUILD_ACE_FOR_TAO + +noinst_LTLIBRARIES = libClient_Test.la + +libClient_Test_la_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) \ + -DACE_CLIENT_TEST_BUILD_DLL + +libClient_Test_la_SOURCES = \ + Client_Test.cpp + +noinst_HEADERS = \ + Client_Test.h + +endif !BUILD_ACE_FOR_TAO + +## Makefile.Netsvcs_Client_Test.am + +if !BUILD_ACE_FOR_TAO +noinst_PROGRAMS = main + +main_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +main_SOURCES = \ + main.cpp \ + Client_Test.h + +main_LDADD = \ + libClient_Test.la \ + $(ACE_BUILDDIR)/ace/libACE.la + +endif !BUILD_ACE_FOR_TAO + +## Clean up template repositories, etc. +clean-local: + -rm -f *~ *.bak *.rpo *.sym lib*.*_pure_* core core.* + -rm -f gcctemp.c gcctemp so_locations *.ics + -rm -rf cxx_repository ptrepository ti_files + -rm -rf templateregistry ir.out + -rm -rf ptrepository SunWS_cache Templates.DB diff --git a/ACE/netsvcs/clients/Naming/Client/README b/ACE/netsvcs/clients/Naming/Client/README new file mode 100644 index 00000000000..68e69b6b3bf --- /dev/null +++ b/ACE/netsvcs/clients/Naming/Client/README @@ -0,0 +1,123 @@ +This directory contains a test for the ACE_Name_Server library. This +test program also illustrates how to use the ACE Service_Config +mechanism, which enables the client code to be dynamically linked into +the process at installation-time or run-time. + +The client test is an application that allows the user to vary the +test parameters through the following menu driven interface: + + Name Service Main Menu + ---------------------- + *** Using Process Local Database *** + +<P> Use Process Local Database +<N> Use Node Local Database +<H> Set Remote Name server <host> and <port> + +<B> Bind <key> <value> [<type>] +<U> Unbind <key> +<R> Rebind <key> <value> [<type>] +<F> Find <key> +<J> Lookup keys matching <pattern> +<K> Lookup values matching <pattern> +<L> Lookup types matching <pattern> +<C> Complete lookup keys matching <pattern> +<D> Complete lookup values matching <pattern> +<E> Complete lookup types matching <pattern> + +<Q> or ^C (exit) + +Initially, the user can select the type of database -- process local, +node local, or net local -- from the menu. + +<P> uses the process local database (i.e., the database is called the + same name as the process and stored in /tmp). +<N> uses the node local database (which defaults to /tmp/localnames). +<H> uses the net local database by specifying host and port number (by + default this is stored in a file called /tmp/globalnames on the server). + +The user can then create new bindings, delete existing bindings, or +rebind bindings: + +<B> Bind <key> <value> [<type>] + -- binds the key to the value and adds the + binding to the database. Note that type + information is optional. +<U> Unbind <key> -- unbind a binding whose key is <key> +<R> Rebind <key> <value> [<type>] + -- rebind <key> to <value>. Once again <type> is optional. +<F> Find <key> -- find the binding associated with key <key> +<Q> or ^C (exit) -- exit gracefully, saving the contents of the + Name Server in persistent shared memory. + +In addition, the user can do pattern matching for keys, values, and +types. Note that pattern matching is supported using regular expressions. + +<J> Lookup keys matching <pattern> + -- find all keys that match <pattern> +<K> Lookup values matching <pattern> + -- find all values that match <pattern> +<L> Lookup types matching <pattern> + -- find all types that match <pattern> + +<C> Complete lookup keys matching <pattern> + -- find all bindings whose keys match <pattern> +<D> Complete lookup values matching <pattern> + -- find all bindings whose values match <pattern> +<E> Complete lookup types matching <pattern> + -- find all bindings whose types match <pattern> + +------------------------- +Running the tests: + +The test program uses a DLL supported by the svc.conf file, which +allows them to configure the client-side dynamically. The client test +program accomplishes this by making use of a Singleton proxy object +(Name_Service) to provide an interface to the client-side. + +The test programs rely on svc.conf to provide the necessary parameters +for dynamically linking the Name Service library and then +executing. In the absence of svc.conf, the test programs would use +static binding. + +client: + +The client test can be started without any parameters. However, if the +user wants to use the net local database, the hostname and the port +number of the server containing the net local database can be given at +"command line" in the svc.conf file, e.g.: + +dynamic ACE_Naming_Context Service_Object * libACE.so:_make_ACE_Naming_Context () + "main -h tango.cs -p 7891" +dynamic Name_Server_test Service_Object * .shobj/Client_Test.so:_make_Client_Test () "" + +The above example starts the client test application and sets up a +connection to port 7891 to a Name Server running on tango.cs, which +has the net local database. The Client_Test directive must come after +ACE_Naming_Context since it relies on the ACE_Naming_Context having +been dynamically linked. + +Note that you can also use environment variables in the "command +line", as follows: + +dynamic ACE_Naming_Context Service_Object * libACE.so:_make_ACE_Naming_Context () + "main -s $DB -p $PORT -h tango" +dynamic Name_Server_test Service_Object * .shobj/Client_Test.so:_make_Client_Test () "" + +In this example, $DB and $PORT are environment variables that are +automatically interpreted and substituted by ACE. In addition, note +how you can give a relative name for the libACE_svcs.so and ACE will +locate this for you automatically by reading your LD search path. + +server: + +The name server is needed only in the case where the net local +database needs to be accessed. The server test needs to run on the +machine that contains the net local database. To execute the server +test, the user has to specify the port number at which the server will +be listening in the svc.conf file. An implementation of a name +service for ACE is available in the $ACE_ROOT/netsvcs/{lib,servers} +directories. Please see the README files there for an explanation of +how to run the server. + + diff --git a/ACE/netsvcs/clients/Naming/Client/main.cpp b/ACE/netsvcs/clients/Naming/Client/main.cpp new file mode 100644 index 00000000000..e355386f4bb --- /dev/null +++ b/ACE/netsvcs/clients/Naming/Client/main.cpp @@ -0,0 +1,80 @@ +// $Id$ + +// Test the client-side of the ACE Name Server... + +#include "ace/Service_Config.h" +#include "ace/Naming_Context.h" +#include "ace/ARGV.h" +#include "ace/Log_Msg.h" +#include "ace/Reactor.h" + +#include "Client_Test.h" + +ACE_RCSID (Client, + main, + "$Id$") + +int +ACE_TMAIN (int, ACE_TCHAR *argv[]) +{ + ACE_Service_Config daemon; + ACE_ARGV new_args; + + // Load the existing <argv> into our new one. + new_args.add (argv); + // Enable loading of static services. + new_args.add (ACE_TEXT ("-y")); + // Enable debugging within dynamically linked services. + new_args.add (ACE_TEXT ("-d")); + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("argc = %d\n"), + new_args.argc ())); + + // Print the contents of the combined <ACE_ARGV>. + for (int i = 0; i < new_args.argc (); i++) + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%d) %s\n"), + i, + new_args.argv ()[i])); + + if (daemon.open (new_args.argc (), + new_args.argv ()) == -1) + { + if (errno != ENOENT) + ACE_ERROR_RETURN ((LM_ERROR, ACE_TEXT ("%p\n"), ACE_TEXT ("open")), + 1); + else // Use static binding. + { + ACE_ARGV args; + + args.add (argv[0]); + args.add (ACE_TEXT ("-p10011")); // Port number. + ACE_Service_Object *so = + ACE_SVC_INVOKE (ACE_Naming_Context); + + if (so->init (args.argc (), + args.argv ()) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("ACE_Naming_Context")), + 1); + + so = ACE_SVC_INVOKE (Client_Test); + + if (so->init (0, + args.argv ()) == -1) + ACE_ERROR ((LM_ERROR, + "%p\n%a", + "Client_Test", + 1)); + } + } + + // Run forever, performing the configured services until we are shut + // down by a SIGINT/SIGQUIT signal. + + ACE_Reactor::run_event_loop (); + + return 0; +} diff --git a/ACE/netsvcs/clients/Naming/Client/svc.conf b/ACE/netsvcs/clients/Naming/Client/svc.conf new file mode 100644 index 00000000000..ba583b0d446 --- /dev/null +++ b/ACE/netsvcs/clients/Naming/Client/svc.conf @@ -0,0 +1,7 @@ +# Note that $PORT is an environment variable that is +# automatically interpreted and substituted by ACE! +# static ACE_Naming_Context "main -p $PORT -h tango" +dynamic ACE_Naming_Context Service_Object * ACE:_make_ACE_Naming_Context () "main -p $PORT -h tango" +dynamic Name_Server_test Service_Object * Client_Test:_make_Client_Test () +# Note: Client_Test must come after ACE_Naming_Context since it relies +# on the ACE_Naming_Context having been linked... diff --git a/ACE/netsvcs/clients/Naming/Client/svc2.conf b/ACE/netsvcs/clients/Naming/Client/svc2.conf new file mode 100644 index 00000000000..44a276a4a44 --- /dev/null +++ b/ACE/netsvcs/clients/Naming/Client/svc2.conf @@ -0,0 +1,9 @@ +# Note that $DB and $PORT are environment variables that are +# automatically interpreted and substituted by ACE! In addition, note +# how you can give a relative name for the libACE_svcs.so and ACE will +# locate this for you automatically by reading your LD search path! +dynamic ACE_Naming_Context Service_Object * ACE:_make_ACE_Naming_Context () "main -s $DB" +dynamic ACE_Naming_Context2 Service_Object * ACE:_make_ACE_Naming_Context () "main -s $DB" +dynamic Name_Server_test Service_Object * Client_Test:_make_Client_Test () +# Note: Client_Test must come after ACE_Naming_Context since it relies +# on the ACE_Naming_Context having been dynamically linked. diff --git a/ACE/netsvcs/clients/Naming/Dump_Restore/Dump_Restore.cpp b/ACE/netsvcs/clients/Naming/Dump_Restore/Dump_Restore.cpp new file mode 100644 index 00000000000..bfda59cd09f --- /dev/null +++ b/ACE/netsvcs/clients/Naming/Dump_Restore/Dump_Restore.cpp @@ -0,0 +1,467 @@ +// $Id$ + +#include "ace/Malloc_Base.h" +#include "ace/Service_Config.h" +#include "ace/Read_Buffer.h" +#include "ace/Thread_Manager.h" + +// FUZZ: disable check_for_streams_include +#include "ace/streams.h" /* Because dump () uses ofstream. */ + +#include "Dump_Restore.h" +#include "ace/OS_NS_signal.h" +#include "ace/OS_NS_stdio.h" +#include "ace/OS_NS_string.h" +#include "ace/OS_NS_unistd.h" + +ACE_RCSID(Dump_Restore, Dump_Restore, "$Id$") + +Dump_Restore::Dump_Restore (int argc, ACE_TCHAR *argv[]) + : infile_ (0) +{ + ACE_NEW (this->ns_context_, + ACE_Naming_Context); + + // Cache the name options + this->name_options_ = this->ns_context_->name_options (); + this->name_options_->parse_args (argc, argv); + + //determine name context + if (ACE_OS::strcmp (this->name_options_->nameserver_host (), + ACE_TEXT ("localhost")) == 0) + { + if (ns_context_->open (ACE_Naming_Context::PROC_LOCAL) == -1) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("ns_context_->open"))); + } + else + { + // Don't really need to do this but it's a hack to fix the + // problme of Display () not printing the right hostname + ACE_OS::strcpy (this->hostname_, + this->name_options_->nameserver_host ()); + this->port_ = + this->name_options_->nameserver_port (); + + if (this->ns_context_->open (ACE_Naming_Context::NET_LOCAL) == -1) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("ns_context_->open"))); + } + + this->display_menu (); + + if (ACE_Event_Handler::register_stdin_handler (this, + ACE_Reactor::instance (), + ACE_Thread_Manager::instance ()) == -1) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("register_stdin_handler"))); +} + +Dump_Restore::~Dump_Restore (void) +{ + // Deregister this handler with the ACE_Reactor. + ACE_Reactor::instance ()->remove_handler + (ACE_STDIN, + ACE_Event_Handler::DONT_CALL | ACE_Event_Handler::READ_MASK); + + ACE_OS::fclose (this->infile_); +} + +int +Dump_Restore::handle_input (ACE_HANDLE) +{ + char option[BUFSIZ]; + char buf1[BUFSIZ]; + u_short port; + + if (::scanf ("%s", option) <= 0) + { + ACE_DEBUG ((LM_ERROR, + ACE_TEXT ("try again\n"))); + return 0; + } + + switch (option[0]) + { + case 'P' : + case 'p' : + set_proc_local (); + break; + case 'N' : + case 'n' : + set_node_local (); + break; + case 'H' : + case 'h' : + if (::scanf ("%s %hu", buf1, &port) <= 0) + break; + set_host (ACE_TEXT_CHAR_TO_TCHAR (buf1), port); + break; + case 'F': + case 'f': + if (::scanf ("%s", filename_) <= 0) + break; + if (this->infile_) + ACE_OS::fclose (this->infile_); + this->infile_ = fopen(filename_,"r"); + break; + case 'B' : + case 'b' : + populate (Dump_Restore::BIND); + break; + case 'U' : + case 'u' : + populate (Dump_Restore::UNBIND); + break; + case 'R' : + case 'r' : + populate (Dump_Restore::REBIND); + break; + case 'D': + case 'd': + if (::scanf ("%s", dump_filename_) <= 0) + break; + this->dump (); + break; + case 'Q' : + case 'q' : + quit (); + break; + default : + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Unrecognized command.\n"))); + } + + display_menu (); + return 0; +} + +void +Dump_Restore::display_menu (void) +{ + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("\n"))); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("Name Service Main Menu\n"))); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("----------------------\n"))); + + // Check if using local name space or remote name space + if (ACE_OS::strcmp (this->name_options_->nameserver_host (), + ACE_TEXT ("localhost")) == 0) + { + if (this->ns_scope_ == ACE_Naming_Context::PROC_LOCAL) + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT (" *** Using Process Local Database ***\n\n"))); + else + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT (" *** Using Node Local Database ***\n\n"))); + } + else + { + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT (" Hostname: %s\n"), + this->hostname_)); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT (" Port Number: %d\n"), + this->port_)); + } + + if (this->infile_) + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("Input File: %C\n"), + this->filename_)); + else + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("** No Input File Specified **\n"))); + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("<P> Use Process Local Database\n"))); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("<N> Use Node Local Database\n"))); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("<H> Set Remote Name server <host> and <port>\n"))); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("<F> Set Input File <file name>\n"))); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("<B> Bind\n"))); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("<U> Unbind\n"))); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("<R> Rebind\n"))); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("<D> Dump <file name>\n"))); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("<Q> or ^C (exit) \n"))); +} + + +int +Dump_Restore::set_proc_local (void) +{ + // Set Name Options + this->name_options_->nameserver_host (ACE_TEXT ("localhost")); + this->name_options_->nameserver_port (0); + + // Set Naming Context scope + this->ns_scope_ = + ACE_Naming_Context::PROC_LOCAL; + + // Remove old naming context + delete this->ns_context_; + + // Create new Naming Context + ACE_NEW_RETURN (this->ns_context_, + ACE_Naming_Context, + -1); + + if (this->ns_context_->open (ACE_Naming_Context::PROC_LOCAL) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("ns_context_->open")), + -1); + + return 0; +} + +int +Dump_Restore::set_node_local (void) +{ + // Set Name Options + this->name_options_->nameserver_host (ACE_TEXT ("localhost")); + this->name_options_->nameserver_port (0); + + // Set Naming Context scope + this->ns_scope_ = ACE_Naming_Context::NODE_LOCAL; + + // Remove old naming context + delete this->ns_context_; + + // Create new Naming Context + ACE_NEW_RETURN (this->ns_context_, + ACE_Naming_Context, + -1); + + if (ns_context_->open (ACE_Naming_Context::NODE_LOCAL) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("ns_context_->open")), + -1); + return 0; +} + +int +Dump_Restore::set_host (const ACE_TCHAR *hostname, + int port) +{ + // Set Name Options + this->name_options_->nameserver_host (hostname); + this->name_options_->nameserver_port (port); + + // Don't really need to do this but it's a hack to fix the problme + // of Display () not printing the right hostname + ACE_OS::strcpy (this->hostname_, hostname); + this->port_ = port; + this->ns_scope_ = ACE_Naming_Context::NET_LOCAL; + + // remove old naming context + delete this->ns_context_; + + // Create new Naming Context + ACE_NEW_RETURN (this->ns_context_, + ACE_Naming_Context, + -1); + + // assume net_local context + if (ns_context_->open (ACE_Naming_Context::NET_LOCAL) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("ns_context_->open")), + -1); + return 0; +} + +int +Dump_Restore::doit (Dump_Restore::Operation_Type op, + const char *name, + const char *value, + const char *type) +{ + int result = -1; + + switch (op) + { + case Dump_Restore::BIND: + { + result = this->bind (name, value, type); + break; + } + case Dump_Restore::UNBIND: + { + result = this->unbind (name); + break; + } + case Dump_Restore::REBIND: + { + result = this->rebind (name, value, type); + break; + } + } + + return result; +} + +int +Dump_Restore::populate (Dump_Restore::Operation_Type op) +{ + if (this->infile_) + { + int result = -1; + enum State { NAME, VALUE, TYPE }; + + State state = NAME; + // reset file pointer + ACE_OS::rewind (this->infile_); + + ACE_Allocator *allocator = + ACE_Allocator::instance (); + ACE_Read_Buffer read_buffer (this->infile_, + 0, + allocator); + + for (char *temp; + (temp = read_buffer.read ('\n')) != 0; + ) + { + char *name = 0; + const char *actual_name = 0; + char *value = 0; + const char *actual_value = 0; + char *type = 0; + const char *actual_type = 0; + + switch (state) + { + case NAME: + name = temp; + ACE_OS::strtok (name, "="); + actual_name = ACE_OS::strtok (0, "="); + state = VALUE; + break; + case VALUE: + value = temp; + ACE_OS::strtok (value, "="); + actual_value = ACE_OS::strtok (0, "="); + state = TYPE; + break; + case TYPE: + type = temp; + ACE_OS::strtok (type, "="); + actual_type = ACE_OS::strtok (0, "="); + + if (actual_type) + result = this->doit (op, + actual_name, + actual_value, + actual_type); + else + result = this->doit (op, + actual_name, + actual_value); + if (name) + allocator->free(name); + if (value) + allocator->free(value); + if (type) + allocator->free(type); + state = NAME; + break; + default: + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("populate")), + -1); + /* NOTREACHED */ + } + } + + return result; + } + else + return -1; +} + +int +Dump_Restore::bind (const char *key, + const char *value, + const char *type) +{ + int result = ns_context_->bind (key, + value, + type); + if (result == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("ns_context_->bind")), + -1); + else if (result == 1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("%s%s%s\n"), + ACE_TEXT ("key <"), + key, + ACE_TEXT ("> already bound")), + 1); + return 0; +} + +int +Dump_Restore::unbind (const char *key) +{ + int result = ns_context_->unbind (key); + + if (result == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("ns_context_->unbind")), + -1); + return 0; +} + +int +Dump_Restore::rebind (const char *key, + const char *value, + const char *type) +{ + if (ns_context_->rebind (key, + value, + type) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("ns_context_->rebind")), + -1); + return 0; +} + +int +Dump_Restore::quit (void) +{ + return ACE_OS::kill (ACE_OS::getpid (), SIGINT); +} + +void +Dump_Restore::dump (void) +{ + ofstream output_file (dump_filename_); + + ostream *orig_stream = ACE_Log_Msg::instance ()->msg_ostream (); + ACE_Log_Msg::instance ()->msg_ostream (&output_file); + ACE_Log_Msg::instance ()->clr_flags (ACE_Log_Msg::STDERR | ACE_Log_Msg::LOGGER ); + ACE_Log_Msg::instance ()->set_flags (ACE_Log_Msg::OSTREAM); + + ns_context_->dump (); + + ACE_Log_Msg::instance ()->msg_ostream (orig_stream); + ACE_Log_Msg::instance ()->clr_flags (ACE_Log_Msg::STDERR); +} diff --git a/ACE/netsvcs/clients/Naming/Dump_Restore/Dump_Restore.h b/ACE/netsvcs/clients/Naming/Dump_Restore/Dump_Restore.h new file mode 100644 index 00000000000..1cdd2113f8e --- /dev/null +++ b/ACE/netsvcs/clients/Naming/Dump_Restore/Dump_Restore.h @@ -0,0 +1,86 @@ +// -*- C++ -*- +// +// $Id$ + +#include "ace/Event_Handler.h" + +#if !defined (ACE_LACKS_PRAGMA_ONCE) +# pragma once +#endif /* ACE_LACKS_PRAGMA_ONCE */ + +#include "ace/Reactor.h" +#include "ace/Naming_Context.h" +#include "ace/svc_export.h" + +class ACE_Svc_Export Dump_Restore : public ACE_Event_Handler +{ +public: + enum Operation_Type + { + BIND, + UNBIND, + REBIND + }; + Dump_Restore (int argc, ACE_TCHAR *argv[]); + // Initialize name options and naming context + + ~Dump_Restore (void); + + virtual int handle_input (ACE_HANDLE handle); + // Handle user entered commands + + void dump (void); + +private: + ACE_TCHAR hostname_[MAXHOSTNAMELEN + 1]; + // Cache the hostname and port number for remote case + + void display_menu (void); + // Display user menu. + + int set_proc_local (void); + // Set options to use PROC_LOCAL naming context. + + int set_node_local (void); + // Set options to use NODE_LOCAL naming context. + + int set_host (const ACE_TCHAR *hostname, + int port); + // Set options to use NET_LOCAL naming context specifying host name + // and port number. + + int quit (void); + // Gracefully exit. + + int populate (Dump_Restore::Operation_Type op); + + int doit (Dump_Restore::Operation_Type op, + const char *name, + const char *value, + const char *type = ""); + int bind (const char *key, + const char *value, + const char *type = ""); + int unbind (const char *key); + int rebind (const char *key, + const char *value, + const char *type = ""); + + char filename_[MAXPATHLEN + 1]; + char dump_filename_[MAXPATHLEN + 1]; + + u_short port_; + // port server is listening on + + ACE_Naming_Context *ns_context_; + // Current naming context + + ACE_Naming_Context::Context_Scope_Type ns_scope_; + // Defines the scope of the naming context + + FILE *infile_; + // input file + + ACE_Name_Options *name_options_; + // Name Options associated with the Naming Context +}; diff --git a/ACE/netsvcs/clients/Naming/Dump_Restore/Dump_Restore.mpc b/ACE/netsvcs/clients/Naming/Dump_Restore/Dump_Restore.mpc new file mode 100644 index 00000000000..c88bb05e2e2 --- /dev/null +++ b/ACE/netsvcs/clients/Naming/Dump_Restore/Dump_Restore.mpc @@ -0,0 +1,21 @@ +// -*- MPC -*- +// $Id$ + +project(Netsvcs_Dump_Restore_Lib): acelib { + avoids += ace_for_tao + sharedname = Dump_Restore + dynamicflags += ACE_BUILD_SVC_DLL + Source_Files { + Dump_Restore.cpp + } +} + +project(Netsvcs_Dump_Restore) : aceexe { + avoids += ace_for_tao + exename = main + libs += Dump_Restore + after += Netsvcs_Dump_Restore_Lib + Source_Files { + main.cpp + } +} diff --git a/ACE/netsvcs/clients/Naming/Dump_Restore/Makefile.am b/ACE/netsvcs/clients/Naming/Dump_Restore/Makefile.am new file mode 100644 index 00000000000..38ed1977b42 --- /dev/null +++ b/ACE/netsvcs/clients/Naming/Dump_Restore/Makefile.am @@ -0,0 +1,59 @@ +## Process this file with automake to create Makefile.in +## +## $Id$ +## +## This file was generated by MPC. Any changes made directly to +## this file will be lost the next time it is generated. +## +## MPC Command: +## /acebuilds/ACE_wrappers-repository/bin/mwc.pl -include /acebuilds/MPC/config -include /acebuilds/MPC/templates -feature_file /acebuilds/ACE_wrappers-repository/local.features -noreldefs -type automake -exclude build,Kokyu + +ACE_BUILDDIR = $(top_builddir) +ACE_ROOT = $(top_srcdir) + + +## Makefile.Netsvcs_Dump_Restore_Lib.am + +if !BUILD_ACE_FOR_TAO + +noinst_LTLIBRARIES = libDump_Restore.la + +libDump_Restore_la_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) \ + -DACE_BUILD_SVC_DLL + +libDump_Restore_la_SOURCES = \ + Dump_Restore.cpp + +noinst_HEADERS = \ + Dump_Restore.h + +endif !BUILD_ACE_FOR_TAO + +## Makefile.Netsvcs_Dump_Restore.am + +if !BUILD_ACE_FOR_TAO +noinst_PROGRAMS = main + +main_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +main_SOURCES = \ + main.cpp \ + Dump_Restore.h + +main_LDADD = \ + libDump_Restore.la \ + $(ACE_BUILDDIR)/ace/libACE.la + +endif !BUILD_ACE_FOR_TAO + +## Clean up template repositories, etc. +clean-local: + -rm -f *~ *.bak *.rpo *.sym lib*.*_pure_* core core.* + -rm -f gcctemp.c gcctemp so_locations *.ics + -rm -rf cxx_repository ptrepository ti_files + -rm -rf templateregistry ir.out + -rm -rf ptrepository SunWS_cache Templates.DB diff --git a/ACE/netsvcs/clients/Naming/Dump_Restore/README b/ACE/netsvcs/clients/Naming/Dump_Restore/README new file mode 100644 index 00000000000..25b1243d03f --- /dev/null +++ b/ACE/netsvcs/clients/Naming/Dump_Restore/README @@ -0,0 +1,66 @@ +This file describes the usage of the Dump-Restore utility for the ACE +Name Server. + +Similar to the test application provided in the ../Client/ directory, +a simple ASCII menu-driven interface is provided to the user: + + Name Service Main Menu + ---------------------- + *** Using Process Local Database *** + +** No Input File Specified ** +<P> Use Process Local Database +<N> Use Node Local Database +<H> Set Remote Name server <host> and <port> +<F> Set Input File <file name> + +<B> Bind +<U> Unbind +<R> Rebind +<D> Dump <file name> +<Q> or ^C (exit) + +Initially, the user can select the type of database from the menu: + +<P> uses the process local database (i.e., the + database is called the same name as the process + and stored in /tmp). +<N> uses the node local database (which defaults + to /tmp/localnames). +<H> uses the net local database by specifying host and port + number (by default this is stored in a file called + /tmp/globalnames on the server). +<F> Sets the name of the input file that will be used by the + test application to populate the database. The format of + the file should be: + + name=<name1> + value=<value1> + type=[<type1>] + name=<name2> + value=<value2> + type=[<type2>] + . + . + . + + Note that the type field is optional. However, if no type + information is associated with a name binding, a null entry still + needs to be present (i.e., type=). + +Once the input file has been specified, the user can then do one of +the following: + +<B> Bind -- bind all the bindings in the file to the database. + This can be used to "restore" the state of the + Name Server. +<U> Unbind -- unbind all the bindings in the file from the database. +<R> Rebind -- rebind all the bindings in the file to the database. +<D> Dump <file name> -- dump the state of the database to <filename>. +<Q> or ^C (exit) -- exit gracefully, saving the contents of the + Name Server in persistent shared memory. + +Note that the dump file is stored in ASCII with exactly the same +format as the input file. Also, one can easily change the test +application so that a call to Dump results in the state of the +database dumped to standard output instead of a file. diff --git a/ACE/netsvcs/clients/Naming/Dump_Restore/createfile.cpp b/ACE/netsvcs/clients/Naming/Dump_Restore/createfile.cpp new file mode 100644 index 00000000000..25bf2fb803c --- /dev/null +++ b/ACE/netsvcs/clients/Naming/Dump_Restore/createfile.cpp @@ -0,0 +1,34 @@ +// $Id$ + +#include <stdio.h> +#include <string.h> +#include "ace/ACE.h" + +ACE_RCSID(Dump_Restore, createfile, "$Id$") + +int +main (int argc, char **argv) +{ + FILE *infile, *outfile; + char buf[BUFSIZ]; + + if ((infile = fopen (argv[1], "r")) == NULL) + return -1; + + if ((outfile = fopen (argv[2], "w")) == NULL) + return -1; + + int count = 0; + while (::fgets (buf, BUFSIZ, infile)) + { + buf[::strlen(buf) - 1] = '\0'; + fputs (buf, outfile); + if (count % 2 == 0) + fputs (" ", outfile); + else + fputs ("\n", outfile); + count++; + } + fclose (outfile); + fclose (infile); +} diff --git a/ACE/netsvcs/clients/Naming/Dump_Restore/main.cpp b/ACE/netsvcs/clients/Naming/Dump_Restore/main.cpp new file mode 100644 index 00000000000..46d298357bc --- /dev/null +++ b/ACE/netsvcs/clients/Naming/Dump_Restore/main.cpp @@ -0,0 +1,26 @@ +// $Id$ + +// Test the client-side of the ACE Name Server... + +#include "ace/Service_Config.h" +#include "ace/Log_Msg.h" +#include "Dump_Restore.h" + +ACE_RCSID(Dump_Restore, main, "$Id$") + +int +ACE_TMAIN (int argc, ACE_TCHAR *argv[]) +{ + ACE_Service_Config daemon (argv[0]); + + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("entering main\n"))); + + // Get a handler + Dump_Restore client_handler (argc, argv); + + ACE_Reactor::run_event_loop (); + + /* NOTREACHED */ + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("leaving main\n"))); + return 0; +} diff --git a/ACE/netsvcs/clients/Naming/Makefile.am b/ACE/netsvcs/clients/Naming/Makefile.am new file mode 100644 index 00000000000..883b9bab54d --- /dev/null +++ b/ACE/netsvcs/clients/Naming/Makefile.am @@ -0,0 +1,14 @@ +## Process this file with automake to create Makefile.in +## +## $Id$ +## +## This file was generated by MPC. Any changes made directly to +## this file will be lost the next time it is generated. +## +## MPC Command: +## /acebuilds/ACE_wrappers-repository/bin/mwc.pl -include /acebuilds/MPC/config -include /acebuilds/MPC/templates -feature_file /acebuilds/ACE_wrappers-repository/local.features -noreldefs -type automake -exclude build,Kokyu + +SUBDIRS = \ + Client \ + Dump_Restore + diff --git a/ACE/netsvcs/clients/README b/ACE/netsvcs/clients/README new file mode 100644 index 00000000000..d47c9bfe7ff --- /dev/null +++ b/ACE/netsvcs/clients/README @@ -0,0 +1,8 @@ +This directory contains a number of test programs that illustrate how +to write clients for the various ACE network services. + + . Logger -- client programs that illustrate the ACE logging service. + + . Naming -- client programs that illustrate the ACE name service. + + . Tokens -- client programs that illustrate the ACE token service. 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 */ |