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 @@ ...@@ -34,6 +34,7 @@
#include <vector> #include <vector>
#include <memory> #include <memory>
#include <utility> #include <utility>
#include <algorithm>
#include "cppa/option.hpp" #include "cppa/option.hpp"
#include "cppa/config.hpp" #include "cppa/config.hpp"
...@@ -43,70 +44,76 @@ ...@@ -43,70 +44,76 @@
namespace cppa { namespace detail { namespace cppa { namespace detail {
class behavior_stack_help_iterator; struct behavior_stack_mover;
class behavior_stack class behavior_stack
{ {
friend class behavior_stack_help_iterator; friend struct behavior_stack_mover;
behavior_stack(const behavior_stack&) = delete; behavior_stack(const behavior_stack&) = delete;
behavior_stack& operator=(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: public:
behavior_stack() = default; 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() // @pre expected_response.valid()
option<behavior&> sync_handler(message_id_t expected_response); option<behavior&> sync_handler(message_id_t expected_response);
// erases the last asynchronous message handler // erases the last asynchronous message handler
void pop_async_back(); void pop_async_back();
// erases the synchronous response handler associated with @p response_id void clear();
void erase(message_id_t response_id);
void push_back(behavior&& what, // erases the synchronous response handler associated with @p rid
message_id_t expected_response = message_id_t()); 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> template<class Policy, class Client>
bool invoke(Policy& policy, Client* client, recursive_queue_node* node) { bool invoke(Policy& policy, Client* client, recursive_queue_node* node) {
CPPA_REQUIRE(!m_elements.empty()); CPPA_REQUIRE(!empty());
CPPA_REQUIRE(client != nullptr); CPPA_REQUIRE(client != nullptr);
CPPA_REQUIRE(node != nullptr); CPPA_REQUIRE(node != nullptr);
// use a copy, because the invoked behavior might change m_elements // use a copy of bhvr, because invoked behavior might change m_elements
behavior what = m_elements.back().first;
auto id = m_elements.back().second; 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 // try to match cached messages
do { do {
// remove synchronous response handler if needed // remove synchronous response handler if needed
if (id.valid()) { if (id.valid()) {
auto last = m_elements.end(); erase_if([id](const element_type& value) {
auto i = std::find_if(m_elements.begin(), last, return id == value.second;
[id](element_type& e) { });
return id == e.second; }
}); if (!empty()) {
if (i != last) { id = m_elements.back().second;
m_erased_elements.emplace_back(std::move(i->first)); bhvr = m_elements.back().first;
m_elements.erase(i); repeat = policy.invoke_from_cache(client, bhvr, id);
}
} }
id = empty() ? message_id_t() : m_elements.back().second; else repeat = false;
} while (!empty() && policy.invoke_from_cache(client, back(), id)); } while (repeat);
return true; return true;
} }
return false; return false;
...@@ -125,6 +132,29 @@ class behavior_stack ...@@ -125,6 +132,29 @@ class behavior_stack
std::vector<element_type> m_elements; std::vector<element_type> m_elements;
std::vector<behavior> m_erased_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 } } // namespace cppa::detail
......
...@@ -29,30 +29,30 @@ ...@@ -29,30 +29,30 @@
#include <iterator> #include <iterator>
#include "cppa/local_actor.hpp" #include "cppa/local_actor.hpp"
#include "cppa/detail/behavior_stack.hpp" #include "cppa/detail/behavior_stack.hpp"
using namespace std;
namespace cppa { namespace detail { namespace cppa { namespace detail {
class behavior_stack_help_iterator struct behavior_stack_mover : iterator<output_iterator_tag,void,void,void,void>{
: public std::iterator<std::output_iterator_tag, void, void, void, void> {
public: 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_mover& operator=(behavior_stack::element_type&& rval) {
m_stack->m_erased_elements.emplace_back(move(rval.first));
behavior_stack_help_iterator& operator=(element_type&& rval) {
m_stack->m_erased_elements.emplace_back(std::move(rval.first));
return *this; 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: private:
...@@ -60,10 +60,12 @@ class behavior_stack_help_iterator ...@@ -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) { option<behavior&> behavior_stack::sync_handler(message_id_t expected_response) {
if (expected_response.valid()) { if (expected_response.valid()) {
auto e = m_elements.rend(); 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; return val.second == expected_response;
}); });
if (i != e) return i->first; if (i != e) return i->first;
...@@ -71,55 +73,19 @@ option<behavior&> behavior_stack::sync_handler(message_id_t expected_response) { ...@@ -71,55 +73,19 @@ option<behavior&> behavior_stack::sync_handler(message_id_t expected_response) {
return {}; return {};
} }
void behavior_stack::pop_async_back() { void behavior_stack::pop_async_back() {
if (m_elements.empty()) { if (m_elements.empty()) { } // nothing to do
// nothing to do else if (!m_elements.back().second.valid()) erase_at(m_elements.end() - 1);
} else rerase_if([](const element_type& e) {
else if (m_elements.back().second.valid() == false) { return e.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 (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() { void behavior_stack::clear() {
if (m_elements.empty() == false) { if (m_elements.empty() == false) {
std::move(m_elements.begin(), m_elements.end(), move(m_elements.begin(), m_elements.end(), move_iter(this));
behavior_stack_help_iterator{*this});
m_elements.clear(); m_elements.clear();
} }
} }
void behavior_stack::cleanup() {
m_erased_elements.clear();
}
} } // namespace cppa::detail } } // 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