diff --git a/examples/coroutine/FunctionalEventExampleNetwork.ned b/examples/coroutine/FunctionalEventExampleNetwork.ned new file mode 100644 index 00000000000..fcbdc0f2645 --- /dev/null +++ b/examples/coroutine/FunctionalEventExampleNetwork.ned @@ -0,0 +1,14 @@ +// +// SPDX-License-Identifier: LGPL-3.0-or-later +// + + +package inet.examples.coroutine; + +import inet.common.FunctionalEventExample; + +network FunctionalEventExampleNetwork +{ + submodules: + functionalEvent: FunctionalEventExample; +} diff --git a/examples/coroutine/SimulationContinuationExampleNetwork.ned b/examples/coroutine/SimulationContinuationExampleNetwork.ned new file mode 100644 index 00000000000..52b0707dae1 --- /dev/null +++ b/examples/coroutine/SimulationContinuationExampleNetwork.ned @@ -0,0 +1,14 @@ +// +// SPDX-License-Identifier: LGPL-3.0-or-later +// + + +package inet.examples.coroutine; + +import inet.common.SimulationContinuationExample; + +network SimulationContinuationExampleNetwork +{ + submodules: + continuation: SimulationContinuationExample; +} diff --git a/examples/coroutine/SimulationTaskExampleNetwork.ned b/examples/coroutine/SimulationTaskExampleNetwork.ned new file mode 100644 index 00000000000..bff97f7b058 --- /dev/null +++ b/examples/coroutine/SimulationTaskExampleNetwork.ned @@ -0,0 +1,14 @@ +// +// SPDX-License-Identifier: LGPL-3.0-or-later +// + + +package inet.examples.coroutine; + +import inet.common.SimulationTaskExample; + +network SimulationTaskExampleNetwork +{ + submodules: + task: SimulationTaskExample; +} diff --git a/examples/coroutine/omnetpp.ini b/examples/coroutine/omnetpp.ini new file mode 100644 index 00000000000..ec3fed5421e --- /dev/null +++ b/examples/coroutine/omnetpp.ini @@ -0,0 +1,11 @@ +[General] +sim-time-limit = 10s + +[Config SimulationTask] +network = SimulationTaskExampleNetwork + +[Config SimulationContinuation] +network = SimulationContinuationExampleNetwork + +[Config FunctionalEvent] +network = FunctionalEventExampleNetwork diff --git a/src/inet/common/FunctionalEvent.cc b/src/inet/common/FunctionalEvent.cc new file mode 100644 index 00000000000..8da22e17f7a --- /dev/null +++ b/src/inet/common/FunctionalEvent.cc @@ -0,0 +1,36 @@ +// +// Copyright (C) 2024 OpenSim Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later +// + + +#include "inet/common/FunctionalEvent.h" + +namespace inet { + +void scheduleAt(const char *name, simtime_t time, std::function f) +{ + if (time < simTime()) + throw cRuntimeError("Cannot schedule function to the past"); + auto event = new FunctionalEvent(name, f); + event->setArrivalTime(time); + getSimulation()->insertEvent(event); +} + +void scheduleAfter(const char *name, simtime_t delay, std::function f) +{ + if (delay < 0) + throw cRuntimeError("Cannot schedule function to the past"); + scheduleAt(name, simTime() + delay, f); +} + +void FunctionalEvent::execute() +{ + f(); + removeFromOwnershipTree(); + delete this; +} + +} // namespace inet + diff --git a/src/inet/common/FunctionalEvent.h b/src/inet/common/FunctionalEvent.h new file mode 100644 index 00000000000..3d3c730e77e --- /dev/null +++ b/src/inet/common/FunctionalEvent.h @@ -0,0 +1,47 @@ +// +// Copyright (C) 2024 OpenSim Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later +// + + +#ifndef __INET_FUNCTIONALEVENT_H +#define __INET_FUNCTIONALEVENT_H + +#include "inet/common/INETDefs.h" + +namespace inet { + +using namespace std; + +/** + * Schedules a lambda function for execution at a specific simulation time. + */ +INET_API void scheduleAt(const char *name, simtime_t time, std::function f); + +/** + * Schedules a lambda function for execution after a specific simulation time. + */ +INET_API void scheduleAfter(const char *name, simtime_t delay, std::function f); + +/** + * This event executes a lambda function. + */ +class INET_API FunctionalEvent : public cEvent +{ + private: + std::function f; + + public: + FunctionalEvent(const char *name, std::function f) : cEvent(name), f(f) { } + + virtual cEvent *dup() const override { return new FunctionalEvent(getName(), f); } + virtual cObject *getTargetObject() const override { return nullptr; } + + virtual void execute() override; +}; + +} // namespace inet + +#endif + diff --git a/src/inet/common/FunctionalEventExample.cc b/src/inet/common/FunctionalEventExample.cc new file mode 100644 index 00000000000..e0244b178a8 --- /dev/null +++ b/src/inet/common/FunctionalEventExample.cc @@ -0,0 +1,34 @@ +// +// Copyright (C) 2024 OpenSim Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later +// + + +#include "inet/common/FunctionalEventExample.h" + +#include "inet/common/FunctionalEvent.h" + +namespace inet { + +Define_Module(FunctionalEventExample); + +void FunctionalEventExample::initialize() +{ + int count = par("count"); + simtime_t maxDelay = par("maxDelay"); + for (int i = 0; i < count; i++) { + simtime_t delay = uniform(0, maxDelay); + inet::scheduleAfter("scheduleAfter", delay, [=] () { + std::cout << "At: " << simTime() << " scheduleAfter event " << i << " executed" << std::endl; + }); + std::cout << "At: " << simTime() << " scheduled scheduleAfter event " << i << " with delay " << delay << std::endl; + simtime_t time = simTime() + uniform(0, maxDelay); + inet::scheduleAt("scheduleAt", time, [=] () { + std::cout << "At: " << simTime() << " scheduleAt event " << i << " executed" << std::endl; + }); + std::cout << "At: " << simTime() << " scheduled scheduleAt event " << i << " at time " << time << std::endl; + } +} + +} // namespace inet diff --git a/src/inet/common/FunctionalEventExample.h b/src/inet/common/FunctionalEventExample.h new file mode 100644 index 00000000000..2f3bf15584c --- /dev/null +++ b/src/inet/common/FunctionalEventExample.h @@ -0,0 +1,23 @@ +// +// Copyright (C) 2024 OpenSim Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later +// + + +#ifndef __INET_FUNCTIONALEVENTEXAMPLE_H +#define __INET_FUNCTIONALEVENTEXAMPLE_H + +#include "inet/common/INETDefs.h" + +namespace inet { + +class INET_API FunctionalEventExample : public cSimpleModule +{ + protected: + virtual void initialize() override; +}; + +} // namespace inet + +#endif diff --git a/src/inet/common/FunctionalEventExample.ned b/src/inet/common/FunctionalEventExample.ned new file mode 100644 index 00000000000..3bb4f7109d6 --- /dev/null +++ b/src/inet/common/FunctionalEventExample.ned @@ -0,0 +1,18 @@ +// +// Copyright (C) 2024 OpenSim Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later +// + +package inet.common; + +// +// Example module that demonstrates the use of ~FunctionalEvent for scheduling +// lambda functions at specific simulation times. +// +simple FunctionalEventExample +{ + parameters: + int count = default(3); + double maxDelay @unit(s) = default(1s); +} diff --git a/src/inet/common/SimulationContinuation.cc b/src/inet/common/SimulationContinuation.cc new file mode 100644 index 00000000000..f5c132f30b7 --- /dev/null +++ b/src/inet/common/SimulationContinuation.cc @@ -0,0 +1,46 @@ +// +// Copyright (C) 2024 OpenSim Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later +// + + +#include "inet/common/SimulationContinuation.h" + +namespace inet { + +// ---- sleepSimulationTime ---- + +void sleepSimulationTime(simtime_t duration) +{ + // Schedule the resume event BEFORE suspending, because suspendEvent() + // does not return until the event is actually resumed. + resumeEvent(currentEventLoopContext, simTime() + duration); + suspendEvent(); + // Resumed after wake-up event fired +} + +// ---- SimulationContinuation ---- + +void SimulationContinuation::suspend() +{ + if (isStopped) + throw cRuntimeError("Cannot suspend event execution because suspend() has been already called"); + isStopped = true; + // Save the context pointer BEFORE suspending, so resume() can use it later + suspendedContext = currentEventLoopContext; + suspendEvent(); + // Resumed after resume() scheduled a wake-up event +} + +void SimulationContinuation::resume() +{ + if (!isStopped) + throw cRuntimeError("Cannot resume event execution because suspend() has not been called yet"); + isStopped = false; + resumeEvent(suspendedContext, simTime()); + suspendedContext = nullptr; +} + +} // namespace inet + diff --git a/src/inet/common/SimulationContinuation.h b/src/inet/common/SimulationContinuation.h new file mode 100644 index 00000000000..c54181e7fff --- /dev/null +++ b/src/inet/common/SimulationContinuation.h @@ -0,0 +1,39 @@ +// +// Copyright (C) 2024 OpenSim Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later +// + + +#ifndef __INET_SIMULATIONCONTINUATION_H +#define __INET_SIMULATIONCONTINUATION_H + +#include "inet/common/INETDefs.h" + +namespace inet { + +/** + * Stops the execution of the current event and schedules resuming the execution after the specified simulation time. + */ +INET_API void sleepSimulationTime(simtime_t duration); + +/** + * This class supports temporarily stopping and later resuming the execution of the current event. + */ +class INET_API SimulationContinuation +{ + protected: + bool isStopped = false; + EventLoopContext *suspendedContext = nullptr; + + public: + virtual ~SimulationContinuation() { } + + virtual void suspend(); + virtual void resume(); +}; + +} // namespace inet + +#endif + diff --git a/src/inet/common/SimulationContinuationExample.cc b/src/inet/common/SimulationContinuationExample.cc new file mode 100644 index 00000000000..fb373870e19 --- /dev/null +++ b/src/inet/common/SimulationContinuationExample.cc @@ -0,0 +1,45 @@ +// +// Copyright (C) 2024 OpenSim Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later +// + + +#include "inet/common/SimulationContinuationExample.h" + +#include "inet/common/FunctionalEvent.h" +#include "inet/common/SimulationContinuation.h" + +namespace inet { + +Define_Module(SimulationContinuationExample); + +void SimulationContinuationExample::initialize() +{ + scheduleAt(simTime(), new cMessage("start")); +} + +void SimulationContinuationExample::handleMessage(cMessage *msg) +{ + delete msg; + runSimulationContinuationExample(); +} + +void SimulationContinuationExample::runSimulationContinuationExample() +{ + int count = par("count"); + simtime_t maxSleepTime = par("maxSleepTime"); + for (int i = 0; i < count; i++) { + SimulationContinuation continuation; + inet::scheduleAfter("resume", uniform(0, maxSleepTime), [&] () { + std::cout << "At: " << simTime() << " step " << i << " resuming" << std::endl; + continuation.resume(); + }); + std::cout << "At: " << simTime() << " step " << i << " suspending" << std::endl; + continuation.suspend(); + std::cout << "At: " << simTime() << " step " << i << " resumed" << std::endl; + } + std::cout << "At: " << simTime() << " all steps finished" << std::endl; +} + +} // namespace inet diff --git a/src/inet/common/SimulationContinuationExample.h b/src/inet/common/SimulationContinuationExample.h new file mode 100644 index 00000000000..4e21c763d97 --- /dev/null +++ b/src/inet/common/SimulationContinuationExample.h @@ -0,0 +1,26 @@ +// +// Copyright (C) 2024 OpenSim Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later +// + + +#ifndef __INET_SIMULATIONCONTINUATIONEXAMPLE_H +#define __INET_SIMULATIONCONTINUATIONEXAMPLE_H + +#include "inet/common/INETDefs.h" + +namespace inet { + +class INET_API SimulationContinuationExample : public cSimpleModule +{ + protected: + virtual void initialize() override; + virtual void handleMessage(cMessage *msg) override; + + virtual void runSimulationContinuationExample(); +}; + +} // namespace inet + +#endif diff --git a/src/inet/common/SimulationContinuationExample.ned b/src/inet/common/SimulationContinuationExample.ned new file mode 100644 index 00000000000..2c49cf8ceb2 --- /dev/null +++ b/src/inet/common/SimulationContinuationExample.ned @@ -0,0 +1,18 @@ +// +// Copyright (C) 2024 OpenSim Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later +// + +package inet.common; + +// +// Example module that demonstrates the use of ~SimulationContinuation for +// suspending and resuming event execution. +// +simple SimulationContinuationExample +{ + parameters: + int count = default(3); + double maxSleepTime @unit(s) = default(1s); +} diff --git a/src/inet/common/SimulationTask.cc b/src/inet/common/SimulationTask.cc new file mode 100644 index 00000000000..8057fed9d16 --- /dev/null +++ b/src/inet/common/SimulationTask.cc @@ -0,0 +1,49 @@ +// +// Copyright (C) 2024 OpenSim Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later +// + + +#include + +#include "inet/common/FunctionalEvent.h" +#include "inet/common/SimulationTask.h" + +namespace inet { + +SimulationTask::~SimulationTask() +{ + delete joinChildrenContinuation; +} + +bool SimulationTask::isChildrenFinished() +{ + return std::all_of(children.begin(), children.end(), [] (SimulationTask *task) { return task->isFinished; }); +} + +void SimulationTask::spawnChild(std::function f) +{ + SimulationTask *childTask = new SimulationTask(); + children.push_back(childTask); + inet::scheduleAfter("TaskStartEvent", 0, [=] () { + f(); + childTask->isFinished = true; + if (isChildrenFinished() && joinChildrenContinuation != nullptr) + joinChildrenContinuation->resume(); + }); +} + +void SimulationTask::joinChildren() +{ + if (!isChildrenFinished()) { + joinChildrenContinuation = new SimulationContinuation(); + joinChildrenContinuation->suspend(); + } + for (auto childTask : children) + delete childTask; + children.clear(); +} + +} // namespace inet + diff --git a/src/inet/common/SimulationTask.h b/src/inet/common/SimulationTask.h new file mode 100644 index 00000000000..30abaf86808 --- /dev/null +++ b/src/inet/common/SimulationTask.h @@ -0,0 +1,39 @@ +// +// Copyright (C) 2024 OpenSim Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later +// + + +#ifndef __INET_SIMULATIONTASK_H +#define __INET_SIMULATIONTASK_H + +#include "inet/common/SimulationContinuation.h" + +namespace inet { + +/** + * This class supports spawning child simulation tasks that run concurrently in simulation time. + */ +class INET_API SimulationTask +{ + protected: + bool isFinished = false; + SimulationTask *parent = nullptr; + std::vector children; + SimulationContinuation *joinChildrenContinuation = nullptr; + + protected: + virtual bool isChildrenFinished(); + + public: + virtual ~SimulationTask(); + + virtual void spawnChild(std::function f); + virtual void joinChildren(); +}; + +} // namespace inet + +#endif + diff --git a/src/inet/common/SimulationTaskExample.cc b/src/inet/common/SimulationTaskExample.cc new file mode 100644 index 00000000000..5c3bbb5873b --- /dev/null +++ b/src/inet/common/SimulationTaskExample.cc @@ -0,0 +1,44 @@ +// +// Copyright (C) 2024 OpenSim Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later +// + + +#include "inet/common/SimulationTaskExample.h" + +#include "inet/common/SimulationTask.h" + +namespace inet { + +Define_Module(SimulationTaskExample); + +void SimulationTaskExample::initialize() +{ + scheduleAt(simTime(), new cMessage("start")); +} + +void SimulationTaskExample::handleMessage(cMessage *msg) +{ + delete msg; + runSimulationTaskExample(); +} + +void SimulationTaskExample::runSimulationTaskExample() +{ + int count = par("count"); + simtime_t maxSleepTime = par("maxSleepTime"); + std::cout << "At: " << simTime() << " parent started" << std::endl; + SimulationTask parentTask; + for (int i = 0; i < count; i++) { + parentTask.spawnChild([=] () { + std::cout << "At: " << simTime() << " child " << i << " started" << std::endl; + sleepSimulationTime(uniform(0, maxSleepTime)); + std::cout << "At: " << simTime() << " child " << i << " finished" << std::endl; + }); + } + parentTask.joinChildren(); + std::cout << "Parent finished" << std::endl; +} + +} // namespace inet diff --git a/src/inet/common/SimulationTaskExample.h b/src/inet/common/SimulationTaskExample.h new file mode 100644 index 00000000000..3ce7f47ecee --- /dev/null +++ b/src/inet/common/SimulationTaskExample.h @@ -0,0 +1,26 @@ +// +// Copyright (C) 2024 OpenSim Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later +// + + +#ifndef __INET_SIMULATIONTASKEXAMPLE_H +#define __INET_SIMULATIONTASKEXAMPLE_H + +#include "inet/common/INETDefs.h" + +namespace inet { + +class INET_API SimulationTaskExample : public cSimpleModule +{ + protected: + virtual void initialize() override; + virtual void handleMessage(cMessage *msg) override; + + virtual void runSimulationTaskExample(); +}; + +} // namespace inet + +#endif diff --git a/src/inet/common/SimulationTaskExample.ned b/src/inet/common/SimulationTaskExample.ned new file mode 100644 index 00000000000..6a931888020 --- /dev/null +++ b/src/inet/common/SimulationTaskExample.ned @@ -0,0 +1,18 @@ +// +// Copyright (C) 2024 OpenSim Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later +// + +package inet.common; + +// +// Example module that demonstrates the use of ~SimulationTask for spawning +// child simulation tasks that run concurrently in simulation time. +// +simple SimulationTaskExample +{ + parameters: + int count = default(3); + double maxSleepTime @unit(s) = default(1s); +}