From: Thomas Markwalder Date: Fri, 20 Jun 2014 12:37:56 +0000 (-0400) Subject: [3407] Updated D2 developer's guide for signal handling. X-Git-Tag: trac3434_base~4^2~2 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=113fdc1f819e1dfc2a37bb9dcce0477a9c8760a6;p=thirdparty%2Fkea.git [3407] Updated D2 developer's guide for signal handling. --- diff --git a/src/bin/d2/d2.dox b/src/bin/d2/d2.dox index ea0c97d256..527c7b1432 100644 --- a/src/bin/d2/d2.dox +++ b/src/bin/d2/d2.dox @@ -52,7 +52,7 @@ base are shown in the following class diagram: - isc::d2::DControllerBase - provides all of the services necessary to manage an application process class derived from isc::d2::DProcess. These services include: - Command line argument handling - - Process instantiation and initialization0 + - Process instantiation and initialization - Support for stand-alone execution - Support for integrated operation as a BUNDY module (session management and event handling) @@ -84,10 +84,6 @@ an application process class derived from isc::d2::DProcess. These services incl The file may contain an arbitrary number of other modules. - @todo DControllerBase will soon support dynamic reloading of the - configuration file upon receipt of the SIGHUP signal, and graceful - shutdown upon receipt of either SIGTERM or SIGINT. - @todo Eventually, some sort of secure socket interface which supports remote control operations such as configuration changes or status reporting will likely be implemented. @@ -118,6 +114,90 @@ through the CPL layer: The CPL classes will likely move into a common library. +@subsection cplSignals CPL Signal Handling + +CPL supports interaction with the outside world via OS signals. The default +implementation supports the following signal driven behavior: +- SIGHUP receipt of this signal will cause a reloading of the configuration +file. +- SIGINT/SIGTERM receipt of either of these signals will initiate an +orderly shutdown. + +CPL applications wait for for process asynchronous IO events through +isc::asiolink::IOService::run() or its variants. These calls are not +interrupted upon signal receipt as is the select() function and while +boost::asio provides a signal mechanism it requires linking in additional +libraries. Therefore, CPL provides its own signal handling mechanism to +propagate an OS signal such as SIGHUP to an IOSerivce as a ready event with a +callback. + +isc::d2::DControllerBase uses two mechanisms to carry out signal handling. It +uses isc::util::SignalSet to catch OS signals, and isc::d2:IOSignalQueue to +propagate them to its isc::asiolink::IOService as instances of +isc::d2::IOSignal. + +This CPL signaling class hierarchy is illustrated in the following diagram: + +@image html cpl_signal_classes.svg "CPL Signal Classes" + +The mechanics of isc::d2::IOSignal are straight forward. Upon construction it +is given the target isc::asiolink::IOService, the value of the OS signal to +send (e.g. SIGINT, SIGHUP...), and an isc::d2::IOSignalHandler. This handler +should contain the logic the caller would normally execute in its OS signal +handler. Each isc::d2::IOSignal instance has a unique identifier called its +sequence_id. + +Internally, IOSignal creates a 1 ms, one-shot timer, on the given +IOService. When the timer expires its event handler invokes the caller's +IOSignalHandler passing it the sequence_id of the IOSignal. + +Sending IOSignals is done through an isc::d2::IOSignalQueue. This class is +used to create the signals, house them until they are delivered, and dequeue +them so they can be been handled. To generate an IOSignal when an OS signal +arrives, the process's OS signal handler need only call +isc::d2::IOSignalQueue::pushSignal() with the appropriate values. + +To dequeue the IOSignal inside the caller's IOSignalHandler, one simply +invokes isc::d2::IOSignalQueue::popSignal() passing it the sequence_id +parameter passed to the handler. This method returns a pointer to +instigating IOSignal from which the value of OS signal (i.e. SIGINT, +SIGUSR1...) can be obtained. Note that calling popSignal() removes the +IOSignalPtr from the queue, which should reduce its reference count to +zero upon exiting the handler (unless a deliberate copy of it is made). + +A typical isc::d2::IOSignalHandler might be structured as follows: +@code + + void processSignal(IOSignalId sequence_id) { + // Pop the signal instance off the queue. + IOSignalPtr signal = io_signal_queue_->popSignal(sequence_id); + + int os_signal_value = signal->getSignum(); + : + // logic based on the signal value + : + } + +@endcode + +IOSignal's handler invocation code will catch, log ,and then swallow any +exceptions thrown by an IOSignalHandler. This is done to protect the integrity +IOService context. + +CPL integrates the use of the two mechanisms by registering the method, +isc::d2::DControllerBase::osSignalHandler(), as the +isc::util::SignalSet::onreceipt_handler_. This configures SignalSet's internal +handler to invoke the method each time a signal arrives. When invoked, this +method will call isc::d2::IOSignalQueue::pushSignal() to create an +isc::d2::IOSignal, passing in the OS signal received and +isc::d2::DControllerBase::ioSignalHandler() to use as the IOSignal's +ready event handler + +The following sequence diagram depicts the initialization of signal handling +during startup and the subsequent receipt of a SIGHUP: + +@image html cpl_signal_sequence.svg "CPL Signal Handling Sequence" + @section d2ProcesDerivation D2's CPL Derivations D2's core application classes are DDNS-specific derivations of the CPL as show @@ -380,8 +460,4 @@ when D2UpdateMgr creates and starts executing a transaction: @image html nc_trans_sequence.svg "Transaction Execution Sequence" - - - - */ diff --git a/src/bin/d2/images/cpl_signal_classes.svg b/src/bin/d2/images/cpl_signal_classes.svg new file mode 100644 index 0000000000..f70e302ad8 --- /dev/null +++ b/src/bin/d2/images/cpl_signal_classes.svg @@ -0,0 +1,393 @@ + + + + + + + + + DControllerBase + + app_name_ + bin_name_ + verbose_ + spec_file_name_ + + DControllerBase() + ~DControllerBase() + launch() + updateConfig() + configFromFile() + executeCommand() + getAppName() + getBinName() + customOption() + createProcess() + customControllerCommand() + getUsageText() + getCustomOpts() + processSignal() + isVerbose() + setVerbose() + getIOService() + getSpecFileName() + setSpecFileName() + getController() + setController() + parseArgs() + initProcess() + runProcess() + shutdownProcess() + initSignalHandling() + osSignalHandler() + ioSignalHandler() + getProcess() + usage() + + + + + + <<typedef>> + IOSignalPtr + + + + + + + + <<typedef>> + IOSignalHandler + + + + + Blue class integrate signal handling into D2 + + + + + + + + + + + + + IOSignal + + signum_ + + IOSignal() + ~IOSignal() + nextSequenceId() + getSequenceId() + getSignum() + + + + + + + + signals_ + + + signal_set_ + + + sequence_id_ + + + onreceipt_handler_ + + + io_signal_queue_ + + + io_service_ + + + io_service_ + + + sequence_id_ + + + handler_ + + + timer_ + + + + + + <<typedef>> + IOSignalQueuePtr + + + + + + + + + + + + + + IOSignalQueue + + + IOSignalQueue() + ~IOSignalQueue() + pushSignal() + popSignal() + clear() + + + + + + + + + + + SignalSet + + local_signals_ + + SignalSet() + SignalSet() + SignalSet() + ~SignalSet() + add() + clear() + getNext() + handleNext() + remove() + setOnReceiptHandler() + clearOnReceiptHandler() + invokeOnReceiptHandler() + block() + erase() + insert() + maskSignals() + popNext() + unblock() + + + + + + <<typedef>> + IOSignalMap + + + + + + + + <<typedef>> + IOServicePtr + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + <<typedef>> + SignalSetPtr + + + + + + + + <<typedef>> + BoolSignalHandler + + + + + + + + <<typedef>> + IOSignalId + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + IntervalTimer + + + IntervalTimer() + operator =() + IntervalTimer() + ~IntervalTimer() + setup() + cancel() + getInterval() + + + + + + + + + + + TimerCallback + + + TimerCallback() + operator ()() + + + + + + <<typedef>> + IntervalTimerPtr + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/bin/d2/images/cpl_signal_sequence.svg b/src/bin/d2/images/cpl_signal_sequence.svg new file mode 100644 index 0000000000..b57f5b1991 --- /dev/null +++ b/src/bin/d2/images/cpl_signal_sequence.svg @@ -0,0 +1,318 @@ + + + + + + + + + + + + :DControllerBase + + + + + + :IOSignalQueue + + + + + + + + + :SignalSet + + + + + + + + + :IOSignal + + + + + + + + + :IntervalTimer + + + + + + + + + :TimerCallback + + + + + + + + + :IOService + + + + + + + + + :DProcessBase + + + + + + + + + + + Details of configFromFile + omitted for clarity + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Sometime after + runProcess is called + SIGHUP is sent + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + launch() + + + initSignalHandling() + + + IOSignalQueue(io_service) + + + setOnReceiptHandler(DControllerBase::osSignalHandler) + + + SignalSet(SIGHUP,SIGINT,SIGTERM) + + + internalHandler(SIGHUP) + + + invokeOnReceiptHandler(SIGHUP) + + + pushSignal(SIGHUP, DControllerBase::ioSignalHandler) + + + IOSignal(io_service, SIGHUP, handler) + + + IntervalTimer(io_service) + + + setup(TimerCallBack(sequence_id, handler), 1, ONE_SHOT)) + + + TimerCallback(sequence_id, handler) + + + operator ()() + + + run() + + + runProcess() + + + run() + + + ioSignalHandler(sequence_id) + + + popSignal() + + + getSignum() + + + processSignal(SIGHUP) + + + configFromFile() + + + configure() + + + + + + + + + + + + + + diff --git a/src/bin/d2/io_service_signal.h b/src/bin/d2/io_service_signal.h index d10e6bf01e..0df1821022 100644 --- a/src/bin/d2/io_service_signal.h +++ b/src/bin/d2/io_service_signal.h @@ -62,14 +62,14 @@ typedef boost::function IOSignalHandler; /// arrives, the process's OS signal handler simply calls @ref /// isc::d2::IOSignalQueue::pushSignal() with the appropriate values. /// -/// @Note that an IOSignalQueue requires a non-null IOServicePtr to construct. +/// @note that an IOSignalQueue requires a non-null IOServicePtr to construct. /// This ensures that the IOService cannot be destroyed before any pending /// signals can be canceled. It also means that a queue can only be used to /// send signals to that IOService. If you need to send signals to more than /// one service, each service must have its own queue. /// /// To dequeue the IOSignal inside the caller's IOSignalHandler, one simply -/// invokes @ref isc::d2::IOSignalQueue:popSignal() passing it the sequence_id +/// invokes @ref isc::d2::IOSignalQueue::popSignal() passing it the sequence_id /// parameter passed to the handler. This method returns a pointer to /// instigating IOSignal from which the value of OS signal (i.e. SIGINT, /// SIGUSR1...) can be obtained. Note that calling popSignal() removes the @@ -151,7 +151,7 @@ public: /// @param handler pointer to the function to handle the IOSignal /// /// @throw IOSignalError if handler is null. - TimerCallback(IOSignalId sequence_id_, IOSignalHandler handler_); + TimerCallback(IOSignalId sequence_id, IOSignalHandler handler); /// @brief () Operator which serves as the timer's callback ///