April 2013

献本御礼。



いつもお世話になっている西尾大先生から献本もらったので、うきうきしながら読みました。

この本をひところで説明するのは大変難しいのだけど、あえていうなら、プログラミング言語の進化よもや話としての読み物の側面と、これから自分の言語を作ろうと考えている人に向けた現在の言語たちの言語仕様のバックグラインドを説明する教科書的な側面があると思う。

C++の世界には「C++の設計と進化」(別名 D&E)というものすごい有名な本があるのだけれども、文体・書かれている内容ともにまったく似ていないのだけれど、読後感は驚くほど似ている。D&EはC++に絞って言語の進化の歴史となぜこの機能を採用したか、なぜ対案を採用しなかったかを記述しており、大変歴史的価値が高い逸品なのであるが、西尾本はプログラミング言語全体において、現代のモダンな言語で使われている諸機能・言語仕様について歴史をさかのぼり、たのしいトリビアを交えつつ設計の背景を教えてくれる。



この本が素晴らしいのは、「どこから調べてきたんだ」唸ってしまうトリビアの数々によりあきさせないこと。それぞれの章が完全に独立して読めるよう注意深く配慮されており、時間がないときでもどこからでも読めるようになっていること。
また、この手の歴史的な経緯を記述する本は往々にして、もう誰もしらない様な古代言語の話で埋まってしまい読んでいて退屈になってしまうのが常であるが、コードが出てくるのは C, Ruby, Perl, Python, JavaScript といった現代言語にだいたい限られており、それ以外は図を多用して説明してくれているので、年代問わず楽しめる書籍になっていることがあげられると思う。

「成り立ちから学ぶプログラミング作法」という副題から感じるニュアンスとはちょっと違って、おっさん層よりも、これからオレオレ言語を作ろうと考えている学生におすすめしたい。


なお公式紹介ページはこちらです: http://nhiro.org/langbook/


目次はこんな感じ

目次

第1章 言語を深く効率的に学ぶには
1.1 比較から学ぶ
1.2 歴史から学ぶ
1.3 まとめ

第2章 プログラミング言語を俯瞰する
2.1 プログラミング言語誕生の歴史
2.2 プログラミング言語の生まれた目的
2.3 まとめ

第3章 文法の誕生
3.1 文法って何だろう?
3.2 スタックマシンとFORTH
3.3 構文木とLISP
3.4 中置記法
3.5 まとめ

第4章 処理の流れのコントロール
4.1 構造化プログラミングの誕生
4.2 ifが生まれる前
4.3 while ──繰り返しのifを読みやすく表現
4.4 for ──数値を増やしながらのwhileを読みやすく表現
4.5 まとめ

第5章 関数
5.1 関数の役割
5.2 戻る命令
5.3 再帰呼び出し
5.4 まとめ

第6章 エラー処理
6.1 プログラムも失敗をする
6.2 失敗をどうやって伝える?
6.3 失敗しそうなコードを囲む構文
6.4 出口を1つにしたい
6.5 どういうときに例外を投げるか
6.6 例外の伝搬
6.7 まとめ

第7章 名前とスコープ
7.1 名前はなぜ必要だったか
7.2 スコープの進化
7.3 静的スコープは完成形?
7.4 まとめ

第8章 型
8.1 型とは何か
8.2 数値をオンとオフで表現する方法
8.3 1つの位に必要なランプはいくつか?
8.4 実数はどうやって表現しよう
8.5 型は何のため?
8.6 型のいろいろな展開
8.7 まとめ

第9章 コンテナと文字列
9.1 いろいろな種類のコンテナがある
9.2 なぜいろいろな種類のコンテナがあるのか
9.3 辞書,ハッシュ,連想配列
9.4 文字とは何か
9.5 文字列とは何か
9.6 まとめ

第10章 並行処理
10.1 並行処理とは何か
10.2 細かく区切って実行する
10.3 処理を切り替える2通りの方法
10.4 競合状態を防ぐには
10.5 ロックの問題点と解決策
10.6 まとめ

第11章 オブジェクトとクラス
11.1 オブジェクト指向とは何か
11.2 変数と関数を束ねて模型を作る方法
11.3 方法1 モジュール,パッケージ
11.4 方法2 関数もハッシュに入れる
11.5 方法3 クロージャ
11.6 方法4 クラス
11.7 まとめ

第12章 継承によるコードの再利用
12.1 継承とは
12.2 多重継承
12.4 まとめ


Linuxでは伝統的にディレクトリ操作(ファイルのrenameとか)を asyncに行なってきた。これはBSDなどよりも性能がいい理由の一つだったんだけど、クラッシュした時にとんでもない壊れ方をするのでまあ色々と評判が悪かった。
さて、じゃあこのようなシステムで安全なアプリケーションを書くためにはどうすればいいかという話になるが、頭の良い人がディレクトリに対してfdatasyncすればいいじゃん。と言い出して、みんなそれがPOSIX filesystem のルールだと思ってたんだな。

