]> granicus.if.org Git - icinga2/commitdiff
Implemented exception support for AsyncTasks.
authorGunnar Beutner <gunnar.beutner@netways.de>
Sun, 15 Jul 2012 08:58:03 +0000 (10:58 +0200)
committerGunnar Beutner <gunnar.beutner@netways.de>
Sun, 15 Jul 2012 08:58:03 +0000 (10:58 +0200)
17 files changed:
base/asynctask.cpp
base/asynctask.h
base/base.vcxproj.filters
base/configobject.cpp
base/configobject.h
base/process.cpp
base/process.h
base/scripttask.cpp
base/scripttask.h
cib/configobjectadapter.cpp
cib/configobjectadapter.h
cib/nagioschecktask.cpp
cib/nagioschecktask.h
components/checker/checkercomponent.cpp
components/checker/checkercomponent.h
icinga-app/icinga-standalone.conf
icinga/icingaapplication.cpp

index 229aed342cc422ea0a205b3c64198fc3b7ddb696..c7c0ffe429defda32c7e05c489de2c9d0e3d4f4b 100644 (file)
@@ -1,53 +1,81 @@
-/******************************************************************************
- * Icinga 2                                                                   *
- * Copyright (C) 2012 Icinga Development Team (http://www.icinga.org/)        *
- *                                                                            *
- * This program is free software; you can redistribute it and/or              *
- * modify it under the terms of the GNU General Public License                *
- * as published by the Free Software Foundation; either version 2             *
- * of the License, or (at your option) any later version.                     *
- *                                                                            *
- * This program is distributed in the hope that it will be useful,            *
- * but WITHOUT ANY WARRANTY; without even the implied warranty of             *
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the              *
- * GNU General Public License for more details.                               *
- *                                                                            *
- * You should have received a copy of the GNU General Public License          *
- * along with this program; if not, write to the Free Software Foundation     *
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.             *
- ******************************************************************************/
-
 #include "i2-base.h"
 
 using namespace icinga;
 
-AsyncTask::AsyncTask(const AsyncTask::CompletionCallback& completionCallback)
-       : m_Finished(false), m_CompletionCallback(completionCallback)
+/**
+ * Constructor for the AsyncTaskBase class.
+ */
+AsyncTaskBase::AsyncTaskBase(void)
+       : m_Finished(false), m_ResultRetrieved(false)
 { }
 
-AsyncTask::~AsyncTask(void)
+/**
+ * Destructor for the AsyncTaskBase class.
+ */
+AsyncTaskBase::~AsyncTaskBase(void)
 {
        if (!m_Finished) {
                Logger::Write(LogCritical, "base", "Contract violation: "
-                   "AsyncTask was destroyed before its completion callback was invoked.");
+                       "AsyncTask was destroyed before its completion callback was invoked.");
+       } else if (!m_ResultRetrieved) {
+               Logger::Write(LogCritical, "base", "Contract violation: "
+                       "AsyncTask was destroyed before its result was retrieved.");
        }
 }
 
-void AsyncTask::Start(void)
+/**
+ * Starts the async task. The caller must hold a reference to the AsyncTask
+ * object until the completion callback is invoked.
+ */
+void AsyncTaskBase::Start(void)
 {
        assert(Application::IsMainThread());
 
-       Run();
+       CallWithExceptionGuard(boost::bind(&AsyncTaskBase::Run, this));
+}
+
+/**
+ * Finishes the task using an exception.
+ *
+ * @param ex The exception.
+ */
+void AsyncTaskBase::Finish(const boost::exception_ptr& ex)
+{
+       m_Exception = ex;
+
+       FinishInternal();
 }
 
-void AsyncTask::Finish(void)
+/**
+ * Finishes the task and causes the completion callback to be invoked. This
+ * function must be called before the object is destroyed.
+ */
+void AsyncTaskBase::FinishInternal(void)
 {
-       Event::Post(boost::bind(&AsyncTask::ForwardCallback, static_cast<AsyncTask::Ptr>(GetSelf())));
+       assert(!m_Finished);
+
+       Event::Post(boost::bind(&AsyncTaskBase::InvokeCompletionCallback,
+           static_cast<AsyncTaskBase::Ptr>(GetSelf())));
 }
 
