#include "ace/Service_Repository.h" #if !defined (__ACE_INLINE__) #include "ace/Service_Repository.inl" #endif /* __ACE_INLINE__ */ #include "ace/Service_Types.h" #include "ace/Object_Manager.h" #include "ace/Log_Category.h" #include "ace/ACE.h" #include "ace/OS_NS_unistd.h" #include "ace/OS_NS_errno.h" #include "ace/OS_NS_string.h" ACE_BEGIN_VERSIONED_NAMESPACE_DECL ACE_ALLOC_HOOK_DEFINE(ACE_Service_Repository) /// Process-wide Service Repository. ACE_Service_Repository *ACE_Service_Repository::svc_rep_ = 0; /// Controls whether the Service_Repository is deleted when we shut /// down (we can only delete it safely if we created it)! bool ACE_Service_Repository::delete_svc_rep_ = false; void ACE_Service_Repository::dump () const { #if defined (ACE_HAS_DUMP) ACE_TRACE ("ACE_Service_Repository::dump"); #endif /* ACE_HAS_DUMP */ } ACE_Service_Repository * ACE_Service_Repository::instance (size_t size) { ACE_TRACE ("ACE_Service_Repository::instance"); if (ACE_Service_Repository::svc_rep_ == 0) { // Perform Double-Checked Locking Optimization. ACE_MT (ACE_GUARD_RETURN (ACE_Recursive_Thread_Mutex, ace_mon, *ACE_Static_Object_Lock::instance (), 0)); if (ACE_Service_Repository::svc_rep_ == 0) { if (ACE_Object_Manager::starting_up () || !ACE_Object_Manager::shutting_down ()) { ACE_NEW_RETURN (ACE_Service_Repository::svc_rep_, ACE_Service_Repository (size), 0); ACE_Service_Repository::delete_svc_rep_ = true; } } } return ACE_Service_Repository::svc_rep_; } ACE_Service_Repository * ACE_Service_Repository::instance (ACE_Service_Repository *s) { ACE_TRACE ("ACE_Service_Repository::instance"); ACE_MT (ACE_GUARD_RETURN (ACE_Recursive_Thread_Mutex, ace_mon, *ACE_Static_Object_Lock::instance (), 0)); ACE_Service_Repository *t = ACE_Service_Repository::svc_rep_; // We can't safely delete it since we don't know who created it! ACE_Service_Repository::delete_svc_rep_ = false; ACE_Service_Repository::svc_rep_ = s; return t; } void ACE_Service_Repository::close_singleton () { ACE_TRACE ("ACE_Service_Repository::close_singleton"); ACE_MT (ACE_GUARD (ACE_Recursive_Thread_Mutex, ace_mon, *ACE_Static_Object_Lock::instance ())); if (ACE_Service_Repository::delete_svc_rep_) { delete ACE_Service_Repository::svc_rep_; ACE_Service_Repository::svc_rep_ = 0; ACE_Service_Repository::delete_svc_rep_ = false; } } /// Initialize the Repository to a clean slate. int ACE_Service_Repository::open (size_t size) { ACE_TRACE ("ACE_Service_Repository::open"); // Create a new array and swap it with the local array array_type local_array (size); this->service_array_.swap (local_array); return 0; } ACE_Service_Repository::ACE_Service_Repository (size_t size) : service_array_ (size) { ACE_TRACE ("ACE_Service_Repository::ACE_Service_Repository"); } /// Finalize (call fini() and possibly delete) all the services. int ACE_Service_Repository::fini () { ACE_TRACE ("ACE_Service_Repository::fini"); ACE_GUARD_RETURN (ACE_SYNCH_RECURSIVE_MUTEX, ace_mon, this->lock_, -1); int retval = 0; // Do not be tempted to use the prefix decrement operator. Use // postfix decrement operator since the index is unsigned and may // wrap around the 0 // // debug output for empty service entries #ifndef ACE_NLOGGING if (ACE::debug ()) { for (size_t i = this->service_array_.size (); i-- != 0;) { ACE_Service_Type *s = const_cast (this->service_array_[i]); if (s == 0) ACELIB_DEBUG ((LM_DEBUG, ACE_TEXT ("ACE (%P|%t) SR::fini, repo=%@ [%d] -> 0\n"), this, i)); } } #endif // // Remove all the Service_Object and Stream instances // for (size_t i = this->service_array_.size (); i-- != 0;) { // the services in reverse order. ACE_Service_Type *s = const_cast (this->service_array_[i]); if (s != 0 && s->type () != 0 && (s->type ()->service_type () != ACE_Service_Type::MODULE)) { #ifndef ACE_NLOGGING if (ACE::debug ()) { ACELIB_DEBUG ((LM_DEBUG, ACE_TEXT ("ACE (%P|%t) SR::fini, repo=%@ [%d], ") ACE_TEXT ("name=%s, type=%@, object=%@, active=%d\n"), this, i, s->name (), s->type (), (s->type () != 0) ? s->type ()->object () : 0, s->active ())); } #endif // Collect any errors. retval += s->fini (); } } // // Remove all the Module instances // for (size_t i = this->service_array_.size (); i-- != 0;) { // the services in reverse order. ACE_Service_Type *s = const_cast (this->service_array_[i]); if (s != 0 && s->type () != 0 && (s->type ()->service_type () == ACE_Service_Type::MODULE)) { #ifndef ACE_NLOGGING if (ACE::debug ()) { ACELIB_DEBUG ((LM_DEBUG, ACE_TEXT ("ACE (%P|%t) SR::fini, repo=%@ [%d], ") ACE_TEXT ("name=%s, type=%@, object=%@, active=%d\n"), this, i, s->name (), s->type (), (s->type () != 0) ? s->type ()->object () : 0, s->active ())); } #endif // Collect any errors. retval += s->fini (); } } return (retval == 0) ? 0 : -1; } /// Close down all the services. int ACE_Service_Repository::close () { ACE_TRACE ("ACE_Service_Repository::close"); ACE_GUARD_RETURN (ACE_SYNCH_RECURSIVE_MUTEX, ace_mon, this->lock_, -1); #ifndef ACE_NLOGGING if(ACE::debug ()) ACELIB_DEBUG ((LM_DEBUG, ACE_TEXT ("ACE (%P|%t) SR::close - repo=%@, size=%d\n"), this, this->service_array_.size())); #endif // Do not use the prefix decrement operator since the index is // unsigned and may wrap around the 0. for (size_t i = this->service_array_.size(); i-- != 0; ) { // Delete services in reverse order. ACE_Service_Type *s = const_cast (this->service_array_[i]); #ifndef ACE_NLOGGING if(ACE::debug ()) { if (s == 0) ACELIB_DEBUG ((LM_DEBUG, ACE_TEXT ("ACE (%P|%t) SR::close - repo=%@ [%d] -> 0\n"), this, i)); else ACELIB_DEBUG ((LM_DEBUG, ACE_TEXT ("ACE (%P|%t) SR::close - repo=%@ [%d], name=%s, object=%@\n"), this, i, s->name (), s)); } #endif delete s; } this->service_array_.clear (); return 0; } ACE_Service_Repository::~ACE_Service_Repository () { ACE_TRACE ("ACE_Service_Repository::~ACE_Service_Repository"); #ifndef ACE_NLOGGING if(ACE::debug ()) ACELIB_DEBUG ((LM_DEBUG, "ACE (%P|%t) SR::, this=%@\n", this)); #endif this->close (); } /// Locate an entry with @a name in the table. If @a ignore_suspended is /// set then only consider services marked as resumed. If the caller /// wants the located entry, pass back a pointer to the located entry /// via @a srp. If @a name is not found -1 is returned. If @a name is /// found, but it is suspended and the caller wants to ignore suspended /// services a -2 is returned. Must be called with locks held. int ACE_Service_Repository::find_i (const ACE_TCHAR name[], size_t &slot, const ACE_Service_Type **srp, bool ignore_suspended) const { ACE_TRACE ("ACE_Service_Repository::find_i"); array_type::const_iterator iter; for (iter = this->service_array_.begin(); iter != this->service_array_.end(); ++iter) { if ((*iter).second != 0 // skip any empty slots && ACE_OS::strcmp (name, (*iter).second->name ()) == 0) { break; } } if (iter != this->service_array_.end ()) { slot = (*iter).first; if ((*iter).second->fini_called ()) { if (srp != 0) *srp = 0; return -1; } if (srp != 0) *srp = (*iter).second; if (ignore_suspended && (*iter).second->active () == 0) return -2; return 0; } return -1; } /// @brief Relocate (a static) service to another DLL. /// /// Works by having the service type keep a reference to a specific /// DLL. No locking, caller makes sure calling it is safe. You can /// forcefully relocate any DLLs in the given range, not only the /// static ones - but that will cause Very Bad Things (tm) to happen. int ACE_Service_Repository::relocate_i (size_t begin, size_t end, const ACE_DLL& adll) { ACE_SHLIB_HANDLE new_handle = adll.get_handle (0); for (size_t i = begin; i < end; i++) { ACE_Service_Type *type = const_cast (this->service_array_[i]); ACE_SHLIB_HANDLE old_handle = (type == 0) ? ACE_SHLIB_INVALID_HANDLE : type->dll ().get_handle (0); #ifndef ACE_NLOGGING if (ACE::debug ()) { if (type == 0) ACELIB_DEBUG ((LM_DEBUG, ACE_TEXT ("ACE (%P|%t) SR::relocate_i - repo=%@ [%d]") ACE_TEXT (": skipping empty slot\n"), this, i)); else ACELIB_DEBUG ((LM_DEBUG, ACE_TEXT ("ACE (%P|%t) SR::relocate_i - repo=%@ [%d]") ACE_TEXT (": trying name=%s, handle: %d -> %d\n"), this, i, type->name (), old_handle, new_handle)); } #endif if (type != 0 // skip any gaps && old_handle == ACE_SHLIB_INVALID_HANDLE && new_handle != old_handle) { #ifndef ACE_NLOGGING if (ACE::debug ()) ACELIB_DEBUG ((LM_DEBUG, ACE_TEXT ("ACE (%P|%t) SR::relocate_i - repo=%@ [%d]") ACE_TEXT (": relocating name=%s, handle: %d -> %d\n"), this, i, type->name (), old_handle, new_handle)); #endif type->dll (adll); // ups the refcount on adll } } return 0; } int ACE_Service_Repository::find (const ACE_TCHAR name[], const ACE_Service_Type **srp, bool ignore_suspended) const { ACE_TRACE ("ACE_Service_Repository::find"); ACE_GUARD_RETURN (ACE_SYNCH_RECURSIVE_MUTEX, ace_mon, this->lock_, -1); size_t ignore_location = 0; return this->find_i (name, ignore_location, srp, ignore_suspended); } /// Insert the ACE_Service_Type SR into the repository. Note that /// services may be inserted either resumed or suspended. Using same /// name as in an existing service causes the delete () to be called /// for the old one, i.e. make sure @code sr is allocated on the heap! int ACE_Service_Repository::insert (const ACE_Service_Type *sr) { ACE_TRACE ("ACE_Service_Repository::insert"); size_t i = 0; int return_value = -1; ACE_Service_Type const *s = 0; // Establish scope for locking while manipulating the service // storage { // @TODO: Do we need a recursive mutex here? ACE_GUARD_RETURN (ACE_SYNCH_RECURSIVE_MUTEX, ace_mon, this->lock_, -1); return_value = find_i (sr->name (), i, &s, false); // Adding an entry. if (s != 0) { this->service_array_[i] = sr; } else { // New services are always added where current_size_ points, // because if any DLL relocation needs to happen, it will be // performed on services with indexes between some old // current_size_ and the new current_size_ value. See // ACE_Service_Type_Dynamic_Guard ctor and dtor for details. if (i < this->service_array_.size ()) i = this->service_array_.size (); this->service_array_[i] = sr; return_value = 0; } } #ifndef ACE_NLOGGING if (ACE::debug ()) ACELIB_DEBUG ((LM_DEBUG, ACE_TEXT ("ACE (%P|%t) SR::insert - repo=%@ [%d],") ACE_TEXT (" name=%s (%C) (type=%@, object=%@, active=%d)\n"), this, i, sr->name(), (return_value == 0 ? ((s==0) ? "new" : "replacing") : "failed"), sr->type (), (sr->type () != 0) ? sr->type ()->object () : 0, sr->active ())); #endif // If necessary, delete but outside the lock. (s may be 0, but // that's okay, too) delete s; if (return_value == -1) ACE_OS::last_error (ENOSPC); return return_value; } /// Resume a service that was previously suspended. int ACE_Service_Repository::resume (const ACE_TCHAR name[], const ACE_Service_Type **srp) { ACE_TRACE ("ACE_Service_Repository::resume"); ACE_GUARD_RETURN (ACE_SYNCH_RECURSIVE_MUTEX, ace_mon, this->lock_, -1); size_t i = 0; if (-1 == this->find_i (name, i, srp, 0)) return -1; return this->service_array_[i]->resume (); } /// Suspend a service so that it will not be considered active under /// most circumstances by other portions of the ACE_Service_Repository. int ACE_Service_Repository::suspend (const ACE_TCHAR name[], const ACE_Service_Type **srp) { ACE_TRACE ("ACE_Service_Repository::suspend"); ACE_GUARD_RETURN (ACE_SYNCH_RECURSIVE_MUTEX, ace_mon, this->lock_, -1); size_t i = 0; if (-1 == this->find_i (name, i, srp, 0)) return -1; return this->service_array_[i]->suspend (); } /** * @brief Completely remove a @a name entry from the Repository and * dynamically unlink it if it was originally dynamically linked. */ int ACE_Service_Repository::remove (const ACE_TCHAR name[], ACE_Service_Type **ps) { ACE_TRACE ("ACE_Service_Repository::remove"); ACE_Service_Type *s = 0; { ACE_GUARD_RETURN (ACE_SYNCH_RECURSIVE_MUTEX, ace_mon, this->lock_, -1); // Not found!? if (this->remove_i (name, &s) == -1) return -1; } if (ps != 0) *ps = s; else delete s; return 0; } /** * @brief Completely remove a @a name entry from the Repository and * dynamically unlink it if it was originally dynamically linked. * * Return a ptr to the entry in @code ps. There is no locking so make * sure you hold the repo lock when calling. * * Since the order of services in the Respository matters, we can't * simply overwrite the entry being deleted with the last and * decrement the @c current_size by 1. A good example of why the order * matters is a dynamic service, in whose DLL there is at least one * static service. In order to prevent SEGV during finalization, those * static services must be finalized _before_the dynamic service that * owns them. Otherwice the TEXT segment, containing the code for the * static service's desructor may be unloaded with the DLL. * * Neither can we "pack" the array because this may happen inside the * scope of a Service_Dynamic_Guard, which caches an index where * loading of a DLL started in order to relocate dependent services. */ int ACE_Service_Repository::remove_i (const ACE_TCHAR name[], ACE_Service_Type **ps) { size_t i = 0; if (-1 == this->find_i (name, i, 0, false)) return -1; // Not found // We may need the old ptr - to be delete outside the lock! *ps = const_cast (this->service_array_[i]); #ifndef ACE_NLOGGING if (ACE::debug ()) ACELIB_DEBUG ((LM_DEBUG, ACE_TEXT ("ACE (%P|%t) SR::remove_i - repo=%@ [%d],") ACE_TEXT (" name=%s (removed) (type=%@, active=%d)\n"), this, i, name, *ps, (*ps)->active ())); #endif this->service_array_[i] = 0; // simply leave a gap return 0; } ACE_ALLOC_HOOK_DEFINE(ACE_Service_Repository_Iterator) void ACE_Service_Repository_Iterator::dump () const { #if defined (ACE_HAS_DUMP) ACE_TRACE ("ACE_Service_Repository_Iterator::dump"); #endif /* ACE_HAS_DUMP */ } /// Initializes the iterator and skips over any suspended entries at /// the beginning of the table, if necessary. Note, you must not /// perform destructive operations on elements during this iteration... ACE_Service_Repository_Iterator::ACE_Service_Repository_Iterator (ACE_Service_Repository &sr, bool ignored_suspended) : svc_rep_ (sr), next_ (0), ignore_suspended_ (ignored_suspended) { while (!(done() || valid())) this->next_++; } /// Obtains a pointer to the next valid service in the table. If there /// are no more entries, returns 0, else 1. int ACE_Service_Repository_Iterator::next (const ACE_Service_Type *&sr) { ACE_TRACE ("ACE_Service_Repository_Iterator::next"); if (done ()) return 0; sr = this->svc_rep_.service_array_[this->next_]; return 1; } /// Advance the iterator by the proper amount. If we are ignoring /// suspended entries and the current entry is suspended, then we must /// skip over this entry. Otherwise, we must advance the NEXT index to /// reference the next valid service entry. int ACE_Service_Repository_Iterator::advance () { ACE_TRACE ("ACE_Service_Repository_Iterator::advance"); if (done()) return 0; do this->next_++; while (!(done () || valid ())); return !done(); } bool ACE_Service_Repository_Iterator::valid () const { ACE_TRACE ("ACE_Service_Repository_Iterator::valid"); if (!this->ignore_suspended_) return (this->svc_rep_.service_array_[this->next_] != 0); // skip over gaps return (this->svc_rep_.service_array_[this->next_] != 0 && this->svc_rep_.service_array_[this->next_]->active ()); } ACE_END_VERSIONED_NAMESPACE_DECL