\documentstyle[12pt]{article}
\large
\newlength{\pfspace}
\renewcommand{\baselinestretch}{1.0}   % 1.0 spacing
\setlength{\pfspace}{\baselineskip}

\title {CL-PVM: A Common Lisp Interface to PVM}
\author {Liwei Li\thanks{{\tt lil@mcs.kent.edu  pwang@mcs.kent.edu}} \ and Paul S. Wang\thanks{Work reported herein has been supported in part by the National Science Foundation under Grant CCR-9503650}\\
Department of Mathematics and Computer Science\\
Kent State University}
\input{psfig}  %% get  /usr/local/lib/tex/inputs/psfig.tex
%\figurespagefalse  
%\tablespagefalse 
\makeindex
\begin{document}
\maketitle
%\tableofcontents
%\listoffigures
\bibliographystyle{plain}
\begin{abstract}
CL-PVM is a set of Common Lisp functions that interfaces
Common Lisp (KCL, AKCL, or GCL) to the C-based library
of PVM.  Generally, there is one CL interface function to each
PVM C library function.  The CL function calls its C-based counter part
and relays data to and from the C function.
This interface is complete and allows Lisp-based programs
to take part in a PVM arrangement and thus facilitates the combination of
symbolic, numeric, graphics, and other useful systems in a distributed
fashion.
The design, implementation, and usage of
the CL-to-PVM interface are explained and examples are given. 
\end{abstract}

\section{Introduction}

{\em Parallel Virtual Machine} (PVM)
is a software package that integrates a heterogeneous network of computers
and workstations to form 
a single parallel/concurrent computing facility\cite{pvm3}.
PVM consists of two parts:  a run-time server and a set of library functions.
The PVM server manages the virtual parallel machine made up by a select set 
of host machines.  The configuration is recorded in a {\em hostfile}
and can be altered dynamically.  The server also enables user control of the
virtual machine from any component host and allows initiation of
tasks anywhere on the virtual machine.

A PVM task uses the PVM library functions to interact with other
tasks: sending and receiving messages, initiating subtasks,
detecting errors, etc.  The PVM version 3.0 library is written 
in C allowing direct calls from C programs.
There is also a Fortran 77 interface to give
F77 programs access to the PVM library.

CL-PVM provides a Common Lisp interface to enable
Lisp-based programs to partake in PVM applications.
A wide variety of useful Lisp programs exists including
symbolic computation systems, expert systems, artificial intelligence
systems, knowledge-based systems, and many more.

With CL-PVM,
a Lisp user can invoke the library routines directly from the Lisp toplevel
or from Lisp programs.
Figure 1 shows a sample PVM with three components:
\begin{itemize}
\item \verb?Monkey?: a Sun4 workstation
\item \verb?Condor?: an HP 9000/730 
\item \verb?Nimitz?: a Sun 4/670
\end{itemize}
Each PVM host has a
{\em PVM daemon} process to support the control and operation of the
virtual machine.  Distributed tasks on the PVM utilize PVM protocols to
interact with one another.  The C-based PVM library supports the
protocols. Lisp or Fortran processes access
these C routines through appropriate interface functions.
%%%% figure 1 %%%%%
\begin{figure}[tbh]
\centerline{\psfig{figure=intro.ps,height=4.2in,width=3.5in}}
\caption{PVM with three machines: {\tt Monkey}, {\tt Condor} and {\tt Nimitz}}
\label{f1}
\end{figure}

