ptr()->finalizeTo(p ? p.ptr() : last().ptr());
}
+template <class Annotation>
+prefix_ Annotation & senf::Packet::annotation()
+{
+ return ptr()->annotation<Annotation>();
+}
+
///////////////////////////////////////////////////////////////////////////
// senf::ConcretePacket<PacketType>
///@}
+ ///\name Annotations
+ ///@{
+
+ template <class Annotation>
+ Annotation & annotation();
+
+ ///@}
+
///\name Other methods
///@{
when using a packet in a boolean context. */
void finalizeThis(); ///< Update calculated fields
- /**< This call will update all calculated fields of the
- packet. This includes checksums, payload size fields or
- other fields, which can be set from other information
- in the packet. Each concrete packet type should
- document, which fields are set by finalize().
+ /**< The finalize() fammily of members will update
+ calculated packet fields: checksums, size fields and so
+ on. This includes any field, which can be set from
+ other information in the packet. Each concrete packet
+ type should document, which fields are set by
+ finalize().
finalizeThis() will \e only process the current
header. Even if only changing fields in this protocol,
template <class Other>
void finalizeTo(); ///< Update calculated fields
- /**< This call will update all calculated fields of the
- packet. This includes checksums, payload size fields or
- other fields, which can be set from other information
- in the packet. Each concrete packet type should
- document, which fields are set by finalize().
+ /**< The finalize() fammily of members will update
+ calculated packet fields: checksums, size fields and so
+ on. This includes any field, which can be set from
+ other information in the packet. Each concrete packet
+ type should document, which fields are set by
+ finalize().
finalizeTo() will automatically process all
- packets/headers/interpreters from the first occurrence
- of packet type \a Other backwards up to \c this. */
+ packets/headers/interpreters from the \e first
+ occurrence of packet type \a Other (beginning at \c
+ this packet searching forward towards deeper nested
+ packets) backwards up to \c this.
+
+ This call is equivalent to
+ \code
+ p.finalizeTo(p.next<Other>())
+ \endcode */
void finalizeTo(Packet other); ///< Update calculated fields
- /**< This call will update all calculated fields of the
- packet. This includes checksums, payload size fields or
- other fields, which can be set from other information
- in the packet. Each concrete packet type should
- document, which fields are set by finalize().
+ /**< The finalize() fammily of members will update
+ calculated packet fields: checksums, size fields and so
+ on. This includes any field, which can be set from
+ other information in the packet. Each concrete packet
+ type should document, which fields are set by
+ finalize().
- finalizeAll(other) will automatically process all
- packets/headers/interpreters from \a other backwards up
- to \c this. */
+ finalizeTo(other) will automatically process all
+ packets/headers/interpreters beginning at \a other
+ backwards towards outer packets up to \c this. */
void finalizeAll(); ///< Update calculated fields
- /**< This call will update all calculated fields of the
- packet. This includes checksums, payload size fields or
- other fields, which can be set from other information
- in the packet. Each concrete packet type should
- document, which fields are set by finalize().
+ /**< The finalize() fammily of members will update
+ calculated packet fields: checksums, size fields and so
+ on. This includes any field, which can be set from
+ other information in the packet. Each concrete packet
+ type should document, which fields are set by
+ finalize().
finalizeAll() will automatically process all
packets/headers/interpreters from the end of the chain
- backwards up to \c this. */
+ (the most inner packet) backwards up to \c this.
+
+ This call is equivalent to
+ \code
+ p.finalizeTo(p.last())
+ \endcode
+
+ Beware, that finalizeAll() will \e not finalize any
+ headers before \c this, it will \e only process inner
+ headers. */
void dump(std::ostream & os) const; ///< Write out a printable packet representation
/**< This method is provided mostly to help debugging packet
senf::PacketRegistry<RegTag>::RegistrationProxy<BarPacket> registerBar(2u);
}
+ struct IntAnnotation {
+ int value;
+ };
+
+ struct ComplexAnnotation {
+ std::string s;
+ int i;
+ };
+
}
BOOST_AUTO_UNIT_TEST(packet)
senf::Packet packet (FooPacket::create());
BarPacket::createAfter(packet);
+ SENF_CHECK_NO_THROW( packet.annotation<IntAnnotation>().value = 0xDEADBEEF );
+ ComplexAnnotation & ca (packet.annotation<ComplexAnnotation>());
+ ca.s = "dead beef";
+ ca.i = 0x12345678;
BOOST_REQUIRE( packet );
BOOST_CHECK( packet.next() );
BOOST_CHECK( ! packet.next().next(senf::nothrow) );
#define prefix_
///////////////////////////////cc.p////////////////////////////////////////
+unsigned senf::detail::AnnotationIndexerBase::maxAnnotations (0);
+
///////////////////////////////////////////////////////////////////////////
// senf::detail::PacketImpl
+prefix_ senf::detail::PacketImpl::~PacketImpl()
+{
+ // We increment refcount_ to ensure, release() won't call delete again
+ ++refcount_;
+ eraseInterpreters(interpreters_.begin(), interpreters_.end());
+ Annotations::const_iterator i (annotations_.begin());
+ Annotations::const_iterator const i_end (annotations_.end());
+ std::vector<bool>::iterator small (AnnotationIndexerBase::small().begin());
+ for (; i != i_end; ++i, ++small)
+ if (! *small && *i)
+ delete *i;
+}
+
// This function has a problem being inlined. Somehow, often when calling this, the size of the
// resulting inlined code would be huge. Need to further debug this.
#define prefix_ inline
///////////////////////////////cci.p///////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////
+// senf::detail::AnnotationIndexerBase
+
+prefix_ std::vector<bool> & senf::detail::AnnotationIndexerBase::small()
+{
+ static std::vector<bool> smalls;
+ return smalls;
+}
+
+///////////////////////////////////////////////////////////////////////////
+// senf::detail::AnnotationP
+
+prefix_ senf::detail::AnnotationP::~AnnotationP()
+{}
+
// Memory management:
//
// * The PacketImpl destructor will *explicitly* clean-up the interpreters_ list by removing
// senf::detail::PacketImpl
prefix_ senf::detail::PacketImpl::PacketImpl()
- : refcount_(0)
+ : refcount_(0), annotations_(AnnotationIndexerBase::maxAnnotations, 0)
{}
prefix_ senf::detail::PacketImpl::PacketImpl(size_type size, byte initValue)
- : refcount_(0), data_(size,initValue)
+ : refcount_(0), data_(size,initValue), annotations_(AnnotationIndexerBase::maxAnnotations, 0)
{}
-prefix_ senf::detail::PacketImpl::~PacketImpl()
-{
- // We increment refcount_ to ensure, release() won't call delete again
- ++refcount_;
- eraseInterpreters(interpreters_.begin(), interpreters_.end());
-}
-
// rerference/memory management
prefix_ void senf::detail::PacketImpl::add_ref(refcount_t n)
///////////////////////////////cti.p///////////////////////////////////////
///////////////////////////////////////////////////////////////////////////
+// senf::detail::AnnotationIndexer<Annotation>
+
+template <class Annotation>
+prefix_ senf::detail::AnnotationIndexer<Annotation>::AnnotationIndexer()
+ : index_ (maxAnnotations++)
+{
+ small().push_back(Small);
+}
+
+template <class Annotation>
+prefix_ unsigned senf::detail::AnnotationIndexer<Annotation>::index()
+{
+ return AnnotationIndexer::instance().index_;
+}
+
+///////////////////////////////////////////////////////////////////////////
+// senf::detail::GetAnnotation<Annotation,Small>
+
+template <class Annotation, bool Small>
+prefix_ Annotation & senf::detail::GetAnnotation<Annotation,Small>::get(AnnotationP * & p)
+{
+ if (!p)
+ p = new TAnnotationP<Annotation>();
+ return static_cast< TAnnotationP<Annotation>* >(p)->annotation;
+}
+
+/*
+template <class Annotation>
+prefix_ Annotation & senf::detail::GetAnnotation<Annotation, true>::get(AnnotationP * & p)
+{
+ return * reinterpret_cast<Annotation*>(p);
+}
+*/
+
+///////////////////////////////////////////////////////////////////////////
// senf::detail::PacketImpl
// Data container
template <class InputIterator>
prefix_ senf::detail::PacketImpl::PacketImpl(InputIterator first, InputIterator last)
- : refcount_(0), data_(first,last)
+ : refcount_(0), data_(first,last), annotations_(AnnotationIndexerBase::maxAnnotations, 0)
{}
+// Annotations
+
+template <class Annotation>
+prefix_ Annotation & senf::detail::PacketImpl::annotation()
+{
+ return GetAnnotation<Annotation>::get(
+ annotations_[AnnotationIndexer<Annotation>::index()]);
+}
+
///////////////////////////////cti.e///////////////////////////////////////
#undef prefix_
// Custom includes
#include <memory>
+#include <vector>
#include <boost/utility.hpp>
#include "../Utils/pool_alloc_mixin.hh"
#include "PacketTypes.hh"
+#include "../Utils/singleton.hh"
//#include "PacketImpl.mpp"
///////////////////////////////hh.p////////////////////////////////////////
namespace senf {
namespace detail {
+ struct AnnotationIndexerBase
+ {
+ static unsigned maxAnnotations;
+ static std::vector<bool> & small();
+ };
+
+ template <class Annotation>
+ struct AnnotationIndexer
+ : public senf::singleton< AnnotationIndexer<Annotation> >,
+ public AnnotationIndexerBase
+ {
+ AnnotationIndexer();
+ unsigned index_;
+ static unsigned index();
+ static bool const Small = (sizeof(Annotation) <= sizeof(void*));
+ };
+
+ struct AnnotationP
+ {
+ virtual ~AnnotationP();
+ };
+
+ template <class Annotation>
+ struct TAnnotationP
+ : public AnnotationP
+ {
+ Annotation annotation;
+ };
+
+ template <class Annotation, bool Small = AnnotationIndexer<Annotation>::Small>
+ struct GetAnnotation
+ {
+ static Annotation & get(AnnotationP * & p);
+ };
+
+/*
+ template <class Annotation>
+ struct GetAnnotation<Annotation, true>
+ {
+ static Annotation & get(AnnotationP * & p);
+ };
+*/
+
/** \brief Internal: Packet data storage
\internal
void erase(PacketData * self, iterator first, iterator last);
void clear(PacketData * self);
+ // Annotations
+ template <class Annotation>
+ Annotation & annotation();
+
/** \brief Internal: Keep PacketImpl instance alive
\internal
refcount_t refcount_;
raw_container data_;
interpreter_list interpreters_;
+
+ typedef std::vector<AnnotationP*> Annotations;
+ Annotations annotations_;
void eraseInterpreters(interpreter_list::iterator b, interpreter_list::iterator e);
void updateIterators(PacketData * self, difference_type pos, difference_type n);
static_cast< PacketInterpreter<Type>* >(this));
}
+template <class Annotation>
+prefix_ Annotation & senf::PacketInterpreterBase::annotation()
+{
+ return impl().annotation<Annotation>();
+}
+
///////////////////////////////////////////////////////////////////////////
// senf::PacketInterpreter<PacketType>
///@}
+ ///\name Annotations
+ ///@{
+
+ template <class Annotation>
+ Annotation & annotation();
+
+ ///@}
+
///\name Access to the abstract interface
///@{
friend class detail::FileDispatcher;
};
+ /** \brief Get file descriptor from handle object
+
+ This function will query the \a handle for it's file descriptor. The real implementation
+ must be provided by a freestanding function \c retrieve_filehandle(Handle const & h) within
+ the namespace of \a Handle.
+ */
template <class Handle>
int get_descriptor(Handle const & handle);
}}
\autotoc
\section scheduler_scheduler The Scheduler
+ \seechapter \ref senf::scheduler
- The Scheduler is based on the RAII principle: Every event is represented by a class
- instance. The event is registered in the constructor and removed by the destructor of that
- instance. This implementation automatically links the lifetime of an event with the lifetime of
- the object resposible for it's creation.
-
- The Scheduler supports the following types of events::
+ The %scheduler provides a single threaded event dispatch architecture with reliable task
+ queueing using FIFO scheduling. The %scheduler provides event handling for
\li File descriptors
\li Timers
\li UNIX signals
- \see \ref senf::scheduler
-
-
\section scheduler_clockservice The ClockService
+ \seechapter senf::ClockService
To support precise event timing, the senf::ClockService class implements a reliable monotonous
time source. It is based on the high precision POSIX clock and adds support for reliable
conversion between an abstract clock type and absolute date/time
- \see senf::ClockService
-
-
\section scheduler_helpers Miscellaneous helpers
To ease the use of the Scheduler there are some additional helpers managing callbacks and
is met (e.g. number of chars read or a specific character sequence is found in the input).
\li senf::WriteHelper writes data to an arbitrary file descriptor until all provided data has
been written.
-
\section scheduler_i Implementation
+ \seechapter \ref scheduler_implementation
senf::Scheduler is only a wrapper around the real implementation. The real implementation is now
based on a modular dispatcher architecture
- \see \ref scheduler_implementation
*/
/** \page scheduler_implementation The Scheduler Implementation
new (fld) detail::FileDispatcher();
}
+prefix_ bool senf::scheduler::empty()
+{
+ return detail::FdDispatcher::instance().empty()
+ && detail::TimerDispatcher::instance().empty()
+ && detail::FileDispatcher::instance().empty()
+ && detail::SignalDispatcher::instance().empty();
+}
+
///////////////////////////////////////////////////////////////////////////
// senf::schedulerLogTimeSource
The %scheduler API is comprised of two parts:
- \li Specific event classes, one for each type of event.
- \li Some generic functions implemented in the \ref senf::scheduler namespace.
+ \li Specific \ref sched_objects, one for each type of event.
+ \li Some <a href="#autotoc-7.">generic functions</a> implemented in the \ref senf::scheduler
+ namespace.
Events are registered via the respective event class. The (global) functions are used to enter
the application main-loop or query for global information.
\section sched_objects Event classes
- Every event registration is represented by a class instance of an event specific class:
+ The Scheduler is based on the RAII principle: Every event is represented by a class
+ instance. The event is registered in the constructor and removed by the destructor of that
+ instance. This implementation automatically links the lifetime of an event with the lifetime of
+ the object resposible for it's creation.
+
+ Every event registration is represented by an instance of an event specific class:
\li senf::scheduler::FdEvent for file descriptor events
\li senf::scheduler::TimerEvent for single-shot deadline timer events
The handler is identified by an arbitrary, user specified name. This name is used in error
messages to identify the failing handler.
+
+ \section sched_exec Executing the Scheduler
+
+ To enter the scheduler main-loop, call
+
+ \code
+ senf::scheduler::process();
+ \endcode
+
+ This call will only return in two cases:
+
+ \li When a handler calls senf::scheduler::terminate()
+ \li When there is no active file descriptor or timer event.
+
+ Additional <a href="#autotoc-7.">generic functions</a> provide information and %scheduler
+ parameters.
+
+ \section sched_container Event objects and container classes
+
+ As the event objects are \e not copyable, they cannot be placed into ordinary
+ containers. However, it is quite simple to use pointer containers to hold event instances:
+
+ \code
+ #include <boost/ptr_container/ptr_map.hpp>
+ #include <boost/bind.hpp>
+
+ class Foo
+ {
+ public:
+ void add(int fd)
+ {
+ fdEvents.insert(
+ fd,
+ new senf::scheduler::FdEvent("foo", boost::bind(&callback, this, fd, _1), fd,
+ senf::scheduler::FdEvent::EV_READ) );
+ }
+
+ void callback(int fd, int events)
+ {
+ FdEvent & event (fdEvents_[fd]);
+
+ // ...
+
+ if (complete)
+ fdEvents_.remove(fd)
+ }
+
+ private:
+ boost::ptr_map<int, FdEvent> fdEvents_;
+ };
+ \endcode
+
+ The pointer container API is (almost) completely identical to the corresponding standard library
+ container API. The only difference is, that all elements added to the container \e must be
+ created via \c new and that the pointer containers themselves are \e not copyable (ok, they are,
+ if the elements are cloneable ...). See <a
+ href="http://www.boost.org/doc/libs/1_36_0/libs/ptr_container/doc/ptr_container.html">Boost.PointerContainer</a>
+ for the pointer container library reference.
+
\todo Fix the file support to use threads (?) fork (?) and a pipe so it works reliably even
over e.g. NFS.
*/
*/
void terminate();
- /** \brief Return date/time of last event
+ /** \brief Return timestamp of last event
This is the timestamp, the last event has been signaled. This is the real time at which the
event is delivered \e not the time it should have been delivered (in the case of timers).
*/
void restart();
+ /** \brief Return \c true, if any event is registered, \c false otherwise. */
+ bool empty();
+
/** \brief %scheduler specific time source for Utils/Logger framework
This time source may be used to provide timing information for log messages within the
::sigemptyset(&cldsig);
LIBC_CALL( ::sigaddset, (&cldsig, SIGCHLD) );
LIBC_CALL( ::sigprocmask, (SIG_BLOCK, &cldsig, &oldsig) );
+
+ if (! senf::scheduler::empty() )
+ std::cerr <<
+ "\n"
+ "*** WARNING ***\n"
+ "Scheduler not empty before fork(). THIS MUST NOT HAPPEN.\n"
+ "The scheduler will be reinitialized by the fork() and lose all registrations.\n"
+ "*** WARNING ***\n"
+ "\n";
LIBC_CALL_RV( pid, ::fork, () );
"idea=\xrefitem idea \"Idea\" \"Ideas\"" \
"implementation=\par \"Implementation note:\"" \
"doc=\xrefitem doc \"Documentation request\" \"Documentation Requests\"" \
- "autotoc=\htmlonly <div id=\"autotoc\"></div> \endhtmlonly"
+ "autotoc=\htmlonly <div id=\"autotoc\"></div> \endhtmlonly" \
+ "seechapter=<b>\htmlonly → \endhtmlonly see </b>"
# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C
# sources only. Doxygen will then generate output that is more tailored for C.
CIDR
classsenf
ClientSocketHandle
+cloneable
CloneSource
cmd
cmdadd
ConnectedRawV
ConnectedUDPv
const
+copyable
CPPDEFINES
CPPPATH
createAfter
ThresholdQueueing
ThrottleBarrier
tigris
+timestamp
todo
tokenizes
TokensRange