source: release/1.6/source/cocoa-ide/hemlock/unused/archive/wire/remote.lisp

Last change on this file was 6, checked in by Gary Byers, 21 years ago

Initial revision

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 13.2 KB
Line 
1;;; -*- Log: code.log; Package: wire -*-
2;;;
3;;; **********************************************************************
4;;; This code was written as part of the CMU Common Lisp project at
5;;; Carnegie Mellon University, and has been placed in the public domain.
6;;;
7#+NIL
8(ext:file-comment
9 "$Header$")
10;;;
11;;; **********************************************************************
12;;;
13;;; This file implements a simple remote procedure call mechanism on top
14;;; of wire.lisp.
15;;;
16;;; Written by William Lott.
17;;;
18
19(defpackage :hemlock.wire
20 (:use :common-lisp))
21
22(in-package :hemlock.wire)
23
24(defstruct remote-wait
25 value1 value2 value3 value4 value5
26 abort
27 finished)
28
29(defvar *pending-returns* nil
30 "AList of wire . remote-wait structs")
31
32;;; MAYBE-NUKE-REMOTE-WAIT -- internal
33;;;
34;;; If the remote wait has finished, remove the external translation.
35;;; Otherwise, mark the remote wait as finished so the next call to
36;;; MAYBE-NUKE-REMOTE-WAIT will really nuke it.
37;;;
38(defun maybe-nuke-remote-wait (remote)
39 (cond ((remote-wait-finished remote)
40 (forget-remote-translation remote)
41 t)
42 (t
43 (setf (remote-wait-finished remote)
44 t)
45 nil)))
46
47;;; REMOTE -- public
48;;;
49;;; Execute the body remotly. Subforms are executed locally in the lexical
50;;; envionment of the macro call. No values are returned.
51;;;
52(defmacro remote (wire-form &body forms)
53 "Evaluates the given forms remotly. No values are returned, as the remote
54evaluation is asyncronus."
55 (let ((wire (gensym)))
56 `(let ((,wire ,wire-form))
57 ,@(mapcar #'(lambda (form)
58 `(wire-output-funcall ,wire
59 ',(car form)
60 ,@(cdr form)))
61 forms)
62 (values))))
63
64;;; REMOTE-VALUE-BIND -- public
65;;;
66;;; Send to remote forms. First, a call to the correct dispatch routine based
67;;; on the number of args, then the actual call. The dispatch routine will get
68;;; the second funcall and fill in the correct number of arguments.
69;;; Note: if there are no arguments, we don't even wait for the function to
70;;; return, cause we can kind of guess at what the currect results would be.
71;;;
72(defmacro remote-value-bind (wire-form vars form &rest body)
73 "Bind vars to the multiple values of form (which is executed remotly). The
74forms in body are only executed if the remote function returned as apposed
75to aborting due to a throw."
76 (cond
77 ((null vars)
78 `(progn
79 (remote ,wire-form ,form)
80 ,@body))
81 (t
82 (let ((remote (gensym))
83 (wire (gensym)))
84 `(let* ((,remote (make-remote-wait))
85 (,wire ,wire-form)
86 (*pending-returns* (cons (cons ,wire ,remote)
87 *pending-returns*)))
88 (unwind-protect
89 (let ,vars
90 (remote ,wire
91 (,(case (length vars)
92 (1 'do-1-value-call)
93 (2 'do-2-value-call)
94 (3 'do-3-value-call)
95 (4 'do-4-value-call)
96 (5 'do-5-value-call)
97 (t 'do-n-value-call))
98 (make-remote-object ,remote))
99 ,form)
100 (wire-force-output ,wire)
101 (loop
102 #+:hemlock.serve-event
103 (serve-all-events)
104 #-:hemlock.serve-event
105 (wire-get-object ,wire)
106 (when (remote-wait-finished ,remote)
107 (return)))
108 (unless (remote-wait-abort ,remote)
109 ,(case (length vars)
110 (1 `(setf ,(first vars) (remote-wait-value1 ,remote)))
111 (2 `(setf ,(first vars) (remote-wait-value1 ,remote)
112 ,(second vars) (remote-wait-value2 ,remote)))
113 (3 `(setf ,(first vars) (remote-wait-value1 ,remote)
114 ,(second vars) (remote-wait-value2 ,remote)
115 ,(third vars) (remote-wait-value3 ,remote)))
116 (4 `(setf ,(first vars) (remote-wait-value1 ,remote)
117 ,(second vars) (remote-wait-value2 ,remote)
118 ,(third vars) (remote-wait-value3 ,remote)
119 ,(fourth vars) (remote-wait-value4 ,remote)))
120 (5 `(setf ,(first vars) (remote-wait-value1 ,remote)
121 ,(second vars) (remote-wait-value2 ,remote)
122 ,(third vars) (remote-wait-value3 ,remote)
123 ,(fourth vars) (remote-wait-value4 ,remote)
124 ,(fifth vars) (remote-wait-value5 ,remote)))
125 (t
126 (do ((remaining-vars vars (cdr remaining-vars))
127 (form (list 'setf)
128 (nconc form
129 (list (car remaining-vars)
130 `(pop values)))))
131 ((null remaining-vars)
132 `(let ((values (remote-wait-value1 ,remote)))
133 ,form)))))
134 ,@body))
135 (maybe-nuke-remote-wait ,remote)))))))
136
137
138;;; REMOTE-VALUE -- public
139;;;
140;;; Alternate interface to getting the single return value of a remote
141;;; function. Works pretty much just the same, except the single value is
142;;; returned.
143;;;
144(defmacro remote-value (wire-form form &optional
145 (on-server-unwind
146 `(error "Remote server unwound")))
147 "Execute the single form remotly. The value of the form is returned.
148 The optional form on-server-unwind is only evaluated if the server unwinds
149 instead of returning."
150 (let ((remote (gensym))
151 (wire (gensym)))
152 `(let* ((,remote (make-remote-wait))
153 (,wire ,wire-form)
154 (*pending-returns* (cons (cons ,wire ,remote)
155 *pending-returns*)))
156 (unwind-protect
157 (progn
158 (remote ,wire
159 (do-1-value-call (make-remote-object ,remote))
160 ,form)
161 (wire-force-output ,wire)
162 (loop
163 #+:hemlock.serve-event
164 (serve-all-events)
165 #-:hemlock.serve-event
166 (wire-get-object ,wire)
167 (when (remote-wait-finished ,remote)
168 (return))))
169 (maybe-nuke-remote-wait ,remote))
170 (if (remote-wait-abort ,remote)
171 ,on-server-unwind
172 (remote-wait-value1 ,remote)))))
173
174;;; DEFINE-FUNCTIONS -- internal
175;;;
176;;; Defines two functions, one that the client runs in the server, and one
177;;; that the server runs in the client:
178;;;
179;;; DO-n-VALUE-CALL -- internal
180;;;
181;;; Executed by the remote process. Reads the next object off the wire and
182;;; sends the value back. Unwind-protect is used to make sure we send something
183;;; back so the requestor doesn't hang.
184;;;
185;;; RETURN-n-VALUE -- internal
186;;;
187;;; The remote procedure returned the given value, so fill it in the
188;;; remote-wait structure. Note, if the requestor has aborted, just throw
189;;; the value away.
190;;;
191(defmacro define-functions (values)
192 (let ((do-call (intern (format nil "~:@(do-~D-value-call~)" values)))
193 (return-values (intern (format nil "~:@(return-~D-value~:P~)" values)))
194 (vars nil))
195 (dotimes (i values)
196 (push (gensym) vars))
197 (setf vars (nreverse vars))
198 `(progn
199 (defun ,do-call (result)
200 (let (worked ,@vars)
201 (unwind-protect
202 (progn
203 (multiple-value-setq ,vars
204 (wire-get-object *current-wire*))
205 (setf worked t))
206 (if worked
207 (remote *current-wire*
208 (,return-values result ,@vars))
209 (remote *current-wire*
210 (remote-return-abort result)))
211 (wire-force-output *current-wire*))))
212 (defun ,return-values (remote ,@vars)
213 (let ((result (remote-object-value remote)))
214 (unless (maybe-nuke-remote-wait result)
215 ,@(let ((setf-forms nil))
216 (dotimes (i values)
217 (push `(setf (,(intern (format nil
218 "~:@(remote-wait-value~D~)"
219 (1+ i)))
220 result)
221 ,(nth i vars))
222 setf-forms))
223 (nreverse setf-forms))))
224 nil))))
225
226(define-functions 1)
227(define-functions 2)
228(define-functions 3)
229(define-functions 4)
230(define-functions 5)
231
232
233;;; DO-N-VALUE-CALL -- internal
234;;;
235;;; For more values then 5, all the values are rolled into a list and passed
236;;; back as the first value, so we use RETURN-1-VALUE to return it.
237;;;
238(defun do-n-value-call (result)
239 (let (worked values)
240 (unwind-protect
241 (progn
242 (setf values
243 (multiple-value-list (wire-get-object *current-wire*)))
244 (setf worked t))
245 (if worked
246 (remote *current-wire*
247 (return-1-values result values))
248 (remote *current-wire*
249 (remote-return-abort result)))
250 (wire-force-output *current-wire*))))
251
252;;; REMOTE-RETURN-ABORT -- internal
253;;;
254;;; The remote call aborted instead of returned.
255;;;
256(defun remote-return-abort (result)
257 (setf result (remote-object-value result))
258 (unless (maybe-nuke-remote-wait result)
259 (setf (remote-wait-abort result) t)))
260
261#+:hemlock.serve-event
262;;; SERVE-REQUESTS -- internal
263;;;
264;;; Serve all pending requests on the given wire.
265;;;
266(defun serve-requests (wire on-death)
267 (handler-bind
268 ((wire-eof #'(lambda (condition)
269 (declare (ignore condition))
270 (close (wire-stream wire))
271 #+NILGB(system:invalidate-descriptor (wire-fd wire))
272 #+NILGB(unix:unix-close (wire-fd wire))
273 (dolist (pending *pending-returns*)
274 (when (eq (car pending)
275 wire)
276 (unless (maybe-nuke-remote-wait (cdr pending))
277 (setf (remote-wait-abort (cdr pending))
278 t))))
279 (when on-death
280 (funcall on-death))
281 (return-from serve-requests (values))))
282 (wire-error #'(lambda (condition)
283 (declare (ignore condition))
284 #+NILGB
285 (system:invalidate-descriptor (wire-fd wire)))))
286 (progn #+NILGB loop
287 #+NILGB
288 (unless (wire-listen wire)
289 (return))
290 (wire-get-object wire)))
291 (values))
292
293;;; NEW-CONNECTION -- internal
294;;;
295;;; Maybe build a new wire and add it to the servers list of fds. If the user
296;;; Supplied a function, close the socket if it returns NIL. Otherwise, install
297;;; the wire.
298;;;
299(defun new-connection (socket addr on-connect)
300 (let ((wire (make-wire socket))
301 (on-death nil))
302 (if (or (null on-connect)
303 (multiple-value-bind (okay death-fn)
304 (funcall on-connect wire addr)
305 (setf on-death death-fn)
306 okay))
307 #+:hemlock.serve-event
308 (add-fd-handler socket :input
309 #'(lambda (socket)
310 (declare (ignore socket))
311 (serve-requests wire on-death)))
312 #-:hemlock.serve-event
313 (make-process (lambda ()
314 (loop (wire-get-object wire)))
315 :name (format nil "Wire process for ~S." wire))
316 (ext-close-connection socket))))
317
318;;; REQUEST-SERVER structure
319;;;
320;;; Just a simple handle on the socket and system:serve-event handler that make
321;;; up a request server.
322;;;
323(defstruct (request-server
324 (:print-function %print-request-server))
325 socket
326 handler)
327
328(defun %print-request-server (rs stream depth)
329 (declare (ignore depth))
330 (print-unreadable-object (rs stream :type t)
331 (format stream "for ~D" (request-server-socket rs))))
332
333;;; CREATE-REQUEST-SERVER -- Public.
334;;;
335;;; Create a TCP/IP listener on the given port. If anyone tries to connect to
336;;; it, call NEW-CONNECTION to do the connecting.
337;;;
338#+:hemlock.serve-event
339(defun create-request-server (port &optional on-connect)
340 "Create a request server on the given port. Whenever anyone connects to it,
341 call the given function with the newly created wire and the address of the
342 connector. If the function returns NIL, the connection is destroyed;
343 otherwise, it is accepted. This returns a manifestation of the server that
344 DESTROY-REQUEST-SERVER accepts to kill the request server."
345 (let* ((socket (ext-create-inet-listener port))
346 (handler (add-fd-handler socket :input
347 #'(lambda (socket)
348 (multiple-value-bind
349 (newconn addr)
350 (ext-accept-tcp-connection socket)
351 (new-connection newconn addr on-connect))))))
352 (make-request-server :socket socket
353 :handler handler)))
354
355#-:hemlock.serve-event
356(defun create-request-server (port &optional on-connect)
357 "Create a request server on the given port. Whenever anyone connects to it,
358 call the given function with the newly created wire and the address of the
359 connector. If the function returns NIL, the connection is destroyed;
360 otherwise, it is accepted. This returns a manifestation of the server that
361 DESTROY-REQUEST-SERVER accepts to kill the request server."
362 (let* ((socket (ext-create-inet-listener port))
363 (handler (make-process
364 (lambda ()
365 (loop
366 (multiple-value-bind
367 (newconn addr)
368 (ext-accept-tcp-connection socket)
369 (new-connection newconn addr on-connect)))))))
370 (make-request-server :socket socket
371 :handler handler)))
372
373;;; DESTROY-REQUEST-SERVER -- Public.
374;;;
375;;; Removes the request server from SERVER's list of file descriptors and
376;;; closes the socket behind it.
377;;;
378(defun destroy-request-server (server)
379 "Quit accepting connections to the given request server."
380 #+:hemlock.serve-event
381 (remove-fd-handler (request-server-handler server))
382 ;;
383 (ext-close-socket (request-server-socket server))
384 nil)
385
386;;; CONNECT-TO-REMOTE-SERVER -- Public.
387;;;
388;;; Just like the doc string says, connect to a remote server. A handler is
389;;; installed to handle return values, etc.
390;;;
391#-NIL
392(defun connect-to-remote-server (hostname port &optional on-death)
393 "Connect to a remote request server addressed with the given host and port
394 pair. This returns the created wire."
395 (let* ((socket (ext-connect-to-inet-socket hostname port))
396 (wire (make-wire socket)))
397 #+:hemlock.serve-event
398 ;; hmm, what exactly should this accomplish?
399 (add-fd-handler socket :input
400 #'(lambda (socket)
401 (declare (ignore socket))
402 (serve-requests wire on-death)))
403 wire))
Note: See TracBrowser for help on using the repository browser.