Hatena::Diary

はてなるせだいあり このページをアンテナに追加 RSSフィード

2010年02月09日(火)

[]観察日記 2010-02-09 09:03 観察日記 2010-02-09 - はてなるせだいあり を含むブックマーク はてなブックマーク - 観察日記 2010-02-09 - はてなるせだいあり 観察日記 2010-02-09 - はてなるせだいあり のブックマークコメント

NetBSDにかまけていてサボっていましたが、まぁぼちぼち。

make clean

(mame) make clean してもえらいいろいろ残るな

(mame) parse.c とか prelude.c とか

(eban) parse.cはtarballでいしょに配布してるので消すわけにはいかない

(mame) tarball のパッケージではどのみち make clean 使うなってスタンスではなかったっけ

(mame) 見つからないから気のせいか (注: 以前 IRC でそのような話がでていた)

(mrkn) そもそも make clean は中途半端で、

(mrkn) ビルドを途中で止めてmake clean したりすると enc 以下の clean でエラーでたりする

(eban) 昔はmake cleanがminirubyに依存してたりしたので、そのあたりの名残りで不整合があるんだろうな

M17N の教訓

(mame) Ruby の M17N 化を通して得られた経験は

(mame) 論文にまとめておくと未来の為になる気がする

(mame) まあ、CSI 方式は難しいから避けよう、という教訓にしかならないかもしれない

(kosaki) スクリプト言語だと、文字列以外も stringでやる伝統があるのでCSIと相性が悪くて云々というスジ

PerlUCS 正規化方式だけれど、やっぱり文字列とバイト列絡みでは色々踏んだ記憶がある。

BigDecimal

あらすじ――RubySpec 修正に燃える mame、しかし、BigDecimal という強大な敵が立ちはだかるのであった― ―

(mame) BigDecimal のターゲットユーザってどんなんだろう

経理とか金融とか Rails とか諸説ありますが、実際の所どうなんでしょうね。

なお、BigDecimalシリーズはしばらく続きます

トラックバック - http://d.hatena.ne.jp/nurse/20100209

2010年02月07日(日)

NetBSD 5.0 における pthread と fork 21:12 NetBSD 5.0 における pthread と fork - はてなるせだいあり を含むブックマーク はてなブックマーク - NetBSD 5.0 における pthread と fork - はてなるせだいあり NetBSD 5.0 における pthread と fork - はてなるせだいあり のブックマークコメント

概要

NetBSD 5.0 において、複数の pthread が生きている状態で fork するとおかしくなる。

再現

再現プログラムではfork前に一度3つのpthreadを作り、その後1つ殺して lwpid 1 と 3 が残っている。この状態で3からforkすると子では3が残り、これにlwpid 1が割り当てられるのだが、どこかに3の情報が残っていて、その後呼びに行ってしまう

22612 2 a.out CALL _lwp_park(0xbb7ffd94,3,0x8049198,0x8049198)

22612 2 a.out RET _lwp_park -1 errno 3 No such process

詳細

NetBSD 5.0 ではカーネルスレッドに当たる lwp (light weight process) とユーザレベルを司る pthread が 1:1 対応している。プロセス内に存在する lwp の情報は、以下のあたりに保存されている。

// /usr/include/sys/lwp.h
struct lwp {
        ...
        /* Process level and global state, misc. */
        LIST_ENTRY(lwp) l_list;         /* a: entry on list of all LWPs */
        void            *l_ctxlink;     /* p: uc_link {get,set}context */
        struct proc     *l_proc;        /* p: parent process */
        LIST_ENTRY(lwp) l_sibling;      /* p: entry on proc's list of LWPs */
        ...
};
// /usr/include/sys/lwp.h
struct proc {
        ...
        pid_t           p_pid;          /* :: Process identifier. */
        LIST_ENTRY(proc) p_pglist;      /* l: List of processes in pgrp. */
        struct proc     *p_pptr;        /* l: Pointer to parent process. */
        LIST_ENTRY(proc) p_sibling;     /* l: List of sibling processes. */
        LIST_HEAD(, proc) p_children;   /* l: List of children. */
        LIST_HEAD(, lwp) p_lwps;        /* p: List of LWPs. */
        struct ras      *p_raslist;     /* a: List of RAS entries */

/* The following fields are all zeroed upon creation in fork. */
#define p_startzero     p_nlwps

        int             p_nlwps;        /* p: Number of LWPs */
        int             p_nzlwps;       /* p: Number of zombie LWPs */
        int             p_nrlwps;       /* p: Number running/sleeping LWPs */
        int             p_nlwpwait;     /* p: Number of LWPs in lwp_wait1() */
        int             p_ndlwps;       /* p: Number of detached LWPs */
        int             p_nlwpid;       /* p: Next LWP ID */
        ...
};

