Browse Source

[3971] Using TimerMgr in the Memfile backend.

Marcin Siodelski 9 years ago
parent
commit
dc43033672

+ 4 - 0
src/bin/dhcp4/ctrl_dhcp4_srv.cc

@@ -19,6 +19,7 @@
 #include <hooks/hooks_manager.h>
 #include <hooks/hooks_manager.h>
 #include <dhcp4/json_config_parser.h>
 #include <dhcp4/json_config_parser.h>
 #include <dhcpsrv/cfgmgr.h>
 #include <dhcpsrv/cfgmgr.h>
+#include <dhcpsrv/timer_mgr.h>
 #include <config/command_mgr.h>
 #include <config/command_mgr.h>
 #include <stats/stats_mgr.h>
 #include <stats/stats_mgr.h>
 
 
@@ -123,7 +124,9 @@ ControlledDhcpv4Srv::processConfig(isc::data::ConstElementPtr config) {
         return (isc::config::createAnswer(1, err.str()));
         return (isc::config::createAnswer(1, err.str()));
     }
     }
 
 
+    TimerMgr::instance()->stopThread();
     ConstElementPtr answer = configureDhcp4Server(*srv, config);
     ConstElementPtr answer = configureDhcp4Server(*srv, config);
+    TimerMgr::instance()->startThread();
 
 
     // Check that configuration was successful. If not, do not reopen sockets
     // Check that configuration was successful. If not, do not reopen sockets
     // and don't bother with DDNS stuff.
     // and don't bother with DDNS stuff.
@@ -157,6 +160,7 @@ ControlledDhcpv4Srv::processConfig(isc::data::ConstElementPtr config) {
     CfgMgr::instance().getStagingCfg()->getCfgIface()->
     CfgMgr::instance().getStagingCfg()->getCfgIface()->
         openSockets(AF_INET, srv->getPort(), getInstance()->useBroadcast());
         openSockets(AF_INET, srv->getPort(), getInstance()->useBroadcast());
 
 
+
     return (answer);
     return (answer);
 }
 }
 
 

+ 0 - 7
src/bin/dhcp4/dhcp4_messages.mes

@@ -312,13 +312,6 @@ be sent to the client in the DHCPACK message. The first argument
 contains the client and the transaction identification information. The
 contains the client and the transaction identification information. The
 second argument contains the allocated IPv4 address.
 second argument contains the allocated IPv4 address.
 
 
-% DHCP4_LEASE_DATABASE_TIMERS_EXEC_FAIL failed to execute timer-based functions for lease database: %1
-A warning message executed when a server process is unable to execute
-the periodic actions for the lease database. An example of the periodic
-action is a Lease File Cleanup. One of the reasons for the failure is
-a misconfiguration of the lease database, whereby the lease database
-hasn't been selected.
-
 % DHCP4_NAME_GEN_UPDATE_FAIL %1: failed to update the lease after generating name %2 for a client: %3
 % DHCP4_NAME_GEN_UPDATE_FAIL %1: failed to update the lease after generating name %2 for a client: %3
 This message indicates the failure when trying to update the lease and/or
 This message indicates the failure when trying to update the lease and/or
 options in the server's response with the hostname generated by the server
 options in the server's response with the hostname generated by the server

+ 1 - 19
src/bin/dhcp4/dhcp4_srv.cc