-void AsyncTask::ForwardCallback(void)
+/**
+ * Invokes the provided callback function and catches any exceptions it throws.
+ * Exceptions are stored into the task so that they can be re-thrown when the
+ * task owner calls GetResult().
+ *
+ * @param task The task where exceptions should be saved.
+ * @param function The function that should be invoked.
+ * @returns true if no exception occured, false otherwise.
+ */
+bool AsyncTaskBase::CallWithExceptionGuard(function<void ()> function)
 {
-       m_CompletionCallback(GetSelf());
-       m_CompletionCallback = CompletionCallback();
-       m_Finished = true;
+       try {
+               function();
+
+               return true;
+       } catch (const exception&) {
+               Finish(boost::current_exception());
+
+               return false;
+       }
 }
index 762c037c31b17a2c98f68aaab7b404ab76104516..7569cfab8ed04d9b09676708857297697274bc16 100644 (file)
 namespace icinga
 {
 
-class I2_BASE_API AsyncTask : public Object
+class I2_BASE_API AsyncTaskBase : public Object
 {
 public:
-       typedef shared_ptr<AsyncTask> Ptr;
-       typedef weak_ptr<AsyncTask> WeakPtr;
+       typedef shared_ptr<AsyncTaskBase> Ptr;
+       typedef weak_ptr<AsyncTaskBase> WeakPtr;
 
-       typedef function<void (const AsyncTask::Ptr&)> CompletionCallback;
-
-       AsyncTask(const CompletionCallback& completionCallback);
-       ~AsyncTask(void);
+       AsyncTaskBase(void);
+       ~AsyncTaskBase(void);
 
        void Start(void);
+       void Finish(const boost::exception_ptr& ex);
 
-       void Finish(void);
+       bool CallWithExceptionGuard(function<void ()> function);
 
 protected:
        virtual void Run(void) = 0;
 
+       virtual void InvokeCompletionCallback(void) = 0;
+
+       void FinishInternal(void);
+
+       bool m_Finished; /**< Whether the task is finished. */
+       bool m_ResultRetrieved; /**< Whether the result was retrieved. */
+       boost::exception_ptr m_Exception;
+};
+
+/**
+ * An asynchronous task.
+ *
+ * @ingroup base
+ */
+ template<typename TClass, typename TResult>
+class AsyncTask : public AsyncTaskBase
+{
+public:
+       typedef shared_ptr<AsyncTask<TClass, TResult> > Ptr;
+       typedef weak_ptr<AsyncTask<TClass, TResult> > WeakPtr;
+
+       typedef function<void (const typename shared_ptr<TClass>&)> CompletionCallback;
+
+       /**
+        * Constructor for the AsyncTask class.
+        *
+        * @param completionCallback Function that is called when the task is completed.
+        */
+       AsyncTask(const CompletionCallback& completionCallback)
+               : m_CompletionCallback(completionCallback)
+       { }
+
+       /**
+        * Retrieves the result of the task. Throws an exception if one is stored in
+        * the AsyncTask object.
+        *
+        * @returns The task's result.
+        */
+       TResult GetResult(void)
+       {
+               if (!m_Finished)
+                       throw runtime_error("GetResult called on an unfinished AsyncTask");
+
+               if (m_ResultRetrieved)
+                       throw runtime_error("GetResult called on an AsyncTask whose result was already retrieved.");
+
+               if (m_Exception)
+                       boost::rethrow_exception(m_Exception);
+
+               return m_Result;
+       }
+
+       void Finish(const TResult& result)
+       {
+               m_Result = result;
+               FinishInternal();
+       }
+
 private:
-       void ForwardCallback(void);
+       /**
+        * Used by the Finish method to proxy the completion callback into the main
+        * thread. Invokes the completion callback and marks the task as finished.
+        */
+       virtual void InvokeCompletionCallback(void)
+       {
+               m_Finished = true;
+               m_CompletionCallback(GetSelf());
+
+               /* Clear callback because the bound function might hold a
+                * reference to this task. */
+               m_CompletionCallback = CompletionCallback();
+       }
 
-       bool m_Finished;
-       CompletionCallback m_CompletionCallback;
+       CompletionCallback m_CompletionCallback; /**< The completion callback. */
+       TResult m_Result; /**< The task's result. */
 };
 
 }
index b033e23c37477650672713106ca3e3464ff852e0..d871a78cf826ccea31655d14a2e76fe1665156a8 100644 (file)
     <ClCompile Include="process.cpp">
       <Filter>Quelldateien</Filter>
     </ClCompile>
-    <ClCompile Include="asynctask.cpp">
-      <Filter>Quelldateien</Filter>
-    </ClCompile>
     <ClCompile Include="scriptfunction.cpp">
       <Filter>Quelldateien</Filter>
     </ClCompile>
     <ClCompile Include="scripttask.cpp">
       <Filter>Quelldateien</Filter>
     </ClCompile>
