ToC DocOverview CGDoc RelNotes FAQ Index PermutedIndex
Allegro CL version 11.0

Remote Procedure Call support in Allegro CL


1.0 Introduction

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.


1.1 The package and module for RPC

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).


1.2 Security considerations

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.


2.0 A Simple RPC Example

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)

3.0 Connection options, Server Classes, and Port Classes

The following classes define the connection parameters and data transmission formats of stream socket connections:


3.1 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:


3.2 Datagram Socket Connection

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:


3.3 Shared Memory 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:


3.4 Creating new RPC port and server classes

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.


3.5 Security mixins and subclasses

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.


4.0 The Lisp RPC Programming Interface

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.


4.1 Stream socket connectionsStream socket connections

In the subsections of this section, we describe the Lisp API for Stream Socket connections.


4.1.1 Connecting stream sockets

The following classes define the connection parameters of stream socket connections.

The functions associated with connecting are:


4.1.2 Explicit port interface of stream sockets

The following operators are used once the port is established.

The variable *rpc-port* is also useful.


4.1.3 Declarative interface of stream sockets


4.1.4 Callback style of stream sockets

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
  • A begins thread A.i and
  • calls function g in B for the first time
  • B responds in (new) thread B.j
  • B.j calls function h in A
  • Call is handled in A.i on top of suspended call chain
  • h in A.i calls function m in B
  • Call is handled in B.j on top of suspended call chain
  • When m completes, B.j resumes wait for h to complete in A
  • When h completes, A.i goes back to wait mode
  • control returns to waiting B.j
  • When g completes, B.j terminates
  • and control returns to A.i
  • with nothing left unfinished.

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
  • A begins thread A.i and
  • calls function g in B for the first time
  • B responds in (new) thread B.j
  • B.j calls function h in A
  • Call is handled in (new) thread A.k
  • h in A.k calls function m in B
  • Call is handled in (new) thread B.l
  • When m completes, B.l terminates (or gets recycled)
  • When h completes, A.k terminates (or gets recycled)
  • control returns to waiting B.j
  • When g completes, B.j terminates
  • and control returns to A.i
  • with nothing left unfinished.

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
  • A begins thread A.i and
  • calls function g in B for the first time
  • B responds in (new) thread B.j
  • B.j calls function h in A
  • Call is handled in (new) thread A.k
  • A.k tries to call function m in B
  • An error is signaled in A.k because the path from A to B is still busy with the initial call from A.i to h in B.j

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.


4.2 Datagram socket connections

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.


4.2.1 Connecting datagram sockets

The following classes define the connection parameters of datagram connections.

The following functions and methods are used for Datagram socket connecting.


4.2.2 Explicit port interface of datagram socket connections

The following functions and methods provide the interface to datagram socket connections.


4.2.3 Declarative interface of datagram socket connections

The following macros and function allow defining remote functions, methods, and classes.


4.2.4 Callback style of datagram socket connections

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
  • A begins thread A.i and
  • calls function g in B for the first time
  • B responds in (new) thread B.j
  • B.j calls function h in A
  • Call is handled in (new) thread A.k
  • A.k tries to call function m in B
  • An error is signaled in A.k because the path from A to B is still busy with the initial call from A.i to h in B.j

4.3 Shared Memory Connections (Windows Only)

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)

4.3.1 Making a Shared Memory Connection

The following functions and methods are used to make shared memory connections.


4.3.2 Explicit port interface of shared memory connections

This interface is identical to the stream socket interface.


4.3.3 Declarative interface of shared memory connections

This interface is identical to the stream socket interface.


4.3.4 Callback style of shared memory connections

The callback styles available with shared memory connections are the same styles that are available with stream socket connections.


5.0 Data Representation

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.


5.1 Uniqueness of Remote References

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.


5.2 Argument Conversions

The arguments to a remote call are passed by the following rules:

Note in addition, any data allocated in memory may be passed by reference.


5.3 Operator Conversion

The operator argument in rcall or rpc-invoke may be one of the following:

Any other argument will signal an error in the calling environment.


5.4 Data Type Qualifiers

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 is one of the keywords :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.


6.0 Error Messages and Tags


6.1 Error messages in simple-error instances

Attempting to use inappropriate rpc port xxx

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.

Attempting to use rpc port in odd state xxx

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.


6.2 Error keywords returned as the second value

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

7.0 Gates

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.


8.0 Examples

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.


9.0 Running several communicating Allegro CL images

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.


10.0 Index of RPC operators, classes, and variables


Copyright (c) Franz Inc. Lafayette, CA., USA. All rights reserved.

ToC DocOverview CGDoc RelNotes FAQ Index PermutedIndex
Allegro CL version 11.0