並行システム
                               システム情報系情報工学域,
			       システム情報工学研究科コンピュータサイエンス専攻
                               新城 靖
                               <yas@cs.tsukuba.ac.jp>
このページは、次の URL にあります。
	http://www.cs.tsukuba.ac.jp/~yas/cs/csys-2018/2018-07-27
あるいは、次のページから手繰っていくこともできます。
	http://www.cs.tsukuba.ac.jp/~yas/cs/
	http://www.cs.tsukuba.ac.jp/~yas/
https://dl.acm.org/citation.cfm?doid=72551.72553
http://www2a.biglobe.ne.jp/~seki/ruby/d208.html,
関 将俊氏による Rinda の解説。本のサンプルコードと説明含む。
https://dx.doi.org/10.1007/s10766-008-0086-1
並列処理の最終結果に焦点を当る。 we might envision parallelism by starting with the finished product, the result.
例: 家の建築: House building
例: 家の建築:
例: 家の建築:
問題にあったものを使う。 Choose the paradigm that is most natural for the problem.
実際の家の建築では、全部の方法が使われている。 All paradigms are used in real house building.
うまく行く例:ベクトルの足し算(adding vectors): S[i] = A[i] + B[i]

図? タスクバッグ
 図? ライブデータ構造体 live data structure
 図? メッセージ・パッシング message passing
 図? 分散データ構造体 distributed data structures
◆N-体問題:結果並列法 N-body problem: result parallelism
ライブデータ構造体の図 参照。位置の配列: M[i][j]、i=0..N-1, j=0..q、M[][0] に最初の位置。
position(i,j), 繰り返し j での 物体 i の位置を計算する。 その時、一つ前のステップの位置 position(i,j-1) (i=0..N-1) の処理が終了するのを待つ。
プロセスは、position(i,j) の計算をするとデータに化けて行く。 ライブデータ構造体の図 で、 赤い丸は活動中のプロセス。黒い丸は終了したプロセス。
◆N-体問題:専門家並列法 N-body problem: specialist parallelism
物体に対応したプロセスを作る。
◆N-体問題:手順並列法 N-body problem: agenda parallelism
手順(ワーカの仕事):集合に含まれている全ての物体について、次の位置を 計算する。マスタで、N 個の物体を作る。
ワーカを作る。ワーカの数は、N 個ではなくて、もっと少ない(CPU数と同じに する)。
物体の位置を分散データ構造体(共有メモリ)に置く。

図? パラダイム間の関係 relationship among paradigms
解決1:ライブデータ構造体を、受動的な構造体に書き換える。プロセスを複数 の構造体に対応させる。
問題2:分散データ構造体で書いたプログラム(共有空間が必要)が、NORMA で うまく動かない。
解決:メッセージ・パッシングに変換する。
Carriero と Gelernter の主張:分散データ構造体がいい。
Java では、スレッド、RMI、Javaspaces の順に導入された。
モニタは、メッセージパッシングの仲間か分散データ構造体か。
タプルペースモデル(tuple space model)で分散データ構造体(distributed datat structures)を支援。(メッセージ・パッシング(message passing)やラ イブデータ構造体(live data structures)的なプログラムも書ける。)
タプルは、型付きの値の並び。 A tuple is a series of typed values.
タプルの例 Examples of tuples:
("a string", 10.01, 17, 10)
(0,1)
2種類のタプル two kinds of tuples
float f; int i,x;
out("a string", 10.01, 17, x)
in("a string", ?f, ?i, y)
eval("e", 7, exp(7))
新しくプロセス(スレッド)が作られ、exp(7) を計算しはじめる。プロセスが
終了すると、最終結果は、値に変わり、out() されたのと同じになる。
初期化:
    out("counter-17",0)
読込み read:
    int val;
    rd("counter-17",?val)
変更 update:
    int val;
    in("counter-17",?val)
    val = val + 1 ;
    out("counter-17",val)
Pthread では、mutex が必要になる所、Linda では不要。
宣言:
    int counter_17;
読込み read:
    int val;
    pthread_mutex_lock( &mutex1 );
    val = counter_17;
    pthread_mutex_unlock( &mutex1 );
変更 update:
    int val;
    pthread_mutex_lock( &mutex1 );
    val = counter_17;
    val = val + 1 ;
    counter_17 = val;
    pthread_mutex_unlock( &mutex1 );
初期化:
    out("sem-1"); /*初期値が 4 の場合。*/
    out("sem-1");
    out("sem-1");
    out("sem-1");
P命令/down/aquire/wait: 
    in("sem-1")
V命令/up/release/signal:
    out("sem-1");
同じタプルを out したら溜る。
注意:in() したデータは、1プロセスでしかアクセスされないので、セマフォ などによるロックは不要。
初期値が 1 なら、バイナリ・セマフォ。
今日の課題のアクセスカウンタでは、セマフォは使わない。 大域的なカウンタ変数の例 を真似する。
仕事を入れる add a task:
    out("task",TaskDescription);
仕事を取り出す remove a task:
    in("task",?NewTask);
for( i=0 ; i<N; i++ )
{
    func(i,args);
}
並列プログラム a parallel program:
for( i=0 ; i<N; i++ )
{
    eval ("loop-33", func(i,args) ); // プロセス生成 process creation, fork
}
for( i=0 ; i<N; i++ )
{
    in("loop-33", 1 ); // 待ち, join
}
func(i,args)
{
   ...
   return( 1 );
}
これは並列処理の fork-join モデルを実現した物。

