hello-world Tutorial
This tutorial shows the basic operation of poco and showcases the scheduler’s operation.
The goal here is to have a program that prints the string Hello World! 5 times, then
exits.
In this tutorial, you will be exposed to the following poco concepts.
Task creation.
Scheduler use.
Yielding.
Note
The hello world sample is located within the project under samples/hello-world.
If using CMake, the target sample_hello_world builds this example.
The full program snippet can be found at the bottom of this page.
Overview
The operation of this program uses 2 tasks. A task for printing the string Hello ``,
which we will call the ``hello_task, and the other for printing World!\n, which
is will be called the world_task.
The execution plan can be summarised below.
Execution plan for tasks.
Defining the Tasks
Thinking about the tasks in isolation, each task will either print the value Hello
or World!\n the console, then wait until the next task.
Each task function has a user provided context. This context allows programs to pass
in references to items the task requires to operate. In this tutorial, the additional
context is not required.
The sample below shows the implementation for the task printing Hello. This can be
implemented using normal flow control, and does not require any special handling.
void hello_task(void *context) {
(void)context;
for (int i = 0; i < 5; ++i) {
printf("Hello ");
coro_yield();
}
}
The key line is where the yield takes place. This instructs the scheduler to pause the current execution of the function, and move on to the next task. When the scheduler returns to this function, it will resume where it left off.
We can also make the World\n task in the same manner.
(void)context;
for (int i = 0; i < 5; ++i) {
printf("World!\n");
coro_yield();
}
}
int main() {
Coroutine Creation
Each function that will be run as a task will need to be associated with a single
coroutine structure. These can be defined in the program’s main.
Each of the calls to coro_create will create a coroutine structure used by the
scheduler. This includes all the necessary information to allow coroutines to pause and
resume execution.
Coro *tasks[] = {
coro_create(hello_task, NULL, STACK_SIZE),
coro_create(world_task, NULL, STACK_SIZE),
};
Scheduler *scheduler =
round_robin_scheduler_create(tasks, sizeof(tasks) / sizeof(tasks[0]));
Each call takes in 3 arguments, the function this coroutine will run, a user provided context, and the size of stack to allocate to this coroutine.
As this is a simple example, we can use the platform provided default stack sizes, as
well as setting the context to NULL. Any context provided here will be passed to
the function signature.
The second step involves creation of the scheduler. poco provides some basic scheduler
implementations to use. In this example, we will use round_robin_scheduler
as it provided the behaviour we require (alternating between all tasks).
Every scheduler implements a generic scheduler interface to allow for implementations
to support a wider variety of schedulers if needed. Once created, we can run using the
scheduler_run() method to run the freshly created scheduler.
if (scheduler == NULL) {
printf("Failed to create scheduler\n");
return -1;
}
scheduler_run(scheduler);
return 0;
}
Running the Program
Execution of the chosen scheduler always begins with the first task in the list. In this example, it would be the hello task.
This results in the printouts to the console.
Hello World!
Hello World!
Hello World!
Hello World!
Hello World!
Full Reference
// SPDX-FileCopyrightText: Copyright contributors to the poco project.
// SPDX-License-Identifier: MIT
/*!
* @file
* @brief Hello world example, performs the classic hello world example using
* coroutines.
*
* Each task sends either Hello, or World!, and alternates between the two tasks.
*
* The result should be 5 instances of the string "Hello world" printed to stdout.
*
* This example uses the basic round robin scheduler.
*/
#include <poco/poco.h>
#include <stdio.h>
#define STACK_SIZE (DEFAULT_STACK_SIZE)
void hello_task(void *context) {
(void)context;
for (int i = 0; i < 5; ++i) {
printf("Hello ");
coro_yield();
}
}
void world_task(void *context) {
(void)context;
for (int i = 0; i < 5; ++i) {
printf("World!\n");
coro_yield();
}
}
int main() {
Coro *tasks[] = {
coro_create(hello_task, NULL, STACK_SIZE),
coro_create(world_task, NULL, STACK_SIZE),
};
Scheduler *scheduler =
round_robin_scheduler_create(tasks, sizeof(tasks) / sizeof(tasks[0]));
if (scheduler == NULL) {
printf("Failed to create scheduler\n");
return -1;
}
scheduler_run(scheduler);
return 0;
}