//------------------------------------------------------------------------------
//	Copyright (C) 2007, 2008, 2009 Humboldt-Universitaet zu Berlin
//
//	This library is free software; you can redistribute it and/or
//	modify it under the terms of the GNU Lesser General Public
//	License as published by the Free Software Foundation; either
//	version 2.1 of the License, or (at your option) any later version.
//
//	This library is distributed in the hope that it will be useful,
//	but WITHOUT ANY WARRANTY; without even the implied warranty of
//	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
//	Lesser General Public License for more details.
//
//	You should have received a copy of the GNU Lesser General Public
//	License along with this library; if not, write to the Free Software
//	Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
//
//------------------------------------------------------------------------------
/**
 * @file PortHeadImpl.h
 * @author Ronald Kluth
 * @date created at 2007/04/04
 * @brief Declaration and Implementation of odemx::synchronization::PortHeadT
 * @see PortImpl.h, PortTailImpl.h
 * @since 2.0
 */

#ifndef ODEMX_SYNC_PORTHEAD_IMPL_INCLUDED
#define ODEMX_SYNC_PORTHEAD_IMPL_INCLUDED

#include <odemx/base/Process.h>
#include <odemx/base/Simulation.h>
#include <odemx/base/DefaultSimulation.h>
#include <odemx/synchronization/Memory.h>
#include <odemx/synchronization/port/PortMode.h>
#include <odemx/synchronization/port/PortImpl.h>

#ifdef _MSC_VER
#include <memory>
#else
#include <tr1/memory>
#endif