@@ -365,16 +365,7 @@ Dhcpv4Srv::run() {
         Pkt4Ptr rsp;
         Pkt4Ptr rsp;
 
 
         try {
         try {
-            // The lease database backend may install some timers for which
-            // the handlers need to be executed periodically. Retrieve the
-            // maximum interval at which the handlers must be executed from
-            // the lease manager.
-            uint32_t timeout = LeaseMgrFactory::instance().getIOServiceExecInterval();
-            // If the returned value is zero it means that there are no
-            // timers installed, so use a default value.
-            if (timeout == 0) {
-                timeout = 1000;
-            }
+            uint32_t timeout = 1000;
             LOG_DEBUG(packet4_logger, DBG_DHCP4_DETAIL, DHCP4_BUFFER_WAIT).arg(timeout);
             LOG_DEBUG(packet4_logger, DBG_DHCP4_DETAIL, DHCP4_BUFFER_WAIT).arg(timeout);
             query = receivePacket(timeout);
             query = receivePacket(timeout);
 
 
@@ -429,15 +420,6 @@ Dhcpv4Srv::run() {
                 .arg(e.what());
                 .arg(e.what());
         }
         }
 
 
-        // Execute ready timers for the lease database, e.g. Lease File Cleanup.
-        try {
-            LeaseMgrFactory::instance().getIOService()->poll();
-
-        } catch (const std::exception& ex) {
-            LOG_WARN(dhcp4_logger, DHCP4_LEASE_DATABASE_TIMERS_EXEC_FAIL)
-                .arg(ex.what());
-        }
-
         // Timeout may be reached or signal received, which breaks select()
         // Timeout may be reached or signal received, which breaks select()
         // with no reception occurred. No need to log anything here because
         // with no reception occurred. No need to log anything here because
         // we have logged right after the call to receivePacket().
         // we have logged right after the call to receivePacket().

+ 45 - 1
src/bin/dhcp4/tests/dhcp4_process_tests.sh.in

@@ -16,6 +16,8 @@
 CFG_FILE=@abs_top_builddir@/src/bin/dhcp4/tests/test_config.json
 CFG_FILE=@abs_top_builddir@/src/bin/dhcp4/tests/test_config.json
 # Path to the Kea log file.
 # Path to the Kea log file.
 LOG_FILE=@abs_top_builddir@/src/bin/dhcp4/tests/test.log
 LOG_FILE=@abs_top_builddir@/src/bin/dhcp4/tests/test.log
+# Path to the Kea lease file.
+LEASE_FILE=@abs_top_builddir@/src/bin/dhcp4/tests/test_leases.csv
 # Expected version
 # Expected version
 EXPECTED_VERSION="@PACKAGE_VERSION@"
 EXPECTED_VERSION="@PACKAGE_VERSION@"
 # Kea configuration to be stored in the configuration file.
 # Kea configuration to be stored in the configuration file.
@@ -31,7 +33,9 @@ CONFIG="{
         \"lease-database\":
         \"lease-database\":
         {
         {
             \"type\": \"memfile\",
             \"type\": \"memfile\",
-            \"persist\": false
+            \"name\": \"$LEASE_FILE\",
+            \"persist\": false,
+            \"lfc-interval\": 1
         },
         },
         \"subnet4\": [
         \"subnet4\": [
         {
         {
@@ -276,9 +280,49 @@ shutdown_test() {
     test_finish 0
     test_finish 0
 }
 }
 
 
+# This test verifies that DHCPv4 can be reconfigured with a SIGHUP signal.
+lfc_timer_test() {
+    # Log the start of the test and print test name.
+    test_start "dhcpv4_srv.lfc_timer_test"
+    # Remove dangling Kea instances and remove log files.
+    cleanup
+    # Create new configuration file.
+    create_config "${CONFIG}"
+    # Instruct Kea to log to the specific file.
+    set_logger
+    # Start Kea.
+    start_kea ${bin_path}/${bin}
+    # Wait up to 20s for Kea to start.
+    wait_for_kea 20
+    if [ ${_WAIT_FOR_KEA} -eq 0 ]; then
+        printf "ERROR: timeout waiting for Kea to start.\n"
+        clean_exit 1
+    fi
+
+    # Check if it is still running. It could have terminated (e.g. as a result
+    # of configuration failure).
+    get_pids ${bin}
+    if [ ${_GET_PIDS_NUM} -ne 1 ]; then
+        printf "ERROR: expected one Kea process to be started. Found %d processes\
+ started.\n" ${_GET_PIDS_NUM}
+        clean_exit 1
+    fi
+
+    wait_for_message 10 "DHCPSRV_MEMFILE_EXECUTE" 1
+    if [ ${_WAIT_FOR_MESSAGE} -eq 0 ]; then
+        printf "ERROR: Server did not execute LFC.\n"
+        clean_exit 1
+    fi
+
+    # All ok. Shut down Kea and exit.
+    test_finish 0
+}
+
+
 server_pid_file_test "${CONFIG}" DHCP4_ALREADY_RUNNING
 server_pid_file_test "${CONFIG}" DHCP4_ALREADY_RUNNING
 dynamic_reconfiguration_test
 dynamic_reconfiguration_test
 shutdown_test "dhcpv4.sigterm_test" 15
 shutdown_test "dhcpv4.sigterm_test" 15
 shutdown_test "dhcpv4.sigint_test" 2
 shutdown_test "dhcpv4.sigint_test" 2
 version_test "dhcpv4.version"
 version_test "dhcpv4.version"
 logger_vars_test "dhcpv4.variables"
 logger_vars_test "dhcpv4.variables"
+lfc_timer_test

+ 0 - 7
src/bin/dhcp6/dhcp6_messages.mes

@@ -376,13 +376,6 @@ The first argument holds the client and the transaction identification
 information. The second argument holds the IAID. The third argument
 information. The second argument holds the IAID. The third argument
 holds the detailed lease information.
 holds the detailed lease information.
 
 
-% DHCP6_LEASE_DATABASE_TIMERS_EXEC_FAIL failed to execute timer-based functions for lease database: %1
-A warning message executed when a server process is unable to execute
-the periodic actions for the lease database. An example of the periodic
-action is a Lease File Cleanup. One of the reasons for the failure is
-a misconfiguration of the lease database, whereby the lease database
-hasn't been selected.
-
 % DHCP6_LEASE_NA_WITHOUT_DUID %1: address lease for address %2 does not have a DUID
 % DHCP6_LEASE_NA_WITHOUT_DUID %1: address lease for address %2 does not have a DUID
 This error message indicates a database consistency problem. The lease
 This error message indicates a database consistency problem. The lease
 database has an entry indicating that the given address is in use,
 database has an entry indicating that the given address is in use,

+ 1 - 19
src/bin/dhcp6/dhcp6_srv.cc

@@ -317,16 +317,7 @@ bool Dhcpv6Srv::run() {
         Pkt6Ptr rsp;
         Pkt6Ptr rsp;
 
 
         try {
         try {
-            // The lease database backend may install some timers for which
-            // the handlers need to be executed periodically. Retrieve the
-            // maximum interval at which the handlers must be executed from
-            // the lease manager.
-            uint32_t timeout = LeaseMgrFactory::instance().getIOServiceExecInterval();
-            // If the returned value is zero it means that there are no
-            // timers installed, so use a default value.
-            if (timeout == 0) {
-                timeout = 1000;
-            }
+            uint32_t timeout = 1000;
             LOG_DEBUG(packet6_logger, DBG_DHCP6_DETAIL, DHCP6_BUFFER_WAIT).arg(timeout);
             LOG_DEBUG(packet6_logger, DBG_DHCP6_DETAIL, DHCP6_BUFFER_WAIT).arg(timeout);
             query = receivePacket(timeout);
             query = receivePacket(timeout);
 
 
@@ -385,15 +376,6 @@ bool Dhcpv6Srv::run() {
                 .arg(e.what());
                 .arg(e.what());
         }
         }
 
 
-        // Execute ready timers for the lease database, e.g. Lease File Cleanup.
-        try {
-            LeaseMgrFactory::instance().getIOService()->poll();
-
-        } catch (const std::exception& ex) {
-            LOG_WARN(dhcp6_logger, DHCP6_LEASE_DATABASE_TIMERS_EXEC_FAIL)
-                .arg(ex.what());
-        }
-
         // Timeout may be reached or signal received, which breaks select()
         // Timeout may be reached or signal received, which breaks select()
         // with no packet received
         // with no packet received
         if (!query) {
         if (!query) {

+ 6 - 0
src/lib/dhcpsrv/dhcpsrv_messages.mes

@@ -346,6 +346,12 @@ failure persist however, the size of the lease file will increase without bound.
 An informational message issued when the Memfile lease database backend
 An informational message issued when the Memfile lease database backend
 starts the periodic Lease File Cleanup.
 starts the periodic Lease File Cleanup.
 
 
+% DHCPSRV_MEMFILE_LFC_UNREGISTER_TIMER_FAILED failed to unregister timer 'memfile-lfc': %1
+This error message is logged when Memfile backend fails to unregister
+timer used for lease file cleanup scheduling. This is highly unlikely
+and indicates programming error. The message include the reason for this
+error.
+
 % DHCPSRV_MEMFILE_NO_STORAGE running in non-persistent mode, leases will be lost after restart
 % DHCPSRV_MEMFILE_NO_STORAGE running in non-persistent mode, leases will be lost after restart
 A warning message issued when writes of leases to disk have been disabled
 A warning message issued when writes of leases to disk have been disabled
 in the configuration. This mode is useful for some kinds of performance
 in the configuration. This mode is useful for some kinds of performance

+ 1 - 40
src/lib/dhcpsrv/lease_mgr.h

@@ -118,16 +118,6 @@ public:
 /// be used directly, but rather specialized derived class should be used
 /// be used directly, but rather specialized derived class should be used
 /// instead.
 /// instead.
 ///
 ///
-/// This class creates an instance of the @c asiolink::IOService in the
-/// constructor. This object is required to execute the
-/// @c asiolink::IntervalTimer for the operations triggered periodically
-/// by the lease database backends which implement @c LeaseMgr interface.
-/// In order to execute the timers installed by the particular backend,
-/// the owner of the backend (e.g. DHCP server) should retrieve the pointer
-/// to the @c asiolink::IOService object by calling @c LeaseMgr::getIOService
-/// and call the appropriate functions, e.g. @c poll_one or @c run_one in a
-/// main loop.
-///
 /// This class throws no exceptions.  However, methods in concrete
 /// This class throws no exceptions.  However, methods in concrete
 /// implementations of this class may throw exceptions: see the documentation
 /// implementations of this class may throw exceptions: see the documentation
 /// of those classes for details.
 /// of those classes for details.
@@ -145,7 +135,7 @@ public:
     /// @param parameters A data structure relating keywords and values
     /// @param parameters A data structure relating keywords and values
     ///        concerned with the database.
     ///        concerned with the database.
     LeaseMgr(const ParameterMap& parameters)
     LeaseMgr(const ParameterMap& parameters)
-        : parameters_(parameters), io_service_(new asiolink::IOService())
+        : parameters_(parameters)
     {}
     {}
 
 
     /// @brief Destructor
     /// @brief Destructor
@@ -439,31 +429,6 @@ public:
     /// @brief returns value of the parameter
     /// @brief returns value of the parameter
     virtual std::string getParameter(const std::string& name) const;
     virtual std::string getParameter(const std::string& name) const;
 
 
-    /// @brief Returns the interval at which the @c IOService events should
-    /// be released.
-    ///
-    /// The implementations of this class may install the timers which
-    /// periodically trigger event handlers defined for them. Depending
-    /// on the intervals specified for these timers the @c IOService::poll,
-    /// @c IOService::run etc. have to be executed to allow the timers
-    /// for checking whether they have already expired and the handler
-    /// must be executed. Running the @c IOService with a lower interval
-    /// would cause the desynchronization of timers with the clock.
-    ///
-    /// @return A maximum interval in seconds at which the @c IOService
-    /// should be executed. A value of 0 means that no timers are installed
-    /// and that there is no requirement for the @c IOService to be
-    /// executed at any specific interval.
-    virtual uint32_t getIOServiceExecInterval() const {
-        return (0);
-    }
-
-    /// @brief Returns a reference to the @c IOService object used
-    /// by the Lease Manager.
-    const asiolink::IOServicePtr& getIOService() const {
-        return (io_service_);
-    }
-
 private:
 private:
     /// @brief list of parameters passed in dbconfig
     /// @brief list of parameters passed in dbconfig
     ///
     ///
@@ -471,10 +436,6 @@ private:
     /// password and other parameters required for DB access. It is not
     /// password and other parameters required for DB access. It is not
     /// intended to keep any DHCP-related parameters.
     /// intended to keep any DHCP-related parameters.
     ParameterMap parameters_;
     ParameterMap parameters_;
-
-    /// @brief Pointer to the IO service object used by the derived classes
-    /// to trigger interval timers.
-    asiolink::IOServicePtr io_service_;
 };
 };
 
 
 }; // end of isc::dhcp namespace
 }; // end of isc::dhcp namespace

+ 48 - 36
src/lib/dhcpsrv/memfile_lease_mgr.cc

@@ -17,6 +17,7 @@
 #include <dhcpsrv/dhcpsrv_log.h>
 #include <dhcpsrv/dhcpsrv_log.h>
 #include <dhcpsrv/lease_file_loader.h>
 #include <dhcpsrv/lease_file_loader.h>
 #include <dhcpsrv/memfile_lease_mgr.h>
 #include <dhcpsrv/memfile_lease_mgr.h>
+#include <dhcpsrv/timer_mgr.h>
 #include <exceptions/exceptions.h>
 #include <exceptions/exceptions.h>
 #include <util/pid_file.h>
 #include <util/pid_file.h>
 #include <util/process_spawn.h>
 #include <util/process_spawn.h>
@@ -73,9 +74,12 @@ public:
     /// @c Memfile_LeaseMgr class.
     /// @c Memfile_LeaseMgr class.
     ///
     ///
     /// @param callback A pointer to the callback function.
     /// @param callback A pointer to the callback function.
-    /// @param io_service An io service used to create the interval timer.
-    LFCSetup(asiolink::IntervalTimer::Callback callback,
-             asiolink::IOService& io_service);
+    LFCSetup(asiolink::IntervalTimer::Callback callback);
+
+    /// @brief Destructor.
+    ///
+    /// Unregisters LFC timer.
+    ~LFCSetup();
 
 
     /// @brief Sets the new configuration for the %Lease File Cleanup.
     /// @brief Sets the new configuration for the %Lease File Cleanup.
     ///
     ///
@@ -92,11 +96,6 @@ public:
     /// @brief Spawns a new process.
     /// @brief Spawns a new process.
     void execute();
     void execute();
 
 
-    /// @brief Returns interval at which the cleanup is performed.
-    ///
-    /// @return Interval in milliseconds.
-    long getInterval() const;
-
     /// @brief Checks if the lease file cleanup is in progress.
     /// @brief Checks if the lease file cleanup is in progress.
     ///
     ///
     /// @return true if the lease file cleanup is being executed.
     /// @return true if the lease file cleanup is being executed.
@@ -107,9 +106,6 @@ public:
 
 
 private:
 private:
 
 
-    /// @brief Interval timer for LFC.
-    asiolink::IntervalTimer timer_;
-
     /// @brief A pointer to the @c ProcessSpawn object used to execute
     /// @brief A pointer to the @c ProcessSpawn object used to execute
     /// the LFC.
     /// the LFC.
     boost::scoped_ptr<util::ProcessSpawn> process_;
     boost::scoped_ptr<util::ProcessSpawn> process_;
@@ -119,11 +115,36 @@ private:
 
 
     /// @brief A PID of the last executed LFC process.
     /// @brief A PID of the last executed LFC process.
     pid_t pid_;
     pid_t pid_;
+
+    /// @brief Pointer to the Timer Manager.
+    TimerMgrPtr timer_mgr_;
 };
 };
 
 
-LFCSetup::LFCSetup(asiolink::IntervalTimer::Callback callback,
-                   asiolink::IOService& io_service)
-    : timer_(io_service), process_(), callback_(callback), pid_(0) {
+LFCSetup::LFCSetup(asiolink::IntervalTimer::Callback callback)
+    : process_(), callback_(callback), pid_(0),
+      timer_mgr_(TimerMgr::instance()) {
+}
+
+LFCSetup::~LFCSetup() {
+    try {
+        // If we're here it means that we are reconfiguring the server or
+        // the server is terminating. In both cases the thread must
+        // be already stopped or must stop now.
+        timer_mgr_->stopThread();
+        // This may throw exception if the timer hasn't been registered
+        // but if the LFC Setup instance exists it means that the timer
+        // must have been registered or such registration have been
+        // attempted. The registration may fail if the duplicate timer
+        // exists or if the TimerMgr's worker thread is running but if
+        // this happens it is a programming error. In any case, we
+        // don't want exceptions being thrown from the destructor so
+        // we just log an error here.
+        timer_mgr_->unregisterTimer("memfile-lfc");
+
+    } catch (const std::exception& ex) {
+        LOG_ERROR(dhcpsrv_logger, DHCPSRV_MEMFILE_LFC_UNREGISTER_TIMER_FAILED)
+            .arg(ex.what());
+    }
 }
 }
 
 
 void
 void
@@ -143,13 +164,6 @@ LFCSetup::setup(const uint32_t lfc_interval,
             executable = c_executable;
             executable = c_executable;
         }
         }
 
 
