Archive for the ‘halt’ Tag
newLISP で On Lisp する...第21章(その2)
(この blog は、“short short story または 晴耕雨読な日々”からの引越してきたもの。スクリプトは、newLISP V10.2.1 以降で動作するように書き直しています。
defun 等の newLISP組込関数に無い関数は、特に断らない限り、newlisp-utility.lsp と onnewlisp.lsp に定義してあります。)
今回は、第21章 マルチプロセス の動作例ですが、その前に前回紹介したコードの修正です。
(define *default-proc*
(make-proc (state (fn (x)
(print "\n>>")
(print (eval-string (read-line) 'MAIN))
(pick-process)))))
eval-string文 に、context シンボルのオプションを追加しています。何故かは、後ほど。
手始めに、マクロforkx から 。前回紹介したコードを動かすには、2010/ 8/24版以降の onnewlisp.lsp と newlisp-utility.lsp が必要です(i+ は newlisp-utility.lsp に、=defun は onnewlisp.lsp に定義してあります)。
> [cmd] (=defun foo (x) (print "Foo was called with " x ".\n") (=values (i+ x))) [/cmd] (lambda (*cont* _x) (letex ((x _x)) (begin (print "Foo was called with " x ".\n") (=values (i+ x))))) > (forkx (foo 2) 25) (foo 2) > *procs* (gensym12) > (proc-state gensym12) (lambda (gensym11) (foo 2) (pick-process)) > (proc-pri gensym12) 25 >
*procs* に登録されたプロセスが gensym変数なのは、context版構造体の仕様です。 stateフィールドのラムダ式の最後に (pick-process) がありますから、継続してプロセスが実行されます。デフォルトのプロセス *default-proc* でも、最後に、pick-process を呼び出していますしね。
次にマクロprogram を
> [cmd] (program two-foos (a b) (forkx (foo a) 25) (forkx (foo b) 25)) [/cmd] (lambda (*cont* _a _b) (letex ((a _a) (b _b)) (begin (setq *procs* nil) (begin (forkx (foo a) 25) (forkx (foo b) 25)) (catch (while true (pick-process)) '*halt*) *halt*))) > (two-foos 1 2) Foo was called with 2. Foo was called with 1. >>(halt) nil >
優先順位が同じですから、後から追加されたプロセスから実行されます。
しかし、優先順位を変えて実行すると、
> [cmd] (program two-foos (a b) (forkx (foo a) 25) (forkx (foo b) 12)) [/cmd] (lambda (*cont* _a _b) (letex ((a _a) (b _b)) (begin (setq *procs* nil) (begin (forkx (foo a) 25) (forkx (foo b) 12)) (catch (while true (pick-process)) '*halt*) *halt*))) > (two-foos 1 2) Foo was called with 1. Foo was called with 2. >>(halt) nil >
優先度の高いプロセスから実行されます。
ここで、先頭に >> がある行は、pick-process 中でデフォルトのプロセス *default-proc* が動作している状態です。そして、それは、multiple-value-bind 中で動作しています。ところで、multiple-value-bind は、multiple-value-bind の context で定義しています。つまり、デフォルトのプロセス *default-proc* の eval-string 文が multiple-value-bind の context で引数を評価してしまうのです。そのため、冒頭のスクリプト、eval-string が引数の文字列を評価する時に使う context を指定することにした訳です。普通の関数処理なら、問題ないのですが、、、
さて、言い訳はこれくらいにして、次は、マクロwait の動作、 wait 式を1つ持つプロセス です。
> (define *open-doors* '()) () > [cmd] (=defun pedestrian () (wait d (car *open-doors*) (print "\nEntering " (term d) ".\n"))) [/cmd] (lambda (*cont*) (letex () (begin (wait d (car *open-doors*) (print "\nEntering " (term d) ".\n"))))) > (program ped () (forkx (pedestrian) 1)) (lambda (*cont*) (letex () (begin (setq *procs* nil) (begin (forkx (pedestrian) 1)) (catch (while true (pick-process)) '*halt*) *halt*))) > (ped) >>(push 'door *open-doors*) (MAIN:door) Entering MAIN:door. >>(halt) nil >
*open-doors* に要素が入るまで、本体のコードの実行が抑制されます。(MAIN:door) は、push の戻り値ですが、MAIN の context が付いてきているのは、前述の理由です。pedestrian で、format の代わりに使っている newLISP組込print で term を使い、出力から context 部分を取り除いています。
予定外に長くなってきたので(汗)、黒板を使った同期 からは、次回に。
以上、如何でしょうか?
newLISP で On Lisp する...第21章(その1)
(この blog は、“short short story または 晴耕雨読な日々”からの引越してきたもの。スクリプトは、newLISP V10.2.1 以降で動作するように書き直しています。
defun 等の newLISP組込関数に無い関数は、特に断らない限り、newlisp-utility.lsp と onnewlisp.lsp に定義してあります。)
第21章 マルチプロセス に入ります。
実は、newLISP は、マルチプロセス対応なのですが、Linux/Unix バージョンのみの機能です。残念ながら、私が使っている windows バージョンでは、使えません(泣)。それだけに、実装し甲斐がある?(笑)
最初の プロセスの抽象化 にある、マルチプロセス対応の基本要素は、そのまま適用されます。それらは、実装で見てもらった方が早いでしょう。
実装 では、まず、プロセスの構造体とその生成 を定義します。とはいっても、newLISP には、構造体がありません。以前定義した構造体マクロを使います。第20章で定義した継続渡しマクロと共に onnewlisp.lsp に追加しました。
(defstruct proc pri state wait)
(define *procs* '())
(define *proc* '())
(define *halt* (gensym))
(define *default-proc*
(make-proc (state (fn (x)
(print "\n>>")
(print (eval-string (read-line) 'MAIN))
(pick-process)))))
(define-macro (forkx)
; (forkx expr pri)
(letex (_expr (args 0)
_pri (args 1)
_gvar (gensym))
(push (make-proc
(state (fn (_gvar)
_expr
(pick-process)))
(pri _pri))
*procs*)
'_expr))
(define-macro (program)
; (program name args &body body)
(letex (_name (args 0)
_args (args 1)
_body (cons 'begin (2 (args))))
(=defun _name _args
(setq *procs* nil)
_body
(catch (while true (pick-process)) '*halt*) *halt*)))
早速、継続渡しマクロが使われます。“On Lisp” 本書と、ほぼ同様な実装です。letex が多用されているとか、nil が 空リストになるとか、defvar は define になるとかありますけど(笑)。
構造体の表記は、、“newLISP で構造体を使う” を参照して下さい。コロン(:)を使わず、括弧で括るだけですけど(笑)。
また、マクロ名fork は forkx と改名してあります。 windowsバージョンでは使えませんが、newLISP組込だからです。
そして、プロセスのスケジューリング の実装は、こうなります。(defun、remove、multiple-value-bind、values、 reference-inversion:setf は newlisp-utility.lsp に定義してあります)
(define setfr reference-inversion:setf)
(defun pick-process ()
;(println "pick process")
(multiple-value-bind (p val) (most-urgent-process)
(setq *proc* p
*procs* (remove p *procs*))
((proc-state p) val)))
(defun most-urgent-process ()
(let ((proc1 *default-proc*) (max1 -1) (val1 t))
(dolist (p *procs*)
(let ((pri (proc-pri p)))
(if (> pri max1)
(let ((val (or (not (proc-wait p))
((proc-wait p)))))
(when val
(setq proc1 p
max1 pri
val1 val))))))
(values proc1 val1)))
(defun arbitrator (test cont)
(setfr (proc-state *proc*) cont
(proc-wait *proc*) test)
(push *proc* *procs*)
(pick-process))
(define-macro (wait)
; (wait parm test &body body)
(letex (_parm (args 0)
_test (args 1)
_body (cons 'begin (2 (args))))
(arbitrator (fn () _test)
(fn (_parm) _body))))
(define-macro (yield)
; (yield &body body)
(letex (_body (cons 'begin (args))
_gsym (gensym))
(arbitrator nil (fn (_gsym) _body))))
(defun setpri (n) (setfr (proc-pri *proc*) n))
(defun halt (val) (throw val)) ; 2008/02/19 corrected
(defun kill (obj)
(if obj
(setq *procs* (remove obj *procs*)))
(pick-process))
setf の代わりに、reference-inversion:setf を setfr として使っています。構造体定義に context バージョンを使ったのは、これが使えるから。おかげで、こちらも、本書とほぼ同様な定義で済みました。
さて、気になる動作は、どうなるでしょうか?
実装だけで長くなってしまったので、動作例は次回に(笑)。
以上、如何でしょうか?