図? fork-join model

図? fork-joinの繰り返し

図? fork-joinの繰り返し
n プロセスのバリア
初期化:
    out("barrier-37",n)
各プロセス: 1減らして、0になるのを待つ。
    in("barrier-37",?val)
    out("barrier-37",val-1)
    rd("barrier-37",0)
C言語 C language
    A[10];
Linda tuple space
    ("A",0,val00)
    ("A",1,val01)
    ("A",2,val02)
    ...
    ("A",9,val99)
C言語 C language
    A[10][10];
Linda tuple space
    ("A",0,0,val00)
    ("A",0,1,val01)
    ("A",0,2,val02)
    ...
    ("A",9,9,val99)

図? Linda in-stream
ストリームデータ stream data
    ("stream",0,val0)
    ("stream",1,val1)
    ("stream",2,val2)
    ...
ポインタ pointers
    ("stream","head",0)
    ("stream","tail",0)
ストリームに要素を追加 add an element to a stream:
    int index; // ローカル変数
    in("stream","tail",?index);
    out("stream","tail",index+1);
    out("stream",index,new_element);
ストリームから要素を取り出す take an element from a stream:
    int index; // ローカル変数
    in("stream","head",?index);
    out("stream","head",index+1);
    in("stream",index,?element);
これは、複数source、複数sink。
source、sinkが1つなら、head, tail をタプル空間に置かなくてもよい。
head の代わりに、局所変数でアクセスする。
ストリームから要素を取り出す take an element from a stream:
    int index=0 ;
    ...
    rd("stream",index++,?element);
rd() しかなされず、in() する人がいないので、タプル空間にストリームのデー
タが残ってしまう。
interface JavaSpace を実現したオブジェクト
| Linda | JavaSpace | 説明 | 
| out | write | タプルをタプル空間内に生成する。 | 
| in | take | タプルを取り去る | 
| rd | read | in/takeと似ているが、タプルがタプル空間に残る。 | 
write, read, take を使う部分のプログラムは簡単だが、space を利用可能に するのには苦労する。
JavaSpaces が提供する空間の特徴
net/jini/core/entry/Entry.java:
public interface Entry extends java.io.Serializable {
}
net/jini/space/JavaSpace.java:
public interface JavaSpace {
    Lease write(Entry entry, Transaction txn, long lease)
        throws TransactionException, RemoteException;
    long NO_WAIT = 0;
    Entry read(Entry tmpl, Transaction txn, long timeout)
        throws UnusableEntryException, TransactionException, 
               InterruptedException, RemoteException;
    Entry readIfExists(Entry tmpl, Transaction txn, long timeout)
        throws UnusableEntryException, TransactionException, 
               InterruptedException, RemoteException;
    Entry take(Entry tmpl, Transaction txn, long timeout)
        throws UnusableEntryException, TransactionException, 
               InterruptedException, RemoteException;
    Entry takeIfExists(Entry tmpl, Transaction txn, long timeout)
        throws UnusableEntryException, TransactionException, 
               InterruptedException, RemoteException;
    EventRegistration
        notify(Entry tmpl, Transaction txn, RemoteEventListener listener,
               long lease, MarshalledObject handback)
        throws TransactionException, RemoteException;
    Entry snapshot(Entry e) throws RemoteException;
}
notify() では、マッチするテンプレートが write された時、 RemoteEventListener の notify(RemoteEvent theEvent) が呼ばれる。
snapshot() では、エントリのスナップショットが返される。元のエントリが 更新されても、変化しない。read(), take() のテンプレート用。
次のチュートリアルが参考になる。
[1] Qusay H. Mamoud: "Getting Started With JavaSpaces Technology: Beyond
Conventional Distributed Programming Paradigms",
July 12, 2005.
http://www.oracle.com/technetwork/articles/javase/javaspaces-140665.html
(リンク切れ 
http://java.sun.com/developer/technicalArticles/tools/JavaSpaces/
)
[2] The Blitz Project: 
"HelloWorld Example",
https://github.com/dancres/blitzjavaspaces/tree/master/examples/helloworld
両方とも、後者のサイトにある Lookup.java を使っている。
Jini は、以前は、"Jini Technology Starter Kit" という名前のパッケージで 配布されていた。現在は、Apache River として開発が続けられ配布されている。
http://river.apache.org/,
Apache River トップページ
http://river.apache.org/user-doc/getting-started.html,
User Guide - Basic River Services,HTTPサーバ、Lookup サーバ "reggie",A Java Space サーバ "outrigger" の使い方。
http://river.apache.org/user-doc/releases.html,
ソースとバイナリのダウンロードページ。(v2.2.3, 2.2.2, 2.2.1, 2.2.0, v3.0.0)
http://river.apache.org/release-doc/current/api/index.html,
Apache River API Documentation。
JavaSpaces の例題を実行する前に、Apache River に含まれているいくつかの サーバを実行する必要がある。
http://www.oracle.com/technetwork/java/introduction-135941.html
Chapter 1
   1: // From the JavaSpaces book
   2: //package jsbook.chapter1.helloWorld;
   3: 
   4: import net.jini.core.entry.Entry;
   5: 
   6: public class Message implements Entry {
   7:     public String content;
   8: 
   9:     public Message() {
  10:     }
  11: }
   1: //
   2: // HelloWriter.java
   3: // 
   4: 
   5: import net.jini.space.JavaSpace;
   6: 
   7: class HelloWriter {
   8:     public static void main(String[] argv)
   9:     {
  10:         Lookup finder = new Lookup(JavaSpace.class);
  11:         JavaSpace space = (JavaSpace) finder.getService();
  12:         if( space == null )
  13:         {
  14:             System.err.println("No JavaSpace found.");
  15:             System.exit( 1 );
  16:         }
  17:         Message msg = new Message();
  18:         msg.content = "Hello" ;
  19:         try
  20:         {
  21:             space.write(msg, null, net.jini.core.lease.Lease.FOREVER);
  22:             System.out.println("HelloWriter: wrote["+msg.content+"]");
  23:         }
  24:         catch( Exception e )
  25:         {
  26:             System.err.println("JavaSpace write error "+e.getMessage());
  27:             e.printStackTrace();
  28:             System.exit( -1 );
  29:         }
  30:         System.exit( 0 );
  31:     }
  32: }
