@node stream, thread, net, Top
@comment  node-name,  next,  previous,  up
@chapter stream

@menu
* stream history::              
* s_check_resource.c::          
* s_flush_pipe.c::              
@end menu

@node stream history, s_check_resource.c, stream, stream
@comment  node-name,  next,  previous,  up
@section stream 履歴



@table @samp
@item 01/10/20
@heading STREAM_LIB ディレクティブ
src/h/machine/stream.h
STREAMタイプの中身を参照することを最小限に限定するために導入した。主に、

@example
src/stream, src/machine/std/stream
@end example

の中のファイル、src/machine のSTREAM 内部を参照する必要があるコードの先頭に

@example
#define STREAM_LIB
@end example

をいれて使用する。これを入れないと、

@example
typedef void STREAM;
@end example

が有効になる。マシン依存コードで、STREAM 内部を参照しているところには注意が必要です。


@item 01/10/20
@heading src/stream/s_close.c その他クローズ処理に関連するところ
@subheading 問題点
クローズ処理のクリティカルセクションを強化した。まず、以下のようなクリティカルセクションが存在していた。
UNIX のように、ファイルをオープンしファイルデスクリプタを返す場合に、ファイルデスクリプタの値に、使われていない一番小さい値を当てる仕組みの場合、先に使っていたデスクリプタを閉じた瞬間に、再び同じ値がファイルオープンによって使われる可能性がある。
スレッドA がSTREAM * st に対してs_read/s_write/s_printf を発行し、まだ、マシン依存部分に達していない瞬間に、
スレッドB がst をクローズする。その直後にスレッドC が全く関係のないファイルをオープンし、STREAM * st2 を得た。たまたま、st2 のオープン時に割り当てられたファイルデスクリプタがst で使われていたもの
と同じであったとする。
スレッドA にCPU が割り当てられ、st のファイルデスクリプタ（マシン依存部分）を参照する。この時点で参照されるのは、st2 のファイルであり、間違っている。

@subheading 修正
各STREAM はどのタスクが現在参照しているか分かるようにする。STREAM のヘッダ部分にthread のリストS_FILE_THREAD * thread を用意する。s_read/s_write/s_printf を参照するときに、S_FILE_THREAD * thread へ自スレッドid を追加、参照した後にそれを取り除く。一方s_close は、まずh.tbl をクリアし、新しいスレッドがSTREAM を参照することのようにする。その後、thread リストを参照し、現在STREAMを参照中のスレッドにシグナルを発生する(USR1 シグナル)。これにより、参照中のスレッドは参照待ちを抜
けだしEINTR エラーでs_read/s_write/s_printf を抜ける。s_close はすべてのスレッドが抜けたことを確認して、s_close のマシン依存コードを参照し、ファイルデスクリプタをclose する。
シグナルを送る瞬間、s_read/s_write/s_printf を呼んだがマシン依存部分に達していないスレッドが存在する可能性があり、そのようなタスクはシグナルを受けても、無視してしまうので、シグナルは定期的に発生し、すべてのタスクを追い出す仕組みを作る。
また、シグナル処理関数の中で、再度シグナル処理関数を設定する処理をするが、この設定が終わるまでの短い間に、新しいシグナルが発生すると、その時点でプロセスが終了してしまう。そのため、s_close はシグナルを発生しては、対応するスレッドが追い出されたかをチェックし、追い出されたことを確認してから次のシグナルを発生する。s_close が同時に複数呼ばれた場合は、それらによるシグナルの競合で、同様の問題が発生するので、s_close 内のシグナル処理部分は唯一のスレッドのみが動くよう工夫する必要がある。

