<volatileの必要性1>
皆様のおかげさまを持ちまして、今年もD-CLUE社員旅行に行くことができました。
今年も恒例の沖縄です。
沖縄にきてしばらくして感じたことは、なんとなく自分がのんびりしているなと感じたことです。それは海があるからなのでしょうか?自然があるからなのでしょうか?昔は全然考えなかったのですが、ふと今回そんなことを感じました。
「海岸線を走っているとき」、「山の中を走っているとき」。それらの全てを含めてここでは時間がゆっくりと流れている感じがしています。もちろん仕事で来ているわけではありませんので、なにかに急かさせることもないのですが、この明るい日差しと波の音がゆっくりとした時間の流れを感じさせてくれるんだなと思い、自分の心をリフレッシュさせることができました。
また、沖縄と言えば「沖縄そば」が有名ですね。私は「ソーキそば」よりもこのもやしがたっぷりと乗った「牛肉そば」が好きです。
testFunc()という関数を考えますと、変数[initialFlag]を"1"に初期化した後、[initialwait()]という関数を呼び出します。
[initialWait()]では[initialFlag]が"0"にクリアされるのを待っていて、"0"になったらwhile文を抜けて[testFunc()]に戻ってきます。
自分で変数を"1"に初期化したのに、"0"を待っているなんて、一見あり得ないようなプログラムですが、以下のような場合が考えられます。
事前にデバイスドライバー等を初期化しておいて、外部からのイベント(電源ボタンの押下等)をひたすら待っている場合等がその例だと思います。
さて、今回これらのプログラムがどう動くかを見てみます。と言うわけで、恒例のアセンブラです。
ここで、問題となるのは49c番地の動作です。
先ほどのソースと大きく違うのは、49c番地の動作では、再度[initialFlag]の中身をLoadせずに、CPU内部の演算結果だけをみていることです。
従って、この場合は、割り込みハンドラ側でイベントを受け付けたことにより"0"を設定しても、while()のLoopは抜けなくなってしまいます。
「デバッグしている時は、動いていたのに、最終試験をすると動かない。。」ということになります。
さて、どうするか。。。そこで[volatile]宣言の活躍です。
先ほどのプログラムから変数の宣言だけを変更します。

そこで、再度アセンブラコードをみますと。

490-494 :r2レジスタにinitialFlagのアドレスをLoadしています。
498 :r3レジスタにinitialFlagの中身をLoadします。(最初は"1")
49a :r3レジスタの値を"0"と比較します。
49c :違っていたら498番地に移動します。
今度の場合は、[498番地]の比較結果がNGの場合は [498番地]に戻っています。
[498番地]では再度r2(initialFlag)の中身をLoadしますので、もしも誰かが変更している場合は、新しく"0"という値が取得出来ることになります。
割り込み処理等が無くシングルタスクの場合、あるいは最適化オプションが無い場合は問題ないかもしれませんが、そのようなプログラムはあまり一般的ではないと思いますので、改めてきちんと付けることにしましょう。
さて、心がリフレッシュできたところで、体はと言いますと。
今年もD-CLUE社員旅行恒例の、「大イベント大会」を行いました。
海岸で体をつかったイベントや、ゲームをやって盛り上がるのですが、今年は急遽「大水泳大会」も追加されました。
急遽はじまった水泳大会ですが、たまにプールに行っていることもあり若干余裕かと思ったのですが、急に泳いだら足がつってしまい。。σ(^◇^;)
ちょっと40歳を超えてなにか、今までのように行かない自分に。。。そんなことも気がつかせてくれるすてきな沖縄でした。(~ヘ~)ウーン。。改めて準備(運動)が必要です。
最後に、沖縄の注意点ですが。。
遊びすぎに注意しましょう。。
あまり遅くに空港に行きますと。

そのvolatileの使い方は、規格上はマズイです。
volatileは揮発修飾子。つまり外部入出力を主目的とした修飾子です。
volatileは、I/Oのために演算の順番保証はします。
ですが、volatile変数自体がメモリー上に配置されていることは
一切保証されません。また、規格上はvolatile修飾子を
つけた変数は、1つ前の代入結果が保持されることを
保証しません。1度代入した後、次の読み出しで値が
変わっていても構わないことになっています。これに従えば、
volatile変数自体の実体はどのようになっていても構わないため、
1つの関数を実行している間中volatile変数の実体を常に
レジスタに割り当てても良いことになります。
呼び出し元に戻ってくる割り込みはともかく、このレジスタ割り当てが
もしマルチコア、マルチスレッド環境で行われた場合、スレッドごとに
変数値が異なることになります。これにより、ループの脱出指示は
正しく記述しているにも関わらず、いつまで立っても別スレッドの
ループを抜けられないという事が発生します。
まぁ、あくまで現実は実装依存ですが。
参考元:
http://www.open-std.org/jtc1/sc22/WG14/www/docs/C99RationaleV5.10.pdf
コメントありがとうございます。
今回、シングルコアでの動作+関数の外部に宣言することでグロー
バル化を図らせていただいております。
その条件において、volatileの宣言と処理の最適化ということにつ
いての記載をさせて頂きました。
コンパイラでの出力結果においても、特に問題なく動作していまし
たので今回記事として記載させて頂きました。
開発の際は、コンパイラーに頼るところもございます。
正規の規格および、コンパイラーの実装状態等を理解し利用すること
は大事ですね。
あらためて、ありがとうございました。