+    <ClCompile Include="asynctask.cpp">
+      <Filter>Quelldateien</Filter>
+    </ClCompile>
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="application.h">
index 14c526ba6f665e2118f5e156b38ff1543ef2d449..bd4f565770b65285e15d5c102d07356c53409d0f 100644 (file)
@@ -198,7 +198,7 @@ void ConfigObject::RemoveTag(const string& key)
 }
 
 ScriptTask::Ptr ConfigObject::InvokeHook(const string& hook,
-    const vector<Variant>& arguments, AsyncTask::CompletionCallback callback)
+    const vector<Variant>& arguments, ScriptTask::CompletionCallback callback)
 {
        Dictionary::Ptr hooks;
        string funcName;
index 293feda8160e9c34ded03b9928f52ae21dc4ccdd..0e9f2213cb407e732eadc07f660fc0e97c58af6f 100644 (file)
@@ -66,7 +66,7 @@ public:
        void RemoveTag(const string& key);
 
        ScriptTask::Ptr InvokeHook(const string& hook,
-           const vector<Variant>& arguments, AsyncTask::CompletionCallback callback);
+           const vector<Variant>& arguments, ScriptTask::CompletionCallback callback);
 
        string GetType(void) const;
        string GetName(void) const;
index 23bde9c518519d1a8c0442f9dcb50f31b31dcb0b..7e92b1cc2fd17af95b647e8b6c4aaf2f34d24262 100644 (file)
@@ -97,7 +97,7 @@ void Process::WorkerThreadProc(void)
                                        it++;
                                        tasks.erase(prev);
 
-                                       task->Finish();
+                                       task->Finish(task->m_Result);
                                } else {
                                        it++;
                                }
@@ -112,9 +112,7 @@ void Process::WorkerThreadProc(void)
 
                        lock.unlock();
 
-                       if (!task->InitTask()) {
-                               task->Finish();
-                       } else {
+                       if (task->CallWithExceptionGuard(boost::bind(&Process::InitTask, task))) {
                                int fd = task->GetFD();
                                if (fd >= 0)
                                        tasks[fd] = task;
@@ -125,9 +123,9 @@ void Process::WorkerThreadProc(void)
        }
 }
 
-bool Process::InitTask(void)
+void Process::InitTask(void)
 {
-       time(&m_ExecutionStart);
+       time(&m_Result.ExecutionStart);
 
 #ifdef _MSC_VER
        m_FP = _popen(m_Command.c_str(), "r");
@@ -138,7 +136,7 @@ bool Process::InitTask(void)
                m_FP = popen_noshell_compat(m_Command.c_str(), "r",
                    (popen_noshell_pass_to_pclose *)m_PCloseArg);
 
-               if (m_FP == NULL) // TODO: add check for valgrind
+               if (m_FP == NULL)
                        m_UsePopen = true;
        }
 
@@ -146,13 +144,8 @@ bool Process::InitTask(void)
                m_FP = popen(m_Command.c_str(), "r");
 #endif /* _MSC_VER */
 
-       if (m_FP == NULL) {
-               m_ExitStatus = 128;
-               m_ExecutionEnd = m_ExecutionStart;
-               return false;
-       }
-
-       return true;
+       if (m_FP == NULL)
+               throw runtime_error("Could not create process.");
 }
 
 bool Process::RunTask(void)
@@ -180,7 +173,7 @@ bool Process::RunTask(void)
        }
 #endif /* _MSC_VER */
 
-       time(&m_ExecutionEnd);
+       time(&m_Result.ExecutionEnd);
 
 #ifndef _MSC_VER
        if (WIFEXITED(status)) {
@@ -204,8 +197,8 @@ bool Process::RunTask(void)
        }
 #endif /* _MSC_VER */
 
-       m_ExitStatus = exitcode;
-       m_Output = output;
+       m_Result.ExitStatus = exitcode;
+       m_Result.Output = output;
 
        return false;
 }
@@ -215,23 +208,3 @@ int Process::GetFD(void) const
        return fileno(m_FP);
 }
 