namespace odemx {
namespace synchronization {

// forward declarations
template < typename > class PortTailT;
template < typename > class PortHeadTObserver;

/**
 * @ingroup synch
 * @author Ronald Kluth
 * @brief %PortHeadT provides read access to a port
 * @see PortTailT, PortT
 *
 * This class manages read access to a limited internal buffer.
 * It can be run in three different error modes according to
 * PortMode. Calling @c get on an empty port head will either
 * block the reader (and alert him upon availability), report
 * an error, or return zero.
 *
 * @since 2.0
 */
template < typename ElementT >
class PortHeadT
:	public Memory	// is a data::Producer
,	public data::Observable< PortHeadTObserver< ElementT > >
,	public std::tr1::enable_shared_from_this< PortHeadT< ElementT > >
{
public:
	/// Type of the elements the port can store
	typedef ElementT ElementType;
	/// Storage type of the internal buffer
	typedef typename PortT< ElementType >::StorageType StorageType;
	/// Size type of the internal buffer
	typedef typename PortT< ElementType >::SizeType SizeType;
	/// Pointer type to manage port head objects
	typedef std::tr1::shared_ptr< PortHeadT< ElementType > > Ptr;
	/// Pointer type to manage port tail objects
	typedef std::tr1::shared_ptr< PortTailT< ElementType > > TailPtr;
	/// Type of the compatible port head observer
	typedef PortHeadTObserver< ElementType > ObserverType;

  /// Type of a selection function
  typedef bool( base::Process::*ElementCondition ) ( ElementType );

	/**
	 * @brief Creation for user-defined Simulation
	 * @param sim Reference to the simulation context
	 * @param label Label of the port, the head's label will be set to "label (head)"
	 * @param mode Port head's mode, default is WAITING_MODE
	 * @param limit Maximum number of elements, default is 10000
	 * @param obs Initial observer
	 * @return Shared pointer to a newly constructed port head
	 */
	static Ptr create( base::Simulation& sim, const data::Label& label,
			PortMode mode = WAITING_MODE, SizeType limit = 10000, ObserverType* obs = 0 )
	{
		Ptr rv( new PortHeadT( sim, label, mode, limit, obs ) );
		rv->initPort();
//		rv->port_.reset( new PortType( sim, label, limit ) );
//		rv->port_->setHead( typename PortType::HeadPtr( rv ) );
		return rv;
	}

	/// Destruction, managed by @c shared_ptr
	virtual ~PortHeadT()
	{
		ODEMX_TRACE << log( "destroy" ).scope( typeid( PortHeadT< ElementType > ) );
		ODEMX_OBS_T( ObserverType, Destroy( this ) );
	}

	/**
		\brief Port read access
		\return Pointer holding the first port element, or an empty pointer

		This function is used to read information from a port. It also
		responds according to the object's PortMode. In @c WAITING_MODE this
		leads to the suspension of the calling Process. If the caller
		is interrupted before receiving an element, it is reactivated
		and the function returns an empty pointer. In @c ZERO_MODE a
		miss only returns a null pointer, while in @c ERROR_MODE an error
		will be sent additionally.

		\note The returned object is always dynamically allocated, which
		is why an \c auto_ptr is used. This way, the caller never needs
		to delete the return value. Checking for null pointer is done in
		the following manner:
		\code
		std::auto_ptr< int > ptr = port.get();
		if( ptr.get() == 0 ) {
			// handle null return value
		}
		else {
			// use ptr like any other pointer
		}
		\endcode
	*/
	std::auto_ptr< ElementType > get()
	{
		ODEMX_TRACE << log( "get" ).detail( "partner", getPartner() )
				.scope( typeid( PortHeadT< ElementType > ) );

		ODEMX_OBS_T( ObserverType, Get( this ) );

		while( port_->isEmpty() )
		{
			base::Sched* caller = getSimulation().getCurrentSched();

			switch( mode_ )
			{
			case WAITING_MODE:
				if( caller == 0 )
				{
					error << log( "PortHeadT::get(): port in waiting mode called from environment" )
							.scope( typeid( PortHeadT< ElementType > ) );

					return std::auto_ptr< ElementType >( 0 );
				}

				if( caller->getSchedType() == base::Sched::PROCESS )
				{

					Memory::remember( caller );
					dynamic_cast< base::Process* >( caller )->sleep();

					// PortHeadT::alert doesn't forget any remembered processes by itself. We have
					// to delete them manually from the inner waiting list.
					Memory::forget( caller );

					// check for interruption of waiting period
					if( dynamic_cast< base::Process* >( caller )->isInterrupted() )
					{
						return std::auto_ptr< ElementType >( 0 );
					}
				}
				else if( caller->getSchedType() == base::Sched::EVENT )
				{
					// error: cannot send an event to sleep
					error << log( "PortHeadT::get(): waiting mode not allowed for events" )
							.scope( typeid( PortHeadT< ElementType > ) );

					return std::auto_ptr< ElementType >( 0 );
				}
				break;

			case ERROR_MODE:
				error << log( "PortHeadT::get(): called on empty port" )
						.scope( typeid( PortHeadT< ElementType > ) );

				return std::auto_ptr< ElementType >( 0 );

			case ZERO_MODE:
				return std::auto_ptr< ElementType >( 0 );
			}
		}


		// the Port was filled to the limit
		bool wasFull = port_->isFull();

		// remove first element from Port
		std::auto_ptr< ElementType > first( port_->get() );

		// alert waiting sender if the port was full and a tail is set
		if( wasFull && port_->hasTail() )
		{
			TailPtr tail = getTail();
			if( tail->waiting() )
			{
				tail->alert();
			}
		}
		return first;
	}

	/**
		\brief Port read access based on criterion
		\return Pointer holding the first port element conforming to the given criterion, or an empty pointer

		This function is used to read information from a port. It also
		responds according to the object's PortMode. In @c WAITING_MODE this
		leads to the suspension of the calling Process. If the caller
		is interrupted before receiving an element, it is reactivated
		and the function returns an empty pointer. In @c ZERO_MODE a
		miss only returns a null pointer, while in @c ERROR_MODE an error
		will be sent additionally.

		\note The returned object is always dynamically allocated, see PortHeadT< ElementType >::get() .
    \since 3.0
	*/
  std::auto_ptr< ElementType > get( ElementCondition sel )
  {
    ODEMX_TRACE << log( "get" ).detail( "partner", getPartner() )
      .scope( typeid( PortHeadT< ElementType > ) );

    ODEMX_OBS_T( ObserverType, Get( this ) );

    bool foundSuitableElement = true; // if no sel function is given, every element in the port is fine
    typename synchronization::PortHeadT< ElementType >::StorageType::iterator element; // remember the element we'll find

    // only Processes can check the elements in the port.
    if ( sel && this->getSimulation() . getCurrentSched ()->getSchedType () == base::Sched::PROCESS ) {
      foundSuitableElement = false;
      
      // check every element in the port if it confirms to the criterion sel
      typename synchronization::PortHeadT< ElementType >::StorageType::iterator it;

      base::Process *current_process = static_cast<base::Process*> (this->getSimulation ().getCurrentSched ());

      for (it = this->port_->begin (); ( it != this->port_->end () && !foundSuitableElement ); it++) 
      {
        if ( ( foundSuitableElement = (current_process->*sel)(*it) ) ) 
          element = it;
      }
    }

    while( this->port_->isEmpty() || !foundSuitableElement )
    {
      base::Sched* caller = this->getSimulation().getCurrentSched();

      switch( this->mode_ )
      {
        case WAITING_MODE:
          if( caller == 0 )
          {
            error << log( "PortHeadT::get( ElementCondition ): port in waiting mode called from environment" )
                                             .scope( typeid( PortHeadT< ElementType > ) );

            return std::auto_ptr< ElementType >( 0 );
          }

          if( caller->getSchedType() == base::Sched::PROCESS )
          {
            // unlike Memory::get(), Memory::get ( ElementCondition ) might try to add a
            // Process more than once to the inner waiting list. We have to avoid that.
            if (!Memory::processIsWaiting (*(static_cast< base::Process* >( caller )))) 
                Memory::remember( caller );

            dynamic_cast< base::Process* >( caller )->sleep();

            // check for interruption of waiting period
            if( dynamic_cast< base::Process* >( caller )->isInterrupted() )
            {
              Memory::forget( caller );
              return std::auto_ptr< ElementType >( 0 );
            } 

            // activation happend via Memory::alert ()

            // if no selection function exists, then we handle this process as if it
            // called get ()
            if (!sel) {
              element = this->port_->inspectLastElement (); 
              foundSuitableElement = true;
            }
            else {
              base::Process *current_process = static_cast<base::Process*> (this->getSimulation ().getCurrentSched ());

              // Is the new element confirming to the criterion sel? If not, wake up other
              // sleeping processes.
              if (!(current_process->*sel)(*(this->port_->inspectLastElement ()))) {
                const base::SchedList& waitingList = this->getWaiting ();
                base::SchedList::const_iterator found = std::find( waitingList.begin(), waitingList.end(), current_process);

                if (found != waitingList.end ()) {
                  ++found;
                  if (found != waitingList.end ()) {
                    /* We found another waiting process */

                    // we know that this cast is ok, because only processes are allowed to wait
                    base::Process *next_process = static_cast<base::Process*>(*found); 
                    next_process->alertProcess (this); 
                  }
                } else {
                  error << log ("PortHeadT: get ( ElementCondition ): Process wasn't in the waiting list")
                                                                      .scope ( typeid ( PortHeadT< ElementType > ) );
                }
              } else {
                foundSuitableElement = true;
                element = this->port_->inspectLastElement ();
                Memory::forget( caller );
              }
            }
          }
          else if( caller->getSchedType() == base::Sched::EVENT )
          {
            // error: cannot send an event to sleep
            error << log( "PortHeadT::get( ElementCondition ): waiting mode not allowed for events" )
                                             .scope( typeid( PortHeadT< ElementType > ) );

            return std::auto_ptr< ElementType >( 0 );
          }
          break;

        case ERROR_MODE:
          error << log( "PortHeadT::get( ElementCondition ): called on empty port" )
                                           .scope( typeid( PortHeadT< ElementType > ) );

          return std::auto_ptr< ElementType >( 0 );

        case ZERO_MODE:
          return std::auto_ptr< ElementType >( 0 );
      }
    }

    // the Port was filled to the limit
    bool wasFull = this->port_->isFull();

    // remove last element from Port
    std::auto_ptr< ElementType > ret( this->port_->get (element) );

    // alert waiting sender if the port was full and a tail is set
    if( wasFull && this->port_->hasTail() )
    {
      typename synchronization::PortHeadT< ElementType >::TailPtr tail = this->getTail();
      if( tail->waiting() )
      {
        tail->alert();
      }
    }
    return ret;
  }


  /**
    \brief Port access, put element back at front
    \param element The element to put back into the buffer
    \return @c true if the port element was successfully added at the front

    This function is used to put an element back at the front
    of the internal port.
    \note This function doesn't work as intended with PortHeadT< ElementType >::get ( ElementCondition ),
    as we don't know where to put the given element in the queue.
    */
  bool unGet( const ElementType& element )
  {
    ODEMX_TRACE << log( "unGet" ).detail( "partner", getPartner() )
      .scope( typeid( PortHeadT< ElementType > ) );

		ODEMX_OBS_T( ObserverType, UnGet( this, element ) );

		if( port_->isFull() )
		{
			switch ( mode_ )
			{
			case WAITING_MODE:
				/* FALL TROUGH */
			case ERROR_MODE:
				error << log( "PortHeadT::unGet(): called on full port" )
						.scope( typeid( PortHeadT< ElementType > ) );
				/* FALL TROUGH */
			case ZERO_MODE:
				return false;
			}
		}
		// insert PortData at the front of the Port queue
		port_->unGet( element );
		return true;
	}

	/**
		\brief Get corresponding port tail
		\return Pointer to the corresponding tail of this head

		This function provides access to the tail of a port head.
		If it does not exist yet, it is created.
	*/
	TailPtr getTail()
	{
		TailPtr tail = port_->getTail();

		// check if the tail pointer is empty
		if( ! tail )
		{
			// create new Tail
			tail.reset( new PortTailT< ElementType >( getSimulation(),
					port_->getLabel(), mode_, port_->getCapacity() ) );

			// register new Tail with Head's Port
			port_->setTail( typename PortType::TailPtr( tail ) );

			// register Head's internal Port with the Tail
			tail->port_ = this->port_;
		}
		return tail;
	}

	/**
		\brief Get maximum capacity of the internal port
		\return Capacity of the buffer

		This function is used to determine the maximum number of
		elements that can be buffered by the internal port.

		\sa setMaxCapacity()
	*/
	SizeType getMaxCapacity()
	{
		return port_->getCapacity();
	}

	/**
		\brief Change maximum capacity of the port
		\param limit The new maximum capacity of the internal port

		This function allows to change the maximum number of elements the
		internal buffer can hold.

		\sa getMaxCapacity()
	*/
	void setMaxCapacity( SizeType limit )
	{
		ODEMX_TRACE << log( "change max capacity" )
				.valueChange( port_->getCapacity(), limit )
				.scope( typeid( PortHeadT< ElementType > ) );

		ODEMX_OBS_ATTR_T( ObserverType, MaxCapacity, port_->getCapacity(), limit );

		port_->setCapacity( limit );
	}

	/// Determine the port head's mode
	PortMode getMode() const
	{
		return mode_;
	}

	/// Change the port head's mode
	void setMode( PortMode newMode )
	{
		ODEMX_TRACE << log( "change mode" ).valueChange( toString( mode_ ), toString( newMode ) )
				.scope( typeid( PortHeadT< ElementType > ) );

		ODEMX_OBS_ATTR_T( ObserverType, Mode, mode_, newMode );

		mode_ = newMode;
	}

	/**
		\brief Get current number of port elements
		\return Current number of elements

		This method returns the current number of buffered elements.
	*/
	SizeType count() const
	{
		return port_->getElementCount();
	}

	/**
		\brief Check for availability of an element
		\return @c true if the port is not empty

		This function is used to determine whether an element is
		available from the internal port.
	*/
	virtual bool isAvailable()
	{
		return ! port_->isEmpty();
	}

	/**
		\brief Cut this port head from its tail
		\return New port head of the old internal port

		This function will create a	new internal port, which
		is attached to this head. A new tail for that port must then be
		requested via getTail(). Furthermore, a new port head will
		be attached to the old internal port. That new head will be
		returned.

		This provides the ability to insert a process into
		the middle of a port connection, for example as a filter.

		\sa splice()
	*/
	Ptr cut()
	{
		// change
		//      old_Port_head -- old_Port -- old_Port_tail
		// into
		//      old_Port_head -- new_Port (___get new tail___)
		//      new_Port_head -- old_Port -- old_Port_tail
		// where
		//      old_Port_head == this
		// return
		//      new_Port_head.

		ODEMX_TRACE << log( "cut" ).detail( "partner", getPartner() )
				.scope( typeid( PortHeadT< ElementType > ) );

		ODEMX_OBS_T( ObserverType, Cut( this ) );

		// remember old Port
		PortPtr oldPort = port_;

		// create a new Head and thereby a new Port
		Ptr newHead( new PortHeadT< ElementType >(
				getSimulation(),
				data::Label( "Cut " ) + oldPort->getLabel(),
				mode_, oldPort->getCapacity() ) );

		// get the new Port
		PortPtr newPort = newHead->port_;

		// put the new head on the old Port
		oldPort->setHead( typename PortType::HeadPtr( newHead ) );
		newHead->port_ = oldPort;

		// put the old head (== this) on the new Port
		newPort->setHead( typename PortType::HeadPtr( this->shared_from_this() ) );
		port_ = newPort;

		return newHead;
	}

	/**
		\brief Splice two separate ports together
		\param oldTail The tail of the port to which this head's buffer will be added

		This function is the reverse operation of cut(). It is used to join
		two ports together. This head must be upstream of, i.e. before,
		oldTail's port, which this buffer will be joined with.
		The contents of this Port is added to oldTail's Port. If
		the new head's buffer becomes non-empty, it is alerted.

		\warning Some objects become invalid with this operation!
		This head, oldTail, and oldTail's Port should not be accessed anymore
		after calling this method.

		\note This method does not check the maximum Port capacity before
		splicing lists.

		\sa cut()
	*/
	void splice( TailPtr oldTail )
	{
		// this PortHead is supposed to be upstream to the portTail oldTail:
		// >> headsTail -- headsPort -- thisHead >> oldTail -- tailsPort -- tailsHead >>
		// what remmains afterwards is this:
		// >> headstail -- tailsPort -- tailsHead >>
		//   add the contents of headsPort to tailsPort
		//   destroy thisHead and oldTail
		//   alert the spliced PortHead if a significant state change happened

		ODEMX_TRACE << log( "splice" ).detail( "partner", getPartner() )
				.scope( typeid( PortHeadT< ElementType > ) );

		ODEMX_OBS_T( ObserverType, Splice( this, oldTail ) );

		// remember parts
		Ptr tailsHead = oldTail->getHead(); // may be 0
		TailPtr headsTail = port_->getTail(); // may be 0
		PortPtr tailsPort = oldTail->port_;
		PortPtr headsPort = port_;

		// and their element counts
		int tailsCount = tailsPort->getElementCount();
		int headsCount = headsPort->getElementCount();

		// append head's buffer to tail's buffer via std::list::splice()
		if( headsCount > 0 )
		{
			tailsPort->spliceBuffer( headsPort->getBuffer(), true );
		}

		// patch the head's tail onto tail's Port, which has it's own head,
		// this PortHead becomes superfluous, requires pointers in both directions!
		if( headsTail )
		{
			tailsPort->setTail( headsTail );
			headsTail->port_ = tailsPort;
		}

		// alert the new head if its Port becomes non-empty
		if( (tailsCount == 0) && (headsCount > 0) && (tailsHead) )
		{
			if( tailsHead->waiting() )
			{
				tailsHead->alert();
			}
		}
	}

	/**
		\brief Splice internal lists of two separate ports together
		\param tail The tail of the port whose buffer will be added to this
		\param append Determines whether to append or prepend tail's contents

		This function is used to join two ports' buffer contents together
		in the same way as std::list::splice(). The contents of the \p
		tail PortT will be moved to \p this port, where parameter \p append
		can	be used to specify whether the data will be appended or prepended
		to this port. Default is append.

		\note This function does not check the maximum Port capacity before
		splicing lists.
	*/
	void cppSplice( TailPtr tail, bool append = true )
	{
		ODEMX_TRACE << log( "cpp splice" ).detail( "partner", getPartner() )
				.scope( typeid( PortHeadT< ElementType > ) );

		ODEMX_OBS_T( ObserverType, CppSplice( this, tail, append ) );

		if( append )
		{
			port_->spliceBuffer( tail->port_->getBuffer(), true );
		}
		else
		{
			port_->spliceBuffer( tail->port_->getBuffer(), false );
		}
	}

	/// Get the storage buffer, allows write access for splicing
	const StorageType& getBuffer() const
	{
		return const_cast< const StorageType& >( port_->getBuffer() );
	}
private:

	friend class PortTailT< ElementType >;

	/**
		\brief Alert the first stored process

		This function is designed to wake up suspended processes,
		i.e. reschedule them at simulation time \c now when they
		are waiting for an empty port to receive an element. Only
		the first remembered process is alerted by the %PortHead.
	*/
	virtual void alert()
	{
		// simpler implementation than in Memory because get() does
		// some checking already and only the first waiting Process is alerted

		if( ! Memory::waiting() )
		{
			// warning: no object to wake
			if ( mode_ != ZERO_MODE )
			{
				warning << log( "PortHeadT::alert(): no process saved" )
						.scope( typeid( PortHeadT< ElementType > ) );
			}
			return;
		}

		// get() allows WAITING_MODE only for Processes, so cast is okay here
		base::Process* rememberedObject =
			static_cast< base::Process* >( Memory::getWaiting().front() );

		ODEMX_TRACE << log( "alert" ).detail( "partner", rememberedObject->getLabel() )
				.scope( typeid( PortHeadT< ElementType > ) );

		ODEMX_OBS_T( ObserverType, Alert( this, rememberedObject ) );

		// wake up the process
		rememberedObject->alertProcess( this );
	}

	/// Get the label of the currently active Sched object, or the simulation
	const data::Label& getPartner()
	{
		// return the current process or event, if one is active
		if( getSimulation().getCurrentSched() != 0 )
		{
			return getSimulation().getCurrentSched()->getLabel();
		}
		else
		{
			return getSimulation().getLabel();
		}
	}

private:
	/// Type of the internal port object
	typedef PortT< ElementType > PortType;
	/// Pointer type to manage the internal PortT object
	typedef std::tr1::shared_ptr< PortType > PortPtr;

	PortPtr port_; ///< Pointer to the internal port object
	PortMode mode_; ///< Mode for read requests

protected:
	/// Construction private, use @c create instead
	PortHeadT( base::Simulation& sim, const data::Label& label,
			PortMode mode = WAITING_MODE, SizeType limit = 10000,
			ObserverType* obs = 0 )
	:	Memory( sim, label + " (head)", IMemory::PORTHEAD )
	,	data::Observable< ObserverType >( obs )
	,	mode_( mode )
	{
		ODEMX_TRACE << log( "create" ).scope( typeid( PortHeadT< ElementType > ) );
		ODEMX_OBS_T( ObserverType, Create( this ) );

		this->port_.reset( new PortType( getSimulation(), label, limit ) );
	}
	/// Connect the port head to the internal buffer, needed after construction
	void initPort()
	{
		this->port_->setHead( this->shared_from_this() );
	}
private:
	/// No copying
	PortHeadT( const PortHeadT& );
	/// No assignment
	PortHeadT& operator=( const PortHeadT& );
};

/**
 * @interface PortHeadTObserver
 * @ingroup synch
 * @author Ronald Kluth
 * @brief Observer for PortHeadT-specific calls.
 * @see PortHeadT
 * @since 2.0
 */
template < typename ElementT >
class PortHeadTObserver: public MemoryObserver {
public:
	typedef ElementT ElementType;
	typedef typename PortT< ElementType >::SizeType SizeType;

