diff options
Diffstat (limited to 'ace/Service_Gestalt.cpp')
-rw-r--r-- | ace/Service_Gestalt.cpp | 317 |
1 files changed, 210 insertions, 107 deletions
diff --git a/ace/Service_Gestalt.cpp b/ace/Service_Gestalt.cpp index 2c8a83f3114..34aa9889c8e 100644 --- a/ace/Service_Gestalt.cpp +++ b/ace/Service_Gestalt.cpp @@ -33,40 +33,77 @@ ACE_RCSID (ace, Service_Gestalt, "$Id$") - ACE_BEGIN_VERSIONED_NAMESPACE_DECL - -// This is here, in the implementation, because it depends on the ACE_DLL type, -// which would be unnecessary to introduuce all over the place, had we declared -// this in the header file. - -// A forward service declaration guard. Used to declare a Service Type with a -// specific name in a Service Repository, which may later be replaced by the -// "real" Service Type. The only application is in the implementation of -// ACE_Service_Gestalt::initialize (), hence the declaration scoping in -// this file. -class ACE_Service_Type_Forward_Declaration_Guard +ACE_BEGIN_VERSIONED_NAMESPACE_DECL + +/// This is in the implementation file because it depends on the +/// ACE_DLL type, which would be unnecessary to introduuce all over +/// the place, had we declared this in the header file. + +/// @class ACE_Service_Type_Dynamic_Guard +/// +/// @brief A forward service declaration guard. +/// +/// Helps to resolve an issue with hybrid services, i.e. dynamic +/// services, accompanied by static services in the same DLL. Only +/// automatic instances of SDG are supposed to exist. Those are +/// created during (dynamic) service initialization and serve to: +/// +/// (a) Ensure the service we are loading is ordered last in the +/// repository, following any other services it may cause to register, +/// as part of its own registration. This is a common case when +/// loading dynamic services from DLLs - there are often static +/// initializers, which register static services. +/// +/// (b) The SDG instance destructor detects if the dynamic service +/// initialized successfully and "fixes-up" all the newly registered +/// static services to hold a reference to the DLL, from which they +/// have originated. + +class ACE_Service_Type_Dynamic_Guard { public: - ACE_Service_Type_Forward_Declaration_Guard (ACE_Service_Repository *r, - ACE_TCHAR const *name); + ACE_Service_Type_Dynamic_Guard (ACE_Service_Repository &r, + ACE_TCHAR const *name); - ~ACE_Service_Type_Forward_Declaration_Guard (void); + ~ACE_Service_Type_Dynamic_Guard (void); private: const ACE_DLL dummy_dll_; - ACE_Service_Repository *repo_; + ACE_Service_Repository & repo_; + size_t repo_begin_; ACE_TCHAR const * const name_; ACE_Service_Type const * dummy_; +# if defined (ACE_MT_SAFE) && (ACE_MT_SAFE != 0) + ACE_Guard< ACE_Recursive_Thread_Mutex > repo_monitor_; +#endif }; -ACE_Service_Type_Forward_Declaration_Guard::ACE_Service_Type_Forward_Declaration_Guard - (ACE_Service_Repository *r, const ACE_TCHAR *name) +ACE_Service_Type_Dynamic_Guard::ACE_Service_Type_Dynamic_Guard + (ACE_Service_Repository &r, const ACE_TCHAR *name) : repo_ (r) + , repo_begin_ (r.current_size ()) , name_ (name) +# if defined (ACE_MT_SAFE) && (ACE_MT_SAFE != 0) + // On this thread (for the duration of the initialize() method), + // we're about to do two things that require locking: (1) fiddle + // with the repository and (2) load a DLL and hence lock the + // DLL_Manager. + // + // Now if we don't lock the repo here, it is possible that two + // threads may deadlock on initialization because they can acquire + // locks (1) and (2) in different order, for instance: + // + // T1: loads a DLL (2) and registers a service (1); + // + // T2: may be relocating a service (1), which could lead to a + // (re)opening or uping the ref count on a DLL (2); + // + // To prevent this, we lock the repo here, using the repo_monitor_ + // member guard. + , repo_monitor_ (r.lock_) +#endif { - ACE_ASSERT (this->repo_ != 0); // No repository specified? ACE_ASSERT (this->name_ != 0); // No name? - ACE_NEW_NORETURN (this->dummy_, // Allocate the forward declaration ... ACE_Service_Type (this->name_, // ... use the same name 0, // ... inactive @@ -77,33 +114,38 @@ ACE_Service_Type_Forward_Declaration_Guard::ACE_Service_Type_Forward_Declaration if(ACE::debug ()) ACE_DEBUG ((LM_DEBUG, - ACE_LIB_TEXT ("ACE (%P|%t) FWDCL::start, repo=%@, \'%s\' ") - ACE_LIB_TEXT ("- type=%@ (impl=(nil))\n"), - this->repo_, - this->name_, - this->dummy_)); - - // Note that the dummy_'s memory can disaper between invoking - // the ctor and dtor, if the expected "real" dynamic service is - // inserted in the repository. - this->repo_->insert (this->dummy_); + ACE_LIB_TEXT ("ACE (%P|%t) STDG::<ctor>, repo=%@ [%d], ") + ACE_LIB_TEXT ("name=%s, type=%@, impl=%@, object=%@, active=%d - inserting dummy forward\n"), + &this->repo_, this->repo_begin_, this->name_, this->dummy_, + this->dummy_->type (), + (this->dummy_->type () != 0) ? this->dummy_->type ()->object () : 0, + this->dummy_->active ())); + + // Note that the dummy_'s memory may be deallocated between invoking + // the ctor and dtor, if the expected ("real") dynamic service is + // inserted in the repository. See how this affects the destructor's + // behavior, below. + this->repo_.insert (this->dummy_); } -ACE_Service_Type_Forward_Declaration_Guard::~ACE_Service_Type_Forward_Declaration_Guard (void) + +/// Destructor + +ACE_Service_Type_Dynamic_Guard::~ACE_Service_Type_Dynamic_Guard (void) { const ACE_Service_Type *tmp = 0; // Lookup without ignoring suspended services. Making sure // not to ignore any inactive services, since those may be forward // declarations - int ret = this->repo_->find (this->name_, &tmp, 0); + int ret = this->repo_.find_i (this->name_, &tmp, 0); // We inserted it (as inactive), so we expect to find it, right? if (ret < 0 && ret != -2) { if(ACE::debug ()) ACE_ERROR ((LM_WARNING, - ACE_LIB_TEXT ("ACE (%P|%t) FWDCL::end - Failed (%d) to find %s\n"), + ACE_LIB_TEXT ("ACE (%P|%t) STDG::<dtor> - Failed (%d) to find %s\n"), ret, this->name_)); return; } @@ -111,62 +153,69 @@ ACE_Service_Type_Forward_Declaration_Guard::~ACE_Service_Type_Forward_Declaratio if (tmp != 0 && tmp->type () != 0) { // Something has registered a proper (non-forward-decl) service with - // the same name as our dummy. The ACE_Service_Gestalt::insert() modifies - // the memory for the previous ACE_Service_Type instance. It has in fact - // taken ownership and deleted the instance when it replaced it with the - // actual implementation, so nothing is left to do. We are hereby giving - // up any ownership claims. - this->dummy_ = 0; + // the same name as our dummy. if(ACE::debug ()) - { - ACE_DEBUG ((LM_DEBUG, - ACE_LIB_TEXT ("ACE (%P|%t) FWDCL::end, repo=%@ - ") - ACE_LIB_TEXT ("Found different decl - "), - this->repo_, - this->name_)); - tmp->dump (); - } + ACE_DEBUG ((LM_DEBUG, + ACE_LIB_TEXT ("ACE (%P|%t) STDG::<dtor>, repo=%@, name=%s - updating [%d - %d]\n"), + &this->repo_, + this->name_, + this->repo_begin_, + this->repo_.current_size ())); + + // Relocate any static services. If any have been registered in + // the context of this guard, those really aren't static + // services because their code is in the DLL's code segment + this->repo_.relocate_i (this->repo_begin_, this->repo_.current_size (), tmp->dll()); + + // The ACE_Service_Gestalt::insert() modifies the memory for the + // original ACE_Service_Type instance. It deletes our dummy + // instance when replacing it with the actual implementation, so + // we are hereby simply giving up ownership. + this->dummy_ = 0; + if(ACE::debug ()) + ACE_DEBUG ((LM_DEBUG, + ACE_LIB_TEXT ("ACE (%P|%t) STDG::<dtor>, repo=%@ [%d], ") + ACE_LIB_TEXT ("name=%s, type=%@, impl=%@, object=%@, active=%d - loaded\n"), + &this->repo_, this->repo_begin_, this->name_, tmp, tmp->type (), + (tmp->type () != 0) ? tmp->type ()->object () : 0, + tmp->active ())); } else { if(ACE::debug ()) - { - ACE_DEBUG ((LM_DEBUG, - ACE_LIB_TEXT ("ACE (%P|%t) FWDCL::end, repo=%@ - ") - ACE_LIB_TEXT ("Removing incomplete decl - "), - this->repo_, - this->name_)); - this->dummy_->dump (); - } + ACE_DEBUG ((LM_DEBUG, + ACE_LIB_TEXT ("ACE (%P|%t) STDG::<dtor>, repo=%@, ") + ACE_LIB_TEXT ("name=%s, type=%@, impl=%@, object=%@, active=%d - removing dummy forward\n"), + &this->repo_, this->name_, this->dummy_, this->dummy_->type (), + (this->dummy_->type () != 0) ? this->dummy_->type ()->object () : 0, + this->dummy_->active ())); // The (dummy) forward declaration is still there and is // the same, which means that no actual declaration was // provided inside the guarded scope. Therefore, the forward // declaration is no longer necessary. - if (this->repo_->remove (this->name_, - const_cast< ACE_Service_Type**> (&this->dummy_)) == 0) + if (this->repo_.remove_i (this->name_, + const_cast< ACE_Service_Type**> (&this->dummy_)) == 0) { + // If it is a dummy then deleting it while holding the repo lock is okay. There will be no + // call to service object's fini() and no possibility for deadlocks. delete this->dummy_; } else { - if(ACE::debug ()) - { - ACE_ERROR ((LM_ERROR, - ACE_LIB_TEXT ("ACE (%P|%t) FWDCL::end, repo=%@ - ") - ACE_LIB_TEXT ("Failed to remove incomplete decl"), - this->repo_, - this->name_)); - this->dummy_->dump (); - } + ACE_ERROR ((LM_WARNING, + ACE_LIB_TEXT ("ACE (%P|%t) STDG::<dtor>, repo=%@, name=%s, ") + ACE_LIB_TEXT ("type=%@, impl=%@, object=%@, active=%d - dummy remove failed\n"), + &this->repo_, this->name_, this->dummy_, this->dummy_->type (), + (this->dummy_->type () != 0) ? this->dummy_->type ()->object () : 0, + this->dummy_->active ())); } } // Clean up this->dummy_ = 0; - this->repo_ = 0; } @@ -180,20 +229,11 @@ Processed_Static_Svc (const ACE_Static_Svc_Descriptor *assd) { ACE_NEW_NORETURN (name_, ACE_TCHAR[ACE_OS::strlen(assd->name_)+1]); ACE_OS::strcpy(name_,assd->name_); -// if (ACE::debug ()) -// ACE_DEBUG ((LM_DEBUG, -// ACE_LIB_TEXT ("ACE (%P|%t) PSS::ctor - name = %s\n"), -// name_)); } ACE_Service_Gestalt::Processed_Static_Svc:: ~Processed_Static_Svc (void) { -// if (ACE::debug ()) -// ACE_DEBUG ((LM_DEBUG, -// ACE_LIB_TEXT ("ACE (%P|%t) PSS::dtor - name = %s\n"), -// name_)); - delete [] name_; } @@ -309,6 +349,8 @@ ACE_Service_Gestalt::find_static_svc_descriptor (const ACE_TCHAR* name, return -1; } +/// @brief + const ACE_Static_Svc_Descriptor* ACE_Service_Gestalt::find_processed_static_svc (const ACE_TCHAR* name) { @@ -323,10 +365,35 @@ ACE_Service_Gestalt::find_processed_static_svc (const ACE_TCHAR* name) return 0; } + + +/// @brief Captures a list of the direcives processed (explicitely) for this +/// Gestalt so that services can be replicated in other repositories +/// upon their first initialization. +/// +/// This is part of the mechanism ensuring distinct local instances +/// for static service objects, loaded in another repository. + void ACE_Service_Gestalt::add_processed_static_svc (const ACE_Static_Svc_Descriptor *assd) { + + /// When process_directive(Static_Svc_Descriptor&) is called, it + /// associates a service object with the Gestalt and makes the + /// resource (a Service Object) local to the repository. This is but + /// the first step in using such SO. The next is the + /// "initialization" step. It is typicaly done through a "static" + /// service configuration directive. + /// + /// In contrast a "dynamic" directive, when processed through the + /// overloaded process_directives(string) both creates the SO + /// locally and initializes it, where the statis directive must + /// first locate the SO and then calls the init() method. This means + /// that durig the "static" initialization there's no specific + /// information about the hosting repository and the gestalt must + /// employ some lookup strategy to find it elsewhere. + if (this->processed_static_svcs_ == 0) ACE_NEW (this->processed_static_svcs_, ACE_PROCESSED_STATIC_SVCS); @@ -345,26 +412,41 @@ ACE_Service_Gestalt::add_processed_static_svc Processed_Static_Svc *tmp = 0; ACE_NEW (tmp,Processed_Static_Svc(assd)); this->processed_static_svcs_->insert(tmp); + + if (ACE::debug ()) + ACE_DEBUG ((LM_DEBUG, + ACE_LIB_TEXT ("ACE (%P|%t) SG::add_processed_statisc_svc, ") + ACE_LIB_TEXT ("repo=%@ - %s\n"), + this->repo_, + assd->name_)); } + +/// Queues static service object descriptor which, during open() will +/// be given to process_directive() to create the Service +/// Object. Normally, only called from static initializers, prior to +/// calling open(). + int ACE_Service_Gestalt::insert (ACE_Static_Svc_Descriptor *stsd) { if (ACE::debug ()) { + static int pid = ACE_OS::getpid (); + // If called during static initialization ACE_Log_Msg may not // have been initialized yet, so use printf intead. Using a "//" // prefix in case the executable is a C++ code generator and the // output gets embedded in the generated code. ACE_OS::fprintf (stderr, "// (%d|0) SG::insert" - " repo=%p, name=%s - queuing a Static_Svc_Descriptor:" - " active=%d, repo opened=%d.\n", - ACE_OS::getpid (), + " repo=%p (opened=%d) - enqueue %s," + " active=%d.\n", + pid, this->repo_, + this->is_opened_, stsd->name_, - stsd->active_, - this->is_opened_); + stsd->active_); } // Inserting a service after the Gestalt has been opened makes it @@ -441,9 +523,9 @@ ACE_Service_Gestalt::initialize (const ACE_TCHAR *svc_name, { // ... report and remove this entry. ACE_ERROR ((LM_ERROR, - ACE_LIB_TEXT ("ACE (%P|%t) SG::initialize - static init of \'%s\'") - ACE_LIB_TEXT (" failed (%p)\n"), - svc_name)); + ACE_LIB_TEXT ("ACE (%P|%t) SG::initialize - static init of \'%s\'") + ACE_LIB_TEXT (" failed (%p)\n"), + svc_name)); this->repo_->remove (svc_name); return -1; } @@ -516,18 +598,23 @@ ACE_Service_Gestalt::initialize (const ACE_Service_Type_Factory *stf, // registered *after* the dynamic service that loads them, so that // their finalization is complete *before* finalizing the dynamic // service. - ACE_Service_Type_Forward_Declaration_Guard dummy (this->repo_, - stf->name ()); + ACE_Service_Type_Dynamic_Guard dummy (*this->repo_, + stf->name ()); // make_service_type() is doing the dynamic loading and also runs // any static initializers ACE_Auto_Ptr<ACE_Service_Type> tmp (stf->make_service_type (this)); + if (tmp.get () != 0 && this->initialize_i (tmp.get (), parameters) == 0) { - // All good the ACE_Service_Type instance is now owned by the repository - // and we should make sure it is not destroyed upon exit from this method. - (void)tmp.release (); + // All good. Tthe ACE_Service_Type instance is now owned by the + // repository and we should make sure it is not destroyed upon + // exit from this method. + tmp.release (); + + + return 0; } @@ -676,16 +763,6 @@ int ACE_Service_Gestalt::process_directive_i (const ACE_Static_Svc_Descriptor &ssd, int force_replace) { -#ifndef ACE_NLOGGING - if (ACE::debug ()) - ACE_DEBUG ((LM_DEBUG, - ACE_LIB_TEXT ("ACE (%P|%t) SG::process_directive, ") - ACE_LIB_TEXT ("repo=%@, replace=%d - %s\n"), - this->repo_, - force_replace, - ssd.name_)); -#endif - if (!force_replace) { if (this->repo_->find (ssd.name_, 0, 0) >= 0) @@ -695,6 +772,7 @@ ACE_Service_Gestalt::process_directive_i (const ACE_Static_Svc_Descriptor &ssd, } } + ACE_Service_Object_Exterminator gobbler; void *sym = (ssd.alloc_)(&gobbler); @@ -711,7 +789,9 @@ ACE_Service_Gestalt::process_directive_i (const ACE_Static_Svc_Descriptor &ssd, ACE_Service_Type *service_type; // This is just a temporary to force the compiler to use the right - // constructor in ACE_Service_Type + // constructor in ACE_Service_Type. Note that, in cases where we are + // called from a static initializer which is part of a DLL, there is + // not enough information about the actuall DLL in this context. ACE_DLL tmp_dll; ACE_NEW_RETURN (service_type, @@ -721,6 +801,17 @@ ACE_Service_Gestalt::process_directive_i (const ACE_Static_Svc_Descriptor &ssd, ssd.active_), -1); +#ifndef ACE_NLOGGING + if (ACE::debug ()) + ACE_DEBUG ((LM_DEBUG, + ACE_LIB_TEXT ("ACE (%P|%t) SG::process_directive_i, ") + ACE_LIB_TEXT ("repo=%@ - %s, dll=%s, force=%d\n"), + this->repo_, + ssd.name_, + (tmp_dll.dll_name_ == 0) ? ACE_LIB_TEXT ("<null>") : tmp_dll.dll_name_, + force_replace)); +#endif + return this->repo_->insert (service_type); } @@ -810,15 +901,16 @@ ACE_Service_Gestalt::process_file (const ACE_TCHAR file[]) if (this->repo_->find (file, 0, 0) >=0) { ACE_DEBUG ((LM_WARNING, - ACE_TEXT ("ACE (%P|%t) Configuration file %s is currently") - ACE_TEXT (" being processed. Ignoring recursive process_file().\n"), - file)); + ACE_TEXT ("ACE (%P|%t) Configuration file %s is currently") + ACE_TEXT (" being processed. Ignoring recursive process_file().\n"), + file)); return 0; } // Register a dummy service as a forward decl, using the file name as name. // The entry will be automaticaly removed once the thread exits this block. - ACE_Service_Type_Forward_Declaration_Guard recursion_guard (this->repo_, file); + ACE_Service_Type_Dynamic_Guard recursion_guard (*this->repo_, + file); /* * @TODO: Test with ACE_USES_CLASSIC_SVC_CONF turned off! @@ -877,11 +969,13 @@ ACE_Service_Gestalt::process_directive (const ACE_TCHAR directive[]) { ACE_TRACE ("ACE_Service_Gestalt::process_directive"); +#ifndef ACE_NLOGGING if (ACE::debug ()) ACE_DEBUG ((LM_DEBUG, ACE_LIB_TEXT ("ACE (%P|%t) SG::process_directive, repo=%@ - %s\n"), this->repo_, directive)); +#endif #if (ACE_USES_CLASSIC_SVC_CONF == 1) ACE_UNUSED_ARG (directive); @@ -927,11 +1021,14 @@ ACE_Service_Gestalt::init_svc_conf_file_queue (void) this->svc_conf_file_queue_ = tmp; } +#ifndef ACE_NLOGGING if (ACE::debug ()) ACE_DEBUG ((LM_DEBUG, ACE_LIB_TEXT ("ACE (%P|%t) SG::init_svc_conf_file_queue ") ACE_LIB_TEXT ("- this=%@, repo=%@\n"), this, this->repo_)); +#endif + return 0; } /* init_svc_conf_file_queue () */ @@ -955,11 +1052,13 @@ ACE_Service_Gestalt::open_i (const ACE_TCHAR /*program_name*/[], u_long old_thread_mask = log_msg->priority_mask (ACE_Log_Msg::THREAD); +#ifndef ACE_NLOGGING if (ACE::debug ()) ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("ACE (%P|%t) SG::open_i - this=%@, ") ACE_TEXT ("opened=%d, loadstatics=%d\n"), this, this->is_opened_, this->no_static_svcs_)); +#endif // Guard against reentrant processing. For example, // if the singleton gestalt (ubergestalt) was already open, @@ -1029,8 +1128,8 @@ ACE_Service_Gestalt::process_commandline_directives (void) if (this->process_directive ((sptr->fast_rep ())) != 0) { ACE_ERROR ((LM_ERROR, - ACE_LIB_TEXT ("ACE (%P|%t) %p\n"), - ACE_LIB_TEXT ("process_directive"))); + ACE_LIB_TEXT ("ACE (%P|%t) %p\n"), + ACE_LIB_TEXT ("process_directive"))); result = -1; } } @@ -1167,11 +1266,13 @@ ACE_Service_Gestalt::close (void) delete this->svc_conf_file_queue_; this->svc_conf_file_queue_ = 0; +#ifndef ACE_NLOGGING // Delete the dynamically allocated static_svcs instance. if (ACE::debug ()) ACE_DEBUG ((LM_DEBUG, ACE_LIB_TEXT ("ACE (%P|%t) SG::close - this=%@, repo=%@, pss = %@\n"), this, this->repo_, this->processed_static_svcs_)); +#endif delete this->static_svcs_; this->static_svcs_ = 0; @@ -1189,11 +1290,13 @@ ACE_Service_Gestalt::close (void) } delete this->processed_static_svcs_; this->processed_static_svcs_ = 0; + +#ifndef ACE_NLOGGING if (ACE::debug ()) ACE_DEBUG ((LM_DEBUG, ACE_LIB_TEXT ("ACE (%P|%t) SG::close - complete this=%@, repo=%@\n"), this, this->repo_)); - +#endif return 0; |