-long Process::GetExitStatus(void) const
-{
-       return m_ExitStatus;
-}
-
-string Process::GetOutput(void) const
-{
-       return m_Output;
-}
-
-time_t Process::GetExecutionStart(void) const
-{
-       return m_ExecutionStart;
-}
-
-time_t Process::GetExecutionEnd(void) const
-{
-       return m_ExecutionEnd;
-}
-
index ec9bde5b9209edfda23e7cc5692149024561928f..03623f8df5f8a8a537aae02d7c39efdc38400c02 100644 (file)
 namespace icinga
 {
 
-class I2_BASE_API Process : public AsyncTask
+struct ProcessResult
+{
+       time_t ExecutionStart;
+       time_t ExecutionEnd;
+       long ExitStatus;
+       string Output;
+};
+
+class I2_BASE_API Process : public AsyncTask<Process, ProcessResult>
 {
 public:
        typedef shared_ptr<Process> Ptr;
@@ -33,12 +41,6 @@ public:
 
        Process(const string& command, const CompletionCallback& completionCallback);
 
-       time_t GetExecutionStart(void) const;
-       time_t GetExecutionEnd(void) const;
-
-       long GetExitStatus(void) const;
-       string GetOutput(void) const;
-
 private:
        static bool m_ThreadCreated;
 
@@ -51,10 +53,7 @@ private:
        void *m_PCloseArg;
 #endif /* _MSC_VER */
 
-       time_t m_ExecutionStart;
-       time_t m_ExecutionEnd;
-       long m_ExitStatus;
-       string m_Output;
+       ProcessResult m_Result;
 
        virtual void Run(void);
 
@@ -64,7 +63,7 @@ private:
 
        static void WorkerThreadProc(void);
 
-       bool InitTask(void);
+       void InitTask(void);
        bool RunTask(void);
 
        int GetFD(void) const;
index be1bd0f58997869eb20340ade00e1746acabcd84..9956d4e4e1a82ea4869b5ca85f3d9c9710beb34d 100644 (file)
@@ -10,13 +10,3 @@ void ScriptTask::Run(void)
 {
        m_Function->Invoke(GetSelf(), m_Arguments);
 }
-
-void ScriptTask::SetResult(const Variant& result)
-{
-       m_Result = result;
-}
-
-Variant ScriptTask::GetResult(void)
-{
-       return m_Result;
-}
\ No newline at end of file
index 20dbbc822f1c52c2f40348653aabd9de55eee439..a3c933518b689355b07c5107b53c3f8f5676b023 100644 (file)
@@ -23,7 +23,7 @@
 namespace icinga
 {
 
-class I2_BASE_API ScriptTask : public AsyncTask
+class I2_BASE_API ScriptTask : public AsyncTask<ScriptTask, Variant>
 {
 public:
        typedef shared_ptr<ScriptTask> Ptr;
@@ -31,16 +31,12 @@ public:
 
        ScriptTask(const ScriptFunction::Ptr& function, const vector<Variant>& arguments, CompletionCallback callback);
 
-       void SetResult(const Variant& result);
-       Variant GetResult(void);
-
 protected:
        virtual void Run(void);
 
 private:
        ScriptFunction::Ptr m_Function;
        vector<Variant> m_Arguments;
-       Variant m_Result;
 };
 
 }
index eec2c19e7c08bf8b77f12a0493663217bb97939b..4e9153d9a2511ec21c1c216dc31d2e2e8ec5be8e 100644 (file)
@@ -47,7 +47,7 @@ void ConfigObjectAdapter::RemoveTag(const string& key)
 }
 
 ScriptTask::Ptr ConfigObjectAdapter::InvokeHook(const string& hook,
-       const vector<Variant>& arguments, AsyncTask::CompletionCallback callback)
+       const vector<Variant>& arguments, ScriptTask::CompletionCallback callback)
 {
        return m_ConfigObject->InvokeHook(hook, arguments, callback);
 }
\ No newline at end of file
index c4ff5dd181f5c58cbf0036f6171d84cb938ae44f..07dc57c475e667aa080dbac37ce720426981cba6 100644 (file)
@@ -58,7 +58,7 @@ public:
        void RemoveTag(const string& key);
 
        ScriptTask::Ptr InvokeHook(const string& hook,
-           const vector<Variant>& arguments, AsyncTask::CompletionCallback callback);
+           const vector<Variant>& arguments, ScriptTask::CompletionCallback callback);
 
 private:
        ConfigObject::Ptr m_ConfigObject;
index deda3a816c4ee94f416c48757194740659f89326..5174d4da81fd41b6def6ae936b74710ab3b47485 100644 (file)
@@ -53,22 +53,21 @@ void NagiosCheckTask::ScriptFunc(const ScriptTask::Ptr& task, const vector<Varia
        process->Start();
 }
 
