Index

Cooperating Sequential Tasks

  1. Introduction
    1. Arduino
    2. Interrupts
  2. Simple Tasks
  3. Multiple Tasks
  4. Communicating Tasks
  5. Communicating Values
  6. Synchronising Tasks
  7. Buffered Communication
  8. Multiple Senders
  9. Conditional Tasks
  10. Transput
  11. Implementation
    1. Common Data
    2. Tasks
    3. task_builder
    4. task
    5. channel
    6. virtual_machine
    7. set
    8. queue
    9. clock
    10. standard
    11. Adjusting Limits

Synchronising Multiple Tasks



In the previous example we saw how one sender could send data to multiple receivers. The ability of a task to send to many receivers can also be used without data being sent to allow one task to synchronise the activities of a number of others. To illustrate this, the following example shows how a simplified race between a number of runners might be started by a starter with a starting pistol.

#include "Tasks.h"

void get_ready(int number) 
{  
  Serial.print("Runner "); Serial.print(number); Serial.println(" Ready");
}

void running(int number) 
{  
  Serial.print("Runner "); Serial.print(number); Serial.println(" Running");
}

void finished(int number) 
{  
  Serial.print("Runner "); Serial.print(number); Serial.println(" Finished");
}

void runner(byte starting_pistol, int number, int race_time)
{
  new_task();
    action(get_ready, number);
    receive_from(starting_pistol);
    action(running, number);
    pause(race_time);
    action(finished, number);
    terminate();
  end_task();
}


Each runner has a number and will run their race in a specific race_time. The first thing each runner does is to get_ready and then waits for the starting_pistol to fire. They then announce that they are running and don't do anything else until their specified race_time has elapsed. At this point they announce that they have finished. So that we can see how the race unfolds, a suitable message is printed on the serial output at each stage.

The terminate function used here tells the multitasking library that when the task reaches this point it is to cease execution and not restart again from the beginning. This is equivalent to the task pausing for an infinite time.

#include "Tasks.h"

void starter(byte starting_pistol)
{
  new_task();
    pause(100);
    send_on(starting_pistol);
    action(bang);
    terminate();
  end_task();
}


The starter task waits for a short time to allow the runners to get_ready, and then sends a signal on the starting_pistol channel to the waiting runners. It then terminates.

#include "Tasks.h"

void setup()
{
  const int runners        = 5;
  int       performances[] = {30001000400020005000};
  
  init_tasks();
  init_serial(9600);
  
  byte starting_pistol = new_channel();
  starter(starting_pistol);
  for (int number = 1; number <= runners; number = number + 1
    runner(starting_pistol, number, performances[number - 1]);
}

void loop()
{
  run_system();
}


The interconnections between the starter and the runners is shown below.

images/7-1.png

The main program defines some number of runners and their races performances. It initialises the serial connection and then defines the starter task and the appropriate number of runner tasks passing each the identity of the starting_pistol channel, its race number and the time it will take to complete the race. All of the times are defined in milliseconds and are ridiculously short so that we don't have to wait a long time to see the results. When this program runs the following output will be send to the serial monitor connected to the Arduino.

Runner 1 Ready
Runner 2 Ready
Runner 3 Ready
Runner 4 Ready
Runner 5 Ready
Runner 1 Running
Runner 2 Running
Runner 3 Running
Runner 4 Running
Runner 5 Running
Runner 2 Finished
Runner 4 Finished
Runner 1 Finished
Runner 3 Finished
Runner 5 Finished


Obviously this is not an exact, nor a complete, description of a conventional running race. It does not describe how runners get_ready, nor how they run. In addition it is not possible for the starter to “fire” the starting pistol before all the runners are ready! However this general scheme of things is useful if a system needs to make sure than a number of tasks all begin their activities at the same time. Although all the receiving tasks are the same in this example, in a more general situation, they might all be different.

Next: Buffered Communication