2018/11/30

[linux][pthread]pthread_cond_wait()を勘違いしていた (2)

前回、けっこうがんばったつもりだったので、このタイトルで2回目の記事は書きたくなかった。。。
つまり、まだあれではダメだった、ということだ。


前回の一番最後の図で、左側に要求を出すスレッドが2つ、右側に要求を処理するスレッドが1つあった。

  1. 右側が先にmutex_lock()してcond_wait()によってロック解除
  2. 左側がmutex_lock()して、リソースを書き換えてcond_signal()
  3. 右側はcond_signal()を受けて動き出したいけど、内部でmutex_lock()をするため、左側がmutex_lock()していることにより動けない
  4. 左側がmutex_unlock()
  5. 右側が動き始める

こういうシナリオだった。


が、左側には要求を出すスレッドが複数あるので、こんなことが起きているようなのだ。

  1. 右側が先にmutex_lock()してcond_wait()によってロック解除
  2. 左側Aがmutex_lock()して、リソースを書き換えてcond_signal()
  3. 左側Bがmutex_lock()して、動けない
  4. 右側はcond_signal()を受けて動き出したいけど、内部でmutex_lock()をするため、左側Aがmutex_lock()していることにより動けない
  5. 左側Aがmutex_unlock()
  6. 左側Bが動き始めて、左側Aが書き換えたリソースを上書きしてしまう

たぶん、こうなっている。
はぁ。


この記事の真ん中から下にも書いてあるが、順番に処理したいならキューイングするしかなかろう。
(まったく読んでなかった。。。)

IBM: 一般的なスレッド: POSIX スレッドの説明: 第3回

やりたいことは異なるのでソースをまねするかは分からないけど、キューイングみたいなことはいるな。


リンク先のworkcrew.cで、L.49でmutex_unlock()、L.53でmutex_lock()しているけど、これはいるんだろうか?
cond_wait()を抜けたときには内部でmutex_lock()して、cond_wait()を呼ぶときには内部でmutex_unlock()されるということだったので、私のコードからは削除したのだ。

全体で何をやっているコードなのか見ていないので何とも言えんな。
隙を見せる(一瞬unlockする)ことで、何か利点があるのかもしれん。

2018/11/23

[win10]ウインドウの枠を太くしたい2018年秋 (3)

なぜか3回目だが、見た目の問題は使い勝手に影響するので、長引くのもしかたあるまい。


今回は、記事の修正だ。

1回目で、色の選択はすべてできるわけではないと書いたけど、そんなことはなかった。
「サポートしない」というメッセージだけで、完了ボタンを押して確定することができたのだった。

image

まあ、見づらいのだけどね。。。


今日は、このくらいの色にした。

image

image


うーん・・・悪くはないのだが、黒文字に合わせる背景色ってのは選択肢が少ない気分になってしまう。
私のセンスの問題のような気もするが、そこは忘れよう。



そもそも、最近はウィンドウを結合して文字が見えない方がデフォルトだったと思うので、気にしないものかもしれん。

image

これに慣れてしまえばいいのだろうが・・・ダメだった。
新しいものについていけない年齢なのかもしれんし、そういうのをずっとサポートするのもメーカーとしては大変なのだろう。

2018/11/18

[win10]ウインドウの枠を太くしたい2018年秋 (2)

このサイトはGoogleの「Blogger」を使ってるのだけど、貼っている画像は「フォト」と同じ扱いなのに気付いてなかった。
もうブログに貼ったから削除して良かろう、とゴミ箱に入れていたら、画像がなくなっているではないか。。。

私は、けっこうバサバサと削除してしまう方なのだ(そして、元に戻せなくて後悔することもしばしば)。
きっと、過去のブログでは画像がなくなっていることだろう。


幸い、気付くことができたので、過去のこと(画像がなくなっているだろうこと)は忘れて、前向きに生きていこう。


さて、ウインドウの枠を太くする記事の2回目。

前回は、Windows10 Homeの1809(2018年秋バージョン)で枠を太くしたのだった。
今回は、1803(2018年春バージョン)にその設定を持っていった話をしよう。


枠を太くするのに使ったのは、AeroLiteという隠しテーマらしい。
1803がインストールされているPCはWindows10 Proで、この子も以前はAeroLiteにして試していて、元に戻したのだった。


テーマファイルはエクスポートできるようだったので、1809で作った設定ファイルを、そのまま1803で開いた。
開いた、というのは、ダブルクリックした、という意味だ。
拡張子は「deskthemepack」で、cabファイルで圧縮されていた。
展開すると「theme」ファイルが入っていただけだった。まあ、壁紙も何もないからね。


image