fork は実際には /sys/kern/kern_fork.c の fork1 で行われていて、ここでプロセスの複製と initial thread の作成を行っている模様。initial thread は fork を実行した lwp をテンプレートにして作られる。

        /*
         * Finish creating the child process.
         * It will return through a different path later.
         */
        lwp_create(l1, p2, uaddr, inmem, (flags & FORK_PPWAIT) ? LWP_VFORK : 0,
            stack, stacksize, (func != NULL) ? func : child_return, arg, &l2,
            l1->l_class);

        /*
         * It's now safe for the scheduler and other processes to see the
         * child process.
         */
        mutex_enter(proc_lock);

        if (p1->p_session->s_ttyvp != NULL && p1->p_lflag & PL_CONTROLT)
                p2->p_lflag |= PL_CONTROLT;

        LIST_INSERT_HEAD(&parent->p_children, p2, p_sibling);
        p2->p_exitsig = exitsig;                /* signal for parent on exit */

        LIST_INSERT_AFTER(p1, p2, p_pglist);
        LIST_INSERT_HEAD(&allproc, p2, p_list);

この lwp_create は /usr/src/sys/kern/kern_lwp.c にある。しかし、引用部の後半にある、プロセスの lwp リストに新しい lwp を追加するコードは、別のプロセスの lwp をテンプレートにした場合を考慮しているように見えない。

/*
 * Create a new LWP within process 'p2', using LWP 'l1' as a template.
 * The new LWP is created in state LSIDL and must be set running,
 * suspended, or stopped by the caller.
 */
int
lwp_create(lwp_t *l1, proc_t *p2, vaddr_t uaddr, bool inmem, int flags,
           void *stack, size_t stacksize, void (*func)(void *), void *arg,
           lwp_t **rnewlwpp, int sclass)
{
        ...

        if (isfree == NULL) {
                l2 = pool_cache_get(lwp_cache, PR_WAITOK);
                memset(l2, 0, sizeof(*l2));
                l2->l_ts = pool_cache_get(turnstile_cache, PR_WAITOK);
                SLIST_INIT(&l2->l_pi_lenders);
        } else {
                l2 = isfree;
                ts = l2->l_ts;
                KASSERT(l2->l_inheritedprio == -1);
                KASSERT(SLIST_EMPTY(&l2->l_pi_lenders));
                memset(l2, 0, sizeof(*l2));
                l2->l_ts = ts;
        }

        ...

        p2->p_nlwpid++;
        if (p2->p_nlwpid == 0)
                p2->p_nlwpid++;
        l2->l_lid = p2->p_nlwpid;
        LIST_INSERT_HEAD(&p2->p_lwps, l2, l_sibling);
        p2->p_nlwps++;
}

つまり推測するに、LIST_INSERT_HEAD(&p2->p_lwps, l2, l_sibling) で、l2 の next が 3 を指したまま insert してしまっているのではあるまいか。その結果、あとで pthread_cond_wait した時に存在しない lwpid 3 を見に行って困る、と。

