The EVAL Server -- A PUP Network Inter-Communications Facility for
                            Interlisp-D

File:		<Lispusers>EvalServer.press
Revised: 	Feb 21, 1983 and Feb 17, 1984, by JonL White

	The file EVALSERVER.DCOM contains routines to facilitate
communication, over the EtherNet, between two or more D-machines
running Interlisp-D.  In simple usage, one or more D-machines
(Dolphin, Dorado, or Dandelion) are started up with "servers"
running, and a "client" of the service  merely requests (via the
ethernet) that a server EVALuate some form, and return the results.
 A user may direct his "remote" evaluation request to a specific
server, or may broadcast it on his directly-connected network; in
the latter case, at most one of the servers will be given the
go-ahead to perform the service, but possibly in the future there
will be an extension to permit results from many diverse servers of 
a broadcast request.  (Likely a request for service will be coupled 
with a function to be applied to any results that come back).  


                   Starting up the "server" side

The function call 
    (EVALSERVER <duration.minutes> <clientslst> <gaggedclientslst>)
will initiate a background process named  "EvalServer.Listening"
All arguments are optional, and have the following meaning:
  <duration.minutes>  --  if non-null, will run the service for
    only the number of minutes specified; otherwise it will run
    indefinitely.
  <clientslst> - -  a list of ether host names (or numbers) whom
    the server is willing to service:  if T is on this list, it
    will service any request addressed to this specific host
    (called, "flirtatious" mode), if NIL is on the list, it will
    service any request broadcast on the connected network (called,
    "promiscuous" mode).   Default for this list, NIL, is converted
    into the union of "flirtatious" and "promiscuous" modes.
  <gaggedclientslst> -- a list of hosts for whom service will not
    be performed; NIL on this list means no service for broadcast
    requests; T on this list means no service for "myself" (i.e.
    attempts by some process running concurrently on the server
    host to send a "remote" evaluation request to the same host,
    will be rejected).
In any case, the global variables EvalServerClientHosts and
EvalServerGaggedHosts will be dynamically consulted for the same
information; this is so that you may tailor the client screening
process while the server is running.  The "gags" always have
precedence over the "hosts" lists.


While the server is running, a call to
  (EVALSERVER.ABORT <transaction> <guiltyparty> <errorflg>)
will stop a currently running process, such as one that is in a
loop, or that is "poaching" more time off the server than is
desireable; see below under EVALSERVER.STATUS for a definition
of the identification terms.
  <transaction> may be either an integer, the "identification"
    number of some service, or a cons of the id number and the
    host; the "cons" form may be required if there are two or more
    services, from separate requesting hosts, with co-incidentally
    the same id number.
  <guiltyparty> permits recording why a service was stopped; it is
    optional, and the default recording is "Aborted Locally by
    Error/Quit". 
  <errorflg> is optional, and if non-NIL, will cause an ERROR to
    occur if there is no transaction as specified by <transaction>
    or of the transaction seems to be wedged.


While the server is running, a call
  (EVALSERVER.STATUS <wherependingflg> <id>)
will return a list of information associated with the state of
currently-running services, already-completed services, and
requests still in the input queue.  A "service" is identified by a
cons of a transaction number and a host number;  the transaction
number is in fact the  packet id in the PUP used to communicate
over the EtherNet (a similar idea will eventually be used  when the
server is implemented in NS protocols).
   <wherependingflg> -- selects one or more of the information
       lists to be output, as follows: 
       DONE (or COMPLETED or FINISHED) adds in the remnants of the
         list of completed services (which list is pruned down from
         time to time by the "cleanup" process mentioned above.   A
         global variable, \ES.PURGEINTERVAL.SECS, controls the
         frequency of the "cleanup" actions, and also the maximum
         time for which old completed service records are kept); 
       RUNNING (or CURRENT) adds in the data for currently executing
         evaluations requests; 
       INPUT (or INPUTQUEUE) adds in the data for PUP's still waiting
         for service.   
       T  acts like the union of DONE and RUNNING; 
       ALL acts like a union of all three of DONE, RUNNING, and
         INPUT;  
       NIL, and no argument,  default to same as ALL.
   <id> --  if non-null, then only records with that
        identification number will be included in the result. 
Similarly, a call
  (EVALSERVER.STATUS.WINDOW <optional-region>)
will put up a window which continuously monitors and displays
the above information.   The middle mouse button is active in this
status window to delete the various items;  deleting a RUNNING or
INPUT request is the same as calling EVALSERVER.ABORT on it.