This document describes
the design, implementation, and usage of the CL\footnote{In this document, 
the wording Common Lisp (CL) refers to UNIX-based KCL, AKCL, or GCL.}
interface to PVM.
The appendix contains a {\it User's Guide}.

\section{Implementation Strategy}

\subsection{Accessing C Codes from CL}

When a CL program is compiled, an intermediate C file is generated.
This C file is then compiled by the C compiler to produce the object code\cite{akcl}.  Because of this arrangement, CL allows embedded C codes
inside Lisp functions or files as {\it literal} lines.
These lines are given as Lisp string constants and become part of the
intermediate C file when the Lisp file is compiled.
CL provides a set of mechanisms including \verb?CLINES?, \verb?DEFCFUN?, and \verb?DEFENTRY? for interfacing to C functions \cite{akcl}:
\begin{itemize}
\item CLINES \\
Syntax: {\tt (clines {string}*)}\\
KCL specific:  
The KCL compiler embeds \verb?string?s into the intermediate C
language code.  The interpreter ignores this form.
\item DEFCFUN \\
Syntax: {\tt (defcfun header n {element}*)}\\
KCL specific: 
Defines a C-language function which calls Lisp functions and/or
handles Lisp objects.  \verb?header? gives the header of the C function 
as a string. N is the number of the main stack entries used by 
the C function, primarily for protecting Lisp objects from being
garbage-collected.  Each \verb?element? may give a C code fragment
as a string, or it may be a list:
\begin{center}
{\tt ((symbol {arg}*) {place}*)}
\end{center}
which, when executed, calls the Lisp function named by \verb?symbol? 
with the specified arguments and saves the value(s) to the
specified places. The DEFCFUN form has the above meanings only 
after compiled;  The KCL interpreter simply ignores this form.
\item DEFENTRY\\ 
Syntax: \verb?(DEFENTRY name arg-types c-function)?\\
The compiler defines a Lisp function whose body consists of a
calling sequence to the C language function specified by 
\verb?c-function?.  The interpreter ignores this form.  The \verb?arg-types?
specifies the C types of the arguments which \verb?c-function? requires.
The list of allowed types is ({\tt object char int float double string}).
Code will be produced to coerce from a Lisp object to the
appropriate before passing the argument to the \verb?c-function?. The 
\verb?c-function? should be of the form {\tt(c-result-type c-fname)}
where the \verb?c-result-type? is
a member of ({\tt void object char int float double string}). The \verb?c-fname? 
may be a symbol (in which case it will be made all lower case) or a string. If
\verb?c-function? is not a list, then {\tt (object c-function)} is assumed.
\end{itemize}

With these utilities, functions with mixed C and Lisp codes can be written to 
interface Lisp and C.\\
\\
\subsection{Interfacing {\tt pvm\_spawn}}

To illustrate the interface method, the
interface to the PVM library function \verb?pvm_spawn? is presented
as an example.  The \verb?%? is a Lisp character macro that quotes
a line as a string.  The \verb?;? is used for comment lines.
\begin{verbatim}
;; Interface to the PVM routine:
;;   int ntask = pvm_spawn( char *task, char **argv, int flag,
;;                         char *where, int ntask, int *tids )
;;   starts new PVM processes                               
;;
;;   tids is a return value -- the list of task ids spawn
;;
(defCfun "int call_spawn(tk, av, flag, we, ntask, tid)
   object tk, av, we, tid; int flag, ntask;" 1
;; tk, av and we are Lisp stings
;; fg and nk are Lisp fixnums
;; return value of call_spawn is a list of task ids
%  char *task, *args[20], *where, *tmp, *avs;
%  int i, *tids;
%  char **argv = NULL;
%  object s;
;; 
;;   Interfacing CL string.
;;
%    task=(char *)calloc(((tk->st).st_dim)+1, sizeof(char));
%    strncpy(task, (tk->st).st_self, (tk->st).st_dim);
%    task[(tk->st).st_dim]='\0';
;;
;;   Interfacing CL string.
;;
%    where=(char *)calloc(((we->st).st_dim)+1, sizeof(char));
%    strncpy(where, we->st.st_self, we->st.st_dim);
%    where[we->st.st_dim]='\0';
;;
;;   Interfacing CL string that contains multiple arguments 
;;   separated by blank or tab.
;;
%    avs   =   (av->st).st_self;
%    i     =   (av->st).st_dim;
%    if ( i > 0 )
%    {   argv = args;
%        tmp = (char *)calloc(i+1, sizeof(char));
%        strncpy(tmp, avs,i);
%        tmp[i]='\0';
%    /* fill in argv */
%        argv[0] = strtok(tmp, " \t");
%        for ( i=1; i < 20 && argv[i-1] != NULL; i++ )
%            argv[i] = strtok(NULL, " \t");
%    /* Refinement: check for too many args */
%    }
;;
;;   Allocates C-integer array for the task id information.
;;
%    tids = (int *)calloc(ntask, sizeof(int));
;;
;;   Call the PVM-C library routine.
;;
%    ntask=pvm_spawn(task, argv, flag, where, ntask, tids);
;;
;;   Allocates Lisp array for the tids and fill it with the
;;   content of C-array tids.
;;
     (make-array (int ntask)) "vs[0]")
%    for ( i=ntask-1; i >= 0; i-- )
%    {
        (fillnum "vs[0]" (int tids[i]) (int i))
%    }
;;
;;   Avoid tid to be accidentally garbage collected.
;;
     (set tid "vs[0]")
%    Creturn(ntask);
)

