<document>
<title "stream 履歴">
<chapter "STREAM_LIBディレクティブ">
<renew "01/10/20">
src/h/machine/stream.h

STREAMタイプの中身を参照することを最小限に限定するために導入した。主に、src/stream, src/machine/std/streamの中のファイル、src/machineのSTREAM内部を参照する必要があるコードの先頭に

#define STREAM_LIB

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

typedef void STREAM;

が有効になる。マシン依存コードで、STREAM内部を参照しているところには注意が必要です。
</chapter>
<chapter "src/stream/s_close.cその他クローズ処理に関連するところ">
<renew "01/10/20">
<section "問題点">
クローズ処理のクリティカルセクションを強化した。まず、以下のようなクリティカルセクションが存在していた。

UNIXのように、ファイルをオープンしファイルデスクリプタを返す場合に、ファイルデスクリプタの値に、使われていない一番小さい値を当てる仕組みの場合、先に使っていたデスクリプタを閉じた瞬間に、再び同じ値がファイルオープンによって使われる可能性がある。

スレッドAがSTREAM * stに対してs_read/s_write/s_printfを発行し、まだ、マシン依存部分に達していない瞬間に、

スレッドBがst をクローズする。
その直後にスレッドCが全く関係のないファイルをオープンし、STREAM * st2を得た。たまたま、st2のオープン時に割り当てられたファイルデスクリプタがstで使われていたものと同じであったとする。

スレッドAにCPUが割り当てられ、stのファイルデスクリプタ（マシン依存部分）を参照する。この時点で参照されるのは、st2のファイルであり、間違っている。
</section>
<section "修正">
各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内のシグナル処理部分は唯一のスレッドのみが動くよう工夫する必要がある。
</section>
<section "実際のコード">
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であった場合は、システムコールの際呼び出しを行います。具体的には、以下のファイルに処理を施しました。
<pre>
src/machine/server/init.c
src/machine/client/new_connect.c
src/machine/thread/task.c
</pre>
OSによっては、クローズしたデスクリプタが再利用されるにまでにかなり時間がかかるような工夫がなされているものはこのアルゴリズムを適用する必要はない。

この場合は、h/machine/stream.hにダミーの関数を以下のように設定してください。
<pre>
#define init_machine_stream()	(0)
#define _s_insert_thread(s,t)	(0)
#define s_delete_thread(s,t)	(0)
</pre>
</section>
</chapter>
<chapter "プロセス関係の改造">
プロセス間のSTREAM の受け渡し
<renew "02/09/02">
これは、今後、サーバをいろいろなアーキテクチャに移植するにあたって、プロセスの生成と、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 は
<pre>
STREAM ** recv_streams;
int recv_streams_len;
</pre>
に納められます。recv_streams_len は受け取ったSTREAM の数です。
launch_proc に指定したSTREAM が複数ある場合、受け取り側は、その順番で
recv_streams にSTREAM をセットします
</chapter>
<chapter "S_TABLE のtype 変数">
<renew "02/09/02">
S_TABLE 構造体にあるtype 変数の使い方ですが、これはS_TABLEごとに異なるキャラクタを当てはめます。また、S_TABLE は最初の初期化で、insert_s_table を使い、リストに登録しなければなりません。さもないと、子プロセスがSTREAM を受け取る手段を探すことが出来ません。

現在使っているtype は以下の通りです。

<pre>
	f	: file
	s	: string
	c	: chain
	p	: pipe　（これは存在しない）
	P	: flushed pipe
	e	: terminal
	a	: accept

	s	: socket
</pre>
UNIX 系では、socket はfに統合されてしまいます。

その他、今後、

<pre>
	x	: XLoHTTP
</pre>
が登場します。
</chapter>
<chapter "s_get_socketip">
<renew "02/9/23">
s_get_socketipをS_TABLEの中のメンバーに加えました。
</chapter>
</title>
</document>