-        // Set the timer to call callback function periodically.
-        LOG_INFO(dhcpsrv_logger, DHCPSRV_MEMFILE_LFC_SETUP).arg(lfc_interval);
-        // Multiple the lfc_interval value by 1000 as this value specifies
-        // a timeout in seconds, whereas the setup() method expects the
-        // timeout in milliseconds.
-        timer_.setup(callback_, lfc_interval * 1000);
-
         // Start preparing the command line for kea-lfc.
         // Start preparing the command line for kea-lfc.
 
 
         // Gather the base file name.
         // Gather the base file name.
@@ -187,12 +201,17 @@ LFCSetup::setup(const uint32_t lfc_interval,
 
 
         // Create the process (do not start it yet).
         // Create the process (do not start it yet).
         process_.reset(new util::ProcessSpawn(executable, args));
         process_.reset(new util::ProcessSpawn(executable, args));
-    }
-}
 
 
-long
-LFCSetup::getInterval() const {
-    return (timer_.getInterval());
+        // Set the timer to call callback function periodically.
+        LOG_INFO(dhcpsrv_logger, DHCPSRV_MEMFILE_LFC_SETUP).arg(lfc_interval);
+
+        // Multiple the lfc_interval value by 1000 as this value specifies
+        // a timeout in seconds, whereas the setup() method expects the
+        // timeout in milliseconds.
+        timer_mgr_->registerTimer("memfile-lfc", callback_, lfc_interval * 1000,
+                                  asiolink::IntervalTimer::REPEATING);
+        timer_mgr_->setup("memfile-lfc");
+    }
 }
 }
 
 
 void
 void