(defentry pvm-spawn(object object int object int object) (int call_spawn))

\end{verbatim}

As declared by the \verb?DEFENTRY?, 
the CL-PVM function \verb?pvm-spawn?
receives six arguments in a Lisp function call and passes them
to the interface function \verb?call_spawn?.
The CL-PVM documentation specifies the data types of each of the
arguments as: 
\begin{itemize}
\item \verb?tk? --- a Lisp string
\item \verb?av? --- a Lisp string containing multiple parts separated by {\sc space} or {\sc tab}
\item \verb?flag? --- a Lisp integer
\item \verb?we? --- a Lisp string
\item \verb?ntask? --- a Lisp integer
\item \verb?tid? --- a Lisp {\em object} that is used to receive a Lisp array
of integer processes ids returned by \verb?call_spawn?
\end{itemize}
The major tasks of an interface function like \verb?call_spwan? are
to convert incoming arguments from Lisp types to C types and to
convert return values from C types to Lisp types.
The four data types \verb?integer?, \verb?character?, \verb?float?, and \verb?double? are converted automatically between the C and CL.
Little effort is required to treat these four data types.
On the other hand, the general Lisp \verb?object? type 
requires careful treatment.  The \verb?object? type can represent any
Lisp {\em s-expression} and encapsulates many different types.
At the C level, a CL \verb?object? is a \verb?union?:
\begin{verbatim}
union lispunion 
{
  struct fixnum_struct FIX;
  struct shortfloat_struct SF;
  struct longfloat_struct LF;
  struct character ch;
  struct symbol   s;
  struct cons     c;
  struct array    a;
  struct vector   v;
  struct string   st;
  struct ustring  ust;
  struct bitvector bv;
  struct structure str;
  struct cfun     cf;
  struct cclosure cc;
  struct dummy    d;
  struct fixarray fixa;
  struct sfarray  sfa;
  struct lfarray  lfa;
};
\end{verbatim}