	virtual ~PortHeadTObserver() {}

	virtual void onCreate( PortHeadT< ElementType >* sender ) {} ///< Construction
	virtual void onDestroy( PortHeadT< ElementType >* sender ) {} ///< Destruction

	virtual void onGet( PortHeadT< ElementType >* sender ) {}
	virtual void onUnGet( PortHeadT< ElementType >* sender, const ElementType& newElement ) {}
	virtual void onCut( PortHeadT< ElementType >* sender ) {}
	virtual void onSplice( PortHeadT< ElementType >* sender,
			typename PortHeadT< ElementType >::TailPtr oldTail ) {}
	virtual void onCppSplice( PortHeadT< ElementType >* sender,
			typename PortHeadT< ElementType >::TailPtr oldTail, bool append ) {}
	virtual void onAlert( PortHeadT< ElementType >* sender, base::Sched* alerted ) {}

	/// PortHead mode change
	virtual void onChangeMode( PortHeadT< ElementType >* sender,
			PortMode oldMode, PortMode newMode ) {}
	/// PortHead capacity change
	virtual void onChangeMaxCapacity( PortHeadT< ElementType >* sender,
			SizeType oldLimit, SizeType newLimit ) {}
};

} } // namespace odemx::synchronization

#endif /* ODEMX_SYNC_PORTHEAD_IMPL_INCLUDED */