@@ -227,9 +246,7 @@ const int Memfile_LeaseMgr::MAJOR_VERSION;
 const int Memfile_LeaseMgr::MINOR_VERSION;
 const int Memfile_LeaseMgr::MINOR_VERSION;
 
 
 Memfile_LeaseMgr::Memfile_LeaseMgr(const ParameterMap& parameters)
 Memfile_LeaseMgr::Memfile_LeaseMgr(const ParameterMap& parameters)
-    : LeaseMgr(parameters),
-      lfc_setup_(new LFCSetup(boost::bind(&Memfile_LeaseMgr::lfcCallback, this),
-                              *getIOService()))
+    : LeaseMgr(parameters), lfc_setup_()
     {
     {
     // Check the universe and use v4 file or v6 file.
     // Check the universe and use v4 file or v6 file.
     std::string universe = getParameter("universe");
     std::string universe = getParameter("universe");
@@ -255,7 +272,7 @@ Memfile_LeaseMgr::Memfile_LeaseMgr(const ParameterMap& parameters)
         LOG_WARN(dhcpsrv_logger, DHCPSRV_MEMFILE_NO_STORAGE);
         LOG_WARN(dhcpsrv_logger, DHCPSRV_MEMFILE_NO_STORAGE);
 
 
     } else  {
     } else  {
-        lfcSetup();
+       lfcSetup();
     }
     }
 }
 }
 
 
