From: g0dil Date: Wed, 1 Oct 2008 12:46:35 +0000 (+0000) Subject: Utils/Daemon: Add warning when the scheduler has registered events at a fork() X-Git-Url: http://g0dil.de/git?a=commitdiff_plain;h=9348e1098d66ac2684c8e280abf8d7143c887982;p=senf.git Utils/Daemon: Add warning when the scheduler has registered events at a fork() Scheduler: Documentation update git-svn-id: https://svn.berlios.de/svnroot/repos/senf/trunk@920 270642c3-0616-0410-b53a-bc976706d245 --- diff --git a/Packets/Packet.cti b/Packets/Packet.cti index fac31c0..cfd8919 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 +prefix_ Annotation & senf::Packet::annotation() +{ + return ptr()->annotation(); +} + /////////////////////////////////////////////////////////////////////////// // senf::ConcretePacket diff --git a/Packets/Packet.hh b/Packets/Packet.hh index af45ff2..58264f8 100644 --- a/Packets/Packet.hh +++ b/Packets/Packet.hh @@ -314,6 +314,14 @@ namespace senf { ///@} + ///\name Annotations + ///@{ + + template + 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 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()) + \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 70b3e1d..b88993e 100644 --- a/Packets/Packet.test.cc +++ b/Packets/Packet.test.cc @@ -109,6 +109,15 @@ namespace { senf::PacketRegistry::RegistrationProxy 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().value = 0xDEADBEEF ); + ComplexAnnotation & ca (packet.annotation()); + 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 2bd41be..5b5e12f 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::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 40489bf..9b8a277 100644 --- a/Packets/PacketImpl.cci +++ b/Packets/PacketImpl.cci @@ -30,6 +30,21 @@ #define prefix_ inline ///////////////////////////////cci.p/////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////// +// senf::detail::AnnotationIndexerBase + +prefix_ std::vector & senf::detail::AnnotationIndexerBase::small() +{ + static std::vector 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 a00a8e3..21956f1 100644 --- a/Packets/PacketImpl.cti +++ b/Packets/PacketImpl.cti @@ -31,6 +31,41 @@ ///////////////////////////////cti.p/////////////////////////////////////// /////////////////////////////////////////////////////////////////////////// +// senf::detail::AnnotationIndexer + +template +prefix_ senf::detail::AnnotationIndexer::AnnotationIndexer() + : index_ (maxAnnotations++) +{ + small().push_back(Small); +} + +template +prefix_ unsigned senf::detail::AnnotationIndexer::index() +{ + return AnnotationIndexer::instance().index_; +} + +/////////////////////////////////////////////////////////////////////////// +// senf::detail::GetAnnotation + +template +prefix_ Annotation & senf::detail::GetAnnotation::get(AnnotationP * & p) +{ + if (!p) + p = new TAnnotationP(); + return static_cast< TAnnotationP* >(p)->annotation; +} + +/* +template +prefix_ Annotation & senf::detail::GetAnnotation::get(AnnotationP * & p) +{ + return * reinterpret_cast(p); +} +*/ + +/////////////////////////////////////////////////////////////////////////// // senf::detail::PacketImpl // Data container @@ -46,9 +81,18 @@ prefix_ void senf::detail::PacketImpl::insert(PacketData * self, iterator pos, F template 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 +prefix_ Annotation & senf::detail::PacketImpl::annotation() +{ + return GetAnnotation::get( + annotations_[AnnotationIndexer::index()]); +} + ///////////////////////////////cti.e/////////////////////////////////////// #undef prefix_ diff --git a/Packets/PacketImpl.hh b/Packets/PacketImpl.hh index 871b9ec..1d1373f 100644 --- a/Packets/PacketImpl.hh +++ b/Packets/PacketImpl.hh @@ -28,9 +28,11 @@ // Custom includes #include +#include #include #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 & small(); + }; + + template + struct AnnotationIndexer + : public senf::singleton< AnnotationIndexer >, + public AnnotationIndexerBase + { + AnnotationIndexer(); + unsigned index_; + static unsigned index(); + static bool const Small = (sizeof(Annotation) <= sizeof(void*)); + }; + + struct AnnotationP + { + virtual ~AnnotationP(); + }; + + template + struct TAnnotationP + : public AnnotationP + { + Annotation annotation; + }; + + template ::Small> + struct GetAnnotation + { + static Annotation & get(AnnotationP * & p); + }; + +/* + template + struct GetAnnotation + { + 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 + 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 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 672436f..f542015 100644 --- a/Packets/PacketInterpreter.cti +++ b/Packets/PacketInterpreter.cti @@ -50,6 +50,12 @@ prefix_ typename senf::PacketInterpreter::ptr senf::PacketInterpreterBase: static_cast< PacketInterpreter* >(this)); } +template +prefix_ Annotation & senf::PacketInterpreterBase::annotation() +{ + return impl().annotation(); +} + /////////////////////////////////////////////////////////////////////////// // senf::PacketInterpreter diff --git a/Packets/PacketInterpreter.hh b/Packets/PacketInterpreter.hh index 24f29c2..790c600 100644 --- a/Packets/PacketInterpreter.hh +++ b/Packets/PacketInterpreter.hh @@ -162,6 +162,14 @@ namespace senf { ///@} + ///\name Annotations + ///@{ + + template + Annotation & annotation(); + + ///@} + ///\name Access to the abstract interface ///@{ diff --git a/Scheduler/FdEvent.hh b/Scheduler/FdEvent.hh index 90ddc7a..d574351 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 int get_descriptor(Handle const & handle); }} diff --git a/Scheduler/Mainpage.dox b/Scheduler/Mainpage.dox index 054af2e..e67784a 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 1d7b28b..7ba69ef 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 3329426..af07c48 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 generic functions 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 generic functions 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 + #include + + 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 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 Boost.PointerContainer + 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 2b09cc0..d6b8d91 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 f589bbf..9603551 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
\endhtmlonly" + "autotoc=\htmlonly
\endhtmlonly" \ + "seechapter=\htmlonly      → \endhtmlonly see " # 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 e8d5956..42a609b 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