|
Allegro CL version 11.0 |
The purpose of the RPC utility is to allow separate Lisp images to communicate with each other by remote procedure calls. This is accomplished by:
Although the interface is very symmetric, there is a need to distinguish between the roles of client and server. The server advertises the possibility of a connection, and the client connects to an advertising server. The distinction between client and server is significant when a connection is established (or re-established). Thus, a client image may connect on demand when a call is made, but a server image may call the client only when the client has made a connection. Once the connection is made, the actual calling protocol makes no reference to the distinction.
It is possible for the same image to be both client and server and each image in a set of communicating Lisp images may be both. But remote references to data are specific to a particular connection.
All the symbols associated with RPC reside in the net.rpc
package. RPC functionality is in the :aclrpc
module. It is loaded into an image by evaluating (require :aclrpc)
.
The base RPC facility allows unrestricted connections between clients and servers. Once the connection is established, arbitrary function calls are allowed in both directions. This could be a serious security exposure unless all possible clients are trusted applications. All the data transmitted between client and server is only encoded for efficiency and is exposed to third-party snooping.
If SSL connections are used (see Secure Socket Layer (SSL) in socket.html), then both client authentication and data protection can be as secure as desired.
Access control based on the remote host may be done in the function supplied as the connect-function keyword argument to make-rpc-server. This function may close the connection before any remote operations are initiated.
In addition, application programs may perform arbitrary filtering before and after each remote operation by implementing rpc-do-request and rpc-do-invoke methods on sub-classes of rpc-port.
These generic functions are called to do the work of any remote request or remote call.
A lightweight approach to access control may be sufficient if the network is secure from malicious access. The application may still need to be protected from inadvertant or inappropriate connections. For this situation, we offer a passcode scheme that can be used to authenticate clients, and methods that can be used to filter individual calls and callbacks. We provide mixin classes with-rpc-server-enabler and with-rpc-port-enabler, and sub-classes rpc-socket-server-with-enabler and rpc-socket-port-with-enabler. The sub-classes implement this passcode scheme with specific implementations of the rpc-do-invoke and rpc-do-request methods.
For the sake of discussion in the following, we assume that two Lisp images, A and B are communicating. Image A is a server and B is a client.
Image A (server) Image B (client)
(require :aclrpc)
(use-package :net.rpc)
(require :aclrpc)
(use-package :net.rpc)
(multiple-value-setq (s p l es)
(make-rpc-server 'rpc-socket-server
:home "lispA" :local-port 4326
:open :listener
:connect-action :call
:connect-function
#'(lambda (pt) (setf p pt))
;; :verbose t
))
(type-of s) -> rpc-socket-server
(null p) -> t
(null es) -> t
(type-of l) -> mp:process
(multiple-value-setq (q ec)
(make-rpc-client 'rpc-socket-port
:home "lispB" :remote-port 4326
:open t
;; :remote-host "[serverhost]"
;;; if server is not localhost
;; :verbose t
))
(type-of q)
-> rpc-socket-port
(null ec) -> t
(rpc-open-p q)
(with-remote-port (q)
(rcall 'print "Hello from B")
)
"Hello from B"
(rpc-open-p p)
(with-remote-port (p)
(rcall 'print "Hello from A")
)
"Hello from A"
(rpc-close q :final t)
(rpc-open-p p)
(rpc-close s :stop :final)
(rpc-open-p :all)
(rpc-open-p :all)
The following classes define the connection parameters and data transmission formats of stream socket connections:
Stream connections are implemented to use the operating system stream socket implementation. Stream sockets provide reliable two-way communication between two host processes. If a Lisp RPC call is made through a stream socket connection, the caller will receive a value or an error signal indicating that the call failed to complete.
If one Lisp host makes two RPC calls in succession to the same Lisp destination host (on the same stream socket connection), then the remote calls will be begun in the same order as they were sent.
The following classes define the connection parameters and data transmission formats of stream socket connections:
Datagram connections are implemented to use the operating system datagram socket implementation. The datagram protocol does not guarantee arrival of messages, nor does it preserve order.
Because of these semantics, callbacks hardly ever make sense and remote pointers are of dubious value since any manipulation of a remote pointer usually requires a call back to the home of the reference.
The following classes define the connection parameters and data transmission formats of datagram socket connections:
NOTE: This feature is only implemented in the Windows version of Allegro CL.
Shared memory connections are implemented by Lisp functions storing data into and fetching data from a memory area shared between two OS processes. On a machine with multiple processors, the processes could be running on separate processors. Shared memory connections are synchronous and reliable, like stream socket connections.
The following classes define the connection parameters and data transmission formats of shared memory connections:
A new sub-class of rpc-port must be created in order to implement rpc-do-request and rpc-do-invoke methods specific to some application. This is done with a form such as
(defclass new-port-class (... rpc-port ...) ())
The only requirement is that rpc-port must appear somewhere in the class precedence list of the new class.
In order to cause the creation of appropriate server ports, the application must also define a sub-class of rpc-port-server with an initial value for the port-instance slot.
(defclass new-server-class (... rpc-port-server ...)
((port-instance :initform (make-instance 'new-port-class))
...))
Again, one requirement is that rpc-port-server must appear somewhere in the class precedence list of the new class. The second requirement is that the port-instance slot must be initialized to an instance of the corresponding rpc-port sub-class.
These classes should not be instantiated with make-instance.
Use the functions make-rpc-server and make-rpc-client to create instances of these classes.
The two mixin classes, with-rpc-server-enabler and with-rpc-port-enabler, and thew corresponding stream socket sub-classes, rpc-socket-server-with-enabler and rpc-socket-port-with-enabler, are examples of server and port sub-classing to implement a lightweight security interface for stream socket connections.
The socket sub-classes implement rpc-do-invoke and rpc-do-request methods that manage a simple passcode scheme.
The function rpc-version returns information about the running Allegro CL RPC version. If the rpc module is updated after release, the version will be changed. Thus, rpc-version will allow you to determine whether you are running the updated version.
In the subsections of this section, we describe the Lisp API for Stream Socket connections.
The following classes define the connection parameters of stream socket connections.
The functions associated with connecting are:
The following operators are used once the port is established.
The variable *rpc-port* is also useful.
This RPC API supports three callback models:
Consider two lisp images A and B where A.i and B.j represent threads (Lisp processes) in A and B respectively.
Nested - Nested mode provides a continuity in dynamic environments in each running image. Nested calls are possible only if every caller at each level is waiting for a result, i.e. the caller uses rcall or rpc-invoke with :wait t
, or has called rpc-wait explicitly.
Callbacks cannot interrupt running code.
Image A -- thread A.i | Image B -- thread B.j |
|
|
|
|
|
|
|
|
|
|
|
|
|
Parallel - Parallel mode may be the preferred mode for GUI event callbacks.This mode may be more prone to deadlocks than the nested mode.
Image A -- thread A.i | Image B -- thread B.j | Image A -- thread A.k | Image B -- thread B.l |
|
|||
|
|||
|
|||
|
|||
|
|||
|
|||
|
Blocking - Blocking mode prevents recursive callbacks entirely. It requires careful programming to avoid deadlocks or errors.
Image A -- thread A.i | Image B -- thread B.j | Image A -- thread A.k |
|
||
|
||
|
Each RPC port defines a default callback mode for calls through the port. The caller may override the default mode for an individual call.
Callbacks from :one-way calls are always handled in the :parallel style since the caller is not waiting for a result, and hence is not prepared to deal with a callback. The remote application making the callback has no basis for any assumptions about the dynamic environment at the destination.
In this section and its subsections, we describe the Lisp API for Datagram Socket connections. The functions and methods have the same names as in the Stream Socket API but the semantics are sometimes different because of the nature of the protocol.
In addition to the indeterminate nature of message arrival, datagram connections exhibit several other unique characteristics:
Thus datagram connections do not exhibit the same symmetry as stream socket connections.
The following classes define the connection parameters of datagram connections.
The following functions and methods are used for Datagram socket connecting.
nil
, in which case it stops the listener. When the (required) port argument is an instance of rpc-datagram-port, the method is effectively a no-op, since a datagram port is closed after each RPC message.The following functions and methods provide the interface to datagram socket connections.
The following macros and function allow defining remote functions, methods, and classes.
As said in Connection options, Server Classes, and Port Classes, the datagram protocol does not guarantee arrival of messages, nor does it preserve the order of messages. Under these semantics, callbacks hardly ever make sense and remote pointers are of dubious value since any manipulation of a remote pointer usually requires a call back to the home of the reference.
Therefore, the datagram interface forces the callback mode to :blocking
. Any mode arguments are ignored. Here is the description of blocking mode repeated from Callback style of stream sockets.
Image A -- thread A.i | Image B -- thread B.j | Image A -- thread A.k |
|
||
|
||
|
NOTE: This feature is only implemented in the Windows version of Allegro CL.
The following classes define the connection parameters of shared memory connections.
There is a one-to-one correspondence between server and client since each shared-memory-rpc-port-server instance can connect to only one client. The server instance must be created with the open parameter nil
, the application must extract the name of the shared memory area from the server and only then begin the wait for a client connection.
(setf server (make-rpc-server :open nil))
(setf name (rps-buffer-name server))
(rpc-open-server server)
The client image must obtain the name of the shared memory area and pass it to make-rpc-client.
(make-rpc-client :connect name-from-server-image)
The following functions and methods are used to make shared memory connections.
make-rpc-server: the local-host and local-port keyword arguments are ignored; the limit and max keyword arguments are treated as if specifed 1; the re-connect keyword argument is treated as if specified nil
.
The open keyword argument should be nil
and the call to make-rpc-server should be followed by calls to rps-buffer-name and rpc-open-server.
The buffer-size keyword argument should be a positive integer in the range (100 100000) to specify a buffer size. The default value is 1024. Larger buffers allow more efficient transfer of large data objects. Smaller buffers make more efficient use of memory if most messages are small.
The buffer-count keyword argument should be a positive integer in the range (3 30) to specify the number of buffers to use between two hosts. The default value is 5. Each rpc interaction involves several messages and each message is transmitted by closing one buffer and advancing to the next. A large number of buffers may improve performance if calls are frequently made in quick succession.
rps-buffer-name is an accessor which is defined for instances of shared-memory-rpc-port-server. It must be called after make-rpc-server has created the shared memory area for this connection.
Since the shared memory area must be unique to each connection, the name is generated with a numeric component. Names are generated until a new name is found.
rpc-open-server: Same behavior as for stream sockets.
rpc-open-listener: Undefined.
rpc-close: Same behavior as for stream sockets.
rpc-begin: Same behavior as for stream sockets.
make-rpc-client: the local-host and local-port keyword arguments are ignored, limit and max are treated as if specifed 1, re-connect is treated as if specified nil
.
The buffer-size and buffer-count keyword arguments are accepted, like with make-rpc-server. If values are specified, they must be eql to the value specified for the server. The connect keyword argument is also accepted. The value must be the string name of the shared memory area created by the server.
rpc-open-client: Same behavior as for stream sockets.
rpc-open-p: Same behavior as for stream sockets.
This interface is identical to the stream socket interface.
This interface is identical to the stream socket interface.
The callback styles available with shared memory connections are the same styles that are available with stream socket connections.
Data may be transmitted by value or by reference between two RPC applications. When data is transmitted by value, a copy of the data is sent to the recipient. Any changes made to the data at the destination do not affect the source data. When data is transmitted by reference, the recipient gets a remote reference object. Remote references are opaque objects that allow arbitrary references to be passed between hosts. Accessing remote references requires a call to the home host of the reference.
The following data types are always transmitted by value:
The following data types may be transmitted by value or by reference (the default is by value):
All other data types must be transmitted by reference.
Lisp symbols form a special case that may be transmitted by reference or by name. When a symbol is transmitted by name (the default), the symbol's name and package are sent to the recipient where a local symbol is interned. With only moderate care, it is possible to preserve eq-ness of symbols transmitted by name. When a symbol is transmitted by reference, it is represented at the receiving end by an opaque remote reference object.
The class rpc-remote-ref (with reader methods rr-home, rr-base, and rr-type) represents remote references in a Lisp image.
The operators rref and rpc-ref create explicit reference objects.
Remote references are relative to a particular connection. Consider a data item d passed from A to B on connection p as the remote reference r. If B passes r back to A on connection p, then A will see the pointer to d. Consider a second connection q between A and B, and a reference to d is passed to B on q as the remote reference s. If B passes s back to A on connection p, then B will see a remote reference, and not the pointer d.
The arguments to a remote call are passed by the following rules:
If the argument is an immediate reference, a symbol reference, or a remote reference, it is passed unchanged to the caller. (This allows the finest degree of control by the user.)
If the data can be passed by value, it is passed by value. The following data may be passed by value:
nil
If the data cannot be passed by value, it is passed as a remote reference.
Note in addition, any data allocated in memory may be passed by reference.
The operator argument in rcall or rpc-invoke may be one of the following:
A remote reference, which is assumed to be a reference to an appropriate funcallable object in the called environment, and is passed without change.
A symbol, which is passed as the symbol reference
A string, which is passed as the symbol reference
(rref t :type :function :symbol-name string)
A compiled function, which is passed as the symbol reference
(rref t :type :function :symbol-name name-of-op)
Any other argument will signal an error in the calling environment.
This section describes the type argument to rref and rpc-ref. The symbol-mode, package-mode, symbol-name, and symbol-package arguments are also described.
The type argument can be any of the following qualifiers:
Qualifier | Effect |
:copy-maybe | Create a transfer copy if possible. This is the default behavior for arguments and returned values. |
:copy | Like :copy-maybe |
:copy-only | Signal an error if the data cannot be passed by value. |
:ref-maybe | Create a remote reference if possible. For example, a float is normally passed by value, but may also be passed as a remote reference since it is a stored object in the Lisp image. |
:ref | Like :ref-maybe |
:ref-only | Signal an error if the data cannot be passed by reference. This is the case for fixnums, characters, and other immediate Lisp pointers. |
:ignore | This reference is being created in a call where the value will be ignored. Treated like :copy-maybe. |
:one-way | This reference is being created in a one-way call. Treated like :copy-maybe. |
The following qualifiers make sense only when wrapping a value explicitly with rref.
:symbol | Create a remote reference to a symbol. The data argument must be a symbol, but the symbol may be ignored if symbol-name and symbol-package keywords are passed. |
:value | Create a remote reference to the value of a symbol. |
:function | Create a remote reference to the function value of a symbol. |
:class | Create a remote reference to the class named by a symbol. |
:boolean | Create an immediate boolean value based on data. |
:null | The data argument must be nil . |
:string | The data argument must be a string. |
:int | The data argument must be coercible to an integer. |
:single | The data argument must be coercible to a single-float value. |
:double | The data argument must be coercible to a double-float value. |
:byte | The data argument must be coercible to an integer. It is truncated to a (signed-byte 8) value. |
:short | The data argument must be coercible to an integer. It is truncated to a (signed-byte 16) value. |
:char | The data argument must be a character. |
When a symbol reference must be created, the symbol-name, symbol-package, symbol-mode and package-mode arguments are used to control how the symbol is transmitted to and is found in the remote environment.
The recommended, most reliable, and case-portable way to send symbol references is with the form
(rref "ppp::nnnnn" :type <symtype>)
or the more verbose form
(rref t :type <symtype> :symbol-name "ppp::nnnnn")
where :symbol
, :function
, :class
, or :value
; ppp is a package prefix; and nnnnn is a symbol name in lower or mixed case. Upper case and mixed case symbol names should be used only to denote upper case symbols and mixed case symbols in a modern ACL.
A symbol in a remote argument list or the simple form
(rref 'symbol)
is equivalent to
(rref "ppp::ssss" :type :symbol)
where ssss is the symbol name and ppp is its package qualifier. This form will usually denote the correct symbol in the remote image if both images are in the same case mode and have similar package usage.
The following notations are retained for back compatibility, but may be deprecated and removed in the future.
(rref "xxx" :type stype . rest) == (rref t :type stype
:symbol-name "xxx"
:symbol-package "" or symbol-package
. rest)
(rref data :type stype . rest)
If symbol-name is supplied, it must be a string, and name of data is ignored. If symbol-package is supplied, it must be a package or a string or symbol.
The following tables show what is sent for symbol and package names given values of symbol-mode.
symbol-mode | What is sent for symbol-name | What is sent for symbol-package |
:absolute |
(or symbol-name (symbol-name data)) |
symbol-package | (symbol-package data) |
:string-equal-any |
(or symbol-name (symbol-name data)) |
symbol-package | "" |
:string-equal-one |
(or symbol-name (symbol-name data)) |
symbol-package | (symbol-package data) |
:read-relative |
(or symbol-name (format nil "~S" data)) |
"" |
:read-case |
symbol-name "ppp::nnn" |
symbol-package | "" |
Then at the destination:
symbol-mode is :absolute ,
:string-equal-one , or :read-case
|
|
Value of package-mode | Action |
:absolute
|
find-package of package-name or *package* at destination |
:string-equal
|
find-package of package-name with equalp or *package* at destination |
:keyword
|
keyword package |
:read-case
|
read-from-string in package |
Value of symbol-mode | Action |
:absolute
|
intern symbol-name string in package |
:string-equal-one
|
find the first equalp symbol in package or read-from-string |
:read-case
|
read-from-string in package |
Or symbol-mode is :read-relative
, in which case package-mode is ignored and the package is determined by read-from-string using package at the destination.
Or symbol-mode is :string-equal-any
, in which case package-mode is ignored and the package is determined by finding the first equalp symbol in any package or (failing that) by read-from-string using package at the destination.
The application is attempting to use a port created in the server when responding to a client connection, but the port is not connected. This port can be used only when the client is connected. The server cannot re-connect to a client that has disconnected.
This error is probably caused by a timing bug in the ACLRPC implementation. A stable port is in on of the three states :idle
, :connected
, or :closed
. There are a few short-lived intermediate states that should be visible only in certain transient situations. If you have a reproducible test case, we may be able to track down the race condition and correct it.
Many RPC functions return a first value of nil
if the operation fails. The second value is one of the following keywords to describe the reason for failure. Some functions return additional values (indicated by +) that add information about the failure.
Keyword | Meaning and additional information |
:rpc-err-open-server | ropen - cannot call on server port |
:rpc-err-server-timeout |
rpc-open-server - failed to get connection in time |
:rpc-err-not-listening |
rpc-open-server - server is not listening |
:rpc-err-accept + err |
rpc-open-server - error from socket accept |
:rpc-err-connect-action |
connect-action is not :call :process or
nil |
:rpc-err-not-string |
ropen... - home name is not a string |
:rpc-err-version + remote-vers |
ropen... - remote version mismatch |
:rpc-err-duplicate |
ropen... - home name is duplicate in remote host |
:rpc-err-not-found |
ropen...(re-connect) - home name not found |
:rpc-err-limit |
ropen... - remote host has reached connection limit |
:rpc-err-max |
ropen... - remote host saturated (temp) |
:rpc-err-home-busy |
ropen...(connect) - home is already in use (connected or waiting for re-connect) |
:rpc-err-re-connect |
ropen...(re-connect) - home is already in use (connected) |
:rpc-err-refused |
ropen...(re-connect) - refused: bad gen or remote |
:rpc-err-connect + code |
ropen or rpc-open-client...- the first byte received by the server was not one of the two expected values (:connect or :re-connect); code is the actual numeric value of the byte. |
:rpc-err-connected-error + code |
ropen...- failed to get :connected confirmation |
:rpc-err-not-client |
from rpc-open-client |
:rpc-err-not-idle |
from rpc-open-client |
:rpc-err-already-open |
from rpc-open-client |
:rpc-err-streams |
from rpc-open-client - stream not consistent |
:rpc-err-connect-failed + err |
ropen... - connect failed, err from remote host |
:rpc-err-connect-failed + rhome |
ropen... - connect failed, duplicate connect attempt |
:rpc-err-connect-failed +
sockerr |
ropen... - connect failed with sockerr |
:rpc-err-connect-failed + long |
ropen... - connect failed after n attempts |
:rpc-err-connect-failed-nopoll +
err |
ropen or rpc-open-client - the socket connection failed and no polling was requested. The err value is the error signaled by acl-socket:make-socket. |
:rpc-err-re-connect-failed + rhome |
ropen... - duplicate re-connect attempt |
:rpc-err-connerr + err |
ropen... - connect failed, err from remote host |
:rpc-err-bad-connect + code |
ropen... - protocol garbled (expected :confirm-connect or :conn-error) |
:rpc-err-begin-failed
+ begin-err... |
rpc-open-server |
:rpc-err-begin-state
+ state |
rpc-begin |
:rpc-err-begin-gate |
|
:rpc-err-begin-fail
+ state + flag |
|
:rpc-err-begin-timeout
+ port |
|
:rpc-err-req-dest |
rpc-send-request - destination thread unknown |
:rpc-err-invoke-dest |
rcall... - destination thread unknown |
:rpc-err-user + ~A |
request/invoke - error in remote application code |
:rpc-err-socket-error +
sockerr | rpc-open-server - error from socket call |
In the initial release of the Allegro CL RPC module, the following macros and functions were provided as an interface to multiprocessing gate objects (see Gates (both models) in multiprocessing.html). However, we have determined that they were not useful for that purpose. They symbols naming them have been unexported and the interface is no longer supported.
We include some examples of RPC coding in examples/aclrpc/ subdirectory of the Allegro directory. The file rpc-nameserve.cl contains a simple nameserver implemented with datagram calls.
The file rpc-inspect.cl contains an extension to the Allegro CL console inspector. The extension uses RPC calls to display the components and contents of remote data objects.
The server image is activated by evaluating (server)
and the client image is activated by evaluating (client)
.
The client function sets the variable *var*
in the server image. The variable is set to a remote reference to a list of objects in the client image.
The following annotated typescript shows how the variable *var*
is examined from the console of the server image.
cl-user(23): *var*
#<rpc-ref inspectee:pointer#161529357/1001/"cons">
cl-user(24): :i *
rpc-remote-ref @ #x20e518aa = #<rpc-ref inspectee:pointer#161529357/1001/"cons">
0 RefType ------> The symbol :pointer
1 RemoteHome ---> A simple-string (9) "inspectee"
2 RemoteType ---> A simple-string (4) "cons"
3 RemoteParts --> #<remote-field-defs @ #x20e5c02a>
As we inspect the remote reference in *var*
, the inspector extension shows the information that is available without leaving the local image. This is a design decision in the inspector extension - remote information is retrieved only on demand to avoid potentially extensive delays.
cl-user(25): :i 3
remote-field-defs @ #x20e5c1e2 = #<remote-field-defs @ #x20e5c1e2>
0 RemoteObject -> #<rpc-ref inspectee:pointer#161529357/1001/"cons">
1 Size ---------> fixnum 5 [#x00000014]
2 LookingAt ----> (0 4), a proper list with 2 elements
3 nil ----------> #<rpc-ref inspectee:pointer#161529357/1002/"myclass">
4 nil ----------> #<rpc-ref inspectee:pointer#161529357/1003/"mystruct">
5 nil ----------> #<rpc-ref inspectee:pointer#161529357/1004/"t">
6 nil ----------> #<rpc-ref inspectee:pointer#161529357/1005/"t">
7 nil ----------> (1 2 3 4 . 5), a dotted list with 4 elements
If we select the RemoteParts field, the inspector goes to the inspectee image and retrieves the top-level components of the list. Line 1 shows that we are looking at a remote object with 5 components. Line 2 shows that we are looking at components 0 through 4, all the components in this case.
cl-user(26): :i 3
rpc-remote-ref @ #x20e60a52 = #<rpc-ref inspectee:pointer#161529357/1006/"myclass">
0 RefType ------> The symbol :pointer
1 RemoteHome ---> A simple-string (9) "inspectee"
2 RemoteType ---> A simple-string (7) "myclass"
3 RemoteParts --> #<remote-field-defs @ #x20e60eca>
As we select line 3 (or component 0), we again see a remote reference.
cl-user(27): :i 3
remote-field-defs @ #x20e61042 = #<remote-field-defs @ #x20e61042>
0 RemoteObject -> #<rpc-ref inspectee:pointer#161529357/1006/"myclass">
1 Size ---------> fixnum 2 [#x00000008]
2 LookingAt ----> (0 1), a proper list with 2 elements
3 a ------------> fixnum 123 [#x000001ec]
4 b ------------> fixnum 455 [#x0000071c]
When we select the RemoteParts field, we see the slot names and the values.
cl-user(28): :i -
rpc-remote-ref @ #x20e60a52 = #<rpc-ref inspectee:pointer#161529357/1006/"myclass">
0 RefType ------> The symbol :pointer
1 RemoteHome ---> A simple-string (9) "inspectee"
2 RemoteType ---> A simple-string (7) "myclass"
3 RemoteParts --> #<remote-field-defs @ #x20e62eb2>
cl-user(29): :i -
remote-field-defs @ #x20e5c1e2 = #<remote-field-defs @ #x20e5c1e2>
0 RemoteObject -> #<rpc-ref inspectee:pointer#161529357/1001/"cons">
1 Size ---------> fixnum 5 [#x00000014]
2 LookingAt ----> (0 4), a proper list with 2 elements
3 nil ----------> #<rpc-ref inspectee:pointer#161529357/1007/"myclass">
4 nil ----------> #<rpc-ref inspectee:pointer#161529357/1008/"mystruct">
5 nil ----------> #<rpc-ref inspectee:pointer#161529357/1009/"t">
6 nil ----------> #<rpc-ref inspectee:pointer#161529357/1010/"t">
7 nil ----------> (1 2 3 4 . 5), a dotted list with 4 elements
cl-user(30): :i 4
rpc-remote-ref @ #x20e6698a = #<rpc-ref inspectee:pointer#161529357/1011/"mystruct">
0 RefType ------> The symbol :pointer
1 RemoteHome ---> A simple-string (9) "inspectee"
2 RemoteType ---> A simple-string (8) "mystruct"
3 RemoteParts --> #<remote-field-defs @ #x20e66e3a>
Returning to the initial object contents, we select line 4, or element 1 of the remote list.
cl-user(31): :i 3
remote-field-defs @ #x20e66fb2 = #<remote-field-defs @ #x20e66fb2>
0 RemoteObject -> #<rpc-ref inspectee:pointer#161529357/1011/"mystruct">
1 Size ---------> fixnum 16 [#x00000040]
2 LookingAt ----> (0 9), a proper list with 2 elements
3 a ------------> The symbol nil
4 b ------------> The symbol nil
5 c ------------> The symbol nil
6 d ------------> The symbol nil
7 e ------------> The symbol nil
8 f ------------> The symbol nil
9 g ------------> The symbol nil
10 h ------------> The symbol nil
11 i ------------> The symbol nil
12 j ------------> The symbol nil
13 MoreParts ----> #<remote-field-defs @ #x20e71622>
As we select the RemoteParts field, we see the first 10 slots of a structure with 16 slots. We can see the remaining slots by selecting the MoreParts field on line 13.
cl-user(32): :i -
rpc-remote-ref @ #x20e6698a = #<rpc-ref inspectee:pointer#161529357/1011/"mystruct">
0 RefType ------> The symbol :pointer
1 RemoteHome ---> A simple-string (9) "inspectee"
2 RemoteType ---> A simple-string (8) "mystruct"
3 RemoteParts --> #<remote-field-defs @ #x20e71842>
cl-user(33): :i -
remote-field-defs @ #x20e5c1e2 = #<remote-field-defs @ #x20e5c1e2>
0 RemoteObject -> #<rpc-ref inspectee:pointer#161529357/1001/"cons">
1 Size ---------> fixnum 5 [#x00000014]
2 LookingAt ----> (0 4), a proper list with 2 elements
3 nil ----------> #<rpc-ref inspectee:pointer#161529357/1012/"myclass">
4 nil ----------> #<rpc-ref inspectee:pointer#161529357/1013/"mystruct">
5 nil ----------> #<rpc-ref inspectee:pointer#161529357/1014/"t">
6 nil ----------> #<rpc-ref inspectee:pointer#161529357/1015/"t">
7 nil ----------> (1 2 3 4 . 5), a dotted list with 4 elements
cl-user(34): :i 5
rpc-remote-ref @ #x20e75572 = #<rpc-ref inspectee:pointer#161529357/1016/"t">
0 RefType ------> The symbol :pointer
1 RemoteHome ---> A simple-string (9) "inspectee"
2 RemoteType ---> A simple-string (14) "(array t (57))"
3 RemoteParts --> #<remote-field-defs @ #x20e75a6a>
cl-user(35): :i 3
remote-field-defs @ #x20e75be2 = #<remote-field-defs @ #x20e75be2>
0 RemoteObject -> #<rpc-ref inspectee:pointer#161529357/1016/"t">
1 Size ---------> fixnum 57 [#x000000e4]
2 LookingAt ----> (0 9), a proper list with 2 elements
3 nil ----------> #<rpc-ref inspectee:pointer#161529357/1017/"cons">
4 nil ----------> #<rpc-ref inspectee:pointer#161529357/1018/"cons">
5 nil ----------> #<rpc-ref inspectee:pointer#161529357/1019/"cons">
6 nil ----------> #<rpc-ref inspectee:pointer#161529357/1020/"cons">
7 nil ----------> #<rpc-ref inspectee:pointer#161529357/1021/"cons">
8 nil ----------> #<rpc-ref inspectee:pointer#161529357/1022/"cons">
9 nil ----------> #<rpc-ref inspectee:pointer#161529357/1023/"cons">
10 nil ----------> #<rpc-ref inspectee:pointer#161529357/1024/"cons">
11 nil ----------> #<rpc-ref inspectee:pointer#161529357/1025/"cons">
12 nil ----------> #<rpc-ref inspectee:pointer#161529357/1026/"cons">
13 MoreParts ----> #<remote-field-defs @ #x20e7cf1a>
cl-user(36): :i 13
remote-field-defs @ #x20e7d0a2 = #<remote-field-defs @ #x20e7d0a2>
0 RemoteObject -> #<rpc-ref inspectee:pointer#161529357/1016/"t">
1 Size ---------> fixnum 57 [#x000000e4]
2 LookingAt ----> (10 19), a proper list with 2 elements
3 nil ----------> #<rpc-ref inspectee:pointer#161529357/1027/"cons">
4 nil ----------> #<rpc-ref inspectee:pointer#161529357/1028/"cons">
5 nil ----------> #<rpc-ref inspectee:pointer#161529357/1029/"cons">
6 nil ----------> #<rpc-ref inspectee:pointer#161529357/1030/"cons">
7 nil ----------> #<rpc-ref inspectee:pointer#161529357/1031/"cons">
8 nil ----------> #<rpc-ref inspectee:pointer#161529357/1032/"cons">
9 nil ----------> #<rpc-ref inspectee:pointer#161529357/1033/"cons">
10 nil ----------> #<rpc-ref inspectee:pointer#161529357/1034/"cons">
11 nil ----------> #<rpc-ref inspectee:pointer#161529357/1035/"cons">
12 nil ----------> #<rpc-ref inspectee:pointer#161529357/1036/"cons">
13 MoreParts ----> #<remote-field-defs @ #x20e843ea>
cl-user(37): :i 13
remote-field-defs @ #x20e84572 = #<remote-field-defs @ #x20e84572>
0 RemoteObject -> #<rpc-ref inspectee:pointer#161529357/1016/"t">
1 Size ---------> fixnum 57 [#x000000e4]
2 LookingAt ----> (20 29), a proper list with 2 elements
3 nil ----------> #<rpc-ref inspectee:pointer#161529357/1037/"cons">
4 nil ----------> #<rpc-ref inspectee:pointer#161529357/1038/"cons">
5 nil ----------> #<rpc-ref inspectee:pointer#161529357/1039/"cons">
6 nil ----------> #<rpc-ref inspectee:pointer#161529357/1040/"cons">
7 nil ----------> #<rpc-ref inspectee:pointer#161529357/1041/"cons">
8 nil ----------> #<rpc-ref inspectee:pointer#161529357/1042/"cons">
9 nil ----------> #<rpc-ref inspectee:pointer#161529357/1043/"cons">
10 nil ----------> #<rpc-ref inspectee:pointer#161529357/1044/"cons">
11 nil ----------> #<rpc-ref inspectee:pointer#161529357/1045/"cons">
12 nil ----------> #<rpc-ref inspectee:pointer#161529357/1046/"cons">
13 MoreParts ----> #<remote-field-defs @ #x20e8b8ba>
To see the components of a very large object, we need to select the MoreParts field several times.
cl-user(38): :i 0
rpc-remote-ref @ #x20e75572 = #<rpc-ref inspectee:pointer#161529357/1016/"t">
0 RefType ------> The symbol :pointer
1 RemoteHome ---> A simple-string (9) "inspectee"
2 RemoteType ---> A simple-string (14) "(array t (57))"
3 RemoteParts --> #<remote-field-defs @ #x20e8bc82>
We can return to the initial reference of the large object by selecting the RemoteObject field on line 0. This avoids the need to climb back through multiple MoreParts steps.
Starting in Allegro CL 8.0, the aclrpc module includes a set of functions to manage a collection of communicating Allegro CL images running in separate processes, possibly on separate hosts.
It is sometimes necessary or desirable to run a collection of Allegro CL images in separate processes and/or separate processors but on a common task. The Allegro CL RPC facility is a convenient way to coordinate the activities of these processes.
The function run-other-client is called in one Allegro CL image to start another. The calling image is a control process that can communicate with any number of client images. The control image can call functions in the client images, and the client images can call functions in the control image.
Each instance of the class client-lisp describes one client image. The function client-ready-p is a predicate that tests when a client image is ready to respond to calls from the control image. The function client-funcall makes a remote funcall in a client image. The function client-exit terminates a client image. The function client-end-all terminates all client images.
In each client image, activity is initiated by a function supplied in the call to run-other-client. The client image may call the control process through the RPC port bound to *other-client-port*. The client image may be terminated from the control image, or the client image may call other-client-exit to end the client image after notifying the control image.
Copyright (c) Franz Inc. Lafayette, CA., USA. All rights reserved.
|
Allegro CL version 11.0 |