@@ -785,12 +802,6 @@ Memfile_LeaseMgr::appendSuffix(const std::string& file_name,
     return (name);
     return (name);
 }
 }
 
 
-
-uint32_t
-Memfile_LeaseMgr::getIOServiceExecInterval() const {
-    return (static_cast<uint32_t>(lfc_setup_->getInterval() / 1000));
-}
-
 std::string
 std::string
 Memfile_LeaseMgr::getDefaultLeaseFilePath(Universe u) const {
 Memfile_LeaseMgr::getDefaultLeaseFilePath(Universe u) const {
     std::ostringstream s;
     std::ostringstream s;
@@ -943,6 +954,7 @@ Memfile_LeaseMgr::lfcSetup() {
     }
     }
 
 
     if (lfc_interval > 0) {
     if (lfc_interval > 0) {
+        lfc_setup_.reset(new LFCSetup(boost::bind(&Memfile_LeaseMgr::lfcCallback, this)));
         lfc_setup_->setup(lfc_interval, lease_file4_, lease_file6_);
         lfc_setup_->setup(lfc_interval, lease_file4_, lease_file6_);
     }
     }
 }
 }

+ 1 - 19
src/lib/dhcpsrv/memfile_lease_mgr.h

@@ -49,12 +49,7 @@ class LFCSetup;
 /// The backend installs an @c asiolink::IntervalTimer to periodically execute
 /// The backend installs an @c asiolink::IntervalTimer to periodically execute
 /// the @c Memfile_LeaseMgr::lfcCallback. This callback function controls
 /// the @c Memfile_LeaseMgr::lfcCallback. This callback function controls
 /// the startup of the background process which removes redundant information
 /// the startup of the background process which removes redundant information
