deferred objects are now correctly destructed + added extra checks so that no crashes occur when someone destructs a channel inside a callback function

This commit is contained in:
Emiel Bruijntjes 2014-04-15 12:25:56 +02:00
parent 745ab512a5
commit 301b8153e3
6 changed files with 44 additions and 16 deletions

View File

@ -34,6 +34,7 @@
#include <amqpcpp/receivedframe.h>
#include <amqpcpp/outbuffer.h>
#include <amqpcpp/watchable.h>
#include <amqpcpp/monitor.h>
// amqp types
#include <amqpcpp/field.h>

View File

@ -61,7 +61,7 @@ private:
*
* @var Deferred
*/
Deferred *_oldestCallback = nullptr;
std::unique_ptr<Deferred> _oldestCallback = nullptr;
/**
* Pointer to the newest deferred result (the last one to be added).
@ -464,11 +464,21 @@ public:
{
// skip if there is no oldest callback
if (!_oldestCallback) return;
// we are going to call callbacks that could destruct the channel
Monitor monitor(this);
// call the callback
auto *next = _oldestCallback->reportSuccess(std::forward<Arguments>(parameters)...);
// report to the oldest callback, and install a new oldest callback
_oldestCallback = _oldestCallback->reportSuccess(std::forward<Arguments>(parameters)...);
// leap out if channel no longer exists
if (!monitor.valid()) return;
// @todo destruct oldest callback
// set the oldest callback
_oldestCallback.reset(next);
// if there was no next callback, the newest callback was just used
if (!next) _newestCallback = nullptr;
}
/**
@ -480,19 +490,28 @@ public:
// change state
_state = state_closed;
// @todo multiple callbacks are called, this could break
// we are going to call callbacks that could destruct the channel
Monitor monitor(this);
// @todo should this be a std::string parameter?
// inform handler
if (_errorCallback) _errorCallback(message.c_str());
// leap out if channel is already destructed, or when there are no further callbacks
if (!monitor.valid() || !_oldestCallback) return;
// skip if there is no oldest callback
if (!_oldestCallback) return;
// report to the oldest callback, and install a new oldest callback
_oldestCallback = _oldestCallback->reportError(message);
// @todo destruct oldest callback
// call the callback
auto *next = _oldestCallback->reportError(message);
// leap out if channel no longer exists
if (!monitor.valid()) return;
// set the oldest callback
_oldestCallback.reset(next);
// if there was no next callback, the newest callback was just used
if (!next) _newestCallback = nullptr;
}
/**

View File

@ -54,6 +54,15 @@ protected:
bool _failed;
/**
* The next deferred object
* @return Deferred
*/
Deferred *next() const
{
return _next;
}
/**
* Indicate success
* @return Deferred Next deferred result

View File

@ -86,8 +86,8 @@ ChannelImpl::~ChannelImpl()
// close the channel now
close();
// @todo destruct deferred resutls
// destruct deferred results
while (_oldestCallback) _oldestCallback.reset(_oldestCallback->next());
}
/**
@ -98,7 +98,7 @@ ChannelImpl::~ChannelImpl()
void ChannelImpl::push(Deferred *deferred, const char *error)
{
// do we already have an oldest?
if (!_oldestCallback) _oldestCallback = deferred;
if (!_oldestCallback) _oldestCallback.reset(deferred);
// do we already have a newest?
if (_newestCallback) _newestCallback->add(deferred);

View File

@ -11,7 +11,6 @@
#include "../amqpcpp.h"
// classes that are very commonly used
#include "monitor.h"
#include "exception.h"
#include "protocolexception.h"
#include "frame.h"