/* * Copyright 2018 Google, Inc. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer; * redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution; * neither the name of the copyright holders nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * Authors: Gabe Black */ #include "systemc/core/process.hh" #include "base/logging.hh" #include "systemc/core/event.hh" #include "systemc/core/scheduler.hh" #include "systemc/ext/core/sc_process_handle.hh" #include "systemc/ext/utils/sc_report_handler.hh" namespace sc_gem5 { SensitivityTimeout::SensitivityTimeout(Process *p, ::sc_core::sc_time t) : Sensitivity(p), timeoutEvent([this]() { this->timeout(); }) { scheduler.schedule(&timeoutEvent, t); } SensitivityTimeout::~SensitivityTimeout() { if (timeoutEvent.scheduled()) scheduler.deschedule(&timeoutEvent); } void SensitivityTimeout::timeout() { notify(); } SensitivityEvent::SensitivityEvent( Process *p, const ::sc_core::sc_event *e) : Sensitivity(p), event(e) { Event::getFromScEvent(event)->addSensitivity(this); } SensitivityEvent::~SensitivityEvent() { Event::getFromScEvent(event)->delSensitivity(this); } SensitivityEventAndList::SensitivityEventAndList( Process *p, const ::sc_core::sc_event_and_list *list) : Sensitivity(p), list(list), count(0) { for (auto e: list->events) Event::getFromScEvent(e)->addSensitivity(this); } SensitivityEventAndList::~SensitivityEventAndList() { for (auto e: list->events) Event::getFromScEvent(e)->delSensitivity(this); } void SensitivityEventAndList::notifyWork(Event *e) { e->delSensitivity(this); count++; if (count == list->events.size()) process->satisfySensitivity(this); } SensitivityEventOrList::SensitivityEventOrList( Process *p, const ::sc_core::sc_event_or_list *list) : Sensitivity(p), list(list) { for (auto e: list->events) Event::getFromScEvent(e)->addSensitivity(this); } SensitivityEventOrList::~SensitivityEventOrList() { for (auto e: list->events) Event::getFromScEvent(e)->delSensitivity(this); } void SensitivityTimeoutAndEventAndList::notifyWork(Event *e) { if (e) { // An event went off which must be part of the sc_event_and_list. SensitivityEventAndList::notifyWork(e); } else { // There's no inciting event, so this must be a timeout. SensitivityTimeout::notifyWork(e); } } class UnwindExceptionReset : public ::sc_core::sc_unwind_exception { public: UnwindExceptionReset() { _isReset = true; } }; class UnwindExceptionKill : public ::sc_core::sc_unwind_exception { public: UnwindExceptionKill() {} }; template struct BuiltinExceptionWrapper : public ExceptionWrapperBase { public: T t; void throw_it() override { throw t; } }; BuiltinExceptionWrapper resetException; BuiltinExceptionWrapper killException; void Process::forEachKid(const std::function &work) { for (auto &kid: get_child_objects()) { Process *p_kid = dynamic_cast(kid); if (p_kid) work(p_kid); } } void Process::suspend(bool inc_kids) { if (inc_kids) forEachKid([](Process *p) { p->suspend(true); }); if (!_suspended) { _suspended = true; _suspendedReady = false; } if (procKind() != ::sc_core::SC_METHOD_PROC_ && scheduler.current() == this) { scheduler.yield(); } } void Process::resume(bool inc_kids) { if (inc_kids) forEachKid([](Process *p) { p->resume(true); }); if (_suspended) { _suspended = false; if (_suspendedReady) ready(); _suspendedReady = false; } } void Process::disable(bool inc_kids) { if (inc_kids) forEachKid([](Process *p) { p->disable(true); }); if (!::sc_core::sc_allow_process_control_corners && dynamic_cast(dynamicSensitivity)) { std::string message("attempt to disable a thread with timeout wait: "); message += name(); SC_REPORT_ERROR("Undefined process control interaction", message.c_str()); } _disabled = true; } void Process::enable(bool inc_kids) { if (inc_kids) forEachKid([](Process *p) { p->enable(true); }); _disabled = false; } void Process::kill(bool inc_kids) { // Propogate the kill to our children no matter what happens to us. if (inc_kids) forEachKid([](Process *p) { p->kill(true); }); // If we're in the middle of unwinding, ignore the kill request. if (_isUnwinding) return; // Update our state. terminate(); _isUnwinding = true; // Make sure this process isn't marked ready popListNode(); // Inject the kill exception into this process if it's started. if (!_needsStart) injectException(killException); } void Process::reset(bool inc_kids) { // Propogate the reset to our children no matter what happens to us. if (inc_kids) forEachKid([](Process *p) { p->reset(true); }); // If we're in the middle of unwinding, ignore the reset request. if (_isUnwinding) return; if (_needsStart) { scheduler.runNow(this); } else { _isUnwinding = true; injectException(resetException); } _resetEvent.notify(); } void Process::throw_it(ExceptionWrapperBase &exc, bool inc_kids) { if (inc_kids) forEachKid([&exc](Process *p) { p->throw_it(exc, true); }); // Only inject an exception into threads that have started. if (!_needsStart) injectException(exc); } void Process::injectException(ExceptionWrapperBase &exc) { excWrapper = &exc; scheduler.runNow(this); }; void Process::syncResetOn(bool inc_kids) { if (inc_kids) forEachKid([](Process *p) { p->syncResetOn(true); }); _syncReset = true; } void Process::syncResetOff(bool inc_kids) { if (inc_kids) forEachKid([](Process *p) { p->syncResetOff(true); }); _syncReset = false; } void Process::dontInitialize() { scheduler.dontInitialize(this); } void Process::finalize() { for (auto &s: pendingStaticSensitivities) { s->finalize(staticSensitivities); delete s; s = nullptr; } pendingStaticSensitivities.clear(); }; void Process::run() { bool reset; do { reset = false; try { func->call(); } catch(const ::sc_core::sc_unwind_exception &exc) { reset = exc.is_reset(); _isUnwinding = false; } } while (reset); needsStart(true); } void Process::addStatic(PendingSensitivity *s) { pendingStaticSensitivities.push_back(s); } void Process::setDynamic(Sensitivity *s) { delete dynamicSensitivity; dynamicSensitivity = s; } void Process::satisfySensitivity(Sensitivity *s) { // If there's a dynamic sensitivity and this wasn't it, ignore. if (dynamicSensitivity && dynamicSensitivity != s) return; setDynamic(nullptr); ready(); } void Process::ready() { if (disabled()) return; if (suspended()) _suspendedReady = true; else scheduler.ready(this); } void Process::lastReport(::sc_core::sc_report *report) { if (report) { _lastReport = std::unique_ptr<::sc_core::sc_report>( new ::sc_core::sc_report(*report)); } else { _lastReport = nullptr; } } ::sc_core::sc_report *Process::lastReport() const { return _lastReport.get(); } Process::Process(const char *name, ProcessFuncWrapper *func, bool _dynamic) : ::sc_core::sc_process_b(name), excWrapper(nullptr), func(func), _needsStart(true), _dynamic(_dynamic), _isUnwinding(false), _terminated(false), _suspended(false), _disabled(false), _syncReset(false), refCount(0), stackSize(::Fiber::DefaultStackSize), dynamicSensitivity(nullptr) { _newest = this; } void Process::terminate() { _terminated = true; _suspendedReady = false; _suspended = false; _syncReset = false; delete dynamicSensitivity; dynamicSensitivity = nullptr; for (auto s: staticSensitivities) delete s; staticSensitivities.clear(); _terminatedEvent.notify(); } Process *Process::_newest; void throw_it_wrapper(Process *p, ExceptionWrapperBase &exc, bool inc_kids) { p->throw_it(exc, inc_kids); } } // namespace sc_gem5