// -*- C++ -*-

/**
 *  @file   ESF_Proxy_Collection.h
 *
 *  $Id: ESF_Proxy_Collection.h 76589 2007-01-25 18:04:11Z elliott_c $
 *
 *  @author Carlos O'Ryan (coryan@cs.wustl.edu)
 *
 *  http://doc.ece.uci.edu/~coryan/EC/index.html
 */

#ifndef TAO_ESF_PROXY_COLLECTION_H
#define TAO_ESF_PROXY_COLLECTION_H
#include /**/ "ace/pre.h"

#include "ace/CORBA_macros.h"

#if !defined (ACE_LACKS_PRAGMA_ONCE)
# pragma once
#endif /* ACE_LACKS_PRAGMA_ONCE */

TAO_BEGIN_VERSIONED_NAMESPACE_DECL

template<class Target> class TAO_ESF_Worker;

/**
 * @class TAO_ESF_Proxy_Collection
 *
 * @brief ESF_Proxy_Collection
 *
 * Many components in an Event Service need to keep a collection
 * of proxies; these collections must be able to cope with several
 * concurrency issues:
 * + Some threads may need to iterate over the collection and
 *   invoke a method on each element.  Locking the collection
 *   while this is done is not feasible in all cases: under some
 *   configurations the same thread that is iterating over the
 *   collection may need to make changes to the set.
 * + A recursive lock does not solve the concurrency problems
 *   because recursive changes to the collection still invalidate
 *   the iterators.
 *
 * There are several solutions to this problem (see the VARIANTS)
 * section, and there is no single one that works bests in all
 * cases.  As usual, we wish the strategize the protocol used to
 * serialize iterations and changes to the collection.  This class
 * encapsulates that protocol.
 *
 * The usual form of the Iterator pattern does not work well in
 * this case: in several variants the synchronization protocol and
 * the iteration loop must collaborate to work efficiently.
 * Exposing an external iterator would require that every other
 * component in the system can support all the synchronization
 * protocols.   It is possible to hide some of that complexity
 * using heavy weight iterators, but their use is ackward,
 * specially since the Koening-style iterators have become more
 * popular.
 *
 * Regular member functions are used to insert, remove and update
 * members of the collection and to shutdown (i.e. perform final
 * cleanup operations).
 *
 * The class must also collaborate with other components of the
 * EC to efficiently and safely perform memory managment of the
 * members in the collection.
 *
 * The PROXY object must be reference counted with the following
 * operations:
 *
 * + _incr_refcnt() - increment the reference count.
 * + _decr_refcnt() - decrement the reference count.
 *
 * = VARIANTS
 *
 * We identify several sources of variation:
 *
 * + Immediate_Changes: in this variant the iteration in performed
 *   while holding some kind of synchronization primitive, such as a
 *   thread mutex, a recursive mutex, a RW lock, etc.
 *   This is only useful in configurations where a separate thread
 *   dispatches the events, and thus, can only be used with real
 *   locks.
 * + Copy_On_Read: before performing the iteration the collection
 *   is duplicated into a temporary array.  Thus no locks are held
 *   during the iteration.  This is a very expensive approach, but
 *   useful in many cases.
 *   The kind of lock is also strategized in this case.
 * + Copy_On_Write: this is very similar to the previous approach,
 *   but the collection is only duplicated when a change is required
 *   while some thread is performing an iteration.  The iteration
 *   continues over the original copy, while the changes are
 *   performed in the duplicate.  The new copy of the collection is
 *   used for any subsequent operations, the original is discarded
 *   when the last thread using it completes its work.
 *   This approach optimizes for the case where no changes are
 *   is duplicated into a temporary array.  Thus no locks are held
 *   during the iteration.  This is a very expensive approach, but
 *   useful in many cases.
 *   The kind of lock is also strategized in this case.
 * + Delayed_Changes: before starting the iteration a counter is
 *   incremented, this counter is used to keep track of the number
 *   of threads concurrently using the collection.
 *   If a thread wants to perform a change to the collection it must
 *   first verify that there are no threads iterating over it.  If
 *   there are any threads then the thread queues the modification
 *   for later execution, using the Command pattern.
 *   The kind of lock is strategized, as this approach is used in
 *   single threaded configurations.
 *   There are two main variations:
 *   - An upcall can result in new dispatches: in this case we
 *     have to keep track of a the list of current threads using
 *     a Set, to avoid dead-locks. The design is not complete,
 *     probably similar to the next one.
 *   - Otherwise we just need to control the concurrency using the
 *     algorithm described below.
 *
 * It assumes ownership of the proxies added to the collection,
 * it increases the reference count.
 *
 * Locking is provided by derived classes.
 */
template<class PROXY>
class TAO_ESF_Proxy_Collection
{
public:
  /// destructor
  virtual ~TAO_ESF_Proxy_Collection (void);

  /**
   * Iterate over the collection and invoke worker->work() for each
   * member of the collection.
   * This encapsulates
   */
  virtual void for_each (TAO_ESF_Worker<PROXY> *worker) = 0;

  /// Insert a new element into the collection.  The collection assumes
  /// ownership of the element.
  virtual void connected (PROXY *proxy) = 0;

  /**
   * Insert an element into the collection.  No errors can be raised
   * if the element is already present.
   * The collection assumes ownership, i.e. must invoke
   * <proxy->_decr_refcnt()> if the element is already present in the
   * collection.
   */
  virtual void reconnected (PROXY *proxy) = 0;

  /// Remove an element from the collection.
  virtual void disconnected (PROXY *proxy) = 0;

  /// The EC is shutting down, must release all the elements.
  virtual void shutdown (void) = 0;
};

// ****************************************************************

TAO_END_VERSIONED_NAMESPACE_DECL

#if defined (ACE_TEMPLATES_REQUIRE_SOURCE)
#include "orbsvcs/ESF/ESF_Proxy_Collection.cpp"
#endif /* ACE_TEMPLATES_REQUIRE_SOURCE */

#if defined (ACE_TEMPLATES_REQUIRE_PRAGMA)
#pragma implementation ("ESF_Proxy_Collection.cpp")
#endif /* ACE_TEMPLATES_REQUIRE_PRAGMA */

#include /**/ "ace/post.h"
#endif /* TAO_ESF_PROXY_COLLECTION_H */