-/// from the lease file(s). Note that the @c asiolink::IntervalTimer uses
-/// @c asiolink::IOService to execute the callback. The @c LeaseMgr class
-/// creates this object, which can be obtained by the caller using the
-/// @c LeaseMgr::getIOService. The caller should later call an appropriate
-/// method, @c boost::asio::io_service::poll_one to execute the callback when
-/// the timer is ready.
+/// from the lease file(s).
 ///
 ///
 /// When the backend is starting up, it reads leases from the lease file (one
 /// When the backend is starting up, it reads leases from the lease file (one
 /// by one) and adds them to the in-memory container as follows:
 /// by one) and adds them to the in-memory container as follows:
@@ -458,19 +453,6 @@ public:
     ///       about the state of the backend.
     ///       about the state of the backend.
     //@{
     //@{
 
 
-    /// @brief Returns the interval at which the @c IOService events should
-    /// be released.
-    ///
-    /// The Memfile backend may install a timer to execute the %Lease File
-    /// Cleanup periodically. If this timer is installed, the method returns
-    /// the LFC interval in milliseconds.
-    ///
-    /// @return A maximum interval (in seconds) at which the @c IOService
-    /// should be executed. A value of 0 means that no timers are installed
-    /// and that there is no requirement for the @c IOService to be
-    /// executed at any specific interval.
-    virtual uint32_t getIOServiceExecInterval() const;
-
     /// @brief Returns default path to the lease file.
     /// @brief Returns default path to the lease file.
     ///
     ///
     /// @param u Universe (V4 or V6).
     /// @param u Universe (V4 or V6).

+ 39 - 56
src/lib/dhcpsrv/tests/memfile_lease_mgr_unittest.cc

@@ -14,12 +14,15 @@
 
 
 #include <config.h>
 #include <config.h>
 
 
+#include <boost/asio.hpp>
 #include <asiolink/io_address.h>
 #include <asiolink/io_address.h>
 #include <dhcp/duid.h>
 #include <dhcp/duid.h>
+#include <dhcp/iface_mgr.h>
 #include <dhcpsrv/cfgmgr.h>
 #include <dhcpsrv/cfgmgr.h>
 #include <dhcpsrv/lease_mgr.h>
 #include <dhcpsrv/lease_mgr.h>
 #include <dhcpsrv/lease_mgr_factory.h>
 #include <dhcpsrv/lease_mgr_factory.h>
 #include <dhcpsrv/memfile_lease_mgr.h>
 #include <dhcpsrv/memfile_lease_mgr.h>