@subheading 実際のコード
src/machine/stream/s_thread.c を追加。src/h/stream.h のS_HEADER に　S_FILE_THREAD * thread;
メンバーを追加。
src/stream/s_read.c , src/stream/s_write , src/stream/s_printf の各関数の前後に、thread に自分を追加・削除する関数、_s_insert_thread, s_delete_thread を追加。これは、src/machine/stream/s_thread.c で定義されている(02.10.07：現在src/stream/s_open.c で定義されている)。最初にがあるのは、stream_lock をしない関数であり、_がないのは、stream_lock をする関数である。
この処理を施した場合、machine/stream/*内部のread 関数を除く、待ちを伴った関数には、EINTR を受けた場合の処理が必要となってきます。具体的には、accept / coneect / wai_sem システムコールが挙げられます。あまり影響はないですが、sleep システムコールもそうです。
以上のシステムコールを使う場合は、リターン後のエラーをチェックし、EINTR であった場合は、システムコールの際呼び出しを行います。具体的には、以下のファイルに処理を施しました。

@example
src/machine/server/init.c
src/machine/client/new_connect.c
src/machine/thread/task.c
@end example

OS によっては、クローズしたデスクリプタが再利用されるにまでにかなり時間がかかるような工夫がなされているものはこのアルゴリズムを適用する必要はない。
この場合は、h/machine/stream.h にダミーの関数を以下のように設定してください。

@example
#define init_machine_stream() (0)
#define _s_insert_thread(s,t) (0)
#define s_delete_thread(s,t) (0)
@end example


@item 02/09/02
@heading プロセス間のSTREAM の受け渡し
これは、今後、サーバをいろいろなアーキテクチャに移植するにあたって、プロセスの生成と、STREAM の受け渡し部分にアーキテクチャから切り離せないコードがあったことを改善するものです。また、GLOBALBASEのHTTP 上への載せ変えなどにも有効に働くと思われます。
いままで、プロセス間で、STREAM 構造体を機種に依存することなく、受け渡す手段がありませんでした。
UNIX 系では、ファイルデスクリプタを直接指定して受け取ることになっていました。この点を改造しました。

方法としては、STREAM を構成するデスクリプタなど、機種依存系のリソースに分解し、どのようにして分解したかというメッセージとともに子プロセスへ私、子プロセス側ではメッセージと渡された機種依存系リソースをもとに再構成するという方法です。
まず、STREAM の分解方法は、STREAM の種類（OOP 的に言えばクラスということです。）によって異なるので、s_proc_send / s_proc_recv というメンバ関数を作り、S_TABLE へ登録出来るようにしました。
s_proc_send がSTREAM を分解し、機種依存系リソースと、メッセージを作る関数。s_proc_recv がメッセージと分解された機種依存系リソースを受け取り、もとのSTREAM を作る関数です。src/machine/std/stream/s file.cや、src/machine/std/stream/s_flush pipe.c を参考にしてください。
s_proc_send はlaunch_proc のなかで呼ばれています。一方、s_proc_recv はproc_recv_stream という関数から呼ばれています。これは、機種依存部分のmain 関数のなかから呼ばれているので、機種非依存部分からは、あたかも、なにもせずにSTREAM を受け取ったかのように見えます。
受け取ったSTREAM は

@example
STREAM ** recv_streams;
int recv_streams_len;
@end example

に納められます。recv_streams_len は受け取ったSTREAM の数です。launch_proc に指定したSTREAM が複数ある場合、受け取り側は、その順番でrecv_streams にSTREAM をセットします



@item 02/09/02
@heading S_TABLE のtype 変数
S_TABLE 構造体にあるtype 変数の使い方ですが、これはS_TABLE ごとに異なるキャラクタを当てはめます。また、S_TABLE は最初の初期化で、insert_s_table を使い、リストに登録しなければなりません。さもないと、子プロセスがSTREAM を受け取る手段を探すことが出来ません。
現在使っているtype は以下の通りです。

@example
f : file
s : string
c : chain
p : pipe 　（これは存在しない）
P : flushed pipe
e : terminal
a : accept
s : socket
@end example

UNIX 系では、socket はf に統合されてしまいます。
その他、今後、

@example
x : XLoHTTP
@end example

が登場します。


@item 02/9/23
@heading s_get_socketip
s_get_socketip をS_TABLE の中のメンバーに加えました。
@end table

@node s_check_resource.c, s_flush_pipe.c, stream history, stream
@comment  node-name,  next,  previous,  up
@section ./s_check_resource.c

@defun s_check_resource

@cartouche
@example
int
s_check_resource( )
@end example
@end cartouche

@heading 説明
ファイルディスクリプタがいくつ開かれているかチェックするルーチン

@heading リターンデータ
now_open_files がオープンできるファイルの最大の半分がオープンされたときに、−１を返し、それ以外のときに０を返す。

@heading 詳細
ファイルディスクリプタがオープンされるところには、int now_open_files をカウントアップするコードを入れ、close するところではカウントダウンする。
ウィンドウズのようにファイルディスクリプタが二種類ある場合は、それぞれのオープンファイル数を調べ、どちらかが半分を超したら−１を返す。どちらも半分以下であれば0 を返すように作るとよい。
特に、ファイルディスクリプタのオープンの数に上限がない場合は、つねに0 を返すように作る。

@heading 履歴

@table @samp
@item 01/10/20
この関数は、xl/lib/lisp/gc task.c にてギャーベージコレクションを発生させるタイミングを決めるために使っている。それでも、足りなくなる可能性はあるので、ファイルディスクリプタを開いた後、エラーをチェックし、EMFILE/EAGAIN エラーが生じた場合は(*gc stream)( ) を呼び、システムコールを再実行するコードを入れてください。(*gc stream) は定義されていない場合もあるので、gc stream==0 をチェックしてください。
@end table
@end defun

@node s_flush_pipe.c,  , s_check_resource.c, stream
@comment  node-name,  next,  previous,  up
@section s_flush_pipe 履歴



@table @samp
@item 02/09/04
@heading flushed_pipe の実装
pipe はUNIX のシェルの起動に使いますが、それ以外の場合は、flushed pipe というストリームを使うことにしました。src/machine/std/stream/s_flush_pipe.c で実装されています。

これは、パイプを通して、データがいつフラッシュされたかをデータの受け手に伝えたいためです。今後、XLoHTTP を実装するために必要になってきます。
方法としては、パイプに書き込むすべてのデータにはその前にデータの長さをヘッダとして付けたパケットにするという方法をとります。このデータ長が-1 のものは、ここでフラッシュが呼び出されたことを意味します。


@item 02/09/23
@heading 単方向から双方向に
機種依存のsrc/machine/std/stream/flush_pipe.c は、かつて単方向だったのを双方向にしました。flush_pipeとは普通のパイプに、データを送信するときに、データ長を付け、パケット化したものです。そのため（負のデータ長）のデータには、様々な意味を付加することが出来ます。
一つ大きな利点は、パイプの片方でフラッシュを発行すると、それがもう一方で認知出来ることです。
その他、fpipe の片方の端にip アドレスを設定するルーチンを作り、もう片方からs_get_socketip により取り出すことが出来ます。
STREAM をHTTP 上に実装する(XLoHTTP) ためには、クライアントがサーバに接続後、サーバは、そのストリームを、fork したプロセスに渡せなければなりません。そのため、サーバ側では、XLoHTTP で入ってきたものをflush_pipe へリレーする仕組みを実装しています。そして、このflush_pipe の端点をプロセスへ受け渡します。
もし、プロセス間に受け渡しが出来ないSTREAM があり、これの受け渡しを実装したい場合は、この方法があります。
@end table
