├── README.md
├── doc
├── A Guide to the Perplexed- CARRIER0 AND GELERNTER.pdf
├── Coordination Languages-George Wells.pdf
├── HOW TO WRITE PARALLEL PROGRAMS-Carriero Gelernter.pdf
├── The Complexity of Theorem-Proving Procedures - Stephen A. Cook.pdf
└── intro.md
├── project.clj
├── resources
└── clotilde.jpg
├── src
└── clotilde
│ ├── core.clj
│ ├── innards.clj
│ └── tools.clj
└── test
└── clotilde
└── core_test.clj
/README.md:
--------------------------------------------------------------------------------
1 | # clotilde
2 |
3 |
5 |
8 |
9 | Clotilde is a port of the Linda process coordination language, in an attempt at idiomatic Clojure.
10 | >"In computer science, Linda is a model of coordination and communication among several
11 | >parallel processes operating upon objects stored in and retrieved from shared, virtual,
12 | >associative memory. Linda was developed by David Gelernter and Nicholas Carriero at
13 | >Yale University and is named for Linda Lovelace, an actress in the porn movie Deep Throat,
14 | >a pun on Ada's tribute to Ada Lovelace."
15 | > - Wikipedia -
16 |
17 | Linda is a registered trademark of SCIENTIFIC Computing Associates, Inc.
18 |
19 | Clotilde ( ~475-545* ) is the registered spouse of Clovis, and therefore is known to be the first ever queen of France.
20 | (*) Yes, 70 years old can be regarded as tough considering the era, and also her job, and especially the hubby.
21 |
24 |
25 | Back to Linda... In order to get anything close to useful done with the language's four instructions
26 | (indeed 4: out, eval, rd, in), one needs some acquaintance
27 | with a small bunch of sparsely taught concepts such as tuple spaces or associative virtual memory,
28 | live data structures, and more generally "the game of parallelism". Fortunately, it's a matter of a couple of hours reading:
29 | HOW TO WRITE PARALLEL PROGRAMS: A FIRST COURSE
30 | , By Nicholas Carriero and David Gelernter
31 | _©1990 Massachusetts Institute of Technology
32 | , ISBN 0-262-03171-X_.
33 | Well worth a read for anyone willing to program against more than a handful of cores, in any language.
34 | Especially when the book is short and unpedantic, and when it's
35 | [freely available for browsing online](http://www.lindaspaces.com/book/),
36 | and when it can also be [downloaded in pdf format](http://www.lindaspaces.com/book/book.pdf).
37 | Great times we live in; if only Clotilde had html and pdf, she'd probably have gone centenarian.
38 | For deeper litterature on the topic (Linda, that is), see [Science Direct](http://www.sciencedirect.com/science/article/pii/S0890540199928237)
39 | (no, I havn't read all the references, and I won't, but some of these papers are really worth the time).
40 |
41 | ## Credits
42 |
43 | David Gelernter, Joe Armstrong, Rich Hickey: thank you for all those nights of mine you ruined.
44 |
45 | Drew Colthorp: mucho thankies for all the nights of mine you saved; illumination! (matchure/fn-match).
46 |
47 | ## Usage
48 |
49 | Add a Leiningen depency to your project.clj:
50 | ```clojure
51 | (defproject your-project "0.0.1"
52 | :dependencies [[org.clojure/clojure "1.5.1"]
53 | [clotilde "0.2.1-SNAPSHOT"]])
54 | ````
55 |
56 | Add a :use clause to your namespace:
57 |
58 | ```clojure
59 | (ns your-project.core
60 | (:use clotilde.core ;; API
61 | clotilde.tools ;; utilities
62 | ))
63 | ````
64 |
65 | For usage and instructions, see the program's documentation.
66 |
67 | ```clojure
68 | ;; TODO: write documentation.
69 | ````
70 |
71 | Kidding, the code is documented, and comes with tests; idiomatic Clojure ain't it?
72 |
73 | Have fun.
74 |
75 | ## License
76 |
77 | Copyright (c) 2013 François De Serres, _aka._ Justiniac
78 |
79 | Distributed under the Eclipse Public License, the same as Clojure.
80 |
--------------------------------------------------------------------------------
/doc/A Guide to the Perplexed- CARRIER0 AND GELERNTER.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fdserr/clotilde/4e97254720ed3fb565a3ea1a5a9738913544b9da/doc/A Guide to the Perplexed- CARRIER0 AND GELERNTER.pdf
--------------------------------------------------------------------------------
/doc/Coordination Languages-George Wells.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fdserr/clotilde/4e97254720ed3fb565a3ea1a5a9738913544b9da/doc/Coordination Languages-George Wells.pdf
--------------------------------------------------------------------------------
/doc/HOW TO WRITE PARALLEL PROGRAMS-Carriero Gelernter.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fdserr/clotilde/4e97254720ed3fb565a3ea1a5a9738913544b9da/doc/HOW TO WRITE PARALLEL PROGRAMS-Carriero Gelernter.pdf
--------------------------------------------------------------------------------
/doc/The Complexity of Theorem-Proving Procedures - Stephen A. Cook.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fdserr/clotilde/4e97254720ed3fb565a3ea1a5a9738913544b9da/doc/The Complexity of Theorem-Proving Procedures - Stephen A. Cook.pdf
--------------------------------------------------------------------------------
/doc/intro.md:
--------------------------------------------------------------------------------
1 | # Introduction to clotilde
2 |
3 | ## initialize! [] / [vector-of-vectors]
4 |
5 | -> nil
6 |
7 | side effect: blank space / space as vector-of-vectors, empty queues.
8 |
9 | ## out! [& exprs]
10 |
11 | -> vector
12 |
13 | side effect: vector added to space.
14 |
15 | ## eval! [& exprs]
16 |
17 | -> (future vector)
18 |
19 | side effect: vector added to space by another thread.
20 |
21 | ## rd! [patterns & exprs]
22 | -> exprs
23 |
24 | with bindings set using patterns, side effect: may queue up and block until matching.
25 |
26 | ## in! [patterns & exprs]
27 | -> exprs
28 |
29 | with bindings set using patterns, side effect: may queue up and block until matching,
30 | remove a matching vector form space.
31 |
32 |
33 |
--------------------------------------------------------------------------------
/project.clj:
--------------------------------------------------------------------------------
1 | (defproject clotilde "0.2.2-SNAPSHOT"
2 | :description "The Linda process coordination language written in Clojure."
3 | :url "https://github.com/justiniac/clotilde"
4 | :license {:name "Eclipse Public License"
5 | :url "http://www.eclipse.org/legal/epl-v10.html"}
6 | :dependencies [[org.clojure/clojure "1.5.1"]
7 | [matchure "0.10.1"]
8 | [clj-tuple "0.1.1"]])
9 |
--------------------------------------------------------------------------------
/resources/clotilde.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fdserr/clotilde/4e97254720ed3fb565a3ea1a5a9738913544b9da/resources/clotilde.jpg
--------------------------------------------------------------------------------
/src/clotilde/core.clj:
--------------------------------------------------------------------------------
1 | (ns clotilde.core
2 | (:use clotilde.innards
3 | clotilde.tools
4 | clj-tuple))
5 |
6 | ;; TODO =========================
7 | ;;
8 | ;; Bugs:
9 | ;; - ?
10 | ;;
11 | ;; Tests:
12 | ;; - Primes finder
13 | ;; - Heavy loading
14 | ;; - Exceptions in all ops (repl-test ok)
15 | ;; - Transactions in all ops (repl-test ok)
16 | ;; - Side effects in all ops (repl-test ok)
17 | ;;
18 | ;; Doc:
19 | ;; - ?
20 | ;;
21 | ;; Features:
22 | ;; - print rd/in queues
23 | ;; - tag with thread id
24 | ;; - print timeline
25 | ;; - abort queues (keep space state)
26 | ;; - pause/resume
27 | ;; - step debugger
28 | ;;
29 | ;; Refactor:
30 | ;; - investigate core.async channels instead of ad-hoc queues
31 | ;;
32 |
33 | ;; API =========================
34 |
35 | (defn initialize!
36 | "Evaluates to nil.
37 | vectors: a vector of vectors.
38 | Side-effect(s): no arg -> empty space; else space starts with vectors"
39 | ([] (init-local))
40 | ([vectors] (init-local vectors)))
41 |
42 | (defmacro out!
43 | "exprs: one or more expressions; they'll be evaluated within the calling thread
44 | (side effects ok, transactions ok).
45 | Evaluates to a tuple form [expr1-result expr2-result .. exprN-result].
46 | Side-effect(s): some waiting in! or rd! succeed, or the tuple is put in space.
47 | => (out! :t (+ 1 0) \"One\")
48 | [:t 1 \"One\"] ;; [:t 1 \"One\"] added to space."
49 | [& exprs]
50 | `(out-eval #_(vector ~@exprs) (tuple ~@exprs)))
51 |
52 | (defmacro eval!
53 | "Just like out!, but exprs are evaluated within a (single) new thread of execution.
54 | Evaluates to a future; when dereferenced, blocks until fully evaluated, then
55 | yields a tuple form [expr1-result expr2-result .. exprN-result];
56 | result is cached (not re-evaluated) on subsequent derefs.
57 | => (let [f (eval! :t (+ 1 0) (out! :i 1))] ;; [:t 1 [:i 1]] and [:i 1] adding to space...
58 | @f)
59 | [:t 1 [:i 1]] ;; return when f is realized (block until then)
60 | => (eval! :job (Thread/sleep 5000) (+ 1 0)) ;; return fast
61 | # ;; [:job nil 1] added to space in ~5 seconds"
62 | [& exprs]
63 | `(future (out-eval #_(vector ~@exprs) (tuple ~@exprs))))
64 |
65 | (defmacro rd!
66 | "patterns: a vector of pattern elements to match against tuples in space.
67 | Valid patterns are literals (eg.: 0, \"bug\", :x, ...), bindings (eg.: x, y, whatnot, ...),
68 | wildcards (_ and ?), regexps (eg.: #\"hello\"), and/or variables to be bound within the
69 | lexical context of rd! (eg.: ?var, [?fst & ?rst], ...).
70 | See matchure on GitHub for more pattern-matching sweetness.
71 | Mucho thankies for writing matchure, Drew!
72 | body: one or more expressions to evaluate within the lexical context of rd!.
73 | Evaluates to body, in an implicit do (side effects ok, transactions ok).
74 | Side-effect(s): rd! will block until a matching tuple is found (no order assumed in space);
75 | variable patterns (eg. ?var) are bound to their respective matching value from the tuple,
76 | within the context of rd! (the pars around it, as in let)."
77 | [patterns & body]
78 | (assert (vector? patterns) "Invalid patterns argument given to rd! (must be a vector).")
79 | `(rd (ptn-matcher ~patterns ~@body)))
80 |
81 | (defmacro in!
82 | "Just like rd!, but the matching tuple is removed from space."
83 | [patterns & body]
84 | (assert (vector? patterns) "Invalid patterns argument given to rd! (must be a vector).")
85 | `(in (ptn-matcher ~patterns ~@body)))
86 |
87 |
88 |
--------------------------------------------------------------------------------
/src/clotilde/innards.clj:
--------------------------------------------------------------------------------
1 | (ns clotilde.innards
2 | (:use [matchure :only (fn-match)]
3 | #_clj-tuple))
4 |
5 | ;; Nothing private => easy test and easy plugs
6 |
7 | (defonce
8 | ^{:doc "Tuple space."}
9 | -space (ref (vector)))
10 |
11 | (defonce
12 | ^{:doc "in! ops wait queue."}
13 | -waitq-ins (ref (vector)))
14 |
15 | (defonce
16 | ^{:doc "rd! ops wait queue."}
17 | -waitq-rds (ref (vector)))
18 |
19 | (defn init-local
20 | "Start a fresh space, blank or equal to vectors."
21 | ([]
22 | (dosync
23 | (ref-set -waitq-ins (vector))
24 | (ref-set -waitq-rds (vector))
25 | (ref-set -space (vector))
26 | nil))
27 | ([vectors]
28 | (assert (vector? vectors)
29 | "illegal argument given to initialize!; vectors must be a vector.")
30 | (assert (every? vector? vectors)
31 | "illegal argument given to initialize!; vectors can only contain vectors.")
32 | (assert (not (some empty? vectors))
33 | "illegal argument given to initialize!; vectors cannot contain empty vectors.")
34 | (dosync
35 | (ref-set -waitq-ins (vector))
36 | (ref-set -waitq-rds (vector))
37 | (ref-set -space (vec vectors))
38 | nil)))
39 |
40 | (defn extract-all-with
41 | "=> (extract-all-with [1 2 3 4 5 6] #(when (odd? %) (* 2 %)))
42 | [[2 6 10] [2 4 6]]"
43 | ([coll fun]
44 | (extract-all-with coll fun (transient []) (transient [])))
45 | ([coll fun applied not-applied]
46 | (if-not (seq coll)
47 | [(persistent! applied) (persistent! not-applied)]
48 | (if-let
49 | [x (fun (first coll))]
50 | (recur (rest coll) fun (conj! applied x) not-applied)
51 | (recur (rest coll) fun applied (conj! not-applied (first coll)))))))
52 |
53 | (defn extract-one-with
54 | "=> (extract-one-with [12 2 3 4 5 6] #(when (odd? %) (* 2 %)))
55 | [[6] [12 2 4 5 6]]"
56 | ([coll fun]
57 | (extract-one-with coll fun [] []))
58 | ([coll fun applied not-applied]
59 | (if-not (seq coll)
60 | [applied not-applied]
61 | (if-let
62 | [x (fun (first coll))]
63 | (recur (empty coll) fun (conj applied x) (vec (concat not-applied (rest coll))))
64 | (recur (rest coll) fun applied (conj not-applied (first coll)))))))
65 |
66 | (defn resolve-match-promise?
67 | "Attempt to resolve a match promise.
68 | Eval to matcher result or nil."
69 | [tuple [promise matcher]]
70 | (when-let [e (matcher tuple)]
71 | (deliver promise e)))
72 |
73 | (defn match-in-queue?
74 | "Pass tuple to all waiting rd!s and to some in!s.
75 | Return truthy as soon as an in! matches, else eval to nil."
76 | [tuple]
77 | (let [rslv (partial resolve-match-promise? tuple)
78 | [rd-ok rd-nok] (extract-all-with @-waitq-rds rslv)
79 | [in-ok in-nok] (extract-one-with @-waitq-ins rslv)]
80 | (when-not (empty? rd-ok)
81 | (ref-set -waitq-rds rd-nok))
82 | (when-not (empty? in-ok)
83 | (ref-set -waitq-ins in-nok))
84 | (not-empty in-ok)))
85 |
86 | (defn out-eval
87 | "out! and eval! ops implementation.
88 | Match in Qs or add to space."
89 | [tuple]
90 | (io! (dosync
91 | (when-not (match-in-queue? tuple)
92 | (alter -space conj (vary-meta tuple assoc :created (java.lang.System/nanoTime))))
93 | tuple)))
94 |
95 | (defn rd
96 | "rd! op implementation.
97 | Match in space or Q up."
98 | [matcher]
99 | (io!
100 | (deref
101 | (dosync
102 | (ensure -waitq-ins)
103 | (let [e (->> @-space (map matcher) (drop-while nil?) first)
104 | p (if e (delay e) (promise))]
105 | (when-not e
106 | (alter -waitq-rds conj [p matcher]))
107 | p)))))
108 |
109 | (defn in
110 | "in! op implementation.
111 | Remove match from space or Q up."
112 | [matcher]
113 | (io!
114 | (deref
115 | (dosync
116 | (ensure -waitq-rds)
117 | (let [[ok nok] (extract-one-with @-space matcher)
118 | e (first ok)
119 | p (if e (delay e) (promise))]
120 | (if-not (empty? ok)
121 | (ref-set -space nok)
122 | (alter -waitq-ins conj [p matcher]))
123 | p)))))
124 |
125 | (defmacro ptn-matcher
126 | [patterns & body]
127 | `(vary-meta (fn-match
128 | ([~patterns] (do ~@body))
129 | ([~'_] nil))
130 | assoc :created (java.lang.System/nanoTime)
131 | :patterns '~patterns
132 | :body '(~@body)))
133 |
134 |
--------------------------------------------------------------------------------
/src/clotilde/tools.clj:
--------------------------------------------------------------------------------
1 | (ns clotilde.tools
2 | (:use clojure.test
3 | clotilde.innards
4 | [matchure :only (fn-match)]))
5 |
6 | ;; Toolset ===========================
7 |
8 | (defn empty-space?
9 | "True if space contains no tuple. Waiting in! or rd! may exist."
10 | []
11 | (= 0 (count @-space)))
12 |
13 | (defn void-space?
14 | "True if space contains no tuple and no waiting in! or rd!."
15 | []
16 | (= 0 (dosync (+ (count @-space)
17 | (count @-waitq-ins)
18 | (count @-waitq-rds)))))
19 |
20 | (defn print-space
21 | "Guess what."
22 | []
23 | (let [s (dosync (str "================================" "\n"
24 | (apply str (interpose "\n" (map str (sort @-space))))
25 | "\n" "================================" "\n"
26 | "Tuples in space: " (count @-space)
27 | "; waiting rd!: " (count @-waitq-rds)
28 | "; waiting in!: " (count @-waitq-ins)))]
29 | (println s)))
30 |
31 |
32 |
--------------------------------------------------------------------------------
/test/clotilde/core_test.clj:
--------------------------------------------------------------------------------
1 | (ns clotilde.core-test
2 | (:use clojure.test
3 | clotilde.core
4 | clotilde.innards
5 | clotilde.tools
6 | matchure
7 | #_clj-tuple))
8 |
9 | (defmacro safe-ops
10 | [time-out & exprs]
11 | `(deref (future ~@exprs) ~time-out nil))
12 |
13 | (def test-matcher (fn-match ([[:x ?v]] v) ([_] nil)))
14 | (def test-matcher-nox (fn-match ([[:l ?v]] v) ([_] nil)))
15 | (def test-promise [(promise) test-matcher])
16 | (def test-promise-nox [(promise) test-matcher-nox])
17 | (def test-space [[:z 0] [:y 0] [:x 1] [:x 1]])
18 | (def test-space-nox [[:z 0] [:y 0] [:z 0] [:y 0]])
19 | (def test-space-ref (ref test-space))
20 | (def test-space-ref-nox (ref test-space-nox))
21 |
22 | (deftest test-test-tools
23 | (testing "safe-ops"
24 | (is (= nil (safe-ops 10 (Thread/sleep 100) nil :ok)))
25 | (is (= :ok (safe-ops 100 (Thread/sleep 10) nil :ok)))))
26 |
27 | ;; API tests ==========================
28 |
29 | ;excess dosync => ensure value of ref?
30 |
31 | (deftest test-initialize!
32 |
33 | (testing "New blank space."
34 | (initialize!)
35 | (dosync
36 | (is (= [] @-space))
37 | (is (= [] @-waitq-rds))
38 | (is (= [] @-waitq-ins))))
39 |
40 | (testing "New space with data."
41 | (initialize! test-space)
42 | (dosync
43 | (is (= test-space @-space)))
44 | (initialize!)
45 | (dosync
46 | (is (= [] @-space)))
47 | (is (thrown? AssertionError
48 | (initialize! :x)))
49 | (is (thrown? AssertionError
50 | (initialize! [[:x] :x])))
51 | (is (thrown? AssertionError
52 | (initialize! [[:x] []]))))
53 |
54 | (testing "Clear space and queues."
55 | (initialize! test-space)
56 | (future (rd! [:l] :l) (in! [:l] :l))
57 | (Thread/sleep 20)
58 | (initialize!)
59 | (dosync
60 | (is (= [] @-space))
61 | (is (= [] @-waitq-rds))
62 | (is (= [] @-waitq-ins)))))
63 |
64 | (deftest test-out!
65 |
66 | (testing "Basic out!"
67 | (initialize!)
68 | (is (= [:x "xx" 2 2 [:x 1]]
69 | (let [x 1]
70 | (out! :x
71 | (str \x \x)
72 | (* 2 x)
73 | (let [a 1 b 1] (+ a b))
74 | (out! :x 1)))))
75 | (dosync
76 | (is (= [[:x 1] [:x "xx" 2 2 [:x 1]]] @-space))))
77 |
78 | (testing "Compare out!s to test-space."
79 | (initialize!)
80 | (is (= [:z 0] (out! :z 0)))
81 | (is (= [:y 0] (out! :y 0)))
82 | (is (= [:x 1] (out! :x 1)))
83 | (is (= [:x 1] (out! :x (+ 0 1))))
84 | (dosync
85 | (is (= test-space @-space))))
86 |
87 | (testing "out! with waiting in! / rd!"
88 | (initialize!)
89 | (let [f (future (in! [:x ?v] v))]
90 | (out! :y 0)
91 | (Thread/sleep 20)
92 | (is (= false (realized? f)))
93 | (out! :x 1)
94 | (Thread/sleep 20)
95 | (is (= 1 @f)))
96 | (initialize!)
97 | (let [f (future (rd! [:x ?v] v))]
98 | (out! :y 0)
99 | (Thread/sleep 20)
100 | (is (= false (realized? f)))
101 | (out! :x 1)
102 | (Thread/sleep 20)
103 | (is (= 1 @f)))))
104 |
105 | (deftest test-eval!
106 |
107 | (testing "Basic eval!"
108 | (initialize!)
109 | (is (= [:x "xx" 2 2 [:x 1]]
110 | (let [x 1]
111 | @(eval! :x
112 | (str \x \x)
113 | (* 2 x)
114 | (let [a 1 b 1] (+ a b))
115 | (out! :x 1)))))
116 | (dosync
117 | (is (= [[:x 1] [:x "xx" 2 2 [:x 1]]] @-space))))
118 |
119 | (testing "Compare eval!s to test-space."
120 | (initialize!)
121 | (is (= [:z 0] @(eval! :z 0)))
122 | (is (= [:y 0] @(eval! :y 0)))
123 | (is (= [:x 1] @(eval! :x 1)))
124 | (is (= [:x 1] @(eval! :x (+ 0 1))))
125 | (dosync
126 | (is (= test-space @-space))))
127 | (testing "eval! with waiting in! / rd!"
128 | (initialize!)
129 | (let [f (future (in! [:x ?v] v))]
130 | (eval! :y 0)
131 | (Thread/sleep 20)
132 | (is (= false (realized? f)))
133 | (eval! :x 1)
134 | (Thread/sleep 20)
135 | (is (= 1 @f)))
136 | (initialize!)
137 | (let [f (future (rd! [:x ?v] v))]
138 | (eval! :y 0)
139 | (Thread/sleep 20)
140 | (is (= false (realized? f)))
141 | (eval! :x 1)
142 | (Thread/sleep 20)
143 | (is (= 1 @f)))))
144 |
145 | (deftest test-rd!
146 |
147 | (testing "Basic rd!"
148 | (initialize!)
149 | (is (= nil (safe-ops 10 (rd! [:x ?v] v))) "No match")
150 | (initialize!)
151 | (is (= 1 (safe-ops 10 (out! :x 1) (rd! [:x ?v] v))) "Match found")
152 | (initialize!)
153 | (is (= 1 (safe-ops 30
154 | (let [r (future (rd! [:x ?v] v))]
155 | (Thread/sleep 20)
156 | (out! :x 1)
157 | @r))) "Wait for match")
158 | (Thread/sleep 50)
159 | (dosync
160 | (is (= [[:x 1]] @-space) "Matching tuple still in space")))
161 |
162 | (testing "rd! all of test-space"
163 | (initialize!)
164 | (let [f (eval!
165 | [:z (rd! [:z ?v] v)]
166 | [:y (rd! [:y ?v] v)]
167 | [:x (rd! [:x ?v] v)]
168 | [:x (rd! [:x ?v] v)])]
169 | (out! :x 1) ;;just once covers all rd!
170 | (out! :y 0)
171 | (out! :z 0)
172 | (is (= test-space @f)))
173 | (Thread/sleep 50)
174 | (dosync
175 | (is (= [[:x 1] [:y 0] [:z 0] test-space] @-space)))))
176 |
177 | (deftest test-in!
178 |
179 | (testing "Basic in!"
180 | (initialize!)
181 | (is (= nil (safe-ops 10 (in! [:x ?v] v))) "No match")
182 | (initialize!)
183 | (is (= 1 (safe-ops 10 (out! :x 1) (in! [:x ?v] v))) "Match found")
184 | (initialize!)
185 | (is (= 1 (safe-ops 30
186 | (let [r (future (in! [:x ?v] v))]
187 | (Thread/sleep 20)
188 | (out! :x 1)
189 | @r))) "Wait for match")
190 | (Thread/sleep 50)
191 | (dosync
192 | (is (= [] @-space) "Matching tuple not in space")))
193 |
194 | (testing "in! all of test-space"
195 | (initialize!)
196 | (let [f (eval!
197 | [:z (in! [:z ?v] v)]
198 | [:y (in! [:y ?v] v)]
199 | [:x (in! [:x ?v] v)]
200 | [:x (in! [:x ?v] v)])]
201 | (out! :x 1)
202 | (out! :x 1) ;;twice to cover all in!
203 | (out! :y 0)
204 | (out! :z 0)
205 | (is (= test-space @f)))
206 | (Thread/sleep 50)
207 | (dosync
208 | (is (= [test-space] @-space)))))
209 |
210 |
211 |
212 |
--------------------------------------------------------------------------------