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 = \ libbase_la_SOURCES = \
application.cpp \ application.cpp \
application.h \ application.h \
asynctask.cpp \
asynctask.h \ asynctask.h \
component.cpp \ component.cpp \
component.h \ 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 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. * An asynchronous task.
* *
* @ingroup base * @ingroup base
*/ */
template<typename TClass, typename TResult> template<typename TClass, typename TResult>
class AsyncTask : public AsyncTaskBase class AsyncTask : public Object
{ {
public: public:
typedef shared_ptr<AsyncTask<TClass, TResult> > Ptr; typedef shared_ptr<AsyncTask<TClass, TResult> > Ptr;
@ -65,13 +39,39 @@ public:
/** /**
* Constructor for the AsyncTask class. * Constructor for the AsyncTask class.
*
* @param completionCallback Function that is called when the task is completed.
*/ */
AsyncTask(const CompletionCallback& completionCallback) AsyncTask(void)
: m_CompletionCallback(completionCallback) : 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 * Retrieves the result of the task. Throws an exception if one is stored in
* the AsyncTask object. * the AsyncTask object.
@ -89,21 +89,66 @@ public:
if (m_Exception) if (m_Exception)
boost::rethrow_exception(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) void Finish(const TResult& result)
{ {
m_Result = result; m_Result = result;
FinishInternal(); 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: private:
/** /**
* Used by the Finish method to proxy the completion callback into the main * Used by the Finish method to proxy the completion callback into the main
* thread. Invokes the completion callback and marks the task as finished. * thread. Invokes the completion callback and marks the task as finished.
*/ */
virtual void InvokeCompletionCallback(void) void InvokeCompletionCallback(void)
{ {
m_Finished = true; m_Finished = true;
m_CompletionCallback(GetSelf()); m_CompletionCallback(GetSelf());
@ -113,8 +158,23 @@ private:
m_CompletionCallback = CompletionCallback(); 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. */ CompletionCallback m_CompletionCallback; /**< The completion callback. */
TResult m_Result; /**< The task's result. */ 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>
<ItemGroup> <ItemGroup>
<ClCompile Include="application.cpp" /> <ClCompile Include="application.cpp" />
<ClCompile Include="asynctask.cpp" />
<ClCompile Include="component.cpp" /> <ClCompile Include="component.cpp" />
<ClCompile Include="configobject.cpp" /> <ClCompile Include="configobject.cpp" />
<ClCompile Include="dictionary.cpp" /> <ClCompile Include="dictionary.cpp" />

View File

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

View File

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

View File

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

View File

@ -27,15 +27,13 @@ vector<Object::Ptr> Object::m_HeldObjects;
* Default constructor for the Object class. * Default constructor for the Object class.
*/ */
Object::Object(void) Object::Object(void)
{ { }
}
/** /**
* Destructor for the Object class. * Destructor for the Object class.
*/ */
Object::~Object(void) Object::~Object(void)
{ { }
}
/** /**
* Temporarily holds onto a reference for an object. This can * 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; deque<Process::Ptr> Process::m_Tasks;
condition_variable Process::m_TasksCV; condition_variable Process::m_TasksCV;
Process::Process(const string& command, const CompletionCallback& completionCallback) Process::Process(const string& command)
: AsyncTask<Process, ProcessResult>(completionCallback), m_Command(command), m_UsePopen(false) : AsyncTask<Process, ProcessResult>(), m_Command(command), m_UsePopen(false)
{ {
if (!m_ThreadCreated) { if (!m_ThreadCreated) {
thread t(&Process::WorkerThreadProc); thread t(&Process::WorkerThreadProc);

View File

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

View File

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

View File

@ -29,7 +29,7 @@ public:
typedef shared_ptr<ScriptTask> Ptr; typedef shared_ptr<ScriptTask> Ptr;
typedef weak_ptr<ScriptTask> WeakPtr; 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: protected:
virtual void Run(void); virtual void Run(void);