Commit b23bf288 authored by Dominik Charousset's avatar Dominik Charousset

Allow actors to consume all upstream msgs at once

parent 8ec58ee2
...@@ -67,6 +67,14 @@ public: ...@@ -67,6 +67,14 @@ public:
return x + x; return x + x;
} }
template <template <class> class Queue>
static deficit_type
quantum(const Queue<upstream_messages>& q, deficit_type) noexcept {
// Allow actors to consume *all* upstream messages. They are lightweight by
// design and require little processing.
return q.total_task_size();
}
template <class Queue> template <class Queue>
static deficit_type quantum(const Queue&, deficit_type x) noexcept { static deficit_type quantum(const Queue&, deficit_type x) noexcept {
return x; return x;
......
...@@ -200,52 +200,6 @@ public: ...@@ -200,52 +200,6 @@ public:
using exception_handler = std::function<error(pointer, std::exception_ptr&)>; using exception_handler = std::function<error(pointer, std::exception_ptr&)>;
#endif // CAF_ENABLE_EXCEPTIONS #endif // CAF_ENABLE_EXCEPTIONS
/// Consumes messages from the mailbox.
struct mailbox_visitor {
scheduled_actor* self;
size_t& handled_msgs;
size_t max_throughput;
bool collect_metrics;
/// Consumes upstream messages.
intrusive::task_result operator()(size_t, upstream_queue&,
mailbox_element&);
/// Consumes downstream messages.
intrusive::task_result
operator()(size_t, downstream_queue&, stream_slot slot,
policy::downstream_messages::nested_queue_type&,
mailbox_element&);
// Dispatches asynchronous messages with high and normal priority to the
// same handler.
template <class Queue>
intrusive::task_result operator()(size_t, Queue&, mailbox_element& x) {
return (*this)(x);
}
// Consumes asynchronous messages.
intrusive::task_result operator()(mailbox_element& x);
template <class F>
intrusive::task_result run(mailbox_element& x, F body) {
if (collect_metrics) {
auto t0 = std::chrono::steady_clock::now();
auto mbox_time = x.seconds_until(t0);
auto res = body();
if (res != intrusive::task_result::skip) {
auto& builtins = self->builtin_metrics();
telemetry::timer::observe(builtins.processing_time, t0);
builtins.mailbox_time->observe(mbox_time);
builtins.mailbox_size->dec();
}
return res;
} else {
return body();
}
}
};
// -- static helper functions ------------------------------------------------ // -- static helper functions ------------------------------------------------
static void default_error_handler(pointer ptr, error& x); static void default_error_handler(pointer ptr, error& x);
...@@ -517,6 +471,9 @@ public: ...@@ -517,6 +471,9 @@ public:
/// Pushes `ptr` to the cache of the default queue. /// Pushes `ptr` to the cache of the default queue.
void push_to_cache(mailbox_element_ptr ptr); void push_to_cache(mailbox_element_ptr ptr);
/// Returns the queue of the mailbox that stores high priority messages.
urgent_queue& get_urgent_queue();
/// Returns the default queue of the mailbox that stores ordinary messages. /// Returns the default queue of the mailbox that stores ordinary messages.
normal_queue& get_normal_queue(); normal_queue& get_normal_queue();
...@@ -526,9 +483,6 @@ public: ...@@ -526,9 +483,6 @@ public:
/// Returns the queue of the mailbox that stores `downstream_msg` messages. /// Returns the queue of the mailbox that stores `downstream_msg` messages.
downstream_queue& get_downstream_queue(); downstream_queue& get_downstream_queue();
/// Returns the queue of the mailbox that stores high priority messages.
urgent_queue& get_urgent_queue();
// -- inbound_path management ------------------------------------------------ // -- inbound_path management ------------------------------------------------
/// Creates a new path for incoming stream traffic from `sender`. /// Creates a new path for incoming stream traffic from `sender`.
...@@ -569,8 +523,7 @@ public: ...@@ -569,8 +523,7 @@ public:
return; return;
} }
CAF_LOG_INFO("no manager found:" << CAF_ARG(slots)); CAF_LOG_INFO("no manager found:" << CAF_ARG(slots));
// TODO: replace with `if constexpr` when switching to C++17 if constexpr (std::is_same<T, upstream_msg::ack_batch>::value) {
if (std::is_same<T, upstream_msg::ack_batch>::value) {
// Make sure the other actor does not falsely believe us a source. // Make sure the other actor does not falsely believe us a source.
inbound_path::emit_irregular_shutdown(this, slots, current_sender(), inbound_path::emit_irregular_shutdown(this, slots, current_sender(),
sec::invalid_upstream); sec::invalid_upstream);
...@@ -657,6 +610,12 @@ public: ...@@ -657,6 +610,12 @@ public:
/// Removes the stream manager mapped to `id` in `O(log n)`. /// Removes the stream manager mapped to `id` in `O(log n)`.
void erase_pending_stream_manager(stream_slot id); void erase_pending_stream_manager(stream_slot id);
/// Moves a pending stream manager to the list of active stream managers.
/// @returns `true` and a pointer to the moved stream manager on success,
/// `false` and `nullptr` otherwise.
[[nodiscard]] std::pair<bool, stream_manager*>
ack_pending_stream_manager(stream_slot id);
/// Removes all entries for `mgr` in `O(n)`. /// Removes all entries for `mgr` in `O(n)`.
void erase_stream_manager(const stream_manager_ptr& mgr); void erase_stream_manager(const stream_manager_ptr& mgr);
...@@ -683,6 +642,10 @@ public: ...@@ -683,6 +642,10 @@ public:
return max_batch_delay_; return max_batch_delay_;
} }
void active_stream_managers(std::vector<stream_manager*>& result);
std::vector<stream_manager*> active_stream_managers();
/// @endcond /// @endcond
protected: protected:
...@@ -746,7 +709,23 @@ protected: ...@@ -746,7 +709,23 @@ protected:
exception_handler exception_handler_; exception_handler exception_handler_;
#endif // CAF_ENABLE_EXCEPTIONS #endif // CAF_ENABLE_EXCEPTIONS
/// @endcond private:
template <class F>
intrusive::task_result run_with_metrics(mailbox_element& x, F body) {
if (metrics_.mailbox_time) {
auto t0 = std::chrono::steady_clock::now();
auto mbox_time = x.seconds_until(t0);
auto res = body();
if (res != intrusive::task_result::skip) {
telemetry::timer::observe(metrics_.processing_time, t0);
metrics_.mailbox_time->observe(mbox_time);
metrics_.mailbox_size->dec();
}
return res;
} else {
return body();
}
}
}; };
} // namespace caf } // namespace caf
...@@ -113,8 +113,6 @@ void inbound_path::handle(downstream_msg::batch& batch) { ...@@ -113,8 +113,6 @@ void inbound_path::handle(downstream_msg::batch& batch) {
if (auto available = available_credit(); available >= desired_batch_size) if (auto available = available_credit(); available >= desired_batch_size)
if (auto acquired = mgr->acquire_credit(this, available); acquired > 0) if (auto acquired = mgr->acquire_credit(this, available); acquired > 0)
emit_ack_batch(self(), acquired); emit_ack_batch(self(), acquired);
// FIXME: move this up to the actor
mgr->push();
} }
void inbound_path::tick(time_point now, duration_type max_batch_delay) { void inbound_path::tick(time_point now, duration_type max_batch_delay) {
......
This diff is collapsed.
...@@ -92,15 +92,13 @@ bool stream_manager::handle(stream_slots slots, upstream_msg::ack_open& x) { ...@@ -92,15 +92,13 @@ bool stream_manager::handle(stream_slots slots, upstream_msg::ack_open& x) {
CAF_ASSERT(ptr->open_credit >= 0); CAF_ASSERT(ptr->open_credit >= 0);
ptr->set_desired_batch_size(x.desired_batch_size); ptr->set_desired_batch_size(x.desired_batch_size);
--pending_handshakes_; --pending_handshakes_;
push();
return true; return true;
} }
void stream_manager::handle(stream_slots slots, upstream_msg::ack_batch& x) { void stream_manager::handle(stream_slots slots, upstream_msg::ack_batch& x) {
CAF_LOG_TRACE(CAF_ARG(slots) << CAF_ARG(x)); CAF_LOG_TRACE(CAF_ARG(slots) << CAF_ARG(x));
CAF_ASSERT(x.desired_batch_size > 0); CAF_ASSERT(x.desired_batch_size > 0);
auto path = out().path(slots.receiver); if (auto path = out().path(slots.receiver); path != nullptr) {
if (path != nullptr) {
path->open_credit += x.new_capacity; path->open_credit += x.new_capacity;
CAF_ASSERT(path->open_credit >= 0); CAF_ASSERT(path->open_credit >= 0);
path->set_desired_batch_size(x.desired_batch_size); path->set_desired_batch_size(x.desired_batch_size);
...@@ -108,7 +106,6 @@ void stream_manager::handle(stream_slots slots, upstream_msg::ack_batch& x) { ...@@ -108,7 +106,6 @@ void stream_manager::handle(stream_slots slots, upstream_msg::ack_batch& x) {
// Gravefully remove path after receiving its final ACK. // Gravefully remove path after receiving its final ACK.
if (path->closing && out().clean(slots.receiver)) if (path->closing && out().clean(slots.receiver))
out().remove_path(slots.receiver, none, false); out().remove_path(slots.receiver, none, false);
push();
} }
} }
...@@ -300,9 +297,11 @@ stream_manager::add_unchecked_inbound_path_impl(type_id_t input_type, ...@@ -300,9 +297,11 @@ stream_manager::add_unchecked_inbound_path_impl(type_id_t input_type,
} }
void stream_manager::tick(time_point now) { void stream_manager::tick(time_point now) {
for (auto path : inbound_paths_) do {
path->tick(now, max_batch_delay_); out().tick(now, max_batch_delay_);
out().tick(now, max_batch_delay_); for (auto path : inbound_paths_)
path->tick(now, max_batch_delay_);
} while (generate_messages());
} }
stream_slot stream_manager::assign_next_slot() { stream_slot stream_manager::assign_next_slot() {
......
...@@ -49,37 +49,39 @@ TESTEE_STATE(file_reader) { ...@@ -49,37 +49,39 @@ TESTEE_STATE(file_reader) {
}; };
VARARGS_TESTEE(file_reader, size_t buf_size) { VARARGS_TESTEE(file_reader, size_t buf_size) {
return {[=](string& fname) -> result<stream<int32_t>, string> { return {
CAF_CHECK_EQUAL(fname, "numbers.txt"); [=](string& fname) -> result<stream<int32_t>, string> {
CAF_CHECK_EQUAL(self->mailbox().empty(), true); CAF_CHECK_EQUAL(fname, "numbers.txt");
return attach_stream_source( CAF_CHECK_EQUAL(self->mailbox().empty(), true);
self, return attach_stream_source(
// forward file name in handshake to next stage self,
std::forward_as_tuple(std::move(fname)), // forward file name in handshake to next stage
// initialize state std::forward_as_tuple(std::move(fname)),
[=](unit_t&) { // initialize state
auto& xs = self->state.buf; [=](unit_t&) {
xs.resize(buf_size); auto& xs = self->state.buf;
std::iota(xs.begin(), xs.end(), 1); xs.resize(buf_size);
}, std::iota(xs.begin(), xs.end(), 1);
// get next element },
[=](unit_t&, downstream<int32_t>& out, size_t num) { // get next element
auto& xs = self->state.buf; [=](unit_t&, downstream<int32_t>& out, size_t num) {
CAF_MESSAGE("push " << num << " messages downstream"); auto& xs = self->state.buf;
auto n = std::min(num, xs.size()); CAF_MESSAGE("push " << num << " messages downstream");
for (size_t i = 0; i < n; ++i) auto n = std::min(num, xs.size());
out.push(xs[i]); for (size_t i = 0; i < n; ++i)
xs.erase(xs.begin(), xs.begin() + static_cast<ptrdiff_t>(n)); out.push(xs[i]);
}, xs.erase(xs.begin(), xs.begin() + static_cast<ptrdiff_t>(n));
// check whether we reached the end },
[=](const unit_t&) { // check whether we reached the end
if (self->state.buf.empty()) { [=](const unit_t&) {
CAF_MESSAGE(self->name() << " is done"); if (self->state.buf.empty()) {
return true; CAF_MESSAGE(self->name() << " is done");
} return true;
return false; }
}); return false;
}}; });
},
};
} }
TESTEE_STATE(sum_up) { TESTEE_STATE(sum_up) {
...@@ -87,26 +89,28 @@ TESTEE_STATE(sum_up) { ...@@ -87,26 +89,28 @@ TESTEE_STATE(sum_up) {
}; };
TESTEE(sum_up) { TESTEE(sum_up) {
return {[=](stream<int32_t>& in, const string& fname) { return {
CAF_CHECK_EQUAL(fname, "numbers.txt"); [=](stream<int32_t>& in, const string& fname) {
using int_ptr = int32_t*; CAF_CHECK_EQUAL(fname, "numbers.txt");
return attach_stream_sink( using int_ptr = int32_t*;
self, return attach_stream_sink(
// input stream self,
in, // input stream
// initialize state in,
[=](int_ptr& x) { x = &self->state.x; }, // initialize state
// processing step [=](int_ptr& x) { x = &self->state.x; },
[](int_ptr& x, int32_t y) { *x += y; }, // processing step
// cleanup [](int_ptr& x, int32_t y) { *x += y; },
[=](int_ptr&, const error&) { // cleanup
CAF_MESSAGE(self->name() << " is done"); [=](int_ptr&, const error&) {
}); CAF_MESSAGE(self->name() << " is done");
}, });
[=](join_atom atm, actor src) { },
CAF_MESSAGE(self->name() << " joins a stream"); [=](join_atom atm, actor src) {
self->send(self * src, atm); CAF_MESSAGE(self->name() << " joins a stream");
}}; self->send(self * src, atm);
},
};
} }
TESTEE_STATE(stream_multiplexer) { TESTEE_STATE(stream_multiplexer) {
......
...@@ -314,7 +314,7 @@ public: ...@@ -314,7 +314,7 @@ public:
kvp.second->tick(now()); kvp.second->tick(now());
} }
virtual bool add_inbound_path(type_id_t input_type, virtual bool add_inbound_path(type_id_t,
std::unique_ptr<inbound_path> path) override { std::unique_ptr<inbound_path> path) override {
using policy_type = policy::downstream_messages::nested; using policy_type = policy::downstream_messages::nested;
auto res = get<dmsg_id::value>(mbox.queues()) auto res = get<dmsg_id::value>(mbox.queues())
...@@ -348,6 +348,13 @@ public: ...@@ -348,6 +348,13 @@ public:
return *global_time_; return *global_time_;
} }
void push() {
if (forwarder)
forwarder->push();
for (auto mgr : active_stream_managers())
mgr->push();
}
// -- member variables ------------------------------------------------------- // -- member variables -------------------------------------------------------
mboxqueue mbox; mboxqueue mbox;
...@@ -402,6 +409,7 @@ struct msg_visitor { ...@@ -402,6 +409,7 @@ struct msg_visitor {
); );
visit(f, um.content); visit(f, um.content);
self->current_mailbox_element(nullptr); self->current_mailbox_element(nullptr);
self->push();
return intrusive::task_result::resume; return intrusive::task_result::resume;
} }
......
...@@ -394,12 +394,6 @@ CAF_TEST(depth_3_pipeline_50_items) { ...@@ -394,12 +394,6 @@ CAF_TEST(depth_3_pipeline_50_items) {
auto src = sys.spawn(file_reader, 50u); auto src = sys.spawn(file_reader, 50u);
auto stg = sys.spawn(filter); auto stg = sys.spawn(filter);
auto snk = sys.spawn(sum_up); auto snk = sys.spawn(sum_up);
auto next_cycle = [&] {
tick();
allow((timeout_msg), from(snk).to(snk));
allow((timeout_msg), from(stg).to(stg));
allow((timeout_msg), from(src).to(src));
};
CAF_MESSAGE(CAF_ARG(self) << CAF_ARG(src) << CAF_ARG(stg) << CAF_ARG(snk)); CAF_MESSAGE(CAF_ARG(self) << CAF_ARG(src) << CAF_ARG(stg) << CAF_ARG(snk));
CAF_MESSAGE("initiate stream handshake"); CAF_MESSAGE("initiate stream handshake");
self->send(snk * stg * src, "numbers.txt"); self->send(snk * stg * src, "numbers.txt");
...@@ -412,17 +406,9 @@ CAF_TEST(depth_3_pipeline_50_items) { ...@@ -412,17 +406,9 @@ CAF_TEST(depth_3_pipeline_50_items) {
expect((downstream_msg::batch), from(src).to(stg)); expect((downstream_msg::batch), from(src).to(stg));
CAF_MESSAGE("the stage should delay its first batch since its underfull"); CAF_MESSAGE("the stage should delay its first batch since its underfull");
disallow((downstream_msg::batch), from(stg).to(snk)); disallow((downstream_msg::batch), from(stg).to(snk));
next_cycle(); CAF_MESSAGE("after running the pipeline the sink received all batches");
CAF_MESSAGE("the source shuts down and the stage sends the final batch"); run();
expect((upstream_msg::ack_batch), from(stg).to(src));
expect((downstream_msg::close), from(src).to(stg));
expect((downstream_msg::batch), from(stg).to(snk));
next_cycle();
CAF_MESSAGE("the stage shuts down and the sink produces its final result");
expect((upstream_msg::ack_batch), from(snk).to(stg));
expect((downstream_msg::close), from(stg).to(snk));
CAF_CHECK_EQUAL(deref<sum_up_actor>(snk).state.x, 625); CAF_CHECK_EQUAL(deref<sum_up_actor>(snk).state.x, 625);
CAF_MESSAGE("verify that each actor called its finalizer once");
CAF_CHECK_EQUAL(deref<file_reader_actor>(src).state.fin_called, 1); CAF_CHECK_EQUAL(deref<file_reader_actor>(src).state.fin_called, 1);
CAF_CHECK_EQUAL(deref<filter_actor>(stg).state.fin_called, 1); CAF_CHECK_EQUAL(deref<filter_actor>(stg).state.fin_called, 1);
CAF_CHECK_EQUAL(deref<sum_up_actor>(snk).state.fin_called, 1); CAF_CHECK_EQUAL(deref<sum_up_actor>(snk).state.fin_called, 1);
......
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