Sagittariusは0.6.9でポート周りにかなりバグを混入した(弄った)のだが、頭を悩ませるバグが顕在化した。それが表題のポートである。どこで使われているかといえばTLSである。
何が問題か?いくつかの問題が合わさってるのではあるんだが、一つは
get-bytevector-n
とカスタムポートの相性。カスタムポートは要求されたバイト(文字数)を返す必要はないので、例えば1バイトずつ返して呼び出し元に判断させるということができる。例えば以下のようなの(import (rnrs)) (let* ([pos 0] [p (make-custom-binary-input-port "custom in" (lambda (bv start count) (if (= pos 16) 0 (begin (set! pos (+ 1 pos)) (bytevector-u8-set! bv start pos) 1))) (lambda () pos) (lambda (p) (set! pos p)) (lambda () 'ok))]) (get-bytevector-n p 3)) ;;-> #vu8(1 2 3)R6RSテストスイーツからの抜粋なのだが、read!手続きは1バイトずつ処理し
get-bytevector-n
が最終的に何を返すか決定するという形である。カスタムポートが扱うのが有限のデータなら特に問題ないのだが、ソケットのようにいつEOFがくるか分からないものだと割と困るのである。例えばread!の部分がrecv
を使っていたとして、あるソケットは要求の半分のデータを受信したする。そうするとget-bytevector-n
は残りの半分を取得するためにもう一回read!を呼び、処理はブロックされる。不定長のデータを扱うのに
get-bytevector-n
なんて使うなという話なのだが、本当の問題は別のところにある。0.6.9からバッファポートを導入したのだが、こいつとカスタムポートの相性が悪い。何が悪いかと言えば、バッファを埋めるのにget-bytevector-n
相当のことがされているのである。カスタムポートを実装したときに、あまり何も考えずにポート側でバイト数を数えるようにしてしまったので複数回の呼び出しが起きるという話なのではある。解決方法は多分2つくらいあって、
- バッファを埋める処理を別枠にする
- 現状ポート側でやってることを
get-bytevector-n
に移す
2 comments:
buffer-modeがlineの時、下請けポートからの読み出しをたかだか1回だけにするってのはどうですか。ちょっとlineという名前が紛らわしいですがもともとそういう含みを持たせてたはず ("or other implementation-dependent behavior")。Gaucheでは出力ポートでのline bufferingに相当するフラグを入力ポートではmodest bufferingと呼んでてそういう動作になってます。確かr6rsのI/Oの議論の時に「ソケットとかで使うならこういうモードがいるよ」って話を出して、何やかやの結果としてじゃあlineモードをそういう目的に使えればいいじゃんって話になったような気がします。
実際、入力バッファポートがline endingでバッファリングを切る意味って全く無いので (get-lineすればいいのだから)、その意味ではあの文言は不適切だったかも。
なるほど、それはありかも。ただ、現状の設計でバッファポートを除く全てのポートはバッファを持たないようにしてるので(transcoded-portみたいにbuffered-portで不可逆変換)、そのままは使えなさそうですね。ポート自体にそういうフラグを持たせるかなぁ。
lineの文言は確かに言い回しとして理解しづらいかも。完全に行指向だと理解してました。o
Post a Comment