mirror of https://github.com/Icinga/icinga2.git
Implemented exception support for AsyncTasks.
This commit is contained in:
parent
e6b99c62a7
commit
3e472012d1
|
@ -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));
|
||||
}
|
||||
|
||||
void AsyncTask::Finish(void)
|
||||
/**
|
||||
* Finishes the task using an exception.
|
||||
*
|
||||
* @param ex The exception.
|
||||
*/
|
||||
void AsyncTaskBase::Finish(const boost::exception_ptr& ex)
|
||||
{
|
||||
Event::Post(boost::bind(&AsyncTask::ForwardCallback, static_cast<AsyncTask::Ptr>(GetSelf())));
|
||||
m_Exception = ex;
|
||||
|
||||
FinishInternal();
|
||||
}
|
||||
|
||||
void AsyncTask::ForwardCallback(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)
|
||||
{
|
||||
m_CompletionCallback(GetSelf());
|
||||
m_CompletionCallback = CompletionCallback();
|
||||
m_Finished = true;
|
||||
assert(!m_Finished);
|
||||
|
||||
Event::Post(boost::bind(&AsyncTaskBase::InvokeCompletionCallback,
|
||||
static_cast<AsyncTaskBase::Ptr>(GetSelf())));
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
try {
|
||||
function();
|
||||
|
||||
return true;
|
||||
} catch (const exception&) {
|
||||
Finish(boost::current_exception());
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,29 +23,98 @@
|
|||
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;
|
||||
|
||||
private:
|
||||
void ForwardCallback(void);
|
||||
virtual void InvokeCompletionCallback(void) = 0;
|
||||
|
||||
bool m_Finished;
|
||||
CompletionCallback m_CompletionCallback;
|
||||
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:
|
||||
/**
|
||||
* 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();
|
||||
}
|
||||
|
||||
CompletionCallback m_CompletionCallback; /**< The completion callback. */
|
||||
TResult m_Result; /**< The task's result. */
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -79,15 +79,15 @@
|
|||
<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">
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -23,7 +23,15 @@
|
|||
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;
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
*
|
||||
|
|
Loading…
Reference in New Issue