summaryrefslogtreecommitdiff
path: root/ACE/ace/Service_Repository.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'ACE/ace/Service_Repository.cpp')
-rw-r--r--ACE/ace/Service_Repository.cpp626
1 files changed, 626 insertions, 0 deletions
diff --git a/ACE/ace/Service_Repository.cpp b/ACE/ace/Service_Repository.cpp
new file mode 100644
index 00000000000..67fbc18e538
--- /dev/null
+++ b/ACE/ace/Service_Repository.cpp
@@ -0,0 +1,626 @@
+// $Id$
+
+#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_Msg.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 (void) 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 (void)
+{
+ 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 (void)
+{
+ ACE_TRACE ("ACE_Service_Repository::fini");
+ ACE_MT (ACE_GUARD_RETURN (ACE_Recursive_Thread_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<ACE_Service_Type *> (this->service_array_[i]);
+ if (s == 0)
+ ACE_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;)
+ {
+ // <fini> the services in reverse order.
+ ACE_Service_Type *s =
+ const_cast<ACE_Service_Type *> (this->service_array_[i]);
+
+ if (s != 0 &&
+ s->type () != 0 &&
+ (s->type ()->service_type () != ACE_Service_Type::MODULE))
+ {
+#ifndef ACE_NLOGGING
+ if (ACE::debug ())
+ {
+ ACE_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;)
+ {
+ // <fini> the services in reverse order.
+ ACE_Service_Type *s =
+ const_cast<ACE_Service_Type *> (this->service_array_[i]);
+
+ if (s != 0 &&
+ s->type () != 0 &&
+ (s->type ()->service_type () == ACE_Service_Type::MODULE))
+ {
+#ifndef ACE_NLOGGING
+ if (ACE::debug ())
+ {
+ ACE_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 (void)
+{
+ ACE_TRACE ("ACE_Service_Repository::close");
+ ACE_MT (ACE_GUARD_RETURN (ACE_Recursive_Thread_Mutex, ace_mon, this->lock_, -1));
+
+#ifndef ACE_NLOGGING
+ if(ACE::debug ())
+ ACE_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<ACE_Service_Type *> (this->service_array_[i]);
+
+#ifndef ACE_NLOGGING
+ if(ACE::debug ())
+ {
+ if (s == 0)
+ ACE_DEBUG ((LM_DEBUG,
+ ACE_TEXT ("ACE (%P|%t) SR::close - repo=%@ [%d] -> 0\n"),
+ this,
+ i));
+ else
+ ACE_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 (void)
+{
+ ACE_TRACE ("ACE_Service_Repository::~ACE_Service_Repository");
+#ifndef ACE_NLOGGING
+ if(ACE::debug ())
+ ACE_DEBUG ((LM_DEBUG, "ACE (%P|%t) SR::<dtor>, 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");
+ size_t i = 0;
+ array_type::const_iterator element = this->service_array_.end ();
+
+ for (i = 0; i < this->service_array_.size(); i++)
+ {
+ array_type::const_iterator iter = this->service_array_.find (i);
+ if (iter != this->service_array_.end ()
+ && (*iter).second != 0 // skip any empty slots
+ && ACE_OS::strcmp (name, (*iter).second->name ()) == 0)
+ {
+ element = iter;
+ break;
+ }
+ }
+
+ if (element != this->service_array_.end ())
+ {
+ slot = i;
+ if ((*element).second->fini_called ())
+ {
+ if (srp != 0)
+ *srp = 0;
+ return -1;
+ }
+
+ if (srp != 0)
+ *srp = (*element).second;
+
+ if (ignore_suspended
+ && (*element).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<ACE_Service_Type *> (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)
+ ACE_DEBUG ((LM_DEBUG,
+ ACE_TEXT ("ACE (%P|%t) SR::relocate_i - repo=%@ [%d]")
+ ACE_TEXT (": skipping empty slot\n"),
+ this,
+ i));
+ else
+ ACE_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 ())
+ ACE_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_MT (ACE_GUARD_RETURN (ACE_Recursive_Thread_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_MT (ACE_GUARD_RETURN (ACE_Recursive_Thread_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 ())
+ ACE_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_MT (ACE_GUARD_RETURN (ACE_Recursive_Thread_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_MT (ACE_GUARD_RETURN (ACE_Recursive_Thread_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_MT (ACE_GUARD_RETURN (ACE_Recursive_Thread_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<ACE_Service_Type *> (this->service_array_[i]);
+
+#ifndef ACE_NLOGGING
+ if (ACE::debug ())
+ ACE_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 (void) 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 (void)
+{
+ 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 (void) 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