1 | (*$R-*)
|
---|
2 | IMPLEMENTATION MODULE CSP;
|
---|
3 | (*
|
---|
4 | Module: Communicating Sequential Processes
|
---|
5 | From: "A Modula-2 Implementation of CSP",
|
---|
6 | M. Collado, R. Morales, J.J. Moreno,
|
---|
7 | SIGPlan Notices, Volume 22, Number 6, June 1987.
|
---|
8 | Some modifications by Ceriel J.H. Jacobs
|
---|
9 | Version: $Header: /cvsup/minix/src/lib/ack/libm2/CSP.mod,v 1.1 2005/10/10 15:27:46 beng Exp $
|
---|
10 |
|
---|
11 | See this article for an explanation of the use of this module.
|
---|
12 | *)
|
---|
13 |
|
---|
14 | FROM random IMPORT Uniform;
|
---|
15 | FROM SYSTEM IMPORT BYTE, ADDRESS, NEWPROCESS, TRANSFER;
|
---|
16 | FROM Storage IMPORT Allocate, Deallocate;
|
---|
17 | FROM Traps IMPORT Message;
|
---|
18 |
|
---|
19 | CONST WorkSpaceSize = 2000;
|
---|
20 |
|
---|
21 | TYPE ByteAddress = POINTER TO BYTE;
|
---|
22 | Channel = POINTER TO ChannelDescriptor;
|
---|
23 | ProcessType = POINTER TO ProcessDescriptor;
|
---|
24 | ProcessDescriptor = RECORD
|
---|
25 | next: ProcessType;
|
---|
26 | father: ProcessType;
|
---|
27 | cor: ADDRESS;
|
---|
28 | wsp: ADDRESS;
|
---|
29 | guardindex: INTEGER;
|
---|
30 | guardno: CARDINAL;
|
---|
31 | guardcount: CARDINAL;
|
---|
32 | opened: Channel;
|
---|
33 | sons: CARDINAL;
|
---|
34 | msgadr: ADDRESS;
|
---|
35 | msglen: CARDINAL;
|
---|
36 | END;
|
---|
37 |
|
---|
38 | Queue = RECORD
|
---|
39 | head, tail: ProcessType;
|
---|
40 | END;
|
---|
41 |
|
---|
42 | ChannelDescriptor = RECORD
|
---|
43 | senders: Queue;
|
---|
44 | owner: ProcessType;
|
---|
45 | guardindex: INTEGER;
|
---|
46 | next: Channel;
|
---|
47 | END;
|
---|
48 |
|
---|
49 | VAR cp: ProcessType;
|
---|
50 | free, ready: Queue;
|
---|
51 |
|
---|
52 | (* ------------ Private modules and procedures ------------- *)
|
---|
53 |
|
---|
54 | MODULE ProcessQueue;
|
---|
55 |
|
---|
56 | IMPORT ProcessType, Queue;
|
---|
57 | EXPORT Push, Pop, InitQueue, IsEmpty;
|
---|
58 |
|
---|
59 | PROCEDURE InitQueue(VAR q: Queue);
|
---|
60 | BEGIN
|
---|
61 | WITH q DO
|
---|
62 | head := NIL;
|
---|
63 | tail := NIL
|
---|
64 | END
|
---|
65 | END InitQueue;
|
---|
66 |
|
---|
67 | PROCEDURE Push(p: ProcessType; VAR q: Queue);
|
---|
68 | BEGIN
|
---|
69 | p^.next := NIL;
|
---|
70 | WITH q DO
|
---|
71 | IF head = NIL THEN
|
---|
72 | tail := p
|
---|
73 | ELSE
|
---|
74 | head^.next := p
|
---|
75 | END;
|
---|
76 | head := p
|
---|
77 | END
|
---|
78 | END Push;
|
---|
79 |
|
---|
80 | PROCEDURE Pop(VAR q: Queue; VAR p: ProcessType);
|
---|
81 | BEGIN
|
---|
82 | WITH q DO
|
---|
83 | p := tail;
|
---|
84 | IF p # NIL THEN
|
---|
85 | tail := tail^.next;
|
---|
86 | IF head = p THEN
|
---|
87 | head := NIL
|
---|
88 | END
|
---|
89 | END
|
---|
90 | END
|
---|
91 | END Pop;
|
---|
92 |
|
---|
93 | PROCEDURE IsEmpty(q: Queue): BOOLEAN;
|
---|
94 | BEGIN
|
---|
95 | RETURN q.head = NIL
|
---|
96 | END IsEmpty;
|
---|
97 |
|
---|
98 | END ProcessQueue;
|
---|
99 |
|
---|
100 |
|
---|
101 | PROCEDURE DoTransfer;
|
---|
102 | VAR aux: ProcessType;
|
---|
103 | BEGIN
|
---|
104 | aux := cp;
|
---|
105 | Pop(ready, cp);
|
---|
106 | IF cp = NIL THEN
|
---|
107 | HALT
|
---|
108 | ELSE
|
---|
109 | TRANSFER(aux^.cor, cp^.cor)
|
---|
110 | END
|
---|
111 | END DoTransfer;
|
---|
112 |
|
---|
113 | PROCEDURE OpenChannel(ch: Channel; n: INTEGER);
|
---|
114 | BEGIN
|
---|
115 | WITH ch^ DO
|
---|
116 | IF guardindex = 0 THEN
|
---|
117 | guardindex := n;
|
---|
118 | next := cp^.opened;
|
---|
119 | cp^.opened := ch
|
---|
120 | END
|
---|
121 | END
|
---|
122 | END OpenChannel;
|
---|
123 |
|
---|
124 | PROCEDURE CloseChannels(p: ProcessType);
|
---|
125 | BEGIN
|
---|
126 | WITH p^ DO
|
---|
127 | WHILE opened # NIL DO
|
---|
128 | opened^.guardindex := 0;
|
---|
129 | opened := opened^.next
|
---|
130 | END
|
---|
131 | END
|
---|
132 | END CloseChannels;
|
---|
133 |
|
---|
134 | PROCEDURE ThereAreOpenChannels(): BOOLEAN;
|
---|
135 | BEGIN
|
---|
136 | RETURN cp^.opened # NIL;
|
---|
137 | END ThereAreOpenChannels;
|
---|
138 |
|
---|
139 | PROCEDURE Sending(ch: Channel): BOOLEAN;
|
---|
140 | BEGIN
|
---|
141 | RETURN NOT IsEmpty(ch^.senders)
|
---|
142 | END Sending;
|
---|
143 |
|
---|
144 | (* -------------- Public Procedures ----------------- *)
|
---|
145 |
|
---|
146 | PROCEDURE COBEGIN;
|
---|
147 | (* Beginning of a COBEGIN .. COEND structure *)
|
---|
148 | BEGIN
|
---|
149 | END COBEGIN;
|
---|
150 |
|
---|
151 | PROCEDURE COEND;
|
---|
152 | (* End of a COBEGIN .. COEND structure *)
|
---|
153 | (* VAR aux: ProcessType; *)
|
---|
154 | BEGIN
|
---|
155 | IF cp^.sons > 0 THEN
|
---|
156 | DoTransfer
|
---|
157 | END
|
---|
158 | END COEND;
|
---|
159 |
|
---|
160 | PROCEDURE StartProcess(P: PROC);
|
---|
161 | (* Start an anonimous process that executes the procedure P *)
|
---|
162 | VAR newprocess: ProcessType;
|
---|
163 | BEGIN
|
---|
164 | Pop(free, newprocess);
|
---|
165 | IF newprocess = NIL THEN
|
---|
166 | Allocate(newprocess,SIZE(ProcessDescriptor));
|
---|
167 | Allocate(newprocess^.wsp, WorkSpaceSize)
|
---|
168 | END;
|
---|
169 | WITH newprocess^ DO
|
---|
170 | father := cp;
|
---|
171 | sons := 0;
|
---|
172 | msglen := 0;
|
---|
173 | NEWPROCESS(P, wsp, WorkSpaceSize, cor)
|
---|
174 | END;
|
---|
175 | cp^.sons := cp^.sons + 1;
|
---|
176 | Push(newprocess, ready)
|
---|
177 | END StartProcess;
|
---|
178 |
|
---|
179 | PROCEDURE StopProcess;
|
---|
180 | (* Terminate a Process (itself) *)
|
---|
181 | VAR aux: ProcessType;
|
---|
182 | BEGIN
|
---|
183 | aux := cp^.father;
|
---|
184 | aux^.sons := aux^.sons - 1;
|
---|
185 | IF aux^.sons = 0 THEN
|
---|
186 | Push(aux, ready)
|
---|
187 | END;
|
---|
188 | aux := cp;
|
---|
189 | Push(aux, free);
|
---|
190 | Pop(ready, cp);
|
---|
191 | IF cp = NIL THEN
|
---|
192 | HALT
|
---|
193 | ELSE
|
---|
194 | TRANSFER(aux^.cor, cp^.cor)
|
---|
195 | END
|
---|
196 | END StopProcess;
|
---|
197 |
|
---|
198 | PROCEDURE InitChannel(VAR ch: Channel);
|
---|
199 | (* Initialize the channel ch *)
|
---|
200 | BEGIN
|
---|
201 | Allocate(ch, SIZE(ChannelDescriptor));
|
---|
202 | WITH ch^ DO
|
---|
203 | InitQueue(senders);
|
---|
204 | owner := NIL;
|
---|
205 | next := NIL;
|
---|
206 | guardindex := 0
|
---|
207 | END
|
---|
208 | END InitChannel;
|
---|
209 |
|
---|
210 | PROCEDURE GetChannel(ch: Channel);
|
---|
211 | (* Assign the channel ch to the process that gets it *)
|
---|
212 | BEGIN
|
---|
213 | WITH ch^ DO
|
---|
214 | IF owner # NIL THEN
|
---|
215 | Message("Channel already has an owner");
|
---|
216 | HALT
|
---|
217 | END;
|
---|
218 | owner := cp
|
---|
219 | END
|
---|
220 | END GetChannel;
|
---|
221 |
|
---|
222 | PROCEDURE Send(data: ARRAY OF BYTE; VAR ch: Channel);
|
---|
223 | (* Send a message with the data to the cvhannel ch *)
|
---|
224 | VAR m: ByteAddress;
|
---|
225 | (* aux: ProcessType; *)
|
---|
226 | i: CARDINAL;
|
---|
227 | BEGIN
|
---|
228 | WITH ch^ DO
|
---|
229 | Push(cp, senders);
|
---|
230 | Allocate(cp^.msgadr, SIZE(data));
|
---|
231 | m := cp^.msgadr;
|
---|
232 | cp^.msglen := HIGH(data);
|
---|
233 | FOR i := 0 TO HIGH(data) DO
|
---|
234 | m^ := data[i];
|
---|
235 | m := ADDRESS(m) + 1
|
---|
236 | END;
|
---|
237 | IF guardindex # 0 THEN
|
---|
238 | owner^.guardindex := guardindex;
|
---|
239 | CloseChannels(owner);
|
---|
240 | Push(owner, ready)
|
---|
241 | END
|
---|
242 | END;
|
---|
243 | DoTransfer
|
---|
244 | END Send;
|
---|
245 |
|
---|
246 | PROCEDURE Receive(VAR ch: Channel; VAR dest: ARRAY OF BYTE);
|
---|
247 | (* Receive a message from the channel ch into the dest variable *)
|
---|
248 | VAR aux: ProcessType;
|
---|
249 | m: ByteAddress;
|
---|
250 | i: CARDINAL;
|
---|
251 | BEGIN
|
---|
252 | WITH ch^ DO
|
---|
253 | IF cp # owner THEN
|
---|
254 | Message("Only owner of channel can receive from it");
|
---|
255 | HALT
|
---|
256 | END;
|
---|
257 | IF Sending(ch) THEN
|
---|
258 | Pop(senders, aux);
|
---|
259 | m := aux^.msgadr;
|
---|
260 | FOR i := 0 TO aux^.msglen DO
|
---|
261 | dest[i] := m^;
|
---|
262 | m := ADDRESS(m) + 1
|
---|
263 | END;
|
---|
264 | Push(aux, ready);
|
---|
265 | Push(cp, ready);
|
---|
266 | CloseChannels(cp)
|
---|
267 | ELSE
|
---|
268 | OpenChannel(ch, -1);
|
---|
269 | DoTransfer;
|
---|
270 | Pop(senders, aux);
|
---|
271 | m := aux^.msgadr;
|
---|
272 | FOR i := 0 TO aux^.msglen DO
|
---|
273 | dest[i] := m^;
|
---|
274 | m := ADDRESS(m) + 1
|
---|
275 | END;
|
---|
276 | Push(cp, ready);
|
---|
277 | Push(aux, ready)
|
---|
278 | END;
|
---|
279 | Deallocate(aux^.msgadr, aux^.msglen+1);
|
---|
280 | DoTransfer
|
---|
281 | END
|
---|
282 | END Receive;
|
---|
283 |
|
---|
284 | PROCEDURE SELECT(n: CARDINAL);
|
---|
285 | (* Beginning of a SELECT structure with n guards *)
|
---|
286 | BEGIN
|
---|
287 | cp^.guardindex := Uniform(1,n);
|
---|
288 | cp^.guardno := n;
|
---|
289 | cp^.guardcount := n
|
---|
290 | END SELECT;
|
---|
291 |
|
---|
292 | PROCEDURE NEXTGUARD(): CARDINAL;
|
---|
293 | (* Returns an index to the next guard to be evaluated in a SELECT *)
|
---|
294 | BEGIN
|
---|
295 | RETURN cp^.guardindex
|
---|
296 | END NEXTGUARD;
|
---|
297 |
|
---|
298 | PROCEDURE GUARD(cond: BOOLEAN; ch: Channel;
|
---|
299 | VAR dest: ARRAY OF BYTE): BOOLEAN;
|
---|
300 | (* Evaluates a guard, including reception management *)
|
---|
301 | (* VAR aux: ProcessType; *)
|
---|
302 | BEGIN
|
---|
303 | IF NOT cond THEN
|
---|
304 | RETURN FALSE
|
---|
305 | ELSIF ch = NIL THEN
|
---|
306 | CloseChannels(cp);
|
---|
307 | cp^.guardindex := 0;
|
---|
308 | RETURN TRUE
|
---|
309 | ELSIF Sending(ch) THEN
|
---|
310 | Receive(ch, dest);
|
---|
311 | cp^.guardindex := 0;
|
---|
312 | RETURN TRUE
|
---|
313 | ELSE
|
---|
314 | OpenChannel(ch, cp^.guardindex);
|
---|
315 | RETURN FALSE
|
---|
316 | END
|
---|
317 | END GUARD;
|
---|
318 |
|
---|
319 | PROCEDURE ENDSELECT(): BOOLEAN;
|
---|
320 | (* End of a SELECT structure *)
|
---|
321 | BEGIN
|
---|
322 | WITH cp^ DO
|
---|
323 | IF guardindex <= 0 THEN
|
---|
324 | RETURN TRUE
|
---|
325 | END;
|
---|
326 | guardcount := guardcount - 1;
|
---|
327 | IF guardcount # 0 THEN
|
---|
328 | guardindex := (guardindex MOD INTEGER(guardno)) + 1
|
---|
329 | ELSIF ThereAreOpenChannels() THEN
|
---|
330 | DoTransfer
|
---|
331 | ELSE
|
---|
332 | guardindex := 0
|
---|
333 | END
|
---|
334 | END;
|
---|
335 | RETURN FALSE
|
---|
336 | END ENDSELECT;
|
---|
337 |
|
---|
338 | BEGIN
|
---|
339 | InitQueue(free);
|
---|
340 | InitQueue(ready);
|
---|
341 | Allocate(cp,SIZE(ProcessDescriptor));
|
---|
342 | WITH cp^ DO
|
---|
343 | sons := 0;
|
---|
344 | father := NIL
|
---|
345 | END
|
---|
346 | END CSP.
|
---|
347 |
|
---|