Commit 8e28cda4 authored by Dominik Charousset's avatar Dominik Charousset

Reimplement thread-safe actor clock

parent d49879bb
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright 2011-2019 Dominik Charousset *
* *
* Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software *
* License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE. *
* *
* If you did not receive a copy of the license files, see *
* http://opensource.org/licenses/BSD-3-Clause and *
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#pragma once
#include <memory>
namespace caf {
namespace detail {
template <class T, class... Ts>
std::unique_ptr<T> make_unique(Ts&&... xs) {
return std::unique_ptr<T>{new T(std::forward<Ts>(xs)...)};
}
} // namespace detail
} // namespace caf
......@@ -18,10 +18,13 @@
#pragma once
#include <cstdint>
#include <map>
#include <memory>
#include "caf/actor_clock.hpp"
#include "caf/actor_control_block.hpp"
#include "caf/detail/make_unique.hpp"
#include "caf/group.hpp"
#include "caf/mailbox_element.hpp"
#include "caf/message.hpp"
......@@ -35,73 +38,234 @@ class simple_actor_clock : public actor_clock {
public:
// -- member types -----------------------------------------------------------
/// Request for a `timeout_msg`.
struct ordinary_timeout {
using super = simple_actor_clock;
struct event;
struct delayed_event;
/// Owning pointer to events.
using unique_event_ptr = std::unique_ptr<event>;
/// Owning pointer to delayed events.
using unique_delayed_event_ptr = std::unique_ptr<delayed_event>;
/// Maps timeouts to delayed events.
using schedule_map = std::multimap<time_point, unique_delayed_event_ptr>;
/// Maps actor IDs to schedule entries.
using actor_lookup_map = std::multimap<actor_id, schedule_map::iterator>;
/// Lists all possible subtypes of ::event.
enum event_type {
ordinary_timeout_type,
multi_timeout_type,
request_timeout_type,
actor_msg_type,
group_msg_type,
ordinary_timeout_cancellation_type,
multi_timeout_cancellation_type,
request_timeout_cancellation_type,
timeouts_cancellation_type,
drop_all_type,
shutdown_type,
};
/// Base class for clock events.
struct event {
event(event_type t) : subtype(t) {
// nop
}
virtual ~event();
/// Identifies the actual type of this object.
event_type subtype;
};
/// An event with a timeout attached to it.
struct delayed_event : event {
delayed_event(event_type type, time_point due) : event(type), due(due) {
// nop
}
/// Timestamp when this event should trigger.
time_point due;
/// Links back to the actor lookup map.
actor_lookup_map::iterator backlink;
};
/// An ordinary timeout event for actors. Only one timeout for any timeout
/// type can be active.
struct ordinary_timeout final : delayed_event {
static constexpr bool cancellable = true;
ordinary_timeout(time_point due, strong_actor_ptr self, atom_value type,
uint64_t id)
: delayed_event(ordinary_timeout_type, due),
self(std::move(self)),
type(type),
id(id) {
// nop
}
strong_actor_ptr self;
atom_value type;
uint64_t id;
};
struct multi_timeout {
/// An timeout event for actors that allows multiple active timers for the
/// same type.
struct multi_timeout final : delayed_event {
static constexpr bool cancellable = true;
multi_timeout(time_point due, strong_actor_ptr self, atom_value type,
uint64_t id)
: delayed_event(multi_timeout_type, due),
self(std::move(self)),
type(type),
id(id) {
// nop
}
strong_actor_ptr self;
atom_value type;
uint64_t id;
};
/// Request for a `sec::request_timeout` error.
struct request_timeout {
/// A delayed `sec::request_timeout` error that gets cancelled when the
/// request arrives in time.
struct request_timeout final: delayed_event {
static constexpr bool cancellable = true;
request_timeout(time_point due, strong_actor_ptr self, message_id id)
: delayed_event(request_timeout_type, due),
self(std::move(self)),
id(id) {
// nop
}
strong_actor_ptr self;
message_id id;
};
/// Request for sending a message to an actor at a later time.
struct actor_msg {
/// A delayed ::message to an actor.
struct actor_msg final : delayed_event {
static constexpr bool cancellable = false;
actor_msg(time_point due, strong_actor_ptr receiver,
mailbox_element_ptr content)
: delayed_event(actor_msg_type, due),
receiver(std::move(receiver)),
content(std::move(content)) {
// nop
}
strong_actor_ptr receiver;
mailbox_element_ptr content;
};
/// Request for sending a message to a group at a later time.
struct group_msg {
/// A delayed ::message to a group.
struct group_msg final : delayed_event {
static constexpr bool cancellable = false;
group_msg(time_point due, group target, strong_actor_ptr sender,
message content)
: delayed_event(group_msg_type, due),
target(std::move(target)),
sender(std::move(sender)),
content(std::move(content)) {
// nop
}
group target;
strong_actor_ptr sender;
message content;
};
using value_type = variant<ordinary_timeout, multi_timeout, request_timeout,
actor_msg, group_msg>;
/// Cancels a delayed event.
struct cancellation : event {
cancellation(event_type t, actor_id aid) : event(t), aid(aid) {
//nop
}
using map_type = std::multimap<time_point, value_type>;
actor_id aid;
};
using secondary_map = std::multimap<abstract_actor*, map_type::iterator>;
/// Cancels matching ordinary timeouts.
struct ordinary_timeout_cancellation final : cancellation {
ordinary_timeout_cancellation(actor_id aid, atom_value type)
: cancellation(ordinary_timeout_cancellation_type, aid), type(type) {
// nop
}
struct ordinary_predicate {
atom_value type;
bool operator()(const secondary_map::value_type& x) const noexcept;
};
struct multi_predicate {
/// Cancels the matching multi timeout.
struct multi_timeout_cancellation final : cancellation {
multi_timeout_cancellation(actor_id aid, atom_value type, uint64_t id)
: cancellation(ordinary_timeout_cancellation_type, aid),
type(type),
id(id) {
// nop
}
atom_value type;
bool operator()(const secondary_map::value_type& x) const noexcept;
uint64_t id;
};
struct request_predicate {
/// Cancels a `sec::request_timeout` error.
struct request_timeout_cancellation final : cancellation {
request_timeout_cancellation(actor_id aid, message_id id)
: cancellation(request_timeout_cancellation_type, aid), id(id) {
// nop
}
message_id id;
bool operator()(const secondary_map::value_type& x) const noexcept;
};
struct visitor {
simple_actor_clock* thisptr;
/// Cancels all timeouts for an actor.
struct timeouts_cancellation final : cancellation {
timeouts_cancellation(actor_id aid)
: cancellation(timeouts_cancellation_type, aid) {
// nop
}
};
void operator()(ordinary_timeout& x);
/// Cancels all timeouts and messages.
struct drop_all final : event {
drop_all() : event(drop_all_type) {
// nop
}
};
void operator()(multi_timeout& x);
/// Shuts down the actor clock.
struct shutdown final : event {
shutdown() : event(shutdown_type) {
// nop
}
};
void operator()(request_timeout& x);
// -- properties -------------------------------------------------------------
void operator()(actor_msg& x);
const schedule_map& schedule() const {
return schedule_;
}
void operator()(group_msg& x);
};
const actor_lookup_map& actor_lookup() const {
return actor_lookup_;
}
// -- convenience functions --------------------------------------------------
/// Triggers all timeouts with timestamp <= now.
/// @returns The number of triggered timeouts.
/// @private
size_t trigger_expired_timeouts();
// -- overridden member functions --------------------------------------------
void set_ordinary_timeout(time_point t, abstract_actor* self,
atom_value type, uint64_t id) override;
......@@ -126,20 +290,13 @@ public:
void cancel_all() override;
inline const map_type& schedule() const {
return schedule_;
}
inline const secondary_map& actor_lookup() const {
return actor_lookup_;
}
protected:
// -- helper functions -------------------------------------------------------
template <class Predicate>
secondary_map::iterator lookup(abstract_actor* self,
Predicate pred) {
actor_lookup_map::iterator lookup(actor_id aid, Predicate pred) {
auto e = actor_lookup_.end();
auto range = actor_lookup_.equal_range(self);
auto range = actor_lookup_.equal_range(aid);
if (range.first == range.second)
return e;
auto i = std::find_if(range.first, range.second, pred);
......@@ -147,8 +304,8 @@ protected:
}
template <class Predicate>
void cancel(abstract_actor* self, Predicate pred) {
auto i = lookup(self, pred);
void cancel(actor_id aid, Predicate pred) {
auto i = lookup(aid, pred);
if (i != actor_lookup_.end()) {
schedule_.erase(i->second);
actor_lookup_.erase(i);
......@@ -156,19 +313,58 @@ protected:
}
template <class Predicate>
void drop_lookup(abstract_actor* self, Predicate pred) {
auto i = lookup(self, pred);
void drop_lookup(actor_id aid, Predicate pred) {
auto i = lookup(aid, pred);
if (i != actor_lookup_.end())
actor_lookup_.erase(i);
}
void handle(const ordinary_timeout_cancellation& x);
void handle(const multi_timeout_cancellation& x);
void handle(const request_timeout_cancellation& x);
void handle(const timeouts_cancellation& x);
void ship(delayed_event& x);
template <class T>
detail::enable_if_t<T::cancellable>
add_schedule_entry(time_point t, std::unique_ptr<T> x) {
auto id = x->self->id();
auto i = schedule_.emplace(t, std::move(x));
i->second->backlink = actor_lookup_.emplace(id, i);
}
template <class T>
detail::enable_if_t<!T::cancellable>
add_schedule_entry(time_point t, std::unique_ptr<T> x) {
auto i = schedule_.emplace(t, std::move(x));
i->second->backlink = actor_lookup_.end();
}
void add_schedule_entry(time_point t, std::unique_ptr<ordinary_timeout> x);
template <class T>
void add_schedule_entry(std::unique_ptr<T> x) {
auto due = x->due;
add_schedule_entry(due, std::move(x));
}
template <class T, class... Ts>
void new_schedule_entry(time_point t, Ts&&... xs) {
add_schedule_entry(t, detail::make_unique<T>(t, std::forward<Ts>(xs)...));
}
// -- member variables -------------------------------------------------------
/// Timeout schedule.
map_type schedule_;
schedule_map schedule_;
/// Secondary index for accessing timeouts by actor.
secondary_map actor_lookup_;
actor_lookup_map actor_lookup_;
};
} // namespace detail
} // namespace caf
......@@ -51,10 +51,6 @@ public:
/// @returns The number of triggered timeouts.
size_t trigger_timeouts();
/// Triggers all timeouts with timestamp <= now.
/// @returns The number of triggered timeouts.
size_t trigger_expired_timeouts();
/// Advances the time by `x` and dispatches timeouts and delayed messages.
/// @returns The number of triggered timeouts.
size_t advance_time(duration_type x);
......
......@@ -18,10 +18,14 @@
#pragma once
#include <mutex>
#include <array>
#include <atomic>
#include <condition_variable>
#include <memory>
#include <mutex>
#include "caf/abstract_actor.hpp"
#include "caf/detail/ringbuffer.hpp"
#include "caf/detail/simple_actor_clock.hpp"
namespace caf {
......@@ -29,9 +33,15 @@ namespace detail {
class thread_safe_actor_clock : public simple_actor_clock {
public:
// -- constants --------------------------------------------------------------
static constexpr size_t buffer_size = 64;
// -- member types -----------------------------------------------------------
using super = simple_actor_clock;
thread_safe_actor_clock();
// -- member functions -------------------------------------------------------
void set_ordinary_timeout(time_point t, abstract_actor* self,
atom_value type, uint64_t id) override;
......@@ -61,11 +71,14 @@ public:
void cancel_dispatch_loop();
private:
std::recursive_mutex mx_;
std::condition_variable_any cv_;
std::atomic<bool> done_;
void push(event* ptr);
/// Receives timer events from other threads.
detail::ringbuffer<unique_event_ptr, buffer_size> queue_;
/// Locally caches events for processing.
std::array<unique_event_ptr, buffer_size> events_;
};
} // namespace detail
} // namespace caf
......@@ -25,108 +25,40 @@
namespace caf {
namespace detail {
bool simple_actor_clock::ordinary_predicate::
operator()(const secondary_map::value_type& x) const noexcept {
auto ptr = get_if<ordinary_timeout>(&x.second->second);
return ptr != nullptr ? ptr->type == type : false;
simple_actor_clock::event::~event() {
// nop
}
bool simple_actor_clock::multi_predicate::
operator()(const secondary_map::value_type& x) const noexcept {
auto ptr = get_if<multi_timeout>(&x.second->second);
return ptr != nullptr ? ptr->type == type : false;
}
bool simple_actor_clock::request_predicate::
operator()(const secondary_map::value_type& x) const noexcept {
auto ptr = get_if<request_timeout>(&x.second->second);
return ptr != nullptr ? ptr->id == id : false;
}
void simple_actor_clock::visitor::operator()(ordinary_timeout& x) {
CAF_ASSERT(x.self != nullptr);
x.self->get()->eq_impl(make_message_id(), x.self, nullptr,
timeout_msg{x.type, x.id});
ordinary_predicate pred{x.type};
thisptr->drop_lookup(x.self->get(), pred);
}
void simple_actor_clock::visitor::operator()(multi_timeout& x) {
CAF_ASSERT(x.self != nullptr);
x.self->get()->eq_impl(make_message_id(), x.self, nullptr,
timeout_msg{x.type, x.id});
multi_predicate pred{x.type};
thisptr->drop_lookup(x.self->get(), pred);
}
void simple_actor_clock::visitor::operator()(request_timeout& x) {
CAF_ASSERT(x.self != nullptr);
x.self->get()->eq_impl(x.id, x.self, nullptr, sec::request_timeout);
request_predicate pred{x.id};
thisptr->drop_lookup(x.self->get(), pred);
}
void simple_actor_clock::visitor::operator()(actor_msg& x) {
x.receiver->enqueue(std::move(x.content), nullptr);
}
void simple_actor_clock::visitor::operator()(group_msg& x) {
x.target->eq_impl(make_message_id(), std::move(x.sender), nullptr,
std::move(x.content));
}
void simple_actor_clock::set_ordinary_timeout(time_point t, abstract_actor* self,
void simple_actor_clock::set_ordinary_timeout(time_point t,
abstract_actor* self,
atom_value type, uint64_t id) {
ordinary_predicate pred{type};
auto i = lookup(self, pred);
auto sptr = actor_cast<strong_actor_ptr>(self);
ordinary_timeout tmp{std::move(sptr), type, id};
if (i != actor_lookup_.end()) {
schedule_.erase(i->second);
i->second = schedule_.emplace(t, std::move(tmp));
} else {
auto j = schedule_.emplace(t, std::move(tmp));
actor_lookup_.emplace(self, j);
}
new_schedule_entry<ordinary_timeout>(t, self->ctrl(), type, id);
}
void simple_actor_clock::set_multi_timeout(time_point t, abstract_actor* self,
atom_value type, uint64_t id) {
auto sptr = actor_cast<strong_actor_ptr>(self);
multi_timeout tmp{std::move(sptr), type, id};
auto j = schedule_.emplace(t, std::move(tmp));
actor_lookup_.emplace(self, j);
new_schedule_entry<multi_timeout>(t, self->ctrl(), type, id);
}
void simple_actor_clock::set_request_timeout(time_point t, abstract_actor* self,
message_id id) {
request_predicate pred{id};
auto i = lookup(self, pred);
auto sptr = actor_cast<strong_actor_ptr>(self);
request_timeout tmp{std::move(sptr), id};
if (i != actor_lookup_.end()) {
schedule_.erase(i->second);
i->second = schedule_.emplace(t, std::move(tmp));
} else {
auto j = schedule_.emplace(t, std::move(tmp));
actor_lookup_.emplace(self, j);
}
new_schedule_entry<request_timeout>(t, self->ctrl(), id);
}
void simple_actor_clock::cancel_ordinary_timeout(abstract_actor* self,
atom_value type) {
ordinary_predicate pred{type};
cancel(self, pred);
ordinary_timeout_cancellation tmp{self->id(), type};
handle(tmp);
}
void simple_actor_clock::cancel_request_timeout(abstract_actor* self,
message_id id) {
request_predicate pred{id};
cancel(self, pred);
request_timeout_cancellation tmp{self->id(), id};
handle(tmp);
}
void simple_actor_clock::cancel_timeouts(abstract_actor* self) {
auto range = actor_lookup_.equal_range(self);
auto range = actor_lookup_.equal_range(self->id());
if (range.first == range.second)
return;
for (auto i = range.first; i != range.second; ++i)
......@@ -137,14 +69,14 @@ void simple_actor_clock::cancel_timeouts(abstract_actor* self) {
void simple_actor_clock::schedule_message(time_point t,
strong_actor_ptr receiver,
mailbox_element_ptr content) {
schedule_.emplace(t, actor_msg{std::move(receiver), std::move(content)});
new_schedule_entry<actor_msg>(t, std::move(receiver), std::move(content));
}
void simple_actor_clock::schedule_message(time_point t, group target,
strong_actor_ptr sender,
message content) {
schedule_.emplace(
t, group_msg{std::move(target), std::move(sender), std::move(content)});
new_schedule_entry<group_msg>(t, std::move(target), std::move(sender),
std::move(content));
}
void simple_actor_clock::cancel_all() {
......@@ -152,5 +84,119 @@ void simple_actor_clock::cancel_all() {
schedule_.clear();
}
void simple_actor_clock::ship(delayed_event& x) {
switch (x.subtype) {
case ordinary_timeout_type: {
auto& dref = static_cast<ordinary_timeout&>(x);
auto& self = dref.self;
self->get()->eq_impl(make_message_id(), self, nullptr,
timeout_msg{dref.type, dref.id});
break;
}
case multi_timeout_type: {
auto& dref = static_cast<ordinary_timeout&>(x);
auto& self = dref.self;
self->get()->eq_impl(make_message_id(), self, nullptr,
timeout_msg{dref.type, dref.id});
break;
}
case request_timeout_type: {
auto& dref = static_cast<request_timeout&>(x);
auto& self = dref.self;
self->get()->eq_impl(dref.id, self, nullptr, sec::request_timeout);
break;
}
case actor_msg_type: {
auto& dref = static_cast<actor_msg&>(x);
dref.receiver->enqueue(std::move(dref.content), nullptr);
break;
}
case group_msg_type: {
auto& dref = static_cast<group_msg&>(x);
dref.target->eq_impl(make_message_id(), std::move(dref.sender), nullptr,
std::move(dref.content));
break;
}
default:
break;
}
}
void simple_actor_clock::handle(const ordinary_timeout_cancellation& x) {
auto pred = [&](const actor_lookup_map::value_type& kvp) {
auto& y = *kvp.second->second;
return y.subtype == ordinary_timeout_type
&& x.type == static_cast<const ordinary_timeout&>(y).type;
};
cancel(x.aid, pred);
}
void simple_actor_clock::handle(const multi_timeout_cancellation& x) {
auto pred = [&](const actor_lookup_map::value_type& kvp) {
auto& y = *kvp.second->second;
if (y.subtype != ordinary_timeout_type)
return false;
auto& dref = static_cast<const multi_timeout&>(y);
return x.type == dref.type && x.id == dref.id;
};
cancel(x.aid, pred);
}
void simple_actor_clock::handle(const request_timeout_cancellation& x) {
auto pred = [&](const actor_lookup_map::value_type& kvp) {
auto& y = *kvp.second->second;
return y.subtype == request_timeout_type
&& x.id == static_cast<const request_timeout&>(y).id;
};
cancel(x.aid, pred);
}
void simple_actor_clock::handle(const timeouts_cancellation& x) {
auto range = actor_lookup_.equal_range(x.aid);
if (range.first == range.second)
return;
for (auto i = range.first; i != range.second; ++i)
schedule_.erase(i->second);
actor_lookup_.erase(range.first, range.second);
}
size_t simple_actor_clock::trigger_expired_timeouts() {
size_t result = 0;
auto t = now();
auto i = schedule_.begin();
auto e = schedule_.end();
while (i != e && i->first <= t) {
auto ptr = std::move(i->second);
auto backlink = ptr->backlink;
if (backlink != actor_lookup_.end())
actor_lookup_.erase(backlink);
i = schedule_.erase(i);
ship(*ptr);
++result;
}
return result;
}
void
simple_actor_clock::add_schedule_entry(time_point t,
std::unique_ptr<ordinary_timeout> x) {
auto aid = x->self->id();
auto type = x->type;
auto pred = [&](const actor_lookup_map::value_type& kvp) {
auto& y = *kvp.second->second;
return y.subtype == ordinary_timeout_type
&& static_cast<const ordinary_timeout&>(y).type == type;
};
auto i = lookup(aid, pred);
if (i != actor_lookup_.end()) {
schedule_.erase(i->second);
i->second = schedule_.emplace(t, std::move(x));
} else {
auto j = schedule_.emplace(t, std::move(x));
i = actor_lookup_.emplace(aid, j);
}
i->second->second->backlink = i;
}
} // namespace detail
} // namespace caf
......@@ -46,13 +46,16 @@ bool test_actor_clock::trigger_timeout() {
CAF_LOG_TRACE(CAF_ARG2("schedule.size", schedule_.size()));
if (schedule_.empty())
return false;
visitor f{this};
auto i = schedule_.begin();
auto tout = i->first;
if (tout > current_time)
current_time = tout;
visit(f, i->second);
auto ptr = std::move(i->second);
schedule_.erase(i);
auto backlink = ptr->backlink;
if (backlink != actor_lookup_.end())
actor_lookup_.erase(backlink);
ship(*ptr);
return true;
}
......@@ -60,28 +63,9 @@ size_t test_actor_clock::trigger_timeouts() {
CAF_LOG_TRACE(CAF_ARG2("schedule.size", schedule_.size()));
if (schedule_.empty())
return 0u;
visitor f{this};
auto result = schedule_.size();
for (auto& kvp : schedule_) {
auto tout = kvp.first;
if (tout > current_time)
current_time = tout;
visit(f, kvp.second);
}
schedule_.clear();
return result;
}
size_t test_actor_clock::trigger_expired_timeouts() {
CAF_LOG_TRACE(CAF_ARG2("schedule.size", schedule_.size()));
visitor f{this};
size_t result = 0;
auto i = schedule_.begin();
while (i != schedule_.end() && i->first <= current_time) {
while (trigger_timeout())
++result;
visit(f, i->second);
i = schedule_.erase(i);
}
return result;
}
......
......@@ -18,133 +18,148 @@
#include "caf/detail/thread_safe_actor_clock.hpp"
#include "caf/actor_control_block.hpp"
#include "caf/logger.hpp"
#include "caf/sec.hpp"
#include "caf/system_messages.hpp"
namespace caf {
namespace detail {
namespace {
using guard_type = std::unique_lock<std::recursive_mutex>;
} // namespace <anonymous>
thread_safe_actor_clock::thread_safe_actor_clock() : done_(false) {
// nop
}
void thread_safe_actor_clock::set_ordinary_timeout(time_point t,
abstract_actor* self,
atom_value type,
uint64_t id) {
guard_type guard{mx_};
if (!done_) {
super::set_ordinary_timeout(t, self, type, id);
cv_.notify_all();
}
push(new ordinary_timeout(t, self->ctrl(), type, id));
}
void thread_safe_actor_clock::set_request_timeout(time_point t,
abstract_actor* self,
message_id id) {
guard_type guard{mx_};
if (!done_) {
super::set_request_timeout(t, self, id);
cv_.notify_all();
}
push(new request_timeout(t, self->ctrl(), id));
}
void thread_safe_actor_clock::set_multi_timeout(time_point t, abstract_actor* self,
void thread_safe_actor_clock::set_multi_timeout(time_point t,
abstract_actor* self,
atom_value type, uint64_t id) {
guard_type guard{mx_};
if (!done_) {
super::set_multi_timeout(t, self, type, id);
cv_.notify_all();
}
push(new multi_timeout(t, self->ctrl(), type, id));
}
void thread_safe_actor_clock::cancel_ordinary_timeout(abstract_actor* self,
atom_value type) {
guard_type guard{mx_};
if (!done_) {
super::cancel_ordinary_timeout(self, type);
cv_.notify_all();
}
push(new ordinary_timeout_cancellation(self->id(), type));
}
void thread_safe_actor_clock::cancel_request_timeout(abstract_actor* self,
message_id id) {
guard_type guard{mx_};
if (!done_) {
super::cancel_request_timeout(self, id);
cv_.notify_all();
}
push(new request_timeout_cancellation(self->id(), id));
}
void thread_safe_actor_clock::cancel_timeouts(abstract_actor* self) {
guard_type guard{mx_};
if (!done_) {
super::cancel_timeouts(self);
cv_.notify_all();
}
push(new timeouts_cancellation(self->id()));
}
void thread_safe_actor_clock::schedule_message(time_point t,
strong_actor_ptr receiver,
mailbox_element_ptr content) {
guard_type guard{mx_};
if (!done_) {
super::schedule_message(t, std::move(receiver), std::move(content));
cv_.notify_all();
}
push(new actor_msg(t, std::move(receiver), std::move(content)));
}
void thread_safe_actor_clock::schedule_message(time_point t, group target,
strong_actor_ptr sender,
message content) {
guard_type guard{mx_};
if (!done_) {
super::schedule_message(t, std::move(target), std::move(sender),
std::move(content));
cv_.notify_all();
}
push(new group_msg(t, std::move(target), std::move(sender),
std::move(content)));
}
void thread_safe_actor_clock::cancel_all() {
guard_type guard{mx_};
super::cancel_all();
cv_.notify_all();
push(new drop_all);
}
void thread_safe_actor_clock::run_dispatch_loop() {
visitor f{this};
guard_type guard{mx_};
while (done_ == false) {
// Wait for non-empty schedule.
// Note: The thread calling run_dispatch_loop() is guaranteed not to lock
// the mutex recursively. Otherwise, cv_.wait() or cv_.wait_until()
// would be unsafe, because wait operations call unlock() only once.
for (;;) {
// Wait until queue is non-empty.
if (schedule_.empty()) {
cv_.wait(guard);
queue_.wait_nonempty();
} else {
auto tout = schedule_.begin()->first;
cv_.wait_until(guard, tout);
auto t = schedule_.begin()->second->due;
if (!queue_.wait_nonempty(t)) {
// Handle timeout by shipping timed-out events and starting anew.
trigger_expired_timeouts();
continue;
}
// Double-check whether schedule is non-empty and execute it.
if (!schedule_.empty()) {
auto t = now();
auto i = schedule_.begin();
while (i != schedule_.end() && i->first <= t) {
visit(f, i->second);
i = schedule_.erase(i);
}
// Push all elements from the queue to the events buffer.
auto i = events_.begin();
auto e = queue_.get_all(i);
for (; i != e; ++i) {
auto& x = *i;
CAF_ASSERT(x != nullptr);
switch (x->subtype) {
case ordinary_timeout_cancellation_type: {
handle(static_cast<ordinary_timeout_cancellation&>(*x));
break;
}
case request_timeout_cancellation_type: {
handle(static_cast<request_timeout_cancellation&>(*x));
break;
}
case timeouts_cancellation_type: {
handle(static_cast<timeouts_cancellation&>(*x));
break;
}
case drop_all_type: {
schedule_.clear();
actor_lookup_.clear();
break;
}
case shutdown_type: {
schedule_.clear();
actor_lookup_.clear();
// Call it a day.
return;
}
case ordinary_timeout_type: {
auto dptr = static_cast<ordinary_timeout*>(x.release());
add_schedule_entry(std::unique_ptr<ordinary_timeout>{dptr});
break;
}
case multi_timeout_type: {
auto dptr = static_cast<multi_timeout*>(x.release());
add_schedule_entry(std::unique_ptr<multi_timeout>{dptr});
break;
}
case request_timeout_type: {
auto dptr = static_cast<request_timeout*>(x.release());
add_schedule_entry(std::unique_ptr<request_timeout>{dptr});
break;
}
case actor_msg_type: {
auto dptr = static_cast<actor_msg*>(x.release());
add_schedule_entry(std::unique_ptr<actor_msg>{dptr});
break;
}
case group_msg_type: {
auto dptr = static_cast<group_msg*>(x.release());
add_schedule_entry(std::unique_ptr<group_msg>{dptr});
break;
}
default: {
CAF_LOG_ERROR("unexpected event type");
break;
}
}
x.reset();
}
}
}
void thread_safe_actor_clock::cancel_dispatch_loop() {
guard_type guard{mx_};
done_ = true;
cv_.notify_all();
push(new shutdown);
}
void thread_safe_actor_clock::push(event* ptr) {
queue_.push_back(unique_event_ptr{ptr});
}
} // namespace 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