A trace facility may be enabled/disabled by a function call:
  (EVALSERVER.TRACE <flg> <region>)
where
  <flg>, if non-null, enables traceing; disables if null.
  <region>, if non-null, should be a "region" and marks a region
    for the trace window; if null, the user is prompted to "mouse"
    a region.
Both the left and m idle mouse buttons are "active" in the trace
window: LEFT toggles a "flg" which turns traceing on or off, and
MIDDLE does  a quick clear of the trace window.




              The Client: Using a remote Eval Server


The basic use of EVAL at a remote host is invoked by:
  (REMOTEVAL <form> <serverhost> <multiple.responses?>)
The S-expression <form> is shipped via the ether net to the host
<serverhost>, where it is EVALuated by an EvalServer and shipped
back  If <serverhost> is NIL or 0, then the request is not directed
to a partricular server, but is merely broadcast on the network; if
any server is willing to service such a request, then it will
"handshake" with the requestor and do so.  If the remote evaluation
causes an error, then that error will be "brought back" locally,
and an error will result which will incorporate the remote message
(of course, the stack and environment of the remote host is not
"brought back").  A current limitation is that the PRIN4 form of <form>, 
and that of the result, must fit within one PUP -- about 530 bytes.  
	
The argument <multiple.responses?> can currently be only either 0
or 1 (NIL defaults to 1); if 0, then the REMOTEVAL function will
not wait around for the result from the remote host, but will
return as soon as there has been an acknowledgement that the
service is being performed; its value in this case will be the
identifier number use for that transaction.  This is especially
useful when invoking a lengthy task which is done primarily for
"effect" rather than value.   Also it may be usefule when "broadcasting"
some evaluation which will later directly send a note back to the
initiator; thus there is no need to go through the several "handshake"
packets, and general "broadcast" time-consuming protocols.

Also,
   (REMOTEAPPLY <fun> <arglist> <serverhost> <multiple.responses?>)
invokes a similar remote use of APPLY (as opposed to EVAL)


Perhaps one may want to see how some server is progressing on its
tasks:  
  (REMOTEVAL '(EVALSERVER.STATUS 'ALL) <server>) 
will get the list documented above.   If one decides to cancel some
request, then 
  (REMOTEABORT <transaction> <serverhost>) 
provides a convenient entry into the EVALSERVER.ABORT function at
the remote host.  <transaction> is either an id number, or a cons,
as described above for EVALSERVER.ABORT; however, if only the id
number is given, it is cons'd with the local host number before
sending to the server.

If a lengthy computation is initiated on a server which is not
running multiple processes, it would be a good idea for it to check
the input queue from time to time, say by  (EVALSERVER.STATUS
'INPUT), and explicity call (EVALSERVER 1)when there are waiting
requests; for it may be that one of these waiting requests is an
abort for the current process.   Consider the example below, where
each time through the PROG loop (which is being sent for remote
evaluation) there is a check for possible inputs, and a short call
to EVALSERVER from that code itself, to insure that  subsequent
calls will have a chance to run.

   74←(REMOTEVAL 
        '(PROG ((CNT 0)) 
            LP (DISMISS 10000) 
               (add CNT 1) 
               (AND (EVALSERVER.STATUS (QUOTE INPUT)) 
                    (EVALSERVER 1))
               (GO LP))
        'PLAZA 
        0)
   55
   
   75←(REMOTEVAL '(EVALSERVER.STATUS 'ALL) 'PLAZA)
   ((Completed.Transactions: 
       ((ID#.ClientHost: 54 BuickoSaurus) 
          (HowStop.#Seconds: COMPLETED 127)))
    (CurrentlyRunning.Transactions: 
       ((ID#.ClientHost: 56 BuickoSaurus) 
          (Process: RUNNING)) 
       ((ID#.ClientHost: 55 BuickoSaurus) 
          (Process: RUNNING))))
   
   77←(REMOTEABORT 55 ' PLAZA)
   ABORTED

Note that the  transaction id for this "lengthy" process was
returned by the call to REMOTEVAL, since we requested no waiting
around for the result (third arg of 0).  Then when the remote
EVALSERVER.STATUS was done, both that transaction, and the one
actually causing the evalserver status to be read, show up as ID's
55 and 56.  A subsequent remote abort for  number 55 will stop it,
and leave a record that it was remotely aborted.   Of course, this
is not necessary if the EVALSERVER is running under the
PROCESSWORLD.



                                 
                   Some Implementation Details:

The semi-well-known socket number 668 is used for receiving eval
service requests.