diff --git a/Packets/Packet.cti b/Packets/Packet.cti index fac31c02bc8ae28f1d29004da6dbdbb03e264dc4..cfd891930d42fed26f51a2bd715e62103e499965 100644 --- a/Packets/Packet.cti +++ b/Packets/Packet.cti @@ -135,6 +135,12 @@ prefix_ void senf::Packet::finalizeTo() ptr()->finalizeTo(p ? p.ptr() : last().ptr()); } +template <class Annotation> +prefix_ Annotation & senf::Packet::annotation() +{ + return ptr()->annotation<Annotation>(); +} + /////////////////////////////////////////////////////////////////////////// // senf::ConcretePacket<PacketType> diff --git a/Packets/Packet.hh b/Packets/Packet.hh index af45ff2b5c53b5a6ba394892a50893bdafdc1246..58264f8ffae1b16533731a0a457f0a4962fff431 100644 --- a/Packets/Packet.hh +++ b/Packets/Packet.hh @@ -314,6 +314,14 @@ namespace senf { ///@} + ///\name Annotations + ///@{ + + template <class Annotation> + Annotation & annotation(); + + ///@} + ///\name Other methods ///@{ @@ -331,11 +339,12 @@ namespace senf { 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, @@ -345,37 +354,56 @@ namespace senf { 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 diff --git a/Packets/Packet.test.cc b/Packets/Packet.test.cc index 70b3e1d5e5e9bf4fc1fcaffaf5773de3a33ae637..b88993e4bc67132b3144a8dc9f4704a974a3fc95 100644 --- a/Packets/Packet.test.cc +++ b/Packets/Packet.test.cc @@ -109,6 +109,15 @@ namespace { senf::PacketRegistry<RegTag>::RegistrationProxy<BarPacket> registerBar(2u); } + struct IntAnnotation { + int value; + }; + + struct ComplexAnnotation { + std::string s; + int i; + }; + } BOOST_AUTO_UNIT_TEST(packet) @@ -116,6 +125,10 @@ 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) ); diff --git a/Packets/PacketImpl.cc b/Packets/PacketImpl.cc index 2bd41bea6db88ddc02216d5ac646f83d693ff203..5b5e12fe0983023b5056328763388d4a0956181d 100644 --- a/Packets/PacketImpl.cc +++ b/Packets/PacketImpl.cc @@ -33,9 +33,24 @@ #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. diff --git a/Packets/PacketImpl.cci b/Packets/PacketImpl.cci index 40489bf3888b554f49b2706a1dbce4f6945b9f33..9b8a27723d6a39ba162b584f53ba8e88d82d21cf 100644 --- a/Packets/PacketImpl.cci +++ b/Packets/PacketImpl.cci @@ -30,6 +30,21 @@ #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 @@ -57,20 +72,13 @@ // 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) diff --git a/Packets/PacketImpl.cti b/Packets/PacketImpl.cti index a00a8e3aea2d2e0ce925060592f0df4ac74e0f56..21956f18c3c105298179e0d5abdebf987aa188ce 100644 --- a/Packets/PacketImpl.cti +++ b/Packets/PacketImpl.cti @@ -30,6 +30,41 @@ #define prefix_ inline ///////////////////////////////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 @@ -46,9 +81,18 @@ prefix_ void senf::detail::PacketImpl::insert(PacketData * self, iterator pos, F 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_ diff --git a/Packets/PacketImpl.hh b/Packets/PacketImpl.hh index 871b9ec5948f9b377595a8e54be21b78a523f215..1d1373f0cfe148d8484a069a647e782330b5514e 100644 --- a/Packets/PacketImpl.hh +++ b/Packets/PacketImpl.hh @@ -28,9 +28,11 @@ // 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//////////////////////////////////////// @@ -38,6 +40,49 @@ 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 @@ -103,6 +148,10 @@ namespace detail { 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 @@ -121,6 +170,9 @@ namespace detail { 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); diff --git a/Packets/PacketInterpreter.cti b/Packets/PacketInterpreter.cti index 672436ffe1df66392b1c0cc0c0ddc67e4b99131b..f5420155eeab42a28de3d18bb1fd3228ce0e020c 100644 --- a/Packets/PacketInterpreter.cti +++ b/Packets/PacketInterpreter.cti @@ -50,6 +50,12 @@ prefix_ typename senf::PacketInterpreter<Type>::ptr senf::PacketInterpreterBase: static_cast< PacketInterpreter<Type>* >(this)); } +template <class Annotation> +prefix_ Annotation & senf::PacketInterpreterBase::annotation() +{ + return impl().annotation<Annotation>(); +} + /////////////////////////////////////////////////////////////////////////// // senf::PacketInterpreter<PacketType> diff --git a/Packets/PacketInterpreter.hh b/Packets/PacketInterpreter.hh index 24f29c223beb90e838269e19528b8f0f023cc1c3..790c600cd24c980f18db3138a686a25f2ab23621 100644 --- a/Packets/PacketInterpreter.hh +++ b/Packets/PacketInterpreter.hh @@ -162,6 +162,14 @@ namespace senf { ///@} + ///\name Annotations + ///@{ + + template <class Annotation> + Annotation & annotation(); + + ///@} + ///\name Access to the abstract interface ///@{ diff --git a/Scheduler/FdEvent.hh b/Scheduler/FdEvent.hh index 90ddc7a4e6270770765032ae8479ddaafbe8ac42..d574351eeb97f495bba01e95c22f1d14a59b2e4b 100644 --- a/Scheduler/FdEvent.hh +++ b/Scheduler/FdEvent.hh @@ -171,6 +171,12 @@ namespace scheduler { 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); }} diff --git a/Scheduler/Mainpage.dox b/Scheduler/Mainpage.dox index 054af2e2edb85c1d162cc081d8b5eacf1ba9d042..e67784aa0b1ca48fedf73180dc4f8a875dd6bb12 100644 --- a/Scheduler/Mainpage.dox +++ b/Scheduler/Mainpage.dox @@ -28,30 +28,22 @@ \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 @@ -61,14 +53,13 @@ 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 diff --git a/Scheduler/Scheduler.cc b/Scheduler/Scheduler.cc index 1d7b28bfaea5beea4496ed3b0f032a96eb37a4d3..7ba69ef7c18acde6d84294f3a677236ecf7755d3 100644 --- a/Scheduler/Scheduler.cc +++ b/Scheduler/Scheduler.cc @@ -89,6 +89,14 @@ prefix_ void senf::scheduler::restart() 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 diff --git a/Scheduler/Scheduler.hh b/Scheduler/Scheduler.hh index 33294261cad395514f75bda69f532051b41cd153..af07c48de994bbe8dbacc2e40e5dcc4d6ab04e95 100644 --- a/Scheduler/Scheduler.hh +++ b/Scheduler/Scheduler.hh @@ -42,8 +42,9 @@ namespace senf { 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. @@ -53,7 +54,12 @@ namespace senf { \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 @@ -128,6 +134,65 @@ namespace senf { 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. */ @@ -149,7 +214,7 @@ namespace scheduler { */ 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). @@ -173,6 +238,9 @@ namespace scheduler { */ 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 diff --git a/Utils/Daemon/Daemon.cc b/Utils/Daemon/Daemon.cc index 2b09cc03bb6c6194419b7c836f30a39e73c893fa..d6b8d917dbb5b410c578072317f029912077a4a2 100644 --- a/Utils/Daemon/Daemon.cc +++ b/Utils/Daemon/Daemon.cc @@ -363,6 +363,15 @@ prefix_ void senf::Daemon::fork() ::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, () ); diff --git a/doclib/Doxyfile.global b/doclib/Doxyfile.global index f589bbf18b6ce97cbf1fa0835a95dc7465d7e9ac..9603551497ae4ea24d168e460a2abb6260c29be3 100644 --- a/doclib/Doxyfile.global +++ b/doclib/Doxyfile.global @@ -190,7 +190,8 @@ ALIASES = "fixme=\xrefitem fixme \"Fix\" \"Fixmes\"" \ "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. diff --git a/senf.dict b/senf.dict index e8d59567db1908f87c7a774cef40b06de10835f7..42a609b913d0df17916867dd10a3ea15bd2afd21 100644 --- a/senf.dict +++ b/senf.dict @@ -60,6 +60,7 @@ checksumPresent CIDR classsenf ClientSocketHandle +cloneable CloneSource cmd cmdadd @@ -76,6 +77,7 @@ ConnectedCommunicationPolicy ConnectedRawV ConnectedUDPv const +copyable CPPDEFINES CPPPATH createAfter @@ -528,6 +530,7 @@ templated ThresholdQueueing ThrottleBarrier tigris +timestamp todo tokenizes TokensRange