1803の方は、それだけでインポートしてくれた。
まあ、AeroLiteを以前に有効にしていたのが効いていたのかもしれんな。



非アクティブなウィンドウタイトルの色は、ものによって違うようだ。

こちらは、TeraTerm。
非アクティブなことがちょっと分かりづらいが、文字は読みやすい。
なお、文字が太文字になっているのは、Tweakerで変更したからだ。

image


こちらは、Explorer。
私には非アクティブがちょっと見づらいが、アクティブな方はこっちの方がよいかも。
枠の色は、変化がないように見える。

image


これはChrome。
非アクティブが緑色なのは、Tweakerで設定しているため。
Tweakerの設定項目にあるのに、これが有効なアプリがどれかわからんのだ。

image


ストアアプリの電卓。
Microsoftのストアアプリは、タイトルバーのところに色が付かないようだ。
文字の色でも区別が付かないので、太くした枠が見分けるのに役立つ。

image



とまあ、こんな感じで、色のルールがよくわかっていない。

なんとなく、Microsoftはタイトルバーを嫌がっているような気がするのだが、「Windows」なのだからウィンドウは見やすくしてほしいものだ。


さて、枠を太くしてみたものの、それが何か役立っているかというと、よくわからん。

劇的に何か変わったというわけではないが、精神的には多少良くなった気がする。
まあ、Windows8になったときは「うおっ、枠が太い!」と思った気がするのだが、枠がなくなってほしいわけではなかったので、ないよりはあるほうが助かるのだ。


とはいえ、この辺は慣れとか作業環境とかあると思うので、私は枠がほしかった、というだけのことだな。

2018/11/17

[c]LevelDB C (3)

11月になって寒くなってきたので、コタツ上のPCに移動。
こちらにはleveldbの環境が無いので、せっかくだからaptでinstallできるか試しておこう。

apt install libleveldb-dev
git clone https://github.com/hirokuma/leveldb-c-example.git
cd leveldb-c-example
gcc -Wall leveldb_example.c -lleveldb
./a.out

コンパイルも通って、実行もできた。


ライブラリやincludeのディレクトリも一緒にcommitしていたけど、それを削除してもビルドできたので大丈夫だろう。


さて、このサンプルだが、実行しても後に何も残らない。
DELETEしているところと、DESTROYしているところをコメントアウトすると、ディレクトリが残った。

「testdb」というフォルダ名で、これはleveldb_open()した名前と一致している。
実行はWindows10のWSLで行ったのだが、ディレクトリの中はこうなっていた。

image

Windowsの人としては、拡張子が付いている「000003.log」がテキスト形式であってほしかったが、テキスト形式らしきものは「CURRENT」と「LOG」だった。


CURRENT

01: MANIFEST-000002
02: 


LOG

01: 2018/11/17-22:09:16.387552 7f3d2e040740 Delete type=3 #1
02: 


000003.log

image


MANIFEST-000002

image


データらしきものは、拡張子がlogのファイルに残っているようだ。
LOGの、時間とコメントの間にある数字はデータのハッシュ値かと考えたが、"testdb"を削除して実行しても値が変わるし、変わるのは一部だけだから、何か時間の要素を使っているか、乱数を使っているだろうし、固定値の意味合いもあるのだろう。
いまだと「7f...」で始まるので時間かと思ったが、7fの次が変わるので単純な時間表現でもなさそうだ。


削除しないようにしてもう一度実行すると、LOGはLOG.oldにリネームして、新しく作り直すようだ。
つまり、1世代前のLOGまで残っている。

MANIFESTの番号も増えるし、拡張子がldbのファイルも増えたりしているので、作ったファイルの中身を知りたければ実装を読むか、解説記事を読んだ方がいいだろう。


私はそういうつもりがないので、今回はここまで。

2018/11/11

[linux][pthread]pthread_cond_wait()を勘違いしていた

(2018/11/11 23:19追記あり)

Linuxでpthreadを使っている。
スレッドがいくつかあって、相手のスレッドに処理要求して、終わるまで待つ、という同期処理が必要になった。
そういうときは、pthread_cond_wait()で待って、pthread_cond_signal()で起こしてもらえば良かろう。


・・・ということはわかったのだが、あまり使い方を読まずに使っていた。
そのせいで、相手のスレッドに要求するスレッドが複数になった場合にmutexがlockするという現象が起きていた。

ちゃんと読まずに使うのはよくない、という反省を込めて記事にしよう。


共有のメモリに処理してほしい内容を通知して、相手は処理したらそのメモリに結果を返す。
処理する方は呼ばれるまで待っているし、呼び出す方のスレッドは終わるまで待たせる。

