volatileの使い方

プログラム

バグを生まないコーディング法、10個の規則でソフト開発を効率化(1/3) ― EE Times Japanで、一箇所だけ気になったのでコメント。

次の条件に当てはまる場合は、常にvolatile修飾子を使って宣言する。

(1)任意の割り込み処理ルーチンからアクセスされるグローバル変数を宣言するとき。
(2)2つ以上のタスクからアクセスされるグローバル変数を宣言するとき。
(3)メモリー・マップトI/O方式を採る周辺機器のレジスタ・セットにアクセスするためのポインタ変数を宣言するとき。例「timer_t volatile const *p_timer」。

 理由は次の通りである。コンパイラは、並列実行される複数のスレッドによって変更される可能性のある変数やレジスタであっても、それらに対する読み出し/書き込みコードが不要だと判断するとコード自体を削除するように最適化する場合がある。

 例えば次のような場合だ。あるスレッド「A」が、ある条件のときに変数の値を変更するとしよう。別のスレッド「B」はその変数をポーリングしていて、値が変更されたときに何らかの処理をする。このとき、スレッドBの中に変数の値を変更するようなコードが書かれていなければ、コンパイラはその変数の値は変更されないと判断してしまって最適化する場合がある。すると、コンパイル後のオブジェクト・コードでは、変数をポーリングするコードが削除されてしまったりする。volatile修飾子を適切に使うと、このようなコンパイラの最適化機能が無効となり、コード全体にわたるような発見が難しいバグの排除につながる。

(3)の場合はvolatileを使うべきだろうけど、(1)と(2)の場合はグローバル変数をvolatileにするのではなく、監視するタスク・関数内でvolatileな一時変数を用意し、そこにグローバル変数を代入して使うべきだと思うよ。

特に(1)の場合、割り込みルーチンから使用されるグローバル変数は、参照側のタスク・関数ではいつ変更されるのか全く予測できない事に注意が必要。

このため関数内での処理中にグローバル変数の内容が書き換えられることまで考慮するべきだから、積極的に一時変数に置き換えないとダメだと思うよ。