-void NagiosCheckTask::ProcessFinishedHandler(const ScriptTask::Ptr& task, const AsyncTask::Ptr& aprocess, CheckResult result)
+void NagiosCheckTask::ProcessFinishedHandler(const ScriptTask::Ptr& task, const Process::Ptr& process, CheckResult result)
 {
-       Process::Ptr process = static_pointer_cast<Process>(aprocess);
+       ProcessResult pr;
+       pr = process->GetResult();
 
-       result.SetExecutionStart(process->GetExecutionStart());
-       result.SetExecutionEnd(process->GetExecutionEnd());
-
-       string output = process->GetOutput();
-       long exitcode = process->GetExitStatus();
+       result.SetExecutionStart(pr.ExecutionStart);
+       result.SetExecutionEnd(pr.ExecutionEnd);
 
+       string output = pr.Output;
        boost::algorithm::trim(output);
        ProcessCheckOutput(result, output);
 
        ServiceState state;
 
-       switch (exitcode) {
+       switch (pr.ExitStatus) {
                case 0:
                        state = StateOK;
                        break;
@@ -89,8 +88,7 @@ void NagiosCheckTask::ProcessFinishedHandler(const ScriptTask::Ptr& task, const
        time(&now);
        result.SetScheduleEnd(now);
 
-       task->SetResult(result.GetDictionary());
-       task->Finish();
+       task->Finish(result.GetDictionary());
 }
 
 void NagiosCheckTask::ProcessCheckOutput(CheckResult& result, const string& output)
@@ -129,5 +127,5 @@ void NagiosCheckTask::ProcessCheckOutput(CheckResult& result, const string& outp
 void NagiosCheckTask::Register(void)
 {
        ScriptFunction::Ptr func = boost::make_shared<ScriptFunction>(&NagiosCheckTask::ScriptFunc);
-       ScriptFunction::Register("builtin::NagiosCheck", func);
+       ScriptFunction::Register("native::NagiosCheck", func);
 }
index f242425141db7b24f0251e84f0bec684f66f2825..483d1d6df7d07ca1e9e8b635225b95412250cd00 100644 (file)
@@ -31,7 +31,7 @@ public:
        static void Register(void);
 
 private:
-       static void ProcessFinishedHandler(const ScriptTask::Ptr& task, const AsyncTask::Ptr& aprocess, CheckResult result);
+       static void ProcessFinishedHandler(const ScriptTask::Ptr& task, const Process::Ptr& process, CheckResult result);
        static void ProcessCheckOutput(CheckResult& result, const string& output);
 };
 
index f402125c6c4e33c361f4d95fb953930f4d950f04..7eabca48760aa4f71651ba064dd1a2220a50bd09 100644 (file)
@@ -99,10 +99,8 @@ void CheckerComponent::CheckTimerHandler(void)
        Logger::Write(LogInformation, "checker", msgbuf.str());
 }
 
-void CheckerComponent::CheckCompletedHandler(Service service, const AsyncTask::Ptr& atask)
+void CheckerComponent::CheckCompletedHandler(Service service, const ScriptTask::Ptr& task)
 {
-       ScriptTask::Ptr task = static_pointer_cast<ScriptTask>(atask);
-
        service.RemoveTag("current_task");
 
        /* if the service isn't in the set of pending services
index ccddc69b9602a9efe033dd6465679262e81f4e86..dd98c64d3ac1d1231760d789b37ff268af26e0fa 100644 (file)
@@ -60,7 +60,7 @@ private:
        void CheckTimerHandler(void);
        void ResultTimerHandler(void);
 
-       void CheckCompletedHandler(Service service, const AsyncTask::Ptr& atask);
+       void CheckCompletedHandler(Service service, const ScriptTask::Ptr& task);
 
        void AdjustCheckTimer(void);
 
index 87ff4dbf913777408704df6ca968bdd56425a515..863cfb383039bd9c3062dfd1ac9eea0eebed9f18 100644 (file)
@@ -41,7 +41,7 @@ object host "localhost" {
 
 abstract object service "nagios-service" {
        hooks = {
-               check = "builtin::NagiosCheck"
+               check = "native::NagiosCheck"
        },
 
        macros = {
@@ -50,7 +50,6 @@ abstract object service "nagios-service" {
 }
 
 abstract object service "ping" inherits "nagios-service" {
-       check_type = "nagios",
        check_command = "$plugindir$/check_ping -H $address$",
        check_interval = 30
 }
index 53e202faff4d4021a920c7882466abdb7fb5ab1d..3ca027a84d2b1c1e72ffc90f7a4fa26145299e80 100644 (file)
@@ -34,12 +34,6 @@ IcingaApplication::IcingaApplication(void)
        : m_PidPath(DefaultPidPath)
 { }
 
-void TestScriptFunc(const ScriptTask::Ptr& task, const vector<Variant>& arguments)
-{
-       std::cout << "Got " << arguments.size() << " arguments." << std::endl;
-       task->Finish();
-}
-
 /**
  * The entry point for the Icinga application.
  *