Skip to content

Usage_of_Barriers

Olaf Maibaum edited this page Oct 9, 2020 · 1 revision

Usage of Barriers

The configuration of inputs is defined at compile time. However, there are situations where the number of expected inputs depends on the run time state. E.g. when a device is not powered you will not receive data from the device. This situation can be handled by the class Barrier.

As a code example, in the following sequence a sender task, a receiver task, and a state estimation task are set up for three redundant devices. For simplicity of the example channels for the current configuration states are left out.

#include <string>
#include <iostream>

#include <schedulerProvider.h>
#include <schedulePolicyFifo.h>
#include <task.h>
#include <taskEvent.h>
#include <taskBarrier.h>

bool powerState[] = {true, true, true};
typedef unsigned int DeviceId;

class EstimatorTask : public Tasking::TaskProvider<1u, Tasking::SchedulePolicyFifo>
{
public:
    EstimatorTask(Tasking::Scheduler& scheduler);
    void execute(void) override;
};

EstimatorTask::EstimatorTask(Tasking::Scheduler& scheduler) :
                TaskProvider(scheduler)
{
    inputs[0].configure(1u);
}

void EstimatorTask::execute(void)
{
    std::cout << "Estimate" << std::endl;
}

class Device
{
public:
    static const Tasking::Time deviceDelay = 500;

    Device(Tasking::Scheduler& sched, DeviceId deviceId, Tasking::Event& trigger, Tasking::Barrier& barrier);

protected:

    class SenderTask : public Tasking::TaskProvider<1, Tasking::SchedulePolicyFifo>
    {
    public:
        SenderTask(Tasking::Scheduler& sched, DeviceId deviceId, Tasking::Barrier& barrier, Tasking::Event& delay);
        void execute(void) override;
    protected:
        DeviceId id;
        Tasking::Barrier& outBarrier;
        Tasking::Event& outDelay;
    };

    class ReceiverTask : public Tasking::TaskProvider<1, Tasking::SchedulePolicyFifo>
    {
    public:
        ReceiverTask(Tasking::Scheduler& sched, DeviceId deviceId, Tasking::Barrier& barrier);
        void execute(void) override;
    protected:
        DeviceId id;
        Tasking::Barrier& outBarrier;
    };

    SenderTask sender;
    ReceiverTask receiver;
    Tasking::Event delay;
};

Device::Device(Tasking::Scheduler& sched, DeviceId deviceId, Tasking::Event& trigger, Tasking::Barrier& barrier) :
                sender(sched, deviceId, barrier, delay), receiver(sched, deviceId, barrier), delay(sched)
{
    sender.configureInput(0u, trigger);
    receiver.configureInput(0u, delay);
}

Device::SenderTask::SenderTask(Tasking::Scheduler& sched,
                               DeviceId deviceId,
                               Tasking::Barrier& barrier,
                               Tasking::Event& delay) :
                                 Tasking::TaskProvider<1, Tasking::SchedulePolicyFifo>(sched), id(deviceId),
                                 outBarrier(barrier), outDelay(delay)
{
    inputs[0].configure(1);
}

void Device::SenderTask::execute(void)
{
    if(powerState[id])
    {
        outBarrier.increase();
        std::cout << "Send data to device " << id << std::endl;
        outDelay.trigger(deviceDelay);
    }
    outBarrier.push();
}

Device::ReceiverTask::ReceiverTask(Tasking::Scheduler& sched, DeviceId deviceId, Tasking::Barrier& barrier) :
                Tasking::TaskProvider<1, Tasking::SchedulePolicyFifo>(sched), id(deviceId), outBarrier(barrier)
{
    inputs[0].configure(1);
}

void Device::ReceiverTask::execute(void)
{
    std::cout << "Read data from device " << id << std::endl;
    outBarrier.push();
}

Tasking::SchedulerProvider<1u, Tasking::SchedulePolicyFifo> scheduler;
Tasking::Event event(scheduler);
Tasking::Barrier barrier(3u);
Device device1(scheduler, 0u, event, barrier);
Device device2(scheduler, 1u, event, barrier);
Device device3(scheduler, 2u, event, barrier);
EstimatorTask estimator(scheduler);

int main(int argc, char** argv)
{
    std::string blocker;

    estimator.configureInput(0u, barrier);
    event.setPeriodicTiming(1000, 400);

    scheduler.start();
    std::cin >> blocker;

    powerState[1]=false;
    std::cin >> blocker;
    
    scheduler.terminate(true);

    return 0;
}

In the example, for each device a sender and receiver task are defined and connected by an event with a delay for the response time of the device. The sender task is triggered by a periodic event. The sender task will start the delay only when the device is powered on. For the case the device is powered on, one more push operation on the barrier is expected, where on is the default for the increase of the barrier. The estimator task will be triggered by the barrier.

The barrier is pushed by three concurrent control flows, so it is set with each reset to the value of three expected pushes. Directly after start all devices are powered on, so six tasks are pushing on the barrier. When device2 is switched off, the number of pushes decreases to five by the one missing increase operation on the barrier for device2. So, the estimator starts when the receiver of the two remaining devices finishes the processing of the current control cycle.

For real programs it is recommded to manage the power state also in a channel and subscribe to it as an optional input. For our real applications based on the Tasking Framework all channels are addressable by the channel identification to sample telemetry data from the application.

Clone this wiki locally