write() のインタフェース
    Lease write(Entry entry, Transaction txn, long lease)
        throws TransactionException, RemoteException;
txnはトランザクションにより複数の操作をグループ化するもの。null を
指定すれば、その機能は使われない。
lease は、時間を指定する。その時間だけは、空間がそのエントリを記憶して いる。空間が Garbage Collection に使う。Lease.FOREVER は、無限に覚えて いることを意味する。単位は、ミリ秒。
   1: //
   2: // HelloReader.java
   3: // 
   4: 
   5: import net.jini.space.JavaSpace;
   6: 
   7: public class HelloReader {
   8:     public static void main(String[] argv)
   9:     {
  10:         Lookup finder = new Lookup(JavaSpace.class);
  11:         JavaSpace space = (JavaSpace) finder.getService();
  12:         if( space == null )
  13:         {
  14:             System.err.println("No JavaSpace found.");
  15:             System.exit( 1 );
  16:         }
  17: 
  18:         Message template = new Message();
  19:         Message result;
  20:         try
  21:         {
  22:             result = (Message)space.read(template, null, Long.MAX_VALUE);
  23:             System.out.println("HelloReader: read ["+result.content+"]");
  24:         }
  25:         catch( Exception e )
  26:         {
  27:             System.err.println("JavaSpace read error "+e.getMessage());
  28:             e.printStackTrace();
  29:             System.exit( -1 );
  30:         }
  31: 
  32:         System.exit( 0 );
  33:     }
  34: }
read() のインタフェース
    Entry read(Entry tmpl, Transaction txn, long timeout)
        throws UnusableEntryException, TransactionException, 
               InterruptedException, RemoteException;
read の最初の引数は、テンプレートである。
read は、空間からテンプレートとマッチするエントリを読み出す。
read() は、マッチするエントリがなければ、timeout するまで待つ。 Long.MAX_VALUE は、無限に待つことを意味する。待ちたくない時には、 JavaSpaces.NO_WAIT を使うか、readIfExists() を使う。
例:
public class Vegetable implements Entry {
}
public class Fruit implements Entry {
}
public class Apple extends Fruit {
}
public class Orange extends Fruit {
}
「同じ値」は、serialize (マーシャリング)してバイトレベルで同じという意 味。エントリは、タプル空間にある時にはserialize された形で保存されてい る。
null をワイルド・カードに使う問題点は、「本当に null の値を持つエントリ 探す」ということができないこと。 区別したい時には、Boolean を添える。
public class NotePtr implements Entry {
    public Boolean ptrIsNull;
    public Node ptr;
}
...
NodePtr template = new NodePtr();
template.ptrIsNull = new Boolean(true);
template.ptr = null; // for completeness; null by default
null をワイルドカードに使ったので、空間に置くエントリのフィールドは、
オブジェクトにする。int, boolean, float, double などは、Integer,
Boolean, Float, Double などの wrappe クラスを使う。
注意:連続する read() が同じオブジェクトを返す保証はない。
   1: //
   2: // HelloTaker.java
   3: // 
   4: 
   5: import net.jini.space.JavaSpace;
   6: 
   7: public class HelloTaker {
   8:     public static void main(String[] argv)
   9:     {
  10:         Lookup finder = new Lookup(JavaSpace.class);
  11:         JavaSpace space = (JavaSpace) finder.getService();
  12:         if( space == null )
  13:         {
  14:             System.err.println("No JavaSpace found.");
  15:             System.exit( 1 );
  16:         }
  17: 
  18:         Message template = new Message();
  19:         Message result;
  20:         try
  21:         {
  22:             result = (Message)space.take(template, null, Long.MAX_VALUE);
  23:             System.out.println("HelloTaker: took ["+result.content+"]");
  24:         }
  25:         catch( Exception e )
  26:         {
  27:             System.err.println("JavaSpace read error "+e.getMessage());
  28:             e.printStackTrace();
  29:             System.exit( -1 );
  30:         }
  31: 
  32:         System.exit( 0 );
  33:     }
  34: }