ちなみに、回避法はあって、pthread_createを十分な数作って捨てることで、欠番になっている lwpid を使った上で消せばよい(ぉ

workaround

Bug #2724 に書いたパッチを Ruby 1.9 に当てることで回避できます。

send-pr

http://www.netbsd.org/cgi-bin/query-pr-single.pl?number=42772 How to Repeat と Fix を使いそこねた

謝辞

この問題の解決には @_enami さんの助けがありました。

トラックバック - http://d.hatena.ne.jp/nurse/20100207

2010年02月04日(木)

スレッドスタック領域情報の取得 15:56 スレッドのスタック領域情報の取得 - はてなるせだいあり を含むブックマーク はてなブックマーク - スレッドのスタック領域情報の取得 - はてなるせだいあり スレッドのスタック領域情報の取得 - はてなるせだいあり のブックマークコメント

Unix 系でもそれぞれ異なるので調べた。

スタックオーバーフローのハンドリング (Stack Overflow Handling)

Linux

manpage

pthread_getattr_np(3)

header
  • #define _GNU_SOURCE
  • pthread.h
API

int pthread_getattr_np(pthread_t thread, pthread_attr_t *attr);

main thread

getrlimit(2) 可、LinuxThreads では pthread_*_np 不可。

FreeBSD, DragonFly BSD

manpage
header
  • pthread_np.h
API
  • int pthread_attr_get_np(pthread_t pid, pthread_attr_t *dst);
main thread

pthread_*_np のみ。getrlimit(2) 不可。

NOTE

Initial thread stack size (Was: postgresql port, link with libc_ror not?)

NetBSD

manpage

なし

header
  • pthread.h
API
  • int pthread_attr_get_np(pthread_t pid, pthread_attr_t *dst);
main thread

pthread_*_np、getrlimit 共に可。

OpenBSD

manpage

pthread_attr_getstacksize(3)

header
  • pthread_np.h
API

int pthread_attr_getstacksize(const pthread_attr_t *attr, size_t *stacksize);

main thread

未調査

Mac OS X

manpage

なし

header
  • pthread.h
API

size_t pthread_get_stacksize_np(pthread_t);

void * pthread_get_stackaddr_np(pthread_t);

main thread

getrlimit(2) を用いる。pthread_*_np 不可。

トラックバック - http://d.hatena.ne.jp/nurse/20100204

2010年02月03日(水)

[]観察日記 2010-02-03 17:35 観察日記 2010-02-03 - はてなるせだいあり を含むブックマーク はてなブックマーク - 観察日記 2010-02-03 - はてなるせだいあり 観察日記 2010-02-03 - はてなるせだいあり のブックマークコメント

RubySpecの人に怒られた

あまりに頭の悪いミスなのでぐうの音も出ない。

library/socket/tcpserver/gets_spec.rb で止まる

Thread.new { sleep }
TCPServer.new(0).gets

mameさんが追跡中

FreeBSDGC中にSEGV

e = [1,2,3].each
10000.times {
  e = [e].each
}
Thread.new { GC.start }.join

FreeBSDで落ちる。FreeBSD はメインスレッドのマシンスタックのサイズの取得方法が違うらしい。DragonFlyBSDも同様のようだ。r26549,r26550 スタックオーバーフローのハンドリング (Stack Overflow Handling)

dlがlibffiで置き換えられた

Solaris (Sun Studio 11) や mswin32/mswin64 で dl が使えなくなってしまった。LLVM/clangでは動いた。

まつもとさんは「mswin32 が解決すれば」「まぁ,いいんじゃない」だそうなので、mswin32/mswin64 対策が行われれば、1.9.2 に入ると思われる。

トラックバック - http://d.hatena.ne.jp/nurse/20100203

2010年02月02日(火)

[]観察日記 2010-02-01 01:44 観察日記 2010-02-01 - はてなるせだいあり を含むブックマーク はてなブックマーク - 観察日記 2010-02-01 - はてなるせだいあり 観察日記 2010-02-01 - はてなるせだいあり のブックマークコメント

RubySpecのバグの潰し方

(mame) http://regional.rubykaigi.org/tokyo03 豪華講師陣だな

(hermit_) 東京Ruby会議03 - Regional RubyKaigi [text/html; charset=utf-8]

(ko1_ndk) mameさん講師やればいいのに

(mame) 話すことないし

(ko1_ndk) RubySpecのバグの潰し方

(mame) 1. rubyspec を変える

(mame) 以上

わたしも今日数十個ほどバグを潰したんですが、全てRubySpec側の修正でした。というわけで、現在RubySpecの失敗は36個です。

RubyVMのソースコードの見方

(kosaki) すると、RubyVMのソースコードの見方が分かるんですね。わかります

(shyouhei) VMソースコードの読み方はマジ不明

(ko1_ndk) なん,だと

(shyouhei) まず1.3くらいのソースを、一回読んで、基礎知識をたくわえて、

(shyouhei) それでもちょとまだむずい。

(mame) vm.c はささださんの牙城

(mame) dfp とか lfp とかの不変条件をすぐに忘れるのと

(mame) 全体的に何のためにその処理をしているのかわからないところが多くて困る

(ko1_ndk) なるほど

(shyouhei) 全体理解のためにはinsns.defからなにがどうなってvm.oまで到達するかがMakefile読まんと分からんのが。

(ko1_ndk) そのネタで yarv maniacs を書くか

(mame) vm_exec が難しい

(shyouhei) 「1時間で読むmatzruby」

(mame) 今は 30% くらいはわかってるけど

(ko1_ndk) 1.8 よりも簡単だと思うんだがなあ>VM

(mame) 1.8 は読んだことないのでしらない

(shyouhei) 1.8は読み始めるハードルは低いが、読んだからといって理解はできない。

(kosaki) 意図が謎すぎるということ?

(shyouhei) ruby 1.8はsetjmp/longjmpが乱舞してどっからどこにいくのかまったく分からない素敵コードです。

YARV ManiacsRuby 1.9の実行系を読む人にはバイブルと言ってもいいのではないのでしょうか。まず、全容が分からないと部分を読んでもさっぱり分からないので。まぁ、そんな難解なYARVですが、eval.cやthread.cがVMから分離したのは福音だと思っています。あ、もちろんわたしはさっぱりです。

互換性〜ブラウザ

IEにあわせて の話。HTML5とか最近の仕様はこの手の互換性維持のためのアルゴリズムが仕様注に記載されていて、もう。

Enumerable#interleave

Enumerable#interleave というメソッドを実装してみた。flattenしたzipという感じだが、欲しい人いる?

http://twitter.com/yukihiro_matz/status/8504330947

そういうのが欲しい人にはチャンスだと思います。

トラックバック - http://d.hatena.ne.jp/nurse/20100202