で、今回、aio_fsync()がなぜか write permissionを要求してるので、ディレクトリにaio_fsync()できねーじゃねーか。という規格の不整合を上訴したひとがいて、議論の結果「ディレクトリに操作に対してfdatasync()を要求するようなasyncなファイルシステムはPOSIX準拠じゃない」という一文が規格書に追記されることになった。


ええええええーーーーー


いまさらかよ。
まあ、同時にnon-comforming filesystemでどうすればいいのかが追記されたので、今後は混乱は減っていく・・・のかなあ?


http://sourceware.org/ml/libc-alpha/2013-04/msg00578.html

Just to close this thread, Geoff Clare has summarized the changes regarding aio_fsync() and relationship wrt. directory entry data.



"In the April 18 teleconference it was agreed that the standard
should mandate that directory operations are always synchronized
on conforming file systems, and should include warnings about
non-conforming configurations. The proposed changes are as follows.

Changes to XBD...

At page 94 line 2581-2588 section 3.376 change:

For read, when the operation has been completed or diagnosed if
unsuccessful. The read is complete only when an image of the data
has been successfully transferred to the requesting process. If
there were any pending write requests affecting the data to be
read at the time that the synchronized read operation was
requested, these write requests are successfully transferred prior
to reading the data.

For write, when the operation has been completed or diagnosed if
unsuccessful. The write is complete only when the data specified
in the write request is successfully transferred and all file
system information required to retrieve the data is successfully
transferred.

to:

For read operations, when the operation has been completed or
diagnosed if unsuccessful. The operation is complete only when an
image of the data has been successfully transferred to the
requesting process. If there were any pending write requests or
(if the file is a directory) directory modifications affecting
the data to be read at the time that the synchronized read
operation was requested, these requests are successfully
transferred prior to reading the data.

For write operations and directory modification operations, when
the operation has been completed or diagnosed if unsuccessful. The
operation is complete only when the written data or (if the file
is a directory) modified directory entries have been successfully
transferred to storage and all file system information required
to retrieve them is successfully transferred.

At page 107 line 2859 add a new XBD 4.2 section (and renumber the
current 4.2 and all later 4.x sections):

4.2 Directory Operations

All file system operations that read a directory or that modify
the contents of a directory (for example creating, unlinking, or
renaming a file) shall be completed as defined for synchronized
I/O data integrity completion (see section 3.376).

Note: Although conforming file systems are required to
perform all directory modifications as synchronized I/O
operations, some file systems may support non-conforming
configurations (for example via mount options) where
directory modifications are not synchronized. Applications
that rely on directory modifications being synchronized should
only be used with such file systems in their conforming
configuration(s).


Changes to XSH...

At page 574 line 19833 section aio_fsync() change the APPLICATION
USAGE section from:

None.

to:

Refer to fdatasync() and fsync().

At page 815 line 27215 section fdatasync() append to the first
paragraph:

If the file is a directory, an implicit fdatasync() is already
performed on every I/O operation (see XBD 4.2) and consequently
if fdatasync() is called explicitly it shall take no action and
shall return the value 0.

At page 815 line 27232 section fdatasync() change the APPLICATION
USAGE section from:

None.

to:

Although conforming file systems are required to complete all
directory modifications as defined for synchronized I/O data
integrity completion, some file systems may support non-conforming
configurations (for example via mount options) where directory
modifications are not synchronized. When the file system is
configured in this way, calls to fdatasync() on directories may
cause I/O operations to be synchronized, rather than being a no-op.

At page 954 line 31987 section fsync() add a new paragraph to the
APPLICATION USAGE section:

Since conforming file systems are required to complete all
directory modifications as defined for synchronized I/O data
integrity completion (see XBD 4.2), calling fsync() on a directory
only synchronizes the file attributes such as timestamps. However,
some file systems may support non-conforming configurations (for
example via mount options) where modifications to directory
contents are not synchronized. When the file system is configured
in this way, calls to fsync() on directories may cause directory
contents to be synchronized in addition to file attributes.

Changes to XRAT...

At page 3444 line 115531 add a new XRAT A.4.2 section (and renumber
the current A.4.2 and all later A.4.x sections):

A.4.2 Directory Operations

Earlier versions of this standard did not make clear that all directory
modifications are performed as synchronized I/O operations, although
that is the historical behavior and was always intended. Applications
have no need to call fdatasync() or fsync() on a directory unless
they want to synchronize the file attributes (using fsync()), provided
the directory is on a conforming file system. However, since
applications may wish to use fdatasync() or fsync() to synchronize
directory modifications on non-conforming file systems, implementations
are required to support fdatasync() on directories as a no-op on
conforming file systems."