pthread_cond_wait()は引数にlockしたmutexを取るようだったので、こう考えていた。

  • cond_wait()する直前にmutex_lock()する
  • mutex_loc()するんだから、他のスレッドが同じことをしようとしたらmutex_lock()で待たされる


そして、こういう実装をしていた。
見づらくてすまんが、黄色から右側が処理するスレッドで、左側は要求するスレッドたちだ。

要求するスレッドは、まず要求側のmutexをlockして、メモリに書込み、cond_signal()で相手を起こしたら、自分はcond_wait()で寝る。
処理側は処理が終わったらcond_signal()で起こして、自分はcond_wait()で寝る。

image


これでよかろうと思っていたのだが、何だかよくわからないタイミングでスレッドが固まることが分かった。
pthread_cond_wait()の使い方など疑ってもいなかったのだが、他にあやしい箇所がない。。。


IBM: 一般的なスレッド: POSIX スレッドの説明: 第3回

これの「もう一度、復習を」を見ると、pthread_cond_wait()呼び出しはロックを解除した後にスリープ状態になる、と書かれている。
そ、そんなばかなー!


よく読むと、こういうことらしい。

  • pthread_cond_wait()は、mutexをunlockした後でスリープ状態になる
  • pthread_cond_signal()などが呼ばれると、mutexをlockしてから起き上がる

今回の現象は、pthread_cond_wait()のあとはunlockされているから、別のスレッドがlockできてしまうために起きているのだ。
実装したものがすべてじゃないのね・・・。


そういうことなので、シーケンスを修正した。
明示的に実装していないmutexのlock/unlockは赤い矢印で表した。


image


これなら、mutexのlock/unlockが意図せずクロスすることはないだろう。


いやー、mutexというかpthreadは内部の動きも理解していないといけませんな。
などと書きつつも、これも間違っていたら嫌だなぁ。。。


2018/11/11 23:19追記


やはり・・・間違えていた。
基本的な考え方は悪くなかったのだが、シーケンスが良くない。

描いたシーケンスだと、

  1. 右側のスレッドがcond_wait()する
  2. 左側のスレッドがcond_signal()を投げる
  3. 左側のスレッドがcond_wait()する
  4. 右側のスレッドがcond_signal()を投げる

としていたのだが、2番でsignalを投げた後、左側がwaitに入る前に右側のスレッドがsignalを投げてしまい、待ち人来たらずになってしまうことがあったのだ。


そもそも、cond_wait()の前にmutex_lock()することで何がよいかというと、cond_wait()されることでmutex_unlock()が行われることだ。
これによって、別のスレッドがmutex_lock()して待ち状態になっているものが解除されることで、順番を制御できるのだ。

大ざっぱに書くと、こういう感じか。

image

先にlockして、signalを投げたい方をlockで待ち状態し、waitでunlockすると、相手がsignalを投げるのは必ずwaitした後になるということだ。


修正は1箇所。
左側のスレッドがsignal()を投げた後にlock()していたのを、その前にするだけだ。
そうしておくと、右側のスレッドはsignal()を投げる前にlockしているので、順番が守られる。

image


さあ、これくらいで直ってくれないだろうか。。。

2018/11/10

[win10]ウインドウの枠を太くしたい2018年秋

定期的に気になる、Windows10のウィンドウ枠が細い問題。
問題というか、仕様なのだろうが、たくさんウィンドウを開いて選択し間違えるたびにイライラするので、個人的には問題なのだ。


以前も試したのだが、Winaero TweakerでテーマをAero Liteにしてみよう。
Windows10は、Homeの1809。
Tweakerのバージョンは、0.12.1.0。


Aero Liteはデフォルトでは有効になっていないので、レジストリ操作などで有効にできたはずだ。
戻すのが面倒そうなので、Winaero Tweakerを使う。

image


まあ、上の画面は変更後なんだけどね。
ウィンドウを重ねた場合は、こう見える。

image


アクティブなウィンドウは文字色が黒で、非アクティブはグレーだ。
変更できたかもしれないが、まあよかろう。


枠の色が黄色っぽいのは、タスクバーのせいだ。
何の話かというと、タスクバーの文字色が黒で固定されるようなのだ。

image

前回も、これが原因でテーマを元に戻している。
Windowsで使える色をそのまま使うと、どれを選んでも見づらいからだ。
私は青っぽい色を使っていたのだが、それだとこの通り。

image


当時はあまり色の選択ができなかったのかもしれないが、今はユーザが色をある程度選べるようになっていたので、自分が許容できる色が選べる。

image


image

(何気なく選んだが、グレーもいいかも)



「ある程度選べる」というのは、明るすぎる色だとWindowsが許してくれないからだ。
(2018/11/23修正:「サポートしない」といっているだけで、選択できました!)

image