+#include <dhcpsrv/timer_mgr.h>
 #include <dhcpsrv/tests/lease_file_io.h>
 #include <dhcpsrv/tests/lease_file_io.h>
 #include <dhcpsrv/tests/test_utils.h>
 #include <dhcpsrv/tests/test_utils.h>
 #include <dhcpsrv/tests/generic_lease_mgr_unittest.h>
 #include <dhcpsrv/tests/generic_lease_mgr_unittest.h>
@@ -106,8 +109,9 @@ public:
     MemfileLeaseMgrTest() :
     MemfileLeaseMgrTest() :
         io4_(getLeaseFilePath("leasefile4_0.csv")),
         io4_(getLeaseFilePath("leasefile4_0.csv")),
         io6_(getLeaseFilePath("leasefile6_0.csv")),
         io6_(getLeaseFilePath("leasefile6_0.csv")),
-        io_service_(),
-        fail_on_callback_(false) {
+        io_service_(new IOService()),
+        fail_on_callback_(false),
+        timeout_(false) {
 
 
         std::ostringstream s;
         std::ostringstream s;
         s << KEA_LFC_BUILD_DIR << "/kea-lfc";
         s << KEA_LFC_BUILD_DIR << "/kea-lfc";
@@ -134,21 +138,10 @@ public:
     ///
     ///
     /// destroys lease manager backend.
     /// destroys lease manager backend.
     virtual ~MemfileLeaseMgrTest() {
     virtual ~MemfileLeaseMgrTest() {
-        // Explicitly destroy the timer and the IO service. Note that they
-        // must be destroyed in this order because test_timer_ depends on
-        // the io_service_ as it is passed its reference during the
-        // construction. It usually doesn't matter unless the timer is
-        // running (hasn't been cancelled). Destroying an underlying
-        // IO service before cancelling the timer relying on it may lead
-        // to a crash. This has been proven on CentOS 6, running boost
-        // version 1.41. Note that destroying the timer also cancels it.
-        // In theory, we could avoid this explicit release of these objects
-        // and rely on the order in which they are declared in the class.
-        // But, this seems to be better as it makes it more visible
-        // what we are doing here.
-        test_timer_.reset();
-        io_service_.reset();
-
+        // Stop TimerMgr worker thread if it is running.
+        TimerMgr::instance()->stopThread();
+        // Make sure there are no timers registered.
+        TimerMgr::instance()->unregisterTimers();
         LeaseMgrFactory::destroy();
         LeaseMgrFactory::destroy();
         // Remove lease files and products of Lease File Cleanup.
         // Remove lease files and products of Lease File Cleanup.
         removeFiles(getLeaseFilePath("leasefile4_0.csv"));
         removeFiles(getLeaseFilePath("leasefile4_0.csv"));
@@ -224,10 +217,24 @@ public:
             boost::bind(&MemfileLeaseMgrTest::testTimerCallback, this);
             boost::bind(&MemfileLeaseMgrTest::testTimerCallback, this);
         test_timer_.reset(new IntervalTimer(*io_service_));
         test_timer_.reset(new IntervalTimer(*io_service_));
         test_timer_->setup(cb, ms, IntervalTimer::ONE_SHOT);
         test_timer_->setup(cb, ms, IntervalTimer::ONE_SHOT);
+
+        // The timeout flag will be set by the timeoutCallback if the test
+        // lasts for too long. In this case we will return from here.
+        while (!timeout_) {
+            // Block for one 1 millisecond.
+            IfaceMgr::instance().receive6(0, 1000);
+
+            // Run ready handlers from the local IO service to execute
+            // the timeout callback if necessary.
+            io_service_->get_io_service().poll_one();
+        }
+
+        timeout_ = false;
     }
     }
 
 
     /// @brief Test timer callback function.
     /// @brief Test timer callback function.
     void testTimerCallback() {
     void testTimerCallback() {
+        timeout_ = true;
         io_service_->stop();
         io_service_->stop();
         if (fail_on_callback_) {
         if (fail_on_callback_) {
             FAIL() << "Test timeout reached";
             FAIL() << "Test timeout reached";
@@ -267,6 +274,9 @@ public:
     /// @brief Indicates if the @c testTimerCallback should cause test failure.
     /// @brief Indicates if the @c testTimerCallback should cause test failure.
     bool fail_on_callback_;
     bool fail_on_callback_;
 
 
+    /// @brief Boolean flag indicating if the test timeout occurred.
+    bool timeout_;
+
 };
 };
 
 
 // This test checks if the LeaseMgr can be instantiated and that it
 // This test checks if the LeaseMgr can be instantiated and that it
@@ -368,16 +378,16 @@ TEST_F(MemfileLeaseMgrTest, lfcTimer) {
     boost::scoped_ptr<LFCMemfileLeaseMgr>
     boost::scoped_ptr<LFCMemfileLeaseMgr>
         lease_mgr(new LFCMemfileLeaseMgr(pmap));
         lease_mgr(new LFCMemfileLeaseMgr(pmap));
 
 
-    // Check that the interval is correct.
-    EXPECT_EQ(1, lease_mgr->getIOServiceExecInterval());
-
-    io_service_ = lease_mgr->getIOService();
+    // Start worker thread to execute LFC periodically.
+    TimerMgr::instance()->startThread();
 
 
     // Run the test for at most 2.9 seconds.
     // Run the test for at most 2.9 seconds.
     setTestTime(2900);
     setTestTime(2900);
 
 
-    // Run the IO service to execute timers.
-    io_service_->run();
+    // Stop worker thread to make sure it is not running when lease
+    // manager is destroyed. The lease manager will be unable to
+    // unregster timer when the thread is active.
+    TimerMgr::instance()->stopThread();
 
 
     // Within 2.9 we should record two LFC executions.
     // Within 2.9 we should record two LFC executions.
     EXPECT_EQ(2, lease_mgr->getLFCCount());
     EXPECT_EQ(2, lease_mgr->getLFCCount());
@@ -397,15 +407,16 @@ TEST_F(MemfileLeaseMgrTest, lfcTimerDisabled) {
     boost::scoped_ptr<LFCMemfileLeaseMgr>
     boost::scoped_ptr<LFCMemfileLeaseMgr>
         lease_mgr(new LFCMemfileLeaseMgr(pmap));
         lease_mgr(new LFCMemfileLeaseMgr(pmap));
 
 
-    EXPECT_EQ(0, lease_mgr->getIOServiceExecInterval());
-
-    io_service_ = lease_mgr->getIOService();
+    // Start worker thread to execute LFC periodically.
+    TimerMgr::instance()->startThread();
 
 
     // Run the test for at most 1.9 seconds.
     // Run the test for at most 1.9 seconds.
     setTestTime(1900);
     setTestTime(1900);
 
 
-    // Run the IO service to execute timers.
-    io_service_->run();
+    // Stop worker thread to make sure it is not running when lease
+    // manager is destroyed. The lease manager will be unable to
+    // unregster timer when the thread is active.
+    TimerMgr::instance()->stopThread();
 
 
     // There should be no LFC execution recorded.
     // There should be no LFC execution recorded.
     EXPECT_EQ(0, lease_mgr->getLFCCount());
     EXPECT_EQ(0, lease_mgr->getLFCCount());
@@ -739,34 +750,6 @@ TEST_F(MemfileLeaseMgrTest, leaseFileCopy) {
     ASSERT_FALSE(input_file.exists());
     ASSERT_FALSE(input_file.exists());
 }
 }
 
 
-
-// Test that the backend returns a correct value of the interval
-// at which the IOService must be executed to run the handlers
-// for the installed timers.
-TEST_F(MemfileLeaseMgrTest, getIOServiceExecInterval) {
-    LeaseMgr::ParameterMap pmap;
-    pmap["type"] = "memfile";
-    pmap["universe"] = "4";
-    pmap["name"] = getLeaseFilePath("leasefile4_0.csv");
-
-    // The lfc-interval is not set, so the returned value should be 0.
-    boost::scoped_ptr<LFCMemfileLeaseMgr> lease_mgr(new LFCMemfileLeaseMgr(pmap));
-    EXPECT_EQ(0, lease_mgr->getIOServiceExecInterval());
-
-    // lfc-interval = 10
-    pmap["lfc-interval"] = "10";
-    lease_mgr.reset();
-    lease_mgr.reset(new LFCMemfileLeaseMgr(pmap));
-    EXPECT_EQ(10, lease_mgr->getIOServiceExecInterval());
-
-    // lfc-interval = 20
-    pmap["lfc-interval"] = "20";
-    lease_mgr.reset();
-    lease_mgr.reset(new LFCMemfileLeaseMgr(pmap));
-    EXPECT_EQ(20, lease_mgr->getIOServiceExecInterval());
-
-}
-
 // Checks that adding/getting/deleting a Lease6 object works.
 // Checks that adding/getting/deleting a Lease6 object works.
 TEST_F(MemfileLeaseMgrTest, addGetDelete6) {
 TEST_F(MemfileLeaseMgrTest, addGetDelete6) {
     startBackend(V6);
     startBackend(V6);

+ 1 - 0
src/lib/testutils/dhcp_test_lib.sh.in

@@ -247,6 +247,7 @@ cleanup() {
 
 
     # Remove temporary files.
     # Remove temporary files.
     rm -rf ${LOG_FILE}
     rm -rf ${LOG_FILE}
+    rm -rf ${LEASE_FILE}
     rm -rf ${CFG_FILE}
     rm -rf ${CFG_FILE}
     rm -rf ${KEACTRL_CFG_FILE}
     rm -rf ${KEACTRL_CFG_FILE}
 }
 }