シンプルすぎる汎用C++タスクスケジューラー(CScheduler)を作ってみた。

初めてアセンブラーを覚えたときCPUに使われるのではなくてCPUを思うままに操作する感覚が楽しすぎてマルチタスクスケジューラーなるものをいくつも自作した時があった。今や、組み込み系含め汎用OS全盛の時代でありそんなもの作ったってしょうがないと言う意見もあるが開発者の知識の高まりとともに主義主張ばかりがまかり通る時代になってきたのは少し残念というしかない。

そこで、多くの人たちが実装が不完全であるとか危険だからという理由で使うべきではないと虐められているsetjmp/longjmp/allocaを利用したC++言語に特化したタスクスケジューラークラスを作ってみた。今朝、ふと思い出して速攻で作ったものであるが似たようなものは数多く公開されているので興味があれば調べてもらいたい。恐らく地球上で一番コンパクトかつシンプルなものに仕上がったのではと思う。(笑)

必須要件はsetjmp/longjmp/allocaが正しく実装されている開発環境であることのみ。Arduino/Linux/Windows/etc…等の数多くの環境で動作するはずだ。

完全なマルチタスクではなくコンテクストスイッチングは不完全だし割り込み処理からの利用もできないが、組み込み系特有の細かく処理を分けて記述する煩雑なコードをマルチタスク風にシンプルに書けるようになるのが一番のメリットだろう。

ちなみにタスク管理領域はスタック領域に確保され次の図のようなメモリ構成になる。管理可能なタスク数に上限はなくスタック領域をどれだけ分割できるかに制限されるだけであるがスタック領域に関するチェックは行っていないので実際の利用にあたっては事前に割り込みを含めたスタックの利用状況の調査が必要であることに注意してほしい。

機能はタスクの切り替えを行うyieldしかないがArduino向けとして特別なdelay/delayMicroseconds(内部でyieldを実行)がおまけで実装してある。

仕組みが理解できればEvent/Semaphore等の機能を追加するのは簡単に出来るはず。やる気のある方のために宿題としておこう。(笑)

【各タスクのスタック構成の例】

【修正履歴】
[2020-02-11]
基本的なタスク同期クラス(CSemaphore/CMutex/CEvent)を追加してみた。ついでにクラス構成を整理整頓し、usedStackSize/freeStackSizeは、CStackクラスに移動。

[2020-02-01]
前回追加したメソッド名を変更。及び、AVR専用ではあるが空きスタック領域サイズを調べるメソッドを新たに追加(AVR以外では間違った結果が返されることがある)。空きスタック領域サイズには割り込み処理で使われるスタック領域(usedStackSizeの戻り値)が含まれることに注意すべし。

unsigned int usedStackSize(unsigned int seconds = 3); // 名称変更
unsigned int freeStackSize(int dummy = 0);

[2020-01-31]
スタック領域サイズ決定の参考用に下記メソッドを追加してみた。戻り値は各タスクに最低必要なスタック領域を返すので、あとは各タスクのネストする関数パラメタ+オート変数領域の合計値に余裕を足したサイズでadd()のスタックパラメタを調整すれば良い。戻り値は正確なものではないが参考用としては使えるだろう。

unsigned int stack(unsigned int seconds = 5);

【C++サンプル】

【Arduinoサンプル】

【CScheduler.h】

【CScheduler.cpp】

【CSynchronize.h】

【CSemaphore.h】

【CMutex.h】

【CEvent.h】

【CStack.h】