Program Listing for File action_impl.hh
↰ Return to documentation for file (include/reactor-cpp/impl/action_impl.hh)
/*
* Copyright (C) 2019 TU Dresden
* All rights reserved.
*
* Authors:
* Christian Menard
*/
#ifndef REACTOR_CPP_IMPL_ACTION_IMPL_HH
#define REACTOR_CPP_IMPL_ACTION_IMPL_HH
#include "../assert.hh"
#include "../environment.hh"
#include <iterator>
#include <mutex>
namespace reactor {
template <class T> template <class Dur> void Action<T>::schedule(const ImmutableValuePtr<T>& value_ptr, Dur delay) {
Duration time_delay = std::chrono::duration_cast<Duration>(delay);
reactor::validate(time_delay >= Duration::zero(), "Schedule cannot be called with a negative delay!");
reactor::validate(value_ptr != nullptr, "Actions may not be scheduled with a nullptr value!");
auto* scheduler = environment()->scheduler();
if (is_logical()) {
time_delay += this->min_delay();
auto tag = Tag::from_logical_time(scheduler->logical_time()).delay(time_delay);
events_[tag] = value_ptr;
scheduler->schedule_sync(this, tag);
} else {
{
std::lock_guard<std::mutex> lock(mutex_events_);
// We must call schedule_async while holding the mutex, because otherwise
// the scheduler could already start processing the event that we schedule
// and call setup() on this action before we insert the value in events_.
// Holding both the local mutex mutex_events_ and them scheduler mutes (in
// schedule async) should not lead to a deadlock as the scheduler will
// only hold one of the two mutexes at once.
auto tag = scheduler->schedule_async(this, time_delay);
events_[tag] = value_ptr;
}
}
}
template <class Dur> void Action<void>::schedule(Dur delay) {
auto time_delay = std::chrono::duration_cast<Duration>(delay);
reactor::validate(time_delay >= Duration::zero(), "Schedule cannot be called with a negative delay!");
auto* scheduler = environment()->scheduler();
if (is_logical()) {
time_delay += this->min_delay();
auto tag = Tag::from_logical_time(scheduler->logical_time()).delay(time_delay);
scheduler->schedule_sync(this, tag);
} else {
// physical action
scheduler->schedule_async(this, time_delay);
}
}
template <class T> void Action<T>::setup() noexcept {
BaseAction::setup();
if (value_ptr_ == nullptr) { // only do this once, even if the action was triggered multiple times
// lock if this is a physical action
std::unique_lock<std::mutex> lock =
is_logical() ? std::unique_lock<std::mutex>() : std::unique_lock<std::mutex>(mutex_events_);
const auto& node = events_.extract(events_.begin());
reactor_assert(!node.empty());
reactor_assert(node.key() == environment()->scheduler()->logical_time());
value_ptr_ = std::move(node.mapped());
}
reactor_assert(value_ptr_ != nullptr);
}
template <class T> void Action<T>::cleanup() noexcept {
BaseAction::cleanup();
value_ptr_ = nullptr;
}
} // namespace reactor
#endif