トップ «前の日記(2009-06-02 (Tue)) 最新 次の日記(2009-08-03 (Mon))» 編集

uDiary

海野秀之(うんのひでゆき)の外部記憶

Twitter (twilog) / RSS / アンテナ / ぶくま

2006|07|08|09|10|11|12|
2007|01|02|03|04|05|06|07|08|09|10|11|12|
2008|01|02|03|04|05|06|07|08|09|10|11|12|
2009|01|02|03|04|05|06|08|
2010|01|02|03|05|06|07|10|11|
2011|03|08|
2012|02|04|07|08|10|
2013|01|02|03|05|06|08|11|12|
2014|01|02|05|06|07|08|09|12|
2015|01|02|03|04|

2009-06-03 (Wed)

pthread_mutex_lock/unlock していれば、volatile は要らないのか?

どうやら、「要らない」で良いらしい。

https://www.securecoding.cert.org/confluence/display/seccode/POS03-C.+Do+not+use+volatile+as+a+synchronization+primitive

When memory accesses are protected by a mutex, POSIX guarantees that memory is synchronized between threads and therefore volatile is not needed. See XBD 4.11 Memory Synchronization. There are very many (probably thousands) of existing pthread applications that rely on this. It's worth remembering that when pthreads were added to POSIX, it did not require implementations to support the C Standard (they had the option of supporting standard C or "common usage C", i.e. K&R), so there was no way it could require applications to use volatile.

ううむ。解せなかったんだが、XBD 4.11 を確認しておわりってことになりそうだ。

ぼくの C 言語仕様に関する理解では、mutex lock は必要、volatile も必要と考えるのが自然だと思う。

ここで問題になるのは、

  • メモリバリア
  • 変数の揮発性

のふたつだと思うのだが、ライブラリとして実現されている pthread_mutex_lock/unlock() は、 前者について適切に処理できても*1、 後者には手がだせないはずだから(と思っていた)。

"POSIX guarantees that memory is synchronized between threads and therefore volatile is not needed."っていうけどさ、 synchronized between threads っていうのは、 メモリオーダに関する(つまりメモリバリアを適切に挿入せよって) 話であって、volatile 云々は、コンパイラによる最適化を抑止して、 必ずメモリを参照してねって話でしょ。 両者は独立している筈なのに*2、なんで "therefore" っていえちゃうんだろうね。

が、どうやら、C の処理系として、どっちもよきにはからえというのが POSIX の要求らしい。

ふーん。

それって、リーズナブルな要求かなぁ。

「人にやさしい」という意味で妥当なのはわかるけど、 C の処理系にとって、適切に処理可能な問題なんだろうか?

この要求にこたえようとすると、すべての変数を、副作用のある関数コールをまたいで値を保持していないかもしれない(= volatile) ものとして扱うことにならない?

C の関数プロトタイプでは、「この関数によってクリティカルセッションに入ったり出たりします」なんて属性が表現されてたりしないし、 仮に宣言可能だとしても、ラップされちゃったらわかんなくなっちゃう。

まあいいけど。

volatile は要らないらしいというのは、わかった。

でも、納得はしない。「へんなのー」と思う。 C に関する「仕様」のなかで、一番嫌いかも知れない。C らしくないよ。

Quote: XBD 4.10 Memory Synchronization

Applications shall ensure that access to any memory location by more than one thread of control (threads or processes) is restricted such that no thread of control can read or modify a memory location while another thread of control may be modifying it. Such access is restricted using functions that synchronize thread execution and also synchronize memory with respect to other threads. The following functions synchronize memory with respect to other threads:

  • fork()
  • pthread_barrier_wait()
  • pthread_cond_broadcast()
  • pthread_cond_signal()
  • pthread_cond_timedwait()
  • pthread_cond_wait()
  • pthread_create()
  • pthread_join()
  • pthread_mutex_lock()
  • pthread_mutex_timedlock()
  • pthread_mutex_trylock()
  • pthread_mutex_unlock()
  • pthread_spin_lock()
  • pthread_spin_trylock()
  • pthread_spin_unlock()
  • pthread_rwlock_rdlock()
  • pthread_rwlock_timedrdlock()
  • pthread_rwlock_timedwrlock()
  • pthread_rwlock_tryrdlock()
  • pthread_rwlock_trywrlock()
  • pthread_rwlock_unlock()
  • pthread_rwlock_wrlock()
  • sem_post()
  • sem_trywait()
  • sem_wait()
  • wait()
  • waitpid()

The pthread_once() function shall synchronize memory for the first call in each thread for a given pthread_once_t object.

Unless explicitly stated otherwise, if one of the above functions returns an error, it is unspecified whether the invocation causes memory to be synchronized.

Applications may allow more than one thread of control to read a memory location simultaneously.

僕には、「これらの関数がロック変数の制御だけでなくて、メモリバリアも適切にいれてくれますよ」 と言っているだけにしか思えないんだけど。 なんでこれが「volatile qualifier いらない」の根拠になるのか、おっちゃん(僕)にはわからん。

FAQ だったもよう(thx to m.ukai さん)

http://www.lambdacs.com/cpt/FAQ.html#Q56

ほかにも、http://ml.tietew.jp/cppll/cppll/article/11701 も教えてもらった。そこには、以下のような記述が:

ちなみに、大抵のC++環境でのmutex lockは、保護の対象となるオブジェクトだけでなく メモリの全領域に対してメモリアクセス最適化を排除してしまうことになると思います。

うんうん、そう思う。

さらに、上で、「なんでこれが『volatile qualifier いらない』の根拠になるのか、おっちゃん(僕)にはわからん」とほざいてしまいましたが、 4.10 だけじゃなくて、A.4.10 も読むべきだった模様。

いまから読む。

→読んだけど…どうだろ。わたしのように、「メモリモデルと volatile は別問題では?」と考えている人間にとって納得のいく説明にはなってませんでした。

けど、まあいいや。

  1. volatile をきちんとつけるのは難しい
  2. しかも、volatile の要求は強すぎるので、付けると速度が犠牲になりすぎ
  3. だから、付けなくても良いようにしましょうというのが POSIX の主張です

ってことなんだよね。

*1 かつ、標準 C の範囲では、メモリバリアを明に挿入する手段がないので、プラットフォーム依存のコードをライブラリ関数内に隠してやる必要がある。

*2 両者が独立というのは言い過ぎというか、間違ってるかなぁ。分けて考えるべきだと思うけど。


2006|07|08|09|10|11|12|
2007|01|02|03|04|05|06|07|08|09|10|11|12|
2008|01|02|03|04|05|06|07|08|09|10|11|12|
2009|01|02|03|04|05|06|08|
2010|01|02|03|05|06|07|10|11|
2011|03|08|
2012|02|04|07|08|10|
2013|01|02|03|05|06|08|11|12|
2014|01|02|05|06|07|08|09|12|
2015|01|02|03|04|
Categories 3imp | Card | Cutter | Dalvik | Euler | Football | GAE/J | Hand | Haskell | Re:View | Ruby | Scheme | TQD | Tiger | TigerBook読 | UikiTeXi | Verilog | Violin | Web | parconc | tDiary | お勉強 | エントロピー | ツン読 | | 将棋 | 政治について | | 模写してみよう | 確率論 | 設定など | 雑文 | 音声