Bugfixes for the AsyncTask class.

This commit is contained in:
Gunnar Beutner 2012-07-15 17:15:49 +02:00
parent 310edc1074
commit 18bbde330c
12 changed files with 104 additions and 132 deletions

View File

@ -7,7 +7,6 @@ pkglib_LTLIBRARIES = \
libbase_la_SOURCES = \
application.cpp \
application.h \
asynctask.cpp \
asynctask.h \
component.cpp \
component.h \

View File

@ -1,81 +0,0 @@
#include "i2-base.h"
using namespace icinga;
/**
* Constructor for the AsyncTaskBase class.
*/
AsyncTaskBase::AsyncTaskBase(void)
: m_Finished(false), m_ResultRetrieved(false)
{ }
/**
* 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.");
} else if (!m_ResultRetrieved) {
Logger::Write(LogCritical, "base", "Contract violation: "
"AsyncTask was destroyed before its result was retrieved.");
}
}
/**
* 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());
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();
}
/**
* 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)
{
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;
}
}

View File

@ -23,39 +23,13 @@
namespace icinga
{
class I2_BASE_API AsyncTaskBase : public Object
{
public:
typedef shared_ptr<AsyncTaskBase> Ptr;
typedef weak_ptr<AsyncTaskBase> WeakPtr;
AsyncTaskBase(void);
~AsyncTaskBase(void);
void Start(void);
void Finish(const boost::exception_ptr& ex);
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
class AsyncTask : public Object
{
public:
typedef shared_ptr<AsyncTask<TClass, TResult> > Ptr;
@ -65,13 +39,39 @@ public:
/**
* Constructor for the AsyncTask class.
*
* @param completionCallback Function that is called when the task is completed.
*/
AsyncTask(const CompletionCallback& completionCallback)
: m_CompletionCallback(completionCallback)
AsyncTask(void)
: m_Finished(false), m_ResultRetrieved(false)
{ }
/**
* Destructor for the AsyncTask class.
*/
~AsyncTask(void)
{
if (!m_Finished) {
Logger::Write(LogCritical, "base", "Contract violation: "
"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.");
}
}
/**
* Starts the async task. The caller must hold a reference to the AsyncTask
* object until the completion callback is invoked.
*/
void Start(const CompletionCallback& completionCallback)
{
assert(Application::IsMainThread());
m_CompletionCallback = completionCallback;
CallWithExceptionGuard(boost::bind(&AsyncTask<TClass, TResult>::Run, this));
}
/**
* Retrieves the result of the task. Throws an exception if one is stored in
* the AsyncTask object.
@ -89,21 +89,66 @@ public:
if (m_Exception)
boost::rethrow_exception(m_Exception);
return m_Result;
m_ResultRetrieved = true;
TResult result;
std::swap(m_Result, result);
return result;
}
/**
* Finishes the task using an exception.
*
* @param ex The exception.
*/
void Finish(const boost::exception_ptr& ex)
{
m_Exception = ex;
FinishInternal();
}
/**
* Finishes the task using an ordinary result.
*
* @param result The result.
*/
void Finish(const TResult& result)
{
m_Result = result;
FinishInternal();
}
/**
* 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 CallWithExceptionGuard(function<void ()> function)
{
try {
function();
return true;
} catch (const exception&) {
Finish(boost::current_exception());
return false;
}
}
protected:
virtual void Run(void) = 0;
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)
void InvokeCompletionCallback(void)
{
m_Finished = true;
m_CompletionCallback(GetSelf());
@ -113,8 +158,23 @@ private:
m_CompletionCallback = CompletionCallback();
}
/**
* Finishes the task and causes the completion callback to be invoked. This
* function must be called before the object is destroyed.
*/
void FinishInternal(void)
{
assert(!m_Finished);
Event::Post(boost::bind(&AsyncTask<TClass, TResult>::InvokeCompletionCallback, this));
}
CompletionCallback m_CompletionCallback; /**< The completion callback. */
TResult m_Result; /**< The task's result. */
boost::exception_ptr m_Exception; /**< The task's exception. */
bool m_Finished; /**< Whether the task is finished. */
bool m_ResultRetrieved; /**< Whether the result was retrieved. */
};
}

