Commit 2c7f5b80 authored by Dominik Charousset's avatar Dominik Charousset

bugfix for `detail::behavior_stack` + maintenance

this patch fixes an issue in `behavior_stack`: in case handlers are
invoked from cache, the behavior is not copied beforehand
parent da2077d8
......@@ -34,6 +34,7 @@
#include <vector>
#include <memory>
#include <utility>
#include <algorithm>
#include "cppa/option.hpp"
#include "cppa/config.hpp"
......@@ -43,70 +44,76 @@
namespace cppa { namespace detail {
class behavior_stack_help_iterator;
struct behavior_stack_mover;
class behavior_stack
{
friend class behavior_stack_help_iterator;
friend struct behavior_stack_mover;
behavior_stack(const behavior_stack&) = delete;
behavior_stack& operator=(const behavior_stack&) = delete;
typedef std::pair<behavior, message_id_t> element_type;
typedef std::pair<behavior,message_id_t> element_type;
public:
behavior_stack() = default;
inline bool empty() const { return m_elements.empty(); }
inline behavior& back() {
CPPA_REQUIRE(!empty());
return m_elements.back().first;
}
// @pre expected_response.valid()
option<behavior&> sync_handler(message_id_t expected_response);
// erases the last asynchronous message handler
void pop_async_back();
// erases the synchronous response handler associated with @p response_id
void erase(message_id_t response_id);
void clear();
void push_back(behavior&& what,
message_id_t expected_response = message_id_t());
// erases the synchronous response handler associated with @p rid
void erase(message_id_t rid) {
erase_if([=](const element_type& e) { return e.second == rid; });
}
void cleanup();
inline bool empty() const { return m_elements.empty(); }
void clear();
inline behavior& back() {
CPPA_REQUIRE(!empty());
return m_elements.back().first;
}
inline void push_back(behavior&& what,
message_id_t response_id = message_id_t::invalid) {
m_elements.emplace_back(std::move(what), response_id);
}
inline void cleanup() {
m_erased_elements.clear();
}
template<class Policy, class Client>
bool invoke(Policy& policy, Client* client, recursive_queue_node* node) {
CPPA_REQUIRE(!m_elements.empty());
CPPA_REQUIRE(!empty());
CPPA_REQUIRE(client != nullptr);
CPPA_REQUIRE(node != nullptr);
// use a copy, because the invoked behavior might change m_elements
behavior what = m_elements.back().first;
// use a copy of bhvr, because invoked behavior might change m_elements
auto id = m_elements.back().second;
if (policy.invoke(client, node, what, id)) {
auto bhvr = m_elements.back().first;
if (policy.invoke(client, node, bhvr, id)) {
bool repeat;
// try to match cached messages
do {
// remove synchronous response handler if needed
if (id.valid()) {
auto last = m_elements.end();
auto i = std::find_if(m_elements.begin(), last,
[id](element_type& e) {
return id == e.second;
});
if (i != last) {
m_erased_elements.emplace_back(std::move(i->first));
m_elements.erase(i);
}
erase_if([id](const element_type& value) {
return id == value.second;
});
}
if (!empty()) {
id = m_elements.back().second;
bhvr = m_elements.back().first;
repeat = policy.invoke_from_cache(client, bhvr, id);
}
id = empty() ? message_id_t() : m_elements.back().second;
} while (!empty() && policy.invoke_from_cache(client, back(), id));
else repeat = false;
} while (repeat);
return true;
}
return false;
......@@ -125,6 +132,29 @@ class behavior_stack
std::vector<element_type> m_elements;
std::vector<behavior> m_erased_elements;
// note: checks wheter i points to m_elements.end() before calling erase()
inline void erase_at(std::vector<element_type>::iterator i) {
if (i != m_elements.end()) {
m_erased_elements.emplace_back(std::move(i->first));
m_elements.erase(i);
}
}
inline void erase_at(std::vector<element_type>::reverse_iterator i) {
// base iterator points to the element *after* the correct element
if (i != m_elements.rend()) erase_at(i.base() - 1);
}
template<typename UnaryPredicate>
inline void erase_if(UnaryPredicate p) {
erase_at(std::find_if(m_elements.begin(), m_elements.end(), p));
}
template<typename UnaryPredicate>
inline void rerase_if(UnaryPredicate p) {
erase_at(std::find_if(m_elements.rbegin(), m_elements.rend(), p));
}
};
} } // namespace cppa::detail
......
......@@ -29,30 +29,30 @@
#include <iterator>
#include "cppa/local_actor.hpp"
#include "cppa/detail/behavior_stack.hpp"
using namespace std;
namespace cppa { namespace detail {
class behavior_stack_help_iterator
: public std::iterator<std::output_iterator_tag, void, void, void, void> {
struct behavior_stack_mover : iterator<output_iterator_tag,void,void,void,void>{
public:
typedef behavior_stack::element_type element_type;
behavior_stack_mover(behavior_stack* st) : m_stack(st) { }
explicit behavior_stack_help_iterator(behavior_stack& st) : m_stack(&st) { }
behavior_stack_help_iterator& operator=(element_type&& rval) {
m_stack->m_erased_elements.emplace_back(std::move(rval.first));
behavior_stack_mover& operator=(behavior_stack::element_type&& rval) {
m_stack->m_erased_elements.emplace_back(move(rval.first));
return *this;
}
behavior_stack_help_iterator& operator*() { return *this; }
behavior_stack_mover& operator*() { return *this; }
behavior_stack_help_iterator& operator++() { return *this; }
behavior_stack_mover& operator++() { return *this; }
behavior_stack_help_iterator operator++(int) { return *this; }
behavior_stack_mover operator++(int) { return *this; }
private:
......@@ -60,10 +60,12 @@ class behavior_stack_help_iterator
};
inline behavior_stack_mover move_iter(behavior_stack* bs) { return {bs}; }
option<behavior&> behavior_stack::sync_handler(message_id_t expected_response) {
if (expected_response.valid()) {
auto e = m_elements.rend();
auto i = std::find_if(m_elements.rbegin(), e, [=](element_type& val) {
auto i = find_if(m_elements.rbegin(), e, [=](element_type& val) {
return val.second == expected_response;
});
if (i != e) return i->first;
......@@ -71,55 +73,19 @@ option<behavior&> behavior_stack::sync_handler(message_id_t expected_response) {
return {};
}
void behavior_stack::pop_async_back() {
if (m_elements.empty()) {
// nothing to do
}
else if (m_elements.back().second.valid() == false) {
m_erased_elements.emplace_back(std::move(m_elements.back().first));
m_elements.pop_back();
}
else {
auto rlast = m_elements.rend();
auto ri = std::find_if(m_elements.rbegin(), rlast,
[](element_type& e) {
return e.second.valid() == false;
});
if (ri != rlast) {
m_erased_elements.emplace_back(std::move(ri->first));
auto i = ri.base();
--i; // adjust 'normal' iterator to point to the correct element
m_elements.erase(i);
}
}
}
void behavior_stack::erase(message_id_t response_id) {
auto last = m_elements.end();
auto i = std::find_if(m_elements.begin(), last, [=](element_type& e) {
return e.second == response_id;
if (m_elements.empty()) { } // nothing to do
else if (!m_elements.back().second.valid()) erase_at(m_elements.end() - 1);
else rerase_if([](const element_type& e) {
return e.second.valid() == false;
});
if (i != last) {
m_erased_elements.emplace_back(std::move(i->first));
m_elements.erase(i);
}
}
void behavior_stack::push_back(behavior&& what, message_id_t response_id) {
m_elements.emplace_back(std::move(what), response_id);
}
void behavior_stack::clear() {
if (m_elements.empty() == false) {
std::move(m_elements.begin(), m_elements.end(),
behavior_stack_help_iterator{*this});
move(m_elements.begin(), m_elements.end(), move_iter(this));
m_elements.clear();
}
}
void behavior_stack::cleanup() {
m_erased_elements.clear();
}
} } // namespace cppa::detail
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment