1. Run a task#
pw_async2: Cooperative async tasks for embedded
The two most fundamental components of pw_async2
are tasks and dispatchers.
Log a welcome message in a task#
Task is the basic unit of execution in the
pw_async2
framework. Tasks are objects that represent jobs to be done, like
blinking an LED, processing sensor data, or running a vending machine.
Study the code in
vending_machine.h
.1// The main task that drives the vending machine. 2class VendingMachineTask : public pw::async2::Task { 3 public: 4 VendingMachineTask() 5 : pw::async2::Task(PW_ASYNC_TASK_NAME("VendingMachineTask")) {} 6 7 private: 8 // This is the core of the asynchronous task. The dispatcher calls this method 9 // to give the task a chance to do work. 10 pw::async2::Poll<> DoPend(pw::async2::Context& cx) override; 11};
VendingMachineTask
inherits fromTask
.The
DoPend()
function is where your task will do work.Study the code in
vending_machine.cc
.Here you’ll find the incomplete implementation of
DoPend
.1pw::async2::Poll<> VendingMachineTask::DoPend(pw::async2::Context& cx) { 2 // Fill in your implementation here. 3 return pw::async2::Ready(); 4}
The
DoPend
method returns a Poll<>. APoll
can be in one of two states:Ready()
: The task has finished its work.Pending()
: The task is not yet finished. The dispatcher should run it again later.
Our current
DoPend()
immediately returnsReady()
, meaning it exits without doing any work.Log a welcome message in
vending_machine.cc
:Log the message at the start of the
DoPend()
implementation:PW_LOG_INFO("Welcome to the Pigweed Vending Machine!");
This logging macro comes from pw_log. We’ve already included the header that provides this macro (
pw_log/log.h
).Keep the
Ready()
return because this tells the dispatcher to complete the task after the message has been logged.
Hint
1pw::async2::Poll<> VendingMachineTask::DoPend(pw::async2::Context& cx) { 2 PW_LOG_INFO("Welcome to the Pigweed Vending Machine!"); 3 return pw::async2::Ready(); 4}
Post the task to a dispatcher#
The Dispatcher is the engine that runs the tasks. It’s a simple, cooperative scheduler. You give it tasks by calling Post(). You run tasks with the dispatcher by calling RunUntilStalled() or RunToCompletion().
The dispatcher maintains a queue of tasks that are ready to be polled. When a
run is triggered, it grabs a task from the queue and invokes the task’s
DoPend()
method. If the task returns Pending()
, the task is put to
sleep until it is woken by the operation that blocked it. If the task returns
Ready()
, the dispatcher considers it complete and will not run the task
again.
In
main.cc
, set up a task and run it with the dispatcher:Create an instance of
VendingMachineTask
Add the task to the dispatcher’s run queue by calling the dispatcher’s Post() method, passing the task as an arg
Tell the dispatcher to run all of its tasks until they return
Ready()
by calling its RunToCompletion() method
Hint
1int main() { 2 pw::async2::Dispatcher dispatcher; 3 codelab::HardwareInit(&dispatcher); 4 5 codelab::VendingMachineTask task; 6 dispatcher.Post(task); 7 8 dispatcher.RunToCompletion(); 9 10 return 0; 11}
Run the app#
Build and run the app again:
bazelisk run //pw_async2/codelab
You should see the same output as before, in addition to your new welcome message:
INF Welcome to the Pigweed Vending Machine!
Next steps#
You’ve written and run your first task with pw_async2
. Continue to
2. Call an async function to learn how to run async operations
in your task.
Check out Informed poll to learn more about the
conceptual programming model of pw_async2
.
Checkpoint#
At this point, your code should look similar to the files below.
#include "coin_slot.h"
#include "hardware.h"
#include "pw_async2/dispatcher.h"
#include "vending_machine.h"
namespace {
codelab::CoinSlot coin_slot;
} // namespace
// Interrupt handler function invoked when the user inserts a coin into the
// vending machine.
void coin_inserted_isr() { coin_slot.Deposit(); }
// Interrupt handler function invoked when the user presses a key on the
// machine's keypad. Receives the value of the pressed key (0-9).
void key_press_isr(int key) {
// In Step 3, implement your keypad handler here.
}
// Interrupt handler function invoked to simulate the item drop detector
// detecting confirmation that an item was successfully dispensed from the
// machine.
void item_drop_sensor_isr() {
// In Step 5 you will uses this as part of a new Dispense task that runs
// the dispenser motor until an item drops, or you time out on the vend
// operation.
}
int main() {
pw::async2::Dispatcher dispatcher;
codelab::HardwareInit(&dispatcher);
codelab::VendingMachineTask task;
dispatcher.Post(task);
dispatcher.RunToCompletion();
return 0;
}
#include "vending_machine.h"
#include "pw_async2/try.h"
#include "pw_log/log.h"
namespace codelab {
pw::async2::Poll<> VendingMachineTask::DoPend(pw::async2::Context& cx) {
PW_LOG_INFO("Welcome to the Pigweed Vending Machine!");
return pw::async2::Ready();
}
} // namespace codelab
#pragma once
#include "pw_async2/context.h"
#include "pw_async2/poll.h"
#include "pw_async2/task.h"
namespace codelab {
// The main task that drives the vending machine.
class VendingMachineTask : public pw::async2::Task {
public:
VendingMachineTask()
: pw::async2::Task(PW_ASYNC_TASK_NAME("VendingMachineTask")) {}
private:
// This is the core of the asynchronous task. The dispatcher calls this method
// to give the task a chance to do work.
pw::async2::Poll<> DoPend(pw::async2::Context& cx) override;
};
} // namespace codelab