% diff HelloReader.java HelloTaker.java
2c2
< // HelloReader.java
---
> // HelloTaker.java
7c7
< public class HelloReader {
---
> public class HelloTaker {
22,23c22,23
<           result = (Message)space.read(template, null, Long.MAX_VALUE);
<           System.out.println("HelloReader: read ["+result.content+"]");
---
>           result = (Message)space.take(template, null, Long.MAX_VALUE);
>           System.out.println("HelloTaker: took ["+result.content+"]");
% 
take() は、read() と同じだが、エントリを空間から取り去る所が異なる。
$ ls -l ![[←]](../icons/screen-return.gif) total 0
drwxr-xr-x@ 7 yas  wheel  374  6 20  2013 apache-river-2.2.2
$ ls apache-river-2.2.2/
total 0
drwxr-xr-x@ 7 yas  wheel  374  6 20  2013 apache-river-2.2.2
$ ls apache-river-2.2.2/ ![[←]](../icons/screen-return.gif) LICENSE		NOTICE		configentry	lib		lib-ext
LICENSE.txt	NOTICE.txt	examples	lib-dl
$
LICENSE		NOTICE		configentry	lib		lib-ext
LICENSE.txt	NOTICE.txt	examples	lib-dl
$ ![[]](../icons/screen-cursor.gif) 
$ cd apache-river-2.2.2/examples/hello/ ![[←]](../icons/screen-return.gif) $ ls
$ ls ![[←]](../icons/screen-return.gif) config		index.html	lib		scripts
doc				krb-setup.html	prebuiltkeys
$
config		index.html	lib		scripts
doc				krb-setup.html	prebuiltkeys
$ ![[]](../icons/screen-cursor.gif) 
Jini関連のサーバ用にウインドウを 3 枚開く。Apache River の examples/hello に cd する。 Open three windows. Change directory to examples/hello in Apache River.
$ cd apache-river-2.2.2/examples/hello/ ![[←]](../icons/screen-return.gif) $ ls
$ ls ![[←]](../icons/screen-return.gif) config  doc  index.html  krb-setup.html  lib  prebuiltkeys  scripts
$
config  doc  index.html  krb-setup.html  lib  prebuiltkeys  scripts
$ ![[]](../icons/screen-cursor.gif) 
最初のウインドウでHTTP server を実行する。.sh は、Unix 用。.bat は、
Windows 用。
$ tail -1 ./scripts/httpd.sh   ![[←]](../icons/screen-return.gif) java -jar ../../lib/classserver.jar -port 8080 -dir lib:../../lib-dl $*
$ tail -1 ./scripts/httpd.bat
java -jar ../../lib/classserver.jar -port 8080 -dir lib:../../lib-dl $*
$ tail -1 ./scripts/httpd.bat ![[←]](../icons/screen-return.gif) java -jar ..\..\lib\classserver.jar -port 8080 -dir lib;..\..\lib-dl %1
$ ./scripts/httpd.sh
java -jar ..\..\lib\classserver.jar -port 8080 -dir lib;..\..\lib-dl %1
$ ./scripts/httpd.sh  ![[←]](../icons/screen-return.gif) + java -jar ../../lib/classserver.jar -port 8080 -dir lib:../../lib-dl
Jan 25, 2012 9:54:13 PM com.sun.jini.tool.ClassServer run
INFO: ClassServer started [[lib/, ../../lib-dl/], port 8080]
^Cで終了
+ java -jar ../../lib/classserver.jar -port 8080 -dir lib:../../lib-dl
Jan 25, 2012 9:54:13 PM com.sun.jini.tool.ClassServer run
INFO: ClassServer started [[lib/, ../../lib-dl/], port 8080]
^Cで終了
2番目のウインドウで Service Registrar (Lookup Service) を実行する。
$ tail -4 scripts/jrmp-reggie.sh ![[←]](../icons/screen-return.gif) java -Djava.security.policy=config/start.policy \
     -Djava.ext.dirs=../../lib-ext/     \
     -jar ../../lib/start.jar   \
     config/start-reggie.config
$ tail -3 scripts/jrmp-reggie.bat
java -Djava.security.policy=config/start.policy \
     -Djava.ext.dirs=../../lib-ext/     \
     -jar ../../lib/start.jar   \
     config/start-reggie.config
$ tail -3 scripts/jrmp-reggie.bat ![[←]](../icons/screen-return.gif) java -Djava.security.policy=config\start.policy ^
     -jar ..\..\lib\start.jar ^
     config\start-reggie.config
$ ./scripts/jrmp-reggie.sh
java -Djava.security.policy=config\start.policy ^
     -jar ..\..\lib\start.jar ^
     config\start-reggie.config
$ ./scripts/jrmp-reggie.sh  ![[←]](../icons/screen-return.gif) + java -Djava.security.policy=config/start.policy -Djava.ext.dirs=../../lib-ext/ -jar ../../lib/start.jar config/start-reggie.config
Jan 25, 2012 9:58:14 PM com.sun.jini.reggie.RegistrarImpl init
INFO: started Reggie: 6879dd38-7782-4f43-a172-baffff666af7, [nonsecure.hello.example.jini.sun.com], jini://example.com/
^Cで終了
+ java -Djava.security.policy=config/start.policy -Djava.ext.dirs=../../lib-ext/ -jar ../../lib/start.jar config/start-reggie.config
Jan 25, 2012 9:58:14 PM com.sun.jini.reggie.RegistrarImpl init
INFO: started Reggie: 6879dd38-7782-4f43-a172-baffff666af7, [nonsecure.hello.example.jini.sun.com], jini://example.com/
^Cで終了
MacOSX Tips 1: java.net.UnknownHostException が出たら /etc/hosts に
`hostname` を登録してみる。
$ hostname  ![[←]](../icons/screen-return.gif) sharon
$ egrep `hostname` /etc/hosts
sharon
$ egrep `hostname` /etc/hosts ![[←]](../icons/screen-return.gif) 127.0.0.1       localhost sharon sharon.domainname
$
127.0.0.1       localhost sharon sharon.domainname
$ ![[]](../icons/screen-cursor.gif) 
MacOSX Tips 2: com.sun.jini.start.ServiceStarter
checkResultFailures で Exception creating service が出たら、
-Djava.rmi.server.useCodebaseOnly=false を試すと良い。
river-user mailing list の記事参照。
Java Release Note の Changes to RMI 参照。
$ java -Djava.security.policy=config/start.policy -Djava.ext.dirs=../../lib-ext/ -Djava.rmi.server.useCodebaseOnly=false -jar ../../lib/start.jar  config/start-reggie.config ![[←]](../icons/screen-return.gif) 
3番目のウインドウで Java Space サーバを実行する。配布されているスクリ
プトに .bat バグがあり、うまく実行できない。config/policy.all というファ
イルは存在しない。また、.sh は、そもそも含まれていない。
$ tail -3 scripts/jrmp-outrigger-group.bat ![[←]](../icons/screen-return.gif) java -Djava.security.policy=config\policy.all ^
     -jar ..\..\lib\start.jar ^
     config\start-outrigger-group.config
$ ls config/policy.all
java -Djava.security.policy=config\policy.all ^
     -jar ..\..\lib\start.jar ^
     config\start-outrigger-group.config
$ ls config/policy.all ![[←]](../icons/screen-return.gif) ls: config/policy.all: No such file or directory
$ ls scripts/jrmp-outrigger-group.sh
ls: config/policy.all: No such file or directory
$ ls scripts/jrmp-outrigger-group.sh   ![[←]](../icons/screen-return.gif) ls: scripts/jrmp-outrigger-group.sh: No such file or directory
$
ls: scripts/jrmp-outrigger-group.sh: No such file or directory
$ ![[]](../icons/screen-cursor.gif) 
次のように手で引数を与えて実行する(Unix)。
$ java -Djava.security.policy=config/start.policy \ ![[←]](../icons/screen-return.gif) -jar ../../lib/start.jar \
     config/start-outrigger-group.config
^Cで終了
     -jar ../../lib/start.jar \
     config/start-outrigger-group.config
^Cで終了
MacOSX Tips 3: 上と同じように、
com.sun.jini.start.ServiceStarter
checkResultFailures で Exception creating service が出たら、
上と同じように、
-Djava.rmi.server.useCodebaseOnly=false を試すと良い。
java -Djava.security.policy=config/start.policy -Djava.rmi.server.useCodebaseOnly=false -jar ../../lib/start.jar config/start-outrigger-group.config
% wget http://www.cs.tsukuba.ac.jp/~yas/cs/csys-2018/2018-07-27/ex/Lookup.java ![[←]](../icons/screen-return.gif) % wget http://www.cs.tsukuba.ac.jp/~yas/cs/csys-2018/2018-07-27/ex/Message.java
% wget http://www.cs.tsukuba.ac.jp/~yas/cs/csys-2018/2018-07-27/ex/Message.java ![[←]](../icons/screen-return.gif) % wget http://www.cs.tsukuba.ac.jp/~yas/cs/csys-2018/2018-07-27/ex/HelloWriter.java
% wget http://www.cs.tsukuba.ac.jp/~yas/cs/csys-2018/2018-07-27/ex/HelloWriter.java ![[←]](../icons/screen-return.gif) % wget http://www.cs.tsukuba.ac.jp/~yas/cs/csys-2018/2018-07-27/ex/HelloReader.java
% wget http://www.cs.tsukuba.ac.jp/~yas/cs/csys-2018/2018-07-27/ex/HelloReader.java ![[←]](../icons/screen-return.gif) % wget http://www.cs.tsukuba.ac.jp/~yas/cs/csys-2018/2018-07-27/ex/HelloTaker.java
% wget http://www.cs.tsukuba.ac.jp/~yas/cs/csys-2018/2018-07-27/ex/HelloTaker.java ![[←]](../icons/screen-return.gif) % wget http://www.cs.tsukuba.ac.jp/~yas/cs/csys-2018/2018-07-27/ex/Makefile
% wget http://www.cs.tsukuba.ac.jp/~yas/cs/csys-2018/2018-07-27/ex/Makefile ![[←]](../icons/screen-return.gif) % emacs Makefile (jinilibを、自分の環境に合わせて書き換える。change jinilib)
% emacs Makefile (jinilibを、自分の環境に合わせて書き換える。change jinilib) ![[←]](../icons/screen-return.gif) % make hello
% make hello ![[←]](../icons/screen-return.gif) javac -classpath .:略/jini-core.jar:略/jini-ext.jar:略/reggie.jar:略/outrigger.jar Lookup.java
javac -classpath .:略/jini-core.jar:略/jini-ext.jar:略/reggie.jar:略/outrigger.jar Message.java
javac -classpath .:略/jini-core.jar:略/jini-ext.jar:略/reggie.jar:略/outrigger.jar HelloWriter.java
javac -classpath .:略/jini-core.jar:略/jini-ext.jar:略/reggie.jar:略/outrigger.jar HelloReader.java
javac -classpath .:略/jini-core.jar:略/jini-ext.jar:略/reggie.jar:略/outrigger.jar HelloTaker.java
% ls
javac -classpath .:略/jini-core.jar:略/jini-ext.jar:略/reggie.jar:略/outrigger.jar Lookup.java
javac -classpath .:略/jini-core.jar:略/jini-ext.jar:略/reggie.jar:略/outrigger.jar Message.java
javac -classpath .:略/jini-core.jar:略/jini-ext.jar:略/reggie.jar:略/outrigger.jar HelloWriter.java
javac -classpath .:略/jini-core.jar:略/jini-ext.jar:略/reggie.jar:略/outrigger.jar HelloReader.java
javac -classpath .:略/jini-core.jar:略/jini-ext.jar:略/reggie.jar:略/outrigger.jar HelloTaker.java
% ls  ![[←]](../icons/screen-return.gif) HelloReader.class       HelloWriter.class       Makefile
HelloReader.java        HelloWriter.java        Message.class
HelloTaker.class        Lookup.class            Message.java
HelloTaker.java         Lookup.java
%
HelloReader.class       HelloWriter.class       Makefile
HelloReader.java        HelloWriter.java        Message.class
HelloTaker.class        Lookup.class            Message.java
HelloTaker.java         Lookup.java
% ![[]](../icons/screen-cursor.gif) 
一方のウインドウを2枚開く。 Open two windows.
一方のウインドウで Writer を動作させる。
$ PS1='Writer$ ' ![[←]](../icons/screen-return.gif) Writer$ make run-HelloWriter
Writer$ make run-HelloWriter![[←]](../icons/screen-return.gif) HelloWriter: wrote[Hello]
Writer$
HelloWriter: wrote[Hello]
Writer$ ![[]](../icons/screen-cursor.gif) 
もう一方のウインドウで Reader や Taker を動作させる。
$ make run-HelloReader ![[←]](../icons/screen-return.gif) HelloReader: read [Hello]
$ make run-HelloReader
HelloReader: read [Hello]
$ make run-HelloReader ![[←]](../icons/screen-return.gif) HelloReader: read [Hello]
$ make run-HelloReader
HelloReader: read [Hello]
$ make run-HelloReader ![[←]](../icons/screen-return.gif) HelloReader: read [Hello]
$ make run-HelloTaker
HelloReader: read [Hello]
$ make run-HelloTaker  ![[←]](../icons/screen-return.gif) HelloTaker: took [Hello]
$ make run-HelloTaker
HelloTaker: took [Hello]
$ make run-HelloTaker ![[←]](../icons/screen-return.gif) (固まる)
(固まる)
タプルがない状態先に take/read すると、止まる。この状態で、write すれば、
read/take は先に進む。
Writer$ make run-HelloWriter![[←]](../icons/screen-return.gif) HelloWriter: wrote[Hello]
Writer$
HelloWriter: wrote[Hello]
Writer$ ![[]](../icons/screen-cursor.gif) 
read() でのマッチングは、serialize(marshaling) された形で比較される。
一度空間に入れて出すと、オブジェクトが増えることがある。
public class E1 implements Entry {
    public Integer obj1;
    public Integer obj2;
}
Integer obj = Integer(0);
E1 e = new E1();
e.obj1 =obj;
e.obj2 =obj;
引数なしのコンストラクタが必要である。
read(), take() でのエントリの復元
read(), take() のテンプレートがループの中で不変な場合、 snapshot() を作ると効率が良くなる。
| Linda | Rinda | 説明 | 
| タプル() | 配列[] | タプル空間に置くことができるデータ構造 | 
| out | write | タプルをタプル空間内に生成する。 | 
| in | take | タプルを取り去る | 
| rd | read | in/takeと似ているが、タプルがタプル空間に残る。 | 
Rinda が提供する空間の特徴
=== で等しい。
% ruby -e 'p 1 === 1' ![[←]](../icons/screen-return.gif) true
% ruby -e 'p Integer === 1'
true
% ruby -e 'p Integer === 1' ![[←]](../icons/screen-return.gif) true
% ruby -e 'p String === 1'
true
% ruby -e 'p String === 1' ![[←]](../icons/screen-return.gif) false
% ruby -e 'p /[abc]xy/ === "axy"'
false
% ruby -e 'p /[abc]xy/ === "axy"' ![[←]](../icons/screen-return.gif) true
% ruby -e 'p /[abc]xy/ === "Axy"'
true
% ruby -e 'p /[abc]xy/ === "Axy"' ![[←]](../icons/screen-return.gif) false
%
false
%
["Message Box", "Hello" ]
   1: #!/usr/bin/env ruby
   2: # make-space.rb -- Make a tuple space and print its URI
   3: 
   4: require 'rinda/tuplespace'
   5: 
   6: def usage()
   7:         $stderr.printf("Usage: %% %s portno\n", $0)
   8:         exit( 1 )
   9: end
  10: 
  11: def main(argv)
  12:         if( argv.length != 1 )
  13:             usage()
  14:         end
  15:         portno = argv[0]
  16:         space = Rinda::TupleSpace.new()
  17:         DRb.start_service("druby://:"+portno, space)
  18:         uri = DRb.uri()
  19:         $stdout.printf("%s\n",uri)
  20:         $stdout.printf("Type ^C to stop this program.\n")
  21:         DRb.thread.join()
  22: end
  23: 
  24: main(ARGV)
   1: #!/usr/bin/env ruby
   2: # mbox-writer.rb -- Write a message to the message box in a tuple space.
   3: 
   4: require 'rinda/tuplespace'
   5: 
   6: def usage()
   7:         $stderr.printf("Usage: %% %s uri message\n", $0)
   8:         exit( 1 )
   9: end
  10: 
  11: def main(argv)
  12:         if( argv.length != 2 )
  13:             usage()
  14:         end
  15:         uri = argv[0]
  16:         message = argv[1]
  17: 
  18:         space = DRbObject.new_with_uri( uri )
  19: 
  20:         tuple = ["Message Box", message ]
  21:         space.write( tuple )
  22:         printf("mbox-writer: wrote[%s]\n",message)
  23: end
  24: 
  25: main(ARGV)
   1: #!/usr/bin/env ruby
   2: # mbox-reader.rb -- Read a message from a message box in a tuple space.
   3: 
   4: require 'rinda/tuplespace'
   5: 
   6: def usage()
   7:         $stderr.printf("Usage: %% %s uri\n", $0)
   8:         exit( 1 )
   9: end
  10: 
  11: def main(argv)
  12:         if( argv.length != 1 )
  13:             usage()
  14:         end
  15:         uri = argv[0]
  16:         DRb.start_service()
  17:         space = DRbObject.new_with_uri( uri )
  18: 
  19:         template = ["Message Box",nil]
  20:         tuple = space.read( template )
  21:         message = tuple[1]
  22:   p tuple # for debug
  23:         printf("mbox-reader: read [%s]\n", message )
  24: end
  25: 
  26: main(ARGV)
space.read()で、タプル空間からタプルを取り出す。
最初の引数は、テンプレートである。
read() は、空間からテンプレートとマッチするエントリを読み出す。
read() は、マッチするエントリがなければ、タイムアウトするまで待つ。 第2引数に秒単位で待ち時間を指定できる。
注意:連続する read() が同じオブジェクトを返す保証はない。
   1: #!/usr/bin/env ruby
   2: # mbox-taker.rb -- Take a message from a message box in a tuple space.
   3: 
   4: require 'rinda/tuplespace'
   5: 
   6: def usage()
   7:         $stderr.printf("Usage: %% %s uri\n", $0)
   8:         exit( 1 )
   9: end
  10: 
  11: def main(argv)
  12:         if( argv.length != 1 )
  13:             usage()
  14:         end
  15:         uri = argv[0]
  16:         DRb.start_service()
  17:         space = DRbObject.new_with_uri( uri )
  18: 
  19:         template = ["Message Box",nil]
  20:         tuple = space.take( template )
  21:         message = tuple[1]
  22:   p tuple # for debug
  23:         printf("mbox-taker: took [%s]\n", message )
  24: end
  25: 
  26: main(ARGV)
take() は、read() と同じだが、エントリを空間から取り去る所が異なる。複
数の take() が重なったとしても、エントリは1つにしか取られない。
% diff mbox-reader.rb mbox-taker.rb ![[←]](../icons/screen-return.gif) 2c2
< # mbox-reader.rb -- Read a message from a message box in a tuple space.
---
> # mbox-taker.rb -- Take a message from a message box in a tuple space.
20c20
<       tuple = space.read( template )
---
>       tuple = space.take( template )
23c23
<       printf("mbox-reader: read [%s]\n", message )
---
>       printf("mbox-taker: took [%s]\n", message )
%
2c2
< # mbox-reader.rb -- Read a message from a message box in a tuple space.
---
> # mbox-taker.rb -- Take a message from a message box in a tuple space.
20c20
<       tuple = space.read( template )
---
>       tuple = space.take( template )
23c23
<       printf("mbox-reader: read [%s]\n", message )
---
>       printf("mbox-taker: took [%s]\n", message )
% ![[]](../icons/screen-cursor.gif) 
% wget http://www.cs.tsukuba.ac.jp/~yas/cs/csys-2018/2018-07-27/ex/make-space.rb ![[←]](../icons/screen-return.gif) % wget http://www.cs.tsukuba.ac.jp/~yas/cs/csys-2018/2018-07-27/ex/mbox-writer.rb
% wget http://www.cs.tsukuba.ac.jp/~yas/cs/csys-2018/2018-07-27/ex/mbox-writer.rb ![[←]](../icons/screen-return.gif) % wget http://www.cs.tsukuba.ac.jp/~yas/cs/csys-2018/2018-07-27/ex/mbox-reader.rb
% wget http://www.cs.tsukuba.ac.jp/~yas/cs/csys-2018/2018-07-27/ex/mbox-reader.rb ![[←]](../icons/screen-return.gif) % wget http://www.cs.tsukuba.ac.jp/~yas/cs/csys-2018/2018-07-27/ex/mbox-taker.rb
% wget http://www.cs.tsukuba.ac.jp/~yas/cs/csys-2018/2018-07-27/ex/mbox-taker.rb ![[←]](../icons/screen-return.gif) % chmod +x *.rb
% chmod +x *.rb ![[←]](../icons/screen-return.gif) % ls -l
% ls -l ![[←]](../icons/screen-return.gif) total 32
-rwxr-xr-x   1 yas  yas  463  2  7 23:42 make-space.rb
-rwxr-xr-x   1 yas  yas  485  2  7 23:48 mbox-reader.rb
-rwxr-xr-x   1 yas  yas  484  2  8 00:06 mbox-taker.rb
-rwxr-xr-x   1 yas  yas  406  2  7 23:40 mbox-writer.rb
% which ruby
total 32
-rwxr-xr-x   1 yas  yas  463  2  7 23:42 make-space.rb
-rwxr-xr-x   1 yas  yas  485  2  7 23:48 mbox-reader.rb
-rwxr-xr-x   1 yas  yas  484  2  8 00:06 mbox-taker.rb
-rwxr-xr-x   1 yas  yas  406  2  7 23:40 mbox-writer.rb
% which ruby ![[←]](../icons/screen-return.gif) /usr/local/bin/ruby
% ruby -v
/usr/local/bin/ruby
% ruby -v ![[←]](../icons/screen-return.gif) ruby 2.1.7p400 (2015-08-18 revision 51632) [x86_64-darwin13]
%
ruby 2.1.7p400 (2015-08-18 revision 51632) [x86_64-darwin13]
% ![[]](../icons/screen-cursor.gif) 
実行には、Ruby の 1.8 以降が必要。drb.rb を含んだもの。
$ ./make-space.rb 1231 ![[←]](../icons/screen-return.gif) druby://ホスト名:1231
Type ^C to stop this program.
(最後に ^C で止める)
druby://ホスト名:1231
Type ^C to stop this program.
(最後に ^C で止める)
引数のポート番号(1231)は、ぶつからないような番号にする。自動的に終了し
ないので、実験が終わったら ^C (Control-C) で殺す。
Writer を動作させる。
$ ./mbox-writer.rb druby://localhost:1231 hello ![[←]](../icons/screen-return.gif) mbox-writer: wrote[hello]
$ ./mbox-writer.rb druby://localhost:1231 hi
mbox-writer: wrote[hello]
$ ./mbox-writer.rb druby://localhost:1231 hi    ![[←]](../icons/screen-return.gif) mbox-writer: wrote[hi]
$
mbox-writer: wrote[hi]
$ ![[]](../icons/screen-cursor.gif) 
最後のウインドウで Reader や Taker を動作させる。
$ ./mbox-reader.rb druby://localhost:1231 ![[←]](../icons/screen-return.gif) ["Message Box", "hello"]
mbox-reader: read [hello]
$ ./mbox-reader.rb druby://localhost:1231
["Message Box", "hello"]
mbox-reader: read [hello]
$ ./mbox-reader.rb druby://localhost:1231 ![[←]](../icons/screen-return.gif) ["Message Box", "hello"]
mbox-reader: read [hello]
$ ./mbox-reader.rb druby://localhost:1231
["Message Box", "hello"]
mbox-reader: read [hello]
$ ./mbox-reader.rb druby://localhost:1231 ![[←]](../icons/screen-return.gif) ["Message Box", "hello"]
mbox-reader: read [hello]
$ ./mbox-taker.rb druby://localhost:1231
["Message Box", "hello"]
mbox-reader: read [hello]
$ ./mbox-taker.rb druby://localhost:1231 ![[←]](../icons/screen-return.gif) ["Message Box", "hello"]
mbox-taker: took [hello]
$ ./mbox-taker.rb druby://localhost:1231
["Message Box", "hello"]
mbox-taker: took [hello]
$ ./mbox-taker.rb druby://localhost:1231 ![[←]](../icons/screen-return.gif) ["Message Box", "hi"]
mbox-taker: took [hi]
$ ./mbox-taker.rb druby://localhost:1231
["Message Box", "hi"]
mbox-taker: took [hi]
$ ./mbox-taker.rb druby://localhost:1231 ![[←]](../icons/screen-return.gif) ...
...
3回目の take は止まる。別ウインドウで write すれば、先に進む。
タプルがない状態先に take/read すると、止まる。この状態で、write すれ ば、read/take は先に進む。
if( check() == OK ) /* time of check */
{
    /* other threads can change the condition after checking. */
    do_action() /* time of use */
}
if( access("file", W_OK) == 0 ) /* time of check */
{
    /* other processes can create the file after checking. */
    fd = open("file", O_WRONLY); /* time of use */
}
static struct s1 *p;
thread() {
    if( p == NULL ) /* time of check */
    {
	/* other processes can allocate memory after checking. */
	p = malloc( sizeof(struct s1) );  /* time of use */
    }
}
url = "http://www.tsukuba.ac.jp";
if( rdp("counter-31", url, ?val) == 0 ) /* time of check */
{
    /* other processes can create the tuple after checking. */
    out("counter-31", url, 0); /* time of use */
}
ポレートには、次のものを含めなさい。 A report must include following.
締切りは、2018年7月31日、 23:59:59 とする。
次の2つのプログラムを作成しなさい。 Write following two programs.
    String url = argv[0];
    tuple = ["counter", url,0]
    space.write( tuple );
$ ./init-counter http://www.example.com/ ![[←]](../icons/screen-return.gif) $ ./init-counter http://www.example.com/hello.html
$ ./init-counter http://www.example.com/hello.html ![[←]](../icons/screen-return.gif) $
$ ![[]](../icons/screen-cursor.gif) 
$ ./counter http://www.example.com/ ![[←]](../icons/screen-return.gif) 1
$ ./counter http://www.example.com/
1
$ ./counter http://www.example.com/ ![[←]](../icons/screen-return.gif) 2
$ ./counter http://www.example.com/
2
$ ./counter http://www.example.com/ ![[←]](../icons/screen-return.gif) 3
$ ./counter http://www.example.com/hello.html
3
$ ./counter http://www.example.com/hello.html ![[←]](../icons/screen-return.gif) 1
$
1
$ ![[]](../icons/screen-cursor.gif) 
Realize a client-sever model by using a tuple space. You must support multiple clients, and match a request message with a reply message.