下面对ACE-6.0.0提供的动态服务配置的实例代码进行分析。
1. DLL代码
Service_Config_DLL.cpp 文件 (在\ACE-6.0.0\ACE_wrappers\tests目录下)
// -*- C++ -*- //============================================================================= /** * @file Service_Config_DLL.cpp * * $Id: Service_Config_DLL.cpp 91673 2010-09-08 18:49:47Z johnnyw $ * * This file is related to, and used with, Service_Config_Test. It's * used when testing the reentrance/thread-safety of the * Service Configurator, in addition to testing the Service * Configurator's ability to handle nested processing of Service * Configurator directives. * * @author Ossama Othman <[email protected]> */ //============================================================================= #include "Service_Config_DLL.h" #include "ace/Service_Config.h" #include "ace/OS_NS_stdio.h" #include "ace/OS_NS_string.h" static ACE_THR_FUNC_RETURN invoke_service_config (void *arg) { const ACE_TCHAR *directive = reinterpret_cast<const ACE_TCHAR *> (arg); // Process a Service Configurator directive in the current thread. // process_directive 方法分析配置语句,调用DLL的导出函数。 if (ACE_Service_Config::process_directive (directive) != 0) ACE_ERROR ((LM_ERROR, ACE_TEXT ("(%P|%t) Service_Config_DLL::svc() - ") ACE_TEXT ("process_directive() failed for:\n") ACE_TEXT ("\"%s\"\n"), directive)); return 0; } Service_Config_DLL::Service_Config_DLL (void) { ACE_OS::memset (this->directive_[0], 0, BUFSIZ * sizeof (ACE_TCHAR)); ACE_OS::memset (this->directive_[1], 0, BUFSIZ * sizeof (ACE_TCHAR)); } int Service_Config_DLL::init (int argc, ACE_TCHAR *argv[]) { if (argc == 2) { ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Loading Test_Object_%s and Test_Object_%s\n"), argv[0], argv[1])); ACE_OS::sprintf (this->directive_[0], #if (ACE_USES_CLASSIC_SVC_CONF == 1) ACE_TEXT ("dynamic Test_Object_%s Service_Object * Service_Config_DLL:_make_Service_Config_DLL() \"Test_Object_%s\""), #else ACE_TEXT ("<?xml version='1.0'?> <dynamic id='Test_Object_%s' type='service_object'> <initializer init='_make_Service_Config_DLL' path='Service_Config_DLL' params='Test_Object_%s'/> </dynamic>"), #endif argv[0], argv[0]); ACE_OS::sprintf (this->directive_[1], #if (ACE_USES_CLASSIC_SVC_CONF == 1) ACE_TEXT ("dynamic Test_Object_%s Service_Object * Service_Config_DLL:_make_Service_Config_DLL() \"Test_Object_%s\""), #else ACE_TEXT ("<?xml version='1.0'?> <dynamic id='Test_Object_%s' type='service_object'> <initializer init='_make_Service_Config_DLL' path='Service_Config_DLL' params='Test_Object_%s'/> </dynamic>"), #endif argv[1], argv[1]); if (ACE_Service_Config::process_directive (this->directive_[0]) != 0) ACE_ERROR ((LM_ERROR, ACE_TEXT ("(%P|%t) Service_Config_DLL::init() - ") ACE_TEXT ("process_directive() failed for:\n") ACE_TEXT ("\"%s\": %m\n"), this->directive_[0])); #if defined (ACE_HAS_THREADS) // Become an Active Object if more than one argument passed. // Two arguments indicate two "test objects" to be dynamically // loaded. return this->activate (); // 执行activate才能调用svc函数。 #endif /* ACE_HAS_THREADS */ } else if (argc == 1) { ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Service_Config_DLL::init () %@ - %s\n"), this, argv[0])); } else { ACE_ERROR_RETURN ((LM_ERROR, ACE_TEXT ("(%P|%t) Incorrect number of arguments ") ACE_TEXT ("(%d) passed to Service_Config_DLL::init ()"), argc), -1); } return 0; } int Service_Config_DLL::fini (void) { return 0; } int Service_Config_DLL::svc (void) { // spawn 方法创建线程,invoke_service_config 是线程函数。 if (ACE_Thread_Manager::instance ()->spawn (invoke_service_config, this->directive_[1]) == -1) ACE_ERROR_RETURN ((LM_ERROR, ACE_TEXT ("(%P|%t) Unable to spawn thread to ") ACE_TEXT ("invoke Service Configurator.\n")), -1); return 0; } // The same class (Service_Config_DLL) is used to implement each of the // Service Objects whose service descriptors are defined below. // ----------------------------------------------------------------- ACE_STATIC_SVC_DEFINE (Test_Object_1, ACE_TEXT ("Test_Object_1"), ACE_SVC_OBJ_T, &ACE_SVC_NAME (Service_Config_DLL), ACE_Service_Type::DELETE_THIS | ACE_Service_Type::DELETE_OBJ, 0) // ----------------------------------------------------------------- ACE_STATIC_SVC_DEFINE (Test_Object_2, ACE_TEXT ("Test_Object_2"), ACE_SVC_OBJ_T, &ACE_SVC_NAME (Service_Config_DLL), ACE_Service_Type::DELETE_THIS | ACE_Service_Type::DELETE_OBJ, 0) // ----------------------------------------------------------------- ACE_STATIC_SVC_DEFINE (Test_Object_3, ACE_TEXT ("Test_Object_3"), ACE_SVC_OBJ_T, &ACE_SVC_NAME (Service_Config_DLL), ACE_Service_Type::DELETE_THIS | ACE_Service_Type::DELETE_OBJ, 0) // ----------------------------------------------------------------- ACE_STATIC_SVC_DEFINE (Test_Object_4, ACE_TEXT ("Test_Object_4"), ACE_SVC_OBJ_T, &ACE_SVC_NAME (Service_Config_DLL), ACE_Service_Type::DELETE_THIS | ACE_Service_Type::DELETE_OBJ, 0) // ----------------------------------------------------------------- ACE_STATIC_SVC_DEFINE (Test_Object_5, ACE_TEXT ("Test_Object_5"), ACE_SVC_OBJ_T, &ACE_SVC_NAME (Service_Config_DLL), ACE_Service_Type::DELETE_THIS | ACE_Service_Type::DELETE_OBJ, 0) // ----------------------------------------------------------------- ACE_STATIC_SVC_DEFINE (Test_Object_6, ACE_TEXT ("Test_Object_6"), ACE_SVC_OBJ_T, &ACE_SVC_NAME (Service_Config_DLL), ACE_Service_Type::DELETE_THIS | ACE_Service_Type::DELETE_OBJ, 0) // ----------------------------------------------------------------- ACE_STATIC_SVC_DEFINE (Final_Object, ACE_TEXT ("Final_Object"), ACE_SVC_OBJ_T, &ACE_SVC_NAME (Service_Config_DLL), ACE_Service_Type::DELETE_THIS | ACE_Service_Type::DELETE_OBJ, 0) // ----------------------------------------------------------------- ACE_STATIC_SVC_DEFINE (Test_Object_1_More, ACE_TEXT ("Test_Object_1_More"), ACE_SVC_OBJ_T, &ACE_SVC_NAME (Service_Config_DLL), ACE_Service_Type::DELETE_THIS | ACE_Service_Type::DELETE_OBJ, 0) // ----------------------------------------------------------------- ACE_STATIC_SVC_DEFINE (Test_Object_2_More, ACE_TEXT ("Test_Object_2_More"), ACE_SVC_OBJ_T, &ACE_SVC_NAME (Service_Config_DLL), ACE_Service_Type::DELETE_THIS | ACE_Service_Type::DELETE_OBJ, 0) // ----------------------------------------------------------------- // Same factory is used for all service descriptors defined above. ACE_FACTORY_DEFINE (Service_Config_DLL, Service_Config_DLL) /* ** Simple service which will refuse to load/initialize correctly. ** The main program should do: ** 1. Try to load this service (which should fail) ** 2. Try to look up this service and call its info() hook; if info() ** can be called, the test has failed. ** Similarly, if fini() is called later, something is very wrong. */ /// Initializes object when dynamic linking occurs. int Refuses_Init::init (int, ACE_TCHAR *[]) { ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Refuses_Init::init - refusing to init\n"))); return -1; } /// Terminates object when dynamic unlinking occurs. int Refuses_Init::fini (void) { ACE_ERROR ((LM_ERROR, ACE_TEXT ("Refuses_Init::fini should not be called!\n"))); return 0; } /// Returns information on a service object. int Refuses_Init::info (ACE_TCHAR **info_string, size_t length) const { ACE_TCHAR const *msg = ACE_TEXT ("Refuses_Init service, shouldn't be here!\n"); if (*info_string == 0) *info_string = ACE_OS::strdup (msg); else ACE_OS::strncpy (*info_string, msg, length); ACE_ERROR ((LM_ERROR, ACE_TEXT ("Refuses_Init::info() called - shouldn't be\n"))); return ACE_OS::strlen (*info_string); } ACE_FACTORY_DEFINE (Service_Config_DLL, Refuses_Init)
2. 宿主程序的代码
Service_Config_Test.cpp 文件 (在\ACE-6.0.0\ACE_wrappers\tests目录下)
// -*- C++ -*- //============================================================================= /** * @file Service_Config_Test.cpp * * $Id: Service_Config_Test.cpp 91673 2010-09-08 18:49:47Z johnnyw $ * * This is a simple test to make sure the ACE Service Configurator * framework is working correctly. * * @author David Levine <[email protected]> * @author Ossama Othman <[email protected]> */ //============================================================================= #include "test_config.h" #include "ace/OS_NS_stdio.h" #include "ace/OS_NS_errno.h" #include "ace/OS_NS_Thread.h" #include "ace/Log_Msg.h" #include "ace/Object_Manager.h" #include "ace/Service_Config.h" #include "ace/Service_Object.h" #include "ace/Service_Repository.h" #include "ace/Service_Types.h" #include "ace/Reactor.h" #include "ace/Thread_Manager.h" #include "ace/ARGV.h" static const u_int VARIETIES = 3; static u_int error = 0; class MyRepository : public ACE_Service_Repository { public: array_type& array () { return service_array_; } }; /** * @class Test_Singleton * * @brief Test the Singleton * * This should be a template class, with singleton instantiations. * But to avoid having to deal with compilers that want template * declarations in separate files, it's just a plain class. The * instance argument differentiates the "singleton" instances. It * also demonstrates the use of the param arg to the cleanup () * function. */ class Test_Singleton { public: static Test_Singleton *instance (u_short variety); ~Test_Singleton (void); private: u_short variety_; static u_short current_; Test_Singleton (u_short variety); friend class misspelled_verbase_friend_declaration_to_avoid_compiler_warning_with_private_ctor; }; u_short Test_Singleton::current_ = 0; extern "C" void test_singleton_cleanup (void *object, void *) { // We can't reliably use ACE_Log_Msg in a cleanup hook. Yet. /* ACE_DEBUG ((LM_DEBUG, "cleanup %d\n", (u_short) param)); */ delete (Test_Singleton *) object; } Test_Singleton * Test_Singleton::instance (u_short variety) { static Test_Singleton *instances[VARIETIES] = { 0 }; if (instances[variety] == 0) { ACE_NEW_RETURN (instances[variety], Test_Singleton (variety), 0); } ACE_Object_Manager::at_exit (instances[variety], test_singleton_cleanup, reinterpret_cast<void *> (static_cast<size_t> (variety)), "Test_Singleton"); return instances[variety]; } Test_Singleton::Test_Singleton (u_short variety) : variety_ (variety) { if (variety_ != current_++) { ACE_DEBUG ((LM_ERROR, ACE_TEXT ("ERROR: instance %u created out of order!\n"), variety_)); ++error; } } // We can't reliably use ACE_Log_Msg in a destructor that is called by // ACE_Object_Manager. Yet. Test_Singleton::~Test_Singleton (void) { /* ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Test_Singleton %u dtor\n"), variety_)); */ if (variety_ != --current_) { ACE_OS::fprintf (stderr, ACE_TEXT ("ERROR: instance %u destroyed out of order!\n"), variety_); /* ACE_DEBUG ((LM_ERROR, ACE_TEXT ("ERROR: instance %u destroyed out of order!\n"), variety_)); */ ++error; } } void testFailedServiceInit (int, ACE_TCHAR *[]) { static const ACE_TCHAR *refuse_svc = #if (ACE_USES_CLASSIC_SVC_CONF == 1) ACE_TEXT ("dynamic Refuses_Svc Service_Object * ") ACE_TEXT (" Service_Config_DLL:_make_Refuses_Init() \"\"") #else ACE_TEXT ("<dynamic id=\"Refuses_Svc\" type=\"Service_Object\">") ACE_TEXT (" <initializer init=\"_make_Refuses_Init\" path=\"Service_Config_DLL\" params=\"\"/>") ACE_TEXT ("</dynamic>") #endif /* (ACE_USES_CLASSIC_SVC_CONF == 1) */ ; int error_count = 0; if ((error_count = ACE_Service_Config::process_directive (refuse_svc)) != 1) { ++error; ACE_ERROR ((LM_ERROR, ACE_TEXT ("Failed init test should have returned 1; ") ACE_TEXT ("returned %d instead\n"), error_count)); } // Try to find the service; it should not be there. ACE_Service_Type const *svcp = 0; if (-1 != ACE_Service_Repository::instance ()->find (ACE_TEXT ("Refuses_Svc"), &svcp)) { ++error; ACE_ERROR ((LM_ERROR, ACE_TEXT ("Found service repo entry for Refuses_Svc\n"))); ACE_Service_Type_Impl const *svc_impl = svcp->type (); ACE_TCHAR msg[1024]; ACE_TCHAR *msgp = msg; if (svc_impl->info (&msgp, sizeof (msg) / sizeof (ACE_TCHAR)) > 0) ACE_ERROR ((LM_ERROR, ACE_TEXT ("Refuses_Svc said: %s\n"), msg)); } else ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Repo reports no Refuses_Svc; correct.\n"))); } void testLoadingServiceConfFileAndProcessNo (int argc, ACE_TCHAR *argv[]) { ACE_ARGV new_argv; #if defined (ACE_USES_WCHAR) // When using full Unicode support, use the version of the Service // Configurator file appropriate to the platform. // For example, Windows Unicode uses UTF-16. // // iconv(1) found on Linux and Solaris, for example, can // be used to convert between encodings. // // Byte ordering is also an issue, so we should be // generating this file on-the-fly from the UTF-8 encoded // file by using functions like iconv(1) or iconv(3). # if defined (ACE_WIN32) const ACE_TCHAR svc_conf[] = ACE_TEXT ("Service_Config_Test.UTF-16") ACE_TEXT (ACE_DEFAULT_SVC_CONF_EXT); # else const ACE_TCHAR svc_conf[] = ACE_TEXT ("Service_Config_Test.WCHAR_T") ACE_TEXT (ACE_DEFAULT_SVC_CONF_EXT); # endif /* ACE_WIN32 */ #else // ASCII (UTF-8) encoded Service Configurator file. const ACE_TCHAR svc_conf[] = ACE_TEXT ("Service_Config_Test") ACE_TEXT (ACE_DEFAULT_SVC_CONF_EXT); #endif /* ACE_USES_WCHAR */ // Process the Service Configurator directives in this test's Making // sure we have more than one option with an argument, to capture // any errors caused by "reshuffling" of the options. if (new_argv.add (argv) == -1 || new_argv.add (ACE_TEXT ("-d")) == -1 || new_argv.add (ACE_TEXT ("-k")) == -1 || new_argv.add (ACE_TEXT ("xxx")) == -1 || new_argv.add (ACE_TEXT ("-p")) == -1 || new_argv.add (ACE_TEXT ("Service_Config_Test.pid")) == -1 || new_argv.add (ACE_TEXT ("-f")) == -1 || new_argv.add (svc_conf) == -1) { ACE_ERROR ((LM_ERROR, ACE_TEXT ("line %l %p\n"), ACE_TEXT ("new_argv.add"))); ++error; } // We need this scope to make sure that the destructor for the // <ACE_Service_Config> gets called. ACE_Service_Config daemon; if (daemon.open (new_argv.argc (), new_argv.argv ()) == -1 && errno != ENOENT) { ACE_ERROR ((LM_ERROR, ACE_TEXT ("line %l %p\n"), ACE_TEXT ("daemon.open"))); ++error; } ACE_Time_Value tv (argc > 1 ? ACE_OS::atoi (argv[1]) : 2); if (ACE_Reactor::instance()->run_reactor_event_loop (tv) == -1) { ++error; ACE_ERROR ((LM_ERROR, ACE_TEXT ("line %l %p\n"), ACE_TEXT ("run_reactor_event_loop"))); } // Wait for all threads to complete. ACE_Thread_Manager::instance ()->wait (); } void testLoadingServiceConfFile (int argc, ACE_TCHAR *argv[]) { ACE_ARGV new_argv; #if defined (ACE_USES_WCHAR) // When using full Unicode support, use the version of the Service // Configurator file appropriate to the platform. // For example, Windows Unicode uses UTF-16. // // iconv(1) found on Linux and Solaris, for example, can // be used to convert between encodings. // // Byte ordering is also an issue, so we should be // generating this file on-the-fly from the UTF-8 encoded // file by using functions like iconv(1) or iconv(3). # if defined (ACE_WIN32) const ACE_TCHAR svc_conf[] = ACE_TEXT ("Service_Config_Test.UTF-16") ACE_TEXT (ACE_DEFAULT_SVC_CONF_EXT); # else const ACE_TCHAR svc_conf[] = ACE_TEXT ("Service_Config_Test.WCHAR_T") ACE_TEXT (ACE_DEFAULT_SVC_CONF_EXT); # endif /* ACE_WIN32 */ #else // ASCII (UTF-8) encoded Service Configurator file. // 确定配置文件名称: const ACE_TCHAR svc_conf[] = ACE_TEXT ("Service_Config_Test") ACE_TEXT (ACE_DEFAULT_SVC_CONF_EXT); #endif /* ACE_USES_WCHAR */ // Process the Service Configurator directives in this test's // 把命令行参数加入到一个ACE参数对象中,便于后面分析,把配置文件作为参数也加入其中。 if (new_argv.add (argv) == -1 || new_argv.add (ACE_TEXT ("-f")) == -1 || new_argv.add (svc_conf) == -1) { ACE_ERROR ((LM_ERROR, ACE_TEXT ("line %l %p\n"), ACE_TEXT ("new_argv.add"))); ++error; } // We need this scope to make sure that the destructor for the // <ACE_Service_Config> gets called. ACE_Service_Config daemon; // open方法解析配置文件。 if (daemon.open (new_argv.argc (), new_argv.argv ()) == -1) { if (errno == ENOENT) ACE_DEBUG ((LM_WARNING, ACE_TEXT ("ACE_Service_Config::open: %p\n"), svc_conf)); else ACE_ERROR ((LM_ERROR, ACE_TEXT ("ACE_Service_Config::open: %p\n"), ACE_TEXT ("error"))); } ACE_Time_Value tv (argc > 1 ? ACE_OS::atoi (argv[1]) : 2); if (ACE_Reactor::instance()->run_reactor_event_loop (tv) == -1) { ++error; ACE_ERROR ((LM_ERROR, ACE_TEXT ("line %l %p\n"), ACE_TEXT ("run_reactor_event_loop"))); } // Wait for all threads to complete. // 等待进程内的其他线程退出。 ACE_Thread_Manager::instance ()->wait (); } // Loading and unloading the ACE logger service should not smash singletons. void testUnloadingACELoggingStrategy (int, ACE_TCHAR *[]) { static const ACE_TCHAR *load_logger = #if (ACE_USES_CLASSIC_SVC_CONF == 1) ACE_TEXT ("dynamic Logger Service_Object * ") ACE_TEXT (" ACE:_make_ACE_Logging_Strategy() \"\"") #else ACE_TEXT ("<dynamic id=\"Logger\" type=\"Service_Object\">") ACE_TEXT (" <initializer init=\"_make_ACE_Logging_Strategy\" ") ACE_TEXT (" path=\"ACE\" params=\"\"/>") ACE_TEXT ("</dynamic>") #endif /* (ACE_USES_CLASSIC_SVC_CONF == 1) */ ; static const ACE_TCHAR *unload_logger = #if (ACE_USES_CLASSIC_SVC_CONF == 1) ACE_TEXT ("remove Logger") #else ACE_TEXT ("<remove id=\"Logger\" />"); #endif /* (ACE_USES_CLASSIC_SVC_CONF == 1) */ ; ACE_Reactor *r1 = ACE_Reactor::instance(); // Ensure no errors are logged while searching for a valid ACE lib name; // these skew the scoreboard results. u_long mask = ACE_LOG_MSG->priority_mask (ACE_Log_Msg::PROCESS); ACE_LOG_MSG->priority_mask (mask & ~LM_ERROR, ACE_Log_Msg::PROCESS); ACE_DEBUG ((LM_DEBUG, "Was %x, now %x\n", mask, mask & ~LM_ERROR)); int error_count = ACE_Service_Config::process_directive (load_logger); ACE_LOG_MSG->priority_mask (mask); if (error_count != 0) { ++error; ACE_ERROR ((LM_ERROR, ACE_TEXT ("Load ACE Logger should have returned 0; ") ACE_TEXT ("returned %d instead\n"), error_count)); } ACE_Service_Config::process_directive (unload_logger); ACE_Reactor *r2 = ACE_Reactor::instance (); if (r1 != r2) ACE_ERROR ((LM_ERROR, ACE_TEXT ("Reactor before %@, after %@\n"), r1, r2)); } // @brief The size of a repository is unlimited and can be exceeded void testLimits (int , ACE_TCHAR *[]) { static const ACE_TCHAR *svc_desc1 = #if (ACE_USES_CLASSIC_SVC_CONF == 1) ACE_TEXT ("dynamic Test_Object_1_More Service_Object * ") ACE_TEXT (" Service_Config_DLL:_make_Service_Config_DLL() \"Test_Object_1_More\"") #else ACE_TEXT ("<dynamic id=\"Test_Object_1_More\" type=\"Service_Object\">") ACE_TEXT (" <initializer init=\"_make_Service_Config_DLL\" path=\"Service_Config_DLL\" params=\"Test_Object_1_More\"/>") ACE_TEXT ("</dynamic>") #endif /* (ACE_USES_CLASSIC_SVC_CONF == 1) */ ; static const ACE_TCHAR *svc_desc2 = #if (ACE_USES_CLASSIC_SVC_CONF == 1) ACE_TEXT ("dynamic Test_Object_2_More Service_Object * ") ACE_TEXT (" Service_Config_DLL:_make_Service_Config_DLL() \"Test_Object_2_More\"") #else ACE_TEXT ("<dynamic id=\"Test_Object_2_More\" type=\"Service_Object\">") ACE_TEXT (" <initializer init=\"_make_Service_Config_DLL\" path=\"Service_Config_DLL\" params=\"Test_Object_2_More\"/>") ACE_TEXT ("</dynamic>") #endif /* (ACE_USES_CLASSIC_SVC_CONF == 1) */ ; u_int error0 = error; // Ensure enough room for one in a own, the repository can extend ACE_Service_Gestalt one (1, true); // Add two. // We cant simply rely on the fact that insertion fails, because it // is typical to have no easy way of getting detailed error // information from a parser. one.process_directive (svc_desc1); one.process_directive (svc_desc2); if (-1 == one.find (ACE_TEXT ("Test_Object_1_More"), 0, 0)) { ++error; ACE_ERROR ((LM_ERROR, ACE_TEXT("Expected to have registered the first service\n"))); } if (-1 == one.find (ACE_TEXT ("Test_Object_2_More"), 0, 0)) { ++error; ACE_ERROR ((LM_ERROR, ACE_TEXT("Expected to have registered the second service\n"))); } ACE_Service_Repository_Iterator sri (*one.current_service_repository (), 0); size_t index = 0; for (const ACE_Service_Type *sr; sri.next (sr) != 0; sri.advance ()) { if (index == 0 && ACE_OS::strcmp (sr->name(), ACE_TEXT ("Test_Object_1_More")) != 0) { ++error; ACE_ERROR ((LM_ERROR, ACE_TEXT("Service 1 is wrong\n"))); } if (index == 1 && ACE_OS::strcmp (sr->name(), ACE_TEXT ("Test_Object_2_More")) != 0) { ++error; ACE_ERROR ((LM_ERROR, ACE_TEXT("Service 2 is wrong\n"))); } ++index; } // Test close one.current_service_repository ()->close(); if (one.current_service_repository ()->current_size () != 0) { ++error; ACE_ERROR ((LM_ERROR, ACE_TEXT("Size of repository should be 0\n"))); } if (error == error0) ACE_DEBUG ((LM_DEBUG, ACE_TEXT("Limits test completed successfully\n"))); else ACE_DEBUG ((LM_DEBUG, ACE_TEXT("Limits test failed\n"))); } void testrepository (int, ACE_TCHAR *[]) { MyRepository repository; ACE_DLL handle; ACE_Service_Type s0 (ACE_TEXT ("0"), 0, handle, false); ACE_Service_Type s1 (ACE_TEXT ("1"), 0, handle, false); ACE_Service_Type s2 (ACE_TEXT ("2"), 0, handle, false); ACE_Service_Type s3 (ACE_TEXT ("3"), 0, handle, false); ACE_Service_Type* result = 0; repository.insert (&s0); if (repository.current_size () != 1) { ++error; ACE_ERROR ((LM_ERROR, ACE_TEXT ("Repository was wrong size %d\n"), repository.current_size ())); } repository.insert (&s1); if (repository.current_size () != 2) { ++error; ACE_ERROR ((LM_ERROR, ACE_TEXT ("Repository was wrong size %d\n"), repository.current_size ())); } repository.insert (&s2); if (repository.current_size () != 3) { ++error; ACE_ERROR ((LM_ERROR, ACE_TEXT ("Repository was wrong size %d\n"), repository.current_size ())); } if (repository.remove (ACE_TEXT ("1"), &result) != 0) { ++error; ACE_ERROR ((LM_ERROR, ACE_TEXT ("Remove failed\n"))); } if (repository.current_size () != 3) { ++error; ACE_ERROR ((LM_ERROR, ACE_TEXT ("Repository was wrong size %d\n"), repository.current_size ())); } if (repository.array ()[1] != 0) { ++error; ACE_ERROR ((LM_ERROR, ACE_TEXT ("Element 1 not zero\n"), repository.current_size ())); } repository.insert (&s3); if (repository.current_size () != 4) { ++error; ACE_ERROR ((LM_ERROR, ACE_TEXT ("Repository was wrong size %d\n"), repository.current_size ())); } repository.remove (ACE_TEXT ("0"), &result); if (repository.remove (ACE_TEXT ("1"), &result) != -1) { ++error; ACE_ERROR ((LM_ERROR, ACE_TEXT ("Double remove didn't return -1\n"))); } repository.remove (ACE_TEXT ("2"), &result); repository.remove (ACE_TEXT ("3"), &result); if (repository.current_size () != 4) { ++error; ACE_ERROR ((LM_ERROR, ACE_TEXT ("Repository was wrong size %d\n"), repository.current_size ())); } repository.close (); if (repository.current_size () != 0) { ++error; ACE_ERROR ((LM_ERROR, ACE_TEXT ("Repository was wrong size %d\n"), repository.current_size ())); } } // @brief ?? void testOrderlyInstantiation (int , ACE_TCHAR *[]) { for (u_int i = 0; i < VARIETIES; ++i) { Test_Singleton *s = Test_Singleton::instance (i); if (s == 0) { ++error; ACE_ERROR ((LM_ERROR, ACE_TEXT ("instance () allocate failed!\n"))); } } } // This test verifies that services loaded by a normal ACE startup can be // located from a thread spawned outside of ACE's control. // // To do this, we need a native thread entry and, thus, it needs special care // for each platform type. Feel free to add more platforms as needed here and // in main() where the test is called. #if defined (ACE_HAS_WTHREADS) || defined (ACE_HAS_PTHREADS_STD) # if defined (ACE_HAS_WTHREADS) extern "C" unsigned int __stdcall # else extern "C" ACE_THR_FUNC_RETURN # endif nonacethreadentry (void *args) { ACE_Log_Msg::inherit_hook (0, *(ACE_OS_Log_Msg_Attributes *)args); if (ACE_Service_Config::instance()->find(ACE_TEXT("Test_Object_1_Thr")) != 0) { ACE_ERROR ((LM_ERROR, ACE_TEXT ("In thr %t cannot find Test_Object_1_Thr ") ACE_TEXT ("via ACE_Service_Config\n"))); ++error; } else ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("In thr %t, located Test_Object_1_Thr ") ACE_TEXT ("via ACE_Service_Config\n"))); if (0 != ACE_Service_Repository::instance()->find (ACE_TEXT("Test_Object_1_Thr"))) { ACE_ERROR ((LM_ERROR, ACE_TEXT ("In thr %t cannot find Test_Object_1_Thr ") ACE_TEXT ("via ACE_Service_Repository\n"))); ++error; } else ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("In thr %t, located Test_Object_1_Thr ") ACE_TEXT ("via ACE_Service_Repository\n"))); return 0; } void testNonACEThread () { ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Beginning non-ACE thread lookup test\n"))); static const ACE_TCHAR *svc_desc = #if (ACE_USES_CLASSIC_SVC_CONF == 1) ACE_TEXT ("dynamic Test_Object_1_Thr Service_Object * ") ACE_TEXT (" Service_Config_DLL:_make_Service_Config_DLL() \"Test_Object_1_Thr\"") #else ACE_TEXT ("<dynamic id=\"Test_Object_1_Thr\" type=\"Service_Object\">") ACE_TEXT (" <initializer init=\"_make_Service_Config_DLL\" path=\"Service_Config_DLL\" params=\"Test_Object_1_Thr\"/>") ACE_TEXT ("</dynamic>") #endif /* (ACE_USES_CLASSIC_SVC_CONF == 1) */ ; static const ACE_TCHAR *svc_remove = #if (ACE_USES_CLASSIC_SVC_CONF == 1) ACE_TEXT ("remove Test_Object_1_Thr") #else ACE_TEXT ("<remove id=\"Test_Object_1_Thr\"/>") ACE_TEXT ("</remove>") #endif /* (ACE_USES_CLASSIC_SVC_CONF == 1) */ ; if (-1 == ACE_Service_Config::process_directive (svc_desc)) { ACE_ERROR ((LM_ERROR, ACE_TEXT ("%p\n"), ACE_TEXT ("Error loading service"))); ++error; return; } // Allow the spawned thread to contribute to the logging output. ACE_OS_Log_Msg_Attributes log_msg_attrs; ACE_Log_Msg::init_hook (log_msg_attrs); u_int errors_before = error; #if defined (ACE_HAS_WTHREADS) && !defined (ACE_HAS_WINCE) HANDLE thr_h = (HANDLE)_beginthreadex (0, 0, &nonacethreadentry, &log_msg_attrs, 0, 0); if (thr_h == 0) { ACE_ERROR ((LM_ERROR, ACE_TEXT ("%p\n"), ACE_TEXT ("_beginthreadex"))); } else { WaitForSingleObject (thr_h, INFINITE); CloseHandle (thr_h); } #elif defined (ACE_HAS_PTHREADS_STD) pthread_t thr_id; int status = pthread_create (&thr_id, 0, nonacethreadentry, &log_msg_attrs); if (status != 0) { errno = status; ACE_ERROR ((LM_ERROR, ACE_TEXT ("%p\n"), ACE_TEXT ("pthread_create"))); } else { pthread_join (thr_id, 0); } #endif if (error != errors_before) // The test failed; see if we can still see it { if (0 != ACE_Service_Config::instance()->find (ACE_TEXT ("Test_Object_1_Thr"))) ACE_ERROR ((LM_ERROR, ACE_TEXT ("Main thr %t cannot find Test_Object_1_Thr\n"))); else ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Main thr %t DOES find Test_Object_1_Thr\n"))); } if (-1 == ACE_Service_Config::process_directive (svc_remove)) { ACE_ERROR ((LM_ERROR, ACE_TEXT ("%p\n"), ACE_TEXT ("Error removing service"))); ++error; return; } ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Non-ACE thread lookup test completed\n"))); } #endif /* ACE_HAS_WTHREADS || ACE_HAS_PTHREADS_STD */ int run_main (int argc, ACE_TCHAR *argv[]) { ACE_START_TEST (ACE_TEXT ("Service_Config_Test")); //[] testOrderlyInstantiation (argc, argv); // 先屏掉这句 //[] testFailedServiceInit (argc, argv); // 先屏掉这句 testLoadingServiceConfFile (argc, argv); // 只留这句测试 //[] testLoadingServiceConfFileAndProcessNo (argc, argv); // 先屏掉这句 //[] testUnloadingACELoggingStrategy (argc, argv); // 先屏掉这句 //[] testLimits (argc, argv); // 先屏掉这句 //[] testrepository (argc, argv); // 先屏掉这句 #if defined (ACE_HAS_WTHREADS) || defined (ACE_HAS_PTHREADS_STD) //[] testNonACEThread(); // 先屏掉这句 #endif ACE_END_TEST; getchar(); // 加这句让窗口停留,便于查看输出内容。 return error; }
3. 配置文件
Service_Config_Test.conf 文件 (在\ACE-6.0.0\ACE_wrappers\tests目录下)
# Dynamically loading each of the Service Objects below causes a # number of threads to be spawned, each one invoking the Service # Configurator (e.g. ACE_Service_Config::process_directive(). If the # Service Configurator is thread safe and reentrant, then parsing of # this `Service_Config_Test.conf' file should run to completion # without error. # # Test_Object_1 will cause Test_Object_2 and Test_Object_3 to be # dynamically loaded. Dynamic loading of each of object will occur in # a separate thread. dynamic Test_Object_1 Service_Object * Service_Config_DLL:_make_Service_Config_DLL() "2 3" // 引号内是参数 # Test_Object_4 will cause Test_Object_5 and Test_Object_6 to be # dynamically loaded. Dynamic loading of each of object will occur in # a separate thread. dynamic Test_Object_4 Service_Object * Service_Config_DLL:_make_Service_Config_DLL() "5 6" // 引号内是参数 # Final_Object does nothing but print a completion message. dynamic Final_Object Service_Object * Service_Config_DLL:_make_Service_Config_DLL() "FINAL" // 引号内是参数