Part 1. One core · lesson 8 of 42
8. Handlers¶
A handler is one piece of work inside a kernel, and the command says which one to run by its number. This lesson relies on lesson 4, Hello, core, lesson 6, A result and lesson 7, Arguments.
Why handlers¶
A program rarely does one thing. The kernels are loaded once (lesson 5, Commands), so the one
kernel has to contain every piece of work the program may ask for, and each command has to
say which one it wants. That is what the number you gave to submit first, the 0 of
lesson 4, was for. A handler is a piece of work with a number and a name.
All three kernels of a core receive every command. Each looks at the handler and does the
part that belongs to it. A kernel that has nothing for a handler ignores it, as the empty
the kernels in reader.cpp and compute.cpp have so far ignored everything.
The general form¶
The numbers and names live in one C++ header, which the kernels include and Python reads:
In the kernel, inside the function you give to run:
In Python:
program = km.Program("...", kernels=FOLDER, handlers=HEADER)
handle = run.submit("name", group=(0, 1))
NAMEis a word that starts with a capital letter, such asAddorBigStep.NUMBERis its number, from 1 to 239. The prefixkHandleris required.km::handler(command)is the number of the handler that this command asked for.WORKis what the kernel does for that handler.HEADERis the path of the header file.Programreads it, and from then onsubmitaccepts the handler's name in place of its number. The name is the C++ name without the prefix, in lower case with underscores between the words:kHandlerBigStepis"big_step",kHandlerAddis"add".- The number
0is the one handler you do not write. A command with handler 0 runs the kernels, and noifmatches it.
One line at a time¶
// The handlers of this program: the numbers that the kernels and Python agree on.
#pragma once
#include <stdint.h>
constexpr uint32_t kHandlerAdd = 1;
constexpr uint32_t kHandlerMultiply = 2;
Two handlers, Add with number 1 and Multiply with number 2. Both kernels and Python
agree on these numbers because they all read this one file.
writer.run([&](const km_command_t& command) {
const uint32_t left = command.a0;
const uint32_t right = command.a1;
if (km::handler(command) == kHandlerAdd) {
writer.result(left + right);
}
if (km::handler(command) == kHandlerMultiply) {
writer.result(left * right);
}
return true;
});
writer.cpp reads the two arguments, then checks the handler. A command for Add makes it
the sum, a command for Multiply the product. The two ifs do not overlap, so at most
one of them runs.
program = km.Program(
"handlers",
kernels=HERE / "kernels",
handlers=HERE / "kernels" / "handlers.h",
)
handlers= is the one new argument of Program: the header.
"add" is the handler's name in place of the number. The same line with "multiply"
asks for the other piece of work.
The whole program¶
"""Handlers: one kernel, two pieces of work; each command names the one it wants."""
from pathlib import Path
import komorebi as km
HERE = Path(__file__).resolve().parent
program = km.Program(
"handlers",
kernels=HERE / "kernels",
handlers=HERE / "kernels" / "handlers.h",
)
with km.Session() as session:
run = session.load(program)
handle = run.submit("add", group=(0, 1), args=[6, 7], result=True)
print(f"6 add 7 = {run.wait(handle, timeout=10.0).result}")
handle = run.submit("multiply", group=(0, 1), args=[6, 7], result=True)
print(f"6 multiply 7 = {run.wait(handle, timeout=10.0).result}")
run.stop()
// The writer kernel: the handler of the command decides what it computes.
#include "komorebi/device/km_worker.hpp"
#include "handlers.h"
void kernel_main() {
km::Writer writer;
writer.run([&](const km_command_t& command) {
const uint32_t left = command.a0;
const uint32_t right = command.a1;
if (km::handler(command) == kHandlerAdd) {
writer.result(left + right);
}
if (km::handler(command) == kHandlerMultiply) {
writer.result(left * right);
}
return true;
});
}
reader.cpp and compute.cpp are the empty ones; the header need not be included in them,
since they do not look at the handler.
Two commands with the same arguments, 6 and 7, and different handlers, and two different answers from the same loaded kernel.
Common mistakes¶
- A name that is not in the header.
run.submit("nothing", ...)stops withKeyError: "handlers has no handler 'nothing'". The same happens when you forgothandlers=inProgram: Python then knows no names, only numbers. - A handler number the kernel has no
iffor.run.submit(77, ...)is accepted, runs the kernels, and the result is 0. No message tells you that nothing matched. - A number outside 1 to 239.
run.submit(240, ...)stops withValueError: handler 240: not NOP and not in 1..KM_HANDLER_USER_MAX. The numbers above 239 belong to Komorebi.
Next: lesson 9, Many commands sends many commands at once.