-/******************************************************************************
- * 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;
+ }
}
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. */
};
}
<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">
}
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;
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;
it++;
tasks.erase(prev);
- task->Finish();
+ task->Finish(task->m_Result);
} else {
it++;
}
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;
}
}
-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");
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;
}
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)
}
#endif /* _MSC_VER */
- time(&m_ExecutionEnd);
+ time(&m_Result.ExecutionEnd);
#ifndef _MSC_VER
if (WIFEXITED(status)) {
}
#endif /* _MSC_VER */
- m_ExitStatus = exitcode;
- m_Output = output;
+ m_Result.ExitStatus = exitcode;
+ m_Result.Output = output;
return false;
}
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;
-}
-
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;
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;
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);
static void WorkerThreadProc(void);
- bool InitTask(void);
+ void InitTask(void);
bool RunTask(void);
int GetFD(void) const;
{
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
namespace icinga
{
-class I2_BASE_API ScriptTask : public AsyncTask
+class I2_BASE_API ScriptTask : public AsyncTask<ScriptTask, Variant>
{
public:
typedef shared_ptr<ScriptTask> Ptr;
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;
};
}
}
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
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;
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;
time(&now);
result.SetScheduleEnd(now);
- task->SetResult(result.GetDictionary());
- task->Finish();
+ task->Finish(result.GetDictionary());
}
void NagiosCheckTask::ProcessCheckOutput(CheckResult& result, const string& output)
void NagiosCheckTask::Register(void)
{
ScriptFunction::Ptr func = boost::make_shared<ScriptFunction>(&NagiosCheckTask::ScriptFunc);
- ScriptFunction::Register("builtin::NagiosCheck", func);
+ ScriptFunction::Register("native::NagiosCheck", func);
}
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);
};
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
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);
abstract object service "nagios-service" {
hooks = {
- check = "builtin::NagiosCheck"
+ check = "native::NagiosCheck"
},
macros = {
}
abstract object service "ping" inherits "nagios-service" {
- check_type = "nagios",
check_command = "$plugindir$/check_ping -H $address$",
check_interval = 30
}
: 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.
*