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:
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>
static deficit_type quantum(const Queue&, deficit_type x) noexcept {
return x;
......
......@@ -200,52 +200,6 @@ public:
using exception_handler = std::function<error(pointer, std::exception_ptr&)>;
#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 void default_error_handler(pointer ptr, error& x);
......@@ -517,6 +471,9 @@ public:
/// Pushes `ptr` to the cache of the default queue.
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.
normal_queue& get_normal_queue();
......@@ -526,9 +483,6 @@ public:
/// Returns the queue of the mailbox that stores `downstream_msg` messages.
downstream_queue& get_downstream_queue();
/// Returns the queue of the mailbox that stores high priority messages.
urgent_queue& get_urgent_queue();
// -- inbound_path management ------------------------------------------------
/// Creates a new path for incoming stream traffic from `sender`.
......@@ -569,8 +523,7 @@ public:
return;
}
CAF_LOG_INFO("no manager found:" << CAF_ARG(slots));
// TODO: replace with `if constexpr` when switching to C++17
if (std::is_same<T, upstream_msg::ack_batch>::value) {
if constexpr (std::is_same<T, upstream_msg::ack_batch>::value) {
// Make sure the other actor does not falsely believe us a source.
inbound_path::emit_irregular_shutdown(this, slots, current_sender(),
sec::invalid_upstream);
......@@ -657,6 +610,12 @@ public:
/// Removes the stream manager mapped to `id` in `O(log n)`.
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)`.
void erase_stream_manager(const stream_manager_ptr& mgr);
......@@ -683,6 +642,10 @@ public:
return max_batch_delay_;
}
void active_stream_managers(std::vector<stream_manager*>& result);
std::vector<stream_manager*> active_stream_managers();
/// @endcond
protected:
......@@ -746,7 +709,23 @@ protected:
exception_handler exception_handler_;
#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
......@@ -113,8 +113,6 @@ void inbound_path::handle(downstream_msg::batch& batch) {
if (auto available = available_credit(); available >= desired_batch_size)
if (auto acquired = mgr->acquire_credit(this, available); acquired > 0)
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) {
......
This diff is collapsed.
......@@ -92,15 +92,13 @@ bool stream_manager::handle(stream_slots slots, upstream_msg::ack_open& x) {
CAF_ASSERT(ptr->open_credit >= 0);
ptr->set_desired_batch_size(x.desired_batch_size);
--pending_handshakes_;
push();
return true;
}
void stream_manager::handle(stream_slots slots, upstream_msg::ack_batch& x) {
CAF_LOG_TRACE(CAF_ARG(slots) << CAF_ARG(x));
CAF_ASSERT(x.desired_batch_size > 0);
auto path = out().path(slots.receiver);
if (path != nullptr) {
if (auto path = out().path(slots.receiver); path != nullptr) {
path->open_credit += x.new_capacity;
CAF_ASSERT(path->open_credit >= 0);
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) {
// Gravefully remove path after receiving its final ACK.
if (path->closing && out().clean(slots.receiver))
out().remove_path(slots.receiver, none, false);
push();
}
}
......@@ -300,9 +297,11 @@ stream_manager::add_unchecked_inbound_path_impl(type_id_t input_type,
}
void stream_manager::tick(time_point now) {
do {
out().tick(now, max_batch_delay_);
for (auto path : inbound_paths_)
path->tick(now, max_batch_delay_);
out().tick(now, max_batch_delay_);
} while (generate_messages());
}
stream_slot stream_manager::assign_next_slot() {
......
......@@ -49,7 +49,8 @@ TESTEE_STATE(file_reader) {
};
VARARGS_TESTEE(file_reader, size_t buf_size) {
return {[=](string& fname) -> result<stream<int32_t>, string> {
return {
[=](string& fname) -> result<stream<int32_t>, string> {
CAF_CHECK_EQUAL(fname, "numbers.txt");
CAF_CHECK_EQUAL(self->mailbox().empty(), true);
return attach_stream_source(
......@@ -79,7 +80,8 @@ VARARGS_TESTEE(file_reader, size_t buf_size) {
}
return false;
});
}};
},
};
}
TESTEE_STATE(sum_up) {
......@@ -87,7 +89,8 @@ TESTEE_STATE(sum_up) {
};
TESTEE(sum_up) {
return {[=](stream<int32_t>& in, const string& fname) {
return {
[=](stream<int32_t>& in, const string& fname) {
CAF_CHECK_EQUAL(fname, "numbers.txt");
using int_ptr = int32_t*;
return attach_stream_sink(
......@@ -106,7 +109,8 @@ TESTEE(sum_up) {
[=](join_atom atm, actor src) {
CAF_MESSAGE(self->name() << " joins a stream");
self->send(self * src, atm);
}};
},
};
}
TESTEE_STATE(stream_multiplexer) {
......
......@@ -314,7 +314,7 @@ public:
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 {
using policy_type = policy::downstream_messages::nested;
auto res = get<dmsg_id::value>(mbox.queues())
......@@ -348,6 +348,13 @@ public:
return *global_time_;
}
void push() {
if (forwarder)
forwarder->push();
for (auto mgr : active_stream_managers())
mgr->push();
}
// -- member variables -------------------------------------------------------
mboxqueue mbox;
......@@ -402,6 +409,7 @@ struct msg_visitor {
);
visit(f, um.content);
self->current_mailbox_element(nullptr);
self->push();
return intrusive::task_result::resume;
}
......
......@@ -394,12 +394,6 @@ CAF_TEST(depth_3_pipeline_50_items) {
auto src = sys.spawn(file_reader, 50u);
auto stg = sys.spawn(filter);
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("initiate stream handshake");
self->send(snk * stg * src, "numbers.txt");
......@@ -412,17 +406,9 @@ CAF_TEST(depth_3_pipeline_50_items) {
expect((downstream_msg::batch), from(src).to(stg));
CAF_MESSAGE("the stage should delay its first batch since its underfull");
disallow((downstream_msg::batch), from(stg).to(snk));
next_cycle();
CAF_MESSAGE("the source shuts down and the stage sends the final batch");
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_MESSAGE("after running the pipeline the sink received all batches");
run();
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<filter_actor>(stg).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