Top / Scheme部会 / ネタ / scbayes_error

scbayesで発生したバグを追っかけてみる

発端

雑談より、

scbayes error

kaw (2009-12-21 (月) 22:39:24)

Gaucheを0.8.14 -> 0.9にバージョンアップしたら、
scmailがうまく動かん。
scmail-deliver / scmail-refileによる振分けは今まで通り正常だが、
scbayesによるSPAM DBのメンテナンスがうまく行かなくなった。

$ scbayes --learn-spam spam-plus
scbayes: $HOME/.scmail/token-table.dbm is now being updated
scbayes: or perhaps $HOME/.scmail/token-table.dbm,lock is staled.
scbayes: (Please remove the lock file if it is staled.)

さてね。

つうことで、

調査

~/.scmail以下を見てみると、確かにlock directoryができたままになってる。 しかも、これを消して何回実行しても上の同じメッセージで止まる。

ソースコードを、エラーメッセージで検索してみる。
scbayes.in中の次の関数が引っかかった。

(define (lock)
  (unless (eq? (create-directory* (lock-file)) #t)
     (scmail-wformat "~a is now being updated" (table-file))
     (scmail-wformat "or perhaps ~a is staled." (lock-file))
     (scmail-eformat "(Please remove the lock file if it is staled.)"))
  )

コードは見たまんま。ディレクトリの作成を排他制御に使っている。

この(lock)という関数はどういう使われ方をしているのかを調べてみる。

$ fgrep -ris '(lock)' .
./ChangeLog:   * scbayes.in (lock): Fixed the lock mechanism. create-directory*
./ChangeLog:   * scbayes.in (lock): Use create-directory* instead of sys-mkdir.
./ChangeLog:   * scbayes.in (lock): Fixed the mode for sys-mkdir.
./ChangeLog:   * scbayes.in (lock): New function.
./scbayes.in:(define (lock)
./scbayes.in:        (lock)
./scbayes:(define (lock)
./scbayes:        (lock)

見たところ、(lock)を使っているところは一箇所のみ。

   :
   :
 (scmail-config-make-directory)
 (for-each (lambda (folder) (check-folder-path mailbox folder)) folders)
 (lock)
 (let ((learned-file-count 0)
	(start-time (current-time)))
   (dynamic-wind
	(lambda () #t)
	(lambda ()
	  (prepare-temporary-files)
   :
   :

戻り値は使っていないようだ。

あと、(lock)呼び出し中にディレクトリの作成に失敗した場合は、(scmail-eformat)で終了するが、

;; for errors
(define (scmail-eformat fmt . args)
  (apply scmail-xformat fmt args)
  (exit 1))

エラーメッセージを出力した後は、単純にexitして終了するだけのようだ。

あと、なんで(create-directory* ...) が失敗するのかを調べてみる。 Gauche-0.8.14:

$ ls -la
total 4
drwxr-xr-x   2 kaw  kaw   512 Dec 23 09:21 .
drwxr-xr-x  29 kaw  kaw  1536 Dec 23 09:21 ..
$ rlwrap gosh -i
gosh> (gauche-version)
"0.8.14"
gosh> (use file.util)
#<undef>
gosh> (create-directory* "foo")
#t
gosh> [1] + Suspended            rlwrap gosh -i
$ ls -al
total 5
drwxr-xr-x   3 kaw  kaw   512 Dec 23 09:22 .
drwxr-xr-x  29 kaw  kaw  1536 Dec 23 09:21 ..
drwxr-xr-x   2 kaw  kaw   512 Dec 23 09:22 foo
$ fg
rlwrap gosh -i
gosh> (create-directory* "foo")
#<undef>
gosh> [1] + Suspended            rlwrap gosh -i
$ rmdir foo
$ fg
rlwrap gosh -i
gosh> (create-directory* "foo")
#t
gosh> ^D
$

Gauche-0.9:

$ ls -la
total 8
drwxr-xr-x   2 kaw  kaw   512 Dec 23 09:25 .
drwxr-xr-x  22 kaw  kaw  2048 Dec 23 09:25 ..
$ rlwrap gosh -i
gosh> (gauche-version)
"0.9"
gosh> (use file.util)
#<undef>
gosh> (create-directory* "foo")
#<undef>
gosh> [1] + Suspended            rlwrap gosh -i
$ ls -la
total 12
drwxr-xr-x   3 kaw  kaw   512 Dec 23 09:25 .
drwxr-xr-x  22 kaw  kaw  2048 Dec 23 09:25 ..
drwxr-xr-x   2 kaw  kaw   512 Dec 23 09:25 foo
$ fg
rlwrap gosh -i
gosh> (create-directory* "foo")
#<undef>
gosh> [1] + Suspended            rlwrap gosh -i
$ rmdir foo
$ fg
rlwrap gosh -i
gosh> (create-directory* "foo")
gosh> ^D
$

作ろうとするディレクトリがすでにある場合の戻り値が0.8.14と0.9で違っている。

0.9では、常に#<undef>を返してくるため、この関数を使ってディレクトリの作成を排他制御に使うことはできないことが判明。

修理

調査の結果、create-directory* を使って、資源の排他制御は出来ないことが判ったので、 他の方法を模索。

Gaucheのinfoファイルをさ迷った結果、sys-mkdirというより低レベルなライブラリ関数が使えそうなことが判明。

(sys-mkdir "foo" #o0755)
#<undef>
gosh> (sys-mkdir "foo" #o0755)
*** SYSTEM-ERROR: mkdir failed on foo: File exists
Stack Trace:
_______________________________________
gosh> 

sys-mkdir呼び出し時、ディレクトリがない場合と既に存在する場合で挙動が異なるので、 sys-mkdirは資源の排他制御に使える。

ただし、旧来のcreate-directory*のように戻り値で結果を返すのではなく、 ディレクトリがすでに存在している場合は、例外が発生するようになっているので、 戻り値をみて判断していた部分を例外処理として書き換える必要がある。

‥というわけで、コードを書いてみる;

diff -ru scmail-1.3.orig/scbayes.in scmail-1.3/scbayes.in
--- scmail-1.3.orig/scbayes.in	Tue Jun  8 00:08:50 2004
+++ scmail-1.3/scbayes.in	Sat Dec 26 18:55:25 2009
@@ -131,11 +131,11 @@
      (sys-rename (temporary-digest-file) (digest-file)))))
 
 (define (lock)
-  (unless (eq? (create-directory* (lock-file)) #t)
-     (scmail-wformat "~a is now being updated" (table-file))
-     (scmail-wformat "or perhaps ~a is staled." (lock-file))
-     (scmail-eformat "(Please remove the lock file if it is staled.)"))
-  )
+  (guard (exc ((<system-error> exc)
+               (begin (scmail-wformat "~a is now being updated" (table-file))
+                      (scmail-wformat "or perhaps ~a is staled." (lock-file))
+                      (scmail-eformat "(Please remove the lock file if it is staled.)"))))
+         (sys-mkdir (lock-file) #o0770)))
 
 (define force-learn? (make-parameter #f))

インストールしなおして、実行。

$ scbayes --learn-spam spam-plus
prepare:   100% |oooooooooooooooooooooooooooooooooooooooooooo| Time: 00:00:02 
learn:     100% |oooooooooooooooooooooooooooooooooooooooooooo| Time: 00:00:00 
summary:   0 spam mails is learned in 53/25 sec. (0 mails/sec.)
$

これにて一件落着。


Top / Scheme部会 / ネタ / scbayes_error

トップ   編集 凍結 差分 バックアップ 添付 複製 名前変更 リロード   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS
Last-modified: 2009-12-26 (土) 20:28:07 (2884d)