なんでWindowsがそういうのを指摘するかというと、ストアアプリのリンク色に反映されるからではなかろうか。
下の図だと「夜間モードの設定」と「Windows HD Color設定」はリンクなのだ。

image


タイトルバーにも反映されるので、そこも考慮すべきだろう。
重ねてみたが、Explorerはグレーだけど、メモ帳やTeraTermはそんなことないな。ストアアプリでもOpenLiveWriterとSkypeは違うし。
そもそも、ストアアプリでタイトル領域が塗りつぶされている方が少ないのか。

image


私では非アクティブなタイトル文字色を変更する方法が分からなかったが、そういうのを探していくのもまたよいだろう。

2018/11/03

[c]LevelDB C (2)

まずは、以前参考にしたC interfaceのexampleを見る。

サンプルだからだろうが、中身はシンプルだ。

  • openオプション設定
  • open
  • writeオプション設定
  • put
  • readオプション設定
  • get
  • delete
  • close
  • destroy

destroyまですると、DBのディレクトリごと削除するようだった。


気になったのでvalgrindを使ってみると、メモリの解放をしていないところがあった。
あと、getしたものをそのまま\0無しで出力しているので、よろしくない。
そこら辺を修正した。

https://github.com/hirokuma/leveldb-c-example/blob/64f448da1f345f4c94b924ceb2dd9f62725e649e/leveldb_example.c


オプションはそれぞれ内部でmalloc()しているので、それぞれの解放APIを呼ぶことになった。
openのオプションはdestroyでも使っているが、同じleveldb_options_tである必要はないかも。
少なくとも、このサンプルを改造して、openとdestroyでそれぞれleveldb_options_create()を使ったが、特にエラーは出ていない。

c.ccを見ても、options->repを渡しているだけだし、参照してはいるものの保持している感じはしないので、たぶん・・・たぶん大丈夫なんじゃなかろうか。

[c]LevelDB C (1)

いままでLMDBを使っていたが、違うタイプのDBも検討したい。
候補として出てきたのは、以前も調べたLevelDB
C++で書かれているが、Cのインターフェースもあるので、そちらを見ていこう。


なぜ検討しようかと思ったかというと、LMDBの特徴のいくつかが、いま作っているアプリと噛み合わなくなってきたからだ。

ここのサイトにまとまっていた。
A short guide to LMDB – Kolab Now


"Caveats"の最初に書かれているように、memory mappingされるようになっていて、それがサイズの上限になってしまうのだ。
そんなに大きいデータを扱うつもりがなかったので気にしていなかったのだけど、だんだん扱うサイズが大きくなってきて、とうとうエラーが発生するようになってしまったのだった。
64bitのLinuxで開発していたら気にならなかったのだけど、Raspberry Pi3に持っていくと1.5GBくらいまでしか指定できなかった。
environmentを複数に分ければ確保できるのかもしれないが、そこまでしてLMDBに合わせなくてもいいんじゃないのか・・・。


DBの内容は削除もしていくのだが、「The database file will never shrink」なので、アクションを起こさないと小さくなってくれない。
mdb_env_copy2()でMDB_CP_COMPACTを実行すれば減りそうだが、それをやるにはcopy2した後のenvに切り替えないといかんだろう。
その時間が致命的かどうかが、ちょっと見積もることができていない。


Transactionの範囲がDBではなく、environment単位というのも手痛かった。
1つのDBだけアクセスしたくても、それを含むenvironmentをロックすることになるのだ。
まあ、だからTransactionは手短にするよう書かれているのだろうがね。

それに関して解決できていないのが、mdb_txn_begin()だ。
複数のスレッドからアクセスすることがあるので、先にmdb_txn_begin()したスレッドが優先され、後から呼んだ方はmdb_txn_commit/abort()されるまでロックされるだろう、という想定でいた。
いたのだが、両方ロックされてしまうことがあったのだ。
mdb_txn_begin()前後にログを出しながらtidを見ていたのだが、2スレッド目がmdb_txn_begin()すると両方のログが止まってしまったから、そう判断している。
なんかAPIの使い方を間違えているような気はするのだけど、よくわかってない。


そんなこんなで、LMDBの問題ではなく特徴が生かせなくなっているので、別のDBも見てみようか、と考えたのだった。


もう1つ、LMDB本体とは直接関係ないのだけど、Windows 10の2018年春バージョン以降のWSLでLMDBがうまく動かないというのも後押ししている。
WSLのディスクキャッシュが変わったのが原因だろうということだったので、予想が当たってちょっとうれしいものの、VirtualBoxなどのLinux環境が必須になったのは面倒だ。
1台のWindows10だけ2018年秋バージョンにしてみたが、そっちでも変わらなかった。