すごい昔に glibc mallocの内部実装についての解説をGoogle Videoにアップロードしたことがあるんだけど(*)、そのときにmallocを「まろっく」と発音していたらすごい勢いでツッコミをもらった。エムアロック派とメイロック派の両方に怒られた。

ところで、先週 glibcのメンテナと一緒に食事をする機会があり、glibcの今後の開発について相談をしたりしてたのだけど、がんばって「めいろっく」「めいろっく」言ってたら、「まろっくね」とやんわり否定されて、恥ずかしさマックスだった。はー、まともに英語が喋れるようになりたいわ。


(*)正確には「されたことがある」だけど。わたしはしてない。あの動画はアップロード者によって削除されてしまったので、もうインターネット上では見れない。知り合いに見られないのはいいことだが、自分がオリジナルをもってないので、たまにコピーをくれと言われると困ってしまうのが玉に瑕。なにかいい方法はないものかのう。

http://www.soum.co.jp/misc/tatuhiko/multi-thread/index.html

READ_RWLOCK_PREFER_READER_NPがデフォルト値のようなのです。字面から書き込みを優先しそうな、PTHREAD_RWLOCK_PREFER_WRITER_NPを設定して、再度確認してみます。しかし、なぜかこれを設定しても、書きこみロック待ちのスレッドがあっても、読み込みロックが取得できてしまします。意味がわかりません。

試しに PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP を指定して試してみると、書きこみロック待ちのスレッドがある場合にうまい具合に読み込みロックがブロックしてくれました。



とかいう記述をみつけたのひとこと。

答えはLSBに書いてある

http://refspecs.linuxfoundation.org/LSB_4.1.0/LSB-Core-generic/LSB-Core-generic/libpthread-pthread-rwlockattr-getkind-np-1.html

Notes
Setting the value read-write lock kind to PTHREAD_RWLOCK_PREFER_WRITER_NP, results in the same behavior as setting the value to PTHREAD_RWLOCK_PREFER_READER_NP. As long as a reader thread holds the lock the thread holding a write lock will be starved. Setting the kind value to PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP, allows the writer to run. However, the writer may not be recursive as is implied by the name.



コードだとこのへん。PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NPのときだけ、flagsが1になってるのがわかると思う


int
__pthread_rwlock_init (rwlock, attr)
pthread_rwlock_t *rwlock;
const pthread_rwlockattr_t *attr;
{
const struct pthread_rwlockattr *iattr;

iattr = ((const struct pthread_rwlockattr *) attr) ?: &default_rwlockattr;

memset (rwlock, '\0', sizeof (*rwlock));

rwlock->__data.__flags
= iattr->lockkind == PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP;



元々、read/write lockで write が recursiveって変なのでなかったことになってる。

ハマりポイントは色々とあるけど基本は難しくない

・git clone
git clone git://sourceware.org/git/glibc.git

・ビルドディレクトリ作成
cd glibc
make build
cd build

・configure

targetdir=${HOME}/local/glibc

../configure \
--enable-add-ons=nptl \
--enable-all-warnings \
--prefix=${targetdir}

・make

make PARALLELMFLAGS=-j8

make -j8はMakefileの書き方がよくなくて、ちゃんと動かないのだそうだ。

・make install

make install

あー、configureの時にprefix指定しておかないとmake installで死ぬから気をつけてね。
prefixをroot権限不要な場所を選ぶ、make installをrootで実行しない。は鉄則

・ld.so.cacheの作成

ldconfig -C ${HOME}/local/glibc/etc/ld.so.cache

libgcc_s.so とかの場所を教えてあげないと、さまざまなトラブルがあなたを襲います

・make tests

make testsでglibcのtest suiteが走ります。いろいろとエラーが出るけど対処方法は別途書く(きっと書かない)


・自分のアプリケーションのテスト

以下のように staticリンクしてしまうのがもっとも楽。

http://sourceware.org/glibc/wiki/Tips_and_Tricks/How_to_Build_an_application_with_your_own_custom_glibc


・make testsでのエラーあれこれ

http://sourceware.org/glibc/wiki/Testing/Testsuite

- abi-check でエラー

prefixをつけると _nl_default_dirname が変わってしまうので絶対出るのだよ。あきらめれ

- bug-atexit3 and nptl tests

わたしがいったように、ldconfigしませんでしたね?反省しなさい

- tst-eintr1

テストがバグってるのでたまにこけます。libc-alpha にパッチあるけど、まだ巻かれてないっぽい

- tst-cpuclock2

それはカーネルのバグ。こないだLKMLにパッチ投げといた。posix timer は誰も使ってないので
まだまだコーナーケースの扱いが甘いようだ。
テストが定期的にコケてるのに、何年も無視し続けるglibc communityすごい

↑このページのトップヘ