This C union contains the complete CL data types where each has its unique structure.
Data types in this union are enumerated so that the correct computation and manipulation
can be performed upon the appropriate instance of the union object.  For instance,  
in the CL \verb?pvm-spawn? the first object is
a CL simple string that specifies the name of the task to be spawned. The second object
is another CL string that supplies the required arguments for the task. The arguments 
are delimited by space or tab within the CL string.  The next CL object indicates some 
particular host machine on which the task has to be spawned. It is a CL simple string.
The last object is a CL object which is bound to a Lisp array
filled when the call to pvm-spawn returns.
Without loss of generality,
we assume that Lisp programmers call \verb?pvm-spawn? in the following format:
\begin{verbatim}
(pvm-spawn "Slave" "1 2 3 4" 1 "monkey.mcs.kent.edu" 2 'status)
\end{verbatim}
We must split the Lisp string \verb?"1 2 3 4"? into four parts.
Assuming a blank or tab is the delimiter, we create a temporary C type array of strings \verb?
tmp?
to hold the four parameter values to the task. In order to obtain the substring values of the Lisp object \verb?task,argv,where? a reference
is made to the Lisp structures in C (see Appendix A):
\begin{verbatim}
/usr/local/include/cmpinclude.h.
\end{verbatim}
Since\verb?object? is a union, address references are necessary in order to reach the actual beginning
of the substring.  The temporary integer array \verb?status? is utilized to hold the returning
status values of each adding action. After the call returns, the content of \verb?status? is copied into Lisp 
\verb?array?. To prevent a Lisp object from being unexpectedly garbage collected, we save the
object on stack \verb?vs[0]? which is recognized by the garbage collector\cite{akcl}. The second parameter
\verb?1? at the end of the \verb?DEFCFUN call_spawn? is used to reserve \verb?1? such place for each call to the C function. Figure 3 illustrates the general structure of a \verb?DEFCFUN? function.
Such CL functions can be compiled by the Lisp compiler
to form part of a Lisp system or be
loaded dynamically into an existing Lisp system.
Figure 2 illustrates this approach. 
The PVM C library consists of two parts: \verb?libpvm3.a? includes the regular routines and \verb?libgpvm3.a?
includes the PVM group communication and management functions.
%%%% figure 2 %%%%%
\begin{figure}[tbh]
\centerline{\psfig{figure=inter.ps,height=4.2in,width=4.2in}}
\caption{Lisp programs access PVM C-libs through interface library}
\label{f2}
\end{figure}

\verb?DEFENTRY? defines an entry for a C function in the Lisp environment. Implicit
data conversion is assumed. C-type allowed in Lisp interface are \verb?int? \verb?char?,
\verb?float?, \verb?double?, \verb?object?, \verb?void?.  It is sufficient to use \verb?DEFENTRY?
to handle the primary data types such as int,float,etc.  More complex data types require
extra handling before and after the entry point.  The CL \verb?pvm-spawn? utilizes another
CL macro that allows the co-existences of C and CL-like code.
The \verb?DEFCFUN? facility allows us to 
write a mixed C-Lisp function and pass data back and forth between the two environments.  

\subsection{Interfacing {\tt pvm\_packf}}

The general form of \verb?DEFCFUN? is 
\begin{verbatim}
(DEFCFUN "type cfn_name(args) arg-type-list;" n
     statement1
     statement2
     ...
)
\end{verbatim}
The statements can be either in C (in double quotes) or in the special form of Lisp as explained in
Section 2.1.  A complicated CL-PVM function requires the usage of the other CL-PVM routines.
We now show how to use \verb?DEFCFUN? and other CL-PVM functions
to interface the PVM library routine \verb?pvm-packf?\cite{pvm3}. Before the implementation
our primary focus is on the design of the CL-PVM interface. For instance
PVM-C \verb?pvm_packf? receives a action-description string and then parse the variable length
argument list to invoke the proper data packing functions.  After examine fortran interface
carefully, we decide to design our CL interface according to the following guidelines:
\begin{itemize}
\item One-to-One interface to  PVM-C function if feasible in CL environment.
\item Maintain the PVM-C function prototype as close as possible.
\end{itemize}
The CL-PVM \verb?pvm-packf? is an example where minimum alteration 
is necessary when PVM-C \verb?
pvm_packf? is interfaced.  The \verb?packf? function has been re-coded in Lisp according
to its C implementation. In C program, programmers invokes the function in the following fashion:
\begin{verbatim}
  double darry[4];
  int count, *iarry;
  ...
  info = pvm_initsend( PvmDataDefault );
  pvm_packf("%+ %d %*d %4lf", PvmDataRaw, count, count, iarry, darry);
  info = pvm_send(tid, msgtag );
\end{verbatim}
The \verb?packf? function sends data in PvmDataRaw mode. It packs the integer \verb?count?. When the 
`*' is parsed it reads in the second \verb?count? as the number item to be packed for the 
following
integer array. Finally, it packs four doubles into the buffer. The CL-PVM \verb?packf? function
behaves in the same way.  In CL a list object is received as the argument for \verb?pvm-packf?
. The first element of the list is treated as the description string and parsed for actions
to be taken for the rest of the list. A typical usage is given below: 
\begin{verbatim}
  (pvm-initsend 0)
  (pvm-packf 
   '("%+ %d %*d %4lf" PvmDataRaw 4 4 '(1 2 3 4) '(2.3 4.5 6.7 8.9)))
  (pvm-send tid msgtag) 
\end{verbatim}
The CL-PVM \verb?pvm-packf? implementation is given below.
 Some portions of the code are omitted to save space.
\begin{verbatim}
(Clines
%#include <string.h>
%#include <ctype.h>
)
;;
;; int info = pvm_packf( const char *fmt, ... )
;;
(defCfun "int call_packf(lst)
    object lst; " 0
;;
;;  Variable declaration section omitted.
;;
;;  Check for the length of the input list
;;
    ((list-length lst) lstlen)
%   parac=fix(lstlen);
;;
;;  Interface the format string if parameter
;;  list is not empty.
;;
%   if (parac > 0)
%   {
      ((car lst)  first)
      ((cdr lst) rest)
%     tmp=(first->st).st_self;
%     sz=(first->st).st_dim;
%     if (sz > 0)
%     { fmt=(char *)calloc(sz+1, sizeof(char));
%       strncpy(fmt, tmp, sz);
%       fmt[sz]='\0';
%       argpt=fmt;
%     }
%   }
;;
;;  Retrieve the initsend parameter and call
;;  pvm-initsend and pack the format string
;;  into the buffer.
;;
%   if (argpt[0]=='%' && argpt[1]=='+')
%   {
      ((car rest) car_arg)
      ((cdr rest) rest)
%     inits=fix(car_arg);
      (pvm-initsend (int inits))
%     argpt +=2;
%   }
    (pvm-pkstr first)
;;
;;  Parse the format string to retrieve the
;;  correspondent data type to pack into
;;  the buffer.
;;
%   while (*argpt != '\0')
%   {
;;
;;   If '%' is seen parse the following
;;   information;
;;
%    if (*argpt++=='%')
%    { intcnt=1;
%      intstd=1;
%      isv=1;
;;
;;     Extract the proper information off
;;     the head of the list if '*' is
;;     encountered.
;;     Extract the number if digit is
;;     encountered.
;;
%      if (*argpt=='*')
%      {
         ((car rest) car_arg)
         ((cdr rest) rest)
%        intcnt=fix(car_arg);
%        isv=0;
%        argpt++;
%      }
%      else if (isdigit(*argpt))
%      {   intcnt=atoi(argpt);
%          isv=0;
%          while(isdigit(*++argpt));
%      }
;;
;;     Extract the proper information off
;;     the head of the list if '.' is
;;     encountered.
;;     Extract the number if digit is
;;     encountered.
%      if (*argpt=='.')
%      { isv=0;
%        if (*++argpt='*')
%        {
           ((car rest) car_arg)
           ((cdr rest) rest)
%          intstd=fix(car_arg);
%          argpt++;
%        }
%        else if (isdigit(*argpt))
%        {
%            intstd=atoi(argpt);
%            while(isdigit(*++argpt));
%        }
%      }
;;
;;     initialize the proper variable.
;;
%      for (cc =1; cc;)
%      { switch (*argpt++)
%         { case 'h':
%               vh = 1; break;
%           case 'l':
%               vl=1;   break;
%           case 'u':
%               vu = 1; break;
%           default:
%               argpt--;
%               cc=0;
%         }
%      }
       ((car rest) lar_arg)
       ((lst2arr lar_arg) car_arg)
       ((cdr rest) rest)
;;     Extract proper information
;;     according to the data type
;;     flag.
;;
%      switch(*argpt++)
%      { case 'c':
          (pvm-pkbyte car_arg (int intcnt) (int intstd))
%            break;
%        case 'd':
%         if (vl)
            (pvm-pklong car_arg (int intcnt) (int intstd))
%         else if (vh)
            (pvm-pkshort car_arg (int intcnt) (int intstd))
%         else
            (pvm-pkint car_arg (int intcnt) (int intstd))
%         break;
%        case 'f':
%         if (vl)
            (pvm-pkdouble car_arg (int intcnt) (int intstd))
%         else
            (pvm-pkfloat car_arg (int intcnt) (int intstd))
%         break;
%        case 'x':
%         if (vl)
            (pvm-pkdcplx car_arg (int intcnt) (int intstd))
%         else
            (pvm-pkcplx car_arg (int intcnt) (int intstd))
%            break;
%        case 's':
          (pvm-pkstr car_arg)
%      }
%    }
%  }
)
\end{verbatim}
%%%% figure 3 %%%%%
\begin{figure}[tbh]
\centerline{\psfig{figure=face1.ps,height=4.0in,width=4.5in}}
\caption{Data type conversion contains two parts: Lisp set of data type maps to C set of data type and
C set of data type maps to Lisp set of data type.}
\label{f3}
\end{figure}

Data type conversion is the key issue. From a mathematician's point of view, we must define a set of functions
which maps from the Lisp set of data types to C set of data types\cite{C} and moreover, whose inverse function maps
from C set of data types to Lisp set of data types. Figure 3 also illustrates this point. 
Only a portion of C data types are used in PVM C routines, thus the conversion function are
determined based on the involving C data types.  Figure 4 shows the formation of the conversion
functions. We are not using the word {\it function} according to its strict mathematical definition.
When an arrow is pointed to a dotted box instead of a specific data type, it implies that such
data type maps to all the data types within the box. 
%%%% figure 4 %%%%%
\begin{figure}[tbh]
\centerline{\psfig{figure=face2.ps,height=4.2in,width=4.5in}}
\caption{The definition of "Lisp to C" and "C to Lisp" mapping function}
\label{f4}
\end{figure}
Appendix B provides a list of the PVM C function headers and Appendix C contains the Lisp interface
headers.

\section{Testing the CL-PVM}

The Lisp interface routines to PVM can be invoked interactively from
the Lisp toplevel. This feature also provides an easy way to test the CL-PVM.

Whenever a user invokes Lisp from the system prompt, at the end of the Lisp starting up program it
checks and see if the file \verb?init.lsp? exists at the current directory. If so, it loads it into
the current Lisp environment. This provides us a way of customization.  We can integrate the 
MAXIMA commands with initial Lisp environment and make it function as a symbolic computational
utility.  One can also initialize it with PVM C routines and their Lisp interface counterparts to
attach the PVM message passing capability to it. A sample \verb?init.lsp? is given below:
\begin{verbatim}
;;;;;;;;;;;;;;;; init.lsp  ;;;;;;;;;;;;;;;;;;;;;
(si::faslink "~/pvm3/SUN4/clpvm.o" 
	     "/vol/pvm/lib/SUN4/libpvm3.a -lc")
\end{verbatim}
\verb?Si::faslink? loads both the \verb?libpvm3.a? library routines and their Lisp interfaces into
the Lisp environment. The \verb?clpvm.o? file is the resulting object file by compiling \verb?clpvm.lsp? with \verb?lc? compiler.

When the setup is complete, one can perform the following simple test.  We begin
with a PVM session on the machine {\it Monkey}. Next, start the Lisp engine. 
CL-PVM routines are loaded with the {\tt init.lsp} into the Lisp environment 
as explained in the previous paragraph.  To construct a simple PVM, we add
two more hosts: {\it Condor} and {\it Nimitz} to our PVM.  {\it Condor} is an HP machine 
and {Nimitz} is a Sun processor. Figure 5 shows in detail the input and the output of \verb?pvm-addhosts?.\\ 
%%%% figure 5 %%%%%
\begin{figure}[tbh]
\centerline{\hbox{\psfig{figure=monkey.ps,height=4.2in,width=4.5in}}}
\caption{Invoking PVM routines interactively from host Monkey}
\label{f5}
\end{figure}

Once the other two hosts are joined, we start the Lisp on everyone of them. We
view the PVM configuration by invoking \verb?pvm-config? on each host. The variables 
\verb?a, b, c? are hosting variables that bring back useful information after
the call.  After the initial checkup we interactively performed a multi-casting from {\it Monkey} to {\it Nimitz} and {\it Condor} by sending a message "
Hello Condor and Nimitz from Monkey" to the other two hosts. Figure 6 and Figure 7
capture the interaction on {\it Nimitz} and {\it Condor} respectively.  The interacting capability of the Lisp environment actually provides us a convenient way of
testing and debugging our interface routines. Entering CL-PVM environment is similar 
to typing in "pvm" at the shell prompt where one can monitor the performance of the PVM interactively. Moreover, the CL-PVM environment not only perform the
same task as a PVM console but also open up all the other library routines for
interaction. 
%%%% figure 6 %%%%%
\begin{figure}[tbh]
\centerline{\hbox{\psfig{figure=nimitz.ps,height=4.2in,width=4.5in}}}
\caption{Capture of the interaction on host Nimitz}
\label{f6}
\end{figure}
%%%% figure 7 %%%%%
\begin{figure}[tbh]
\centerline{\hbox{\psfig{figure=condor.ps,height=4.2in,width=4.5in}}}
\caption{Capture of the interaction on host Condor}
\label{f7}
\end{figure}
\section{Linking Lisp and C Programs}
\label{ciaece}
Testing CL-PVM can be divided into three tasks:
testing Lisp-C, C-Lisp, and Lisp-Lisp interactions.
The tests can be performed with five simple programs:
\begin{verbatim}
hello.c, hello.lsp, 
hello_other.c, hello_other.lsp, 
master.lsp, slave.c
\end{verbatim}
The C versions of these test programs have been
written by the developers of the origin PVM3. The rest of the  
programs are written in Lisp (see below) 
to perform the same tests.
\begin{verbatim}
;;;;;;;;;;;;;;;;;;  hello.lsp  ;;;;;;;;;;;;;;;;;;
(defvar buf (make-string 100))
(defun hello()
    (pvm-mytid)
    (cond ( (not (equal 
             (pvm-spawn "hello_other" "" 0 "nimitz.mcs.kent.edu" 1 'psids) 
                 1 ))
            (princ '|can't start hello_other|))
          (t 
            (pvm-bufinfo (pvm-recv -1 -1) 'len 'code 'tid)
            (pvm-upkstr 'buf)
            (princ buf)))
)

;;;;;;;;;;;;;;  hello_other.lsp  ;;;;;;;;;;;;;;;;
(defvar buf "Hello World From Lisp") 
(defun hello_other() 
    (pvm-initsend 0)
    (pvm-pkstr buf)
    (pvm-send (pvm-parent) 1)
    (pvm-exit)
)
\end{verbatim}
The \verb?pvm_spawn? spawns process(es) on the specified hosts.
It obviously works whether the executable has been generated by C, Fortran
or Lisp.  For this function it is only important to test if spawning from
a Lisp process is successful. The
program given below spawns three other slave processes and perform simple
arithmetic operations.
\begin{verbatim}
;;;;;;;;;;;;;;;;;;  master.lsp  ;;;;;;;;;;;;;;;;;;;;;
(defun master1()
    (pvm-mytid)
    (cond ( (not (equal (pvm-parent) 'PvmNoParent))
            (print "How many slaves to start 1-32?") (terpri)
            (let ((in (read)))
            (setq nproc in)))
          (t 
            (pvm-config 'nhost 'narch 'hostp)
            (setq nproc (car nhost))
            (if (> nproc 32) (setq nproc 32)))
    )
    (setq numt (pvm-spawn SLAVENAME "" 0 
                  "nimitz.mcs.kent.edu" nproc 'tids))
    (cond ( (< numt nproc)
            (print "Trouble spawning slaves, 
                   Aborting. Error Codes are:") (terpri)
            (print tids)(terpri) 
            (loop
               (if (equal (car tids) nil) (return (pvm-exit)))
               (pvm-kill (car tids))
               (setq tids (cdr tids))))
    )
    (setq n 100)
    (pvm-initsend 0)
    (pvm-pkint  nproc 1 1)
    (pvm-pkint  tids nproc 1)
    (pvm-pkint  n 1 1)
    (pvm-pkfloat (make-array '(100) :initial-element 
          (float 1.0 0.1s0) :element-type 'short-float)  n 1)
    (pvm-mcast  tids nproc 0)
    (setq msgtype 5)
    (setq cnt 0)
    (loop 
        (if (equal cnt nproc) (return (pvm-exit)))
        (pvm-recv -1 msgtype)
        (pvm-upkint  'who 1 1)
        (pvm-upkfloat 'result 1 1)
        (print "From")
        (print who)
        (print "Result=")
        (print result)(terpri)
        (setq cnt (+ cnt 1)))
)
\end{verbatim}
To invoke a Lisp executable file to test \verb?hello_other.lsp?
a simple shell script \verb?cltest? can be constructed:
\begin{verbatim}
## cltest script

/usr/local/bin/akcl <<EOF
     (setq *load-verbose* nil)
     (load "hello_other.lsp")
     (hello_other)
     (bye)
EOF
\end{verbatim}
And the \verb?cltest? script is the task spawned by PVM.

\section{Conclusion}
\label{con}
CL-PVM is a working interface that allows CL programs to participate
in PVM applications.  The work is part of the overall effort at Kent
\cite{mp}
to interface symbolic computation systems with other systems to
construct {\em problem solving environments} \cite{pse}.
The CL-PVM implementation exploits the fact that
CL is implemented in C and has macros to embed C codes. The CL compiler
becomes the bridge that connects Lisp and C codes. 
The data type conversion relies on knowledge of the C structure
used for Lisp objects.
CL-PVM, as it stands, provides a minimum, lowest-level interface to
the C routines of PVM.
The name and arguments of each CL-PVM routine correspond directly
to its PVM counterpart.  This is a logical and sensible design.
However, the convention makes usage of the CL-PVM routines less than natural
for Lisp programmers who are more at home with Lisp objects such as
lists and s-expressions and less so with strings separated by
white space, for example.
A natural extension of the current  work is to build a layer on top of CL-PVM
to supply convenient features for distributed Lisp applications that
completely hides the C interface layer.
The approach here can also be used to interface CL to the developing
{\em Message Passing Interface}\cite{mp} standard.
\appendix
\section{C structure header of Lisp}
\input{struct.tex}
\section{Headers of PVM C Library Routines} 
\input{head.tex}
\section{CL-PVM Routines}
\input{pvm3.tex}
\begin{thebibliography}{99}
\bibitem{pvm3} A. Geist, A. Beguelin, J. Dongarra, W. Jiang, R. Manchek and V. Sunderam, {\em PVM 3 User's Guide and Reference Manual},
Oak Ridge National Laboratory, 1993.
\bibitem{mp} S. Gray, N. Kajler, and P. Wang,
``MP: A Protocol for Efficient Exchange of Mathematical Expressions,''
Proceedings, ISSAC'94, Oxford UK, ACM Press, pp. 330-335, 
July, 1994.
\bibitem{C} P. Wang, {\em An Introduction to ANSI C on UNIX},
PWS Publishing Co. Boston, MA., 1991.
\bibitem{Lisp} R. Wilensky, {\em Common LISPcraft}, W. W. Norton \& Co., N.Y.,
1986.
\bibitem{akcl} T. Yuasa and M. Hagiya, ``Kyoto Common Lisp Dictionary'',
	Kyoto University, KCL on-line documentation" 1986.
\end{thebibliography}
\end{document}

