Polling: Event Driven Programming#
Threads Are Expensive#
Each thread requires its own stack
Too big ⟶ waste
Too small ⟶ undefined behavior
Too many ⟶ bad by nature
Context switches are not free
#include <why.h>
#include <why-thread.h>
#include <why-messagequeue.h>
#include <why-irq.h>
#include <why-time.h>
int main()
{
Why::init();
Why::MessageQueue<int> q1(7);
Why::MessageQueue<int> q2(5);
Why::IRQ::connect(7, [&q1](int){auto ok = q1.put_noblock(1); assert(ok);});
Why::IRQ::connect(42, [&q2](int){auto ok = q2.put_noblock(2); assert(ok);});
Why::Thread t1([&q1](){ // <-- stack memory usage
while (true) {
int elem = q1.get(); // <-- blocks
std::println("t1: {}", elem);
}
});
Why::Thread t2([&q2](){ // <-- stack memory usage
while (true) {
int elem = q2.get(); // <-- blocks
std::println("t2: {}", elem);
}
});
return 0;
}
Polling Is Expensive#
Alternative: collapse into one single thread
⟶ How to block on two different message queues in one single thread?
Naive answer: don’t block - poll!
#include <why.h>
#include <why-messagequeue.h>
#include <why-irq.h>
int main()
{
Why::init();
Why::MessageQueue<int> q1(7);
Why::MessageQueue<int> q2(5);
Why::IRQ::connect(7, [&q1](int){auto ok = q1.put_noblock(1); assert(ok);});
Why::IRQ::connect(42, [&q2](int){auto ok = q2.put_noblock(2); assert(ok);});
while (true) {
auto elem1 = q1.get_noblock();
if (elem1)
std::println("q1: {}", *elem1);
auto elem2 = q2.get_noblock();
if (elem2)
std::println("q2: {}", *elem2);
}
return 0;
}
Polling Made Cool#
Polling API (much like Linux/UNIX)
May block until one or more kernel objects are ready
Zephyr (see Zephyr: Polling API)
A semaphore becomes available
A kernel FIFO contains data ready to be retrieved
A kernel LIFO contains data ready to be retrieved
A kernel message queue contains data ready to be retrieved
A kernel pipe contains data ready to be retrieved
A poll signal is raised
Linux: anything that can be represented by a file descriptor
#include <why.h>
#include <why-messagequeue.h>
#include <why-irq.h>
#include <why-eventloop.h>
int main()
{
Why::init();
Why::MessageQueue<int> q1(7);
Why::MessageQueue<int> q2(5);
Why::IRQ::connect(7, [&q1](int){auto ok = q1.put_noblock(1); assert(ok);});
Why::IRQ::connect(42, [&q2](int){auto ok = q2.put_noblock(2); assert(ok);});
Why::Eventloop loop;
loop.watch(q1, [&q1](){
int elem = q1.get(); // <-- does not block
std::println("q1: {}", elem);
});
loop.watch(q2, [&q2](){
int elem = q2.get(); // <-- does not block
std::println("q2: {}", elem);
});
loop(); // <-- blocks
return 0;
}