View File

@ -12,7 +12,6 @@
</ItemGroup>
<ItemGroup>
<ClCompile Include="application.cpp" />
<ClCompile Include="asynctask.cpp" />
<ClCompile Include="component.cpp" />
<ClCompile Include="configobject.cpp" />
<ClCompile Include="dictionary.cpp" />

View File

@ -85,9 +85,6 @@
<ClCompile Include="scripttask.cpp">
<Filter>Quelldateien</Filter>
</ClCompile>
<ClCompile Include="asynctask.cpp">
<Filter>Quelldateien</Filter>
</ClCompile>
<ClCompile Include="i2-base.cpp">
<Filter>Quelldateien</Filter>
</ClCompile>

View File

@ -210,8 +210,8 @@ ScriptTask::Ptr ConfigObject::InvokeHook(const string& hook,
if (!func)
throw invalid_argument("Function '" + funcName + "' does not exist.");
ScriptTask::Ptr task = boost::make_shared<ScriptTask>(func, arguments, callback);
task->Start();
ScriptTask::Ptr task = boost::make_shared<ScriptTask>(func, arguments);
task->Start(callback);
return task;
}

View File

@ -167,6 +167,7 @@ using boost::system_time;
#include "tcpclient.h"
#include "tcpserver.h"
#include "tlsclient.h"
#include "logger.h"
#include "asynctask.h"
#include "process.h"
#include "scriptfunction.h"
@ -177,7 +178,6 @@ using boost::system_time;
#include "application.h"
#include "component.h"
#include "threadpool.h"
#include "logger.h"
#include "streamlogger.h"
#include "sysloglogger.h"

View File

@ -27,15 +27,13 @@ vector<Object::Ptr> Object::m_HeldObjects;
* Default constructor for the Object class.
*/
Object::Object(void)
{
}
{ }
/**
* Destructor for the Object class.
*/
Object::~Object(void)
{
}
{ }
/**
* Temporarily holds onto a reference for an object. This can

View File

@ -30,8 +30,8 @@ boost::mutex Process::m_Mutex;
deque<Process::Ptr> Process::m_Tasks;
condition_variable Process::m_TasksCV;
Process::Process(const string& command, const CompletionCallback& completionCallback)
: AsyncTask<Process, ProcessResult>(completionCallback), m_Command(command), m_UsePopen(false)
Process::Process(const string& command)
: AsyncTask<Process, ProcessResult>(), m_Command(command), m_UsePopen(false)
{
if (!m_ThreadCreated) {
thread t(&Process::WorkerThreadProc);

View File

@ -39,7 +39,7 @@ public:
static const int MaxTasksPerThread = 128;
Process(const string& command, const CompletionCallback& completionCallback);
Process(const string& command);
private:
static bool m_ThreadCreated;

View File

@ -2,8 +2,8 @@
using namespace icinga;
ScriptTask::ScriptTask(const ScriptFunction::Ptr& function, const vector<Variant>& arguments, CompletionCallback callback)
: AsyncTask<ScriptTask, Variant>(callback), m_Function(function), m_Arguments(arguments)
ScriptTask::ScriptTask(const ScriptFunction::Ptr& function, const vector<Variant>& arguments)
: AsyncTask<ScriptTask, Variant>(), m_Function(function), m_Arguments(arguments)
{ }
void ScriptTask::Run(void)

View File

@ -29,7 +29,7 @@ public:
typedef shared_ptr<ScriptTask> Ptr;
typedef weak_ptr<ScriptTask> WeakPtr;
ScriptTask(const ScriptFunction::Ptr& function, const vector<Variant>& arguments, CompletionCallback callback);
ScriptTask(const ScriptFunction::Ptr& function, const vector<Variant>& arguments);
protected:
virtual void Run(void);