Hereze

Mám rád exotické programovací jazyky i Arduino a říkám si: fakt jsme pro osmibity nevymysleli nic lepšího a pohodlnějšího než je C? Mám tendenci nevěřit lidem, co si myslí, že to dokonalé už vzniklo kdysi a všechno novější je nanic… Následují zmatené úvahy a kacířské myšlenky, které si tak poznamenávám, a asi by je nikdo neměl číst.

Jasně, takový Forth pro Arduino, to je sice kuriozní věc, ale mohla by fungovat. Teda kdyby lidi uměli ještě dneska Forth. Jinak to je sui generis stále rychlý nástroj se zajímavými vlastnostmi. Zlí jazykové sice tvrdí, že to je assembler, jen míň pochopitelný, autor Forthu je velký podivín (nebo radikální, jak to vezmete), ale zajímavý jazyk to rozhodně je, pro osmibity je docela vhodný a když víte jak na to, je to i silný vyjadřovací prostředek.

Občas přemýšlím nad Arduinem z tohoto úhlu pohledu. Máme tu harvardskou architekturu jednočipu, málo RAMky, osmibitový procesor – a céčko. Nebo assembler. Pořád stejné, s prominutím, paradigma. Nešlo by použít něco specifičtějšího? Něco, co by mělo silnější vyjadřovací schopnosti než céčko, ale bylo to zároveň nenáročné, aby se to vešlo do osmibitu?

Někdy, když mám čas a náladu, tak přemýšlím nad „alternativními vývojovými jazyky“ pro Arduino. Jako takové mentální cvičení, rozumíte? Teď v pátek mě napadlo udělat jednu věc, nad kterou jsem už před časem přemýšlel: nedívat se na Arduino jako na init() a loop(), ale jako na soubor mnoha souběžně fungujících programových modulů, které spolu komunikují. Takže například rozsvícení LEDky na stisk tlačítka by neprobíhalo stylem „if tlačítko then ledka“ v nekonečné smyčce, ale pomocí dvou deklarovaných procesů. Jeden hlídá tlačítko a jeho stav někam ukládá, druhý rozsvěcí LEDku podle instrukcí, co má. Blikač by byly taky dva procesy, jen místo toho s tlačítkem by tam byl proces s časovačem… Jo, trošku, ale fakt jen trošičku jsem se inspiroval Erlangem.

Jasně, že někde pod tímhle vším by ležel zase jen ten obyčejný assembler, nebo to céčko, nekonečná smyčka, a tohle všechno je jen syntaktický cukr, ale to asi není ten hlavní problém. Vždyť do důsledku vzato cokoli nad strojový kód je cukr, včetně hexadecimálního vyjádření operačních kódů. 😉

No a jak si tak občas s touhle myšlenkou pohrávám, tak si říkám, k čemu by to vedlo. Vývojář by musel programovat jinak. Jako že fakt jinak. Hodně to připomíná posun od klasického imperativního Céčka třeba někam k VHDL, kde taky není žádné „nejdřív, pak, potom, potom, potom ve smyčce toto, pak pokud toto, tak tamto, a nakonec toto“. Jen spousta bloků, které pracují najednou. Něco podobného, ale v menším, zažijete při první srážce s asynchronním programováním: nikdo vám nezaručí, že se toto stane až po tamtom.

Na druhou stranu: není snad Arduino plné asynchronních událostí, které je třeba obsloužit? Implementace JavaScriptu by byla (asi, ještě jsem to nepromýšlel) náročná, ale něco ve stylu „událost – obsluha“ by se možná hodilo…

A tak mě zrovna teď o víkendu napadlo udělat takový miniengine. Výsledkem je spousta omezení programátorského rozletu a kooperativní multithreading (proto ta omezení).

Představte si systém, kde může běžet několik vláken naráz. Protože jde o kooperativní multithreading, nikoli preemptivní, je potřeba trocha sebekontroly. Proto jsem si program rozdělil do miniaturních subprocesů. Každý subproces běží v jednom vlákně a platí pro něj několik základních věcí:

  • Subproces odpovídá céčkové funkci void(){…}
  • Subproces je buď opakován v daném vlákně stále dokola (loop), nebo je proveden jen jednou (a pak je vlákno uvolněno – one shoot), nebo může říct, který subproces má být spuštěný po něm (chain).
  • Pokud subproces určí svého následovníka (kterým může být i on sám), může říct, za jak dlouho má být následovník spuštěn (delayed).
  • Důvodem je to, že subproces má zakázáno volat delay()…
  • U kooperativního multithreadingu by vůbec nikdo neměl zdržovat. Měl by udělat to, co musí, a skončit. A protože jsem trochu radikální, tak bych možná zakázal i smyčky (ostatně jsou jazyky, co se bez nich obejdou). Fakt. Smyčku jako takovou lze nasimulovat pomocí subprocesu, který běží stále dokola, a při dosažení nějaké podmínky buď skončí, nebo předá řízení jinam.
  • Pokud máte tendenci třeba čekat na stisknutí tlačítka ve smyčce while(), tak to tady nepůjde. Místo toho je na místě udělat subproces, který někam oznámí, že tlačítko bylo stisknuto.
  • Subproces může zavolat další subproces v novém vlákně. Ovšem nečeká na jeho doběhnutí, ani na výsledek, prostě sám běží dál, nezávisle na ostatních vláknech.
  • Když subproces skončí (one shoot), vlákno je uvolněno.
  • Subproces má lokální proměnné. Jakmile doběhne, uvolní je.
  • S okolím komunikuje subproces tím, že mění hodnoty proměnných prostředí a spouští další subprocesy. Tady zvažuju, jestli toto nějak neomezit a nezvolit třeba model posílání zpráv mezi procesy, ale se všemi těmi semafory a frontami mi to připadá na osmibit s 2kB RAM hodně nákladné… Ještě promyslím.
  • Máme dvě kila RAMky, takže asi nějaké velké hraní si na ochranu paměti a izolaci procesů nepřipadá v úvahu, o správě zdrojů nemluvě. Ale asi by šlo udělat něco na způsob „prostředí“…
  • Nic vám nebrání spustit si subproces, který přistupuje k jednomu HW zdroji, třeba ve třech kopiích, ale dopadne to špatně. Lepší je postupovat stylem „každý HW má obslužný subproces“.
  • Přemýšlím, jestli je účelné povolit uspání vlákna a jeho probuzení jiným vláknem…

Výhoda toho všeho je, že subprocesy jsou maličké, přehledné, s jasně daným koncem (není nic jako „čekání“ nebo „nekonečná smyčka“). Nevýhoda je, že konečnost subprocesů závisí jen na tom, jestli programátor dobrovolně dodrží pravidla, která ho omezí. Třeba nebude používat delay(), ale odpočítá si potřebný čas z hodnoty časovače…

Na druhou stranu, když se toto vhodně zakapotuje a dostatečně osladí nějakými hezkými featurami, tak by to mohl být zajímavý nástroj pro určité kategorie aplikací. Ještě nad tím budu přemýšlet.

Líbil se vám článek? Podpořte autora na Patreonu
banner