[Openmcl-cvs-notifications] r8570 - in /trunk/source/doc/src: Makefile.fedora Makefile.macports openmcl-documentation.xml
rme at clozure.com
rme at clozure.com
Fri Feb 22 21:34:53 EST 2008
Author: rme
Date: Fri Feb 22 21:34:53 2008
New Revision: 8570
Log:
Superseded by split version.
Removed:
trunk/source/doc/src/openmcl-documentation.xml
Modified:
trunk/source/doc/src/Makefile.fedora
trunk/source/doc/src/Makefile.macports
Modified: trunk/source/doc/src/Makefile.fedora
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D
--- trunk/source/doc/src/Makefile.fedora (original)
+++ trunk/source/doc/src/Makefile.fedora Fri Feb 22 21:34:53 2008
@@ -70,7 +70,7 @@
=
include makehtml
=
-install: $(HTMLFILES)
+install: $(HTMLFILES) distclean
cp $(HTMLFILES) ../HTML
=
$(TEMP):
@@ -80,7 +80,7 @@
rm -rf build-*
=
distclean: clean
- -find . -type f -name "*~" | xargs rm
+ rm -f *~
=
# All this rigamarole is to make the pathnames stored in the tarfile
# have a uniform prefix regardless of where the build directory actually
Modified: trunk/source/doc/src/Makefile.macports
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D
--- trunk/source/doc/src/Makefile.macports (original)
+++ trunk/source/doc/src/Makefile.macports Fri Feb 22 21:34:53 2008
@@ -21,7 +21,7 @@
# are, you should make sure that the correct catalog file is being used
# (see below), and, if so, that its contents are correct.
#EXTRAPARAMS=3D --load-trace
-EXTRAPARAMS=3D
+EXTRAPARAMS=3D --xinclude --nonet
=
# The catalog file tells the translator where to find XSL stylesheets on t=
he
# local system. The first choice here is what should be used for builds
@@ -54,9 +54,11 @@
=
# Compute some targets.
=
-XMLFILES =3D $(shell find . -name "*.xml")
+#XMLFILES =3D $(shell find . -name "*.xml")
+XMLFILES =3D [0-9][0-9]-*.xml ccl-documentation.xml
XSLFILES =3D $(shell find xsl -name "*.xsl")
-HTMLFILES =3D $(patsubst %.xml,%.html, $(XMLFILES))
+#HTMLFILES =3D $(patsubst %.xml,%.html, $(XMLFILES))
+HTMLFILES =3D ccl-documentation.html
=
# Save the xsltproc version string for use in debugging.
=
Removed: trunk/source/doc/src/openmcl-documentation.xml
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D
--- trunk/source/doc/src/openmcl-documentation.xml (original)
+++ trunk/source/doc/src/openmcl-documentation.xml (removed)
@@ -1,15693 +1,0 @@
-<?xml version=3D"1.0" encoding=3D"US-ASCII"?>
-<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN" "http://www.oas=
is-open.org/docbook/xml/4.2/docbookx.dtd" [
-<!ENTITY rest "<varname>&rest</varname>">
-<!ENTITY key "<varname>&key</varname>">
-<!ENTITY optional "<varname>&optional</varname>">
-<!ENTITY body "<varname>&body</varname>">
-<!ENTITY aux "<varname>&aux</varname>">
-<!ENTITY allow-other-keys "<varname>&allow-other-keys</varname>">
-]>
-<book lang=3D"en">
- <bookinfo>
- <title>OpenMCL Documentation</title>
- </bookinfo>
-
- <chapter><title>Obtaining, Installing, and Running OpenMCL</title>
- =
- <sect1><title>Releases and System Requirements</title>
- =
- <para>There are three active versions of OpenMCL. Version 1.0 was
- a stable release (released in late 2005); it is no longer under
- active development. Version 1.1 has been under active development
- since shortly after 1.0 was released; it's been distributed as a
- series of development "snapshots" and CVS updates. 1.1 snapshots
- have introduced support for x86-64 platforms, internal use of
- Unicode, and many other features, but have presented something of a
- moving target. Version 1.2 (being released in early 2008) is
- intended to both a more stable and predictable release schedule and
- to make it a bit easier for users who wish to track the "bleeding
- edge" of development to do so.</para>
-
- <para>Version 1.0 is available for three platform configurations:</p=
ara>
- <itemizedlist>
- <listitem>
- <para>Linux on PowerPC (32-bit implementation)</para>
- </listitem>
- <listitem>
- <para>Mac OS X on PowerPC (32-bit implementation)</para>
- </listitem>
- <listitem>
- <para>Mac OS X on PowerPC (64-bit implementation)</para>
- </listitem>
- </itemizedlist>
-
- <para>Versions 1.1 and 1.2 are available for five platform
- configurations:</para>
- <itemizedlist>
- <listitem>
- <para>Linux on PowerPC (32-bit and 64-bit implementations)</para>
- </listitem>
- <listitem>
- <para>>Mac OS X on PowerPC (32-bit and 64-bit implementations)</=
para>
- </listitem>
- <listitem>
- <para>Linux on X86-64 (64-bit implementation)</para>
- </listitem>
- <listitem>
- <para>Mac OS X on X86-64 (64-bit implementation)</para>
- </listitem>
- <listitem><para>FreeBSD on X86-64 (64-bit implementation)</para></=
listitem>
- </itemizedlist>
-
- <para>A 64-bit version of OpenMCL requires a 64-bit processor
- (obviously) running a 64-bit OS variant.</para>
- =
- <para>There are ongoing efforts to port OpenMCL to the Windows
- operating system and 32-bit x86 processors.</para>
- =
- =
- <para>Additional platform-specific information is given in the
- following subsections.</para>
-
- <sect2><title>LinuxPPC</title> =
- =
- <para>OpenMCL versions 1.0 and later run under relatively
- recent versions of LinuxPPC. All versions of OpenMCL require
- version 2.2.13 (or later) of the Linux kernel and version
- 2.1.3 (or later) of the GNU C library (glibc) at a bare
- minimum.</para>
- </sect2>
-
- <sect2><title>LinuxX8664</title> =
-
- <para>Version 1.1 and later of OpenMCL runs on relatively
- recent Linux distributions for the x86-64 architecture. It
- requires a Linux with Thread Local Storage support in the
- toolchain and standard libraries, and the New Posix Thread
- Library (NPTL). Fortunately, these features seem to be
- present in all current Linux distributions for x86-64, though
- there may be some problems with early Linux distributions for
- x86-64. (Some GCC versions older than 4.0 on Linux have been
- known to have problems compiling some of the C code in the
- kernel, some very old Linux distributions don't follow the
- current ABI standards wrt segment register usage, some early
- Linux kernels for x86-64 had problems mapping large regions of
- the address space ... it's difficult to enumerate exactly what
- versions of what Linux distributions have what problems. A
- rule of thumb is that - since much of the development of
- OpenMCL for x86-64 took place in that time frame - Linux
- distributions released earlier than early 2006 may have
- problems running OpenMCL.) </para>
- </sect2>
-
- <sect2><title>FreeBSD-amd64</title>
- <para>Versions 1.1 and later of OpenMCL runs on FreeBSD on
- x86-64 (FreeBSD releases generally call the platform "and64")
- OpenMCL should run under FreeBSD 6.0 or later; as of this
- writing, FreeBSD 7.0 is about to be released and it may be
- necessary for FreeBSD 7 users to install the "compat6x"
- package in order to use a version of OpenMCL built on FreeBSD
- 6.x under FreeBSD 7. .</para>
- </sect2>
-
- <sect2><title>DarwinPPC-MacOS-X</title>
-
- <para> OpenMCL 1.0 runs on MacOS X versions 10.2, 10.3 and
- 10.4 on the PowerPC.</para>
-
- <para>Current development on version 1.1 and later takes place
- under OS X versions 10.4 and 10.5 and requires at least
- version 10.3.9</para>
-
- <para>The 64-bit DarwinPPC version of OpenMCL requires
- functionality introduced in OSX 10.4 (namely, the ability to
- run 64-bit binaries). It also, obviously, requires a G5
- processor.</para>
-
- <para>OpenMCL hasn't been tested under Darwin proper, but
- OpenMCL doesn't intentionally use any MacOS X features beyond
- the Darwin subset and therefore it seems likely that OpenMCL
- would run on PPC Darwin versions that correspond to recent OSX
- versions.</para>
- </sect2>
-
- <sect2><title>Darwinx8664-MacOS-X</title>
- <para>Versions 1.1 and later of OpenMCL runs on 64-bit
- DarwinX86 (MacOS on Intel).</para>
-
- <para>OpenMCL Darwinx8664/MacOS X requires a 64-bit processor.
- All Macintoshes currently sold by Apple (as of early 2008) and
- all Macintoshes introduced by Apple since August 2006 have
- such processors. However, the original MacBooks, MacBook Pros
- and Intel iMacs (models introduced in early 2006) used 32-bit
- Core Duo processors, and so OpenMCL will not (yet) run on
- them.</para>
-
- </sect2>
- </sect1>
-
- <sect1><title>Installation</title>
- <para>Installing OpenMCL consists of</para>
- <orderedlist> =
- <listitem>
- <para>Downloading an appropriate distribution of OpenMCL;</para>
- </listitem>
- <listitem>
- <para>If necessary, extracting the archive somewhere on your
- computer;</para>
- </listitem>
- <listitem>
- <para>Configuring a shell script so OpenMCL can locate
- necessary library files and other auxilliary files.</para>
- </listitem>
- </orderedlist>
-
- <sect2><title>Downloading and untarring OpenMCL binary source
- releases</title> <para> OpenMCL releases and snapshots are
- distributed as tarballs (compressed tar archives).</para>
-
- <para>Tarballs of version 1.0 for supported platforms are
- available from the download page of the OpenMCL website.</para>
-
- <para>Tarballs of the latest development snapshots of version
- 1.1, along with release notes, are available from the testing
- directory on Clozure.com.</para>
- <para>Both the release and snapshot archives contain a directory
- named <literal>ccl</literal>, which in turn contains a number of
- files and subdirectories. The <literal>ccl</literal> directory
- can reside anywhere in the filesystem, assuming appropriate
- permissions. If you wanted the <literal>ccl</literal> directory
- to reside in <literal>``~/openmcl/ccl''</literal> and the
- directory <literal>``~/openmcl/''</literal> already existed, you
- could do anything equivalent to:</para>
- <programlisting>
-shell> cd ~/openmcl
-shell> tar xvzf <emphasis>path-to-downloaded-openmcl-archive.tar.gz</em=
phasis>
- </programlisting>
- <para><emphasis>Important Note for Macintosh Users:</emphasis>
- Double-clicking the archive in the Finder may work, but it also
- may have unintended side-effects. In some versions of the Mac
- OS double-clicking an archive will invoke Stuffit, which may try
- to replace linefeeds with carriage returns as it extracts
- files. Also, tar can be used to merge the archive contents into
- an existing <literal>ccl</literal> directory, whereas
- double-clicking in the Finder will create a new directory named
- <literal>ccl 2</literal> (or <literal>ccl 3</literal>, or...)
- Bottom line is that you're better off using tar from the
- shell.</para>
-
- <para>Once the <literal>ccl</literal> directory is installed,
- it's necessary to install and configure a shell script
- distributed with OpenMCL before using it.</para>
- </sect2>
-
- <sect2><title>The-openmcl-Shell-Script"</title>
- <para>OpenMCL needs to be able to find the
- <literal>ccl</literal> directory in order to support features
- such as <literal>require</literal> and
- <literal>provide</literal>, access to foreign interface
- information (see ) and the lisp build process (see
- ). Specifically, it needs to set up logical pathname
- translations for the <literal>"ccl:"</literal> logical host. If
- this logical host isn't defined (or isn't defined correctly),
- some things might work, some things might not... and it'll
- generally be hard to invoke and use OpenMCL productively.</para>
-
- <para>OpenMCL uses the value of the environment variable
- <literal>CCL_DEFAULT_DIRECTORY</literal> to determine the
- filesystem location of the <literal>ccl</literal> directory;
- the openmcl shell script is intended to provide a way to
- invoke OpenMCL with that environment variable set
- correctly.</para>
- <para>There are two versions of the shell script:
- <literal>"ccl/scripts/openmcl"</literal> is used to invoke
- 32-bit implementations of OpenMCL and
- <literal>"ccl/scripts/openmcl64"</literal> is used to invoke
- 64-bit implementations.</para>
- <para>To use the script:</para>
- <orderedlist>
- <listitem>
- <para>Edit the definition of
- <literal>CCL_DEFAULT_DIRECTORY</literal> near the
- beginning of theshell script so that it refers to your ccl
- directory. Alternately,set
- <literal>CCL_DEFAULT_DIRECTORY</literal> in your .cshrc,
- .tcshrc, .bashrc,.bash_profile, .MacOSX/environment.plist,
- or wherever you usually set environment variables. If
- there is an existing definition of thevariable, the
- openmcl script will not override it.The shell script sets
- a local variable (<literal>OPENMCL_KERNEL</literal>) to
- the standard name of the OpenMCL kernel approprate for the
- platform (asdetermined by 'uname -s'.) You might prefer to
- set this variable manually in the shell script</para>
- </listitem>
-
- <listitem>
- <para>Ensure that the shell script is executable, for
- example:<literal>$ chmod +x
- ~/openmcl/ccl/scripts/openmcl64</literal>This command
- grants execute permission to the named script. If you
- areusing a 32-bit platform, substitute "openmcl" in place
- of "openmcl64".
- <warning>
- <para>The above command won't work if you are not the
- owner of the installed copy of OpenMCL. In that case,
- you can use the "sudo" command like this:</para>
- <para><literal>$ sudo chmod +x
- ~/openmcl/ccl/scripts/openmcl64</literal></para>
- <para>Give your password when prompted.</para>
- <para>If the "sudo" command doesn't work, then you are
- not an administrator on the system you're using, and you
- don't have the appropriate "sudo" permissions. In that
- case you'll need to get help from the system's
- administrator.</para>
- </warning></para>
- </listitem>
- <listitem>
- <para>Install the shell script somewhere on your shell's
- search path</para>
- </listitem>
- </orderedlist>
-
- <para>Once this is done, it should be possible to invoke
- OpenMCL by typing <literal>openmcl</literal> at a shell
- prompt:</para>
- <programlisting>
-> openmcl [args ...]
-Welcome to OpenMCL Version whatever (DarwinPPC32)!
-?
-</programlisting>
- <para>The openmcl shell script will pass all of its arguments
- to the OpenMCL kernel. See <xref linkend=3D"Invocation"/> for
- more information about "args". When invoked this way, the
- lisp should be able to initialize the
- <literal>"ccl:"</literal> logical host so that its
- translations refer to the <literal>"ccl"</literal>
- directory. To test this, you can call
- <literal>probe-file</literal> in OpenMCL's read-eval-print
- loop:</para>
- <programlisting>
-? (probe-file "ccl:level-1;level-1.lisp") ;will return the physical pathn=
ame of the file
-#P"/Users/alms/my_lisp_stuff/ccl/level-1/level-1.lisp"
-</programlisting>
- </sect2>
- <sect2 id=3D"Invocation">
- <title>Invocation</title>
- <para>Assuming that the shell script is properly installed, it can be use=
d to invoke OpenMCL from a shell prompt:
- <programlisting>
-shell><replaceable>ccl</replaceable> <emphasis>args</emphasis>
- </programlisting>
- <literal>ccl</literal> will run a 32-bit session;
- <literal>ccl64</literal> will run a 64-bit session.
- </para>
- </sect2>
- <sect2 id=3D"Personal-Customization-with-the-Init-File">
- <title>Personal Customization with the Init File</title>
- <para>By default OpenMCL will try to load the file
- <literal>"home:openmcl-init.lisp"</literal> or the compiled
- =
- <literal>"home:openmcl-init.fasl"</literal> upon starting
- up. It does this by executing <literal>(load
- "home:openmcl-init")</literal>. If it is unable to load the
- file (for example because it does not exist) no action is
- taken; an error is not signalled.</para>
- <para>The <literal>"home:"</literal> prefix to the filename is
- a Common Lisp logical host, which OpenMCL initializes to refer
- to your home directory, so this looks for either of the files
- <literal>~/openmcl-init.lisp</literal> or
- <literal>~/openmcl-init.fasl</literal>.</para>
- <para>Since the init file is loaded the same way as normal
- Lisp code is, you can put anything you want in it. For
- example, you can change the working directory, and load
- packages which you use frequently.</para>
- <para>To suppress the loading of this init-file, invoke OpenMCL wi=
th the
-<literal>--no-init</literal> option.</para>
- </sect2>
-
- <sect2 id=3D"Some--hopefully--useful-options">
- <title>Some (hopefully) useful options</title>
- <para> The exact set of command-line arguments accepted by
- OpenMCL may vary slightly from release to release;
- <literal>openmcl --help</literal> will provide a definitive
- (if somewhat terse) summary of the options accepted by the
- current implementation and then exit. Some of those options
- are described below.</para>
- <itemizedlist>
- <listitem>
- <para>-S (or --stack-size). Specify the size of the initial process
- stack.</para>
- </listitem>
-
- <listitem>
- <para>-b (or --batch). Execute in "batch mode". End-of-file
- from *STANDARD-INPUT* will cause OpenMCL to exit, as will attempts to
- enter a break loop.</para>
- </listitem>
-
- <listitem>
- <para>-n (or --no-init). If this option is given, the init file
- is not loaded. This is useful if OpenMCL is being invoked by a
- shell script which should not be affected by whatever
- customizations a user might have in place.
- </para>
- </listitem>
-
- <listitem>
- <para>-e <form> (or --eval <form>). An expression is
- read (via READ-FROM-STRING from the string <form> and
- evaluated. If <form> contains shell metacharacters, it may be
- necessary to escape or quote them to prevent the shell from
- interpreting them.</para>
- </listitem>
-
- <listitem>
- <para>-l >path> (or --load <path>). Executes (load
- "<path>").</para>
- </listitem>
- </itemizedlist>
-
- <para>The <literal>--load</literal> and
- <literal>--eval</literal> options can each be provided
- multiple times. They're executed in the order specified on
- the command line, after the init file (if there is one) is
- loaded and before the toplevel read-eval-print loop is
- entered.</para>
- </sect2>
- </sect1>
-
- <sect1 id=3D"Using-OpenMCL-with-GNU-Emacs-and-SLIME">
- <title>Using OpenMCL with GNU Emacs and SLIME</title>
- <para>A very common way to use OpenMCL is to run it within the
- GNU Emacs editor, using a Lisp interface called SLIME ("Superior
- Lisp Interaction Mode for Emacs"). SLIME is an Emacs package
- designed to provide good support within Emacs for any of several
- Common Lisp implementations; one of the supported
- implementations is OpenMCL. This page describes how you can
- download SLIME and set it up to work with your OpenMCL
- installation.</para>
- <para>Why use SLIME? With SLIME, you can do the following things fro=
m within
-an Emacs editing session:</para>
- <itemizedlist>
- <listitem><para>run and control Lisp</para></listitem>
- <listitem><para>evaluate, compile, and load files or expressions</=
para></listitem>
- <listitem><para>macroexpand expressions</para></listitem>
- <listitem><para>fetch documentation and source code for Lisp symbo=
ls</para></listitem>
- <listitem><para>autocomplete symbols and package names</para></lis=
titem>
- <listitem><para>cross-reference function calls</para></listitem>
- <listitem><para>examine stack traces and debug errors</para></list=
item>
- =
- </itemizedlist>
- <para>There is an excellent SLIME tutorial video available at
-Common-Lisp.net. For
-complete information about SLIME, see the
-SLIME home page.</para>
-
- <sect2 id=3D"Assumptions-and-Requirements">
- <title>Assumptions and Requirements</title>
- <para>In order to simplify these instructions, we'll make
- several assumptions about your system. Specifically, we
- assume:</para>
- <itemizedlist>
- <listitem>
- <para>You have a working installation of GNU Emacs. If you
- don't have a working copy of GNU Emacs, see the web page on
- obtaining Emacs. If you prefer to use XEmacs instead of
- GNU Emacs,these instructions should still work; SLIME
- supports XEmacs Version21. Mac OS X includes an Emacs
- installation. If you want to look into different versions,
- you can check out theEmacsWiki, whichmaintains a
- page,EmacsForMacOS,that provides much more information
- about using Emacs on the Mac.
- <warning>
- <para>A popular version of Emacs among Mac users is
- Aquamacs. This application is a version of GNU Emacs
- with a number of customizations meant to make it behave
- more like a standard Maciontosh application, with
- windows, a menubar, etc. Aquamacs includes SLIME; if
- you like Aquamacs then you can use SLIME right away,
- without getting and installing it separately. You just
- need to tell SLIME where to find your installation of
- OpenMCL. (See FIXTHIS.)</para>
- </warning>
- </para>
- </listitem>
- <listitem>
- <para>You have a working copy of OpenMCL, installed in
- <literal>"~/openmcl/ccl"</literal>If you prefer to install
- OpenMCL in some directory other
- than<literal>"~/openmcl/ccl"</literal> then these
- instructions still work, but you must remember to use your
- path to your ccl directory instead of theone that we give
- here.</para>
- </listitem>
- <listitem>
- <para>You install emacs add-ons in the folder
- <literal>"~/emacs/site/"</literal>If this directory
- doesn't exist on your system, you can just create it.If
- you prefer to install Emacs add-ons in some place other
- than<literal>"~/emacs/site/"</literal> then you must
- remember to use your path toEmacs add-ons in place of
- ours.</para>
- </listitem>
- =
- </itemizedlist>
- </sect2>
-
- <sect2 id=3D"Getting_Slime"><title>Getting SLIME</title> =
-
- <para>You can get SLIME from the SLIME Home Page. Stable
- releases and CVS snapshots are available as archive files, or
- you can follow the instructions on the SLIME Home Page to
- check out the latest version from their CVS repository.</para>
-
- <para>It's worth noting that stable SLIME releases happen very
- seldom, but the SLIME developers often make changes and
- improvements that are available through CVS updates. If you
- asked the SLIM developers, they would most likely recommend
- that you get SLIME from their CVS repository and update it
- frequently.</para>
-
- <para>Whether you get it from CVS, or download and unpack one
- of the available archives, you should end up with a folder
- named "slime" that contains the SLIME distribution.</para>
- </sect2>
-
- <sect2><title>Installing SLIME</title> =
-
- <para>Once you have the "slime" folder described in the
- previous section,installation is a simple matter of copying
- the folder to the proper place. You can drag it into the
- "~/emacs/site/" folder, or you can use a terminal command to
- copy it there. For example, assuming your working directory
- contains the unpacked "slime" folder:</para> <para><literal>$
- cp -R slime ~/emacs/site/</literal></para> <para>That's all it
- takes.</para>
-
- </sect2>
-
- <sect2 id=3D"Telling-Emacs-About-SLIME">
- <title>Telling Emacs About SLIME</title>
- <para> Once SLIME and OpenMCL are installed, you just need to
- add a line to your "~/.emacs" file that tells SLIME where to
- find the script that runs OpenMCL:</para>
- <para><literal>(setq inferior-lisp-program "~/openmcl/ccl/scripts/=
openmcl64")</literal></para>
- <para>or</para>
- <para><literal>(setq inferior-lisp-program "~/openmcl/ccl/scripts/=
openmcl")</literal></para>
- <warning>
- <para>Aquamacs users should add this line to the file "~/Library=
/Preferences/Aquamacs Emacs/Preferences.el".</para>
- </warning>
- </sect2>
-
- <sect2 id=3D"Running-OpenMCL-with-SLIME">
- <title>Running OpenMCL with SLIME</title>
- <para>Once the preparations in the previous section are
- complete, exit Emacs and restart it, to ensure that it reads
- the changes you made in your ".emacs" file (alternatively, you
- could tell Emacs to reload the ".emacs" file). If all went
- well, you should now be ready to run OpenMCL using
- SLIME.</para>
- <para>To run OpenMCL, execute the command "M-x slime". SLIME
- should start an OpenMCL session in a new buffer. (If you are
- unfamiliar with the Emacs notation "M-x command", see the GNU
- Emacs FAQ; specifically, take a look at questions 1, 2, and
- 128.)</para>
- </sect2>
-
- <sect2 id=3D"What-if-a-New-Version-of-OpenMCL-Breaks-SLIME-">
- <title>What if a New Version of OpenMCL Breaks SLIME?</title>
- <para>Sometimes you'll get a new version of OpenMCL, set up
- Emacs to use it with SLIME, and SLIME will fail. Most likely
- what has happened is that the new version of OpenMCL has a
- change in the output files produced by the compiler (OpenMCL
- developers will say "the fasl version has changed." fasl
- stands for “fast load” aka compiled files). This
- problem is easy to fix: just delete the existing SLIME fasl
- files. The next time you launch Emacs and start SLIME, it will
- automatically recompile the Lisp files, and that should fix
- the problem.</para>
- <para>SLIME's load process stores its fasl files in a hidden
- folder inside your home folder. The path is</para>
- <para><literal>~/.slime/fasl</literal></para>
- <para>You can use a shell command to remove the fasl files, or
- remove them using your system's file browser.</para>
- <screen><emphasis role=3D"bold">Note for Macintosh Users:</emphasi=
s> =
- The leading "." character in the ".slime" folder's name
- prevents the Finder from showing this folder to you. If you
- use the "Go To Folder" menu item in the Finder's "Go" menu,
- you can type in "~/.slime" and the Finder will show it to
- you. You can then drag the "fasl" folder to the trash.
- </screen>
- </sect2>
-
- <sect2 id=3D"Known-Bugs">
- <title>Known Bugs</title>
- <para>SLIME has not been updated to account for recent changes
- made in OpenMCL to support x86-64 processors. You may run into
- bugs running on those platforms.</para>
- <para>The SLIME backtrace sometimes shows incorrect information.</=
para>
- <para><literal>return-from-frame</literal> and
- <literal>apply-in-frame</literal> do not work reliably. (If
- they work at all, it's pure luck.)</para>
- <para>Some versions of Emacs on the Macintosh may have trouble
- finding the shell script that runs OpenMCL unless you specify
- a full path to it. See the above section "Telling Emacs About
- SLIME" to learn how to specify the path to the shell
- script.</para>
- <para>For more help with OpenMCL on Mac OS X, consult the
- OpenMCL mailing lists.</para>
- </sect2>
- </sect1>
-
- <sect1 id=3D"Example-Programs">
- <title>Example Programs</title>
- <para>Beginning with release 0.9, a number (ok, a
- <emphasis>small</emphasis> number, at least initially) of
- example programs are distributed in the "ccl:examples;"
- directory of the source distribution. See the
- README-OPENMCL-EXAMPLES text file in that directory for
- information about prerequisites and usage.</para>
- <para>Some of the example programs are derived from C examples
- in textbooks, etc.; in those cases, the original author and work
- are cited in the source code.</para>
- <para>It may be a stretch to imply that the examples have a
- great deal of value as "intellectual property", but I might as
- well say this: Unless the original author or contributor claims
- other rights, you're free to incorporate any of this example
- code or derivative thereof in any of you're own works without
- restriction. In doing so, you agree that the code was provided
- "as is", and that no other party is legally or otherwise
- responsible for any consequences of your decision to use
- it.</para>
- <para>If you've developed OpenMCL examples that you'd like to
- see added to the distribution, please send mail to let me
- know. Any such contributions would be welcome and appreciated
- (as would bug fixes and improvements to the existing
- examples.)</para>
- </sect1>
- </chapter>
-
- <chapter><title>Building OpenMCL from its Source Code</title>
-
- <para>OpenMCL, like many other Lisp implementations, consists of a
- kernel and a heap image. The kernel is an ordinary C program, and
- is built with a C compiler. It provides very basic and
- fundamental facilities, such as memory management, garbage
- collection, and bootstrapping. All the higher-level features are
- written in Lisp, and compiled into the heap image. Both parts are
- needed to have a working Lisp implementation; neither the kernel
- nor the heap image can stand on their own.</para>
-
- <para>You may already know that, when you have a C compiler which
- is written in C, to build the compiler, you need to already have a
- C compiler available. The heap image includes a Lisp compiler,
- which is written in Lisp. Therefore, you need a working Lisp
- compiler in order to build the heap image!</para>
- =
- <para>Where will you get a working Lisp compiler? No worries; you
- can use a precompiled copy of a (slightly older and compatible)
- version of OpenMCL. For help, read on.</para>
-
- <para>In principle it would be possible to use another Lisp as the
- host compiler, rather than an old OpenMCL; this would be a
- challenging and experimental way to build, and is not described
- here.</para>
-
- <sect1 id=3D"building-definitions"><title>building definitions</title>
- <para>The following terms are used in subsequent sections; it
- may be helpful to refer to these definitions.</para>
-
- <para><indexterm><primary>fasl files</primary></indexterm> are
- the object files produced by<literal>compile-file</literal>.
- fasl files store the machine codeassociated with function
- definitions and the external representationof other lisp objects
- in a compact, machine-readable form. fasl is short for
- “<literal>FAS</literal>t
- <literal>L</literal>oading”.OpenMCL uses different
- pathname types (extensions) to name faslfiles on different
- platforms; see <xref
- linkend=3D"Platform-specific-filename-conventions"/> </para>
-
- <para>The <indexterm><primary>lisp kernel</primary></indexterm> is
- a C program (with a fair amount ofplatform-specific assembly
- language code as well.) Its basic job isto map a lisp heap
- image into memory, transfer control to somecompiled lisp code
- that the image contains, handle any exceptionsthat occur during
- the execution of that lisp code, and provide variousother forms
- of runtime support for that code.OpenMCL uses different
- filenames to name the lisp kernel fileson different platforms;
- see FIXTHIS.</para>
-
- <para>A <indexterm><primary>heap image</primary></indexterm> is
- a file that can be quickly mapped into aprocess's address space.
- Conceptually, it's not too different from anexecutable file or
- shared library in the OS's native format (ELF orMach-O/dyld
- format); for historical reasons, OpenMCL's own heap images are in
- their own (fairly simple) format.The term <literal>full heap
- image</literal> refers to a heap image file thatcontains all of
- the code and data that comprise OpenMCL.OpenMCL uses different
- filenames to name the standard full heapimage files on different
- platforms; see FIXTHIS .</para>
-
- <para>A <indexterm><primary>bootstrapping
- image</primary></indexterm> is a minimal heap image used in
- the process of building OpenMCL itself. The bootstrapping image
- containsjust enough code to load the rest of OpenMCL from fasl
- files. It mayhelp to think of the bootstrapping image as the
- egg and the full heapimage as the chicken...OpenMCL uses
- different filenames to name the standardbootstrapping image
- files on different platforms; see FIXTHIS .</para>
-
- <para>Each supported platform (and possibly a few
- as-yet-unsupported ones) has a uniquely named subdirectory of
- <literal>ccl/lisp-kernel/</literal>; each such
- <indexterm><primary>kernel build directory</primary></indexterm>
- contains a Makefile and may contain some auxiliary files (linker
- scripts, etc.) that are used to build the lispkernel on a
- particular platform.The platform-specific name of the kernel
- build directory is described in FIXTHIS.</para>
-
- <sect2 id=3D"filename_conventions">
- <title>Platform-specific filename conventions</title>
- <table id =3D"Platform-specific-filename-conventions">
- <title>Platform-specific filename conventions</title>
- <tgroup cols=3D"6">
- <thead>
- <row>
- <entry>Platform</entry>
- <entry>kernel</entry>
- <entry>full-image</entry>
- <entry>boot-image</entry>
- <entry>fasl extension</entry>
- <entry>kernel-build directory</entry>
- </row>
- </thead>
- <tbody>
- <row>
- <entry>DarwinPPC32</entry>
- <entry>dppccl</entry>
- <entry>dppccl.image</entry>
- <entry>ppc-boot.image</entry>
- <entry>.dfsl</entry>
- <entry>darwinppc</entry>
- </row>
- <row>
- <entry>LinuxPPC32</entry>
- <entry>ppccl</entry>
- <entry>PPCCL</entry>
- <entry>ppc-boot</entry>
- <entry>.pfsl</entry>
- <entry>linuxppc</entry>
- </row>
- <row>
- <entry>DarwinPPC64</entry>
- <entry>dppccl64</entry>
- <entry>dppccl64.image</entry>
- <entry>ppc-boot64.image</entry>
- <entry>.d64fsl</entry>
- <entry>darwinppc64</entry>
- </row>
- <row>
- <entry>LinuxPPC64</entry>
- <entry>ppccl64</entry>
- <entry>PPCCL64</entry>
- <entry>ppc-boot64</entry>
- <entry>.p64fsl</entry>
- <entry>linuxppc64</entry>
- </row>
- <row>
- <entry>LinuxX8664</entry>
- <entry>lx86cl64</entry>
- <entry>LX86CL64</entry>
- <entry>x86-boot64</entry>
- <entry>.lx64fsl</entry>
- <entry>linuxx8664</entry>
- </row>
- <row>
- <entry>DarwinX8664</entry>
- <entry>dx86cl64</entry>
- <entry>dx86cl64.image</entry>
- <entry>x86-boot64.image</entry>
- <entry>.dx64fsl</entry>
- <entry>darwinx8664</entry>
- </row>
- <row>
- <entry>FreeBSDX8664</entry>
- <entry>fx86cl64</entry>
- <entry>FX86CL64</entry>
- <entry>fx86-boot64</entry>
- <entry>.fx64fsl</entry>
- <entry>freebsdx8664</entry>
- </row>
- </tbody>
- </tgroup>
- </table>
- </sect2>
- </sect1>
-
- <sect1 id=3D"Setting-Up-to-Build">
- <title>Setting Up to Build</title>
- <para>There are currently three versions of OpenMCL that you
- might want to use (and therefore might want to build from
- source):</para>
- <itemizedlist>
- <listitem><para>Version 1.0 - the more stable version</para></list=
item>
- <listitem><para>Version 1.1 - the more recent version, which
- runs on more platforms (including x86-64 platforms) and
- supports Unicode</para></listitem>
- <listitem><para>Version 1.2 - supports (at least) all of the
- features and platforms as 1.1, but is distributed and updated
- differently</para></listitem>
- </itemizedlist>
- <para>All versions are available for download from the OpenMCL
- website in the form of archives that contain everything you need
- to work with OpenMCL, including the complete sources, a full
- heap image, and the foreign-function interface database.</para>
- <para>Version 1.0 archives are named
- <literal>openmcl-</literal><replaceable>platform</replaceable><liter=
al>-all-1.0.tar.gz</literal>,
- where <replaceable>platform</replaceable> is either
- <literal>darwinppc</literal>, <literal>darwinppc64</literal>, or
- <literal>linuxppc</literal>. Because version 1.0 is no longer
- undergoing active development, you won't ever need to update
- these sources.</para>
- <para>Version 1.1 archives are named
- <literal>openmcl-</literal><replaceable>platform</replaceable><liter=
al>-snapshot-</literal><replaceable>yymmdd</replaceable><literal>.tar.gz</l=
iteral>,
- where <replaceable>platform</replaceable> is either
- <literal>darwinppc</literal>, <literal>darwinx8664</literal>,
- <literal>linuxppc</literal>, <literal>linuxx8664</literal>, or
- <literal>freebsdx8664</literal>, and where
- <replaceable>yymmdd</replaceable> is the year, month, and day
- the snapshot was released.</para>
- <para>Because version 1.1 is undergoing active development,
- there may be times when you want to get sources that are more
- recent than the most recent snapshot and use them to build
- yourself a new bleeding-edge OpenMCL. In that case, you should
- download and install the latest snapshot, and then update your
- sources via CVS. At that point you can rebuild and you'll have
- the latest and greatest OpenMCL. The snapshot has CVS
- working-copy information in it, so all you need to do to update
- is</para>
- <programlisting>
-$ cd ccl
-$ cvs login # password is "cvs"
- # this step only needs to be done once,
- # that'll store the trivially encrypted
- # password in ~/.cvspas
-$ cvs update
- </programlisting>
- <para>Unless you tell it to, cvs won't delete ("prune") empty
- directories or create new ones when the repository changes.
- It's generally a good habit to use</para>
- <programlisting>
-$ cvs update -d -P # create dirs as needed, prune empty ones
- </programlisting>
- <para>Version 1.2 archives follow naming conventions that are
- similar to those used by 1.0 (though more platforms are supported.)
- However, rather than containing CVS working-copy information, the
- 1.2 (and, presumably, later) archives contain metainformation used
- by the Subversion (svn) source-code control system.</para>
- <para>Subversion client programs are pre-installed on OSX 10.5 and
- later and are typically either pre-installed or readily available
- on Linux and FreeBSD platforms. The <ulink url=3D"http://subversion=
.tigris.org">Subversion web page</ulink> contains links to subversion clien=
t programs
- for many platforms; users of OSX versions 10.4 and earlier can also
- install Subversion clients via Fink or MacPorts.</para>
- =
- </sect1>
-
- <sect1 id=3D"Building-Everything">
- <title>Building Everything</title>
- <para>Given that you now have everything you need, do the
- following in a running OpenMCL to bring your Lisp system
- completely up to date.</para>
- <programlisting>
-? (ccl:rebuild-ccl :full t)
- </programlisting>
- <para>That call to the function <literal>rebuild-ccl</literal>
- will perform the following steps:</para>
- <itemizedlist>
- <listitem>
- <para>Deletes all fasl files and other object files in the
- <literal>ccl</literal>directory tree</para>
- </listitem>
- <listitem>
- <para>Runs an external process which does a
- <literal>make</literal> in the currentplatform's kernel
- build directory to create a new kernel</para>
- </listitem>
- <listitem>
- <para>Does <literal>(compile-ccl t)</literal> in the running
- lisp, to produce aset of fasl files from the “higher
- level” lisp sources.</para>
- </listitem>
- <listitem>
- <para>Does <literal>(xload-level-0 :force)</literal> in the
- running lisp, to compile thelisp sources in the
- “ccl:level-0;” directory into fasl files and
- then createa bootstrapping image from those fasl
- files.</para>
- </listitem>
- <listitem>
- <para>Runs another external process, which causes the newly
- compiled lispkernel to load the new bootstrapping image.
- The bootsrtrapping image then loadsthe “higher
- level” fasl files and a new copy of the platform's
- full heap imageis then saved.</para>
- </listitem>
- </itemizedlist>
- <para>If all goes well, it'll all happen without user
- intervention and with some simple progress messages. If
- anything goes wrong during execution of either of the external
- processes, the process output is displayed as part of a lisp
- error message.</para>
- <para><literal>rebuild-ccl</literal> is essentially just a short
- cut for running all the individual steps involved in rebuilding
- the system. You can also execute these steps individually, as
- described below.</para>
- </sect1>
-
- <sect1 id=3D"Building-the-kernel">
- <title>Building the kernel</title>
- <para>The Lisp kernel is the executable which you run to use
- Lisp. It doesn't actually contain the entire Lisp
- implementation; rather, it loads a heap image which contains the
- specifics - the "library", as it might be called if this was a C
- program. The kernel also provides runtime support to the heap
- image, such as garbage collection, memory allocation, exception
- handling, and the OS interface.</para>
-
- <para>The Lisp kernel file has different names on different
- platforms. See FIXTHIS . On all platforms the lisp kernel sources re=
side
- in <literal>ccl/lisp-kernel</literal>.</para>
-
- <para>This section gives directions on how to rebuild the Lisp
- kernel from its source code. Most OpenMCL users will rarely
- have to do this. You probably will only need to do it if you are
- attempting to port OpenMCL to a new architecture or extend or enhance
- its kernel in some way. As mentioned above, this step happens
- automatically when you do
- <programlisting>
-? (rebuild-ccl :full t)
- </programlisting>
- </para>
-
-
- <sect2 id=3D"Kernel-build-prerequisites">
- <title>Kernel build prerequisites</title>
- <para>The OpenMCL kernel can be bult with the following widely
- available tools:</para>
- <itemizedlist>
- <listitem><para>cc or gcc- the GNU C compiler</para></listitem>
- <listitem><para>ld - the GNU linker</para></listitem>
- <listitem><para>m4 or gm4- the GNU m4 macro processor</para></li=
stitem>
- <listitem><para>as - the GNU assembler (version 2.10.1 or later)=
</para></listitem>
- <listitem><para>make - either GNU make or, on FreeBSD, the default BSD =
make program</para></listitem>
- </itemizedlist>
- <para> In general, the more recent the versions of those
- tools, the better; some versions of gcc 3.x on Linux have
- difficulty compiling some of the kernel source code correctly
- (so gcc 4.0 should be used, if possible.) On OSX, the
- versions of the tools distributed with XCode should work fine;
- on Linux, the versions of the tools installed with the OS (or
- available through its package management system) should work
- fine if they're "recent enough". On FreeBSD, the installed
- version of the <literal>m4</literal> program doesn't support
- some features that the kernel build process depends on; the
- GNU version of the m4 macroprocessor (called
- <literal>gm4</literal> on FreeBSD) should be installed
-
- </para>
- </sect2>
- <sect2 id=3D"kernel-build-command">
- <title>Using "make" to build the lisp kernel</title>
- <para>With those tools in place, do:
- <programlisting>
-shell> cd ccl/lisp-kernel/<replaceable>PLATFORM</replaceable>
-shell> make
- </programlisting>
- </para>
- <para>That'll assemble several assembly language source files,
- compile several C source files, and link
- ../../<replaceable>the kernel</replaceable>.
- </para>
- </sect2>
- </sect1>
- <sect1 id=3D"Building-the-heap-image">
- <title>Building the heap image</title>
- <para>The initial heap image is loaded by the Lisp kernel, and
- provides most all of the language implementation The heap image
- captures the entire state of a running Lisp (except for external
- resources, such as open files and TCP sockets). After it is
- loaded, the contents of the new Lisp process's memory are
- exactly the same as those of the old Lisp process when the image
- was created.</para>
- <para>The heap image is how we get around the fact that we can't
- run Lisp code until we have a working Lisp implementation, and
- we can't make our Lisp implementation work until we can run Lisp
- code. Since the heap image already contains a fully-working
- implementation, all we need to do is load it into memory and
- start using it.</para>
- <para>If you're building a new version of OpenMCL, you need to
- build a new heap image.</para>
- <para>(You might also wish to build a heap image if you have a
- large program which it is very complicated or time-consuming to
- load, so that you will be able to load it once, save an image,
- and thenceforth never have to load it again. At any time, a heap
- image capturing the entire memory state of a running Lisp can be
- created by calling the function
- <literal>ccl:save-application</literal>.)</para>
-
- <sect2 id=3D"Development-cycle">
- <title>Development cycle</title>
- <para>Creating a new OpenMCL full heap image consists of the
- following steps:</para>
- <orderedlist>
- <listitem><para>Using your existing OpenMCL, create a
- bootstrapping image</para></listitem>
- <listitem><para>Using your existing OpenMCL, recompile your
- updated OpenMCL sources</para></listitem>
- <listitem><para>Invoke OpenMCL with the bootstrapping image
- you just created (rather than with the existing full heap
- image).</para></listitem>
- </orderedlist>
- <para>When you invoke OpenMCL with the bootstrapping image, it
- will start up, load al of the OpenMCL fasl files, and save out
- a new full heap image. Voila. You've created a new heap
- image.</para>
- <para>A few points worth noting:</para>
- <itemizedlist>
- <listitem>
- <para>There's a circular dependency between the full heap
- image and thebootstrapping image, in that each is used to
- build the other.</para>
- </listitem>
- <listitem>
- <para>There are some minor implementation
- differences, but the environment in effect after the
- bootstrapping image has loaded its fasl files is essentially
- equivalent to the environment provided by the full heap
- image; the latter loads a lot faster and is easier to
- distribute, of course.</para>
- </listitem>
- <listitem>
- <para>If the full heap image doesn't work (because
- of an OScompatibilty problem or other bug), it's very likely
- that thebootstrapping image will suffer the same
- problems.</para>
- </listitem>
- </itemizedlist>
- <para>Given a bootstrapping image and a set of up-to-date fasl
- files, the development cycle usually involves editing lisp
- sources (or updating those sources via cvs update),
- recompiling modified files, and using the bootstrapping image
- to produce a new heap image.</para>
- </sect2>
-
- <sect2 id=3D"Generating-a-bootstrapping-image">
- <title>Generating a bootstrapping image</title>
- <para>The bootstrapping image isn't provided in OpenMCL
- distributions. It can be built from the source code provided
- in distributions (using a lisp image and kernel provided in
- those distributions) using the procedure described
- below.</para>
-
- <para>The bootstrapping image is built by invoking a special
- utility inside a running OpenMCL heap image to load files
- contained in the <literal>ccl/level-0</literal> directory. The
- bootstrapping image loads several dozen fasl files. After
- it's done so, it saves a heap image via
- <literal>save-application</literal>. This process is called
- "cross-dumping".</para>
-
- <para>Given a source distribution, a lisp kernel, and aheap
- image, one can produce a bootstapping image by first invoking
- OpenMCL from the shell:</para>
- <programlisting>
-shell> openmcl
-Welcome to OpenMCL .... !
-?
- </programlisting>
- <para>then calling <literal>ccl:xload-level-0</literal> at the
- lisp prompt</para>
- <programlisting>
-? (ccl:xload-level-0)
- </programlisting>
- <para>This will compile the lisp sources in the ccl/level-0
- directory if they're newer than the corresponding fasl files
- and will then load the resulting fasl files into a simulated
- lisp heap contained inside data structures inside the running
- lisp. That simulated heap image is then written to
- disk.</para>
- <para><literal>xload-level-0</literal> should be called
- whenever your existing boot image is out-of-date with respect
- to the source files in <literal>ccl:level-0;</literal>
- :</para>
- <programlisting>
-? (ccl:xload-level-0 :force)
-</programlisting>
- <para>will force recompilation of the level-0 sources.</para>
- </sect2>
-
- <sect2 id=3D"Generating-fasl-files">
- <title>Generating fasl files</title>
- <para> Calling:</para>
- <programlisting>
-? (ccl:compile-ccl)
- </programlisting>
- <para>at the lisp prompt will compile any fasl files that are
- out-of-date with respect to the corresponding lisp sources;
- <literal>(ccl:compile-ccl t)</literal> will force
- recompilation. <literal>ccl:compile-ccl</literal> will reload
- newly-compiled versions of some files;
- <literal>ccl:xcompile-ccl</literal> is analogous, but skips
- this reloading step.</para>
- <para>Unless there are bootstrapping considerations involved,
- it usually doesn't matter whether these files reloaded after
- they're recompiled.</para>
- <para>Calling <literal>compile-ccl</literal> or
- <literal>xcompile-ccl</literal> in an environment where fasl
- files don't yet exist may produce warnings to that effect
- whenever files are <literal>require</literal>d during
- compilation; those warnings can be safely ignored. Depending
- on the maturity of the OpenMCL release, calling
- <literal>compile-ccl</literal> or
- <literal>xcompile-ccl</literal> may also produce several
- warnings about undefined functions, etc. They should be
- cleaned up at some point.</para>
- </sect2>
-
- <sect2 id=3D"Building-a-full-image-from-a-bootstrapping-image">
- <title>Building a full image from a bootstrapping image</title>
- <para>To build a full image from a bootstrapping image, just
- invoke the kernel with the bootstrapping image is an
- argument</para>
- <programlisting>
-$ cd ccl # wherever your ccl directory is
-$ ./KERNEL BOOT_IMAGE
- </programlisting>
- <para>Where <replaceable>KERNEL</replaceable> and
- <replaceable>BOOT_IMAGE</replaceable> are the names of
- the kernel and boot image appropriate to the platform you are
- running on. See FIXTHIS</para>
- <para>That should load a few dozen fasl files (printing a
- message as each file is loaded.) If all of these files
- successfully load, the lisp will print a prompt. You should be
- able to do essentially everything in that environment that you
- can in the environment provided by a "real" heap image. If
- you're confident that things loaded OK, you can save that
- image.</para>
- <programlisting>
-? (ccl:save-application "<replaceable>image_name</replaceable>") ; Overwit=
ing the existing heap image
- </programlisting>
- <para>Where <replaceable>image_name</replaceable> is the name
- of the full heap image for your platform. See FIXTHIS.</para>
- <para>If things go wrong in the early stages of the loading
- sequence, errors are often difficult to debug; until a fair
- amount of code (CLOS, the CL condition system, streams, the
- reader, the read-eval-print loop) is loaded, it's generally
- not possible for the lisp to report an error. Errors that
- occur during these early stages ("the cold load") sometimes
- cause the lisp kernel debugger (see ) to be invoked; it's
- primitive, but can sometimes help one to get oriented.</para>
- </sect2>
- </sect1>
- </chapter>
-
- <chapter id=3D"Questions-and-Answers">
-
- <title>Questions and Answers</title>
-
- <sect1 id=3D"How-can-I-do-nonblocking--aka--unbuffered--and--raw---IO-=
">
- <title>How can I do nonblocking (aka "unbuffered" and "raw") IO?</ti=
tle>
- <para>There's some code for manipulating TTY modes in
- "ccl:library;pty.lisp".</para>
- <programlisting>
-? (require "PTY")
-
-? (ccl::disable-tty-local-modes 0 #$ICANON)
-T
- </programlisting>
- <para>will turn off "input canonicalization" on file descriptor
- 0, which is at least part of what you need to do here. This
- disables the #$ICANON mode, which tells the OS not to do any
- line-buffering or line-editing. Of course, this only has any
- effect in situations where the OS ever does that, which means
- when stdin is a TTY or PTY.</para>
- <para>If the #$ICANON mode is disabled, you can do things like:</par=
a>
- <programlisting>
-? (progn (read-char) (read-char))
-a
-#\a
- </programlisting>
- <para>(where the first READ-CHAR consumes the newline, which
- isn't really necessary to make the reader happy anymore.) So,
- you can do:</para>
- <programlisting>
-? (read-char)
-#\Space
-</programlisting>
- <para>(where there's a space after the close-paren) without
- having to type a newline.</para>
- </sect1>
-
- <sect1 id=3D"I-m-using-the-graphics-demos--Why-doesn-t-the-menubar-cha=
nge-">
- <title>I'm using the graphics demos. Why doesn't the menubar
- change?</title>
- <para>When you interact with text-only OpenMCL, you're either
- in Terminal or in Emacs, running OpenMCL as a subprocess. When
- you load Cocoa or the graphical environment, the subprocess does
- some tricky things that turn it into a full-fledged Application,
- as far as the OS is concerned.</para>
- <para>So, it gets its own icon in the dock, and its own menubar,
- and so on. It can be confusing, because standard input and
- output will still be connected to Terminal or Emacs, so you can
- still type commands to OpenMCL from there. To see the menubar
- you loaded, or the windows you opened, just click on the OpenMCL
- icon in the dock.</para>
- </sect1>
-
- <sect1 id=3D"I-m-using-Slime-and-Cocoa--Why-doesn-t--standard-output--=
seem-to-work-">
- <title>I'm using Slime and Cocoa. Why doesn't *standard-output*
- seem to work? </title>
- <para>This comes up if you're using the Slime interface
- to run OpenMCL under Emacs, and you are doing Cocoa programming
- which involves printing to *standard-output*. It seems as
- though the output goes nowhere; no error is reported, but it
- doesn't appear in the *slime-repl* buffer.</para>
-
- <para>For the most part, this is only relevant when you are
- trying to insert debug code into your event handlers. The SLIME
- listener runs in a thread where the standard stream varaiables
- (like <literal>*STANDARD-OUTPUT* and</literal> and
- <literal>*TERMINAL-IO*</literal> are bound to the stream used to
- communicate with Emacs; the Cocoa event thread has its own
- bindings of these standard stream variables, and output to these
- streams goes to the *inferior-lisp* buffer instead. Look for it
- there.</para>
- </sect1>
- </chapter>
-
- <chapter id=3D"Programming-with-Threads">
- <title>Programming with Threads</title>
-
- <sect1 id=3D"Threads-overview">
- <title>Threads Overview</title>
-
- <para>OpenMCL provides facilities which enable multiple threads
- of execution (<emphasis>threads</emphasis>, sometimes called
- <emphasis>lightweight processes</emphasis> or just
- <emphasis>processes</emphasis>, though the latter term shouldn't
- be confused with the OS's notion of a process) within a lisp
- session. This document describes those facilities and issues
- related to multitheaded programming in OpenMCL.</para>
-
- <para>Wherever possible, I'll try to use the term "thread" to
- denote a lisp thread, even though many of the functions in the
- API have the word "process" in their name. A
- <emphasis>lisp-process</emphasis> is a lisp object (of type
- CCL:PROCESS) which is used to control and communicate with an
- underlying <emphasis>native thread</emphasis>. Sometimes, the
- distinction between these two (quite different) objects can be
- blurred; other times, it's important to maintain.</para>
- <para>Lisp threads share the same address space, but maintain
- their own execution context (stacks and registers) and their own
- dynamic binding context.</para>
- =
- <para>Traditionally, OpenMCL's threads have been
- <emphasis>cooperatively scheduled</emphasis>: through a
- combination of compiler and runtime suppport, the currently
- executing lisp thread arranged to be interrrupted at certain
- discrete points in its execution (typically on entry to a
- function and at the beginning of any looping construct). This
- interrupt occurred several dozen times per second; in response,
- a handler function might observe that the current thread had
- used up its time slice and another function (<emphasis>the lisp
- scheduler</emphasis>) would be called to find some other thread
- that was in a runnable state, suspend execution of the current
- thread, and resume execution of the newly executed thread. The
- process of switching contexts between the outgoing and incoming
- threads happened in some mixture of Lisp and assembly language
- code; as far as the OS was concerned, there was one native
- thread running in the Lisp image and its stack pointer and other
- registers just happened to change from time to time.</para>
- <para>Under OpenMCL's cooperative scheduling model, it was
- possible (via the use of the CCL:WITHOUT-INTERRUPTS construct)
- to defer handling of the periodic interrupt that invoked the
- lisp scheduler; it was not uncommon to use WITHOUT-INTERRUPTS to
- gain safe, exclusive access to global data structures. In some
- code (including much of OpenMCL itself) this idiom was very
- common: it was (justifiably) believed to be an efficient way of
- inhibiting the execution of other threads for a short period of
- time.</para>
-
- <para>The timer interrupt that drove the cooperative scheduler
- was only able to (pseudo-)preempt lisp code: if any thread
- called a blocking OS I/O function, no other thread could be
- scheduled until that thread resumed execution of lisp code. Lisp
- library functions were generally attuned to this constraint, and
- did a complicated mixture of polling and "timed blocking" in an
- attempt to work around it. Needless to say, this code is
- complicated and less efficient than it might be; it meant that
- the lisp was a little busier than it should have been when it
- was "doing nothing" (waiting for I/O to be possible.)</para>
-
- <para>For a variety of reasons - better utilization of CPU
- resources on single and multiprocessor systems and better
- integration with the OS in general - threads in OpenMCL 0.14 and
- later are <emphasis>preemptively scheduled. </emphasis>In this
- model, lisp threads are native threads and all scheduling
- decisions involving them are made by the OS kernel. (Those
- decisions might involve scheduling multiple lisp threads
- simultaneously on multiple processors on SMP systems.) This
- change has a number of subtle effects:</para>
-
- <itemizedlist>
- <listitem>
- <para>it is possible for two (or more) lisp threads to be
- executingsimultaneously, possibly trying to access and/or
- modify the same datastructures. Such access really should
- have been coordinated throughthe use of synchronization
- objects regardless of the scheduling modelin effect;
- preemptively scheduled threads increase the chance ofthings
- going wrong at the wrong time and do not offer
- lightweightalternatives to the use of those synchronization
- objects.</para>
- </listitem>
- <listitem>
- <para>even on a single-processor system, a context switch
- can happenon any instruction boundary. Since (in general)
- other threads mightallocate memory, this means that a GC can
- effectively take place atany instruction boundary. That's
- mostly an issue for the compilerand runtime system to be
- aware of, but it means that certain practices(such as trying
- to pass the address of a lisp object to foreign code)that
- were always discouraged are now discouraged
- ... vehemently.</para>
- </listitem>
- <listitem>
- <para>there is no simple and efficient way to "inhibit the
- scheduler"or otherwise gain exclusive access to the entire
- CPU.</para>
- </listitem>
- <listitem>
- <para>There are a variety of simple and efficient ways
- tosynchronize access to particular data
- structures.</para>
- </listitem>
- </itemizedlist>
- <para>As a broad generalization: code that's been aggressively
- tuned to the constraints of the cooperative scheduler may need
- to be redesigned to work well with the preemptive scheduler (and
- code written to run under OpenMCL's interface to the native
- scheduler may be less portable to other CL implementations, many
- of which offer a cooperative scheduler and an API similar to
- OpenMCL (< 0.14) 's.) At the same time, there's a large
- overlap in functionality in the two scheduling models, and it'll
- hopefully be possible to write interesting and useful MP code
- that's largely independent of the underlying scheduling
- details.</para>
- <para>The keyword :OPENMCL-NATIVE-THREADS is on *FEATURES* in
- 0.14 and later and can be used for conditionalization where
- required.</para>
- </sect1>
-
- <sect1 id=3D"Intentionally--Missing-Functionality">
- <title>(Intentionally) Missing Functionality</title>
- <para>Much of the functionality described above is similar to
- that provided by OpenMCL's cooperative scheduler, some other
- parts of which make no sense in a native threads
- implementation.</para>
- <itemizedlist>
- <listitem>
- <para>PROCESS-RUN-REASONS and PROCESS-ARREST-REASONS were
- SETFable process attributes; each was just a list of
- arbitrary tokens. A thread was eligible for scheduling
- (roughly equivalent to being "enabled") if its arrest-reasons
- list was empty and its run-reasons list was not. I don't
- think that it's appropriate to encourage a programming style
- in which otherwise runnable threads are enabled and disabled
- on a regular basis (it's preferable for threads to wait for
- some sort of synchronization event to occur if they can't
- occupy their time productively.)</para>
- </listitem>
- <listitem>
- <para>There were a number of primitives for maintaining
- process queues;that's now the OS's job.</para>
- </listitem>
- <listitem>
- <para>Cooperative threads were based on coroutining
- primitivesassociated with objects of type
- STACK-GROUP. STACK-GROUPs no longerexist.</para>
- </listitem>
- </itemizedlist>
- </sect1>
-
- <sect1 id=3D"Implementation-Decisions-and-Open-Questions">
- <title>Implementation Decisions and Open Questions</title>
- <para> As of August 2003:</para>
- <itemizedlist>
- <listitem>
- <para>It's not clear that exposing
- PROCESS-SUSPEND/PROCESS-RESUME is a good idea: it's not clear
- that they offer ways to win, and it's clear that they offer
- ways to lose.</para>
- </listitem>
- <listitem>
- <para>It has traditionally been possible to reset and enable
- a process that's "exhausted" . (As used here, the
- term"exhausted" means that the process's initial function
- hasrun and returned and the underlying native thread has
- beendeallocated.) One of the principle uses of PROCESS-RESET
- is to "recycle" threads; enabling an exhausted process
- involves creating a new native thread (and stacks and
- synchronization objects and ...),and this is the sort of
- overhead that such a recycling scheme is seeking to avoid. It
- might be worth trying to tighten things up and declare that
- it's an error to apply PROCESS-ENABLE to an exhausted thread
- (and to make PROCESS-ENABLE detect this error.)</para>
- </listitem>
- <listitem>
- <para>When native threads that aren't created by OpenMCL
- first call into lisp, a "foreign process" is created, and
- that process is given its own set of initial bindings and set
- up to look mostly like a process that had been created by
- MAKE-PROCESS. The life cycle of a foreign process is
- certainly different from that of a lisp-created one: it
- doesn't make sense to reset/preset/enable a foreign process,
- and attempts to perform these operations should be
- detectedand treated as errors.</para>
- </listitem>
- </itemizedlist>
- </sect1>
-
- <sect1 id=3D"Porting-Code-from-the-Old-Thread-Model">
- <title>Porting Code from the Old Thread Model</title>
- <para>Older versions of OpenMCL used what are often called
- "user-mode threads", a less versatile threading model which does
- not require specific support from the operating system. This
- section discusses how to port code which was written for that
- mode.</para>
- <para>It's hard to give step-by-step instructions; there are certain=
ly
- a few things that one should look at carefully:</para>
- <itemizedlist>
- <listitem>
- <para>It's wise to be suspicious of most uses
- of WITHOUT-INTERRUPTS; there may be exceptions, but
- WITHOUT-INTERRUPTS is often used as shorthand for
- WITH-APPROPRIATE-LOCKING. Determining what type of locking
- is appropriate and writing the code to implement it is
- likely to be straightforward and simple most of the
- time.</para>
- </listitem>
- <listitem>
- <para>I've only seen one case where a process's "run reasons"
- were used to communicate information as well as tocontrol
- execution; I don't think that this is a common idiom, but may
- be mistaken about that.
- </para>
- </listitem>
- <listitem>
- <para>It's certainly possible that programs written
- for cooperatively scheduled lisps that have run reliably for
- a long timehave done so by accident: resource-contention
- issues tend to be timing-sensitive, and decoupling thread
- scheduling from lisp program execution affects timing. I know
- that there is or was code in both OpenMCL and commercial MCL
- that was written under the explicit assumption that certain
- sequences of open-coded operations were uninterruptable; it's
- certainly possible that the same assumptions have been made
- (explicitly or otherwise) by application developers.</para>
- </listitem>
- </itemizedlist>
- </sect1>
-
- <sect1 id=3D"Background-Terminal-Input">
- <title>Background Terminal Input</title>
-
- <sect2 id=3D"backgrount-ti-overview">
- <title>Overview</title>
- <para>
- Unless and until OpenMCL provides alternatives (via window
- streams, telnet streams, or some other mechanism) all lisp
- processes share a common *TERMINAL-IO* stream (and therefore
- share *DEBUG-IO*, *QUERY-IO*, and other standard and
- internal interactive streams.)</para>
- <para>It's anticipated that most lisp processes other than
- the "Initial" process run mostly in the background. If a
- background process writes to the output side of
- *TERMINAL-IO*, that may be a little messy and a little
- confusing to the user, but it shouldn't really be
- catastrophic. All I/O to OpenMCL's buffered streams goes
- thru a locking mechanism that prevents the worst kinds of
- resource-contention problems.</para>
- <para>Although the problems associated with terminal output
- from multiple processes may be mostly cosmetic, the question
- of which process receives input from the terminal is likely
- to be a great deal more important. The stream locking
- mechanisms can make a confusing situation even worse:
- competing processes may "steal" terminal input from each
- other unless locks are held longer than they otherwise need
- to be, and locks can be held longer than they need to be (as
- when a process is merely waiting for input to become
- available on an underlying file descriptor).</para>
- <para>Even if background processes rarely need to
- intentionally read input from the terminal, they may still
- need to do so in response to errors or other unanticipated
- situations. There are tradeoffs involved in any solution to
- this problem. The protocol described below allows background
- processes which follow it to reliably prompt for and receive
- terminal input. Background processes which attempt to
- receive terminal input without following this protocol will
- likely hang indefinitely while attempting to do so. That's
- certainly a harsh tradeoff, but since attempts to read
- terminal input without following this protocol only worked
- some of the time anyway, it doesn't seem to be an
- unreasonable one.</para>
- <para>In the solution described here (and introduced in
- OpenMCL 0.9), the internal stream used to provide terminal
- input is always locked by some process (the "owning"
- process.) The initial process (the process that typically
- runs the read-eval-print loop) owns that stream when it's
- first created. By using the macro WITH-TERMINAL-INPUT,
- background processes can temporarily obtain ownership of the
- terminal and relinquish ownership to the previous owner when
- they're done with it.</para>
- <para>In OpenMCL, BREAK, ERROR, CERROR, Y-OR-N-P,
- YES-OR-NO-P, and CCL:GET-STRING- FROM-USER are all defined
- in terms of WITH-TERMINAL-INPUT, as are the :TTY
- user-interfaces to STEP and INSPECT.</para>
- </sect2>
-
- <sect2 id=3D"background-terminal-example">
- <title>An example</title>
- <programlisting>
-? Welcome to OpenMCL Version (Beta: linux) 0.9!
-?
-
-? (process-run-function "sleeper" #'(lambda () (sleep 5) (break "broken")))
-#<PROCESS sleeper(1) [Enabled] #x3063B33E>
-
-?
-;;
-;; Process sleeper(1) needs access to terminal input.
-;;
-</programlisting>
- <para>This example was run under ILISP; ILISP often gets confused =
if one
- tries to enter input and "point" doesn't follow a prompt.
- Entering a "simple" expression at this point gets it back in
- synch; that's otherwise not relevant to this example.</para>
- <programlisting>
-()
-NIL
-? (:y 1)
-;;
-;; process sleeper(1) now controls terminal input
-;;
-> Break in process sleeper(1): broken
-> While executing: #<Anonymous Function #x3063B276>
-> Type :GO to continue, :POP to abort.
-> If continued: Return from BREAK.
-Type :? for other options.
-1 > :b
-(30C38E30) : 0 "Anonymous Function #x3063B276" 52
-(30C38E40) : 1 "Anonymous Function #x304984A6" 376
-(30C38E90) : 2 "RUN-PROCESS-INITIAL-FORM" 340
-(30C38EE0) : 3 "%RUN-STACK-GROUP-FUNCTION" 768
-1 > :pop
-;;
-;; control of terminal input restored to process Initial(0)
-;;
-?
-</programlisting>
- </sect2>
-
- <sect2 id=3D"A-more-elaborate-example-">
- <title>A more elaborate example.</title>
- <para>If a background process ("A") needs access to the terminal
- input stream and that stream is owned by another background process
- ("B"), process "A" announces that fact, then waits until
- the initial process regains control.</para>
- <programlisting>
-? Welcome to OpenMCL Version (Beta: linux) 0.9!
-?
-
-? (process-run-function "sleep-60" #'(lambda () (sleep 60) (break "Huh?")))
-#<PROCESS sleep-60(1) [Enabled] #x3063BF26>
-
-? (process-run-function "sleep-5" #'(lambda () (sleep 5) (break "quicker")=
))
-#<PROCESS sleep-5(2) [Enabled] #x3063D0A6>
-
-? ;;
-;; Process sleep-5(2) needs access to terminal input.
-;;
-()
-NIL
-
-? (:y 2)
-;;
-;; process sleep-5(2) now controls terminal input
-;;
-> Break in process sleep-5(2): quicker
-> While executing: #x3063CFDE>
-> Type :GO to continue, :POP to abort.
-> If continued: Return from BREAK.
-Type :? for other options.
-1 > ;; Process sleep-60(1) will need terminal access when
-;; the initial process regains control of it.
-;;
-()
-NIL
-1 > :pop
-;;
-;; Process sleep-60(1) needs access to terminal input.
-;;
-;;
-;; control of terminal input restored to process Initial(0)
-;;
-
-? (:y 1)
-;;
-;; process sleep-60(1) now controls terminal input
-;;
-> Break in process sleep-60(1): Huh?
-> While executing: #x3063BE5E>
-> Type :GO to continue, :POP to abort.
-> If continued: Return from BREAK.
-Type :? for other options.
-1 > :pop
-;;
-;; control of terminal input restored to process Initial(0)
-;;
-
-?
-</programlisting>
- </sect2>
-
- <sect2 id=3D"Summary">
- <title>Summary</title>
- <para>This scheme is certainly not bulletproof: imaginative
- use of PROCESS-INTERRUPT and similar functions might be able
- to defeat it and deadlock the lisp, and any scenario where
- several background processes are clamoring for access to the
- shared terminal input stream at the same time is likely to be
- confusing and chaotic. (An alternate scheme, where the input
- focus was magically granted to whatever thread the user was
- thinking about, was considered and rejected due to technical
- limitations.)</para>
- <para>The longer-term fix would probably involve using network or
- window-system streams to give each process unique instances of
- *TERMINAL-IO*.</para>
- <para>Existing code that attempts to read from *TERMINAL-IO*
- from a background process will need to be changed to use
- WITH-TERMINAL-INPUT. Since that code was probably not working
- reliably in previous versions of OpenMCL, this requirement
- doesn't seem to be too onerous.</para>
- <para>Note that WITH-TERMINAL-INPUT both requests ownership of
- the terminal input stream and promises to restore that
- ownership to the initial process when it's done with it. An ad
- hoc use of READ or READ-CHAR doesn't make this promise; this
- is the rationale for the restriction on the :Y command.</para>
- </sect2>
- </sect1>
-
- <sect1 id=3D"The-Threads-which-OpenMCL-Uses-for-Its-Own-Purposes">
- <title>The Threads which OpenMCL Uses for Its Own Purposes</title>
- <para>
-In the "tty world", OpenMCL starts out with 2 lisp-level threads:</para>
- <programlisting>
-? :proc
-1 : -> listener [Active]
-0 : Initial [Active]
-</programlisting>
- <para>If you look at a running OpenMCL with a debugging tool,
- such as GDB, or Apple's Thread Viewer.app, you'll see an
- additional kernel-level thread on Darwin; this is used by the
- Mach exception-handling mechanism.</para>
- <para>The initial thread, conveniently named "initial", is the
- one that was created by the operating system when it launched
- OpenMCL. It maps the heap image into memory, does some
- Lisp-level initialization, and, when the Cocoa IDE isn't being
- used, creates the thread "listener", which runs the top-level
- loop that reads input, evaluates it, and prints the
- result.</para>
- <para>After the listener thread is created, the initial thread
- does "housekeeping": it sits in a loop, sleeping most of the
- time and waking up occasionally to do "periodic tasks". These
- tasks include forcing output on specified interactive streams,
- checking for and handling control-C interrupts, etc. Currently,
- those tasks also include polling for the exit status of external
- processes and handling some kinds of I/O to and from those
- processes.</para>
- <para>In this environment, the initial thread does these
- "housekeeping" activities as necessary, until
- <literal>ccl:quit</literal> is called;
- <literal>quit</literal>ting interrupts the initial thread, which
- then ends all other threads in as orderly a fashion as possible
- and calls the C function <literal>#_exit</literal>.</para>
- <para>The short-term plan is to handle each external-process in
- a dedicated thread; the worst-case behavior of the current
- scheme can involve busy-waiting and excessive CPU utilization
- while waiting for an external process to terminate in some
- cases.</para>
- <para>The Cocoa features use more threads. Adding a Cocoa
- listener creates two threads:</para>
- <programlisting>
-? :proc
-3 : -> Listener [Active]
-2 : housekeeping [Active]
-1 : listener [Active]
-0 : Initial [Active]
- </programlisting>
- <para>The Cocoa event loop has to run in the initial thread;
- when the event loop starts up, it creates a new thread to do the
- "housekeeping" tasks which the initial thread would do in the
- terminal-only mode. The initial thread then becomes the one to
- receive all Cocoa events from the window server; it's the only
- thread which can.</para>
- <para>It also creates one "Listener" (capital-L) thread for each
- listener window, with a lifetime that lasts as long as the
- thread does. So, if you open a second listener, you'll see five
- threads all together:</para>
- <programlisting>
-? :proc
-4 : -> Listener-2 [Active]
-3 : Listener [Active]
-2 : housekeeping [Active]
-1 : listener [Active]
-0 : Initial [Active]
-</programlisting>
- <para>Unix signals, such as SIGINT (control-C), invoke a handler
- installed by the Lisp kernel. Although the OS doesn't make any
- specific guarantee about which thread will receive the signal,
- in practice, it seems to be the initial thread. The handler
- just sets a flag and returns; the housekeeping thread (which may
- be the initial thread, if Cocoa's not being used) will check for
- the flag and take whatever action is appropriate to the
- signal.</para>
- <para>In the case of SIGINT, the action is to enter a break
- loop, by calling on the thread being interrupted. When there's
- more than one Lisp listener active, it's not always clear what
- thread that should be, since it really depends on the user's
- intentions, which there's no way to divine programmatically. To
- make its best guess, the handler first checks whether the value
- of <literal>ccl:*interactive-abort-process*</literal> is a
- thread, and, if so, uses it. If that fails, it chooses the
- thread which currently "owns" the default terminal input stream;
- see .</para>
- <para>In the bleeding-edge version of the Cocoa support which is
- based on Hemlock, an Emacs-like editor, each editor window has a
- dedicated thread associated with it. When a keypress event
- comes in which affects that specific window the initial thread
- sends it to the window's dedicated thread. The dedicated thread
- is responsible for trying to interpret keypresses as Hemlock
- commands, applying those commands to the active buffer; it
- repeats this in a loop, until the window closes. The initial
- thread handles all other events, such as mouse clicks and
- drags.</para>
- <para>This thread-per-window scheme makes many things simpler,
- including the process of entering a "recursive command loop" in
- commands like "Incremental Search Forward", etc. (It might be
- possible to handle all Hemlock commands in the Cocoa event
- thread, but these "recursive command loops" would have to
- maintain a lot of context/state information; threads are a
- straightforward way of maintaining that information.)</para>
- <para>Currently (August 2004), when a dedicated thread needs to
- alter the contents of the buffer or the selection, it does so by
- invoking methods in the initial thread, for synchronization
- purposes, but this is probably overkill and will likely be
- replaced by a more efficient scheme in the future.</para>
- <para>The per-window thread could probably take more
- responsibility for drawing and handling the screen than it
- currently does; -something- needs to be done to buffer screen
- updates a bit better in some cases: you don't need to see
- everything that happens during something like indentation; you
- do need to see the results...</para>
- <para>When Hemlock is being used, listener windows are editor
- windows, so in addition to each "Listener" thread, you should
- also see a thread which handles Hemlock command
- processing.</para>
- <para>The Cocoa runtime may make additional threads in certain
- special situations; these threads usually don't run lisp code,
- and rarely if ever run much of it.</para>
- </sect1>
-
- <sect1 id=3D"Threads-Dictionary">
- <title>Threads Dictionary</title>
- <refentry id=3D"f_all-processes">
- <indexterm zone=3D"f_all-processes">
- <primary>all-processes</primary>
- </indexterm>
-
- <refnamediv>
- <refname>ALL-PROCESSES</refname>
- <refpurpose>Obtain a fresh list of all known Lisp
- threads.</refpurpose>
- <refclass>Function</refclass>
- </refnamediv>
-
- <refsynopsisdiv>
- <synopsis>
- <function>all-processes</function> =3D> result
- </synopsis>
- </refsynopsisdiv>
-
- <refsect1>
- <title>Values</title>
- =
- <variablelist>
- <varlistentry>
- <term>result</term>
- <listitem>
- <para>a list of all lisp processes (threads)
- known to OpenMCL.</para>
- </listitem>
- </varlistentry>
- </variablelist>
- </refsect1>
- <refsect1>
- <title>Description</title>
-
- <para>Returns a list of all lisp processes (threads) known
- to OpenMCL as of
- the precise instant it's called. It's safe to traverse
- this list and to modify the cons cells that comprise that list
- (it's freshly consed.) Since other threads can create and kill
- threads at any time, there's generally no way to get an
- "accurate" list of all threads, and (generally) no
- sense in which such a list can be accurate.</para>
- </refsect1>
-
- <refsect1>
- <title>See Also</title>
- =
- <simplelist type=3D"inline">
- <member><xref linkend=3D"v_current-process"/></member>
- </simplelist>
- </refsect1>
- </refentry>
-
- <refentry id=3D"f_make-process">
- <indexterm zone=3D"f_make-process">
- <primary>make-process</primary>
- </indexterm>
-
- <refnamediv>
- <refname>MAKE-PROCESS</refname>
- <refpurpose>Creates and returns a new process.</refpurpose>
- <refclass>Function</refclass>
- </refnamediv>
-
- <refsynopsisdiv>
- <synopsis>
- <function>make-process</function>
- name &key;
- persistent priority class stack-size vstack-size
- tstack-size initial-bindings use-standard-initial-bindings
- =3D> process
- </synopsis>
- </refsynopsisdiv>
-
- <refsect1>
- <title>Arguments and Values</title>
- =
- <variablelist>
- <varlistentry>
- <term>name</term>
- =
- <listitem>
- <para>a string, used to identify the process.</para>
- </listitem>
- </varlistentry>
- =
- <varlistentry>
- <term>persistent</term>
- =
- <listitem>
- <para>if true, requests that information about the process
- be retained by SAVE-APPLICATION so that an equivalent
- process can be restarted when a saved image is run. The
- default is nil.</para>
- </listitem>
- </varlistentry>
- =
- <varlistentry>
- <term>priority</term>
- =
- <listitem>
- <para>ignored. It
- shouldn't be ignored of course, but there are
- complications on some platforms. The default is 0.</para>
- </listitem>
- </varlistentry>
- =
- <varlistentry>
- <term>class</term>
- =
- <listitem>
- <para>the class of process object to create;
- should be a subclass of CCL:PROCESS. The default is
- CCL:PROCESS.</para>
- </listitem>
- </varlistentry>
- =
- <varlistentry>
- <term>stack-size</term>
- =
- <listitem>
- <para>the size, in bytes, of the newly-created process's
- control stack; used for foreign function calls and to save
- function return address context. The default is
- CCL:*DEFAULT-CONTROL-STACK-SIZE*.</para>
- </listitem>
- </varlistentry>
- =
- <varlistentry>
- <term>vstack-size</term>
- =
- <listitem>
- <para>the size, in bytes, of the newly-created process's
- value stack; used for lisp function arguments, local
- variables, and other stack-allocated lisp objects.
- The default is CCL:*DEFAULT-VALUE-STACK-SIZE*.</para>
- </listitem>
- </varlistentry>
- =
- <varlistentry>
- <term>tstack-size</term>
- =
- <listitem>
- <para>the size, in bytes, of the newly-created process's
- temp stack; used for the allocation of dynamic-extent
- objects. The default is CCL:*DEFAULT-TEMP-STACK-SIZE*.</para>
- </listitem>
- </varlistentry>
- =
- <varlistentry>
- <term>use-standard-initial-bindings</term>
- =
- <listitem>
- <para>when true, the global "standard initial
- bindings" are put into effect in the new thread before. See
- DEF-STANDARD-INITIAL-BINDING. "standard" initial bindings
- are put into effect before any bindings specified by
- :initial-bindings are. The default is t.</para>
- </listitem>
- </varlistentry>
- =
- <varlistentry>
- <term>initial-bindings</term>
- =
- <listitem>
- <para>an alist of (<varname>symbol</varname> .
- <varname>valueform</varname>) pairs, which can be
- used to initialize special variable bindings in the new
- thread. Each <varname>valueform</varname> is used to
- compute the value of a new binding of
- <varname>symbol</varname> in the execution environment of
- the newly-created thread. The default is nil.</para>
- </listitem>
- </varlistentry>
- =
- <varlistentry>
- <term>process</term>
- =
- <listitem>
- <para>the newly-created process.</para>
- </listitem>
- </varlistentry>
- </variablelist>
- </refsect1>
-
- <refsect1>
- <title>Description</title>
-
- <para>Creates and returns a new lisp process (thread) with the
- specified attributes. <varname>process</varname> will not begin
- execution immediately; it will need to be
- <emphasis>preset</emphasis> (given
- an initial function to run, as by
- <xref linkend=3D"f_process-preset"/>) and
- <emphasis>enabled</emphasis>
- (allowed to execute, as by <xref linkend=3D"f_process-enable"/>)
- before it's able to actually do anything.</para>
-
- <para>If <varname>valueform</varname> is a function, it is
- called, with no arguments, in the execution environment of the
- newly-created thread; the primary value it returns is used for
- the binding of the corresponding <varname>symbol</varname>.</para>
-
- <para>Otherwise, <varname>valueform</varname> is evaluated in the
- execution
- environment of the newly-created thread, and the resulting value
- is used.</para>
- </refsect1>
-
- <refsect1>
- <title>See Also</title>
- =
- <simplelist type=3D"inline">
- <member><xref linkend=3D"f_process-preset"/></member>
- <member><xref linkend=3D"f_process-enable"/></member>
- <member><xref linkend=3D"f_process-run-function"/></member>
- </simplelist>
- </refsect1>
- </refentry>
-
- <refentry id=3D"f_process-suspend">
- <indexterm zone=3D"f_process-suspend">
- <primary>process-suspend</primary>
- </indexterm>
-
- <refnamediv>
- <refname>PROCESS-SUSPEND</refname>
- <refpurpose>Suspends a specified process.</refpurpose>
- <refclass>Function</refclass>
- </refnamediv>
- =
- <refsynopsisdiv>
- <synopsis><function>process-suspend</function> process
- =3D> result</synopsis>
- </refsynopsisdiv>
-
- <refsect1>
- <title>Arguments and Values</title>
- =
- <variablelist>
- <varlistentry>
- <term>process</term>
- <listitem>
- <para>a lisp process (thread).</para>
- </listitem>
- </varlistentry>
- <varlistentry>
- <term>result</term>
- <listitem>
- <para>T if <varname>process</varname> had been runnable
- and is now suspended; NIL otherwise. That is, T if
- <varname>process</varname>'s
- <xref linkend=3D"f_process-suspend-count"/>
- transitioned from 0 to 1.</para>
- </listitem>
- </varlistentry>
- </variablelist>
- </refsect1>
-
- <refsect1>
- <title>Description</title>
-
- <para>Suspends <varname>process</varname>, preventing it from
- running, and stopping it if it was already running. This is a fairly
- expensive operation, because it involves a few
- calls to the OS. It also risks creating deadlock if used
- improperly, for instance, if the process being suspended owns a
- lock or other resource which another process will wait for.</para>
-
- <para>
- Each
- call to <function>process-suspend</function> must be reversed by
- a matching call to <xref linkend=3D"f_process-resume"/>
- before <varname>process</varname> is able to run. What
- <function>process-suspend</function> actually does is increment
- the <xref linkend=3D"f_process-suspend-count"/> of
- <varname>process</varname>.
- </para>
-
- <para>A process can suspend itself; it it's successful in doing
- so, then it can obviously only be resumed by some other
- process.</para>
- </refsect1>
-
- <refsect1>
- <title>See Also</title>
- =
- <simplelist type=3D"inline">
- <member><xref linkend=3D"f_process-resume"/></member>
- <member><xref linkend=3D"f_process-suspend-count"/></member>
- </simplelist>
- </refsect1>
-
- <refsect1>
- <title>Notes</title>
- <para><function>process-suspend</function> was previously called
- <function>process-disable</function>.
- <xref linkend=3D"f_process-enable"/>
- now names a function for which there is no
- obvious inverse, so <function>process-disable</function>
- is no longer
- defined.</para>
- </refsect1>
- </refentry>
-
- <refentry id=3D"f_process-resume">
- <indexterm zone=3D"f_process-resume">
- <primary>process-resume</primary>
- </indexterm>
-
- <refnamediv>
- <refname>PROCESS-RESUME</refname>
- <refpurpose>Resumes a specified process which had previously
- been suspended by process-suspend.</refpurpose>
- <refclass>Function</refclass>
- </refnamediv>
-
- <refsynopsisdiv>
- <synopsis><function>process-resume</function> process
- =3D> result</synopsis>
- </refsynopsisdiv>
-
- <refsect1>
- <title>Arguments and Values</title>
- =
- <variablelist>
- <varlistentry>
- <term>process</term>
- <listitem>
- <para>a lisp process (thread).</para>
- </listitem>
- </varlistentry>
- <varlistentry>
- <term>result</term>
- <listitem>
- <para>T if <varname>process</varname> had been suspended
- and is now runnable; NIL otherwise. That is, T if
- <varname>process</varname>'s
- <xref linkend=3D"f_process-suspend-count"/>
- transitioned from to 0.
- </para>
- </listitem>
- </varlistentry>
- </variablelist>
- </refsect1>
-
- <refsect1>
- <title>Description</title>
-
- <para>Undoes the effect of a previous call to
- <xref linkend=3D"f_process-suspend"/>; if
- all such calls are undone, makes the process runnable. Has no
- effect if the process is not suspended. What
- <function>process-resume</function> actually does is decrement
- the <xref linkend=3D"f_process-suspend-count"/> of
- <varname>process</varname>, to a minimum of 0.</para>
- </refsect1>
-
- <refsect1>
- <title>See Also</title>
- =
- <simplelist type=3D"inline">
- <member><xref linkend=3D"f_process-suspend"/></member>
- <member><xref linkend=3D"f_process-suspend-count"/></member>
- </simplelist>
- </refsect1>
-
- <refsect1>
- <title>Notes</title>
-
- <para>
- This was previously called PROCESS-ENABLE;
- <xref linkend=3D"f_process-enable"/> now does something slightly
- different.
- </para>
- </refsect1>
- </refentry>
-
- <refentry id=3D"f_process-suspend-count">
- <indexterm zone=3D"f_process-suspend-count">
- <primary>process-suspend-count</primary>
- </indexterm>
-
- <refnamediv>
- <refname>PROCESS-SUSPEND-COUNT</refname>
- <refpurpose>Returns the number of currently-pending suspensions
- applicable to a given process.</refpurpose>
- <refclass>Function</refclass>
- </refnamediv>
-
- <refsynopsisdiv>
- <synopsis>
- <function>process-suspend-count</function>
- process =3D> result
- </synopsis>
- </refsynopsisdiv>
-
- <refsect1>
- <title>Arguments and Values</title>
-
- <variablelist>
- <varlistentry>
- <term>process</term>
- <listitem>
- <para>a lisp process (thread).</para>
- </listitem>
- </varlistentry>
- <varlistentry>
- <term>result</term>
- <listitem>
- <para>The number of "outstanding"
- <xref linkend=3D"f_process-suspend"/> calls on
- <varname>process</varname>, or NIL if
- <varname>process</varname> has expired.
- </para>
- </listitem>
- </varlistentry>
- </variablelist>
- </refsect1>
-
- <refsect1>
- <title>Description</title>
-
- <para>An "outstanding" <xref linkend=3D"f_process-suspend"/> call
- is one which has not yet been reversed by a call to
- <xref linkend=3D"f_process-resume"/>. A process expires when
- its initial function returns, although it may later be
- reset.</para>
-
- <para>A process is <emphasis>runnable</emphasis> when it has a
- <function>process-suspend-count</function> of 0, has been
- preset as by <xref linkend=3D"f_process-preset"/>, and has been
- enabled as by <xref linkend=3D"f_process-enable"/>. Newly-created
- processes have a <function>process-suspend-count</function> of
- 0.</para>
- </refsect1>
-
- <refsect1>
- <title>See Also</title>
- =
- <simplelist type=3D"inline">
- <member><xref linkend=3D"f_process-suspend"/></member>
- <member><xref linkend=3D"f_process-resume"/></member>
- </simplelist>
- </refsect1>
- </refentry>
-
- <refentry id=3D"f_process-preset">
- <indexterm zone=3D"f_process-preset">
- <primary>process-preset</primary>
- </indexterm>
-
- <refnamediv>
- <refname>PROCESS-PRESET</refname>
- <refpurpose>Sets the initial function and arguments of a specified
- process.</refpurpose>
- <refclass>Function</refclass>
- </refnamediv>
-
- <refsynopsisdiv>
- <synopsis><function>process-preset</function>
- process function &rest; args
- =3D> result</synopsis>
- </refsynopsisdiv>
-
- <refsect1>
- <title>Arguments and Values</title>
-
- <variablelist>
- <varlistentry>
- <term>process</term>
- <listitem>
- <para>a lisp process (thread).</para>
- </listitem>
- </varlistentry>
- <varlistentry>
- <term>function</term>
- <listitem>
- <para>a function, designated by itself or by a symbol
- which names it.
- </para>
- </listitem>
- </varlistentry>
- <varlistentry>
- <term>args</term>
- <listitem>
- <para>a list of values, appropriate as arguments to
- <varname>function</varname>.</para>
- </listitem>
- </varlistentry>
- <varlistentry>
- <term>result</term>
- <listitem>
- <para>undefined.</para>
- </listitem>
- </varlistentry>
- </variablelist>
- </refsect1>
-
- <refsect1>
- <title>Description</title>
-
- <para>Typically used to initialize a newly-created or newly-reset
- process, setting things up so that when <varname>process</varname>
- becomes enabled, it will begin execution by
- applying <varname>function</varname> to <varname>args</varname>.
- <function>process-preset</function> does not enable
- <varname>process</varname>,
- although a process must be <function>process-preset</function>
- before it can be enabled. Processes are normally enabled by
- <xref linkend=3D"f_process-enable"/>.
- </para>
- </refsect1>
-
- <refsect1>
- <title>See Also</title>
- =
- <simplelist type=3D"inline">
- <member><xref linkend=3D"f_make-process"/></member>
- <member><xref linkend=3D"f_process-enable"/></member>
- <member><xref linkend=3D"f_process-run-function"/></member>
- </simplelist>
- </refsect1>
- </refentry>
-
- <refentry id=3D"f_process-enable">
- <indexterm zone=3D"f_process-enable">
- <primary>process-enable</primary>
- </indexterm>
-
- <refnamediv>
- <refname>PROCESS-ENABLE</refname>
- <refpurpose>Begins executing the initial function of a specified
- process.</refpurpose>
- <refclass>Function</refclass>
- </refnamediv>
-
- <refsynopsisdiv>
- <synopsis><function>process-enable</function>
- process &optional; timeout
- </synopsis>
- </refsynopsisdiv>
-
- <refsect1>
- <title>Arguments and Values</title>
-
- <variablelist>
- <varlistentry>
- <term>process</term>
- <listitem>
- <para>a lisp process (thread).</para>
- </listitem>
- </varlistentry>
- <varlistentry>
- <term>timeout</term>
- <listitem>
- <para>a time interval in seconds. May be any
- non-negative real number the <function>floor</function> of
- which fits in 32 bits. The default is 1.</para>
- </listitem>
- </varlistentry>
- <varlistentry>
- <term>result</term>
- <listitem>
- <para>undefined.</para>
- </listitem>
- </varlistentry>
- </variablelist>
- </refsect1>
-
- <refsect1>
- <title>Description</title>
-
- <para>Tries to begin the execution of <varname>process</varname>.
- An error is signaled if <varname>process</varname> has never
- been <xref linkend=3D"f_process-preset"/>. Otherwise,
- <varname>process</varname> invokes its initial function.
- </para>
- =
- <para><function>process-enable</function> attempts to
- synchronize with <varname>process</varname>, which is presumed
- to be reset or in the act of resetting itself. If this attempt
- is not successful within the time interval specified by
- <varname>timeout</varname>, a continuable error is signaled,
- which offers the opportunity to continue waiting.
- </para>
-
- <para>A process cannot meaningfully attempt to enable itself.</para>
- </refsect1>
-
- <refsect1>
- <title>See Also</title>
- =
- <simplelist type=3D"inline">
- <member><xref linkend=3D"f_make-process"/></member>
- <member><xref linkend=3D"f_process-preset"/></member>
- <member><xref linkend=3D"f_process-run-function"/></member>
- </simplelist>
- </refsect1>
-
- <refsect1>
- <title>Notes</title>
-
- <para>It would be nice to have more discussion of what it means
- to synchronize with the process.</para>
- </refsect1>
- </refentry>
-
- <refentry id=3D"f_process-run-function">
- <indexterm zone=3D"f_process-run-function">
- <primary>process-run-function</primary>
- </indexterm>
-
- <refnamediv>
- <refname>PROCESS-RUN-FUNCTION</refname>
- <refpurpose>Creates a process, presets it, and enables it.
- </refpurpose>
- <refclass>Function</refclass>
- </refnamediv>
-
- <refsynopsisdiv>
- <synopsis><function>process-run-function</function>
- process-specifier function &rest; args =3D> process</synopsis>
-
- <variablelist>
- <varlistentry>
- <term>process-specifier</term>
- <listitem>
- <para>
- <varname>name</varname> | =
- (&key; <varname>name</varname>
- <varname>persistent</varname>
- <varname>priority</varname>
- <varname>class</varname>
- <varname>stack-size</varname>
- <varname>vstack-size</varname>
- <varname>tstack-size</varname>)
- </para>
- </listitem>
- </varlistentry>
- </variablelist>
- </refsynopsisdiv>
-
- <refsect1>
- <title>Arguments and Values</title>
-
- <variablelist>
- <varlistentry>
- <term>name</term>
- <listitem>
- <para>a string, used to identify the process.
- Passed to <function>make-process</function>.</para>
- </listitem>
- </varlistentry>
- <varlistentry>
- <term>function</term>
- <listitem>
- <para>a function, designated by itself or by a symbol
- which names it. Passed to
- <function>preset-process</function>.
- </para>
- </listitem>
- </varlistentry>
- <varlistentry>
- <term>persistent</term>
- =
- <listitem>
- <para>a boolean, passed to <function>make-process</function>.
- </para>
- </listitem>
- </varlistentry>
- =
- <varlistentry>
- <term>priority</term>
- =
- <listitem>
- <para>ignored.</para>
- </listitem>
- </varlistentry>
- =
- <varlistentry>
- <term>class</term>
- =
- <listitem>
- <para>a subclass of CCL:PROCESS. Passed to
- <function>make-process</function>.</para>
- </listitem>
- </varlistentry>
- =
- <varlistentry>
- <term>stack-size</term>
- =
- <listitem>
- <para>a size, in bytes. Passed to
- <function>make-process</function>.</para>
- </listitem>
- </varlistentry>
- =
- <varlistentry>
- <term>vstack-size</term>
- =
- <listitem>
- <para>a size, in bytes. Passed to
- <function>make-process</function>.</para>
- </listitem>
- </varlistentry>
- =
- <varlistentry>
- <term>tstack-size</term>
- =
- <listitem>
- <para>a size, in bytes. Passed to
- <function>make-process</function>.</para>
- </listitem>
- </varlistentry>
- <varlistentry>
- <term>process</term>
- <listitem>
- <para>the newly-created process.</para>
- </listitem>
- </varlistentry>
- </variablelist>
- </refsect1>
-
- <refsect1>
- <title>Description</title>
-
- <para>Creates a lisp process (thread) via
- <xref linkend=3D"f_make-process"/>,
- presets it via <xref linkend=3D"f_process-preset"/>, and
- enables it via <xref linkend=3D"f_process-enable"/>. This means
- that <varname>process</varname> will immediately begin to
- execute.
- <function>process-run-function</function> is
- the simplest way to create and run a process.
- </para>
- </refsect1>
-
- <refsect1>
- <title>See Also</title>
- =
- <simplelist type=3D"inline">
- <member><xref linkend=3D"f_make-process"/></member>
- <member><xref linkend=3D"f_process-preset"/></member>
- <member><xref linkend=3D"f_process-enable"/></member>
- </simplelist>
- </refsect1>
- </refentry>
-
- <refentry id=3D"f_process-interrupt">
- <indexterm zone=3D"f_process-interrupt">
- <primary>process-interrupt</primary>
- </indexterm>
-
- <refnamediv>
- <refname>PROCESS-INTERRUPT</refname>
- <refpurpose>Arranges for the target process to invoke a
- specified function at some point in the near future, and then
- return to what it was doing.</refpurpose>
- <refclass>Function</refclass>
- </refnamediv>
-
- <refsynopsisdiv>
- <synopsis><function>process-interrupt</function>
- process function &rest; args =3D> result</synopsis>
- </refsynopsisdiv>
-
- <refsect1>
- <title>Arguments and Values</title>
-
- <variablelist>
- <varlistentry>
- <term>process</term>
- <listitem>
- <para>a lisp process (thread).</para>
- </listitem>
- </varlistentry>
- <varlistentry>
- <term>function</term>
- <listitem>
- <para>a function.
- </para>
- </listitem>
- </varlistentry>
- <varlistentry>
- <term>args</term>
- <listitem>
- <para>a list of values, appropriate as arguments to
- <varname>function</varname>.</para>
- </listitem>
- </varlistentry>
- <varlistentry>
- <term>result</term>
- <listitem>
- <para>the result of applying <varname>function</varname>
- to <varname>args</varname> if <varname>proceess</varname>
- is the <function>current-process</function>, otherwise
- NIL.</para>
- </listitem>
- </varlistentry>
- </variablelist>
- </refsect1>
-
- <refsect1>
- <title>Description</title>
-
- <para>Arranges for <varname>process</varname>
- to apply <varname>function</varname> to <varname>args</varname> at
- some point in the near future (interrupting whatever
- <varname>process</varname>
- was doing.) If <varname>function</varname> returns normally,
- <varname>process</varname> resumes
- execution at the point at which it was interrupted.</para>
-
- <para><varname>process</varname> must be in an enabled state in
- order to respond
- to a <function>process-interrupt</function> request. It's
- perfectly legal for a process to call
- <function>process-interrupt</function> on itself.</para>
-
- <para><function>process-interrupt</function>
- uses asynchronous POSIX signals to interrupt threads. If the
- thread being interrupted is executing lisp code, it can
- respond to the interrupt almost immediately (as soon as it
- has finished pseudo-atomic operations like consing and
- stack-frame initialization.)</para>
-
- <para>If the interrupted thread is
- blocking in a system call, that system call is aborted by
- the signal and the interrupt is handled on return.
- </para>
-
- <para>It is
- still difficult to reliably interrupt arbitrary foreign code
- (that may be stateful or otherwise non-reentrant); the
- interrupt request is handled when such foreign code returns
- to or enters lisp.</para>
- </refsect1>
-
- <refsect1>
- <title>See Also</title>
- =
- <simplelist type=3D"inline">
- <member><xref linkend=3D"m_without-interrupts"/></member>
- </simplelist>
- </refsect1>
-
- <refsect1>
- <title>Notes</title>
-
- <para>It would probably be better for <varname>result</varname>
- to always be NIL, since the present behaviour is inconsistent.
- </para>
-
- <para>
- <function>Process-interrupt</function> works by sending signals
- between threads, via the C function
- <function>#_pthread_signal</function>. It could be argued
- that it should be done in one of several possible other ways
- under
- Darwin, to make it practical to asynchronously interrupt
- things which make heavy use of the Mach nanokernel.
- </para>
- </refsect1>
- </refentry>
-
- <refentry id=3D"v_current-process">
- <indexterm zone=3D"v_current-process">
- <primary>*current-process*</primary>
- </indexterm>
-
- <refnamediv>
- <refname>*CURRENT-PROCESS*</refname>
- <refpurpose>Bound in each process, to that process
- itself.</refpurpose>
- <refclass>Variable</refclass>
- </refnamediv>
-
- <refsect1>
- <title>Value Type</title>
-
- <para>A lisp process (thread).</para>
- </refsect1>
-
- <refsect1>
- <title>Initial Value</title>
- =
- <para>Bound separately in each process, to that process itself.
- </para>
- </refsect1>
-
- <refsect1>
- <title>Description</title>
-
- <para>Used when lisp code needs to find out what process it is
- executing in. Shouldn't be set by user code.</para>
- </refsect1>
-
- <refsect1>
- <title>See Also</title>
- =
- <simplelist type=3D"inline">
- <member><xref linkend=3D"f_all-processes"/></member>
- </simplelist>
- </refsect1>
- </refentry>
-
- <refentry id=3D"f_process-reset">
- <indexterm zone=3D"f_process-reset">
- <primary>process-reset</primary>
- </indexterm>
-
- <refnamediv>
- <refname>PROCESS-RESET</refname>
- <refpurpose>Causes a specified process to cleanly exit from
- any ongoing computation.</refpurpose>
- <refclass>Function</refclass>
- </refnamediv>
-
- <refsynopsisdiv>
- <synopsis><function>process-reset</function>
- process &optional; kill-option =3D> result</synopsis>
- </refsynopsisdiv>
-
- <refsect1>
- <title>Arguments and Values</title>
-
- <variablelist>
- <varlistentry>
- <term>process</term>
- <listitem>
- <para>a lisp process (thread).</para>
- </listitem>
- </varlistentry>
- <varlistentry>
- <term>kill-option</term>
- <listitem>
- <para>a generalized boolean. The default is T.</para>
- </listitem>
- </varlistentry>
- <varlistentry>
- <term>result</term>
- <listitem>
- <para>undefined.</para>
- </listitem>
- </varlistentry>
- </variablelist>
- </refsect1>
-
- <refsect1>
- <title>Description</title>
-
- <para>Causes <varname>process</varname> to cleanly exit
- from any ongoing computation. If <varname>kill-option</varname>
- is true, <varname>process</varname> then exits. Otherwise, it
- enters a state where it can be
- <xref linkend=3D"f_process-preset"/>. This
- is implemented by signaling a condition of type PROCESS-RESET;
- user-defined condition handlers should generally refrain from
- attempting to handle conditions of this type.</para>
-
- <para>A process can meaningfully reset itself.</para>
-
- <para>There is in general no way to know precisely when
- <varname>process</varname>
- has completed the act of resetting or killing itself; a process
- which has either entered the limbo of the reset state or exited
- has few ways of communicating either fact.
- <xref linkend=3D"f_process-enable"/>
- can reliably determine when a process has entered
- the "limbo of the reset state", but can't predict how long the
- clean exit from ongoing computation might take: that depends on
- the behavior of <function>unwind-protect</function> cleanup
- forms, and of the OS scheduler.</para>
-
- <para>Resetting a process other than
- <xref linkend=3D"v_current-process"/> involves the
- use of <xref linkend=3D"f_process-interrupt"/>.</para>
- </refsect1>
-
- <refsect1>
- <title>See Also</title>
- =
- <simplelist type=3D"inline">
- <member><xref linkend=3D"f_process-kill"/></member>
- <member><xref linkend=3D"f_process-abort"/></member>
- </simplelist>
- </refsect1>
- </refentry>
-
- <refentry id=3D"f_process-kill">
- <indexterm zone=3D"f_process-kill">
- <primary>process-kill</primary>
- </indexterm>
-
- <refnamediv>
- <refname>PROCESS-KILL</refname>
- <refpurpose>Causes a specified process to cleanly exit from any
- ongoing computation, and then exit.</refpurpose>
- <refclass>Function</refclass>
- </refnamediv>
-
- <refsynopsisdiv>
- <synopsis><function>process-kill</function> process
- =3D> result</synopsis>
- </refsynopsisdiv>
-
- <refsect1>
- <title>Arguments and Values</title>
-
- <variablelist>
- <varlistentry>
- <term>process</term>
- <listitem>
- <para>a lisp process (thread).</para>
- </listitem>
- </varlistentry>
- <varlistentry>
- <term>result</term>
- <listitem>
- <para>undefined.</para>
- </listitem>
- </varlistentry>
- </variablelist>
- </refsect1>
-
- <refsect1>
- <title>Description</title>
-
- <para>Entirely equivalent to calling
- (PROCESS-RESET PROCESS T). Causes <varname>process</varname>
- to cleanly exit from any ongoing computation, and then exit.</para>
- </refsect1>
-
- <refsect1>
- <title>See Also</title>
- =
- <simplelist type=3D"inline">
- <member><xref linkend=3D"f_process-reset"/></member>
- <member><xref linkend=3D"f_process-abort"/></member>
- </simplelist>
- </refsect1>
- </refentry>
-
- <refentry id=3D"f_process-abort">
- <indexterm zone=3D"f_process-abort">
- <primary>process-abort</primary>
- </indexterm>
-
- <refnamediv>
- <refname>PROCESS-ABORT</refname>
- <refpurpose>Causes a specified process to process an abort
- condition, as if it had invoked
- <function>abort</function>.</refpurpose>
- <refclass>Function</refclass>
- </refnamediv>
-
- <refsynopsisdiv>
- <synopsis><function>process-abort</function> process
- &optional; condition
- =3D> NIL</synopsis>
- </refsynopsisdiv>
-
- <refsect1>
- <title>Arguments and Values</title>
-
- <variablelist>
- <varlistentry>
- <term>process</term>
- <listitem>
- <para>a lisp process (thread).</para>
- </listitem>
- </varlistentry>
- <varlistentry>
- <term>condition</term>
- <listitem>
- <para>a lisp condition. The default is NIL.</para>
- </listitem>
- </varlistentry>
- </variablelist>
- </refsect1>
-
- <refsect1>
- <title>Description</title>
-
- <para>Entirely equivalent to calling
- (<xref linkend=3D"f_process-interrupt"/> <varname>process</varname>
- (<function>lambda</function> ()
- (<function>abort</function> <varname>condition</varname>))).
- Causes <varname>process</varname> to transfer control to the
- applicable handler or restart for <function>abort</function>.</para>
-
- <para>If <varname>condition</varname> is non-NIL,
- <function>process-abort</function> does not consider any
- handlers which are explicitly bound to conditions other than
- <varname>condition</varname>.</para>
- </refsect1>
-
- <refsect1>
- <title>See Also</title>
- =
- <simplelist type=3D"inline">
- <member><xref linkend=3D"f_process-reset"/></member>
- <member><xref linkend=3D"f_process-kill"/></member>
- </simplelist>
- </refsect1>
- </refentry>
-
- <refentry id=3D"v_ticks-per-second">
- <indexterm zone=3D"v_ticks-per-second">
- <primary>*ticks-per-second*</primary>
- </indexterm>
-
- <refnamediv>
- <refname>*TICKS-PER-SECOND*</refname>
- <refpurpose>Bound to the clock resolution of the OS
- scheduler.</refpurpose>
- <refclass>Variable</refclass>
- </refnamediv>
-
- <refsect1>
- <title>Value Type</title>
-
- <para>A positive integer.</para>
- </refsect1>
-
- <refsect1>
- <title>Initial Value</title>
- =
- <para>The clock resoluton of the OS scheduler. Currently,
- both LinuxPPC and DarwinPPC yeild an initial value of 100.
- </para>
- </refsect1>
-
- <refsect1>
- <title>Description</title>
-
- <para>This value is ordinarily of marginal interest at best,
- but, for backward compatibility, some functions accept timeout
- values expressed in "ticks". This value gives the number of
- ticks per second.</para>
- </refsect1>
-
- <refsect1>
- <title>See Also</title>
- =
- <simplelist type=3D"inline">
- <member><xref linkend=3D"f_process-wait-with-timeout"/></member>
- </simplelist>
- </refsect1>
- </refentry>
-
- <refentry id=3D"f_process-whostate">
- <indexterm zone=3D"f_process-whostate">
- <primary>process-whostate</primary>
- </indexterm>
-
- <refnamediv>
- <refname>PROCESS-WHOSTATE</refname>
- <refpurpose>Returns a string which describes the status of
- a specified process.</refpurpose>
- <refclass>Function</refclass>
- </refnamediv>
-
- <refsynopsisdiv>
- <synopsis><function>process-whostate</function> process
- =3D> whostate</synopsis>
- <variablelist>
- <varlistentry>
- <term>process</term>
- <listitem>
- <para>a lisp process (thread).</para>
- </listitem>
- </varlistentry>
- <varlistentry>
- <term>whostate</term>
- <listitem>
- <para>a string which describes the "state" of
- <varname>process</varname>.</para>
- </listitem>
- </varlistentry>
- </variablelist>
- </refsynopsisdiv>
-
- <refsect1>
- <title>Description</title>
-
- <para>This information is primarily for the benefit of
- debugging tools. <varname>whostate</varname> is a terse report
- on what <varname>process</varname> is doing, or not doing,
- and why.</para>
-
- <para>If the process is currently waiting in a call to
- <xref linkend=3D"f_process-wait"/> or
- <xref linkend=3D"f_process-wait-with-timeout"/>, its
- <function>process-whostate</function> will be the value
- which was passed to that function as <varname>whostate</varname>.
- </para>
- </refsect1>
-
- <refsect1>
- <title>See Also</title>
- =
- <simplelist type=3D"inline">
- <member><xref linkend=3D"f_process-wait"/></member>
- <member><xref linkend=3D"f_process-wait-with-timeout"/></member>
- <member><xref linkend=3D"m_with-terminal-input"/></member>
- </simplelist>
- </refsect1>
-
- <refsect1>
- <title>Notes</title>
-
- <para>This should arguably be SETFable, but doesn't seem to
- ever have been.</para>
- </refsect1>
- </refentry>
-
- <refentry id=3D"f_process-allow-schedule">
- <indexterm zone=3D"f_process-allow-schedule">
- <primary>process-allow-schedule</primary>
- </indexterm>
-
- <refnamediv>
- <refname>PROCESS-ALLOW-SCHEDULE</refname>
- <refpurpose>Used for cooperative multitasking; probably never
- necessary.</refpurpose>
- <refclass>Function</refclass>
- </refnamediv>
-
- <refsynopsisdiv>
- <synopsis><function>process-allow-schedule</function></synopsis>
- </refsynopsisdiv>
-
- <refsect1>
- <title>Description</title>
-
- <para>Advises the OS scheduler that the current thread has nothing
- useful to do and that it should try to find some other thread to
- schedule in its place. There's almost always a better
- alternative, such as waiting for some specific event to
- occur. For example, you could use a lock or semaphore.</para>
- </refsect1>
-
- <refsect1>
- <title>See Also</title>
- =
- <simplelist type=3D"inline">
- <member><xref linkend=3D"f_make-lock"/></member>
- <member><xref linkend=3D"f_make-read-write-lock"/></member>
- <member><xref linkend=3D"f_make-semaphore"/></member>
- <member><xref linkend=3D"f_process-input-wait"/></member>
- <member><xref linkend=3D"f_process-output-wait"/></member>
- <member><xref linkend=3D"m_with-terminal-input"/></member>
- </simplelist>
- </refsect1>
-
- <refsect1>
- <title>Notes</title>
-
- <para>This is a holdover from the days of cooperative
- multitasking. All modern general-purpose operating systems use
- preemptive multitasking.</para>
- </refsect1>
- </refentry>
-
- <refentry id=3D"f_process-wait">
- <indexterm zone=3D"f_process-wait">
- <primary>process-wait</primary>
- </indexterm>
-
- <refnamediv>
- <refname>PROCESS-WAIT</refname>
- <refpurpose>Causes the current lisp process (thread) to wait for
- a given
- predicate to return true.</refpurpose>
- <refclass>Function</refclass>
- </refnamediv>
-
- <refsynopsisdiv>
- <synopsis><function>process-wait</function>
- whostate function &rest; args =3D> result</synopsis>
- </refsynopsisdiv>
-
- <refsect1>
- <title>Arguments and Values</title>
-
- <variablelist>
- <varlistentry>
- <term>whostate</term>
-
- <listitem>
- <para>a string, which will be the value of
- <xref linkend=3D"f_process-whostate"/>
- while the process is waiting.</para>
- </listitem>
- </varlistentry>
- <varlistentry>
- <term>function</term>
- <listitem>
- <para>a function, designated by itself or by a symbol
- which names it.
- </para>
- </listitem>
- </varlistentry>
- <varlistentry>
- <term>args</term>
- <listitem>
- <para>a list of values, appropriate as arguments to
- <varname>function</varname>.</para>
- </listitem>
- </varlistentry>
- <varlistentry>
- <term>result</term>
- <listitem>
- <para>NIL.</para>
- </listitem>
- </varlistentry>
- </variablelist>
- </refsect1>
-
- <refsect1>
- <title>Description</title>
-
- <para>Causes the current lisp process (thread) to repeatedly
- apply <varname>function</varname> to
- <varname>args</varname> until the call returns a true result, then
- returns NIL. After
- each failed call, yields the CPU as if by
- <xref linkend=3D"f_process-allow-schedule"/>.</para>
- =
- <para>
- As with <xref linkend=3D"f_process-allow-schedule"/>, it's almost
- always more efficient to wait for some
- specific event to occur; this isn't exactly busy-waiting, but
- the OS scheduler can do a better job of scheduling if it's given
- the relevant information. For example, you could use a lock
- or semaphore.</para>
- </refsect1>
-
- <refsect1>
- <title>See Also</title>
- =
- <simplelist type=3D"inline">
- <member><xref linkend=3D"f_process-whostate"/></member>
- <member><xref linkend=3D"f_process-wait-with-timeout"/></member>
- <member><xref linkend=3D"f_make-lock"/></member>
- <member><xref linkend=3D"f_make-read-write-lock"/></member>
- <member><xref linkend=3D"f_make-semaphore"/></member>
- <member><xref linkend=3D"f_process-input-wait"/></member>
- <member><xref linkend=3D"f_process-output-wait"/></member>
- <member><xref linkend=3D"m_with-terminal-input"/></member>
- </simplelist>
- </refsect1>
- </refentry>
-
- <refentry id=3D"f_process-wait-with-timeout">
- <indexterm zone=3D"f_process-wait-with-timeout">
- <primary>process-wait-with-timeout</primary>
- </indexterm>
-
- <refnamediv>
- <refname>PROCESS-WAIT-WITH-TIMEOUT</refname>
- <refpurpose>Causes the current thread to wait for a given
- predicate to return true, or for a timeout to expire.</refpurpose>
- <refclass>Function</refclass>
- </refnamediv>
-
- <refsynopsisdiv>
- <synopsis><function>process-wait-with-timeout</function>
- whostate ticks function args =3D> result</synopsis>
- </refsynopsisdiv>
-
- <refsect1>
- <title>Arguments and Values</title>
-
- <variablelist>
- <varlistentry>
- <term>whostate</term>
- <listitem>
- <para>a string, which will be the value of
- <xref linkend=3D"f_process-whostate"/>
- while the process is waiting.</para>
- </listitem>
- </varlistentry>
- <varlistentry>
- <term>ticks</term>
- <listitem>
- <para>either a positive integer expressing a duration
- in "ticks" (see <xref linkend=3D"v_ticks-per-second"/>),
- or NIL.</para>
- </listitem>
- </varlistentry>
- <varlistentry>
- <term>function</term>
- <listitem>
- <para>a function, designated by itself or by a symbol
- which names it.</para>
- </listitem>
- </varlistentry>
- <varlistentry>
- <term>args</term>
- <listitem>
- <para>a list of values, appropriate as arguments to
- <varname>function</varname>.</para>
- </listitem>
- </varlistentry>
- <varlistentry>
- <term>result</term>
- <listitem>
- <para>T if <function>process-wait-with-timeout</function>
- returned because its <varname>function</varname> returned
- true, or NIL if it returned because the duration
- <varname>ticks</varname> has been exceeded.</para>
- </listitem>
- </varlistentry>
- </variablelist>
- </refsect1>
-
- <refsect1>
- <title>Description</title>
-
- <para>If <varname>ticks</varname> is NIL, behaves exactly like
- <xref linkend=3D"f_process-wait"/>, except for returning T.
- Otherwise, <varname>function</varname> will be tested repeatedly,
- in the same
- kind of test/yield loop as in <xref linkend=3D"f_process-wait"/>>
- until either <varname>function</varname> returns true,
- or the duration <varname>ticks</varname> has been exceeded.
- </para>
-
- <para> Having already read the descriptions of
- <xref linkend=3D"f_process-allow-schedule"/> and
- <xref linkend=3D"f_process-wait"/>, the
- astute reader has no doubt anticipated the observation that
- better alternatives should be used whenever possible.</para>
- </refsect1>
-
- <refsect1>
- <title>See Also</title>
- =
- <simplelist type=3D"inline">
- <member><xref linkend=3D"v_ticks-per-second"/></member>
- <member><xref linkend=3D"f_process-whostate"/></member>
- <member><xref linkend=3D"f_process-wait"/></member>
- <member><xref linkend=3D"f_make-lock"/></member>
- <member><xref linkend=3D"f_make-read-write-lock"/></member>
- <member><xref linkend=3D"f_make-semaphore"/></member>
- <member><xref linkend=3D"f_process-input-wait"/></member>
- <member><xref linkend=3D"f_process-output-wait"/></member>
- <member><xref linkend=3D"m_with-terminal-input"/></member>
- </simplelist>
- </refsect1>
- </refentry>
-
- <refentry id=3D"m_without-interrupts">
- <indexterm zone=3D"m_without-interrupts">
- <primary>without-interrupts</primary>
- </indexterm>
-
- <refnamediv>
- <refname>WITHOUT-INTERRUPTS</refname>
- <refpurpose>Evaluates its body in an environment in which
- process-interrupt requests are deferred.</refpurpose>
- <refclass>Macro</refclass>
- </refnamediv>
-
- <refsynopsisdiv>
- <synopsis><function>without-interrupts</function>
- &body; body =3D> result</synopsis>
- </refsynopsisdiv>
-
- <refsect1>
- <title>Arguments and Values</title>
-
- <variablelist>
- <varlistentry>
- <term>body</term>
- <listitem>
- <para>an implicit progn.</para>
- </listitem>
- </varlistentry>
- <varlistentry>
- <term>result</term>
- <listitem>
- <para>the primary value returned by
- <varname>body</varname>.</para>
- </listitem>
- </varlistentry>
- </variablelist>
- </refsect1>
-
- <refsect1>
- <title>Description</title>
-
- <para>Executes <varname>body</varname>
- in an environment in which <xref linkend=3D"f_process-interrupt"/>
- requests are
- deferred. As noted in the description of
- <xref linkend=3D"f_process-interrupt"/>, this has nothing to do
- with the
- scheduling of other threads; it may be necessary to inhibit
- <xref linkend=3D"f_process-interrupt"/> handling when
- (for instance) modifying some data
- structure (for which the current thread holds an appropriate lock)
- in some manner that's not reentrant.</para>
- </refsect1>
-
- <refsect1>
- <title>See Also</title>
- =
- <simplelist type=3D"inline">
- <member><xref linkend=3D"f_process-interrupt"/></member>
- </simplelist>
- </refsect1>
- </refentry>
-
- <refentry id=3D"f_make-lock">
- <indexterm zone=3D"f_make-lock">
- <primary>make-lock</primary>
- </indexterm>
-
- <refnamediv>
- <refname>MAKE-LOCK</refname>
- <refpurpose>Creates and returns a lock object, which can
- be used for synchronization between threads.</refpurpose>
- <refclass>Function</refclass>
- </refnamediv>
-
- <refsynopsisdiv>
- <synopsis><function>make-lock</function> &optional;
- name =3D> lock</synopsis>
- </refsynopsisdiv>
-
- <refsect1>
- <title>Arguments and Values</title>
-
- <variablelist>
- <varlistentry>
- <term>name</term>
- <listitem>
- <para>any lisp object; saved as part of
- <varname>lock</varname>. Typically a string or symbol
- which may appear in the <xref linkend=3D"f_process-whostate"/>s
- of threads which are waiting for <varname>lock</varname>.
- </para>
- </listitem>
- </varlistentry>
- <varlistentry>
- <term>lock</term>
- <listitem>
- <para>a newly-allocated object of type CCL:LOCK.</para>
- </listitem>
- </varlistentry>
- </variablelist>
- </refsect1>
-
- <refsect1>
- <title>Description</title>
-
- <para>Creates and returns a lock object, which can
- be used to synchronize access to some shared resource.
- <varname>lock</varname> is
- initially in a "free" state; a lock can also be
- "owned" by a
- thread.</para>
- </refsect1>
-
- <refsect1>
- <title>See Also</title>
- =
- <simplelist type=3D"inline">
- <member><xref linkend=3D"m_with-lock-grabbed"/></member>
- <member><xref linkend=3D"f_grab-lock"/></member>
- <member><xref linkend=3D"f_release-lock"/></member>
- <member><xref linkend=3D"f_try-lock"/></member>
- <member><xref linkend=3D"f_make-read-write-lock"/></member>
- <member><xref linkend=3D"f_make-semaphore"/></member>
- <member><xref linkend=3D"f_process-input-wait"/></member>
- <member><xref linkend=3D"f_process-output-wait"/></member>
- <member><xref linkend=3D"m_with-terminal-input"/></member>
- </simplelist>
- </refsect1>
- </refentry>
-
- <refentry id=3D"m_with-lock-grabbed">
- <indexterm zone=3D"m_with-lock-grabbed">
- <primary>with-lock-grabbed</primary>
- </indexterm>
-
- <refnamediv>
- <refname>WITH-LOCK-GRABBED</refname>
- <refpurpose>Waits until a given lock can be obtained, then
- evaluates its body with the lock held.</refpurpose>
- <refclass>Macro</refclass>
- </refnamediv>
-
- <refsynopsisdiv>
- <synopsis><function>with-lock-grabbed</function>
- (lock) &body; body</synopsis>
- </refsynopsisdiv>
-
- <refsect1>
- <title>Arguments and Values</title>
-
- <variablelist>
- <varlistentry>
- <term>lock</term>
- <listitem>
- <para>an object of type CCL:LOCK.</para>
- </listitem>
- </varlistentry>
- <varlistentry>
- <term>body</term>
- <listitem>
- <para>an implicit progn.</para>
- </listitem>
- </varlistentry>
- <varlistentry>
- <term>result</term>
- <listitem>
- <para>the primary value returned by
- <varname>body</varname>.</para>
- </listitem>
- </varlistentry>
- </variablelist>
- </refsect1>
-
- <refsect1>
- <title>Description</title>
-
- <para>Waits until <varname>lock</varname> is either free or
- owned by the calling
- thread, then excutes <varname>body</varname> with the
- lock owned by the calling thread. If <varname>lock</varname>
- was free when <function>with-lock-grabbed</function> was called,
- it is restored to a free state after <varname>body</varname>
- is executed.</para>
- </refsect1>
-
- <refsect1>
- <title>See Also</title>
- =
- <simplelist type=3D"inline">
- <member><xref linkend=3D"f_make-lock"/></member>
- <member><xref linkend=3D"f_grab-lock"/></member>
- <member><xref linkend=3D"f_release-lock"/></member>
- <member><xref linkend=3D"f_try-lock"/></member>
- <member><xref linkend=3D"f_make-read-write-lock"/></member>
- <member><xref linkend=3D"f_make-semaphore"/></member>
- <member><xref linkend=3D"f_process-input-wait"/></member>
- <member><xref linkend=3D"f_process-output-wait"/></member>
- <member><xref linkend=3D"m_with-terminal-input"/></member>
- </simplelist>
- </refsect1>
- </refentry>
-
- <refentry id=3D"f_grab-lock">
- <indexterm zone=3D"f_grab-lock">
- <primary>grab-lock</primary>
- </indexterm>
-
- <refnamediv>
- <refname>GRAB-LOCK</refname>
- <refpurpose>Waits until a given lock can be obtained, then
- obtains it.</refpurpose>
- <refclass>Function</refclass>
- </refnamediv>
-
- <refsynopsisdiv>
- <synopsis><function>grab-lock</function> lock</synopsis>
- </refsynopsisdiv>
-
- <refsect1>
- <title>Arguments and Values</title>
-
- <variablelist>
- <varlistentry>
- <term>lock</term>
- <listitem>
- <para>an object of type CCL:LOCK.</para>
- </listitem>
- </varlistentry>
- </variablelist>
- </refsect1>
-
- <refsect1>
- <title>Description</title>
-
- <para>Blocks until <varname>lock</varname> is owned by the
- calling thread.</para>
-
- <para>The macro <xref linkend=3D"m_with-lock-grabbed"/>
- <emphasis>could</emphasis> be defined in
- terms of <function>grab-lock</function> and
- <xref linkend=3D"f_release-lock"/>, but it is actually
- implemented at a slightly lower level.</para>
- </refsect1>
-
- <refsect1>
- <title>See Also</title>
- =
- <simplelist type=3D"inline">
- <member><xref linkend=3D"f_make-lock"/></member>
- <member><xref linkend=3D"m_with-lock-grabbed"/></member>
- <member><xref linkend=3D"f_release-lock"/></member>
- <member><xref linkend=3D"f_try-lock"/></member>
- <member><xref linkend=3D"f_make-read-write-lock"/></member>
- <member><xref linkend=3D"f_make-semaphore"/></member>
- <member><xref linkend=3D"f_process-input-wait"/></member>
- <member><xref linkend=3D"f_process-output-wait"/></member>
- <member><xref linkend=3D"m_with-terminal-input"/></member>
- </simplelist>
- </refsect1>
- </refentry>
-
- <refentry id=3D"f_release-lock">
- <indexterm zone=3D"f_release-lock">
- <primary>release-lock</primary>
- </indexterm>
-
- <refnamediv>
- <refname>RELEASE-LOCK</refname>
- <refpurpose>Relinquishes ownership of a given lock.</refpurpose>
- <refclass>Function</refclass>
- </refnamediv>
-
- <refsynopsisdiv>
- <synopsis><function>release-lock</function> lock</synopsis>
- </refsynopsisdiv>
-
- <refsect1>
- <title>Arguments and Values</title>
-
- <variablelist>
- <varlistentry>
- <term>lock</term>
- <listitem>
- <para>an object of type CCL:LOCK.</para>
- </listitem>
- </varlistentry>
- </variablelist>
- </refsect1>
-
- <refsect1>
- <title>Description</title>
-
- <para>Signals an error of type CCL:LOCK-NOT-OWNER if
- <varname>lock</varname>
- is not already owned by the calling thread; otherwise, undoes the
- effect of one previous =
- <xref linkend=3D"f_grab-lock"/>. If this means that
- <function>release-lock</function> has now been called on
- <varname>lock</varname> the same number of times as
- <xref linkend=3D"f_grab-lock"/> has, <varname>lock</varname>
- becomes free.</para>
- </refsect1>
-
- <refsect1>
- <title>See Also</title>
- =
- <simplelist type=3D"inline">
- <member><xref linkend=3D"f_make-lock"/></member>
- <member><xref linkend=3D"m_with-lock-grabbed"/></member>
- <member><xref linkend=3D"f_grab-lock"/></member>
- <member><xref linkend=3D"f_try-lock"/></member>
- <member><xref linkend=3D"f_make-read-write-lock"/></member>
- <member><xref linkend=3D"f_make-semaphore"/></member>
- <member><xref linkend=3D"f_process-input-wait"/></member>
- <member><xref linkend=3D"f_process-output-wait"/></member>
- <member><xref linkend=3D"m_with-terminal-input"/></member>
- </simplelist>
- </refsect1>
- </refentry>
-
- <refentry id=3D"f_try-lock">
- <indexterm zone=3D"f_try-lock">
- <primary>try-lock</primary>
- </indexterm>
-
- <refnamediv>
- <refname>TRY-LOCK</refname>
- <refpurpose>Obtains the given lock, but only if it is not
- necessary to wait for it.</refpurpose>
- <refclass>Function</refclass>
- </refnamediv>
-
- <refsynopsisdiv>
- <synopsis><function>try-lock</function> lock =3D> result</synopsis>
- </refsynopsisdiv>
-
- <refsect1>
- <title>Arguments and Values</title>
-
- <variablelist>
- <varlistentry>
- <term>lock</term>
- <listitem>
- <para>an object of type CCL:LOCK.</para>
- </listitem>
- </varlistentry>
- <varlistentry>
- <term>result</term>
- <listitem>
- <para>T if <varname>lock</varname> has been obtained,
- or NIL if it has not.</para>
- </listitem>
- </varlistentry>
- </variablelist>
- </refsect1>
-
- <refsect1>
- <title>Description</title>
-
- <para>Tests whether <varname>lock</varname>
- can be obtained without blocking - that is, either
- <varname>lock</varname> is already free, or it is already owned
- by <xref linkend=3D"v_current-process"/>. If it can,
- causes it to
- be owned by the calling lisp process (thread) and returns T.
- Otherwise, the lock
- is already owned by another thread and cannot be obtained without
- blocking; NIL is returned in this case.</para>
- </refsect1>
-
- <refsect1>
- <title>See Also</title>
- =
- <simplelist type=3D"inline">
- <member><xref linkend=3D"f_make-lock"/></member>
- <member><xref linkend=3D"m_with-lock-grabbed"/></member>
- <member><xref linkend=3D"f_grab-lock"/></member>
- <member><xref linkend=3D"f_release-lock"/></member>
- <member><xref linkend=3D"f_make-read-write-lock"/></member>
- <member><xref linkend=3D"f_make-semaphore"/></member>
- <member><xref linkend=3D"f_process-input-wait"/></member>
- <member><xref linkend=3D"f_process-output-wait"/></member>
- <member><xref linkend=3D"m_with-terminal-input"/></member>
- </simplelist>
- </refsect1>
- </refentry>
-
- <refentry id=3D"f_make-read-write-lock">
- <indexterm zone=3D"f_make-read-write-lock">
- <primary>make-read-write-lock</primary>
- </indexterm>
-
- <refnamediv>
- <refname>MAKE-READ-WRITE-LOCK</refname>
- <refpurpose>Creates and returns a read-write lock, which can
- be used for synchronization between threads.</refpurpose>
- <refclass>Function</refclass>
- </refnamediv>
-
- <refsynopsisdiv>
- <synopsis><function>make-read-write-lock</function>
- =3D> read-write-lock</synopsis>
- </refsynopsisdiv>
-
- <refsect1>
- <title>Arguments and Values</title>
-
- <variablelist>
- <varlistentry>
- <term>read-write-lock</term>
- <listitem>
- <para>a newly-allocated object of type
- CCL:READ-WRITE-LOCK.</para>
- </listitem>
- </varlistentry>
- </variablelist>
- </refsect1>
-
- <refsect1>
- <title>Description</title>
-
- <para>Creates and returns an object of type CCL::READ-WRITE-LOCK.
- A read-write lock may, at any given time, belong to any number
- of lisp processes (threads) which act as "readers"; or, it may
- belong to at most one process which acts as a "writer". A
- read-write lock may never be held by a reader at the same time as
- a writer. Intially, <varname>read-write-lock</varname> has
- no readers and no writers.</para>
- </refsect1>
-
- <refsect1>
- <title>See Also</title>
- =
- <simplelist type=3D"inline">
- <member><xref linkend=3D"m_with-read-lock"/></member>
- <member><xref linkend=3D"m_with-write-lock"/></member>
- <member><xref linkend=3D"f_make-lock"/></member>
- <member><xref linkend=3D"f_make-semaphore"/></member>
- <member><xref linkend=3D"f_process-input-wait"/></member>
- <member><xref linkend=3D"f_process-output-wait"/></member>
- <member><xref linkend=3D"m_with-terminal-input"/></member>
- </simplelist>
- </refsect1>
-
- <refsect1>
- <title>Notes</title>
-
- <para>There probably should be some way to
- atomically "promote" a reader, making it a writer without
- releasing the lock, which could otherwise cause delay.</para>
- </refsect1>
- </refentry>
-
- <refentry id=3D"m_with-read-lock">
- <indexterm zone=3D"m_with-read-lock">
- <primary>with-read-lock</primary>
- </indexterm>
-
- <refnamediv>
- <refname>WITH-READ-LOCK</refname>
- <refpurpose>Waits until a given lock is available for
- read-only access, then evaluates its body with the lock
- held.</refpurpose>
- <refclass>Macro</refclass>
- </refnamediv>
-
- <refsynopsisdiv>
- <synopsis><function>with-read-lock</function>
- (read-write-lock) &body; body =3D> result</synopsis>
- </refsynopsisdiv>
-
- <refsect1>
- <title>Arguments and Values</title>
-
- <variablelist>
- <varlistentry>
- <term>read-write-lock</term>
- <listitem>
- <para>an object of type
- CCL:READ-WRITE-LOCK.</para>
- </listitem>
- </varlistentry>
- <varlistentry>
- <term>body</term>
- <listitem>
- <para>an implicit progn.</para>
- </listitem>
- </varlistentry>
- <varlistentry>
- <term>result</term>
- <listitem>
- <para>the primary value returned by
- <varname>body</varname>.</para>
- </listitem>
- </varlistentry>
- </variablelist>
- </refsect1>
-
- <refsect1>
- <title>Description</title>
-
- <para>Waits until <varname>read-write-lock</varname> has no
- writer,
- ensures that <xref linkend=3D"v_current-process"/> is a
- reader of it, then executes <varname>body</varname>.
- </para>
-
- <para>After executing <varname>body</varname>, if
- <xref linkend=3D"v_current-process"/> was not a reader of
- <varname>read-write-lock</varname> before
- <function>with-read-lock</function> was called, the lock is
- released. If it was already a reader, it remains one.</para>
- </refsect1>
-
- <refsect1>
- <title>See Also</title>
- =
- <simplelist type=3D"inline">
- <member><xref linkend=3D"f_make-read-write-lock"/></member>
- <member><xref linkend=3D"m_with-write-lock"/></member>
- <member><xref linkend=3D"f_make-lock"/></member>
- <member><xref linkend=3D"f_make-semaphore"/></member>
- <member><xref linkend=3D"f_process-input-wait"/></member>
- <member><xref linkend=3D"f_process-output-wait"/></member>
- <member><xref linkend=3D"m_with-terminal-input"/></member>
- </simplelist>
- </refsect1>
- </refentry>
-
- <refentry id=3D"m_with-write-lock">
- <indexterm zone=3D"m_with-write-lock">
- <primary>with-write-lock</primary>
- </indexterm>
-
- <refnamediv>
- <refname>WITH-WRITE-LOCK</refname>
- <refpurpose>Waits until the given lock is available for write
- access, then executes its body with the lock held.</refpurpose>
- <refclass>Macro</refclass>
- </refnamediv>
-
- <refsynopsisdiv>
- <synopsis><function>with-write-lock</function>
- (read-write-lock) &body; body</synopsis>
- </refsynopsisdiv>
-
- <refsect1>
- <title>Arguments and Values</title>
-
- <variablelist>
- <varlistentry>
- <term>read-write-lock</term>
- <listitem>
- <para>an object of type
- CCL:READ-WRITE-LOCK.</para>
- </listitem>
- </varlistentry>
- <varlistentry>
- <term>body</term>
- <listitem>
- <para>an implicit progn.</para>
- </listitem>
- </varlistentry>
- <varlistentry>
- <term>result</term>
- <listitem>
- <para>the primary value returned by
- <varname>body</varname>.</para>
- </listitem>
- </varlistentry>
- </variablelist>
- </refsect1>
-
- <refsect1>
- <title>Description</title>
-
- <para>Waits until <varname>read-write-lock</varname> has no
- readers and no writer other than <xref linkend=3D"v_current-process"/>,
- then ensures that <xref linkend=3D"v_current-process"/> is the
- writer of it. With the lock held, executes <varname>body</varname>.
- </para>
-
- <para>After executing <varname>body</varname>, if
- <xref linkend=3D"v_current-process"/> was not the writer of
- <varname>read-write-lock</varname> before
- <function>with-write-lock</function> was called, the lock is
- released. If it was already the writer, it remains the
- writer.</para>
- </refsect1>
-
- <refsect1>
- <title>See Also</title>
- =
- <simplelist type=3D"inline">
- <member><xref linkend=3D"f_make-read-write-lock"/></member>
- <member><xref linkend=3D"m_with-read-lock"/></member>
- <member><xref linkend=3D"f_make-lock"/></member>
- <member><xref linkend=3D"f_make-semaphore"/></member>
- <member><xref linkend=3D"f_process-input-wait"/></member>
- <member><xref linkend=3D"f_process-output-wait"/></member>
- <member><xref linkend=3D"m_with-terminal-input"/></member>
- </simplelist>
- </refsect1>
- </refentry>
-
- <refentry id=3D"f_make-semaphore">
- <indexterm zone=3D"f_make-semaphore">
- <primary>make-semaphore</primary>
- </indexterm>
-
- <refnamediv>
- <refname>MAKE-SEMAPHORE</refname>
- <refpurpose>Creates and returns a semaphore, which can be used
- for synchronization between threads.</refpurpose>
- <refclass>Function</refclass>
- </refnamediv>
-
- <refsynopsisdiv>
- <synopsis><function>make-semaphore</function>
- =3D> semaphore</synopsis>
- </refsynopsisdiv>
-
- <refsect1>
- <title>Arguments and Values</title>
- =
- <variablelist>
- <varlistentry>
- <term>semaphore</term>
- <listitem>
- <para>a newly-allocated object of type CCL:SEMAPHORE.</para>
- </listitem>
- </varlistentry>
- </variablelist>
- </refsect1>
-
- <refsect1>
- <title>Description</title>
-
- <para>Creates and returns an object of type CCL:SEMAPHORE.
- A semaphore has an associated "count" which may be incremented
- and decremented atomically; incrementing it represents sending
- a signal, and decrementing it represents handling that signal.
- <varname>semaphore</varname> has an initial count of 0.</para>
- </refsect1>
-
- <refsect1>
- <title>See Also</title>
- =
- <simplelist type=3D"inline">
- <member><xref linkend=3D"f_signal-semaphore"/></member>
- <member><xref linkend=3D"f_wait-on-semaphore"/></member>
- <member><xref linkend=3D"f_timed-wait-on-semaphore"/></member>
- <member><xref linkend=3D"f_make-lock"/></member>
- <member><xref linkend=3D"f_make-read-write-lock"/></member>
- <member><xref linkend=3D"f_process-input-wait"/></member>
- <member><xref linkend=3D"f_process-output-wait"/></member>
- <member><xref linkend=3D"m_with-terminal-input"/></member>
- </simplelist>
- </refsect1>
- </refentry>
-
- <refentry id=3D"f_signal-semaphore">
- <indexterm zone=3D"f_signal-semaphore">
- <primary>signal-semaphore</primary>
- </indexterm>
-
- <refnamediv>
- <refname>SIGNAL-SEMAPHORE</refname>
- <refpurpose>Atomically increments the count of a given
- semaphore.</refpurpose>
- <refclass>Function</refclass>
- </refnamediv>
-
- <refsynopsisdiv>
- <synopsis><function>signal-semaphore</function>
- semaphore =3D> result</synopsis>
- </refsynopsisdiv>
-
- <refsect1>
- <title>Arguments and Values</title>
- =
- <variablelist>
- <varlistentry>
- <term>semaphore</term>
- <listitem>
- <para>an object of type CCL:SEMAPHORE.</para>
- </listitem>
- </varlistentry>
- <varlistentry>
- <term>result</term>
- <listitem>
- <para>an integer representing an error identifier
- which was returned by the underlying OS call.</para>
- </listitem>
- </varlistentry>
- </variablelist>
- </refsect1>
-
- <refsect1>
- <title>Description</title>
-
- <para>Atomically increments <varname>semaphore</varname>'s
- "count" by 1; this
- may enable a waiting thread to resume execution.</para>
- </refsect1>
-
- <refsect1>
- <title>See Also</title>
- =
- <simplelist type=3D"inline">
- <member><xref linkend=3D"f_make-semaphore"/></member>
- <member><xref linkend=3D"f_wait-on-semaphore"/></member>
- <member><xref linkend=3D"f_timed-wait-on-semaphore"/></member>
- <member><xref linkend=3D"f_make-lock"/></member>
- <member><xref linkend=3D"f_make-read-write-lock"/></member>
- <member><xref linkend=3D"f_process-input-wait"/></member>
- <member><xref linkend=3D"f_process-output-wait"/></member>
- <member><xref linkend=3D"m_with-terminal-input"/></member>
- </simplelist>
- </refsect1>
-
- <refsect1>
- <title>Notes</title>
-
- <para><varname>result</varname> should probably be interpreted
- and acted on by <function>signal-semaphore</function>, because
- it is not likely to be meaningful to a lisp program, and the
- most common cause of failure is a type error.</para>
- </refsect1>
- </refentry>
-
- <refentry id=3D"f_wait-on-semaphore">
- <indexterm zone=3D"f_wait-on-semaphore">
- <primary>wait-on-semaphore</primary>
- </indexterm>
-
- <refnamediv>
- <refname>WAIT-ON-SEMAPHORE</refname>
- <refpurpose>Waits until the given semaphore has a positive
- count which can be atomically decremented.</refpurpose>
- <refclass>Function</refclass>
- </refnamediv>
-
- <refsynopsisdiv>
- <synopsis><function>wait-on-semaphore</function>
- semaphore =3D> result</synopsis>
- </refsynopsisdiv>
-
- <refsect1>
- <title>Arguments and Values</title>
- =
- <variablelist>
- <varlistentry>
- <term>semaphore</term>
- <listitem>
- <para>an object of type CCL:SEMAPHORE.</para>
- </listitem>
- </varlistentry>
- <varlistentry>
- <term>result</term>
- <listitem>
- <para>an integer representing an error identifier
- which was returned by the underlying OS call.</para>
- </listitem>
- </varlistentry>
- </variablelist>
- </refsect1>
-
- <refsect1>
- <title>Description</title>
-
- <para>Waits until <varname>semaphore</varname>
- has a positive count that can be
- atomically decremented; this will succeed exactly once for each
- corresponding call to SIGNAL-SEMAPHORE.</para>
- </refsect1>
-
- <refsect1>
- <title>See Also</title>
- =
- <simplelist type=3D"inline">
- <member><xref linkend=3D"f_make-semaphore"/></member>
- <member><xref linkend=3D"f_signal-semaphore"/></member>
- <member><xref linkend=3D"f_timed-wait-on-semaphore"/></member>
- <member><xref linkend=3D"f_make-lock"/></member>
- <member><xref linkend=3D"f_make-read-write-lock"/></member>
- <member><xref linkend=3D"f_process-input-wait"/></member>
- <member><xref linkend=3D"f_process-output-wait"/></member>
- <member><xref linkend=3D"m_with-terminal-input"/></member>
- </simplelist>
- </refsect1>
-
- <refsect1>
- <title>Notes</title>
-
- <para><varname>result</varname> should probably be interpreted
- and acted on by <function>wait-on-semaphore</function>, because
- it is not likely to be meaningful to a lisp program, and the
- most common cause of failure is a type error.</para>
- </refsect1>
- </refentry>
-
- <refentry id=3D"f_timed-wait-on-semaphore">
- <indexterm zone=3D"f_timed-wait-on-semaphore">
- <primary>timed-wait-on-semaphore</primary>
- </indexterm>
-
- <refnamediv>
- <refname>TIMED-WAIT-ON-SEMAPHORE</refname>
- <refpurpose>Waits until the given semaphore has a postive
- count which can be atomically decremented, or until a timeout
- expires.</refpurpose>
- <refclass>Function</refclass>
- </refnamediv>
-
- <refsynopsisdiv>
- <synopsis><function>timed-wait-on-semaphore</function>
- semaphore timeout =3D> result</synopsis>
- </refsynopsisdiv>
-
- <refsect1>
- <title>Arguments and Values</title>
- =
- <variablelist>
- <varlistentry>
- <term>semaphore</term>
- <listitem>
- <para>An object of type CCL:SEMAPHORE.</para>
- </listitem>
- </varlistentry>
- <varlistentry>
- <term>timeout</term>
- <listitem>
- <para>a time interval in seconds. May be any
- non-negative real number the <function>floor</function> of
- which fits in 32 bits. The default is 1.</para>
- </listitem>
- </varlistentry>
- <varlistentry>
- <term>result</term>
- <listitem>
- <para>T if <function>timed-wait-on-semaphore</function>
- returned because it was able to decrement the count of
- <varname>semaphore</varname>; NIL if it returned because
- the duration <varname>timeout</varname> has been
- exceeded.</para>
- </listitem>
- </varlistentry>
- </variablelist>
- </refsect1>
-
- <refsect1>
- <title>Description</title>
-
- <para>Waits until <varname>semaphore</varname>
- has a positive count that can be
- atomically decremented, or until the duration
- <varname>timeout</varname> has
- elapsed.</para>
- </refsect1>
-
- <refsect1>
- <title>See Also</title>
- =
- <simplelist type=3D"inline">
- <member><xref linkend=3D"f_make-semaphore"/></member>
- <member><xref linkend=3D"f_wait-on-semaphore"/></member>
- <member><xref linkend=3D"f_make-lock"/></member>
- <member><xref linkend=3D"f_make-read-write-lock"/></member>
- <member><xref linkend=3D"f_process-input-wait"/></member>
- <member><xref linkend=3D"f_process-output-wait"/></member>
- <member><xref linkend=3D"m_with-terminal-input"/></member>
- </simplelist>
- </refsect1>
- </refentry>
-
- <refentry id=3D"f_process-input-wait">
- <indexterm zone=3D"f_process-input-wait">
- <primary>process-input-wait</primary>
- </indexterm>
-
- <refnamediv>
- <refname>PROCESS-INPUT-WAIT</refname>
- <refpurpose>Waits until input is available on a given
- file-descriptor.</refpurpose>
- <refclass>Function</refclass>
- </refnamediv>
-
- <refsynopsisdiv>
- <synopsis><function>process-input-wait</function>
- fd &optional; timeout</synopsis>
- </refsynopsisdiv>
-
- <refsect1>
- <title>Arguments and Values</title>
- =
- <variablelist>
- <varlistentry>
- <term>fd</term>
- <listitem>
- <para>a file descriptor, which is a non-negative integer
- used by the OS to refer to an open file, socket, or similar
- I/O connection. See <xref linkend=3D"f_stream-device"/>.</para>
- </listitem>
- </varlistentry>
- <varlistentry>
- <term>timeout</term>
- <listitem>
- <para>either NIL, or a time interval in seconds. May be any
- non-negative real number the <function>floor</function> of
- which fits in 32 bits. The default is NIL.</para>
- </listitem>
- </varlistentry>
- </variablelist>
- </refsect1>
-
- <refsect1>
- <title>Description</title>
-
- <para>Wait until input is available on <varname>fd</varname>.
- This uses the <function>select()</function> system call, and is
- generally a fairly
- efficient way of blocking while waiting for input. More
- accurately, <function>process-input-wait</function>
- waits until it's possible to read
- from fd without blocking, or until <varname>timeout</varname>, if
- it is not NIL, has been exceeded.</para>
-
- <para>
- Note that it's possible to read without blocking if
- the file is at its end - although, of course, the read will
- return zero bytes.</para>
- </refsect1>
- =
- <refsect1>
- <title>See Also</title>
- =
- <simplelist type=3D"inline">
- <member><xref linkend=3D"f_make-lock"/></member>
- <member><xref linkend=3D"f_make-read-write-lock"/></member>
- <member><xref linkend=3D"f_make-semaphore"/></member>
- <member><xref linkend=3D"f_process-output-wait"/></member>
- <member><xref linkend=3D"m_with-terminal-input"/></member>
- </simplelist>
- </refsect1>
-
- <refsect1>
- <title>Notes</title>
-
- <para>
- <function>process-input-wait</function> has a timeout parameter,
- and
- <xref linkend=3D"f_process-output-wait"/> does not. This
- inconsistency should probably be corrected.
- </para>
- </refsect1>
- </refentry>
-
- <refentry id=3D"f_process-output-wait">
- <indexterm zone=3D"f_process-output-wait">
- <primary>process-output-wait</primary>
- </indexterm>
-
- <refnamediv>
- <refname>PROCESS-OUTPUT-WAIT</refname>
- <refpurpose>Waits until output is possible on a given file
- descriptor.</refpurpose>
- <refclass>Function</refclass>
- </refnamediv>
-
- <refsynopsisdiv>
- <synopsis><function>process-output-wait</function> fd</synopsis>
- </refsynopsisdiv>
-
- <refsect1>
- <title>Arguments and Values</title>
- =
- <variablelist>
- <varlistentry>
- <term>fd</term>
- <listitem>
- <para>a file descriptor, which is a non-negative integer
- used by the OS to refer to an open file, socket, or similar
- I/O connection. See <xref linkend=3D"f_stream-device"/>.</para>
- </listitem>
- </varlistentry>
- </variablelist>
- </refsect1>
-
- <refsect1>
- <title>Description</title>
-
- <para>Wait until output is possible on <varname>fd</varname>.
- This uses the <function>select()</function> system call, and is
- generally a fairly
- efficient way of blocking while waiting to output.</para>
-
- <para>If <function>process-output-wait</function> is called on
- a network socket which has not yet established a connection, it
- will wait until the connection is established. This is an
- important use, often overlooked.</para>
- </refsect1>
-
- <refsect1>
- <title>See Also</title>
- =
- <simplelist type=3D"inline">
- <member><xref linkend=3D"f_make-lock"/></member>
- <member><xref linkend=3D"f_make-read-write-lock"/></member>
- <member><xref linkend=3D"f_make-semaphore"/></member>
- <member><xref linkend=3D"f_process-input-wait"/></member>
- <member><xref linkend=3D"m_with-terminal-input"/></member>
- </simplelist>
- </refsect1>
-
- <refsect1>
- <title>Notes</title>
-
- <para>
- <xref linkend=3D"f_process-input-wait"/> has a timeout parameter,
- and
- <function>process-output-wait</function> does not. This
- inconsistency should probably be corrected.
- </para>
- </refsect1>
- </refentry>
-
- <refentry id=3D"m_with-terminal-input">
- <indexterm zone=3D"m_with-terminal-input">
- <primary>with-terminal-input</primary>
- </indexterm>
-
- <refnamediv>
- <refname>WITH-TERMINAL-INPUT</refname>
- <refpurpose>Executes its body in an environment with exclusive
- read access to the terminal.</refpurpose>
- <refclass>Macro</refclass>
- </refnamediv>
-
- <refsynopsisdiv>
- <synopsis><function>with-terminal-input</function>
- &body; body =3D> result</synopsis>
- </refsynopsisdiv>
-
- <refsect1>
- <title>Arguments and Values</title>
- =
- <variablelist>
- <varlistentry>
- <term>body</term>
- <listitem>
- <para>an implicit progn.</para>
- </listitem>
- </varlistentry>
- <varlistentry>
- <term>result</term>
- <listitem>
- <para>the primary value returned by
- <varname>body</varname>.</para>
- </listitem>
- </varlistentry>
- </variablelist>
- </refsect1>
-
- <refsect1>
- <title>Description</title>
-
- <para>Requests exclusive read access to the standard terminal
- stream, <varname>*terminal-io*</varname>. Executes
- <varname>body</varname> in an environment with that access.
- </para>
- </refsect1>
-
- <refsect1>
- <title>See Also</title>
- =
- <simplelist type=3D"inline">
- <member><xref
- linkend=3D"v_request-terminal-input-via-break"/></member>
- <member><xref linkend=3D"cmd_y"/></member>
- <member><xref linkend=3D"f_make-lock"/></member>
- <member><xref linkend=3D"f_make-read-write-lock"/></member>
- <member><xref linkend=3D"f_make-semaphore"/></member>
- <member><xref linkend=3D"f_process-input-wait"/></member>
- <member><xref linkend=3D"f_process-output-wait"/></member>
- </simplelist>
- </refsect1>
- </refentry>
-
- <refentry id=3D"v_request-terminal-input-via-break">
- <indexterm zone=3D"v_request-terminal-input-via-break">
- <primary>request-terminal-input-via-break</primary>
- </indexterm>
-
- <refnamediv>
- <refname>*REQUEST-TERMINAL-INPUT-VIA-BREAK*</refname>
- <refpurpose>Controls how attempts to obtain ownership of
- terminal input are made.</refpurpose>
- <refclass>Variable</refclass>
- </refnamediv>
-
- <refsect1>
- <title>Value Type</title>
-
- <para>A boolean.</para>
- </refsect1>
-
- <refsect1>
- <title>Initial Value</title>
- =
- <para>NIL.</para>
- </refsect1>
-
- <refsect1>
- <title>Description</title>
-
- <para>Controls how attempts to obtain ownership of terminal input
- are made. When NIL, a message is printed on *TERMINAL-IO*;
- it's expected that the user will later yield
- control of the terminal via the :Y toplevel command. When T, a
- BREAK condition is signaled in the owning process; continuing from
- the break loop will yield the terminal to the requesting process
- (unless the :Y command was already used to do so in the break
- loop.)</para>
- </refsect1>
-
- <refsect1>
- <title>See Also</title>
- =
- <simplelist type=3D"inline">
- <member><xref linkend=3D"m_with-terminal-input"/></member>
- <member><xref linkend=3D"cmd_y"/></member>
- <member><xref linkend=3D"f_make-lock"/></member>
- <member><xref linkend=3D"f_make-read-write-lock"/></member>
- <member><xref linkend=3D"f_make-semaphore"/></member>
- <member><xref linkend=3D"f_process-input-wait"/></member>
- <member><xref linkend=3D"f_process-output-wait"/></member>
- </simplelist>
- </refsect1>
- </refentry>
-
- <refentry id=3D"cmd_y">
- <indexterm zone=3D"cmd_y">
- <primary>:y</primary>
- </indexterm>
-
- <refnamediv>
- <refname>:Y</refname>
- <refpurpose>Yields control of terminal input to a specified
- lisp process (thread).</refpurpose>
- <refclass>Toplevel Command</refclass>
- </refnamediv>
-
- <refsynopsisdiv>
- <synopsis>(<function>:y</function> p)</synopsis>
- </refsynopsisdiv>
-
- <refsect1>
- <title>Arguments and Values</title>
-
- <variablelist>
- <varlistentry>
- <term>p</term>
- <listitem>
- <para>a lisp process (thread), designated either by
- an integer which matches its
- <function>process-serial-number</function>,
- or by a string which is <function>equal</function> to
- its <function>process-name</function>.</para>
- </listitem>
- </varlistentry>
- </variablelist>
- </refsect1>
-
- <refsect1>
- <title>Description</title>
-
- <para>:Y is a toplevel command, not a function. As such, it
- can only be used interactively, and only from the initial
- process.</para>
-
- <para>The command yields control of terminal input to the
- process <varname>p</varname>, which must have used
- <xref linkend=3D"m_with-terminal-input"/> to request access to the
- terminal input stream.</para>
- </refsect1>
-
- <refsect1>
- <title>See Also</title>
- =
- <simplelist type=3D"inline">
- <member><xref linkend=3D"m_with-terminal-input"/></member>
- <member><xref
- linkend=3D"v_request-terminal-input-via-break"/></member>
- <member><xref linkend=3D"f_make-lock"/></member>
- <member><xref linkend=3D"f_make-read-write-lock"/></member>
- <member><xref linkend=3D"f_make-semaphore"/></member>
- <member><xref linkend=3D"f_process-input-wait"/></member>
- <member><xref linkend=3D"f_process-output-wait"/></member>
- </simplelist>
- </refsect1>
- </refentry>
-
- </sect1>
- </chapter>
-
- <chapter id=3D"Programming-with-Sockets">
- <title>Programming with Sockets</title>
-
- <sect1 id=3D"Sockets-Overview">
- <title>Overview</title>
-
- <para>OpenMCL supports the socket abstraction for
- interprocess communication. A socket represents a connection to
- another process, typically (but not necessarily) a TCP/IP
- network connection to a client or server running on some other
- machine on the network.</para>
- <para>All symbols mentioned in this chapter are exported from
- the CCL package. As of version 0.13, these symbols are
- additionally exported from the OPENMCL-SOCKET package.</para>
- <para>OpenMCL supports three types of sockets: TCP sockets, UDP
- sockets, and Unix-domain sockets. This should be enough for all
- but the most esoteric network situations. All sockets are
- created by <xref linkend=3D"f_make-socket"/>. The type of socket
- depends on the arguments to it, as follows:</para>
-
- <variablelist>
- <varlistentry>
- <term>tcp-stream</term>
-
- <listitem>
- <para>A buffered bi-directional stream over a TCP/IP connection.
- tcp-stream is a subclass of stream, and you can read and write to it
- using all the usual stream functions. Created by (make-socket
- :addess-family :internet :type :stream :connect :active ...) or by
- (accept-connection ...).</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>file-socket-stream</term>
-
- <listitem>
- <para>A buffered bi-directional stream over a "UNIX domain"
- connection. file-socket-stream is a subclass of stream, and you can
- read and write to it using all the usual stream functions. Created
- by (make-socket :address-family :file :type :stream :connect :active
- ...) or by (accept-connection ...),</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>listener-socket</term>
-
- <listitem>
- <para>A passive socket used to listen for incoming TCP/IP
- connections on a particular port. A listener-socket is not a stream.
- It doesn't support I/O. It can only be used to create new
- tcp-streams by accept-connection. Created by (make-socket :type
- :stream :connect :passive ...)</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>file-listener-socket</term>
-
- <listitem>
- <para>A passive socket used to listen for incoming UNIX domain
- connections named by a file in the local filesystem. A
- listener-socket is not a stream. It doesn't support I/O. It can
- only be used to create new file-socket-streams by accept-connection.
- Created by (make-socket :address-family :file :type :stream :connect
- :passive ...)</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>udp-socket</term>
-
- <listitem>
- <para>A socket representing a packet-based UDP/IP connection. A
- udp-socket supports I/O but it is not a stream. Instead, you must
- use the special functions send-to and receive-from to read and write
- to it. Created by (make-socket :type :datagram ...)</para>
- </listitem>
- </varlistentry>
- </variablelist>
- </sect1>
-
- <sect1 id=3D"Sockets-Dictionary">
- <title>Sockets Dictionary</title>
- <refentry id=3D"f_make-socket">
- <indexterm zone=3D"f_make-socket">
- <primary>make-socket</primary>
- </indexterm>
- <refnamediv>
- <refname>MAKE-SOCKET</refname>
- <refpurpose></refpurpose>
- <refclass>Function</refclass>
- </refnamediv>
-
- <refsynopsisdiv>
- <synopsis><function>make-socket</function>
- &key; address-family type connect eol format
- remote-host remote-port local-host local-port local-filename
- remote-filename keepalive reuse-address nodelay broadcast linger
- backlog</synopsis>
- </refsynopsisdiv>
-
- <refsect1>
- <title>Arguments and Values</title>
-
- <variablelist>
- <varlistentry>
- <term>address-family</term>
-
- <listitem>
- <para>The address/protocol family of this socket. Currently
- only :internet (the default), meaning IP, and :file,
- referring to UNIX domain addresses, are supported.</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>type</term>
-
- <listitem>
- <para>One of :stream (the default) to request a
- connection-oriented socket, or :datagram to request a
- connectionless socket. The default is :stream.</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>connect</term>
-
- <listitem>
- <para>This argument is only relevant to sockets of type
- :stream. One of :active (the default) to request a :passive
- to request a file or TCP listener socket.</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>eol</term>
-
- <listitem>
- <para>This argument is currently ignored (it is accepted for
- compatibility with Franz Allegro).</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>format</term>
-
- <listitem>
- <para>One of :text (the default), :binary, or :bivalent.
- This argument is ignored for :stream sockets for now, as
- :stream sockets are currently always bivalent (i.e. they
- support both character and byte I/O). For :datagram sockets,
- the format determines the type of buffer created by
- receive-from.</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>remote-host</term>
-
- <listitem>
- <para>Required for TCP streams, it specifies the host to
- connect to (in any format acceptable to lookup-hostname).
- Ignored for listener sockets. For UDP sockets, it can be
- used to specify a default host for subsequent calls to
- send-to or receive-from.</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>remote-port</term>
-
- <listitem>
- <para>Required for TCP streams, it specifies the port to
- connect to (in any format acceptable to lookup-port).
- Ignored for listener sockets. For UDP sockets, it can be
- used to specify a default port for subsequent calls to for
- subsequent calls to send-to or receive-from.</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>remote-filename</term>
-
- <listitem>
- <para>Required for file-socket streams, it specifies the
- name of a file in the local filesystem (e.g., NOT mounted
- via NFS, AFP, SMB, ...) which names and controls access to a
- UNIX-domain socket.</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>local-host</term>
-
- <listitem>
- <para>Allows you to specify a local host address for a
- listener or UDP socket, for the rare case where you want to
- restrict connections to those coming to a specific local
- address for security reasons.</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>local-port</term>
-
- <listitem>
- <para>Specify a local port for a socket. Most useful for
- listener sockets, where it is the port on which the socket
- will listen for connections.</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>local-filename</term>
-
- <listitem>
- <para>Required for file-listener-sockets. Specifies the name
- of a file in the local filesystem which is used to name a
- UNIX-domain socket. The actual filesystem file should not
- previously exist when the file-listener-socket is created;
- its parent directory should exist and be writable by the
- caller. The file used to name the socket will be deleted
- when the file-listener-socket is closed.</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>keepalive</term>
-
- <listitem>
- <para>If true, enables the periodic transmission of
- "keepalive" messages.</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>reuse-address</term>
-
- <listitem>
- <para>If true, allows the reuse of local ports in listener
- sockets, overriding some TCP/IP protocol specifications. You
- will need this if you are debugging a server..</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>nodelay</term>
-
- <listitem>
- <para>If true, disables Nagle's algorithm, which tries
- to minimize TCP packet fragmentation by introducing
- transmission delays in the absence of replies. Try setting
- this if you are using a protocol which involves sending a
- steady stream of data with no replies and are seeing
- significant degradations in throughput.</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>broadcast</term>
-
- <listitem>
- <para>If true, requests permission to broadcast datagrams on
- a UDP socket.</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>linger</term>
-
- <listitem>
- <para>If specified and non-nil, should be the number of
- seconds the OS is allowed to wait for data to be pushed
- through when a close is done. Only relevant for TCP sockets.</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>backlog</term>
-
- <listitem>
- <para>For a listener socket, specifies the number of
- connections which can be pending but not accepted. The
- default is 5, which is also the maximum on some operating
- systems.</para>
- </listitem>
- </varlistentry>
- </variablelist>
- </refsect1>
-
- <refsect1>
- <title>Description</title>
-
- <para>Creates and returns a new socket</para>
- </refsect1>
- </refentry>
-
- <refentry id=3D"f_accept-connection">
- <indexterm zone=3D"f_accept-connection">
- <primary>accept-connection</primary>
- </indexterm>
- <refnamediv>
- <refname>ACCEPT-CONNECTION</refname>
- <refpurpose></refpurpose>
- <refclass>Function</refclass>
- </refnamediv>
-
- <refsynopsisdiv>
- <synopsis><function>accept-connection</function>
- (socket listener-socket) &key; wait</synopsis>
- </refsynopsisdiv>
-
- <refsect1>
- <title>Arguments and Values</title>
-
- <variablelist>
- <varlistentry>
- <term>socket</term>
-
- <listitem>
- <para>The listener-socket to listen on.</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>wait</term>
-
- <listitem>
- <para>If true (the default), and there are no connections
- waiting to be accepted, waits until one arrives. If false,
- returns NIL immediately.</para>
- </listitem>
- </varlistentry>
- </variablelist>
- </refsect1>
-
- <refsect1>
- <title>Description</title>
-
- <para>Extracts the first connection on the queue of pending
- connections, accepts it (i.e. completes the connection startup
- protocol) and returns a new tcp-stream or file-socket-stream
- representing the newly established connection. The tcp stream
- inherits any properties of the listener socket that are relevant
- (e.g. :keepalive, :nodelay, etc.) The original listener socket
- continues to be open listening for more connections, so you can
- call accept-connection on it again.</para>
- </refsect1>
- </refentry>
-
- <refentry id=3D"f_dotted-to-ipaddr">
- <indexterm zone=3D"f_dotted-to-ipaddr">
- <primary>dotted-to-ipaddr</primary>
- </indexterm>
- <refnamediv>
- <refname>DOTTED-TO-IPADDR</refname>
- <refpurpose></refpurpose>
- <refclass>Function</refclass>
- </refnamediv>
-
- <refsynopsisdiv>
- <synopsis><function>dotted-to-ipaddr</function>
- dotted &key; errorp</synopsis>
- </refsynopsisdiv>
-
- <refsect1>
- <title>Arguments and Values</title>
-
- <variablelist>
- <varlistentry>
- <term>dotted</term>
-
- <listitem>
- <para>A string representing an IP address in the
- "nn.nn.nn.nn" format</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>errorp</term>
-
- <listitem>
- <para>If true (the default) an error is signaled if dotted
- is invalid. If false, NIL is returned.</para>
- </listitem>
- </varlistentry>
- </variablelist>
- </refsect1>
-
- <refsect1>
- <title>Description</title>
-
- <para>Converts a dotted-string representation of a host address to
- a 32-bit unsigned IP address.</para>
- </refsect1>
- </refentry>
-
- <refentry id=3D"f_ipaddr-to-dotted">
- <indexterm zone=3D"f_ipaddr-to-dotted">
- <primary>ipaddr-to-dotted</primary>
- </indexterm>
- <refnamediv>
- <refname>IPADDR-TO-DOTTED</refname>
- <refpurpose></refpurpose>
- <refclass>Function</refclass>
- </refnamediv>
-
- <refsynopsisdiv>
- <synopsis><function>ipaddr-to-dotted</function>
- ipaddr &key; values</synopsis>
- </refsynopsisdiv>
-
- <refsect1>
- <title>Arguments and Values</title>
-
- <variablelist>
- <varlistentry>
- <term>ipaddr</term>
-
- <listitem>
- <para>A 32-bit integer representing an internet host address</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>values</term>
-
- <listitem>
- <para>If false (the default), returns a string in the form
- "nn.nn.nn.nn". If true, returns four values
- representing the four octets of the address as unsigned
- 8-bit integers.</para>
- </listitem>
- </varlistentry>
- </variablelist>
- </refsect1>
-
- <refsect1>
- <title>Description</title>
-
- <para>Converts a 32-bit unsigned IP address into octets.</para>
- </refsect1>
- </refentry>
-
- <refentry id=3D"f_ipaddr-to-hostname">
- <indexterm zone=3D"f_ipaddr-to-hostname">
- <primary>ipaddr-to-hostname</primary>
- </indexterm>
- <refnamediv>
- <refname>IPADDR-TO-HOSTNAME</refname>
- <refpurpose></refpurpose>
- <refclass>Function</refclass>
- </refnamediv>
-
- <refsynopsisdiv>
- <synopsis><function>ipaddr-to-hostname</function>
- ipaddr &key; ignore-cache</synopsis>
- </refsynopsisdiv>
-
- <refsect1>
- <title>Arguments and Values</title>
-
- <variablelist>
- <varlistentry>
- <term>ipaddr</term>
-
- <listitem>
- <para>a 32-bit integer representing an internet host address</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>ignore-cache</term>
-
- <listitem>
- <para>This argument is ignored (it is accepted for
- compatibility with Franz Allegro)</para>
- </listitem>
- </varlistentry>
- </variablelist>
- </refsect1>
-
- <refsect1>
- <title>Description</title>
-
- <para>Converts a 32-bit unsigned IP address into a host name
- string</para>
- </refsect1>
- </refentry>
-
- <refentry id=3D"f_lookup-hostname">
- <indexterm zone=3D"f_lookup-hostname">
- <primary>lookup-hostname</primary>
- </indexterm>
- <refnamediv>
- <refname>LOOKUP-HOSTNAME</refname>
- <refpurpose></refpurpose>
- <refclass>Function</refclass>
- </refnamediv>
-
- <refsynopsisdiv>
- <synopsis><function>lookup-hostname</function>
- host</synopsis>
- </refsynopsisdiv>
-
- <refsect1>
- <title>Arguments and Values</title>
-
- <variablelist>
- <varlistentry>
- <term>host</term>
-
- <listitem>
- <para>Specifies the host. It can be either a host name
- string such as "clozure.com", or a dotted address
- string such as "192.168.0.1", or a 32-bit unsigned
- IP address such as 3232235521.</para>
- </listitem>
- </varlistentry>
- </variablelist>
- </refsect1>
-
- <refsect1>
- <title>Description</title>
-
- <para>Converts a host spec in any of the acceptable formats into a
- 32-bit unsigned IP address</para>
- </refsect1>
- </refentry>
-
- <refentry id=3D"f_lookup-port">
- <indexterm zone=3D"f_lookup-port">
- <primary>lookup-port</primary>
- </indexterm>
- <refnamediv>
- <refname>LOOKUP-PORT</refname>
- <refpurpose></refpurpose>
- <refclass>Function</refclass>
- </refnamediv>
-
- <refsynopsisdiv>
- <synopsis><function>lookup-port</function>
- port protocol</synopsis>
- </refsynopsisdiv>
-
- <refsect1>
- <title>Arguments and Values</title>
-
- <variablelist>
- <varlistentry>
- <term>port</term>
-
- <listitem>
- <para>Specifies the port. It can be either a string, such as
- "http" or a symbol, such as :http, or an unsigned
- port number. Note that a string is case-sensitive. A symbol
- is lowercased before lookup.</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>protocol</term>
-
- <listitem>
- <para>Must be one of "tcp" or "udp".</para>
- </listitem>
- </varlistentry>
- </variablelist>
- </refsect1>
-
- <refsect1>
- <title>Description</title>
-
- <para>Finds the port number for the specified port and protocol</para>
- </refsect1>
- </refentry>
-
- <refentry id=3D"f_receive-from">
- <indexterm zone=3D"f_receive-from">
- <primary>receive-from</primary>
- </indexterm>
- <refnamediv>
- <refname>RECEIVE-FROM</refname>
- <refpurpose></refpurpose>
- <refclass>Function</refclass>
- </refnamediv>
-
- <refsynopsisdiv>
- <synopsis><function>receive-from</function>
- (socket udp-socket) size &key; buffer
- extract offset</synopsis>
- </refsynopsisdiv>
-
- <refsect1>
- <title>Arguments and Values</title>
-
- <variablelist>
- <varlistentry>
- <term>socket</term>
-
- <listitem>
- <para>The socket to read from</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>size</term>
-
- <listitem>
- <para>Maximum number of bytes to read. If the packet is
- larger than this, any extra bytes are discarded.</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>buffer</term>
-
- <listitem>
- <para>If specified, must be either a string or a byte vector
- which will be used to read in the data. If not specified, a
- new buffer will be created (of type determined by
- socket-format).</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>extract</term>
-
- <listitem>
- <para>If true, the subsequence of the buffer corresponding
- only to the data read in is extracted and returned as the
- first value. If false (the default) the original buffer is
- returned even if it is only partially filled.</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>offset</term>
-
- <listitem>
- <para>Specifies the start offset into the buffer at which
- data is to be stored. The default is 0.</para>
- </listitem>
- </varlistentry>
- </variablelist>
- </refsect1>
-
- <refsect1>
- <title>Description</title>
-
- <para>Reads a UDP packet from a socket. If no packets are
- available, waits for a packet to arrive. Returns four values:</para>
-
- <orderedlist continuation=3D"restarts" inheritnum=3D"ignore">
- <listitem>
- <para>The buffer with the data</para>
- </listitem>
-
- <listitem>
- <para>The number of bytes read</para>
- </listitem>
-
- <listitem>
- <para>The 32-bit unsigned IP address of the sender of the data</par=
a>
- </listitem>
-
- <listitem>
- <para>The port number of the sender of the data</para>
- </listitem>
- </orderedlist>
- </refsect1>
- </refentry>
-
- <refentry id=3D"f_send-to">
- <indexterm zone=3D"f_send-to">
- <primary>send-to</primary>
- </indexterm>
- <refnamediv>
- <refname>SEND-TO</refname>
- <refpurpose></refpurpose>
- <refclass>Function</refclass>
- </refnamediv>
-
- <refsynopsisdiv>
- <synopsis><function>send-to</function>
- (socket udp-socket) buffer size &key; remote-host
- remote-port offset</synopsis>
- </refsynopsisdiv>
-
- <refsect1>
- <title>Arguments and Values</title>
-
- <variablelist>
- <varlistentry>
- <term>socket</term>
-
- <listitem>
- <para>The socket to write to</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>buffer</term>
-
- <listitem>
- <para>A vector containing the data to send. It must be
- either a string or a byte vector (either one is acceptable
- regardless of the stream format).</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>size</term>
-
- <listitem>
- <para>Number of bytes to send</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>remote-host</term>
-
- <listitem>
- <para>The host to send the packet to, in any format
- acceptable to lookup-hostname. The default is the remote
- host specified in the call to make-socket.</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>remote-port</term>
-
- <listitem>
- <para>The port to send the packet to, in any format
- acceptable to lookup-port. The default is the remote port
- specified in the call to make-socket.</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>offset</term>
-
- <listitem>
- <para>The offset in the buffer where the packet data starts</para>
- </listitem>
- </varlistentry>
- </variablelist>
- </refsect1>
-
- <refsect1>
- <title>Description</title>
-
- <para>Send a UDP packet over a socket.</para>
- </refsect1>
- </refentry>
-
- <refentry id=3D"f_shutdown">
- <indexterm zone=3D"f_shutdown">
- <primary>shutdown</primary>
- </indexterm>
- <refnamediv>
- <refname>SHUTDOWN</refname>
- <refpurpose></refpurpose>
- <refclass>Function</refclass>
- </refnamediv>
-
- <refsynopsisdiv>
- <synopsis><function>shutdown</function>
- socket &key; direction</synopsis>
- </refsynopsisdiv>
-
- <refsect1>
- <title>Arguments and Values</title>
-
- <variablelist>
- <varlistentry>
- <term>socket</term>
-
- <listitem>
- <para>The socket to shut down (typically a tcp-stream)</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>direction</term>
-
- <listitem>
- <para>One of :input to disallow further input, or :output to
- disallow further output.</para>
- </listitem>
- </varlistentry>
- </variablelist>
- </refsect1>
-
- <refsect1>
- <title>Description</title>
-
- <para>Shuts down part of a bidirectional connection. This is
- useful if e.g. you need to read responses after sending an
- end-of-file signal.</para>
- </refsect1>
- </refentry>
-
- <refentry id=3D"f_socket-os-fd">
- <indexterm zone=3D"f_socket-os-fd">
- <primary>socket-os-fd</primary>
- </indexterm>
- <refnamediv>
- <refname>SOCKET-OS-FD</refname>
- <refpurpose></refpurpose>
- <refclass>Function</refclass>
- </refnamediv>
-
- <refsynopsisdiv>
- <synopsis><function>socket-os-fd</function>
- socket</synopsis>
- </refsynopsisdiv>
-
- <refsect1>
- <title>Arguments and Values</title>
-
- <variablelist>
- <varlistentry>
- <term>socket</term>
-
- <listitem>
- <para>The socket</para>
- </listitem>
- </varlistentry>
- </variablelist>
- </refsect1>
-
- <refsect1>
- <title>Description</title>
-
- <para>Returns the native OS's representation of the socket, or
- NIL if the socket is closed. On Unix, this is the Unix 'file
- descriptor', a small non-negative integer. Note that it is
- rather dangerous to mess around with tcp-stream fd's, as there
- is all sorts of buffering and asynchronous I/O going on above the
- OS level. listener-socket and udp-socket fd's are safer to
- mess with directly as there is less magic going on.</para>
- </refsect1>
- </refentry>
-
- <refentry id=3D"f_remote-host">
- <indexterm zone=3D"f_remote-host">
- <primary>remote-host</primary>
- </indexterm>
- <refnamediv>
- <refname>REMOTE-HOST</refname>
- <refpurpose></refpurpose>
- <refclass>Function</refclass>
- </refnamediv>
-
- <refsynopsisdiv>
- <synopsis><function>remote-host</function>
- socket</synopsis>
- </refsynopsisdiv>
-
- <refsect1>
- <title>Arguments and Values</title>
-
- <variablelist>
- <varlistentry>
- <term>socket</term>
-
- <listitem>
- <para>The socket</para>
- </listitem>
- </varlistentry>
- </variablelist>
- </refsect1>
-
- <refsect1>
- <title>Description</title>
-
- <para>Returns the 32-bit unsigned IP address of the remote host,
- or NIL if the socket is not connected.</para>
- </refsect1>
- </refentry>
-
- <refentry id=3D"f_remote-port">
- <indexterm zone=3D"f_remote-port">
- <primary>remote-port</primary>
- </indexterm>
- <refnamediv>
- <refname>REMOTE-PORT</refname>
- <refpurpose></refpurpose>
- <refclass>Function</refclass>
- </refnamediv>
-
- <refsynopsisdiv>
- <synopsis><function>remote-port</function>
- socket</synopsis>
- </refsynopsisdiv>
-
- <refsect1>
- <title>Arguments and Values</title>
-
- <variablelist>
- <varlistentry>
- <term>socket</term>
-
- <listitem>
- <para>The socket</para>
- </listitem>
- </varlistentry>
- </variablelist>
- </refsect1>
-
- <refsect1>
- <title>Description</title>
-
- <para>Returns the remote port number, or NIL if the socket is not
- connected.</para>
- </refsect1>
- </refentry>
-
- <refentry id=3D"f_local-host">
- <indexterm zone=3D"f_local-host">
- <primary>local-host</primary>
- </indexterm>
- <refnamediv>
- <refname>LOCAL-HOST</refname>
- <refpurpose></refpurpose>
- <refclass>Function</refclass>
- </refnamediv>
-
- <refsynopsisdiv>
- <synopsis><function>local-host</function>
- socket</synopsis>
- </refsynopsisdiv>
-
- <refsect1>
- <title>Arguments and Values</title>
-
- <variablelist>
- <varlistentry>
- <term>socket</term>
-
- <listitem>
- <para>The socket</para>
- </listitem>
- </varlistentry>
- </variablelist>
- </refsect1>
-
- <refsect1>
- <title>Description</title>
-
- <para>Returns 32-bit unsigned IP address of the local host.</para>
- </refsect1>
- </refentry>
-
- <refentry id=3D"f_local-port">
- <indexterm zone=3D"f_local-port">
- <primary>local-port</primary>
- </indexterm>
- <refnamediv>
- <refname>LOCAL-PORT</refname>
- <refpurpose></refpurpose>
- <refclass>Function</refclass>
- </refnamediv>
-
- <refsynopsisdiv>
- <synopsis><function>local-port</function>
- socket</synopsis>
- </refsynopsisdiv>
-
- <refsect1>
- <title>Arguments and Values</title>
-
- <variablelist>
- <varlistentry>
- <term>socket</term>
-
- <listitem>
- <para>The socket</para>
- </listitem>
- </varlistentry>
- </variablelist>
- </refsect1>
-
- <refsect1>
- <title>Description</title>
-
- <para>Returns the local port number</para>
- </refsect1>
- </refentry>
-
- <refentry id=3D"f_socket-address-family">
- <indexterm zone=3D"f_socket-address-family">
- <primary>socket-address-family</primary>
- </indexterm>
- <refnamediv>
- <refname>SOCKET-ADDRESS-FAMILY</refname>
- <refpurpose></refpurpose>
- <refclass>Function</refclass>
- </refnamediv>
-
- <refsynopsisdiv>
- <synopsis><function>socket-address-family</function>
- socket</synopsis>
- </refsynopsisdiv>
-
- <refsect1>
- <title>Arguments and Values</title>
-
- <variablelist>
- <varlistentry>
- <term>socket</term>
-
- <listitem>
- <para>The socket</para>
- </listitem>
- </varlistentry>
- </variablelist>
- </refsect1>
-
- <refsect1>
- <title>Description</title>
-
- <para>Returns :internet or :file, as appropriate.</para>
- </refsect1>
- </refentry>
-
- <refentry id=3D"f_socket-connect">
- <indexterm zone=3D"f_socket-connect">
- <primary>socket-connect</primary>
- </indexterm>
- <refnamediv>
- <refname>SOCKET-CONNECT</refname>
- <refpurpose></refpurpose>
- <refclass>Function</refclass>
- </refnamediv>
-
- <refsynopsisdiv>
- <synopsis><function>socket-connect</function>
- socket</synopsis>
- </refsynopsisdiv>
-
- <refsect1>
- <title>Arguments and Values</title>
-
- <variablelist>
- <varlistentry>
- <term>socket</term>
-
- <listitem>
- <para>The socket</para>
- </listitem>
- </varlistentry>
- </variablelist>
- </refsect1>
-
- <refsect1>
- <title>Description</title>
-
- <para>Returns :active for tcp-stream, :passive for
- listener-socket, and NIL for udp-socket</para>
- </refsect1>
- </refentry>
-
- <refentry id=3D"f_socket-format">
- <indexterm zone=3D"f_socket-format">
- <primary>socket-format</primary>
- </indexterm>
- <refnamediv>
- <refname>SOCKET-FORMAT</refname>
- <refpurpose></refpurpose>
- <refclass>Function</refclass>
- </refnamediv>
-
- <refsynopsisdiv>
- <synopsis><function>socket-format</function>
- socket</synopsis>
- </refsynopsisdiv>
-
- <refsect1>
- <title>Arguments and Values</title>
-
- <variablelist>
- <varlistentry>
- <term>socket</term>
-
- <listitem>
- <para>The socket</para>
- </listitem>
- </varlistentry>
- </variablelist>
- </refsect1>
-
- <refsect1>
- <title>Description</title>
-
- <para>Returns the socket format as specified by the :format
- argument to make-socket.</para>
- </refsect1>
- </refentry>
-
- <refentry id=3D"f_socket-type">
- <indexterm zone=3D"f_socket-type">
- <primary>socket-type</primary>
- </indexterm>
- <refnamediv>
- <refname>SOCKET-TYPE</refname>
- <refpurpose></refpurpose>
- <refclass>Function</refclass>
- </refnamediv>
-
- <refsynopsisdiv>
- <synopsis><function>socket-type</function>
- socket</synopsis>
- </refsynopsisdiv>
-
- <refsect1>
- <title>Arguments and Values</title>
-
- <variablelist>
- <varlistentry>
- <term>socket</term>
-
- <listitem>
- <para>The socket</para>
- </listitem>
- </varlistentry>
- </variablelist>
- </refsect1>
-
- <refsect1>
- <title>Description</title>
-
- <para>returns :stream for tcp-stream and listener-socket, and
- :datagram for udp-socket.</para>
- </refsect1>
- </refentry>
-
- <refentry id=3D"c_socket-error">
- <indexterm zone=3D"c_socket-error">
- <primary>socket-error</primary>
- </indexterm>
- <refnamediv>
- <refname>SOCKET-ERROR</refname>
- <refpurpose></refpurpose>
- <refclass>Class</refclass>
- </refnamediv>
-
- <refsect1>
- <title>Description</title>
-
- <para>The class of OS errors signaled by socket functions</para>
- </refsect1>
-
- <refsect1>
- <title>Superclasses</title>
-
- <para>simple-error</para>
- </refsect1>
- </refentry>
-
- <refentry id=3D"f_socket-error-code">
- <indexterm zone=3D"f_socket-error-code">
- <primary>socket-error-code</primary>
- </indexterm>
- <refnamediv>
- <refname>SOCKET-ERROR-CODE</refname>
- <refpurpose></refpurpose>
- <refclass>Function</refclass>
- </refnamediv>
-
- <refsynopsisdiv>
- <synopsis><function>socket-error-code</function>
- socket-error</synopsis>
- </refsynopsisdiv>
-
- <refsect1>
- <title>Arguments and Values</title>
-
- <variablelist>
- <varlistentry>
- <term>socket-error</term>
-
- <listitem>
- <para>the condition</para>
- </listitem>
- </varlistentry>
- </variablelist>
- </refsect1>
-
- <refsect1>
- <title>Description</title>
-
- <para>The OS error code of the error</para>
- </refsect1>
- </refentry>
-
- <refentry id=3D"f_socket-error-identifier">
- <indexterm zone=3D"f_socket-error-identifier">
- <primary>socket-error-identifier</primary>
- </indexterm>
- <refnamediv>
- <refname>SOCKET-ERROR-IDENTIFIER</refname>
- <refpurpose></refpurpose>
- <refclass>Function</refclass>
- </refnamediv>
-
- <refsynopsisdiv>
- <synopsis><function>socket-error-identifier</function>
- socket-error</synopsis>
- </refsynopsisdiv>
-
- <refsect1>
- <title>Arguments and Values</title>
-
- <variablelist>
- <varlistentry>
- <term>socket-error</term>
-
- <listitem>
- <para>the condition</para>
- </listitem>
- </varlistentry>
- </variablelist>
- </refsect1>
-
- <refsect1>
- <title>Description</title>
-
- <para>A symbol representing the error code in a more
- OS-independent way.</para>
-
- <para>One of: :address-in-use :connection-aborted :no-buffer-space
- :connection-timed-out :connection-refused :host-unreachable
- :host-down :network-down :address-not-available :network-reset
- :connection-reset :shutdown :access-denied or :unknown.</para>
- </refsect1>
- </refentry>
-
- <refentry id=3D"f_socket-error-situation">
- <indexterm zone=3D"f_socket-error-situation">
- <primary>socket-error-situation</primary>
- </indexterm>
- <refnamediv>
- <refname>SOCKET-ERROR-SITUATION</refname>
- <refpurpose></refpurpose>
- <refclass>Function</refclass>
- </refnamediv>
-
- <refsynopsisdiv>
- <synopsis><function>socket-error-situation</function>
- socket-error</synopsis>
- </refsynopsisdiv>
-
- <refsect1>
- <title>Arguments and Values</title>
-
- <variablelist>
- <varlistentry>
- <term>socket-error</term>
-
- <listitem>
- <para>the condition</para>
- </listitem>
- </varlistentry>
- </variablelist>
- </refsect1>
-
- <refsect1>
- <title>Description</title>
-
- <para>A string describing the context where the error happened. On
- Linux, this is the name of the system call which returned the
- error.</para>
- </refsect1>
- </refentry>
-
- <refentry id=3D"o_close">
- <indexterm zone=3D"o_close">
- <primary>close</primary>
- </indexterm>
- <refnamediv>
- <refname>CLOSE</refname>
- <refpurpose></refpurpose>
- <refclass>Method</refclass>
- </refnamediv>
-
- <refsynopsisdiv>
- <synopsis><function>close</function>
- (socket socket) &key; abort</synopsis>
- </refsynopsisdiv>
-
- <refsect1>
- <title>Arguments and Values</title>
-
- <variablelist>
- <varlistentry>
- <term>socket</term>
-
- <listitem>
- <para>The socket to close</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>abort</term>
-
- <listitem>
- <para>If false (the default), closes the socket in an
- orderly fashion, finishing up any buffered pending I/O,
- before closing the connection. If true, aborts/ignores
- pending I/O. (For listener and udp sockets, this argument is
- effectively ignored since there is never any buffered I/O to
- clean up).</para>
- </listitem>
- </varlistentry>
- </variablelist>
- </refsect1>
-
- <refsect1>
- <title>Description</title>
-
- <para>The close generic function can be applied to sockets. It
- releases the operating system resources associated with the
- socket.</para>
- </refsect1>
- </refentry>
-
- <refentry id=3D"m_with-open-socket">
- <indexterm zone=3D"m_with-open-socket">
- <primary>with-open-socket</primary>
- </indexterm>
- <refnamediv>
- <refname>WITH-OPEN-SOCKET</refname>
- <refpurpose></refpurpose>
- <refclass>Macro</refclass>
- </refnamediv>
-
- <refsynopsisdiv>
- <synopsis><function>with-open-socket</function>
- (var . make-socket-args) &body; body</synopsis>
- </refsynopsisdiv>
-
- <refsect1>
- <title>Arguments and Values</title>
-
- <variablelist>
- <varlistentry>
- <term>var</term>
-
- <listitem>
- <para>variable to bind</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>make-socket-args</term>
-
- <listitem>
- <para>arguments suitable for passing to make-socket</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>body</term>
-
- <listitem>
- <para>body to execute</para>
- </listitem>
- </varlistentry>
- </variablelist>
- </refsect1>
-
- <refsect1>
- <title>Description</title>
-
- <para>executes body with var bound to the result of applying
- make-socket to make-socket-args. The socket gets closed on exit.</para>
- </refsect1>
- </refentry>
- </sect1>
- </chapter>
-
- <chapter id=3D"Running-Other-Programs-as-Subprocesses">
- <title>Running Other Programs as Subprocesses</title>
-
- <sect1 id=3D"Subprocess-Overview">
- <title>Overview</title>
- <para>OpenMCL provides primitives to run external Unix programs,
- to select and connect Lisp streams to their input and output
- sources, to (optionally) wait for their completion and to check
- their execution and exit status.</para>
- <para>All of the global symbols described below are exported
- from the CCL package.</para>
- <para>This implementation is modeled on - and uses some code
- from - similar facilities in CMUCL.</para>
- </sect1>
-
- <sect1 id=3D"Subprocess-Examples">
- <title>Examples</title>
- <programlisting>
-;;; Capture the output of the "uname" program in a lisp string-stream
-;;; and return the generated string (which will contain a trailing
-;;; newline.)
-? (with-output-to-string (stream)
- (run-program "uname" '("-r") :output stream))
-;;; Write a string to *STANDARD-OUTPUT*, the hard way.
-? (run-program "cat" () :input (make-string-input-stream "hello") :output =
t)
-;;; Find out that "ls" doesn't expand wildcards.
-? (run-program "ls" '("*.lisp") :output t)
-;;; Let the shell expand wildcards.
-? (run-program "sh" '("-c" "ls *.lisp") :output t)
-</programlisting>
- <para>These last examples will only produce output if OpenMCL's
- current directory contains .lisp files, of course.</para>
- </sect1>
-
- <sect1 id=3D"Limitations-and-known-bugs">
- <title>Limitations and known bugs</title>
- <itemizedlist>
- <listitem><para>OpenMCL and the external processs may get
- confused about whoowns which streams when input, output, or
- error are specified as T and wait is specified as
- NIL.</para></listitem>
- <listitem><para>External processes that need to talk to a
- terminal device may not work properly; the environment (SLIME,
- ILISP) under which OpenMCL is run can affect
- this.</para></listitem>
- =
- </itemizedlist>
- </sect1>
-
- <sect1 id=3D"External-Program-Dictionary">
- <title>External-Program Dictionary</title>
- <refentry id=3D"f_run-program">
- <indexterm zone=3D"f_run-program">
- <primary>run-program</primary>
- </indexterm>
-
- <refnamediv>
- <refname>RUN-PROGRAM</refname>
- <refpurpose>Invokes an external program as an OS subprocess
- of lisp.</refpurpose>
- <refclass>Function</refclass>
- </refnamediv>
-
- <refsynopsisdiv>
- <synopsis><function>run-program</function>
- program args &key; (wait t) pty input
- if-input-does-not-exist output (if-output-exists :error) (error
- :output) (if-error-exists :error) status-hook</synopsis>
- </refsynopsisdiv>
-
- <refsect1>
- <title>Arguments and Values</title>
-
- <variablelist>
- <varlistentry>
- <term>program</term>
-
- <listitem>
- <para>A string or pathname which denotes an executable file.
- The PATH environment variable is used to find programs whose
- name doesn't contain a directory component.</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>args</term>
-
- <listitem>
- <para>A list of simple-strings</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>wait</term>
-
- <listitem>
- <para>Indicates whether or not run-program should wait for
- the EXTERNAL-PROCESS to complete or should return
- immediately.</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>pty</term>
-
- <listitem>
- <para>This option is accepted but currently ignored;
- it's intended to make it easier to run external programs
- that need to interact with a terminal device.</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>input</term>
-
- <listitem>
- <para>Selects the input source used by the EXTERNAL-PROCESS.
- May be any of the following:</para>
-
- <itemizedlist>
- <listitem>
- <para>NIL Specifies that a null input stream (e.g.,
- /dev/null) should be used.</para>
- </listitem>
-
- <listitem>
- <para>T Specifies that the EXTERNAL-PROCESS should use
- the input source with which OpenMCL was invoked.</para>
- </listitem>
-
- <listitem>
- <para>A string or pathname. Specifies that the
- EXTERNAL-PROCESS should receive its input from the named
- existing file.</para>
- </listitem>
-
- <listitem>
- <para>:STREAM Creates a Lisp stream opened for character
- output. Any data written to this stream (accessible as
- the EXTERNAL-PROCESS-INPUT-STREAM of the
- EXTERNAL-PROCESS object) appears as input to the
- external process.</para>
- </listitem>
-
- <listitem>
- <para>A stream. Specifies that the lisp stream should
- provide input to the EXTERNAL-PROCESS.</para>
- </listitem>
- </itemizedlist>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>if-input-does-not-exist</term>
-
- <listitem>
- <para>If the input argument specifies the name of an
- existing file, this argument is used as the
- if-does-not-exist argument to OPEN when that file is opened.</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>output</term>
-
- <listitem>
- <para>Specifies where standard output from the external
- process should be sent. Analogous to input above.</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>if-output-exists</term>
-
- <listitem>
- <para>If output is specified as a string or pathname, this
- argument is used as the if-exists argument to OPEN when that
- file is opened.</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>error</term>
-
- <listitem>
- <para>Specifies where error output from the external process
- should be sent. In addition to the values allowed for
- output, the keyword :OUTPUT can be used to indicate that
- error output should be sent where standard output goes.</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>if-error-exists</term>
-
- <listitem>
- <para>Analogous to if-output-exists.</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>status-hook</term>
-
- <listitem>
- <para>A user-defined function of one argument (the
- EXTERNAL-PROCESS structure.) This function is called
- whenever OpenMCL detects a change in the staus of the
- EXTERNAL-PROCESS.</para>
- </listitem>
- </varlistentry>
- </variablelist>
- </refsect1>
-
- <refsect1>
- <title>Description</title>
-
- <para>Runs the specified program in an external (Unix) process,
- returning an object of type EXTERNAL-PROCESS if successful.</para>
- </refsect1>
- </refentry>
-
- <refentry id=3D"f_signal-external-process">
- <indexterm zone=3D"f_signal-external-process">
- <primary>signal-external-process</primary>
- </indexterm>
-
- <refnamediv>
- <refname>SIGNAL-EXTERNAL-PROCESS</refname>
- <refpurpose></refpurpose>
- <refclass>Function</refclass>
- </refnamediv>
-
- <refsynopsisdiv>
- <synopsis><function>signal-external-process</function>
- proc signal-number</synopsis>
- </refsynopsisdiv>
-
- <refsect1>
- <title>Arguments and Values</title>
-
- <variablelist>
- <varlistentry>
- <term>proc</term>
-
- <listitem>
- <para>An EXTERNAL-PROCESS, as returned by RUN-PROGRAM.</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>signal</term>
-
- <listitem>
- <para>A small integer.</para>
- </listitem>
- </varlistentry>
- </variablelist>
- </refsect1>
-
- <refsect1>
- <title>Description</title>
-
- <para>Sends the specified "signal" to the specified
- external process. (Typically, it would only be useful tocall
- this function if the EXTERNAL-PROCESS was created with :WAIT
- NIL. ) Returns T if successful; signals an error otherwise.</para>
- </refsect1>
- </refentry>
-
- <refentry id=3D"f_external-process-id">
- <indexterm zone=3D"f_external-process-id">
- <primary>external-process-id</primary>
- </indexterm>
-
- <refnamediv>
- <refname>EXTERNAL-PROCESS-ID</refname>
- <refpurpose>Returns the "process ID" of an OS subprocess,
- a positive integer which identifies it.</refpurpose>
- <refclass>Function</refclass>
- </refnamediv>
-
- <refsynopsisdiv>
- <synopsis><function>external-process-id</function>
- proc</synopsis>
- </refsynopsisdiv>
-
- <refsect1>
- <title>Arguments and Values</title>
-
- <variablelist>
- <varlistentry>
- <term>proc</term>
-
- <listitem>
- <para>An EXTERNAL-PROCESS, as returned by RUN-PROGRAM.</para>
- </listitem>
- </varlistentry>
- </variablelist>
- </refsect1>
-
- <refsect1>
- <title>Description</title>
-
- <para>Returns the <emphasis>process id</emphasis> assigned to
- the external process by the operating system. This is typically
- a positive, 16-bit number.</para>
- </refsect1>
- </refentry>
-
- <refentry id=3D"f_external-process-input-stream">
- <indexterm zone=3D"f_external-process-input-stream">
- <primary>external-process-input-stream</primary>
- </indexterm>
-
- <refnamediv>
- <refname>EXTERNAL-PROCESS-INPUT-STREAM</refname>
- <refpurpose>Returns the lisp stream which is used to write
- input to a given OS subprocess, if it has one.</refpurpose>
- <refclass>Function</refclass>
- </refnamediv>
-
- <refsynopsisdiv>
- <synopsis><function>external-process-input-stream</function>
- proc</synopsis>
- </refsynopsisdiv>
-
- <refsect1>
- <title>Arguments and Values</title>
-
- <variablelist>
- <varlistentry>
- <term>proc</term>
-
- <listitem>
- <para>An EXTERNAL-PROCESS, as returned by RUN-PROGRAM.</para>
- </listitem>
- </varlistentry>
- </variablelist>
- </refsect1>
-
- <refsect1>
- <title>Description</title>
-
- <para>Returns the stream created when the input argument to
- run-program is specified as :STREAM.</para>
- </refsect1>
- </refentry>
-
- <refentry id=3D"f_external-process-output-stream">
- <indexterm zone=3D"f_external-process-output-stream">
- <primary>external-process-output-stream</primary>
- </indexterm>
-
- <refnamediv>
- <refname>EXTERNAL-PROCESS-OUTPUT-STREAM</refname>
- <refpurpose>Returns the lisp stream which is used to read
- output from an OS subprocess, if there is one.</refpurpose>
- <refclass>Function</refclass>
- </refnamediv>
-
- <refsynopsisdiv>
- <synopsis><function>external-process-output-stream</function>
- proc</synopsis>
- </refsynopsisdiv>
-
- <refsect1>
- <title>Arguments and Values</title>
-
- <variablelist>
- <varlistentry>
- <term>proc</term>
-
- <listitem>
- <para>An EXTERNAL-PROCESS, as returned by RUN-PROGRAM.</para>
- </listitem>
- </varlistentry>
- </variablelist>
- </refsect1>
-
- <refsect1>
- <title>Description</title>
-
- <para>Returns the stream created when the output argument to
- run-program is specified as :STREAM.</para>
- </refsect1>
- </refentry>
-
- <refentry id=3D"f_external-process-error-stream">
- <indexterm zone=3D"f_external-process-error-stream">
- <primary>external-process-error-stream</primary>
- </indexterm>
-
- <refnamediv>
- <refname>EXTERNAL-PROCESS-ERROR-STREAM</refname>
- <refpurpose>Returns the stream which is used to read
- "error" output from a given OS subprocess, if it has
- one.</refpurpose>
- <refclass>Function</refclass>
- </refnamediv>
-
- <refsynopsisdiv>
- <synopsis><function>external-process-error-stream</function>
- proc</synopsis>
- </refsynopsisdiv>
-
- <refsect1>
- <title>Arguments and Values</title>
-
- <variablelist>
- <varlistentry>
- <term>proc</term>
-
- <listitem>
- <para>An EXTERNAL-PROCESS, as returned by RUN-PROGRAM.</para>
- </listitem>
- </varlistentry>
- </variablelist>
- </refsect1>
-
- <refsect1>
- <title>Description</title>
-
- <para>Returns the stream created when the error argument to
- run-program is specified as :STREAM.</para>
- </refsect1>
- </refentry>
-
- <refentry id=3D"f_external-process-status">
- <indexterm zone=3D"f_external-process-status">
- <primary>external-process-status</primary>
- </indexterm>
-
- <refnamediv>
- <refname>EXTERNAL-PROCESS-STATUS</refname>
- <refpurpose>Returns information about whether an OS
- subprocess is running; or, if not, why not; and what its
- result code was if it completed.</refpurpose>
- <refclass>Function</refclass>
- </refnamediv>
-
- <refsynopsisdiv>
- <synopsis><function>external-process-status</function>
- proc</synopsis>
- </refsynopsisdiv>
-
- <refsect1>
- <title>Arguments and Values</title>
-
- <variablelist>
- <varlistentry>
- <term>proc</term>
-
- <listitem>
- <para>An EXTERNAL-PROCESS, as returned by RUN-PROGRAM.</para>
- </listitem>
- </varlistentry>
- </variablelist>
- </refsect1>
-
- <refsect1>
- <title>Description</title>
-
- <para>Returns, as multiple values, a keyword denoting the status
- of the external process (one of :running, :stopped, :signaled, or
- :exited), and the exit code or terminating signal if the first
- value is other than :running.</para>
- </refsect1>
- </refentry>
- </sect1>
- </chapter>
-
- <chapter id=3D"Creating-Your-Own-Stream-Classes-with-Gray-Streams">
- <title>Creating Your Own Stream Classes with Gray Streams</title>
-
- <sect1 id=3D"Streams-Overview">
- <title>Overview</title>
- <para>This chapter is still being written and revised, because
- it is woefully incomplete. The dictionary section currently
- only lists a couple functions. Caveat lector.</para>
- <para>Gray streams are an extension to Common Lisp. They were
- proposed for standardization by David Gray (the astute reader
- now understands their name) quite some years ago, but not
- accepted, because they had not been tried sufficiently to find
- conceptual problems with them.</para>
- <para>They have since been implemented by quite a few modern
- Lisp implementations. However, they do indeed have some
- inadequacies, and each implementation has addressed these in
- different ways. The situation today is that it's difficult to
- even find out how to get started using Gray streams. This is
- why standards are important.</para>
- <para>Here's a list of some classes which you might wish for
- your new stream class to inherit from:</para>
- =
- <simplelist>
- <member>fundamental-stream</member>
- <member>fundamental-input-stream</member>
- <member>fundamental-output-stream</member>
- <member>fundamental-character-stream</member>
- <member>fundamental-binary-stream</member>
- <member>fundamental-character-input-stream</member>
- <member>fundamental-character-output-stream</member>
- <member>fundamental-binary-input-stream</member>
- <member>fundamental-binary-output-stream</member>
- <member>ccl::buffered-stream-mixin</member>
- <member>ccl::buffered-input-stream-mixin</member>
- <member>ccl::buffered-output-stream-mixin</member>
- <member>ccl::buffered-io-stream-mixin</member>
- <member>ccl::buffered-character-input-stream-mixin</member>
- <member>ccl::buffered-character-output-stream-mixin</member>
- <member>ccl::buffered-character-io-stream-mixin</member>
- <member>ccl::buffered-binary-input-stream-mixin</member>
- <member>ccl::buffered-binary-output-stream-mixin</member>
- <member>ccl::buffered-binary-io-stream-mixin</member>
- <member>file-stream</member>
- <member>file-input-stream</member>
- <member>file-output-stream</member>
- <member>file-io-stream</member>
- <member>file-character-input-stream</member>
- <member>file-character-output-stream</member>
- <member>file-charcter-io-stream</member>
- <member>file-binary-input-stream</member>
- <member>file-binary-output-stream</member>
- <member>file-binary-io-stream</member>
- <member>ccl::fd-stream</member>
- <member>ccl::fd-input-stream</member>
- <member>ccl::fd-output-stream</member>
- <member>ccl::fd-io-stream</member>
- <member>ccl::fd-character-input-stream</member>
- <member>ccl::fd-character-output-stream</member>
- <member>ccl::fd-character-io-stream</member>
- <member>ccl::fd-binary-input-stream</member>
- <member>ccl::fd-binary-output-stream</member>
- <member>ccl::fd-binary-io-stream</member>
- </simplelist>
-
- <para>All of these are defined in ccl/level-1/l1-streams.lisp,
- except for the ccl:file-* ones, which are in
- ccl/level-1/l1-sysio.lisp.</para>
- <para>According to the original Gray streams proposal, you
- should inherit from the most specific of the fundamental-*
- classes which applies. Using OpenMCL, though, if you want
- buffering for better performance, which, unless you know of some
- reason you wouldn't, you do, you should instead inherit from the
- appropriate ccl::buffered-* class The buffering you get this way
- is exactly the same as the buffering which is used on ordinary,
- non-Gray streams, and force-output will work properly on
- it.</para>
- <para>Notice that -mixin suffix in the names of all the
- ccl::buffered-* classes? The suffix means that this class is
- not "complete" by itself; you still need to inherit from a
- fundamental-* stream, even if you also inherit from a *-mixin
- stream. You might consider making your own class like this.
- .... Except that they do inherit from the fundamental-*
- streams, that's weird.</para>
- <para>If you want to be able to create an instance of your class
- with the :class argument to (open) and (with-open-file), you
- should make it inherit from one of the file-* classes. If you
- do this, it's not necessary to inherit from any of the other
- classes (though it won't hurt anything), since the file-*
- classes already do.</para>
- <para>When you inherit from the file-* classes, you can use
- (call-next-method) in any of your methods to get the standard
- behaviour. This is especially useful if you want to create a
- class which performs some simple filtering operation, such as
- changing everything to uppercase or to a different character
- encoding. If you do this, you will definitely need to
- specialize ccl::select-stream-class. Your method on
- ccl::stream-select-class should accept an instance of the class,
- but pay no attention to its contents, and return a symbol naming
- the class to actually be instantiated.</para>
- <para>If you need to make your functionality generic across all
- the different types of stream, probably the best way to
- implement it is to make it a mixin, define classes with all the
- variants of input, output, io, character, and binary, which
- inherit both from your mixin and from the appropriate other
- class, then define a method on ccl::select-stream-class which
- chooses from among those classes.</para>
- <para>Note that some of these classes are internal to the CLL
- package. If you try to inherit from those ones without the
- ccl:: prefix, you'll get an error which may confuse you, calling
- them "forward-referenced classes". That just means you used the
- wrong symbol, so add the prefix.</para>
- <para>Here's a list of some generic functions which you might
- wish to specialize for your new stream class, and which ought to
- be documented at some point.</para>
- <simplelist>
- <member>stream-direction stream =3D></member>
- <member>stream-device stream direction =3D></member>
- <member>stream-length stream &optional; new =3D></member>
- <member>stream-position stream &optional; new =3D></member>
- <member>streamp stream =3D> boolean</member>
- <member>stream-write-char output-stream char =3D></member>
- <member>stream-write-entire-string output-stream string =3D></member>
- <member>stream-read-char input-stream =3D></member>
- <member>stream-unread-char input-stream char =3D></member>
- <member>stream-force-output output-stream =3D> nil</member>
- <member>stream-maybe-force-output output-stream =3D> nil</member>
- <member>stream-finish-output output-stream =3D> nil</member>
- <member>stream-clear-output output-stream =3D> nil</member>
- <member>close stream &key; abort =3D> boolean</member>
- <member>stream-fresh-line stream =3D> t</member>
- <member>stream-line-length stream =3D> length</member>
- <member>interactive-stream-p stream =3D> boolean</member>
- <member>stream-clear-input input-stream =3D> nil</member>
- <member>stream-listen input-stream =3D> boolean</member>
- <member>stream-filename stream =3D> string</member>
- <member>ccl::select-stream-class instance in-p out-p char-p =3D>
- class</member>
- </simplelist>
- <para>The following functions are standard parts of Common Lisp, but
-behave in special ways with regard to Gray streams.</para>
- <simplelist>
- <member>open-stream-p stream =3D> generalized-boolean</member>
- <member>input-stream-p stream =3D> generalized-boolean</member>
- <member>output-stream-p stream =3D> generalized-boolean</member>
- <member>stream-element-type stream =3D></member>
- <member>stream-error-stream =3D></member>
- <member>open</member>
- <member>close</member>
- <member>with-open-file</member>
- </simplelist>
-
- <para>Specifically, (open) and (with-open-file) accept a new
- keyword argument, :class, which may be a symbol naming a class;
- the class itself; or an instance of it. The class so given must
- be a subtype of 'stream, and an instance of it with no
- particular contents will be passed to ccl::select-stream-class
- to determine what class to actually instantiate.</para>
- <para>The following are standard, and do not behave specially
- with regard to Gray streams, but probably should.</para>
- <simplelist>
- <member>stream-external-format</member>
- </simplelist>
- </sect1>
-
- <sect1 id=3D"Extending-READ-SEQUENCE-and-WRITE-SEQUENCE">
- <title>Extending READ-SEQUENCE and WRITE-SEQUENCE</title>
-
- <sect2 id=3D"extending-read-write-overview">
- <title>Overview</title>
- <para>The "Gray Streams" API is based on an informal proposal that was
- made before ANSI CL adopted the READ-SEQUENCE and WRITE-SEQUENCE
- functions; as such, there is no "standard" way for the author of a Gray
- stream class to improve the performance of these functions by exploiting
- knowledge of the stream's internals (e.g., the buffering mechanism it
- uses.)</para>
- <para>In the absence of any such knowledge, READ-SEQUENCE and
- WRITE-SEQUENCE are effectively just convenient shorthand for a
- loop which calls READ-CHAR/READ-BYTE/WRITE-CHAR/WRITE-BYTE as
- appropriate. The mechanism described below allows subclasses
- of FUNDAMENTAL-STREAM to define more specialized (and
- presumably more efficient) behavior.</para>
- </sect2>
-
- <sect2 id=3D"Notes">
- <title>Notes</title>
- <para>READ-SEQUENCE and WRITE-SEQUENCE do a certain amount of
- sanity-checking and normalization of their arguments before
- dispatching to one of the methods above. If an individual
- method can't do anything particularly clever, CALL-NEXT-METHOD
- can be used to handle the general case.</para>
- </sect2>
-
- <sect2 id=3D"Example">
- <title>Example</title>
- <programlisting>
-(defclass my-string-input-stream (fundamental-character-input-stream)
- ((string :initarg :string :accessor my-string-input-stream-string)
- (index :initform 0 :accessor my-string-input-stream-index)
- (length)))
-
-(defmethod stream-read-vector ((stream my-string-input-stream) vector star=
t end)
- (if (not (typep vector 'simple-base-string))
- (call-next-method)
- (with-slots (string index length)
- (do* ((outpos start (1+ outpos)))
- ((or (=3D outpos end)
- (=3D index length))
- outpos))
- (setf (schar vector outpos)
- (schar string index))
- (incf index)))))
-</programlisting>
- </sect2>
- </sect1>
-
- <sect1 id=3D"Multibyte-I-O">
- <title>Multibyte I/O</title>
- <para>All heap-allocated objects in OpenMCL that cannot contain
- pointers to lisp objects are represented as
- <emphasis>ivectors</emphasis>. OpenMCL provides low-level
- functions, and , to efficiently transfer data between buffered
- streams and ivectors. There's some overlap in functionality
- between the functions described here and the ANSI CL
- READ-SEQUENCE and WRITE-SEQUENCE functions.</para>
- <para>As used here, the term "octet" means roughly the same
- thing as the term "8-bit byte". The functions described below
- transfer a specified sequence of octets between a buffered
- stream and an ivector, and don't really concern themselves with
- higher-level issues (like whether that octet sequence is within
- bounds or how it relates to the logical contents of the
- ivector.) For these reasons, these functions are generally less
- safe and more flexible than their ANSI counterparts.</para>
- </sect1>
-
- <sect1 id=3D"Gray-Streams-Dictionary">
- <title>Gray Streams Dictionary</title>
- <refentry id=3D"f_stream-read-list">
- <indexterm zone=3D"f_stream-read-list">
- <primary>stream-read-list</primary>
- </indexterm>
-
- <refnamediv>
- <refname>CCL:STREAM-READ-LIST</refname>
- <refpurpose></refpurpose>
- <refclass>Generic Function</refclass>
- </refnamediv>
-
- <refsynopsisdiv>
- <synopsis><function>stream-read-list</function>
- stream list count</synopsis>
- </refsynopsisdiv>
-
- <refsect1>
- <title>Arguments and Values</title>
-
- <variablelist>
- <varlistentry>
- <term>stream</term>
-
- <listitem>
- <para>a stream, presumably a fundamental-input-stream.</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>list</term>
-
- <listitem>
- <para>a list. When a STREAM-READ-LIST method is called by
- READ-SEQUENCE, this argument is guaranteed to be a proper
- list.</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>count</term>
-
- <listitem>
- <para>a non-negative integer. When a STREAM-READ-LIST method
- is called by READ-SEQUENCE, this argument is guaranteed not
- to be greater than the length of the list.</para>
- </listitem>
- </varlistentry>
- </variablelist>
- </refsect1>
-
- <refsect1>
- <title>Description</title>
-
- <para>Should try to read up to count elements from stream into the
- list list, returning the number of elements actually read (which
- may be less than count in case of a premature end-of-file.)</para>
- </refsect1>
- </refentry>
-
- <refentry id=3D"f_stream-write-list">
- <indexterm zone=3D"f_stream-write-list">
- <primary>stream-write-list</primary>
- </indexterm>
-
- <refnamediv>
- <refname>CCL:STREAM-WRITE-LIST</refname>
- <refpurpose></refpurpose>
- <refclass>Generic Function</refclass>
- </refnamediv>
-
- <refsynopsisdiv>
- <synopsis><function>stream-write-list</function>
- stream list count</synopsis>
- </refsynopsisdiv>
-
- <refsect1>
- <title>Arguments and Values</title>
-
- <variablelist>
- <varlistentry>
- <term>stream</term>
-
- <listitem>
- <para>a stream, presumably a fundamental-ouput-stream.</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>list</term>
-
- <listitem>
- <para>a list. When a STREAM-WRITE-LIST method is called by
- WRITE-SEQUENCE, this argument is guaranteed to be a proper
- list.</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>count</term>
-
- <listitem>
- <para>a non-negative integer. When a STREAM-WRITE-LIST
- method is called by WRITE-SEQUENCE, this argument is
- guaranteed not to be greater than the length of the list.</para>
- </listitem>
- </varlistentry>
- </variablelist>
- </refsect1>
-
- <refsect1>
- <title>Description</title>
-
- <para>should try to write the first count elements of list to
- stream. The return value of this method is ignored.</para>
- </refsect1>
- </refentry>
-
- <refentry id=3D"f_stream-read-vector">
- <indexterm zone=3D"f_stream-read-vector">
- <primary>stream-read-vector</primary>
- </indexterm>
-
- <refnamediv>
- <refname>CCL:STREAM-READ-VECTOR</refname>
- <refpurpose></refpurpose>
- <refclass>Generic Function</refclass>
- </refnamediv>
-
- <refsynopsisdiv>
- <synopsis><function>stream-read-vector</function>
- stream vector start end</synopsis>
- </refsynopsisdiv>
-
- <refsect1>
- <title>Arguments and Values</title>
-
- <variablelist>
- <varlistentry>
- <term>stream</term>
-
- <listitem>
- <para>a stream, presumably a fundamental-input-stream</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>vector</term>
-
- <listitem>
- <para>a vector. When a STREAM-READ-VECTOR method is called
- by READ-SEQUENCE, this argument is guaranteed to be a simple
- one-dimensional array.</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>start</term>
-
- <listitem>
- <para>a non-negative integer. When a STREAM-READ-VECTOR
- method is called by READ-SEQUENCE, this argument is
- guaranteed to be no greater than end and not greater than
- the length of vector.</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>end</term>
-
- <listitem>
- <para>a non-negative integer. When a STREAM-READ-VECTOR
- method is called by READ-SEQUENCE, this argument is
- guaranteed to be no less than end and not greater than the
- length of vector.</para>
- </listitem>
- </varlistentry>
- </variablelist>
- </refsect1>
-
- <refsect1>
- <title>Description</title>
-
- <para>should try to read successive elements from stream into
- vector, starting at element start (inclusive) and continuing
- through element end (exclusive.) Should return the index of the
- vector element beyond the last one stored into, which may be less
- than end in case of premature end-of-file.</para>
- </refsect1>
- </refentry>
-
- <refentry id=3D"f_stream-write-vector">
- <indexterm zone=3D"f_stream-write-vector">
- <primary>stream-write-vector</primary>
- </indexterm>
-
- <refnamediv>
- <refname>CCL:STREAM-WRITE-VECTOR</refname>
- <refpurpose></refpurpose>
- <refclass>Generic Function</refclass>
- </refnamediv>
-
- <refsynopsisdiv>
- <synopsis><function>stream-write-vector</function>
- stream vector start end</synopsis>
- </refsynopsisdiv>
-
- <refsect1>
- <title>Arguments and Values</title>
-
- <variablelist>
- <varlistentry>
- <term>stream</term>
-
- <listitem>
- <para>a stream, presumably a fundamental-output-stream</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>vector</term>
-
- <listitem>
- <para>a vector. When a STREAM-WRITE-VECTOR method is called
- by WRITE-SEQUENCE, this argument is guaranteed to be a
- simple one-dimensional array.</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>start</term>
-
- <listitem>
- <para>a non-negative integer. When a STREAM-WRITE-VECTOR
- method is called by WRITE-SEQUENCE, this argument is
- guaranteed to be no greater than end and not greater than
- the length of vector.</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>end</term>
-
- <listitem>
- <para>a non-negative integer. When a STREAM-WRITE-VECTOR
- method is called by WRITE-SEQUENCE, this argument is
- guaranteed to be no less than end and not greater than the
- length of vector.</para>
- </listitem>
- </varlistentry>
- </variablelist>
- </refsect1>
-
- <refsect1>
- <title>Description</title>
-
- <para>should try to write successive elements of vector to stream,
- starting at element start (inclusive) and continuing through
- element end (exclusive.)</para>
- </refsect1>
- </refentry>
-
- <refentry id=3D"f_stream-device">
- <indexterm zone=3D"f_stream-device">
- <primary>stream-device</primary>
- </indexterm>
-
- <refnamediv>
- <refname>CCL::STREAM-DEVICE</refname>
- <refpurpose>Returns the OS file descriptor associated with a
- given lisp stream.</refpurpose>
- <refclass>Generic Function</refclass>
- </refnamediv>
-
- <refsynopsisdiv>
- <synopsis><function>ccl::stream-device</function>
- s direction</synopsis>
- </refsynopsisdiv>
-
- <refsect1>
- <title>Method Signatures</title>
-
- <synopsis><function>ccl::stream-device</function>
- (s stream) direction =3D> fd</synopsis>
- </refsect1>
-
- <refsect1>
- <title>Arguments and Values</title>
- =
- <variablelist>
- <varlistentry>
- <term>s</term>
- <listitem>
- <para>a stream.</para>
- </listitem>
- </varlistentry>
- <varlistentry>
- <term>direction</term>
- <listitem>
- <para>either :INPUT or :OUTPUT.</para>
- </listitem>
- </varlistentry>
- <varlistentry>
- <term>fd</term>
- <listitem>
- <para>a file descriptor, which is a non-negative integer
- used by the OS to refer to an open file, socket, or similar
- I/O connection. NIL if there is no file descriptor associated
- with <varname>s</varname> in the direction given by
- <varname>direction</varname>.</para>
- </listitem>
- </varlistentry>
- </variablelist>
- </refsect1>
-
- <refsect1>
- <title>Description</title>
-
- <para>Returns the file descriptor associated with
- <varname>s</varname> in the direction given by
- <varname>direction</varname>. It is necessary to specify
- <varname>direction</varname> because the input and output
- file descriptors may be different; the most common case is when
- one of them has been redirected by the Unix shell.</para>
- </refsect1>
- </refentry>
-
- <refentry id=3D"f_stream-read-ivector">
- <indexterm zone=3D"f_stream-read-ivector">
- <primary>stream-read-ivector</primary>
- </indexterm>
-
- <refnamediv>
- <refname>STREAM-READ-IVECTOR</refname>
- <refpurpose></refpurpose>
- <refclass>Generic Function</refclass>
- </refnamediv>
-
- <refsynopsisdiv>
- <synopsis><function>stream-read-ivector</function>
- stream ivector start-octet max-octets</synopsis>
- </refsynopsisdiv>
-
- <refsect1>
- <title>Description</title>
-
- <para>Reads up to max-octets octets from stream into ivector,
- storing them at start-octet. Returns the number of octets actually
- read.</para>
- </refsect1>
-
- <refsect1>
- <title>Arguments</title>
-
- <variablelist>
- <varlistentry>
- <term>stream</term>
-
- <listitem>
- <para>An input stream. The method defined on
- BUFFERED-INPUT-STREAMs requires that the size in octets of
- an instance of the stream's element type is 1.</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>ivector</term>
-
- <listitem>
- <para>Any ivector.</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>start-octet</term>
-
- <listitem>
- <para>A non-negative integer.</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>max-octets</term>
-
- <listitem>
- <para>A non-negative integer. The return value may be less
- than the value of this parameter if EOF was encountered.</para>
- </listitem>
- </varlistentry>
- </variablelist>
- </refsect1>
- </refentry>
-
- <refentry id=3D"f_stream-write-ivector">
- <indexterm zone=3D"f_stream-write-ivector">
- <primary>stream-write-ivector</primary>
- </indexterm>
-
- <refnamediv>
- <refname>STREAM-WRITE-IVECTOR</refname>
- <refpurpose></refpurpose>
- <refclass>Generic Function</refclass>
- </refnamediv>
-
- <refsynopsisdiv>
- <synopsis><function>stream-write-ivector stream</function>
- ivector start-octet max-octets</synopsis>
- </refsynopsisdiv>
-
- <refsect1>
- <title>Description</title>
-
- <para>Writes max-octets octets to stream from ivector, starting at
- start-octet. Returns max-octets.</para>
- </refsect1>
-
- <refsect1>
- <title>Arguments</title>
-
- <variablelist>
- <varlistentry>
- <term>stream</term>
-
- <listitem>
- <para>An input stream. The method defined on
- BUFFERED-OUTPUT-STREAMs requires that the size in octets of
- an instance of the stream's element type is 1.</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>ivector</term>
-
- <listitem>
- <para>Any ivector</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>start-octet</term>
-
- <listitem>
- <para>A non-negative integer.</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>max-octet</term>
-
- <listitem>
- <para>A non-negative integer.</para>
- </listitem>
- </varlistentry>
- </variablelist>
- </refsect1>
-
- <refsect1>
- <title>Examples</title>
-
-<programlisting format=3D"linespecific">;;; Write the contents of a (SIMPL=
E-ARRAY(UNSIGNED-BYTE 16) 3) =
-;;; to a character file stream. Read back the characters.
-(let* ((a (make-array 3 =
- :element-type '(unsigned-byte 16)
- :initial-contents '(26725 27756 28449))))
- (with-open-file (s "junk"
- :element-type 'character
- :direction :io
- :if-does-not-exist :create
- :if-exists :supersede)
- ;; Write six octets (three elements).
- (stream-write-ivector s a 0 6)
- ;; Rewind, then read a line
- (file-position s 0)
- (read-line s)))
-
-;;; Write a vector of DOUBLE-FLOATs. Note that (to maintain
-;;; alignment) there are 4 octets of padding before the 0th =
-;;; element of a (VECTOR DOUBLE-FLOAT).
-;;; (Note that (=3D (- arch::misc-dfloat-offset =
-;;; arch::misc-data-offset) 4))
-(defun write-double-float-vector
- (stream vector &key (start 0) (end (length vector)))
- (check-type vector (vector double-float))
- (let* ((start-octet (+ (* start 8) =
- (- arch::misc-dfloat-offset
- arch::misc-data-offset)))
- (num-octets (* 8 (- end start))))
- (stream-write-ivector stream vector start-octet num-octets)))</program=
listing>
- </refsect1>
- </refentry>
- </sect1>
- </chapter>
-
- <chapter id=3D"Writing-Portable-Extensions-to-the-Object-System-using-th=
e-MetaObject-Protocol">
- <title>Writing Portable Extensions to the Object System using the Met=
aObject Protocol</title>
-
- <sect1 id=3D"MOP-Overview">
- <title>Overview</title>
- <para>OpenMCL supports a fairly large subset of the
- semi-standard MetaObject Protocol (MOP) for CLOS, as defined in
- chapters 5 and 6 of "The Art Of The Metaobject Protocol",
- (Kiczales et al, MIT Press 1991, ISBN 0-262-61074-4); this
- specification is also available online at
- http://www.alu.org/mop/index.html.</para>
- </sect1>
-
- <sect1 id=3D"MOP-Implementation-status">
- <title>Implementation status</title>
- <para>The keyword :openmcl-partial-mop is on *FEATURES* to
- indicate the presence of this functionality.</para>
-
- <para>All of the symbols defined in the MOP specification
- (whether implemented or not) are exported from the "CCL" package
- and from an "OPENMCL-MOP" package.</para>
- <para><informaltable><tgroup cols=3D"2" colsep=3D"1"
- rowsep=3D"1"><colspec align=3D"center" colname=3D"col0" /><colspec
- align=3D"center" colname=3D"col1" /><thead><row><entry
- align=3D"center" valign=3D"top"><para>construct</para></entry><entry
- align=3D"center"
- valign=3D"top"><para>status</para></entry></row></thead><tbody><row>=
<entry
- align=3D"center"
- valign=3D"top"><para>accessor-method-slot-definition</para></entry><=
entry
- align=3D"center"
- valign=3D"top"><para>+</para></entry></row><row><entry
- align=3D"center"
- valign=3D"top"><para>add-dependent</para></entry><entry
- align=3D"center"
- valign=3D"top"><para>+</para></entry></row><row><entry
- align=3D"center"
- valign=3D"top"><para>add-direct-method</para></entry><entry
- align=3D"center"
- valign=3D"top"><para>+</para></entry></row><row><entry
- align=3D"center"
- valign=3D"top"><para>add-direct-subclass</para></entry><entry
- align=3D"center"
- valign=3D"top"><para>+</para></entry></row><row><entry
- align=3D"center"
- valign=3D"top"><para>add-method</para></entry><entry
- align=3D"center"
- valign=3D"top"><para>+</para></entry></row><row><entry
- align=3D"center"
- valign=3D"top"><para>class-default-initargs</para></entry><entry
- align=3D"center"
- valign=3D"top"><para>+</para></entry></row><row><entry
- align=3D"center"
- valign=3D"top"><para>class-direct-default-initargs</para></entry><en=
try
- align=3D"center"
- valign=3D"top"><para>+</para></entry></row><row><entry
- align=3D"center"
- valign=3D"top"><para>class-direct-slots</para></entry><entry
- align=3D"center"
- valign=3D"top"><para>+</para></entry></row><row><entry
- align=3D"center"
- valign=3D"top"><para>class-direct-subclasses</para></entry><entry
- align=3D"center"
- valign=3D"top"><para>+</para></entry></row><row><entry
- align=3D"center"
- valign=3D"top"><para>class-direct-superclasses</para></entry><entry
- align=3D"center"
- valign=3D"top"><para>+</para></entry></row><row><entry
- align=3D"center"
- valign=3D"top"><para>class-finalized-p</para></entry><entry
- align=3D"center"
- valign=3D"top"><para>+</para></entry></row><row><entry
- align=3D"center"
- valign=3D"top"><para>class-prototype</para></entry><entry
- align=3D"center"
- valign=3D"top"><para>+</para></entry></row><row><entry
- align=3D"center"
- valign=3D"top"><para>class-slots</para></entry><entry
- align=3D"center"
- valign=3D"top"><para>+</para></entry></row><row><entry
- align=3D"center"
- valign=3D"top"><para>compute-applicable-methods</para></entry><entry
- align=3D"center"
- valign=3D"top"><para>-</para></entry></row><row><entry
- align=3D"center"
- valign=3D"top"><para>compute-applicable-methods-using-classes</para>=
</entry><entry
- align=3D"center"
- valign=3D"top"><para>-</para></entry></row><row><entry
- align=3D"center"
- valign=3D"top"><para>compute-class-precedence-list</para></entry><en=
try
- align=3D"center"
- valign=3D"top"><para>+</para></entry></row><row><entry
- align=3D"center"
- valign=3D"top"><para>compute-direct-initargs</para></entry><entry
- align=3D"center"
- valign=3D"top"><para>+</para></entry></row><row><entry
- align=3D"center"
- valign=3D"top"><para>compute-discriminating-function</para></entry><=
entry
- align=3D"center"
- valign=3D"top"><para>-</para></entry></row><row><entry
- align=3D"center"
- valign=3D"top"><para>compute-effective-method</para></entry><entry
- align=3D"center"
- valign=3D"top"><para>+</para></entry></row><row><entry
- align=3D"center"
- valign=3D"top"><para>compute-effective-slot-definition</para></entry=
><entry
- align=3D"center"
- valign=3D"top"><para>+</para></entry></row><row><entry
- align=3D"center"
- valign=3D"top"><para>compute-slots</para></entry><entry
- align=3D"center"
- valign=3D"top"><para>+</para></entry></row><row><entry
- align=3D"center"
- valign=3D"top"><para>direct-slot-definition-class</para></entry><ent=
ry
- align=3D"center"
- valign=3D"top"><para>+</para></entry></row><row><entry
- align=3D"center"
- valign=3D"top"><para>effective-slot-definition-class</para></entry><=
entry
- align=3D"center"
- valign=3D"top"><para>+</para></entry></row><row><entry
- align=3D"center"
- valign=3D"top"><para>ensure-class</para></entry><entry
- align=3D"center"
- valign=3D"top"><para>+</para></entry></row><row><entry
- align=3D"center"
- valign=3D"top"><para>ensure-class-using-class</para></entry><entry
- align=3D"center"
- valign=3D"top"><para>+</para></entry></row><row><entry
- align=3D"center"
- valign=3D"top"><para>ensure-generic-function-using-class</para></ent=
ry><entry
- align=3D"center"
- valign=3D"top"><para>+</para></entry></row><row><entry
- align=3D"center"
- valign=3D"top"><para>eql-specializer-object</para></entry><entry
- align=3D"center"
- valign=3D"top"><para>+</para></entry></row><row><entry
- align=3D"center"
- valign=3D"top"><para>extract-lambda-list</para></entry><entry
- align=3D"center"
- valign=3D"top"><para>+</para></entry></row><row><entry
- align=3D"center"
- valign=3D"top"><para>extract-specializer-names</para></entry><entry
- align=3D"center"
- valign=3D"top"><para>+</para></entry></row><row><entry
- align=3D"center"
- valign=3D"top"><para>finalize-inheritance</para></entry><entry
- align=3D"center"
- valign=3D"top"><para>+</para></entry></row><row><entry
- align=3D"center"
- valign=3D"top"><para>find-method-combination</para></entry><entry
- align=3D"center"
- valign=3D"top"><para>+</para></entry></row><row><entry
- align=3D"center"
- valign=3D"top"><para>funcallable-standard-instance-access</para></en=
try><entry
- align=3D"center"
- valign=3D"top"><para>+</para></entry></row><row><entry
- align=3D"center"
- valign=3D"top"><para>generic-function-argument-precedence-order</par=
a></entry><entry
- align=3D"center"
- valign=3D"top"><para>+</para></entry></row><row><entry
- align=3D"center"
- valign=3D"top"><para>generic-function-declarations</para></entry><en=
try
- align=3D"center"
- valign=3D"top"><para>+</para></entry></row><row><entry
- align=3D"center"
- valign=3D"top"><para>generic-function-lambda-list</para></entry><ent=
ry
- align=3D"center"
- valign=3D"top"><para>+</para></entry></row><row><entry
- align=3D"center"
- valign=3D"top"><para>generic-function-method-class</para></entry><en=
try
- align=3D"center"
- valign=3D"top"><para>+</para></entry></row><row><entry
- align=3D"center"
- valign=3D"top"><para>generic-function-method-combination</para></ent=
ry><entry
- align=3D"center"
- valign=3D"top"><para>+</para></entry></row><row><entry
- align=3D"center"
- valign=3D"top"><para>generic-function-methods</para></entry><entry
- align=3D"center"
- valign=3D"top"><para>+</para></entry></row><row><entry
- align=3D"center"
- valign=3D"top"><para>generic-function-name</para></entry><entry
- align=3D"center"
- valign=3D"top"><para>+</para></entry></row><row><entry
- align=3D"center"
- valign=3D"top"><para>intern-eql-specializer</para></entry><entry
- align=3D"center"
- valign=3D"top"><para>+</para></entry></row><row><entry
- align=3D"center"
- valign=3D"top"><para>make-method-lambda</para></entry><entry
- align=3D"center"
- valign=3D"top"><para>-</para></entry></row><row><entry
- align=3D"center"
- valign=3D"top"><para>map-dependents</para></entry><entry
- align=3D"center"
- valign=3D"top"><para>+</para></entry></row><row><entry
- align=3D"center"
- valign=3D"top"><para>method-function</para></entry><entry
- align=3D"center"
- valign=3D"top"><para>+</para></entry></row><row><entry
- align=3D"center"
- valign=3D"top"><para>method-generic-function</para></entry><entry
- align=3D"center"
- valign=3D"top"><para>+</para></entry></row><row><entry
- align=3D"center"
- valign=3D"top"><para>method-lambda-list</para></entry><entry
- align=3D"center"
- valign=3D"top"><para>+</para></entry></row><row><entry
- align=3D"center"
- valign=3D"top"><para>method-qualifiers</para></entry><entry
- align=3D"center"
- valign=3D"top"><para>+</para></entry></row><row><entry
- align=3D"center"
- valign=3D"top"><para>method-specializers</para></entry><entry
- align=3D"center"
- valign=3D"top"><para>+</para></entry></row><row><entry
- align=3D"center"
- valign=3D"top"><para>reader-method-class</para></entry><entry
- align=3D"center"
- valign=3D"top"><para>+</para></entry></row><row><entry
- align=3D"center"
- valign=3D"top"><para>remove-dependent</para></entry><entry
- align=3D"center"
- valign=3D"top"><para>+</para></entry></row><row><entry
- align=3D"center"
- valign=3D"top"><para>remove-direct-method</para></entry><entry
- align=3D"center"
- valign=3D"top"><para>+</para></entry></row><row><entry
- align=3D"center"
- valign=3D"top"><para>remove-direct-subclass</para></entry><entry
- align=3D"center"
- valign=3D"top"><para>+</para></entry></row><row><entry
- align=3D"center"
- valign=3D"top"><para>remove-method</para></entry><entry
- align=3D"center"
- valign=3D"top"><para>+</para></entry></row><row><entry
- align=3D"center"
- valign=3D"top"><para>set-funcallable-instance-function</para></entry=
><entry
- align=3D"center"
- valign=3D"top"><para>-</para></entry></row><row><entry
- align=3D"center"
- valign=3D"top"><para>slot-boundp-using-class</para></entry><entry
- align=3D"center"
- valign=3D"top"><para>+</para></entry></row><row><entry
- align=3D"center"
- valign=3D"top"><para>slot-definition-allocation</para></entry><entry
- align=3D"center"
- valign=3D"top"><para>+</para></entry></row><row><entry
- align=3D"center"
- valign=3D"top"><para>slot-definition-initargs</para></entry><entry
- align=3D"center"
- valign=3D"top"><para>+</para></entry></row><row><entry
- align=3D"center"
- valign=3D"top"><para>slot-definition-initform</para></entry><entry
- align=3D"center"
- valign=3D"top"><para>+</para></entry></row><row><entry
- align=3D"center"
- valign=3D"top"><para>slot-definition-initfunction</para></entry><ent=
ry
- align=3D"center"
- valign=3D"top"><para>+</para></entry></row><row><entry
- align=3D"center"
- valign=3D"top"><para>slot-definition-location</para></entry><entry
- align=3D"center"
- valign=3D"top"><para>+</para></entry></row><row><entry
- align=3D"center"
- valign=3D"top"><para>slot-definition-name</para></entry><entry
- align=3D"center"
- valign=3D"top"><para>+</para></entry></row><row><entry
- align=3D"center"
- valign=3D"top"><para>slot-definition-readers</para></entry><entry
- align=3D"center"
- valign=3D"top"><para>+</para></entry></row><row><entry
- align=3D"center"
- valign=3D"top"><para>slot-definition-type</para></entry><entry
- align=3D"center"
- valign=3D"top"><para>+</para></entry></row><row><entry
- align=3D"center"
- valign=3D"top"><para>slot-definition-writers</para></entry><entry
- align=3D"center"
- valign=3D"top"><para>+</para></entry></row><row><entry
- align=3D"center"
- valign=3D"top"><para>slot-makunbound-using-class</para></entry><entry
- align=3D"center"
- valign=3D"top"><para>+</para></entry></row><row><entry
- align=3D"center"
- valign=3D"top"><para>slot-value-using-class</para></entry><entry
- align=3D"center"
- valign=3D"top"><para>+</para></entry></row><row><entry
- align=3D"center"
- valign=3D"top"><para>specializer-direct-generic-functions</para></en=
try><entry
- align=3D"center"
- valign=3D"top"><para>+</para></entry></row><row><entry
- align=3D"center"
- valign=3D"top"><para>specializer-direct-methods</para></entry><entry
- align=3D"center"
- valign=3D"top"><para>+</para></entry></row><row><entry
- align=3D"center"
- valign=3D"top"><para>standard-instance-access</para></entry><entry
- align=3D"center"
- valign=3D"top"><para>+</para></entry></row><row><entry
- align=3D"center"
- valign=3D"top"><para>update-dependent</para></entry><entry
- align=3D"center"
- valign=3D"top"><para>+</para></entry></row><row><entry
- align=3D"center"
- valign=3D"top"><para>validate-superclass</para></entry><entry
- align=3D"center"
- valign=3D"top"><para>+</para></entry></row><row><entry
- align=3D"center"
- valign=3D"top"><para>writer-method-class</para></entry><entry
- align=3D"center"
- valign=3D"top"><para>+</para></entry></row></tbody></tgroup></inform=
altable></para>
- =
- <para>Note that those generic functions whose status is "-" in
- the table above deal with the internals of generic function
- dispatch and method invocation (the "Generic Function Invocation
- Protocol".) Method functions are implemented a bit differently
- in OpenMCL from what the MOP expects, and it's not yet clear if
- or how this subprotocol can be well-supported.</para>
- <para>Those constructs that are marked as "+" in the table above
- are nominally implemented as the MOP document specifies
- (deviations from the specification should be considered bugs;
- please report them as such.) Note that some CLOS implementations
- in widespread use (e.g., PCL) implement some things
- (ENSURE-CLASS-USING-CLASS comes to mind) a bit differently from
- what the MOP specifies.</para>
- </sect1>
-
- <sect1 id=3D"Concurrency-issues">
- <title>Concurrency issues</title>
- <para>The entire CLOS class and generic function hierarchy is
- effectively a (large, complicated) shared data structure; it's
- not generally practical for a thread to request exclusive access
- to all of CLOS, and the effects of volitional modification of
- the CLOS hierarchy (via clas redefinition, CHANGE-CLASS, etc) in
- a multithreaded environment aren't always tractable.</para>
- <para>Native threads exacerbate this problem (in that they
- increase the opportunities for concurrent modification and
- access.) The implementation should try to ensure that a thread's
- view of any subset of the CLOS hierarchy is consistent (to the
- extent that that's possible) and should try to ensure that
- incidental modifications of the hierarchy (cache updates, etc.)
- happen atomically; it's not generally possible for the
- implementation to guarantee that a thread's view of things is
- correct and current.</para>
- <para>If you are loading code and defining classes in the most
- usual way, which is to say, via the compiler, using only a
- single thread, these issues are probably not going to affect you
- much.</para>
- <para>If, however, you are making finicky changes to the class
- hierarchy while you're running multiple threads which manipulate
- objects related to each other, more care is required. Before
- doing such a thing, you should know what you're doing and
- already be aware of what precautions to take, without being
- told. That said, if you do it, you should seriously consider
- what your application's critical data is, and use locks for
- critical code sections.</para>
- </sect1>
- </chapter>
-
- <chapter id=3D"The-Foreign-Function-Interface">
- <title>The Foreign-Function Interface</title>
-
- <sect1 id=3D"Specifying-And-Using-Foreign-Types">
- <title>Specifying And Using Foreign Types</title>
-
- <sect2 id=3D"Overview-foreign-types">
- <title>Overview</title>
- <para>OpenMCL provides a fairly rich language for defining and
- specifying foreign data types (this language is derived from
- CMUCL's "alien type" system.)</para>
- <para>In practice, most foreign type definitions are
- introduced into OpenMCL via its interface database (see ),
- though it's also possible to define foreign types
- interactively and/or programmatically.</para>
- <para>OpenMCL's foreign type system is "evolving" (a polite
- word for not-quite-complete): there are some inconsistencies
- involving package usage, for instance. Symbols used in foreign
- type specifiers <emphasis>should</emphasis> be keywords, but
- this convention isn't always enforced.</para>
- <para>Foreign type, record, and field names are
- case-sensitive; OpenMCL uses some escaping conventions (see )
- to allow keywords to be used to denote these names.</para>
- </sect2>
-
- <sect2 id=3D"Syntax-of-Foreign-Type-Specifiers">
- <title>Syntax of Foreign Type Specifiers</title>
- <itemizedlist>
- <listitem>
- <para>Some foreign types are builtin: keywords denote
- primitive,builtin types such as the IEEE-double-float type
- (denoted:DOUBLE-FLOAT), in much the same way as certain
- symbols(CONS, FIXNUM,etc.) define primitive CL
- types.</para>
- </listitem>
- <listitem>
- <para>Constructors such as :SIGNED and :UNSIGNED can be
- used to denotesigned and unsigned integer subtypes
- (analogous to the CL typespecifiers SIGNED-BYTE and
- UNSIGNED-BYTE.) :SIGNED is shorthand for(:SIGNED 32) and
- :UNSIGNED is shorthand for (:UNSIGNED 32).</para>
- </listitem>
- <listitem>
- <para>Aliases for other (perhaps more complicated) types
- can bedefined via CCL:DEF-FOREIGN-TYPE (sort of like
- CL:DEFTYPE or the Ctypedef facility). The type :CHAR is
- defined as an alias for (:SIGNED8) on some platforms, as
- (:UNSIGNED 8) on others.</para>
- </listitem>
- <listitem>
- <para>The construct (:STRUCT <emphasis>name</emphasis>)
- can be used torefer to a named structure type; (:UNION
- <emphasis>name</emphasis>)can be used to refer to a named
- union type. It isn't necessary toenumerate a structure or
- union type's fields in order to refer tothe type.</para>
- </listitem>
- <listitem>
- <para>If <emphasis>X</emphasis> is a valid foreign type
- reference,then (:* <emphasis>X</emphasis>) denotes the
- foreign type "pointerto<emphasis> X</emphasis>". By
- convention, (:* T) denotes ananonymous pointer type,
- vaguely equivalent to "void*" in C.</para>
- </listitem>
- <listitem>
- <para>If a fieldlist is a list of lists, each of whose CAR
- is a foreign field name (keyword) and whose CADR is a
- foreign type specifier, then (:STRUCT
- <emphasis>name</emphasis> , at fieldlist) is adefinition of
- the structure type <emphasis>name</emphasis>,
- and (:UNION<emphasis> name</emphasis> , at fieldlist) is a
- definition of theunion type
- <emphasis>name</emphasis>. Note that it's necessary
- todefine a structure or union type in order to include
- that type in a structure, union, or array, but only
- necessary to "refer to" a strucure or union type in order
- to define a type alias or a pointer type.</para>
- </listitem>
- <listitem>
- <para>If <emphasis>X</emphasis> is a defined foreign type
- , then (:array <emphasis>X</emphasis> &rest dims)
- denotes the foreigntype "array of
- <emphasis>X</emphasis>". Although multiplearray dimensions
- are allowed by the :array constructor,
- only single-dimensioned arrays are (at all) well-supported
- in OpenMCL.</para>
- </listitem>
- </itemizedlist>
- </sect2>
- </sect1>
-
- <sect1 id=3D"Foreign-Function-Calls">
- <title>Foreign Function Calls</title>
-
- <sect2 id=3D"Overview-foreign-calls">
- <title>Overview</title>
- <para>OpenMCL provides a number of constructs for calling
- foreign functions from Lisp code (all of them based on the
- function CCL:%FF-CALL). In many cases, OpenMCL's interface
- translator (see ) provides information about the foreign
- function's entrypoint name and argument and return types; this
- enables the use of the #_ reader macro (described below),
- which may be more concise and/or more readable than other
- constructs.</para>
- <para>OpenMCL also provides a mechanism for defining
- <emphasis>callbacks</emphasis>: lisp functions which can be
- called from foreign code.</para>
- <para>There's no supported way to directly pass lisp data to
- foreign functions: scalar lisp data must be coerced to an
- equivalent foreign representatation, and lisp arrays (notably
- strings) must be copied to non-GCed memory.</para>
-
- <sect3 id=3D"Type-Designators-for-Arguments-and-Return-Values">
- <title>Type Designators for Arguments and Return Values</title>
- <para>The types of foreign argument and return values in foreign
- function calls and callbacks can be specified by any of the following
-keywords:</para>
- <variablelist>
- <varlistentry>
- <term>:UNSIGNED-BYTE</term>
-
- <listitem>
- <para>The argument/return value is of type (UNSIGNED-BYTE 8)</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>:SIGNED-BYTE</term>
-
- <listitem>
- <para>The argument/return value is of type (SIGNED-BYTE 8)</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>:UNSIGNED-HALFWORD</term>
-
- <listitem>
- <para>The argument/return value is of type (UNSIGNED-BYTE 16)</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>:SIGNED-HALFWORD</term>
-
- <listitem>
- <para>The argument/return value is of type (SIGNED-BYTE 16)</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>:UNSIGNED-FULLWORD</term>
-
- <listitem>
- <para>The argument/return value is of type (UNSIGNED-BYTE 32)</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>:SIGNED-FULLWORD</term>
-
- <listitem>
- <para>The argument/return value is of type (SIGNED-BYTE 32)</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>:UNSIGNED-DOUBLEWORD</term>
-
- <listitem>
- <para>The argument/return value is of type (UNSIGNED-BYTE 64)</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>:SIGNED-DOUBLEWORD</term>
-
- <listitem>
- <para>The argument/return value is of type (SIGNED-BYTE 64)</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>:SINGLE-FLOAT</term>
-
- <listitem>
- <para>The argument/return value is of type SINGLE-FLOAT</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>:DOUBLE-FLOAT</term>
-
- <listitem>
- <para>The argument/return value is of type DOUBLE-FLOAT</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>:ADDRESS</term>
-
- <listitem>
- <para>The argument/return values
- is <link linkend=3D"Referencing-and-Using-Foreign-Memory-Addresses">a MA=
CPTR</link>.</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>:VOID</term>
-
- <listitem>
- <para>or NIL Not valid as an argument type specifier; specifies
- that there is no meaningful return value</para>
- </listitem>
- </varlistentry>
- </variablelist>
-
- <para>On some platforms, a small positive integer
- <emphasis>N</emphasis> can also be used as an argument
- specifier; it indicates that the corresponding argument is a
- pointer to an <emphasis>N</emphasis>-word structure or union
- which should be passed by value to the foreign
- function. Exactly which foreign structures are passed
- by value and how is very dependent on the Application
- Binary Interface (ABI) of the platform; unless you're
- very familar with ABI detatils (some of which are quite
- baroque), it's often easier to let higher-level constructs
- deal with these details.</para>
- </sect3>
-
- <sect3 id=3D"External-Entrypoints-and-Named-External-Entrypoints">
- <title>External Entrypoints and Named External Entrypoints</title>
- <para>PowerPC machine instructions are always aligned on
- 32-bit boundaries, so the two least significant bits of the
- first instruction ("entrypoint") of a foreign function are
- always 0. OpenMCL often represents an entrypoint address as
- a fixnum that's binary-equivalent to the entrypoint address:
- if<emphasis> E</emphasis> is an entrypoint address expressed
- as a signed 32-bit integer, then (ash <emphasis>E</emphasis>
- -2) is an equivalent fixnum representation of that
- address. An entrypoint address can also be encapsulated in a
- MACPTR (see FIXTHIS), but that's somewhat less efficient.</para>
- <para>Although it's possible to use fixnums or macptrs to
- represent entrypoint addresses, it's somewhat cumbersome to
- do so. OpenMCL can cache the addresses of named external
- functions in structure-like objects of type
- CCL:EXTERNAL-ENTRY-POINT (sometimes abbreviated as EEP).
- Through the use of LOAD-TIME-VALUE, compiled lisp functions
- are able to reference EEPs as constants; the use of an
- indirection allows OpenMCL runtime system to ensure that the
- EEP's address is current and correct.</para>
- </sect3>
- </sect2>
-
- <sect2 id=3D"Return-Conventions-for-C-Structures">
- <title>Return Conventions for C Structures</title>
- <para> On some platforms, C functions that are defined to
- return structures do so by reference: they actually
- accept a first parameter of type "pointer to returned
- struct/union" - which must be allocated by the caller - and
- don't return a meaningful value.</para>
- <para><emphasis>Exactly</emphasis> how a C function that's
- defined to return a foreign structure does so is dependent on
- the ABI (and on the size ad composition of the structure/union
- in many cases.)</para>
- </sect2>
- </sect1>
-
- <sect1 id=3D"Referencing-and-Using-Foreign-Memory-Addresses">
- <title>Referencing and Using Foreign Memory Addresses</title>
-
- <sect2 id=3D"Overview-memory-addresses">
- <title>Overview</title>
-
- <sect3 id=3D"Basics">
- <title>Basics</title>
- <para>For a variety of technical reasons, it isn't generally
- possible to directly reference arbitrary absolute addresses
- (such as those returned by the C library function malloc(),
- for instance) in OpenMCL. In OpenMCL (and in MCL), such
- addresses need to be <emphasis>encapsulated</emphasis> in
- objects of type CCL:MACPTR; one can think of a MACPTR as
- being a specialized type of structure whose sole purpose is
- to provide a way of referring to an underlying "raw"
- address.</para>
- <para>It's sometimes convenient to blur the distinction
- between a MACPTR and the address it represents; it's
- sometimes necessary to maintain that distiction. It's
- important to remember that a MACPTR is (generally) a
- first-class Lisp object in the same sense that a CONS cell
- is: it'll get GCed when it's no longer possible to reference
- it. The "lifetime" of a MACPTR doesn't generally have
- anything to do with the lifetime of the block of memory its
- address points to.</para>
- <para>It might be tempting to ask "How does one obtain the
- address encapsulated by a MACPTR ?". The answer to that
- question is that one doesn't do that (and there's no way to
- do that): addresses aren't first-class objects, and there's
- no way to refer to one.</para>
- <para>Two MACPTRs that encapsulate the same address are EQL
- to each other.</para>
- <para>There are a small number of ways to directly create a
- MACPTR (and there's a fair amount of syntactic sugar built
- on top of of those primitives.) These primitives will be
- discussed in greater detail below, but they include:</para>
-
- <itemizedlist>
- <listitem>
- <para>Creating a MACPTR with a specified address, usually
- via thefunction CCL:%INT-TO-PTR.</para>
- </listitem>
- <listitem>
- <para>Referencing the return valueof a foreign function
- call (see )that's specified to return an address.</para>
- </listitem>
- <listitem>
- <para>Referencing a memory location that's specified to
- contain an address.</para>
- </listitem>
- </itemizedlist>
-
- <para>All of these primitive MACPTR-creating operations are
- usually open-coded by the compiler; it has a fairly good
- notion of what low-level operations "produce" MACPTRs and
- which operations "consume" the addresses that the
- encapsulate, and will usually optimize out the introduction
- of intermediate MACPTRs in a simple expression.</para>
- <para>One consequence of the use of MACPTR objects to
- encapsulate foreign addresses is that (naively)
- <emphasis>every reference to a foreign address causes a
- MACPTR to be allocated.</emphasis></para>
- <para>Consider a code fragment like the following:</para>
- <programlisting>
-(defun get-next-event ()
- "get the next event from a hypothetical window system"
- (loop
- (let* ((event (#_get_next_window_system_event))) ; via an FF-CALL
- (unless (null-event-p event)
- (handle-event event)))))
-</programlisting>
- <para>As this is written, each call to the (hypothetical)
- foreign function #_get_next_window_system_event will return
- a new MACPTR object. Ignoring for the sake of argument the
- question of whether this code fragment exhibits a good way
- to poll for external events (it doesn't), it's not hard to
- imagine that this loop could execute several millon times
- per second (producing several million MACPTRs per second.)
- Clearly, the "naive" approach is impractical in many
- cases.</para>
- </sect3>
-
- <sect3 id=3D"Stack-allocation-of---and-destructive-operations-on--=
-MACPTRs-">
- <title>Stack allocation of - and destructive operations on - MAC=
PTRs.</title>
- <para>If certain conditions held in the environment in which
- GET-NEXT-EVENT ran - namely, if it was guaranteed that
- neither NULL-EVENT-P nor HANDLE-EVENT cached or otherwise
- retained their arguments (the "event" pointer) - there'd be
- a few alternatives to the naive approach. One of those
- approaches would be to use the primitive function
- %SETF-MACPTR (described in greater detail below) to
- destructively modify a MACPTR (to change the value of the
- address it encapsulates.) The GET-NEXT-EVENT example could
- be re-written as:</para>
- <programlisting>
-(defun get-next-event ()
- (let* ((event (%int-to-ptr 0))) ; create a MACPTR with address 0
- (loop
- (%setf-macptr event (#_get_next_window_system_event)) ; re-use it
- (unless (null-event-p event)
- (handle-event event)))))
-</programlisting>
- <para>That version's a bit more realistic: it allocates a
- single MACPTR outside if the loop, then changes its address
- to point to the current address of the hypothetical event
- structure on each loop iteration. If there are a million
- loop iterations per call to GET-NEXT-EVENT, we're allocating
- a million times fewer MACPTRs per call; that sounds like a
- Good Thing.</para>
- <para>An Even Better Thing would be to advise the compiler
- that the initial value (the null MACPTR) bound to the
- variable event has dynamic extent (that value won't be
- referenced once control leaves the extent of the binding of
- that variable.) Common Lisp allows us to make such an
- assertion via a DYNAMIC-EXTENT declaration; OpenMCL's
- compiler can recognize the "primitive MACPTR-creating
- operation" involved and can replace it with an equivalent
- operation that stack-allocates the MACPTR object. If we're
- not worried about the cost of allocating that MACPTR on
- every iteration (the cost is small and there's no hidden GC
- cost), we could move the binding back inside the
- loop:</para>
- <programlisting>
-(defun get-next-event ()
- (loop
- (let* ((event (%null-ptr))) ; (%NULL-PTR) is shorthand for (%INT-TO-PT=
R 0)
- (declare (dynamic-extent event))
- (%setf-macptr event (#_get_next_window_system_event))
- (unless (null-event-p event)
- (handle-event event)))))
-</programlisting>
- <para>The idiom of binding one or more variables to
- stack-allocated MACPTRs, then destructively modifying those
- MACPTRs before executing a body of code is common enough
- that OpenMCL provides a macro (WITH-MACPTRS) that handles
- all of the gory details. The following version of
- GET-NEXT-EVENT is semantically equivalent to the previous
- version, but hopefully a bit more concise:</para>
- <programlisting>
-(defun get-next-event ()
- (loop
- (with-macptrs ((event (#_get_next_window_system_event)))
- (unless (null-event-p event)
- (handle-event event)))))
-</programlisting>
- </sect3>
-
- <sect3 id=3D"Stack-allocated-memory--and-stack-allocated-pointers-=
to-it--">
- <title>Stack-allocated memory (and stack-allocated pointers to i=
t.)</title>
- <para>Fairly often, the blocks of foreign memory (obtained
- by malloc or something similar) have well-defined lifetimes
- (they can safely be freed at some point when it's known that
- they're no longer needed and it's known that they're no
- longer referenced.) A common idiom might be:</para>
- <programlisting>
-(with-macptrs (p (#_allocate_foreign_memory size))
- (unwind-protect
- (use-foreign-memory p)
- (#_deallocate_foreign_memory p)))
-</programlisting>
- <para>That's not unreasonable code, but it's fairly
- expensive for a number of reasons: foreign functions calls
- are themselves fairly expensive (as is UNWIND-PROTECT), and
- most library routines for allocating and deallocating
- foreign memory (things like malloc and free) can be fairly
- expensive in their own right.</para>
- <para>In the idiomatic code above, both the MACPTR P and the
- block of memory that's being allocated and freed have
- dynamic extent and are therefore good candidates for stack
- allocation. OpenMCL provides the %STACK-BLOCK macro, which
- executes a body of code with one or more variables bound to
- stack-allocated MACPTRs which encapsulate the addresses of
- stack-allocated blocks of foreign memory. Using
- %STACK-BLOCK, the idiomatic code is:</para>
- <programlisting>
-(%stack-block ((p size))
- (use-foreign-memory p))
-</programlisting>
- <para>which is a bit more efficient and a bit more concise
- than the version presented earlier.</para>
- <para>%STACK-BLOCK is used as the basis for slightly
- higher-level things like RLET. (See FIXTHIS for more information
- about RLET.)</para>
- </sect3>
-
- <sect3 id=3D"Caveats-">
- <title>Caveats.</title>
- <para>Reading from, writing to, allocating, and freeing
- foreign memory are all potentially dangerous operations;
- this is no less true when these operations are performed in
- OpenMCL than when they're done in C or some other
- lower-level language. In addition, destructive operations on
- Lisp objects be dangerous, as can stack allocation if it's
- abused (if DYNAMIC-EXTENT declarations are violated.)
- Correct use of the constructs and primitives described here
- is reliable and safe; slightly incorrect use of these
- constructs and primitives can crash OpenMCL.</para>
- </sect3>
- </sect2>
-
- <sect2 id=3D"Foreign-Memory-Addresses-Dictionary">
- <title>Foreign-Memory-Addresses Dictionary</title>
- <para>Unless otherwise noted, all of the symbols mentioned
- below are exported from the CCL package.</para>
-
- <sect3 id=3D"Scalar-memory-reference">
- <title>Scalar memory reference</title>
- <variablelist>
- <varlistentry>
- <term>Syntax</term>
-
- <listitem>
- <para>%get-signed-byte ptr &optional (offset 0)</para>
-
- <para>%get-unsigned-byte ptr &optional (offset 0)</para>
-
- <para>%get-signed-word ptr &optional (offset 0)</para>
-
- <para>%get-unsigned-word ptr &optional (offset 0)</para>
-
- <para>%get-signed-long ptr &optional (offset 0)</para>
-
- <para>%get-unsigned-long ptr &optional (offset 0)</para>
-
- <para>%%get-signed-longlong ptr &optional (offset 0)</para>
-
- <para>%%get-unsigned-longlong ptr &optional (offset 0)</para>
-
- <para>%get-ptr ptr &optional (offset 0)</para>
-
- <para>%get-single-float ptr &optional (offset 0)</para>
-
- <para>%get-double-float ptr &optional (offset 0)</para>
- </listitem>
- </varlistentry>
- <varlistentry>
- <term>Description</term>
-
- <listitem>
- <para>References and returns the signed or unsigned 8-bit byte,
- signed or unsigned 16-bit word, signed or unsigned 32-bit long
- word, signed or unsigned 64-bit long long word, 32-bit address,
- 32-bit single-float, or 64-bit double-float at the effective byte
- address formed by adding offset to the address encapsulated by
- ptr.</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>Arguments</term>
-
- <listitem>
-
- <variablelist>
- <varlistentry>
- <term>ptr</term>
-
- <listitem>
- <para>A MACPTR</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>offset</term>
-
- <listitem>
- <para>A fixnum</para>
- </listitem>
- </varlistentry>
- </variablelist>
- </listitem>
- </varlistentry>
- </variablelist>
-
- <para>All of the memory reference primitives described above can=
be</para>
- <para>used with SETF.</para>
- </sect3>
-
- <sect3 id=3D"iget-bit--Function-">
- <title>%get-bit [Function]</title>
-
- <variablelist>
- <varlistentry>
- <term>Syntax</term>
-
- <listitem>
- <para>%get-bit ptr bit-offset</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>Description</term>
-
- <listitem>
- <para>References and returns the bit-offsetth bit at the address
- encapsulated by ptr. (Bit 0 at a given address is the most
- significant bit of the byte at that address.) Can be used with
- SETF.</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>Arguments</term>
-
- <listitem>
- <para> </para>
-
- <variablelist>
- <varlistentry>
- <term>ptr</term>
-
- <listitem>
- <para>A MACPTR</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>bit-offset</term>
-
- <listitem>
- <para>A fixnum</para>
- </listitem>
- </varlistentry>
- </variablelist>
- </listitem>
- </varlistentry>
- </variablelist>
- </sect3>
-
- <sect3 id=3D"iget-bitfield--Function-">
- <title>%get-bitfield [Function]</title>
- <variablelist>
- <varlistentry>
- <term>Syntax</term>
-
- <listitem>
- <para>%get-bitfield ptr bit-offset width</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>Description</term>
-
- <listitem>
- <para>References and returns an unsigned integer composed from the
- width bits found bit-offsetbits from the address encapsulated by
- ptr. (The least significant bit of the result is the value of
- (%get-bit ptr (1- (+ bit-offset width))). Can be used with SETF.</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>Arguments</term>
-
- <listitem>
- <para> </para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>ptr</term>
-
- <listitem>
- <para>A MACPTR</para>
-
- <variablelist>
- <varlistentry>
- <term>bit-offset</term>
-
- <listitem>
- <para>A fixnum</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>width</term>
-
- <listitem>
- <para>A positive fixnum</para>
- </listitem>
- </varlistentry>
- </variablelist>
- </listitem>
- </varlistentry>
- </variablelist>
- </sect3>
-
- <sect3>
- <title>%int-to-ptr [Function]</title>
-
- <variablelist>
- <varlistentry>
- <term>Syntax</term>
-
- <listitem>
- <para>%int-to-ptr int</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>Description</term>
-
- <listitem>
- <para>Creates and returns a MACPTR whose address matches int.</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>Arguments</term>
-
- <listitem>
- <para> </para>
-
- <variablelist>
- <varlistentry>
- <term>int</term>
-
- <listitem>
- <para>An (unsigned-byte 32)</para>
- </listitem>
- </varlistentry>
- </variablelist>
- </listitem>
- </varlistentry>
- </variablelist>
- </sect3>
-
- <sect3>
- <title>%inc-ptr [Function]</title>
-
- <variablelist>
- <varlistentry>
- <term>Syntax</term>
-
- <listitem>
- <para>%inc-ptr ptr &optional (delta 1)</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>Description</term>
-
- <listitem>
- <para>Creates and returns a MACPTR whose address is the address of
- ptr plus delta. The idiom (%inc-ptr ptr 0) is sometimes used to
- copy a MACPTR, e.g., to create a new MACPTR encapsulating the same
- address as ptr.</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>Arguments</term>
-
- <listitem>
- <para> </para>
-
- <variablelist>
- <varlistentry>
- <term>ptr</term>
-
- <listitem>
- <para>A MACPTR</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>delta</term>
-
- <listitem>
- <para>A fixnum</para>
- </listitem>
- </varlistentry>
- </variablelist>
- </listitem>
- </varlistentry>
- </variablelist>
- </sect3>
-
- <sect3>
- <title>%ptr-to-int [Function]</title>
-
- <variablelist>
- <varlistentry>
- <term>Syntax</term>
-
- <listitem>
- <para>%ptr-to-int ptr</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>Description</term>
-
- <listitem>
- <para>Returns the address encapsulated by ptr, as an
- (unsigned-byte 32).</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>Arguments</term>
-
- <listitem>
- <para> </para>
-
- <variablelist>
- <varlistentry>
- <term>ptr</term>
-
- <listitem>
- <para>A MACPTR</para>
- </listitem>
- </varlistentry>
- </variablelist>
- </listitem>
- </varlistentry>
- </variablelist>
- </sect3>
-
- <sect3>
- <title>%null-ptr [Macro]</title>
-
- <variablelist>
- <varlistentry>
- <term>Syntax</term>
-
- <listitem>
- <para>%null-ptr</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>Description</term>
-
- <listitem>
- <para>Equivalent to (%ptr-to-int 0).</para>
- </listitem>
- </varlistentry>
- </variablelist>
- </sect3>
-
- <sect3>
- <title>%null-ptr-p [Function]</title>
-
- <variablelist>
- <varlistentry>
- <term>Syntax</term>
-
- <listitem>
- <para>%null-ptr-p ptr</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>Description</term>
-
- <listitem>
- <para>Returns T If ptr is a MACPTR encapsulating the address 0,
- NIL if ptr encapsulates some other address.</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>Arguments</term>
-
- <listitem>
- <para> </para>
-
- <variablelist>
- <varlistentry>
- <term>ptr</term>
-
- <listitem>
- <para>A MACPTR</para>
- </listitem>
- </varlistentry>
- </variablelist>
- </listitem>
- </varlistentry>
- </variablelist>
- </sect3>
-
- <sect3>
- <title>%setf-macptr [Function]</title>
-
- <variablelist>
- <varlistentry>
- <term>Syntax</term>
-
- <listitem>
- <para>%setf-macptr dest-ptr src-ptr</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>Description</term>
-
- <listitem>
- <para>Causes dest-ptr to encapsulate the same address that src-ptr
- does, then returns dest-ptr.</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>Arguments</term>
-
- <listitem>
- <para> </para>
-
- <variablelist>
- <varlistentry>
- <term>dest-ptr</term>
-
- <listitem>
- <para>A MACPTR</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>src-ptr</term>
-
- <listitem>
- <para>A MACPTR</para>
- </listitem>
- </varlistentry>
- </variablelist>
- </listitem>
- </varlistentry>
- </variablelist>
- </sect3>
-
- <sect3>
- <title>%incf-ptr [Macro]</title>
-
- <variablelist>
- <varlistentry>
- <term>Syntax</term>
-
- <listitem>
- <para>%incf-ptr ptr &optional (delta 1)</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>Description</term>
-
- <listitem>
- <para>Destructively modifies ptr, by adding delta to the address
- it encapsulates. Returns ptr.</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>Arguments</term>
-
- <listitem>
- <para> </para>
-
- <variablelist>
- <varlistentry>
- <term>ptr</term>
-
- <listitem>
- <para>A MACPTR</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>delta</term>
-
- <listitem>
- <para>A fixnum</para>
- </listitem>
- </varlistentry>
- </variablelist>
- </listitem>
- </varlistentry>
- </variablelist>
- </sect3>
-
- <sect3>
- <title>with-macptrs [Macro]</title>
-
- <variablelist>
- <varlistentry>
- <term>Syntax</term>
-
- <listitem>
- <para>with-macptrs (var expr)* &body body</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>Description</term>
-
- <listitem>
- <para>Executes body in an environment in which each var is bound
- to a stack-allocated macptr which encapsulates the foreign address
- yielded by the corresponding expr. Returns whatever value(s) body
- returns.</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>Arguments</term>
-
- <listitem>
- <para> </para>
-
- <variablelist>
- <varlistentry>
- <term>var</term>
-
- <listitem>
- <para>A symbol (variable name)</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>expr</term>
-
- <listitem>
- <para>A MACPTR-valued expression</para>
- </listitem>
- </varlistentry>
- </variablelist>
- </listitem>
- </varlistentry>
- </variablelist>
- </sect3>
-
- <sect3>
- <title>%stack-block [Macro]</title>
-
- <variablelist>
- <varlistentry>
- <term>Syntax</term>
-
- <listitem>
- <para>%stack-block (var expr)* &body body</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>Description</term>
-
- <listitem>
- <para>Executes body in an environment in which each var is bound
- to a stack-allocated macptr which encapsulates the address of a
- stack-allocated region of size expr bytes. Returns whatever
- value(s) body returns.</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>Arguments</term>
-
- <listitem>
- <para> </para>
-
- <variablelist>
- <varlistentry>
- <term>var</term>
-
- <listitem>
- <para>A symbol (variable name)</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>expr</term>
-
- <listitem>
- <para>An expression which should evaluate to a non-negative
- fixnum</para>
- </listitem>
- </varlistentry>
- </variablelist>
- </listitem>
- </varlistentry>
- </variablelist>
- </sect3>
-
- <sect3>
- <title>make-cstring [Function]</title>
-
- <variablelist>
- <varlistentry>
- <term>Syntax</term>
-
- <listitem>
- <para>make-cstring string</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>Description</term>
-
- <listitem>
- <para>Allocates a block of memory (via malloc) of length (1+
- (length string)). Copies the string to this block and appends a
- trailing NUL byte; returns a MACPTR to the block.</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>Arguments</term>
-
- <listitem>
- <para> </para>
-
- <variablelist>
- <varlistentry>
- <term>string</term>
-
- <listitem>
- <para>A lisp string</para>
- </listitem>
- </varlistentry>
- </variablelist>
- </listitem>
- </varlistentry>
- </variablelist>
- </sect3>
-
- <sect3>
- <title>with-cstrs [Macro]</title>
-
- <variablelist>
- <varlistentry>
- <term>Syntax</term>
-
- <listitem>
- <para>with-cstrs (var string)* &body body</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>Description</term>
-
- <listitem>
- <para>Executes body in an environment in which each var is bound
- to a stack-allocated macptr which encapsulates the %address of a
- stack-allocated region of into which each string (and a trailing
- NUL byte) has been copied. Returns whatever value(s) body returns.</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>Arguments</term>
-
- <listitem>
- <para> </para>
-
- <variablelist>
- <varlistentry>
- <term>var</term>
-
- <listitem>
- <para>A symbol (variable name)</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>string</term>
-
- <listitem>
- <para>An expression which should evaluate to a lisp string</para>
- </listitem>
- </varlistentry>
- </variablelist>
- </listitem>
- </varlistentry>
- </variablelist>
- </sect3>
-
- <sect3>
- <title>%get-cstring [Function]</title>
-
- <variablelist>
- <varlistentry>
- <term>Syntax</term>
-
- <listitem>
- <para>%get-cstring ptr</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>Description</term>
-
- <listitem>
- <para>Interprets ptr as a pointer to a (NUL -terminated) C string;
- returns an equivalent lisp string.</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>Arguments</term>
-
- <listitem>
- <para></para>
-
- <variablelist>
- <varlistentry>
- <term>ptr</term>
-
- <listitem>
- <para>A MACPTR</para>
- </listitem>
- </varlistentry>
- </variablelist>
- </listitem>
- </varlistentry>
- </variablelist>
- </sect3>
-
- <sect3>
- <title>%str-from-ptr [Function]</title>
-
- <variablelist>
- <varlistentry>
- <term>Syntax</term>
-
- <listitem>
- <para>%str-from-ptr ptr length</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>Description</term>
-
- <listitem>
- <para>Returns a lisp string of length <varname>length</varname>,
- whose contents are initialized from the bytes at<varname> ptr.</varname>
- </para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>Arguments</term>
-
- <listitem>
- <para><variablelist><varlistentry><term>ptr</term><listitem><para>A
- MACPTR</para></listitem></varlistentry><varlistentry><term>length</term>=
<listitem><para>a
- non-negative fixnum</para></listitem></varlistentry></variablelist></par=
a>
- </listitem>
- </varlistentry>
- </variablelist>
- </sect3>
- </sect2>
- </sect1>
-
- <sect1 id=3D"The-Interface-Database">
- <title>The Interface Database</title>
-
- <sect2 id=3D"interface-database-Overview">
- <title>Overview</title>
- <para>OpenMCL uses a set of database files which contain
- foreign type, record, constant, and function definitions
- derived from the operating system's header files, be that
- Linux or Darwin. An archive containing these database files
- (and the shell scripts which were used in their creation) is
- available; see the Distributions page for information about
- obtaining current interface database files.</para>
- <para>Not surprisingly, different platforms use different database=
files.</para>
- <para>OpenMCL defines reader macros that consult these databases:<=
/para>
- <itemizedlist>
- <listitem>
- <para>#$foo looks up the value of the constant definition of foo</par=
a>
- </listitem>
- <listitem>
- <para>#_foo looks up the foreign function definition for foo</para>
- </listitem>
- =
- </itemizedlist>
- <para>In both cases, the symbol foo is interned in the "OS"
- package. The #$ reader macro has the side-effect of defining
- foo as a constant (as if via DEFCONSTANT); the #_ reader macro
- has the side effect of defining foo as a macro which will
- expand into an (EXTERNAL-CALL form.)</para>
- <para>It's important to remember that the side-effect happens
- when the form containing the reader macro is
- read. Macroexpansion functions that expand into forms which
- contain instances of those reader macros don't do what one
- might think that they do, unless the macros are expanded in
- the same lisp session as the reader macro was read in.</para>
- <para>In addition, references to foreign type,
- structure/union, and field names (when used in the RREF/PREF
- and RLET macros) will cause these database files to be
- consulted.</para>
- <para>Since the OpenMCL sources contain instances of these
- reader macros (and references to foreign record types and
- fields), compiling OpenMCL from those sources depends on the
- ability to find and use (see <xref
- linkend=3D"Building-the-heap-image"/>).</para>
- </sect2>
-
- <sect2 id=3D"Other-issues">
- <title>Other issues:</title>
- <itemizedlist>
- <listitem>
- <para>OpenMCL now preserves the case of external symbols
- in itsdatabase files. See for information about case in
- foreign symbol names.</para>
- </listitem>
- <listitem>
- <para>The Linux databases are derived from a somewhat
- arbitrary set of Linux header files. Linux is enough of a
- moving target that it may be difficult to define a standard,
- reference set of interfaces from which to derive a standard,
- reference set of database files.This seems to be less of
- an issue with Darwin and FreeBSD.</para>
- </listitem>
- </itemizedlist>
- <para>For information about building the database files,
- see <xref linkend=3D"The-Interface-Translator"/>.</para>
- </sect2>
- </sect1>
-
- <sect1 id=3D"Using-Interface-Directories">
- <title>Using Interface Directories</title>
-
- <sect2 id=3D"Interface-Directory-Overview">
- <title>Overview</title>
- <para>As distributed, the "ccl:headers;" (for LinuxPPC)
- directory is organized like:</para>
- <programlisting>
-headers/
-headers/gl/
-headers/gl/C/
-headers/gl/C/populate.sh
-headers/gl/constants.cdb
-headers/gl/functions.cdb
-headers/gl/records.cdb
-headers/gl/objc-classes.cdb
-headers/gl/objc-methods.cdb
-headers/gl/types.cdb
-headers/gnome/
-headers/gnome/C/
-headers/gnome/C/populate.sh
-headers/gnome/constants.cdb
-headers/gnome/functions.cb
-headers/gnome/records.cdb
-headers/gnome/objc-classes.cdb
-headers/gnome/objc-methods.cdb
-headers/gnome/types.cdb
-headers/gtk/
-headers/gtk/C/
-headers/gtk/C/populate.sh
-headers/gtk/constants.cdb
-headers/gtk/functions.cdb
-headers/gtk/records.cdb
-headers/gtk/objc-classes.cdb
-headers/gtk/objc-methods.cdb
-headers/gtk/types.cdb
-headers/libc/
-headers/libc/C/
-headers/libc/C/populate.sh
-headers/libc/constants.cdb
-headers/libc/functions.cdb
-headers/libc/records.cdb
-headers/libc/objc-classes.cdb
-headers/libc/objc-methods.cdb
-headers/libc/types.cdb
-</programlisting>
- <para>e.g, as a set of parallel subdirectories, each with a
- lowercase name and each of which contains a set of 6 database
- files and a "C" subdirectory which contains a shell script
- used in the database creation process.</para>
- <para>As one might assume, the database files in each of these
- subdirectories contain foreign type, constant, and function
- definitions - as well as ObjC class and method info -that
- correspond (roughly) to the information contained in the
- header files associated with a "-dev" package in a Linux
- distribution. "libc" corresponds pretty closely to the
- interfaces associated with "glibc/libc6" header files, "gl"
- corresponds to an "openGL+GLUT" developmnent package, "gtk"
- and "gnome" contain interface information from the GTK+1.2 and
- GNOME libraries, respectively.</para>
- <para>For Darwin, the "ccl:darwin-headers" directory contains
- a "libc" subdirectory, whose contents roughly correspond to
- those of "/usr/include" under Darwin, as well as
- subdirectories corresponding to the MacOSX Carbon and Cocoa
- frameworks.</para>
- <para>To see the precise set of .h files used to generate the
- database files in a given interface directory, consult the
- corresponding "populate.sh" shell script (in the interface
- directory's "C" subdirectory.)</para>
- <para>The intent is that this initial set can be augmented to
- meet local needs, and that this can be done in a fairly
- incremental fashion: one needn't have unrelated header files
- installed in order to generate interface databases for a
- package of interest.</para>
- <para>Hopefully, this scheme will also make it easier to
- distribute patches and bug fixes.</para>
- <para>OpenMCL maintains a list of directories; when looking
- for a foreign type, constant, function, or record definition,
- it'll consult the database files in each directory on that
- list. Initially, the list contains an entry for the "libc"
- interface directory. OpenMCL needs to be explicitly told to
- look in other interface directories should it need to do
- so.</para>
- </sect2>
-
- <sect2 id=3D"Creating-new-interface-directories">
- <title>Creating new interface directories</title>
- <para>This example refers to "ccl:headers;", which is
- appropriate for LinuxPPC. The procedure's analogous under
- Darwin, where the "ccl:darwin-headers;" directory would be
- used instead.</para>
- <para>To create a new interface directory, "foo", and a set of
- database files in that directory:</para>
- <orderedlist continuation=3D"restarts" inheritnum=3D"ignore">
- <listitem>
- <para>Create a subdirectory of "ccl:headers;" named
- "foo".</para>
- </listitem>
-
- <listitem>
- <para>Create a subdirectory of "ccl:headers;foo;" named
- "C".</para>
- </listitem>
-
- <listitem>
- <para>Create a file in "ccl:headers;foo;C;" named
- "populate.sh".</para>
-
- <para>One way of accomplishing the above steps is:</para>
-
- <programlisting format=3D"linespecific">? (close (open "ccl:heade=
rs;foo;C;populate.sh" :direction :output :
- if-does-not-exist :create :if-exists :overwrite))</programl=
isting>
- </listitem>
-
- <listitem>
- <para>Edit the file created above, using the "populate.sh"
- files in the distribution as guidelines.</para>
-
- <para>The file might wind up looking something like:</para>
-
- <programlisting format=3D"linespecific">#/bin/sh
-h-to-ffi.sh `foo-config -cflags` /usr/include/foo/foo.h</programlisting>
- </listitem>
- </orderedlist>
-
- <para>Refer to <xref linkend=3D"The-Interface-Translator"/> for
- information about running the interface translator and .ffi
- parser.</para>
- <para>Assuming that all went well, there should now be .cdb
- files in "ccl:headers;foo;". You can then do
- <programlisting>
-? (use-interface-dir :foo)
- </programlisting> =
- whenever you need to
- access the foreign type information in those database
- files.</para>
- </sect2>
- </sect1>
-
- <sect1 id=3D"Using-Shared-Libraries">
- <title>Using Shared Libraries</title>
-
- <sect2 id=3D"Shared-Library-Overview">
- <title>Overview</title>
-
- <para>OpenMCL provides facilities to open and close shared
- libraries.</para>
- <para>"Opening" a shared library, which is done with <xref
- linkend=3D"f_open-shared-library"/>, maps the library's code and
- data into OpenMCL's address space and makes its exported
- symbols accessible to OpenMCL.</para>
- <para>"Closing" a shared library, which is done with <xref
- linkend=3D"f_close-shared-library"/>, unmaps the library's code
- and and removes the library's symbols from the global
- namespace.</para>
- <para>A small number of shared libraries (including libc,
- libm, libdl under Linux, and the "system" library under
- Darwin) are opened by the lisp kernel and can't be
- closed.</para>
- <para>OpenMCL uses data structures of type
- EXTERNAL-ENTRY-POINT to map a foreign function name (string)
- to that foreign function's <emphasis>current</emphasis>
- address. (A function's address may vary from session to
- session as different versions of shared libraries may load at
- different addresses; it may vary within a session for similar
- reasons.)</para>
- <para>An EXTERNAL-ENTRY-POINT whose address is known is said
- to be <emphasis>resolved</emphasis>. When an external entry
- point is resolved, the shared library which defines that entry
- point is noted; when a shared library is closed, the entry
- points that it defines are made unresolved. An
- EXTERNAL-ENTRY-POINT must be in the resolved state in order to
- be FF-CALLed; calling an unresolved entry point causes a "last
- chance" attempt to resolve it. Attempting to resolve an
- entrypoint that was defined in a closed library will cause an
- attempt to reopen that library.</para>
- <para>OpenMCL keeps track of all libraries that have been
- opened in a lisp session. When a saved application is first
- started, an attempt is made to reopen all libraries that were
- open when the image was saved, and an attempt is made to
- resolve all entrypoints that had been referenced when the
- image was saved. Either of these attempts can fail "quietly",
- leaving some entry points in an unresolved state.</para>
- <para>Linux shared libraries can be referred to either by a
- string which describes their full pathname or by their
- <emphasis>soname</emphasis>, a shorter string that can be
- defined when the library is created. The dynamic linker
- mechanisms used in Linux make it possible (through a series of
- filesystem links and other means) to refer to a library via
- several names; the library's soname is often the most
- appropriate identifier.</para>
- <para>sonames are often less version-specific than other names
- for libraries; a program that refers to a library by the name
- "libc.so.6" is more portable than one which refers to
- "libc-2.1.3.so" or to "libc-2.2.3.so", even though the latter
- two names might each be platform-specific aliases of the
- first.</para>
- <para>All of the global symbols described below are exported
- from the CCL package.</para>
- </sect2>
-
- <sect2 id=3D"Limitations-and-known-bugs--1-">
- <title>Limitations and known bugs</title>
- <itemizedlist>
- <listitem>
- <para>Don't get me started.</para>
- </listitem>
- <listitem>
- <para>The underlying functionality has a poor notion of
- dependency;it's not always possible to open libraries that
- depend on unopened libraries, but it's possible to close
- libraries on which other libraries depend. It
- <emphasis>may</emphasis> be possible to generate
- more explicit dependency information by parsing the output
- of the Linux ldd and ldconfig programs.</para>
- </listitem>
- =
- </itemizedlist>
- </sect2>
-
- <sect2 id=3D"Darwin-Notes">
- <title>>Darwin Notes</title>
- <para>Darwin shared libraries come in two (basic) flavors:</para>
- <itemizedlist>
- <listitem>
- <para>"dylibs" (which often have the extension".dylib")
- are primarily intended to be linked against atcompile/link
- time. They can be loaded dynamically,<emphasis>but can't
- be unloaded</emphasis>. Accordingly,OPEN-SHARED-LIBRARY
- can be used to open a .dylib-style library;calling
- CLOSE-SHARED-LIBRARY on the result of such a callproduces
- a warning, and has no other effect.It appears that (due to
- an OS bug) attempts to open .dylibshared-libraries that
- are already open can cause memory corruptionunless the
- full pathname of the .dylib file is specified on thefirst
- and all subsequent calls.</para>
- </listitem>
- <listitem>
- <para>"bundles" are intended to serve as application
- extensions; they can be opened multiple times (creating
- multiple instances of the library!) and closed
- properly.</para>
- </listitem>
- </itemizedlist>
- <para>Thanks to Michael Klingbeil for getting both kinds of
- Darwin shared libraries working in OpenMCL.</para>
- </sect2>
- </sect1>
-
- <sect1 id=3D"The-Interface-Translator">
- <title>The Interface Translator</title>
-
- <sect2 id=3D"Inteface-translator-overview">
- <title>Overview</title>
- <para>OpenMCL uses an interface translation system based on the FFIGEN
- system, which is described at
- http://www.ccs.neu.edu/home/lth/ffigen/.
- The interface translator makes
- the constant, type, structure, and function definitions in a set of
- C-language header files available to lisp code.</para>
- <para>The basic idea of the FFIGEN scheme is to use the C
- compiler's frontend and parser to translate .h files into
- semantically equivalent .ffi files, which represent the
- definitions from the headers using a syntax based on
- S-expressions. Lisp code can then concentrate on the .ffi
- representation, without having to concern itself with the
- semantics of header file inclusion or the arcana of C
- parsing.</para>
- <para>The original FFIGEN system used a modified version of
- the LCC C compiler to produce .ffi files. Since many LinuxPPC
- header files contain GCC-specific constructs, OpenMCL's
- translation system uses a modified version of GCC (called,
- somewhat confusingly, ffigen.)</para>
- <para>A version of ffigen based on GCC-4.0 was developed
- during the spring and summer of 2005. Sources (diffs relative
- to the GCC-4.0 release) are available here, and binaries are
- available for DarwinPPC and for LinuxPPC. These versions
- should be insensitive to to the version of GCC (and its
- preprocessor) installed on the system.</para>
- <para>An older version was developed in 2001-2002; it depended
- on the installed version of GCC being 2.95. It may still be
- of interest for people unable to run the GCC-4.0-based version
- for whatever reason.</para>
- <para>A LinuxPPC binary of this older version is available at
- ftp://clozure.com/pub/ffigen-0.1.tar.gz, and LinuxPPC source
- differences are at
- ftp://clozure.com/pub/ffigen-src.tar.gz.</para>
- <para>For Darwin, the binary of the older FFIGEN is available
- at ftp://clozure.com/pub/ffigen-darwin.tar.gz, and the source
- differences are at
- ftp://clozure.com/pub/ffigen-darwin-src.tar.gz.</para>
- <para>A shell script (distributed with the source and binary
- packages) called h-to-ffi.sh reads a specified .h file (and
- optional preprocessor arguments) and writes a (hopefully)
- equivalent .ffi file to standard output, calling the installed
- C preprocessor and the ffigen program with appropriate
- arguments.</para>
- <para>For each interface directory (see FIXTHIS)
- <emphasis>subdir</emphasis> distributed with OpenMCL, a shell
- script (distributed with OpenMCL as
- "ccl:headers;<emphasis>subdir</emphasis>;C;populate.sh"
- ("ccl:darwin-headers;<emphasis>subdir</emphasis>;C;populate.sh"
- for Darwin)) calls h-to-ffi.sh on a large number of the header
- files in /usr/include (or some other <emphasis>system header
- path</emphasis>) and creates a parallel directory tree in
- "ccl:headers;<emphasis>subdir</emphasis>;C;<emphasis>system</empha=
sis>;<emphasis>header</emphasis>;<emphasis>path</emphasis>;"
- (or
- "ccl:darwin-headers;<emphasis>subdir</emphasis>;C;<emphasis>system=
</emphasis>;<emphasis>header</emphasis>;<emphasis>path</emphasis>;"),
- populating that directory with .ffi files.</para>
- <para>A lisp function defined in "ccl:library;parse-ffi.lisp"
- reads the .ffi files in a specified interface directory
- <emphasis>subdir</emphasis> and generates new versions of the
- databases (files with the extension .cdb).</para>
- <para>The CDB databases are used by the #$ and #_ reader
- macros and are used in the expansion of RREF, RLET, and
- related macros.</para>
- </sect2>
-
- <sect2 id=3D"Details--rebuilding-the-CDB-databases--step-by-step">
- <title>Details: rebuilding the CDB databases, step by step</title>
- <orderedlist>
- <listitem>
- <para>Ensure that the FFIGEN program is installed. See
- the"README" file in the source or binary archive for
- specificinstallation instructions.This example assumes
- LinuxPPC; for 32-bit DarwinPPC,
- substitute"ccl:darwin-headers;" for "ccl:headers;". For
- 64-bit DarwinPPC,substitute
- "ccl:darwin-headers64;".</para>
- </listitem>
- <listitem>
- <para>Edit the
- "ccl:headers;<emphasis>subdir</emphasis>;C;populate.sh"shell
- script. When you're confident that the files
- andpreprocessor options match your environment, cd to
- the"ccl:headers;<emphasis>subdir</emphasis>;C;" directory
- andinvoke ./populate.sh. Repeat this step until you're
- able tocleanly translate all files refrenced in the shell
- script.</para>
- </listitem>
- <listitem>
- <para>Run OpenMCL:
- <programlisting>
-? (require "PARSE-FFI")
-PARSE-FFI
-
-? (parse-standard-ffi-files :SUBDIR)
-;;; lots of output ... after a while, shiny new .cdb files should
-;;; appear in "ccl:headers;subdir;"
-;;; (or "ccl:darwin-headers;subdir;" under Darwin)
-</programlisting></para>
- </listitem>
- </orderedlist>
- </sect2>
- </sect1>
-
- <sect1 id=3D"Case-sensitivity-of-foreign-names-in-OpenMCL">
- <title>Case-sensitivity of foreign names in OpenMCL</title>
-
- <sect2 id=3D"Case-sensitivity-overview">
- <title>Overview</title>
- <para>As of release 0.11, OpenMCL addresses the fact that
- foreign type, constant, record, field, and function nams are
- case-sensitive and provides mechanisms to refer to these names
- via lisp symbols.</para>
- <para>Previous versions of OpenMCL have tried to ignore that
- fact, under the belief that case conflicts were rare and that
- many users (and implementors) would prefer not to deal with
- case-related issues. The fact that some information in the
- interface databases was incomplete or inaccessable because of
- this policy made it clearer that the policy was untenable. I
- can't claim that the approach described here is aesthetically
- pleasing, but I can honestly say that it's less unpleasant
- than other approaches that I'd thought of. I'd be interested
- to hear alternate proposals.</para>
- <para>The issues described here have to do with how lisp
- symbols are used to denote foreign functions, constants,
- types, records, and fields. It doesn't affect how other lisp
- objects are sometimes used to denote foreign objects. For
- instance, the first argument to the EXTERNAL-CALL macros is
- now and has always been a case-sensitive string.</para>
- </sect2>
-
- <sect2 id=3D"Foreign-constant-and-function-names">
- <title>Foreign constant and function names</title>
- <para>The primary way of referring to foreign constant and
- function names in OpenMCL is via the #$ and #_ reader
- macros. These reader macro functions each read a symbol into
- the "OS" package, look up its constant or function definition
- in the interface database, and assign the value of the
- constant to the symbol or install a macroexpansion function on
- the symbol.</para>
- <para>In order to observe case-sensitivity, the reader-macros
- now read the symbol with (READTABLE-CASE :PRESERVE) in
- effect.</para>
- <para>This means that it's necessary to type the foreign
- constant or function name in correct case, but it isn't
- necessary to use any special escaping constructs when writing
- the variable name. For instance:</para>
- <programlisting>
-(#_read fd buf n) ; refers to foreign symbol "read"
-(#_READ fd buf n) ; refers to foreign symbol "READ", which may
-; not exist ...
-#$o_rdonly ; Probably doesn't exist
-#$O_RDONLY ; Exists on most platforms
-</programlisting>
- </sect2>
-
- <sect2 id=3D"Foreign-type--record--and-field-names">
- <title>Foreign type, record, and field names</title>
- <para>Constructs like RLET expect a foreign type or record
- name to be denoted by a symbol (typically a keyword); RREF
- (and PREF) expect an "accessor" form, typically a keyword
- formed by concatenating a foreign type or record name with a
- sequence of one or more foreign field names, separated by
- dots. These names are interned by the reader as other lisp
- symbols are, with an arbitrary value of READTABLE-CASE in
- effect (typically :UPCASE.) It seems like it would be very
- tedious to force users to manually escape (via vertical bar or
- backslash syntax) all lowercase characters in symbols used to
- specify foreign type, record, and field names (especially
- given that many traditional POSIX structure, type, and field
- names are entirely lowercase.)</para>
- <para>The approach taken by OpenMCL is to allow the symbols
- (keywords) used to denote foreign type, record, and field
- names to contain angle brackets (<literal><</literal> and
- <literal>></literal>). Such symbols are translated to
- foreign names via the following set of conventions:</para>
- <itemizedlist>
- <listitem>
- <para>All instances of < and > in the symbol's pname
- are balanced and don't nest.</para>
- </listitem>
- <listitem>
- <para>Any alphabetic characters in the symbol's pname
- thataren't enclosed in angle brackets are treated as
- lower-case,regardless of the value of READTABLE-CASE and
- regardless of the case in which they were written.</para>
- </listitem>
- <listitem>
- <para>Alphabetic characters that appear within angle
- brackets aremapped to upper-case, again regardless of how
- they were written orinterned.</para>
- </listitem>
- </itemizedlist>
- <para>There may be many ways of "escaping" (with angle
- brackets) sequences of upper-case and non-lower-case
- characters in a symbol used to denote a foreign name. When
- translating in the other direction, OpenMCL always escapes the
- longest sequence that starts with an upper-case character and
- doesn't contain a lower-case character.</para>
- <para>It's often preferable to use this canonical form of a
- foreign type name.</para>
- <para>The accessor forms used by PREF/RREF should be viewed as
- a series of foreign type/record and field names; upper-case
- sequences in the component names should be escaped with angle
- brackets, but those sequences shouldn't span components. (More
- simply, the separating dots shouldn't be enclosed, even if
- both surrounding characters need to be.)</para>
- <para>Older POSIX code tends to use lower-case exclusively for
- type, record, and field names; there are only a few cases in
- the OpenMCL sources where mixed-case names need to be
- escaped.</para>
- =
- </sect2>
-
- <sect2 id=3D"Examples--1-">
- <title>Examples</title>
- <programlisting>
-;;; Allocate a record of type "window".
-(rlet ((w :window)) ...)
-;;; Allocate a record of type "Window", which is probably a
-;;; different type
-(rlet ((w :<w>indow)) ...)
-;;; This is equivalent to the last example
-(rlet ((w :<w>INDOW)))
-</programlisting>
- </sect2>
- </sect1>
-
- <sect1 id=3D"Tutorial--Using-Basic-Calls-and-Types">
- <title>Tutorial: Using Basic Calls and Types</title>
- <para>This tutorial is meant to cover the basics of OpenMCL for
- calling external C functions and passing data back and forth.
- These basics will provide the foundation for more advanced
- techniques which will allow access to the various external
- libraries and toolkits.</para>
- <para>The first step is to start with a simple C dynamic library
- in order to actually observe what is actually passing between
- OpenMCL and C. So, some C code is in order:</para>
- <para>Create the file typetest.c, and put the following code
- into it:</para>
- <programlisting>
-
-#include <stdio.>
-
-void
-void_void_test(void)
-{
- printf("Entered %s:\n", __FUNCTION__);
- printf("Exited %s:\n", __FUNCTION__);
- fflush(stdout);
-}
-
-signed char
-sc_sc_test(signed char data)
-{
- printf("Entered %s:\n", __FUNCTION__);
- printf("Data In: %d\n", (signed int)data);
- printf("Exited %s:\n", __FUNCTION__);
- fflush(stdout);
- return data;
-}
-
-unsigned char
-uc_uc_test(unsigned char data)
-{
- printf("Entered %s:\n", __FUNCTION__);
- printf("Data In: %d\n", (signed int)data);
- printf("Exited %s:\n", __FUNCTION__);
- fflush(stdout);
- return data;
-}
- </programlisting>
- <para>This defines three functions. If you're familiar with C,
- notice that there's no <literal>main()</literal>, because we're
- just building a library, not an executable.</para>
- <para>The function <literal>void_void_test()</literal> doesn't
- take any parameters, and doesn't return anything, but it prints
- two lines to let us know it was called.
- <literal>sc_sc_test()</literal> takes a signed char as a
- parameter, prints it, and returns it.
- <literal>uc_uc_test()</literal> does the same thing, but with an
- unsigned char. Their purpose is just to prove to us that we
- really can call C functions, pass them values, and get values
- back from them.</para>
- <para>This code is compiled into a dynamic library on OS X
- 10.3.4 with the command:</para>
- <programlisting>
-
-gcc -dynamiclib -Wall -o libtypetest.dylib typetest.c \
- -install_name ./libtypetest.dylib
- </programlisting>
- <tip><para>Users of 64-bit platforms may need to pass options such
- as "-m64" to gcc, may need to give the output library a different
- extension (such as ".so"), and may need to user slightly different
- values for other options in order to create an equivalent test
- library.</para></tip>
-
- <para>The -dynamiclib tells gcc that we will be compiling this
- into a dynamic library and not an executable binary program.
- The output filename is "libtypetest.dylib". Notice that we
- chose a name which follows the normal OS X convention, being in
- the form "libXXXXX.dylib", so that other programs can link to
- the library. OpenMCL doesn't need it to be this way, but it is
- a good idea to adhere to existing conventions.</para>
- <para>The -install_name flag is primarily used when building OS
- X "bundles". In this case, we are not using it, so we put a
- placeholder into it, "./libtypetest.dylib". If we wanted to use
- typetest in a bundle, the -install_name argument would be a
- relative path from some "current" directory.</para>
- <para>After creating this library, the first step is to tell
- OpenMCL to open the dynamic library. This is done by calling
- .</para>
- <programlisting>
-
-Welcome to OpenMCL Version (Beta: Darwin) 0.14.2-040506!
-
-? (open-shared-library "/Users/andewl/openmcl/libtypetest.dylib")
-#<SHLIB /Users/andewl/openmcl/libtypetest.dylib #x638EF3E>
-</programlisting>
- <para>You should use an absolute path here; using a relative
- one, such as just "libtypetest.dylib", would appear to work, but
- there are subtle problems which occur after reloading it. See
- the Darwin notes on for details. It would be a bad idea anyway,
- because software should never rely on its starting directory
- being anything in particular.</para>
- <para>This command returns a reference to the opened shared library,=
and
-OpenMCL also adds one to the global variable
-<literal>ccl::*shared-libraries*</literal>:</para>
- <programlisting>
-
-? ccl::*shared-libraries*
-(#<SHLIB /Users/andewl/openmcl/libtypetest.dylib #x638EF3E>
- #<SHLIB /usr/lib/libSystem.B.dylib #x606179E>)
- </programlisting>
- <para>Before we call anything, let's check that the individual
- functions can actually be found by the system. We don't have to
- do this, but it helps to know how to find out whether this is
- the problem, when something goes wrong. We use <xref
- linkend=3D"m_external-call"/>:</para>
- <programlisting>
-
-? (external "_void_void_test")
-#<EXTERNAL-ENTRY-POINT "_void_void_test" (#x000CFDF8) /Users/andewl/ope=
nmcl/libtypetest.dylib #x638EDF6>
-
-? (external "_sc_sc_test")
-#<EXTERNAL-ENTRY-POINT "_sc_sc_test" (#x000CFE50) /Users/andewl/openmcl=
/libtypetest.dylib #x638EB3E>
-
-? (external "_uc_uc_test")
-#<EXTERNAL-ENTRY-POINT "_uc_uc_test" (#x000CFED4) /Users/andewl/openmcl=
/libtypetest.dylib #x638E626>
- </programlisting>
- <para>Notice that the actual function names have been "mangled"
- by the C linker. The first function was named "void_void_test"
- in typetest.c, but in libtypetest.dylib, it has an underscore (a
- "_" symbol) before it: "_void_void_test". So, this is the name
- which you have to use. The mangling - the way the name is
- changed - may be different for other operating systems or other
- versions, so you need to "just know" how it's done...</para>
- <para>Also, pay particular attention to the fact that a
- hexadecimal value appears in the EXTERNAL-ENTRY-POINT.
- (#x000CFDF8, for example - but what it is doesn't matter.)
- These hex numbers mean that the function can be dereferenced.
- Functions which aren't found will not have a hex number. For
- example:</para>
- <programlisting>
-
-? (external "functiondoesnotexist")
-#<EXTERNAL-ENTRY-POINT "functiondoesnotexist" {unresolved} #x638E3F6>
-</programlisting>
- <para>The "unresolved" tells us that OpenMCL wasn't able to find this
- function, which means you would get an error, "Can't resolve foreign
- symbol," if you tried to call it.</para>
- <para>These external function references also are stored in a
- hash table which is accessible through a global variable,
- <literal>ccl::*eeps*</literal>.</para>
- <para>At this point, we are ready to try our first external
- function call:</para>
- <programlisting>
-
-? (external-call "_void_void_test" :void)
-Entered void_void_test:
-Exited void_void_test:
-NIL
- </programlisting>
- <para>We used , which is is the normal mechanism for accessing
- externally linked code. The "_void_void_test" is the mangled
- name of the external function. The :void refers to the return
- type of the function.</para>
- <para>The next step is to try passing a value to C, and getting one
-back:</para>
- <programlisting>
-
-? (external-call "_sc_sc_test" :signed-byte -128 :signed-byte)
-Entered sc_sc_test:
-Data In: -128
-Exited sc_sc_test:
--128
-</programlisting>
- <para>The first :signed-byte gives the type of the first
- argument, and then -128 gives the value to pass for it. The
- second :signed-byte gives the return type. The return type is
- always given by the last argument to .</para>
- <para>Everything looks good. Now, let's try a number outside
- the range which fits in one byte:</para>
- <programlisting>
-
-? (external-call "_sc_sc_test" :signed-byte -567 :signed-byte)
-Entered sc_sc_test:
-Data In: -55
-Exited sc_sc_test:
--55
-</programlisting>
- <para>Hmmmm. A little odd. Let's look at the unsigned stuff to
- see how it reacts:</para>
- <programlisting>
-
-? (external-call "_uc_uc_test" :unsigned-byte 255 :unsigned-byte)
-Entered uc_uc_test:
-Data In: 255
-Exited uc_uc_test:
-255
-</programlisting>
- <para>That looks okay. Now, let's go outside the valid range again:=
</para>
- <programlisting>
-
-? (external-call "_uc_uc_test" :unsigned-byte 567 :unsigned-byte)
-Entered uc_uc_test:
-Data In: 55
-Exited uc_uc_test:
-55
-
-? (external-call "_uc_uc_test" :unsigned-byte -567 :unsigned-byte)
-Entered uc_uc_test:
-Data In: 201
-Exited uc_uc_test:
-201
- </programlisting>
- <para>Since a signed byte can only hold values from -128 through 127=
, and
- an unsigned one can only hold values from 0 through 255, any number
- outside that range gets "clipped": only the low eight bits of it
- are used.</para>
- <para>What is important to remember is that <emphasis>external
- function calls have
- very few safety checks.</emphasis>
- Data outside the valid range for its type will silently do
- very strange things; pointers outside the valid range can very well
- crash the system.</para>
- <para>That's it for our first example library. If you're still
- following along, let's add some more C code to look at the rest
- of the primitive types. Then we'll need to recompile the
- dynamic library, load it again, and then we can see what
- happens.</para>
- <para>Add the following code to typetest.c:</para>
- <programlisting>
-
-int
-si_si_test(int data)
-{
- printf("Entered %s:\n", __FUNCTION__);
- printf("Data In: %d\n", data);
- printf("Exited %s:\n", __FUNCTION__);
- fflush(stdout);
- return data;
-}
-
-long
-sl_sl_test(long data)
-{
- printf("Entered %s:\n", __FUNCTION__);
- printf("Data In: %ld\n", data);
- printf("Exited %s:\n", __FUNCTION__);
- fflush(stdout);
- return data;
-}
-
-long long
-sll_sll_test(long long data)
-{
- printf("Entered %s:\n", __FUNCTION__);
- printf("Data In: %lld\n", data);
- printf("Exited %s:\n", __FUNCTION__);
- fflush(stdout);
- return data;
-}
-
-float
-f_f_test(float data)
-{
- printf("Entered %s:\n", __FUNCTION__);
- printf("Data In: %e\n", data);
- printf("Exited %s:\n", __FUNCTION__);
- fflush(stdout);
- return data;
-}
-
-double
-d_d_test(double data)
-{
- printf("Entered %s:\n", __FUNCTION__);
- printf("Data In: %e\n", data);
- printf("Exited %s:\n", __FUNCTION__);
- fflush(stdout);
- return data;
-}
-</programlisting>
- <para>The command line to compile the dynamic library is the same as=
before:</para>
- <programlisting>
-
-gcc -dynamiclib -Wall -o libtypetest.dylib typetest.c \
- -install_name ./libtypetest.dylib
-</programlisting>
- <para>Now, restart OpenMCL. This step is required because
- OpenMCL cannot close and reload a dynamic library on OS
- X.</para>
- <para>Have you restarted? Okay, try out the new code:</para>
- <programlisting>
-
-Welcome to OpenMCL Version (Beta: Darwin) 0.14.2-040506!
-
-? (open-shared-library "/Users/andewl/openmcl/libtypetest.dylib")
-#<SHLIB /Users/andewl/openmcl/libtypetest.dylib #x638EF3E>
-
-? (external-call "_si_si_test" :signed-fullword -178965 :signed-fullword)
-Entered si_si_test:
-Data In: -178965
-Exited si_si_test:
--178965
-
-? ;; long is the same size as int on 32-bit machines.
-(external-call "_sl_sl_test" :signed-fullword -178965 :signed-fullword)
-Entered sl_sl_test:
-Data In: -178965
-Exited sl_sl_test:
--178965
-
-? (external-call "_sll_sll_test"
- :signed-doubleword -973891578912 :signed-doubleword)
-Entered sll_sll_test:
-Data In: -973891578912
-Exited sll_sll_test:
--973891578912
-</programlisting>
- <para>Okay, everything seems to be acting as expected. However,
- just to remind you that most of this stuff has no safety net,
- here's what happens if somebody mistakes
- <literal>sl_sl_test()</literal> for
- <literal>sll_sll_test()</literal>, thinking that a long is
- actually a doubleword:</para>
- <programlisting>
-
-? (external-call "_sl_sl_test"
- :signed-doubleword -973891578912 :signed-doubleword)
-Entered sl_sl_test:
-Data In: -227
-Exited sl_sl_test:
--974957576192
- </programlisting>
- <para>Ouch. The C function changes the value with no warning
- that something is wrong. Even worse, it manages to pass the
- original value back to OpenMCL, which hides the fact that
- something is wrong.</para>
- <para>Finally, let's take a look at doing this with
- floating-point numbers.</para>
- <programlisting>
-
-Welcome to OpenMCL Version (Beta: Darwin) 0.14.2-040506!
-
-? (open-shared-library "/Users/andewl/openmcl/libtypetest.dylib")
-#<SHLIB /Users/andewl/openmcl/libtypetest.dylib #x638EF3E>
-
-? (external-call "_f_f_test" :single-float -1.256791e+11 :single-float)
-Entered f_f_test:
-Data In: -1.256791e+11
-Exited f_f_test:
--1.256791E+11
-
-? (external-call "_d_d_test" :double-float -1.256791d+290 :double-float)
-Entered d_d_test:
-Data In: -1.256791e+290
-Exited d_d_test:
--1.256791D+290
- </programlisting>
- <para>Notice that the number ends with "...e+11" for the single-floa=
t,
-and "...d+290" for the
-double-float. Lisp has both of these float types itself, and the
-d instead of the e is how you specify which to create. If
-you tried to pass :double-float 1.0e2 to external-call, Lisp would
-be nice enough to notice and give you a type error. Don't get the
-:double-float wrong, though, because then there's no protection.</para>
- <para>Congratulations! You now know how to call external C function=
s from
-within OpenMCL, and pass numbers back and forth. Now that the basic
-mechanics of calling and passing work, the next step is to examine how
-to pass more complex data structures around.</para>
-
- <sect2 id=3D"Acknowledgement">
- <title>Acknowledgement</title>
- <para>This chapter was generously contributed by Andrew
- P. Lentvorski Jr.</para>
- </sect2>
- </sect1>
-
- <sect1 id=3D"Tutorial--Allocating-Foreign-Data-on-the-Lisp-Heap">
- <title>Tutorial: Allocating Foreign Data on the Lisp Heap </title>
- <para>Not every foreign function is so marvelously easy to use
- as the ones we saw in the last section. Some of them require
- you to allocate a C struct, fill it in with your own
- information, and pass it a pointer to the struct. Some of them
- require you to allocate an empty struct so they can fill it in,
- and then you can read the information out of it.</para>
- <para>Also, some of them have their own structs and return a
- pointer to that same struct every time you call them, but those
- are easier to deal with, so they won't be covered in this
- section.</para>
- <para>You might know that Lisp (and, indeed, most programming
- languages) has two separate regions of memory. There's the
- stack, which is where variable bindings are kept. Memory on the
- stack is allocated every time any function is called, and
- deallocated when it returns, so it's useful for anything that
- doesn't need to last longer than one function call, when there's
- only one thread. If that's all you need, you can do it with
- .</para>
- <para>Then, there's the heap, which holds everything else, and
- is our subject here. There are two advantages and one big
- disadvantage to putting things on the heap rather than the
- stack. First, data allocated on the heap can be passed outside
- of the scope in which it was created. This is useful for data
- which may need to be passed between multiple C calls or multiple
- threads. Also, some data may be too large to copy multiple times
- or may be too large to allocate on the stack.</para>
- <para>The second advantage is security. If incoming data is
- being placed directly onto the stack, the input data can cause
- stack overflows and underflows. This is not something which
- Lisp users generally worry about since garbage collection
- generally handles memory management. However, "stack smashing"
- is one of the classic exploits in C which malicious hackers can
- use to gain control of a machine. Not checking external data is
- always a bad idea; however, allocating it into the heap at least
- offers more protection than direct stack allocation.</para>
- <para>The big disadvantage to allocating data on the heap is
- that it must be explicitly deallocated - you need to "free" it
- when you're done with it. Ordinarily, in Lisp, you wouldn't
- allocate memory yourself, and the garbage collector would know
- about it, so you wouldn't have to think about it again. When
- you're doing it manually, it's very different. Memory
- management becomes a manual process, just like in C and
- C++.</para>
- <para>What that means is that, if you allocate something and
- then lose track of the pointer to it, there's no way to ever
- free that memory. That's what's called a memory leak, and if
- your program leaks enough memory it will eventually use up all
- of it! So, you need to be careful to not lose your
- pointers.</para>
- <para>That disadvantage, though, is also an advantage for using
- foreign functions. Since the garbage collector doesn't know
- about this memory, it will never move it around. External C
- code needs this, because it doesn't know how to follow it to
- where it moved, the way that Lisp code does. If you allocate
- data manually, you can pass it to foreign code and know that no
- matter what that code needs to do with it, it will be able to,
- until you deallocated it. Of course, you'd better be sure it's
- done before you do. Otherwise, your program will be unstable
- and might crash sometime in the future, and you'll have trouble
- figuring out what caused the trouble, because there won't be
- anything pointing back and saying "you deallocated this too
- soon."</para>
- <para>And, so, on to the code...</para>
- <para>As in the last tutorial, our first step
- is to create a local dynamic library in order to help show
- what is actually going on between OpenMCL and C. So, create the file
- ptrtest.c, with the following code:</para>
- <programlisting>
-#include <stdio.h>
-
-void reverse_int_array(int * data, unsigned int dataobjs)
-{
- int i, t;
-
- for(i=3D0; i<dataobjs/2; i++)
- {
- t =3D *(data+i);
- *(data+i) =3D *(data+dataobjs-1-i);
- *(data+dataobjs-1-i) =3D t;
- }
-}
-
-void reverse_int_ptr_array(int **ptrs, unsigned int ptrobjs)
-{
- int *t;
- int i;
-
- for(i=3D0; i<ptrobjs/2; i++)
- {
- t =3D *(ptrs+i);
- *(ptrs+i) =3D *(ptrs+ptrobjs-1-i);
- *(ptrs+ptrobjs-1-i) =3D t;
- }
-}
-
-void
-reverse_int_ptr_ptrtest(int **ptrs)
-{
- reverse_int_ptr_array(ptrs, 2);
-
- reverse_int_array(*(ptrs+0), 4);
- reverse_int_array(*(ptrs+1), 4);
-}
- </programlisting>
- <para>This defines three functions.
- <literal>reverse_int_array</literal> takes a pointer to an array
- of <literal>int</literal>s, and a count telling how many items
- are in the array, and loops through it putting the elements in
- reverse. <literal>reverse_int_ptr_array</literal> does the same
- thing, but with an array of pointers to <literal>int</literal>s.
- It only reverses the order the pointers are in; each pointer
- still points to the same thing.
- <literal>reverse_int_ptr_ptrtest</literal> takes an array of
- pointers to arrays of <literal>int</literal>s. (With me?) It
- doesn't need to be told their sizes; it just assumes that the
- array of pointers has two items, and that both of those are
- arrays which have four items. It reverses the array of
- pointers, then it reverses each of the two arrays of
- <literal>int</literal>s.</para>
- <para>Now, compile ptrtest.c into a dynamic library using the
- command:</para>
- <programlisting>
-gcc -dynamiclib -Wall -o libptrtest.dylib ptrtest.c -install_name ./libptr=
test.dylib
- </programlisting>
- <para>If that command doesn't make sense to you, feel free to go back
-and read about it at .</para>
- <para>Now, start OpenMCL and enter:</para>
- <programlisting>
-? ;; make-heap-ivector courtesy of Gary Byers
-(defun make-heap-ivector (element-count element-type)
- (let* ((subtag (ccl::element-type-subtype element-type)))
- (unless (=3D (logand subtag target::fulltagmask)
- target::fulltag-immheader)
- (error "~s is not an ivector subtype." element-type))
- (let* ((size-in-bytes (ccl::subtag-bytes subtag element-count)))
- (ccl::%make-heap-ivector subtag size-in-bytes element-count))))
-MAKE-HEAP-IVECTOR
-
-? ;; dispose-heap-ivector created for symmetry
-(defmacro dispose-heap-ivector (a mp)
- `(progn
- (ccl::%dispose-heap-ivector ,a)
- ;; Demolish the arguments for safety
- (setf ,a nil)
- (setf ,mp nil)))
-DISPOSE-HEAP-IVECTOR
-</programlisting>
- <para>If you don't understand how those functions do what they do.
- That's okay; it gets into very fine detail which really doesn't
- matter, because you don't need to change them.</para>
- <para>The function <literal>make-heap-ivector</literal> is the
- primary tool for allocating objects in heap memory. It
- allocates a fixed-size OpenMCL object in heap memory. It
- returns both an array reference, which can be used directly from
- OpenMCL, and a <literal>macptr</literal>, which can be used to
- access the underlying memory directly. For example:</para>
- <programlisting>
-? ;; Create an array of 3 4-byte-long integers
-(multiple-value-bind (la lap)
- (make-heap-ivector 3 '(unsigned-byte 32))
- (setq a la)
- (setq ap lap))
-;Compiler warnings :
-; Undeclared free variable A, in an anonymous lambda form.
-; Undeclared free variable AP, in an anonymous lambda form.
-#<A Mac Pointer #x10217C>
-
-? a
-#(1396 2578 97862649)
-
-? ap
-#<A Mac Pointer #x10217C>
-</programlisting>
- <para>It's important to realize that the contents of the
- <literal>ivector</literal> we've just created haven't been
- initialized, so their values are unpredictable, and you should
- be sure not to read from them before you set them, to avoid
- confusing results.</para>
- <para>At this point, <literal>a</literal> references an object
- which works just like a normal array. You can refer to any item
- of it with the standard <literal>aref</literal> function, and
- set them by combining that with <literal>setf</literal>. As
- noted above, the <literal>ivector</literal>'s contents haven't
- been initialized, so that's the next order of business:</para>
- <programlisting>
-? a
-#(1396 2578 97862649)
-
-? (aref a 2)
-97862649
-
-? (setf (aref a 0) 3)
-3
-
-? (setf (aref a 1) 4)
-4
-
-? (setf (aref a 2) 5)
-5
-
-? a
-#(3 4 5)
-</programlisting>
- <para>In addition, the <literal>macptr</literal> allows direct
- access to the same memory:</para>
- <programlisting>
-? (setq *byte-length-of-long* 4)
-4
-
-? (%get-signed-long ap (* 2 *byte-length-of-long*))
-5
-
-? (%get-signed-long ap (* 0 *byte-length-of-long*))
-3
-
-? (setf (%get-signed-long ap (* 0 *byte-length-of-long*)) 6)
-6
-
-? (setf (%get-signed-long ap (* 2 *byte-length-of-long*)) 7)
-7
-
-? ;; Show that a actually got changed through ap
-a
-#(6 4 7)
- </programlisting>
- <para>So far, there is nothing about this object that could not
- be done much better with standard Lisp. However, the
- <literal>macptr</literal> can be used to pass this chunk of
- memory off to a C function. Let's use the C code to reverse the
- elements in the array:</para>
- <programlisting>
-? ;; Insert the full path to your copy of libptrtest.dylib
-(open-shared-library "/Users/andrewl/openmcl/openmcl/gtk/libptrtest.dylib")
-#<SHLIB /Users/andrewl/openmcl/openmcl/gtk/libptrtest.dylib #x639D1E6>
-
-? a
-#(6 4 7)
-
-? ap
-#<A Mac Pointer #x10217C>
-
-? (external-call "_reverse_int_array" :address ap :unsigned-int (length a)=
:address)
-#<A Mac Pointer #x10217C>
-
-? a
-#(7 4 6)
-
-? ap
-#<A Mac Pointer #x10217C>
-</programlisting>
- <para>The array gets passed correctly to the C function,
- <literal>reverse_int_array</literal>. The C function reverses
- the contents of the array in-place; that is, it doesn't make a
- new array, just keeps the same one and reverses what's in it.
- Finally, the C function passes control back to OpenMCL. Since
- the allocated array memory has been directly modifed, OpenMCL
- reflects those changes directly in the array as well.</para>
- <para>There is one final bit of housekeeping to deal with.
- Before moving on, the memory needs to be deallocated:</para>
- <programlisting>
-? ;; dispose-heap-ivector created for symmetry
-;; Macro repeated here for pedagogy
-(defmacro dispose-heap-ivector (a mp)
- `(progn
- (ccl::%dispose-heap-ivector ,a)
- ;; Demolish the arguments for safety
- (setf ,a nil)
- (setf ,mp nil)))
-DISPOSE-HEAP-IVECTOR
-
-? (dispose-heap-ivector a ap)
-NIL
-
-? a
-NIL
-
-? ap
-NIL
-</programlisting>
- <para>The <literal>dispose-heap-ivector</literal> macro actually
- deallocates the ivector, releasing its memory into the heap for
- something else to use. In addition, it makes sure that the
- variables which it was called with are set to nil, because
- otherwise they would still be referencing the memory of the
- ivector - which is no longer allocated, so that would be a bug.
- Making sure there are no other variables set to it is up to
- you.</para>
- <para>When do you call <literal>dispose-heap-ivector</literal>?
- Anytime after you know the ivector will never be used again, but
- no sooner. If you have a lot of ivectors, say, in a hash table,
- you need to make sure that when whatever you were doing with the
- hash table is done, those ivectors all get freed. Unless
- there's still something somewhere else which refers to them, of
- course! Exactly what strategy to take depends on the situation,
- so just try to keep things simple unless you know better.</para>
- <para>The simplest situation is when you have things set up so
- that a Lisp object "encapsulates" a pointer to foreign data,
- taking care of all the details of using it. In this case, you
- don't want those two things to have different lifetimes: You
- want to make sure your Lisp object exists as long as the foreign
- data does, and no longer; and you want to make sure the foreign
- data doesn't get deallocated while your Lisp object still refers
- to it.</para>
- <para>If you're willing to accept a few limitations, you can
- make this easy. First, you can't let foreign code keep a
- permanent pointer to the memory; it has to always finish what
- it's doing, then return, and not refer to that memory again.
- Second, you can't let any Lisp code that isn't part of your
- encapsulating "wrapper" refer to the pointer directly. Third,
- nothing, either foreign code or Lisp code, should explicitly
- deallocate the memory.</para>
- <para>If you can make sure all of these are true, you can at
- least ensure that the foreign pointer is deallocated when the
- encapsulating object is about to become garbage, by using
- OpenMCL's nonstandard "termination" mechanism, which is
- essentially the same as what Java and other languages call
- "finialization".</para>
- <para>Termination is a way of asking the garbage collector to
- let you know when it's about to destroy an object which isn't
- used anymore. Before destroying the object, it calls a function
- which you write, called a terminator.</para>
- <para>So, you can use termination to find out when a particular
- <literal>macptr</literal> is about to become garbage. That's
- not quite as helpful as it might seem: It's not exactly the same
- thing as knowing that the block of memory it points to is
- unreferenced. For example, there could be another
- <literal>macptr</literal> somewhere to the same block; or, if
- it's a struct, there could be a <literal>macptr</literal> to one
- of its fields. Most problematically, if the address of that
- memory has been passed to foreign code, it's sometimes hard to
- know whether that code has kept the pointer. Most foreign
- functions don't, but it's not hard to think of
- exceptions.</para>
- <para>You can use code such as this to make all this happen:</para>
- <programlisting>
-(defclass wrapper (whatever)
- ((element-type :initarg :element-type)
- (element-count :initarg :element-count)
- (ivector)
- (macptr)))
-
-(defmethod initialize-instance ((wrapper wrapper) &rest initargs)
- (declare (ignore initargs))
- (call-next-method)
- (ccl:terminate-when-unreachable wrapper)
- (with-slots (ivector macptr element-type element-count) wrapper
- (multiple-value-bind (new-ivector new-macptr)
- (make-heap-ivector element-count element-type)
- (setq ivector new-ivector
- macptr new-macptr))))
-
-(defmethod ccl:terminate ((wrapper wrapper))
- (with-slots (ivector macptr) wrapper
- (when ivector
- (dispose-heap-ivector ivector macptr)
- (setq ivector nil
- macptr nil))))
- </programlisting>
- <para>The <literal>ccl:terminate</literal> method will be called
- on some arbitrary thread sometime (hopefully soon) after the GC
- has decided that there are no strong references to an object
- which has been the argument of a
- <literal>ccl:terminate-when-unreachable</literal> call.</para>
- <para>If it makes sense to say that the foreign object should
- live as long as there's Lisp code that references it (through
- the encapsulating obect) and no longer, this is one way of doing
- that.</para>
- <para>Now we've covered passing basic types back and forth with
- C, and we've done the same with pointers. You may think this is
- all... but we've only done pointers to basic types. Join us
- next time for pointers... to pointers.</para>
-
- <sect2 id=3D"Acknowledgement--1-">
- <title>Acknowledgement</title>
- <para>Much of this chapter was generously contributed by
- Andrew P. Lentvorski Jr.</para>
- </sect2>
- </sect1>
-
- <sect1>
- <title>The Foreign-Function-Interface Dictionary</title>
- =
- <refentry id=3D"m_def-foreign-type">
- <indexterm zone=3D"m_def-foreign-type">
- <primary>def-foreign-type</primary>
- </indexterm>
-
- <refnamediv>
- <refname>DEF-FOREIGN-TYPE</refname>
- <refpurpose></refpurpose>
- <refclass>Macro</refclass>
- </refnamediv>
-
- <refsynopsisdiv>
- <synopsis>
- <function>def-foreign-type</function> name foreign-type-spec
- </synopsis>
- </refsynopsisdiv>
-
- <refsect1>
- <title>Values</title>
-
- <variablelist>
- <varlistentry>
- <term>name</term>
- =
- <listitem>
- <para>NIL or a keyword; the keyword may contain
- <link linkend=3D"Case-sensitivity-of-foreign-names-in-OpenMCL" >escaping=
constructs</link>.</para>
- </listitem>
- </varlistentry>
- =
- <varlistentry>
- <term>foreign-type-spec</term>
- =
- <listitem>
- <para>A foreign type specifier, whose syntax is (loosely)
- defined above.</para>
- </listitem>
- </varlistentry>
- </variablelist>
- </refsect1>
-
- <refsect1>
- <title>Description</title>
-
- <para>If name is non-NIL, defines name to be an alias for the
- foreign type specified by foreign-type-spec. If foreign-type-spec
- is a named structure or union type, additionally defines that
- structure or union type.</para>
- =
- <para>If name is NIL, foreign-type-spec must be a named foreign
- struct or union definition, in which case the foreign structure
- or
- union definition is put in effect.</para>
- =
- <para>Note that there are two separate namespaces for foreign
- type names, one for the names of ordinary types and one for
- the names of structs and unions. Which one
- <varname>name</varname> refers to depends on
- <varname>foreign-type-spec</varname> in the obvious manner.
- </para>
- </refsect1>
- </refentry>
-
- <refentry id=3D"m_make-record">
- <indexterm zone=3D"m_make-record">
- <primary>make-record</primary>
- </indexterm>
-
- <refnamediv>
- <refname>MAKE-RECORD</refname>
- <refpurpose></refpurpose>
- <refclass>Macro</refclass>
- </refnamediv>
-
- <refsynopsisdiv>
- <synopsis>
- <function>make-record</function> typespec
- &rest; initforms =3D> result
- </synopsis>
- </refsynopsisdiv>
-
- <refsect1>
- <title>Values</title>
-
- <variablelist>
- <varlistentry>
- <term>typespec</term>
-
- <listitem>
- <para>A foreign type specifier, or a keyword which is used
- as the name of a foreign struct or union.</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>initforms</term>
-
- <listitem>
- <para>If the type denoted by <varname>typespec</varname>
- is scalar, a single value appropriate for that type;
- otherwise, a list of alternating field names and
- values appropriate for the types of those fields.</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>result</term>
-
- <listitem>
- <para>
- A <type>macptr</type> which encapsulates the address of a
- newly-allocated record on the foreign heap.
- </para>
- </listitem>
- </varlistentry>
- </variablelist>
- </refsect1>
-
- <refsect1>
- <title>Description</title>
-
- <para>
- Expands into code which allocates and initalizes
- an instance of the type =
- denoted by <varname>typespec</varname>, on the foreign
- heap. The record is allocated using the C function
- <function>malloc</function>, and the user of
- <function>make-record</function> must explicitly call
- the C function <function>free</function> to deallocate the
- record, when it is no longer needed.
- </para>
-
- <para>
- If <varname>initforms</varname> is provided, its value
- or values are used in the initialization. When the type
- is a scalar, <varname>initforms</varname> is either a single
- value which can be coerced to that type, or no value, in which
- case binary 0 is used. When the type is a <type>struct</type>,
- <varname>initforms</varname> is a list, giving field names
- and the values for each. Each field is treated in the same way
- as a scalar is: If a value for it is given, it must be
- coerceable to the field's type; if not, binary 0 is used.
- </para>
-
- <para>
- When the type is an array, <varname>initforms</varname> may
- not be provided, because <function>make-record</function>
- cannot initialize its values. <function>make-record</function>
- is also unable to initialize fields of a <type>struct</type>
- which are themselves
- <type>struct</type>s. The user of
- <function>make-record</function> should set these values
- by another means.
- </para>
-
- <para>
- A possibly-significant limitation is that it must be possible to
- find the foreign type at the time the macro is expanded;
- <function>make-record</function> signals an error if this is
- not the case.
- </para>
- </refsect1>
-
- <refsect1>
- <title>Notes</title>
-
- <para>
- It is inconvenient that <function>make-record</function> is a
- macro, because this means that <varname>typespec</varname>
- cannot be a variable; it must be an immediate value.
- </para>
- =
- <para>
- If it weren't for this requirement,
- <function>make-record</function> could be a function. However,
- that would mean that any stand-alone application using it would
- have to include a copy of the interface database
- (see <xref linkend=3D"The-Interface-Database"/>), which is undesireab=
le
- because it's large.
- </para>
- </refsect1>
- </refentry>
-
- <refentry id=3D"m_rlet">
- <indexterm zone=3D"m_rlet">
- <primary>rlet</primary>
- </indexterm>
-
- <refnamediv>
- <refname>RLET</refname>
- <refpurpose></refpurpose>
- <refclass>Macro</refclass>
- </refnamediv>
-
- <refsynopsisdiv>
- <synopsis>
- <function>rlet</function> (var typespec &rest; initforms)*
- &body; body
- </synopsis>
- </refsynopsisdiv>
-
- <refsect1>
- <title>Values</title>
-
- <variablelist>
- <varlistentry>
- <term>var</term>
-
- <listitem>
- <para>A symbol (a lisp variable)</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>typespec</term>
-
- <listitem>
- <para>A foreign type specifier or foreign record name.</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>initforms</term>
-
- <listitem>
- <para>As described above, for
- <xref linkend=3D"m_make-record"/></para>
- </listitem>
- </varlistentry>
- </variablelist>
- </refsect1>
-
- <refsect1>
- <title>Description</title>
-
- <para>Executes <varname>body</varname>
- in an environment in which each var is bound
- to <link linkend=3D"Referencing-and-Using-Foreign-Memory-Addresses">a M=
ACPTR</link> encapsulating the
- address of a stack-allocated foreign memory block, allocated and
- initialized from typespec and initforms as per
- <xref linkend=3D"m_make-record"/>.
- Returns whatever value(s) <varname>body</varname>
- returns.</para>
- =
- <para>Record fields that aren't explicitly initialized have
- unspecified contents.</para>
- </refsect1>
- </refentry>
-
- <refentry id=3D"m_rletz">
- <indexterm zone=3D"m_rletz">
- <primary>rletz</primary>
- </indexterm>
-
- <refnamediv>
- <refname>RLETZ</refname>
- <refpurpose></refpurpose>
- <refclass>Macro</refclass>
- </refnamediv>
-
- <refsynopsisdiv>
- <synopsis>
- <function>rletz</function> (var typespec &rest; initforms)*
- &body; body
- </synopsis>
- </refsynopsisdiv>
-
- <refsect1>
- <title>Values</title>
-
- <variablelist>
- <varlistentry>
- <term>var</term>
-
- <listitem>
- <para>A symbol (a lisp variable)</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>typespec</term>
-
- <listitem>
- <para>A foreign type specifier or foreign record name.</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>initforms</term>
-
- <listitem>
- <para>As described above, for ccl:make-record</para>
- </listitem>
- </varlistentry>
- </variablelist>
- </refsect1>
-
- <refsect1>
- <title>Description</title>
-
- <para>Executes body in an environment in which each var is
- bound to <link
- linkend=3D"Referencing-and-Using-Foreign-Memory-Addresses">a
- MACPTR</link> encapuslating the address of a stack-allocated
- foreign memory block, allocated and initialized from
- typespec and initforms as ccl:make-record.</para>
- =
- <para>Returns whatever value(s) body returns.</para>
-
- <para>Unlike rlet, record fields that aren't explicitly
- initialized are set to binary 0.</para>
- </refsect1>
- </refentry>
-
- <refentry id=3D"m_pref">
- <indexterm zone=3D"m_pref">
- <primary>pref</primary>
- </indexterm>
-
- <refnamediv>
- <refname>PREF</refname>
- <refpurpose></refpurpose>
- <refclass>Macro</refclass>
- </refnamediv>
-
- <refsynopsisdiv>
- <synopsis>
- <function>pref</function> ptr accessor-form
- </synopsis>
-
- </refsynopsisdiv>
-
- <refsect1>
- <title>Values</title>
-
- <variablelist>
- <varlistentry>
- <term>ptr</term>
-
- <listitem>
- <para><link linkend=3D"Referencing-and-Using-Foreign-Memory-Addresses">a=
MACPTR</link>.</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>accessor-form</term>
-
- <listitem>
- <para>a keyword which names a foreign type or record, as
- described in <xref linkend=3D"Foreign-type--record--and-field-names"/>.
- </para>
- </listitem>
- </varlistentry>
- </variablelist>
- </refsect1>
-
- <refsect1>
- <title>Description</title>
-
- <para>References an instance of a foreign type (or a component of
- a foreign type) accessible via ptr.</para>
- =
- <para>Expands into code which references the indicated scalar type
- or component, or returns a pointer to a composite type.</para>
- =
- <para>PREF can be used with SETF.</para>
- =
- <para>RREF is a deprecated alternative to PREF. It accepts a
- :STORAGE keyword and rather loudly ignores it.</para>
- </refsect1>
- </refentry>
-
- <refentry id=3D"f_open-shared-library">
- <indexterm zone=3D"f_open-shared-library">
- <primary>open-shared-library</primary>
- </indexterm>
- =
- <refnamediv>
- <refname>OPEN-SHARED-LIBRARY</refname>
- <refpurpose>Asks the operating system to load a shared library
- for OpenMCL to use.</refpurpose>
- <refclass>Function</refclass>
- </refnamediv>
- =
- <refsynopsisdiv>
- <synopsis>
- <function>open-shared-library</function> name =3D> library
- </synopsis>
- </refsynopsisdiv>
- =
- <refsect1>
- <title>Values</title>
- =
- <variablelist>
- <varlistentry>
- <term>name</term> =
- <listitem>
- <para>A SIMPLE-STRING which is presumed to be the so-name of
- or a filesystem path to the library.</para>
- </listitem>
- </varlistentry>
- =
- <varlistentry>
- <term>library</term>
- <listitem>
- <para>An object of type SHLIB which describes the
- library denoted by <varname>name</varname>.</para>
- </listitem>
- </varlistentry>
- </variablelist>
- </refsect1>
- =
- <refsect1>
- <title>Description</title>
-
- <para>If the library denoted by <varname>name</varname> can
- be loaded by the
- operating system, returns an object of type SHLIB that describes
- the library; if the library is already open, increments a
- reference count. If the library can't be loaded, signals a
- SIMPLE-ERROR which contains an often-cryptic message from the
- operating system.</para>
- </refsect1>
-
- <refsect1>
- <title>Examples</title>
-
- <programlisting format=3D"linespecific">;;; Try to do something simple.
-? (open-shared-library "libgtk.so")
-> Error: Error opening shared library "libgtk.so": /usr/lib/li=
bgtk.so: undefined symbol: gdk_threads_mutex
-> While executing: OPEN-SHARED-LIBRARY
-
-;;; Grovel around, curse, and try to find out where "gdk_threads_mutex=
"
-;;; might be defined. Then try again:
-
-? (open-shared-library "libgdk.so")
-#<SHLIB libgdk.so #x3046DBB6>
-
-? (open-shared-library "libgtk.so")
-#<SHLIB libgtk.so #x3046DC86>
-
-;;; Reference an external symbol defined in one of those libraries.
-
-? (external "gtk_main")
-#<EXTERNAL-ENTRY-POINT "gtk_main" (#x012C3004) libgtk.so #x304=
6FE46>
-
-;;; Close those libraries.
-
-? (close-shared-library "libgtk.so")
-T
-
-? (close-shared-library "libgdk.so")
-T
-
-;;; Reference the external symbol again.
-
-? (external "gtk_main")
-#<EXTERNAL-ENTRY-POINT "gtk_main" {unresolved} libgtk.so #x304=
6FE46></programlisting>
- </refsect1>
-
- <refsect1>
- <title>Notes</title>
-
- <para>It would be helpful to describe what an soname is and give
- examples of one.</para>
-
- <para>Does the SHLIB still get returned if the library is
- already open?</para>
- </refsect1>
- </refentry>
-
- <refentry id=3D"f_close-shared-library">
- <indexterm zone=3D"f_close-shared-library">
- <primary>close-shared-library</primary>
- </indexterm>
-
- <refnamediv>
- <refname>CLOSE-SHARED-LIBRARY</refname>
- <refpurpose>Stops using a shared library, informing the operating
- system that it can be unloaded if appropriate.</refpurpose>
- <refclass>Function</refclass>
- </refnamediv>
-
- <refsynopsisdiv>
- <synopsis>
- <function>close-shared-library</function> library &key;
- completely</synopsis>
- </refsynopsisdiv>
-
- <refsect1>
- <title>Values</title>
-
- <variablelist>
- <varlistentry>
- <term>library</term>
-
- <listitem>
- <para>either an object of type SHLIB, or a string which
- designates one by its so-name.</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>completely</term>
-
- <listitem>
- <para>a boolean. The default is T.</para>
- </listitem>
- </varlistentry>
- </variablelist>
- </refsect1>
-
- <refsect1>
- <title>Description</title>
-
- <para>If <varname>completely</varname> is T, sets the
- reference count of <varname>library</varname> to 0. Otherwise,
- decrements it by 1. In either case, if the reference count
- becomes 0, <function>close-shared-library</function>
- frees all memory resources consumed <varname>library</varname>
- and
- causes any EXTERNAL-ENTRY-POINTs known to be defined by it to
- become unresolved.</para>
- </refsect1>
- </refentry>
-
- <refentry id=3D"m_external">
- <indexterm zone=3D"m_external">
- <primary>external</primary>
- </indexterm>
- =
- <refnamediv>
- <refname>EXTERNAL</refname>
- <refpurpose>Resolves a reference to an external symbol which
- is defined in a shared library.</refpurpose>
- <refclass>Macro</refclass>
- </refnamediv>
-
- <refsynopsisdiv>
- <synopsis>
- <function>external</function> name =3D> entry
- </synopsis>
- </refsynopsisdiv>
-
- <refsect1>
- <title>Values</title>
-
- <variablelist>
- <varlistentry>
- <term>name</term>
- <listitem>
- <para>
- a simple-string which names an external symbol.
- Case-sensitive.
- </para>
- </listitem>
- </varlistentry>
- <varlistentry>
- <term>entry</term>
- <listitem>
- <para>
- an object of type EXTERNAL-ENTRY-POINT which maintains
- the address of the foreign symbol named by
- <varname>name</varname>.
- </para>
- </listitem>
- </varlistentry>
- </variablelist>
- </refsect1>
-
- <refsect1>
- <title>Description</title>
-
- <para>If there is already an EXTERNAL-ENTRY-POINT for
- the symbol named by <varname>name</varname>, finds it and
- returns it. If not, creates one and returns it.</para>
-
- <para>Tries to resolve the entry point to a memory address,
- and identify the containing library.</para>
-
- <para>Be aware that under Darwin, external functions which
- are callable from C have underscores prepended to their names,
- as in "_fopen".</para>
- </refsect1>
- </refentry>
-
- <refentry id=3D"f_Pff-call">
- <indexterm zone=3D"f_Pff-call">
- <primary>%ff-call</primary>
- </indexterm>
-
- <refnamediv>
- <refname>%FF-CALL</refname>
- <refpurpose></refpurpose>
- <refclass>Function</refclass>
- </refnamediv>
-
- <refsynopsisdiv>
- <synopsis>
- <function>%ff-call</function> entrypoint
- {arg-type-keyword arg}* &optional; result-type-keyword
- </synopsis>
- </refsynopsisdiv>
-
- <refsect1>
- <title>Values</title>
-
- <variablelist>
- <varlistentry>
- <term>entrypoint</term>
- =
- <listitem>
- <para>A fixnum or MACPTR</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>arg-type-keyword</term>
-
- <listitem>
- <para>One of the foreign argument-type keywords, described
- above</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>arg</term>
-
- <listitem>
- <para>A lisp value of type indicated by the corresponding
- arg-type-keyword</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>result-type-keyword</term>
-
- <listitem>
- <para>One of the foreign argument-type keywords, described
- above</para>
- </listitem>
- </varlistentry>
- </variablelist>
- </refsect1>
-
- <refsect1>
- <title>Description</title>
-
- <para>Calls the foreign function at address entrypoint passing the
- values of each arg as a foreign argument of type indicated by the
- corresponding arg-type-keyword. Returns the foreign function
- result (coerced to a Lisp object of type indicated by
- result-type-keyword), or NIL if result-type-keyword is :VOID or
- NIL</para>
- </refsect1>
- </refentry>
-
- <refentry id=3D"m_ff-call">
- <indexterm zone=3D"m_ff-call">
- <primary>ff-call</primary>
- </indexterm>
-
- <refnamediv>
- <refname>FF-CALL</refname>
- <refpurpose></refpurpose>
- <refclass>Macro</refclass>
- </refnamediv>
-
- <refsynopsisdiv>
- <synopsis>
- <function>ff-call</function> entrypoint
- {arg-type-specifier arg}* &optional; result-type-specifier
- </synopsis>
- </refsynopsisdiv>
-
- <refsect1>
- <title>Values</title>
-
- <variablelist>
- <varlistentry>
- <term>entrypoint</term>
-
- <listitem>
- <para>A fixnum or MACPTR</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>arg-type-specifer</term>
-
- <listitem>
- <para>One of the foreign argument-type keywords, described
- above, or an equivalent <link linkend=3D"Specifying-And-Using-Foreign-Ty=
pes">foreign
- type specifier</link>.</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>arg</term>
-
- <listitem>
- <para>A lisp value of type indicated by the corresponding
- arg-type-specifier</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>result-type-specifier</term>
-
- <listitem>
- <para>One of the foreign argument-type keywords, described
- above, or an equivalent <link linkend=3D"Specifying-And-Using-Foreign-Ty=
pes">foreign
- type specifier</link>.</para>
- </listitem>
- </varlistentry>
- </variablelist>
- </refsect1>
-
- <refsect1>
- <title>Description</title>
-
- <para>Calls the foreign function at address entrypoint passing the
- values of each arg as a foreign argument of type indicated by the
- corresponding arg-type-specifier. Returns the foreign function
- result (coerced to a Lisp object of type indicated by
- result-type-specifier), or NIL if result-type-specifer is :VOID or
- NIL</para>
- </refsect1>
- </refentry>
-
- <refentry id=3D"f_Preference-external-entry-point">
- <indexterm zone=3D"f_Preference-external-entry-point">
- <primary>%reference-external-entry-point</primary>
- </indexterm>
-
- <refnamediv>
- <refname>%REFERENCE-EXTERNAL-ENTRY-POINT</refname>
- <refpurpose></refpurpose>
- <refclass>Function</refclass>
- </refnamediv>
-
- <refsynopsisdiv>
- <synopsis>
- <function>%reference-external-entry-point</function> eep
- </synopsis>
- </refsynopsisdiv>
-
- <refsect1>
- <title>Values</title>
-
- <variablelist>
- <varlistentry>
- <term>eep</term>
-
- <listitem>
- <para>An EXTERNAL-ENTRY-POINT, as obtained by the EXTERNAL
- macro.</para>
- </listitem>
- </varlistentry>
- </variablelist>
- </refsect1>
-
- <refsect1>
- <title>Description</title>
-
- <para>Tries to resolve the address of the EXTERNAL-ENTRY-POINT
- eep; returns a fixnum representation of that address if
- successful, else signals an error.</para>
- </refsect1>
- </refentry>
-
- <refentry id=3D"m_external-call">
- <indexterm zone=3D"m_external-call">
- <primary>external-call</primary>
- </indexterm>
-
- <refnamediv>
- <refname>EXTERNAL-CALL</refname>
- <refpurpose></refpurpose>
- <refclass>Macro</refclass>
- </refnamediv>
-
- <refsynopsisdiv>
- <synopsis>
- <function>external-call</function> name
- {arg-type-specifier arg}* &optional; result-type-specifier
- </synopsis>
- </refsynopsisdiv>
-
- <refsect1>
- <title>Values</title>
-
- <variablelist>
- <varlistentry>
- <term>name</term>
-
- <listitem>
- <para>A lisp string. See external, above.</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>arg-type-specifer</term>
-
- <listitem>
- <para>One of the foreign argument-type keywords, described
- above, or an equivalent <link linkend=3D"Specifying-And-Using-Foreign-Ty=
pes">foreign
- type specifier</link>.</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>arg</term>
-
- <listitem>
- <para>A lisp value of type indicated by the corresponding
- arg-type-specifier</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>result-type-specifier</term>
-
- <listitem>
- <para>One of the foreign argument-type keywords, described
- above, or an equivalent <link linkend=3D"Specifying-And-Using-Foreign-Ty=
pes">foreign
- type specifier</link>.</para>
- </listitem>
- </varlistentry>
- </variablelist>
- </refsect1>
-
- <refsect1>
- <title>Description</title>
-
- <para>Calls the foreign function at the address obtained by
- resolving the external-entry-point associated with name, passing
- the values of each arg as a foreign argument of type indicated by
- the corresponding arg-type-specifier. Returns the foreign function
- result (coerced to a Lisp object of type indicated by
- result-type-specifier), or NIL if result-type-specifer is :VOID or
- NIL</para>
- </refsect1>
- </refentry>
-
- <refentry id=3D"f_foreign-symbol-entry">
- <indexterm zone=3D"f_foreign-symbol-entry">
- <primary>foreign-symbol-entry</primary>
- </indexterm>
-
- <refnamediv>
- <refname>FOREIGN-SYMBOL-ENTRY</refname>
- <refpurpose></refpurpose>
- <refclass>Function</refclass>
- </refnamediv>
-
- <refsynopsisdiv>
- <synopsis>
- <function>foreign-symbol-entry</function> name
- </synopsis>
- </refsynopsisdiv>
-
- <refsect1>
- <title>Values</title>
-
- <variablelist>
- <varlistentry>
- <term>name</term>
-
- <listitem>
- <para>A lisp string.</para>
- </listitem>
- </varlistentry>
- </variablelist>
- </refsect1>
-
- <refsect1>
- <title>Description</title>
-
- <para>Tries to resolve the address of the foreign symbol name. If
- successful, returns a fixnum representation of that address, else
- returns NIL.</para>
- </refsect1>
- </refentry>
-
- <refentry id=3D"f_foreign-symbol-address">
- <indexterm zone=3D"f_foreign-symbol-address">
- <primary>foreign-symbol-address</primary>
- </indexterm>
-
- <refnamediv>
- <refname>FOREIGN-SYMBOL-ADDRESS</refname>
- <refpurpose></refpurpose>
- <refclass>Function</refclass>
- </refnamediv>
-
- <refsynopsisdiv>
- <synopsis>
- <function>foreign-symbol-address</function> name
- </synopsis>
- </refsynopsisdiv>
-
- <refsect1>
- <title>Values</title>
-
- <variablelist>
- <varlistentry>
- <term>name</term>
-
- <listitem>
- <para>A lisp string.</para>
- </listitem>
- </varlistentry>
- </variablelist>
- </refsect1>
-
- <refsect1>
- <title>Description</title>
-
- <para>Tries to resolve the address of the foreign symbol
- name. If successful, returns that address encapsulated in
- <link
- linkend=3D"Referencing-and-Using-Foreign-Memory-Addresses">a
- MACPTR</link>, else returns NIL.</para>
- </refsect1>
- </refentry>
-
- <refentry id=3D"m_defcallback">
- <indexterm zone=3D"m_defcallback">
- <primary>defcallback</primary>
- </indexterm>
-
- <refnamediv>
- <refname>DEFCALLBACK</refname>
- <refpurpose></refpurpose>
- <refclass>Macro</refclass>
- </refnamediv>
-
- <refsynopsisdiv>
- <synopsis>
- <function>defcallback</function> name
- ({arg-type-specifier var}* &optional; result-type-specifier)
- &body; body
- </synopsis>
- </refsynopsisdiv>
-
- <refsect1>
- <title>Values</title>
-
- <variablelist>
- <varlistentry>
- <term>name</term>
-
- <listitem>
- <para>A symbol which can be made into a special variable</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>arg-type-specifer</term>
-
- <listitem>
- <para>One of the foreign argument-type keywords,
- described above, or an equivalent <link
- linkend=3D"Specifying-And-Using-Foreign-Types">foreign
- type specifier</link>. In addition, if the keyword
- :WITHOUT-INTERRUPTS is specified, the callback will be
- executed with lisp interrupts disabled if the
- corresponding var is non-NIL. If :WITHOUT-INTERRUPTS
- is specified more than once, the rightmost instance
- wins.</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>var</term>
-
- <listitem>
- <para>A symbol (lisp variable), which will be bound to a
- value of the specified type.</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>body</term>
-
- <listitem>
- <para>A sequence of lisp forms, which should return a value
- which can be coerced to the specified result-type.</para>
- </listitem>
- </varlistentry>
- </variablelist>
- </refsect1>
-
- <refsect1>
- <title>Description</title>
-
- <para>Proclaims <varname>name</varname>
- to be a special variable; sets its value to a
- MACPTR which, when called by foreign code, calls a lisp function
- which expects foreign arguments of the specified types and which
- returns a foreign value of the specified result type. Any argument
- variables which correspond to foreign arguments of type :ADDRESS
- are bound to stack-allocated MACPTRs.</para>
- =
- <para>If <varname>name</varname>
- is already a callback function pointer, its value is
- not changed; instead, it's arranged
- that an
- updated version of the lisp callback function will be called.
- This feature allows for callback functions to be redefined
- incrementally, just like Lisp functions are.</para>
-
- <para><function>defcallback</function>
- returns the callback pointer, e.g., the
- value of <varname>name</varname>.</para>
- </refsect1>
- </refentry>
-
- <refentry id=3D"rm_sharpsign-underscore">
- <indexterm zone=3D"rm_sharpsign-underscore">
- <primary>#_</primary>
- </indexterm>
-
- <refnamediv>
- <refname>#_</refname>
- <refpurpose></refpurpose>
- <refclass>Reader Macro</refclass>
- </refnamediv>
-
- <refsect1>
- <title>Description</title>
-
- <para>Reads a symbol from the current input stream, with *PACKAGE*
- bound to the "OS" package and with readtable-case preserved.</p=
ara>
- =
- <para>Does a lookup on that symbol in <link
- linkend=3D"The-Interface-Database">the OpenMCL interface
- database</link>, signalling an error if no foreign function
- information can be found for the symbol in any active <link
- linkend=3D"Using-Interface-Directories">interface
- directory</link>.</para>
-
- <para>Notes the foreign function information, including the foreign
- function's return type, the number and type of the foreign
- function's required arguments, and an indication of whether or
- not the function accepts additional arguments (via e.g., the
- "varargs" mechanism in C).</para>
-
- <para>Defines a macroexpansion function on the symbol, which expand
- macro calls involving the symbol into EXTERNAL-CALL forms where
- foreign argument type specifiers for required arguments and the
- return value specifer are provided from the information ind the
- database.</para>
-
- <para>Returns the symbol.</para>
-
- <para>The effect of these steps is that it's possible to call
- foreign functions that take fixed numbers of arguments by simply
- providing argument values, as in:</para>
-
- <programlisting format=3D"linespecific">(#_isatty fd)
-(#_read fd buf n)</programlisting>
-
- <para>and to call foreign functions that take variable numbers of
- arguments by specifying the types of non-required args, as in:</para>
-
- <programlisting format=3D"linespecific">(with-cstrs ((format-string =
4;the answer is: %d"))
- (#_printf format-string :int answer))</programlisting>
- </refsect1>
- </refentry>
-
- <refentry id=3D"rm_sharpsign-questionmark">
- <indexterm zone=3D"rm_sharpsign-questionmark">
- <primary>#?</primary>
- </indexterm>
-
- <refnamediv>
- <refname>#?</refname>
- <refpurpose></refpurpose>
- <refclass>Reader Macro</refclass>
- </refnamediv>
-
- <refsect1>
- <title>Description</title>
-
- <para>In OpenMCL 0.14.2 and later, the #? reader macro can be used to
- access foreign variables; this functionality depends on the presence of
- "vars.cdb" files in the interface database. The current behavior
- of the #? reader macro is to:</para>
-
- <para>Read a symbol from the current input stream, with *PACKAGE*
- bound to the "OS" package and with readtable-case preserved.</p=
ara>
- =
- <para>Use that symbol's pname to access the OpenMCL interface
- database, signalling an error if no appropriate foreign variable
- information can be found with that name in any active interface
- directory.</para>
-
- <para>Use type information recorded in the database to construct a
- form which can be used to access the foreign variable, and return
- that form.</para>
-
- <para>Please note that the set of foreign variables declared in header =
files
- may or may not match the set of foreign variables exported from
- libraries (we're generally talking about C and Unix here ...). When
- they do match, the form constructed by the #? reader macro manages the
- details of resolving and tracking changes to the foreign variable's
- address.</para>
-
- <para>Future extensions (via prefix arguments to the reader macro) may
- offer additional behavior; it might be convenient (for instance) to be
- able to access the address of a foreign variable without dereferencing
- that address.</para>
-
- <para>Foreign variables in C code tend to be platform- and
- packge-specific (the canonical example - "errno" - is typically
- not a variable when threads are involved. )</para>
-
- <para>In LinuxPPC, </para>
-
- <programlisting>? #?stderr</programlisting>
-
- <para>returns a pointer to the stdio error stream ("stderr" is a
- macro under OSX/Darwin).</para>
-
- <para>On both LinuxPPC and DarwinPPC, </para>
-
- <programlisting>? #?sys_errlist</programlisting>
-
- <para>returns a pointer to a C array of C error message strings.</para>
- </refsect1>
- </refentry>
-
- <refentry id=3D"f_use-interface-dir">
- <indexterm zone=3D"f_use-interface-dir">
- <primary>use-interface-dir</primary>
- </indexterm>
-
- <refnamediv>
- <refname>USE-INTERFACE-DIR</refname>
- <refpurpose></refpurpose>
- <refclass>Function</refclass>
- </refnamediv>
-
- <refsynopsisdiv>
- <synopsis>
- <function>use-interface-dir</function> dir-id
- </synopsis>
- </refsynopsisdiv>
-
- <refsect1>
- <title>Values</title>
-
- <variablelist>
- <varlistentry>
- <term>dir-id</term>
-
- <listitem>
- <para>A keyword whose pname, mapped to lower case, names a
- subdirectory of "ccl:headers;" (or
- "ccl:darwin-headers;")</para>
- </listitem>
- </varlistentry>
- </variablelist>
- </refsect1>
-
- <refsect1>
- <title>Description</title>
-
- <para>Tells OpenMCL to add the interface directory denoted by
- dir-id to the list of interface directories which it consults for
- foreign type and function information. Arranges that that
- directory is searched before any others.</para>
-
- <para>Note that <function>use-interface-dir</function>
- merely adds an entry
- to a search list.
- If the named directory doesn't exist in the file system
- or doesn't
- contain a set of database files, a runtime error may occur
- when OpenMCL
- tries to open some database file in that directory, and it
- will try to
- open such a database file whenever it needs to find any
- foreign type or
- function information. <xref linkend=3D"f_unuse-interface-dir"/>
- may come in
- handy in that case.</para>
- </refsect1>
-
- <refsect1>
- <title>Examples</title>
-
- <para>One typically wants interface information to be
- available at compile-time (or, in many cases, at read-time).
- A typical idiom would be:</para>
-
- <programlisting format=3D"linespecific">(eval-when (:compile-toplevel :=
execute)
- (use-interface-dir :GTK))</programlisting>
-
- <para>Using the :GTK interface directory makes available
- information on
- foreign types, functions, and constants. It's generally
- necessary to
- load foreign libraries before actually calling the
- foreign code, which for GTK can be done like this:</para>
-
- <programlisting>(load-gtk-libraries)</programlisting>
-
- <para>It should now be possible to do things like:</para>
-
- <programlisting>(#_gtk_widget_destroy w)</programlisting>
- </refsect1>
- </refentry>
-
- <refentry id=3D"f_unuse-interface-dir">
- <indexterm zone=3D"f_unuse-interface-dir">
- <primary>unuse-interface-dir</primary>
- </indexterm>
-
- <refnamediv>
- <refname>UNUSE-INTERFACE-DIR</refname>
- <refpurpose></refpurpose>
- <refclass>Function</refclass>
- </refnamediv>
-
- <refsynopsisdiv>
- <synopsis>
- <function>unuse-interface-dir</function> dir-id
- </synopsis>
- </refsynopsisdiv>
-
- <refsect1>
- <title>Values</title>
-
- <variablelist>
- <varlistentry>
- <term>dir-id</term>
-
- <listitem>
- <para>A keyword whose pname, mapped to lower case, names a
- subdirectory of "ccl:headers;" (or
- "ccl:darwin-headers;")</para>
- </listitem>
- </varlistentry>
- </variablelist>
- </refsect1>
-
- <refsect1>
- <title>Description</title>
-
- <para>Tells OpenMCL to remove the interface directory denoted by
- dir-id from the list of interface directories which are
- consulted for
- foreign type and function information. Returns T if the directory
- was on the search list, NIL otherwise.</para>
- </refsect1>
- </refentry>
-
- <refentry id=3D"f_terminate-when-unreachable">
- <indexterm zone=3D"f_terminate-when-unreachable">
- <primary>terminate-when-unreachable</primary>
- </indexterm>
-
- <refnamediv>
- <refname>TERMINATE-WHEN-UNREACHABLE</refname>
- <refpurpose></refpurpose>
- <refclass>Function</refclass>
- </refnamediv>
-
- <refsynopsisdiv>
- <synopsis>
- <function>terminate-when-unreachable</function> object
- </synopsis>
- </refsynopsisdiv>
-
- <refsect1>
- <title>Values</title>
-
- <variablelist>
- <varlistentry>
- <term>object</term>
-
- <listitem>
- <para>A CLOS object of a class for which there exists
- a method of the generic function
- <function>ccl:terminate</function>.
- </para>
- </listitem>
- </varlistentry>
- </variablelist>
- </refsect1>
-
- <refsect1>
- <title>Description</title>
-
- <para>
- The "termination" mechanism is a way to have the garbage
- collector run a function right before an object is about to
- become garbage. It is very similar to the "finalization"
- mechanism which Java has. It is not standard Common Lisp,
- although other Lisp implementations have similar features.
- It is useful when there is some sort of special cleanup,
- deallocation, or releasing of resources which needs to happen
- when a certain object is no longer being used.
- </para>
-
- <para>
- When the garbage collector discovers that an object is no
- longer referred to anywhere in the program, it deallocates
- that object, freeing its memory. However, if
- <function>ccl:terminate-when-unreachable</function> has been
- called on the object at any time, the garbage collector first
- invokes the generic function <function>ccl:terminate</function>,
- passing it the object as a parameter.
- </para>
-
- <para>
- Therefore, to make termination do something useful, you need to
- define a method on <function>ccl:terminate</function>.
- </para>
-
- <para>
- Because calling
- <function>ccl:terminate-when-unreachable</function> only
- affects a single object, rather than all objects of its
- class, you
- may wish to put a call to it in the
- <function>initialize-instance</function> method of a
- class. Of course, this is only appropriate if you do in fact
- want to use termination for all objects of a given class.
- </para>
- </refsect1>
-
- <refsect1>
- <title>Example</title>
-
- <programlisting format=3D"linespecific">
-(defclass resource-wrapper ()
- ((resource :accessor resource)))
-
-(defmethod initialize-instance :after ((x resource-wrapper) &rest init=
args)
- (ccl:terminate-when-unreachable x))
-
-(defmethod ccl:terminate ((x resource-wrapper))
- (when (resource x)
- (deallocate (resource x))))</programlisting>
- </refsect1>
-
- <refsect1>
- <title>See Also</title>
-
- <simplelist type=3D"inline">
- <member><xref linkend=3D"Tutorial--Allocating-Foreign-Data-on-the-Lis=
p-Heap"/></member>
- </simplelist>
- </refsect1>
-
- </refentry>
-
- </sect1>
- </chapter>
-
- <chapter id=3D"The-Objective-C-Bridge">
- <title>The Objective-C Bridge</title>
-
- <para>OS X APIs use a language called "Objective C", which is
- built on C. The Objective-C bridge makes it possible to work with
- ObjC objects and classes from Lisp, and to define classes in Lisp
- which can be used by ObjC.</para>
- <para>The ultimate purpose of the ObjC and Cocoa bridges is to
- make Cocoa as easy as possible to use from OpenMCL, in order to
- support the development of GUI applications and IDEs. The
- eventual goal, which is much closer than it used to be, is
- complete integration of Cocoa into CLOS (whatever that
- means).</para>
- <para>The current release provides Lisp-like syntax and naming
- conventions for the basic ObjC operations, with automatic type
- processing and messages checked for validity at compile-time. It
- also provides some convenience facilities for working with
- Cocoa.</para>
-
- <sect1 id=3D"Using-Objective-C-Classes">
- <title>Using Objective-C Classes</title>
-
- <para>The class of most "standard" CLOS classes is the class
- named STANDARD-CLASS. In the Objective-C object model, each
- class is an instance of a (usually unique) metaclass, which is
- itself an instance of a "base" metaclass (often the metaclass of
- the class named "NSObject".) So, the Objective-C class named
- "NSWindow" and the ObjC class "NSArray" are (sole) instances of
- their distinct metaclasses whose names are also "NSWindow" and
- "NSArray", respectively. (In the Objective-C world, it's much
- more common and useful to specialize class behavior such as
- instance allocation.)</para>
- <para>When foreign libraries containing Objective-C classes are first
-loaded, the classes they contain are identified. The foreign class
-name, such as "NSWindow", is mapped to an external symbol in the
-"NS" package via the bridge's translation rules, such as NS:NS-WINDOW.
-A similar transformation happens to the
-metaclass name, with a "+" prepended, yielding something like
-NS:+NS-WINDOW.</para>
- <para>These classes are integrated into CLOS such that the
-metaclass is an instance of the class OBJC:OBJC-METACLASS and
-the class
-is an instance of the metaclass. SLOT-DESCRIPTION metaobjects are
-created for each instance variable, and the class and metaclass go
-through something very similar to the "standard" CLOS class
-initialization protocol (with a difference being that these classes
-have already been allocated.)</para>
- <para>Performing all this initialization, which is done when you
-(require "COCOA"), currently takes several
-seconds; it could conceivably be sped up some, but it's never likely
-to be fast.</para>
- <para>When the process is complete, CLOS is aware of several hundred
-new ObjC classes and their metaclasses. OpenMCL's runtime system can
-reliably recognize MACPTRs to ObjC classes as being CLASS objects, and
-can (fairly reliably but heuristically) recognize instances of those
-classes (though there are complicating factors here; see below.)
-SLOT-VALUE can be used to access (and, with care, set) instance
-variables in ObjC instances. To see this, do:</para>
- <programlisting>
-? (require "COCOA")
-</programlisting>
- <para>and, after waiting a bit longer for a Cocoa listener window to
-appear, activate that Cocoa listener and do:</para>
- <programlisting>? (describe (ccl::send ccl::*NSApp* 'key-window))
-</programlisting>
- <para>This sends a message asking for the key window, which is the w=
indow
-that has the input focus (often the frontmost), and then describes
-it. As we can see, NS:NS-WINDOWs have lots of interesting slots.</para>
- </sect1>
-
- <sect1 id=3D"Instantiating-Objective-C-Objects">
- <title>Instantiating Objective-C Objects</title>
- <para>Making an instance of an ObjC class (whether the class in
- question is predefined or defined by the application) involves
- calling MAKE-INSTANCE with the class and a set of initargs as
- arguments. As with STANDARD-CLASS, making an instance involves
- initializing (with INITIALIZE-INSTANCE) an object allocated with
- ALLOCATE-INSTANCE.</para>
- <para>For example, you can create an ns:ns-number like this:</para>
- <programlisting>
-? (make-instance 'ns:ns-number :init-with-int 42)
-#<NS-CF-NUMBER 42 (#x85962210)>
-</programlisting>
- <para>It's worth looking at how this would be done if you were
- writing in Objective C:</para>
- <programlisting>
-[[NSNumber alloc] initWithInt: 42]
- </programlisting>
- <para>Allocating an instance of an ObjC class involves sending the
-class an "alloc" message, and then using those initargs that
-<emphasis>don't</emphasis> correspond to slot initags as the
-"init" message to be sent to the newly-allocated instance. So, the
-example above could have been done more verbosely as:</para>
- <programlisting>
-? (defvar *n* (ccl::send (find-class 'ns:ns-number) 'alloc))
-*N*
-
-? (setq *n* (ccl::send *n* :init-with-int 42))
-#<NS-CF-NUMBER 42 (#x16D340)>
-</programlisting>
- <para>That setq is important; this is a case where init
-decides to replace the object and return the new one, instead
-of modifying the existing one.
-In fact, if you leave out the setq and
-then try to view the value of *N*, OpenMCL will freeze. There's
-little reason to ever do it this way; this is just to show
-what's going on.</para>
- <para>You've seen that an ObjC initialization method doesn't have to
-return the same object it was passed. In fact, it doesn't have
-to return any object at all; in this case, the initialization fails
-and make-instance returns nil.</para>
- <para>In some special cases, such as loading an ns:ns-window-control=
ler
-from a .nib file, it may be necessary for you to pass the
-instance itself as one of the parameters to the initialization
-method. It goes like this:</para>
- <programlisting>
-? (defvar *controller*
- (make-instance 'ns:ns-window-controller))
-*CONTROLLER*
-
-? (setq *controller*
- (ccl::send *controller*
- :init-with-window-nib-name #@"DataWindow"
- :owner *controller*))
-#<NS-WINDOW-CONTROLLER <NSWindowController: 0x1fb520> (#x1FB520)>
-</programlisting>
- <para>This example calls (make-instance) with no initargs. When you
-do this, the object is only allocated, and not initialized. It
-then sends the "init" message to do the initialization by hand.</para>
- </sect1>
-
- <sect1 id=3D"Calling-Objective-C-Methods">
- <title>Calling Objective-C Methods</title>
- <para>In Objective-C, methods are called "messages", and there's
- a special syntax to send a message to an object:</para>
- <programlisting>
-[w alphaValue]
-[w setAlphaValue: 0.5]
-[v mouse: p inRect: r]
- </programlisting>
- <para>The first line sends the method "alphaValue" to the object
- <literal>w</literal>, with no parameters. The second line sends
- the method "setAlphaValue", with the parameter 0.5. The third
- line sends the method "mouse:inRect:" - yes, all one long word -
- with the parameters <literal>p</literal> and
- <literal>r</literal>.</para>
- <para>In Lisp, these same three lines are:</para>
- <programlisting>
-(send w 'alpha-value)
-(send w :set-alpha-value 0.5)
-(send v :mouse p :in-rect r)
-</programlisting>
- <para>Notice that when a method has no parameters, its name is an or=
dinary
-symbol (it doesn't matter what package the symbol is in, as
-only its name is checked). When a method has parameters,
-each part of its name is a keyword, and the keywords alternate
-with the values.</para>
- <para>These two lines break those rules, and both will
-result in error messages:</para>
- <programlisting>
-(send w :alpha-value)
-(send w 'set-alpha-value 0.5)
-</programlisting>
- <para>Instead of (send), you can also invoke (send-super), with the
-same interface. It has roughly the same purpose as CLOS's
-(call-next-method); when you use (send-super), the message is
-handled by the superclass. This can be used to get at the
-original implementation of a method when it is shadowed by a
-method in your subclass.</para>
-
- <sect2 id=3D"Type-Coercion-for-ObjC-Method-Calls">
- <title>Type Coercion for ObjC Method Calls</title>
- <para>OpenMCL's FFI handles many common conversions between
- Lisp and foreign data, such as unboxing floating-point args
- and boxing floating-point results. The bridge adds a few more
- automatic conversions:</para>
- <para>NIL is equivalent to (%NULL-PTR) for any message
- argument that requires a pointer.</para>
- <para>T/NIL are equivalent to #$YES/#$NO for any boolean argument.=
</para>
- <para>A #$YES/#$NO returned by any method that returns BOOL
- will be automatically converted to T/NIL.</para>
- </sect2>
-
- <sect2 id=3D"Methods-which-Return-Structures">
- <title>Methods which Return Structures</title>
- <para>Some Cocoa methods return small structures, such as
- those used to represent points, rects, sizes and ranges. When
- writing in Objective C, the compiler hides the implementation
- details. Unfortunately, in Lisp we must be slightly more
- aware of them.</para>
- <para>Methods which return structures are called in a special
- way; the caller allocates space for the result, and passes a
- pointer to it as an extra argument to the method. This is
- called a Structure Return, or STRET. Don't look at me; I
- don't name these things.</para>
- <para>Here's a simple use of this in Objective C. The first line
- sends the "bounds" message to v1, which returns a rectangle.
- The second line sends the "setBounds" message to v2, passing
- that same rectangle as a parameter.</para>
- <programlisting>
-NSRect r =3D [v1 bounds];
-[v2 setBounds r];
- </programlisting>
- <para>In Lisp, we must explicitly allocate the memory, which
- is done most easily and safely with <xref linkend=3D"m_rlet"/>.
- We do it like this:</para>
- <programlisting>
-(rlet ((r :<NSR>ect))
- (send/stret r v1 'bounds)
- (send v2 :set-bounds r))
-</programlisting>
- <para>The rlet allocates the storage (but doesn't initialize
- it), and makes sure that it will be deallocated when we're
- done. It binds the variable r to refer to it. The call to
- <literal>send/stret</literal> is just like an ordinary call to
- <literal>send</literal>, except that r is passed as an extra,
- first parameter. The third line, which calls
- <literal>send</literal>, does not need to do anything special,
- because there's nothing complicated about passing a structure
- as a parameter.</para>
- <para>In order to make STRETs easier to use, the bridge
- provides two conveniences.</para>
- <para>First, you can use the macros <literal>slet</literal>
- and <literal>slet*</literal> to allocate and initialize local
- variables to foreign structures in one step. The example
- above could have been written more tersely as:</para>
- <programlisting>
-(slet ((r (send v1 'bounds)))
- (send v2 :set-bounds r))
- </programlisting>
- <para>Second, when one call to <literal>send</literal> is made
-inside another, the inner one has an implicit
-<literal>slet</literal> around it. So, one could in fact
-just write:</para>
- <programlisting>
-(send v1 :set-bounds (send v2 'bounds))
-</programlisting>
- <para>There are also several psuedo-functions provided for conveni=
ence
-by the ObjC compiler, to make objects of specific types. The
-following are currently supported by the bridge: NS-MAKE-POINT,
-NS-MAKE-RANGE, NS-MAKE-RECT, and NS-MAKE-SIZE.</para>
- <para>These pseudo-functions can be used within an SLET initform:<=
/para>
- <programlisting>
-(slet ((p (ns-make-point 100.0 200.0)))
- (send w :set-frame-origin p))
-</programlisting>
- <para>Or within a call to <literal>send</literal>:</para>
- <programlisting>
-(send w :set-origin (ns-make-point 100.0 200.0))
-</programlisting>
- <para>However, since these aren't real functions, a call like the
-following won't work:</para>
- <programlisting>
-(setq p (ns-make-point 100.0 200.0))
-</programlisting>
- <para>To extract fields from these objects, there are also some
-convenience macros: NS-MAX-RANGE, NS-MIN-X,
-NS-MIN-Y, NS-MAX-X, NS-MAX-Y, NS-MID-X, NS-MID-Y,
-NS-HEIGHT, and NS-WIDTH.</para>
- <para>Note that there is also a <literal>send-super/stret</literal>
-for use within methods. Like <literal>send-super</literal>,
-it ignores any shadowing methods in a subclass, and calls the
-version of a method which belongs to its superclass.</para>
- </sect2>
-
- <sect2 id=3D"Variable-Arity-Messages">
- <title>Variable-Arity Messages</title>
- <para>
-There are a few messages in Cocoa which take variable numbers
-of arguments. Perhaps the most common examples involve
-formatted strings:</para>
- <programlisting>
-[NSClass stringWithFormat: "%f %f" x y]
-</programlisting>
- <para>In Lisp, this would be written:</para>
- <programlisting>
-(send (find-class 'ns:ns-string)
- :string-with-format #@"%f %f"
- (:double-float x :double-float y))
-</programlisting>
- <para>Note that it's necessary to specify the foreign types of the
-variables (in this example, :double-float), because the
-compiler has no general way of knowing these types. (You
-might think that it could parse the format string, but this
-would only work for format strings which are not determined
-at runtime.)</para>
- <para>Because the ObjC runtime system does not provide any informa=
tion
-on which messages are variable arity, they must be explicitly
-declared. The standard variable arity messages in Cocoa are
-predeclared by the bridge. If you need to declare a new
-variable arity message, use
-(DEFINE-VARIABLE-ARITY-MESSAGE "myVariableArityMessage:").</para>
- </sect2>
-
- <sect2 id=3D"Optimization">
- <title>Optimization</title>
- <para>The bridge works fairly hard to optimize message sends,
- when it has enough information to do so. There are two cases
- when it does. In either, a message send should be nearly as
- efficient as when writing in Objective C.</para>
- <para>The first case is when both the message and the
- receiver's class are known at compile-time. In general, the
- only way the receiver's class is known is if you declare it,
- which you can do with either a DECLARE or a THE form. For
- example:</para>
- <programlisting>
-(send (the ns:ns-window w) 'center)
- </programlisting>
- <para>Note that there is no way in ObjC to name the class of a
- class. Thus the bridge provides a declaration, @METACLASS.
- The type of an instance of "NSColor" is ns:ns-color. The type
- of the <emphasis>class</emphasis> "NSColor" is (@metaclass
- ns:ns-color):</para>
- <programlisting>
-(let ((c (find-class 'ns:ns-color)))
- (declare ((ccl::@metaclass ns:ns-color) c))
- (send c 'white-color))
-</programlisting>
- <para>The other case that alllows optimization is when only
- the message is known at compile-time, but its type signature
- is unique. Of the more-than-6000 messages currently provided
- by Cocoa, only about 50 of them have nonunique type
- signatures.</para>
- <para>An example of a message with a type signature that is
- not unique is SET. It returns VOID for NSColor, but ID for
- NSSet. In order to optimize sends of messages with nonunique
- type signatures, the class of the receiver must be declared at
- compile-time.</para>
- <para>If the type signature is nonunique or the message is
- unknown at compile-time, then a slower runtime call must be
- used.</para>
- <para>When the receiver's class is unknown, the bridge's
- ability to optimize relies on a type-signature table which it
- maintains. When first loaded, the bridge initializes this
- table by scanning every method of every ObjC class. When new
- methods are defined later, the table must be updated. This
- happens automatically when you define methods in Lisp. After
- any other major change, such as loading an external framework,
- you should rebuild the table:</para>
- <programlisting>
-? (update-type-signatures)
-</programlisting>
- <para>Because <literal>send</literal> and its relatives
- <literal>send-super</literal>, <literal>send/stret</literal>,
- and <literal>send-super/stret</literal> are macros, they
- cannot be <literal>funcall</literal>ed,
- <literal>apply</literal>ed, or passed as arguments to
- functions.</para>
- <para>To work around this, there are function equivalents to
- them: <literal>%send</literal>,
- <literal>%send-super</literal>,
- <literal>%send/stret</literal>, and
- <literal>%send-super/stret</literal>. However, these
- functions should be used only when the macros will not do,
- because they are unable to optimize.</para>
- </sect2>
- </sect1>
-
- <sect1 id=3D"Defining-Objective-C-Classes">
- <title>Defining Objective-C Classes</title>
- <para>You can define your own foreign classes, which can then be
- passed to foreign functions; the methods which you implement in
- Lisp will be made available to the foreign code as
- callbacks.</para>
- <para>You can also define subclasses of existing classes,
- implementing your subclass in Lisp even though the parent class
- was in Objective C. One such subclass is CCL::NS-LISP-STRING.
- It is also particularly useful to make subclasses of
- NS-WINDOW-CONTROLLER.</para>
- <para>We can use the MOP to define new Objective-C classes, but
- we have to do something a little funny: the :METACLASS that we'd
- want to use in a DEFCLASS option generally doesn't exist until
- we've created the class (recall that ObjC classes have, for the
- sake of argument, unique and private metaclasses.) We can sort
- of sleaze our way around this by specifying a known ObjC
- metaclass object name as the value of the DEFCLASS :METACLASS
- object; the metaclass of the root class NS:NS-OBJECT,
- NS:+NS-OBJECT, makes a good choice. To make a subclass of
- NS:NS-WINDOW (that, for simplicity's sake, doesn't define any
- new slots), we could do:</para>
- <programlisting>
-(defclass example-window (ns:ns-window)
- ()
- (:metaclass ns:+ns-object))
-</programlisting>
- <para>That'll create a new ObjC class named EXAMPLE-WINDOW whose
- metaclass is the class named +EXAMPLE-WINDOW. The class will be
- an object of type OBJC:OBJC-CLASS, and the metaclass will be of
- type OBJC:OBJC-METACLASS. EXAMPLE-WINDOW will be a subclass of
- NS-WINDOW.</para>
-
- <sect2 id=3D"Defining-classes-with-foreign-slots">
- <title>Defining classes with foreign slots</title>
- <para>If a slot specification in an Objective-C class
- definition contains the keyword :FOREIGN-TYPE, the slot will
- be a "foreign slot" (i.e. an ObjC instance variable). Be aware
- that it is an error to redefine an ObjC class so that its
- foreign slots change in any way, and OpenMCL doesn't do
- anything consistent when you try to.</para>
- <para>The value of the :FOREIGN-TYPE initarg should be a
- foreign type specifier. For example, if we wanted (for some
- reason) to define a subclass of NS:NS-WINDOW that kept track
- of the number of key events it had received (and needed an
- instance variable to keep that information in), we could
- say:</para>
- <programlisting>
-(defclass key-event-counting-window (ns:ns-window)
- ((key-event-count :foreign-type :int
- :initform 0
- :accessor window-key-event-count))
- (:metaclass ns:+ns-object))
-</programlisting>
- <para>Foreign slots are always SLOT-BOUNDP, and the initform
- above is redundant: foreign slots are initialized to binary
- 0.</para>
- </sect2>
-
- <sect2 id=3D"Defining-classes-with-Lisp-slots">
- <title>Defining classes with Lisp slots</title>
- <para>A slot specification in an ObjC class definition that
- doesn't contain the :FOREIGN-TYPE initarg defines a
- pretty-much normal lisp slot that'll happen to be associated
- with "an instance of a foreign class". For instance:</para>
- <programlisting>
-(defclass hemlock-buffer-string (ns:ns-string)
- ((hemlock-buffer :type hi::hemlock-buffer
- :initform hi::%make-hemlock-buffer
- :accessor string-hemlock-buffer))
- (:metaclass ns:+ns-object))
- </programlisting>
- <para>As one might expect, this has memory-management
- implications: we have to maintain an association between a
- MACPTR and a set of lisp objects (its slots) as long as the
- ObjC instance exists, and we have to ensure that the ObjC
- instance exists (does not have its -dealloc method called)
- while lisp is trying to think of it as a first-class object
- that can't be "deallocated" while it's still possible to
- reference it. Associating one or more lisp objects with a
- foreign instance is something that's often very useful; if you
- were to do this "by hand", you'd have to face many of the same
- memory-management issues.</para>
- </sect2>
- </sect1>
-
- <sect1 id=3D"Defining-Objective-C-Methods">
- <title>Defining Objective-C Methods</title>
- <para>In ObjC, unlike in CLOS, every method belongs to some
- particular class. This is probably not a strange concept to
- you, because C++ and Java do the same thing. When you use Lisp
- to define ObjC methods, it is only possible to define methods
- belonging to ObjC classes which have been defined in
- Lisp.</para>
- <para>The macro <literal>define-objc-method</literal> is used
- for this. As described in , the names of ObjC methods are
- broken into pieces, each piece followed by a parameter. The
- types of all parameters must be explicitly declared.</para>
- <para>Right now, I'm not sure how to formally describe the usage
-of define-objc-method, so I'm going to do it with some short
-examples. Let us define a class to use in them:</para>
- <programlisting>
-(defclass data-window-controller (ns:ns-window-controller)
- ((window :foreign-type :id :accessor window)
- (data :initform nil :accessor data))
- (:metaclass ns:+ns-object))
-</programlisting>
- <para>There's nothing special about this class. It inherits
- from ns:ns-window-controller. It has two slots:
- <literal>window</literal> is a foreign slot, stored in the ObjC
- world; and <literal>data</literal> is an ordinary slot, stored
- in the Lisp world.</para>
- <para>Here is an example of how to define a method which takes
- no arguments. It happens to be an initialization method, but
- that's not important:</para>
- <programlisting>
- (define-objc-method ((:id get-window)
- data-window-controller)
- (window self))
- </programlisting>
- <para>The return type of this method is the foreign type :id,
- which is used for all ObjC objects. The name of the method is
- <literal>get-window</literal>. The body of the method is the
- single line (window self). The variable <literal>self</literal>
- is bound, within the body, to the instance which is receiving
- the message. The call to <literal>window</literal> uses the
- CLOS accessor to get the value of the window field.</para>
- <para>Here's an example which takes a parameter. Notice that
- the name of the method without a parameter was an ordinary
- symbol, but with a parameter, it's a keyword:</para>
- <programlisting>
-(define-objc-method ((:id :init-with-multiplier (:int multiplier))
- data-window-controller)
- (setf (data self) (make-array 100))
- (dotimes (i 100)
- (setf (aref (data self) i)
- (* i multiplier)))
- self)
- </programlisting>
- <para>To Objective-C code which uses the class, the name of this
- method is "initWithMultiplier:". The name of the parameter is
- <literal>multiplier</literal>, and its type is :int. The body
- of the method does some meaningless things. Then it returns
- <literal>self</literal>, because this is an initialization
- method.</para>
- <para>Here's an example with more than one parameter:</para>
- <programlisting>
-(define-objc-method ((:id :init-with-multiplier (:int multiplier)
- :and-addend (:int addend))
- data-window-controller)
- (setf (data self) (make-array size))
- (dotimes (i 100)
- (setf (aref (data self) i)
- (+ (* i multiplier)
- addend)))
- self)
- </programlisting>
- <para>To Objective-C, the name of this method is
- "initWithMultiplier:andAddend:". Both parameters are of type
- :int; the first is named <literal>multiplier</literal>, and the
- second is <literal>addend</literal>. Again, the method returns
- <literal>self</literal>.</para>
- <para>Here is a method which does not return any value, a
- so-called "void method". Where our other methods said :id, this
- one says :void for the return type:</para>
- <programlisting>
-(define-objc-method ((:void :take-action (:id sender))
- data-window-controller)
- (declare (ignore sender))
- (dotimes (i 100)
- (setf (aref (data self) i)
- (- (aref (data self) i)))))
-</programlisting>
- <para>This method would be called "takeAction:" in ObjC. The
- convention for methods that are going to be used as Cocoa
- actions is that they take one parameter, which is the object
- responsible for triggering the action. However, this method
- doesn't actually need to use that parameter, so it explicitly
- ignores it to avoid a compiler warning. As promised, the method
- doesn't return any value.</para>
- <para>There is also an alternate syntax, illustrated here. The
- following two method definitions are equivalent:</para>
- <programlisting>
-(define-objc-method ("applicationShouldTerminate:"
- "LispApplicationDelegate")
- (:id sender :<BOOL>)
- (declare (ignore sender))
- nil)
-
-(define-objc-method ((:<BOOL>
- :application-should-terminate sender)
- lisp-application-delegate)
- (declare (ignore sender))
- nil)
-</programlisting>
- <sect2 id=3D"Method-Redefinition-Constraints">
- <title>Method Redefinition Constraints</title>
- <para>Objective C was not designed, as Lisp was, with runtime
- redefinition in mind. So, there are a few constraints about
- how and when you can replace the definition of an Objective C
- method. Currently, if you break these rules, nothing will
- collapse, but the behaviour will be confusing; so
- don't.</para>
- <para>Objective C methods can be redefined at runtime, but
- their signatures shouldn't change. That is, the types of the
- arguments and the return type have to stay the same. The
- reason for this is that changing the signature changes the
- selector which is used to call the method.</para>
- <para>When a method has already been defined in one class, and
- you define it in a subclass, shadowing the original method,
- they must both have the same type signature. There is no such
- constraint, though, if the two classes aren't related and the
- methods just happen to have the same name.</para>
- </sect2>
- </sect1>
-
- <sect1 id=3D"How-Objective-C-Names-are-Mapped-to-Lisp-Symbols">
- <title>How Objective-C Names are Mapped to Lisp Symbols</title>
- <para>There is a standard set of naming conventions for Cocoa
- classes, messages, etc. As long as they are followed, the
- bridge is fairly good at automaticallly translating between ObjC
- and Lisp names.</para>
- <para>For example, "NSOpenGLView" becomes ns:ns-opengl-view;
- "NSURLHandleClient" becomes ns:ns-url-handle-client; and
- "nextEventMatchingMask:untilDate:inMode:dequeue:" becomes
- (:next-event-matching-mask :until-date :in-mode :dequeue). What
- a mouthful.</para>
- <para>To see how a given ObjC or Lisp name will be translated by
- the bridge, you can use the following functions:</para>
- <simplelist type=3D"vert">
- <member>(ccl::objc-to-lisp-classname string)</member>
- <member>(ccl::lisp-to-objc-classname symbol)</member>
- <member>(ccl::objc-to-lisp-message string)</member>
- <member>(ccl::lisp-to-objc-message string)</member>
- <member>(ccl::objc-to-lisp-init string)</member>
- <member>(ccl::lisp-to-objc-init keyword-list)</member>
- </simplelist>
-
- <para>Of course, there will always be exceptions to any naming
- convention. Please tell us on the mailing lists if you come
- across any name translation problems that seem to be bugs.
- Otherwise, the bridge provides two ways of dealing with
- exceptions:</para>
- <para>First, you can pass a string as the class name of
- MAKE-OBJC-INSTANCE and as the message to SEND. These strings
- will be directly interpreted as ObjC names, with no
- translation. This is useful for a one-time exception. For
- example:</para>
- <programlisting>
-(ccl::make-objc-instance "WiErDclass")
-(ccl::send o "WiErDmEsSaGe:WithARG:" x y)
- </programlisting>
- <para>Alternatively, you can define a special translation rule
- for your exception. This is useful for an exceptional name that
- you need to use througout your code. Some examples:</para>
- <programlisting>
-(ccl::define-classname-translation "WiErDclass" wierd-class)
-(ccl::define-message-translation "WiErDmEsSaGe:WithARG:" (:weird-message :=
with-arg))
-(ccl::define-init-translation "WiErDiNiT:WITHOPTION:" (:weird-init :option=
))
-</programlisting>
- <para>The normal rule in ObjC names is that each word begins with a
-capital letter (except possibly the first). Using this rule
-literally, "NSWindow" would be translated as N-S-WINDOW, which
-seems wrong. "NS" is a special word in ObjC that should not be
-broken at each capital letter. Likewise "URL", "PDF", "OpenGL",
-etc. Most common special words used in Cocoa are already defined
-in the bridge, but you can define new ones as follows:</para>
- <programlisting>
-(ccl::define-special-objc-word "QuickDraw")
-</programlisting>
- <para>Note that message keywords in a SEND such as (SEND V
- :MOUSE P :IN-RECT R) may look like the keyword arguments in a
- Lisp function call, but they really aren't. All keywords must be
- present and the order is significant. Neither (:IN-RECT :MOUSE)
- nor (:MOUSE) translate to "mouse:inRect:"</para>
- <para>Also, as a special exception, an "init" prefix is optional
- in the initializer keywords, so (MAKE-OBJC-INSTANCE 'NS-NUMBER
- :INIT-WITH-FLOAT 2.7) can also be expressed as
- (MAKE-OBJC-INSTANCE 'NS-NUMBER :WITH-FLOAT 2.7)</para>
- </sect1>
- </chapter>
-
- <chapter id=3D"Platform-specific-notes">
- <title>Platform-specific notes</title>
- =
-
- <sect1 id=3D"Platform-specific-overview">
- <title>Overview</title>
- <para> The documentation and whatever experience you may have in
- using OpenMCL under Linux should also apply to using it under
- Darwin/MacOS X and FreeBSD. There are some differences between
- the platforms, and these differences are sometimes exposed in
- the implementation.</para>
-
-
- <sect2 id=3D"File-system-case">
- <title>File-system case</title>
-
- <para>Darwin and MacOS X use HFS+ file systems by default;
- HFS+ file systems are usually case-insensitive. Most of
- OpenMCL's filesystem and pathname code assumes that the
- underlying filesystem is case-sensitive; this assumption
- extends to functions like EQUAL, which assumes that #p"FOO"
- and #p"foo" denote different, un-EQUAL filenames. Since
- Darwin/MacOS X can also use UFS and NFS filesystems, the
- opposite assumption would be no more correct than the one
- that's currently made.</para>
- <para>Whatever the best solution to this problem turns out to be, =
there are
-some practical considerations. Doing:</para>
- <programlisting>
-? (save-application "DPPCCL")
- </programlisting>
- <para>on 32-bit DarwinPPC has the unfortunate side-effect of
- trying to overwrite the Darwin OpenMCL kernel, "dppccl", on a
- case-insensitive filesystem.</para>
- <para>To work around this, the Darwin OpenMCL kernel expects
- the default heap image file name to be the kernel's own
- filename with the string ".image" appended, so the idiom would
- be:</para>
- <programlisting>
-? (save-application "dppccl.image")
- </programlisting>
- </sect2>
-
- <sect2 id=3D"Line-Termination-Characters">
- <title>Line Termination Characters</title>
- <para>MacOSX effectively supports two distinct line-termination
- conventions. Programs in its Darwin substrate follow the Unix
- convention of recognizing #\LineFeed as a line terminator; traditional
- MacOS programs use #\Return for this purpose. Many modern
- GUI programs try to support several different line-termination
- conventions (on the theory that the user shouldn't be too concerned
- about what conventions are used an that it probably doesn't matter.
- Sometimes this is true, other times ... not so much.
- </para>
- <para>OpenMCL follows the Unix convention on both Darwin and
- LinuxPPC, but offers some support for reading and writing
- files that use other conventions (including traditional MacOS
- conventions) as well.</para> =
- <para>This support (and anything like it) is by nature
- heuristic: it can successfully hide the distinction between
- newline conventions much of the time, but could mistakenly
- change the meaning of otherwise correct programs (typically
- when files contain both #\Return and #\Linefeed characters or
- when files contain mixtures of text and binary data.) Because
- of this concern, the default settings of some of the variables
- that control newline translation and interpretation are
- somewhat conservative.</para>
- <para>Although the issue of multiple newline conventions
- primarily affects MacOSX users, the functionality described
- here is available under LinuxPPC as well (and may occasionally
- be useful there.)</para> <para>None of this addresses issues
- related to the third newline convention ("CRLF") in widespread
- use (since that convention isn't native to any platform on
- which OpenMCL currently runs). If OpenMCL is ever ported to
- such a platform, that issue might be revisited.</para>
- <para>Note that some MacOS programs (including some versions
- of commercial MCL) may use HFS file type information to
- recognize TEXT and other file types and so may fail to
- recognize files created with OpenMCL or other Darwin
- applications (regardless of line termination issues.)</para>
- <para>Unless otherwise noted, the symbols mentioned in this
- documentation are exported from the CCL package.</para>
- </sect2>
-
- <sect2 id=3D"Single-precision-trig---transcendental-functions">
- <title>Single-precision trig & transcendental functions</title>
- <para>
- Despite what Darwin's man pages say, early versions of its math library
- (up to and including at least OSX 10.2 (Jaguar) don't implement
- single-precision variants of the transcendental and trig functions
- (#_sinf, #_atanf, etc.) OpenMCL worked around this by coercing
- single-precision args to double-precision, calling the
- double-precision version of the math library function, and coercing
- the result back to a SINGLE-FLOAT. These steps can introduce rounding
- errors (and potentially overflow conditions) that might not be present
- or as severe if true 32-bit variants were available.</para>
- </sect2>
-
- <sect2 id=3D"Shared-libraries">
- <title>Shared libraries</title>
- <para>Darwin/MacOS X distinguishes between "shared libraries"
- and "bundles" or "extensions"; Linux and FreeBSD don't. In
- Darwin, "shared libraries" have the file type "dylib" : the
- expectation is that this class of file is linked against when
- executable files are created and loaded by the OS when the
- executable is launched. The latter class -
- "bundles/extensions" - are expected to be loaded into and
- unloaded from a running application, via a mechanism like the
- one used by OpenMCL's OPEN-SHARED-LIBRARY function.</para>
- </sect2>
- </sect1>
-
- <sect1 id=3D"Unix-Posix-Darwin-Features">
- <title>Unix/Posix/Darwin Features</title>
- <para>OpenMCL has several convenience functions which allow you
- to make Posix (portable Unix) calls without having to use the
- foreign-function interface. Each of these corresponds directly
- to a single Posix function call, as it might be made in C.
- There is no attempt to make these calls correspond to Lisp
- idioms, such as <literal>setf</literal>. This means that their
- behaviour is simple and predictable.</para>
- <para>For working with environment variables, there are
- CCL::GETENV and CCL::SETENV.</para>
- <para>For working with user and group IDs, there are
- CCL::GETUID, CCL::SETUID, and CCL::SETGID. To find the home
- directory of an arbitrary user, as set in the user database
- (/etc/passwd), there is CCL::GET-USER-HOME-DIR.</para>
- <para>For process IDs, there is CCL::GETPID.</para>
- <para>For the <literal>system()</literal> function, there is
- CCL::OS-COMMAND. Ordinarily, it is better - both more efficient
- and more predictable - to use the features described in <xref
- linkend=3D"Running-Other-Programs-as-Subprocesses"/>. However,
- sometimes you may want to specifically ask the shell to invoke a
- command for you.</para>
- </sect1>
-
- <sect1 id=3D"Cocoa-Programming-in-OpenMCL">
- <title>Cocoa Programming in OpenMCL</title>
- <para>Cocoa is one of Apple's APIs for GUI programming; for most
- purposes, development is considerably faster with Cocoa than
- with the alternatives. You should have a little familiarity
- with it, to better understand this section.</para>
- <para>A small sample Cocoa program can be invoked by evaluating
- (REQUIRE 'TINY) and then (CCL::TINY-SETUP). This program
- provides a simple example of using several of the bridge's
- capabilities.</para>
- <para>The Tiny demo creates Cocoa objects dynamically, at
- runtime, which is always an option. However, for large
- applications, it is usually more convenient to create your
- objects with Apple Interface Builder, and store them in .nib
- files to be loaded when needed. Both approaches can be freely
- mixed in a single program.</para>
-
- <sect2 id=3D"The-Command-Line-and-the-Window-System">
- <title>The Command Line and the Window System</title>
- <para>OpenMCL is ordinarily a command-line application (it
- doesn't have a connection to the OSX Window server, doesn't
- have its own menubar or dock icon, etc.) By opening some
- libraries and jumping through some hoops, it's able to sort of
- transform itself into a full-fledged GUI application (while
- retaining its original TTY-based listener.) The general idea
- is that this hybrid environment can be used to test and
- protoype UI ideas and the resulting application can eventually
- be fully transformed into a bundled, double-clickable
- application. This is to some degree possible, but there needs
- to be a bit more infrastructure in place before many people
- would find it easy.</para>
- <para>Cocoa applications use the NSLog function to write
- informational/warning/error messages to the application's
- standard output stream. When launched by the Finder, a GUI
- application's standard output is diverted to a logging
- facility that can be monitored with the Console application
- (found in /Applications/Utilities/Console.app). In the hybrid
- environment, the application's standard output stream is
- usually the initial listener's standard output stream. With
- two different buffered stream mechanisms trying to write to
- the same underlying Unix file descriptor, it's not uncommon to
- see NSLog output mixed with lisp output on the initial
- listener.</para>
- </sect2>
-
- <sect2 id=3D"Writing--and-reading--Cocoa-code">
- <title>Writing (and reading) Cocoa code</title> <para>The
- syntax of the constructs used to define Cocoa classes and
- methods has changed a bit (it was never documented outside of
- the source code and never too well documented at all), largely
- as the result of functionality offered by Randall Beer's
- bridge; the “standard name-mapping conventions”
- referenced below are described in his CocoaBridgeDoc.txt file,
- as are the constructs used to invoke (“send messages
- to”) Cocoa methods.</para>
- <para>All of the symbols described below are currently internal to
-the CCL package.</para>
- <simplelist type=3D"vert" columns=3D"1">
- <member><xref linkend=3D"m_class"/></member>
- <member><xref linkend=3D"m_selector"/></member>
- <member><xref linkend=3D"m_define-objc-method"/></member>
- <member><xref linkend=3D"m_define-objc-class-method"/></member>
- </simplelist>
- </sect2>
-
- <sect2 id=3D"The-Application-Kit-and-Multiple-Threads">
- <title>The Application Kit and Multiple Threads</title>
- <para>The Cocoa API is broken into several pieces. The
- Application Kit, affectionately called AppKit, is the one
- which deals with window management, drawing, and handling
- events. AppKit really wants all these things to be done by a
- "distinguished thread". creation, and drawing to take place
- on a distinguished thread.</para>
- <para>Apple has published some guidelines which discuss these
- issues in some detail; see the Apple Multithreading
- Documentation, and in particular the guidelines on Using the
- Application Kit from Multiple Threads. The upshot is that
- there can sometimes be unexpected behavior when objects are
- created in threads other than the distinguished event thread;
- eg, the event thread sometimes starts performing operations on
- objects that haven't been fully initialized.</para> <para>It's
- certainly more convenient to do certain types of exploratory
- programming by typing things into a listener or evaluating a
- “defun” in an Emacs buffer; it may sometimes be
- necessary to be aware of this issue while doing so.</para>
- <para>Each thread in the Cocoa runtime system is expected to
- maintain a current “autorelease pool” (an instance
- of the NSAutoreleasePool class); newly created objects are
- often added to the current autorelease pool (via the
- -autorelease method), and periodically the current autorelease
- pool is sent a “-release” message, which causes it
- to send “-release” messages to all of the objects
- that've been added to it.</para>
- <para>If the current thread doesn't have a current autorelease
- pool, the attempt to autorelease any object will result in a
- severe-looking warning being written via NSLog. The event
- thread maintains an autorelease pool (it releases the current
- pool after each event is processed and creates a new one for
- the next event), so code that only runs in that thread should
- never provoke any of these severe-looking NSLog
- messages.</para> <para>To try to suppress these messages (and
- still participate in the Cocoa memory management scheme), each
- listener thread (the initial listener and any created via the
- “New Listener” command in the IDE) is given a
- default autorelease pool; there are REPL colon-commands for
- manipulating the current listener's “toplevel
- auturelease pool”.</para>
- <para>In the current scheme, every time that Cocoa calls lisp
- code, a lisp error handler is established which maps any lisp
- conditions to ObjC exceptions and arranges that this exception
- is raised when the callback to lisp returns. Whenever lisp
- code invokes a Cocoa method, it does so with an ObjC exception
- handler in place; this handler maps ObjC exceptions to lisp
- conditions and signals those conditions.</para> <para>Any
- unhandled lisp error or ObjC exception that occurs during the
- execution of the distinguished event thread's event loop
- causes a message to be NSLog'ed and the event loop to (try to)
- continue execution. Any error that occurs in other threads is
- handled at the point of the outermost Cocoa method
- invocation. (Note that the error is not necessarily
- “handled” in the dynamic context in which it
- occurs.)</para>
- <para>Both of these behaviors could possibly be improved; both of =
them
-seem to be substantial improvements over previous behaviors (where,
-for instance, a misspelled message name typically terminated the
-application.)</para>
- </sect2>
-
- <sect2 id=3D"Acknowledgement--2-">
- <title>Acknowledgement</title>
- <para>The Cocoa bridge was originally developed, and
- generously contributed by, Randal Beer.</para>
- </sect2>
- </sect1>
-
- <sect1 id=3D"Building-an-Application-Bundle">
- <title>Building an Application Bundle</title>
- <para>You may have noticed that (require "COCOA") takes a long
- time to load. It is possible to avoid this by saving a Lisp
- heap image which has everything already loaded. There is an
- example file which allows you to do this,
- "ccl/examples/cocoa-application.lisp", by producing a
- double-clickable application which runs your program. First,
- load your own program. Then, do:</para>
- <programlisting>
-? (require "COCOA-APPLICATION")
- </programlisting>
- <para>When it finishes, you should be able to double-click the OpenM=
CL icon
-in the ccl directory, to quickly start your program.</para>
- <para>The OS may have already decided that OpenMCL.app isn't a valid
-executable bundle, and therefore won't let you double-click it.
-If this happens to you, to force it to reconsider, just update the
-last-modified time of the bundle. In Terminal:</para>
- <programlisting>> touch OpenMCL.app
-</programlisting>
- <para>There is one important caveat.</para>
- <para>Because of the way that the ObjC bridge currently works, a sav=
ed
-image is dependent upon the <emphasis>exact</emphasis> versions of
-the Cocoa libraries which were present when it was saved.
-Specifically, the interface database is. So, for example, an
-application produced under OS X 10.3.5 will not work under
-OS X 10.3.6. This is inconvenient when you wish to distribute an
-application you have built this way.</para>
- <para>Work in this direction is ongoing. It is worth looking at the=
project
-"Bosco", by Mikel Evins, which is a template that can be used to build
-application bundles in a different way. It is
-available here, as part
-of the "Clotho" project, and there is
-here.</para>
- <para>When an image which had contained ObjC classes (which are also
-CLOS classes) is re-launched, those classes are "revived": all
-preexisting classes have their addresses updated destructively, so that
-existing subclass/superclass/metaclass relationships are maintained.
-It's not possible (and may never be) to preserve foreign
-instances across SAVE-APPLICATION. (It may be the case that NSArchiver
-and NSCoder and related classes offer some approximation of that.)</para>
- </sect1>
-
- <sect1 id=3D"Recommended-Reading">
- <title>Recommended Reading></title>
- <variablelist>
- <varlistentry>
- <term>
- <ulink url=3D"http://developer.apple.com/documentation/Cocoa/">Cocoa=
Documentation</ulink>
- </term>
-
- <listitem>
- <para>
- This is the top page for all of Apple's documentation on
- Cocoa. If you are unfamiliar with Cocoa, it is a good
- place to start.
- </para>
- </listitem>
- </varlistentry>
- <varlistentry>
- <term>
- <ulink url=3D"http://developer.apple.com/documentation/Cocoa/Referenc=
e/Foundation/ObjC_classic/index.html">Foundation Reference for Objective-C<=
/ulink>
- </term>
-
- <listitem>
- <para>
- This is one of the two most important Cocoa references; it
- covers all of the basics, except for GUI programming. This is
- a reference, not a tutorial.
- </para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>
- <ulink url=3D"http://developer.apple.com/documentation/Cocoa/Referenc=
e/ApplicationKit/ObjC_classic/index.html">Application Kit Reference for Obj=
ective-C</ulink>
- </term>
-
- <listitem>
- <para>
- This is the other; it covers GUI programming with Cocoa
- in considerable depth. This is a reference, not a tutorial.
- </para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>
- <ulink url=3D"http://developer.apple.com/documentation/index.html">Ap=
ple Developer Documentation</ulink>
- </term>
-
- <listitem>
- <para>
- This is the site which the above two documents are found on;
- go here to find the documentation on any other Apple API.
- Also go here if you need general guidance about OS X, Carbon,
- Cocoa, Core Foundation, or Objective C.
- </para>
- </listitem>
- </varlistentry>
- </variablelist>
-
- </sect1>
-
- <sect1 id=3D"Operating-System-Dictionary">
- <title>Operating-System Dictionary</title>
-
- <refentry id=3D"f_getenv">
- <indexterm zone=3D"f_getenv">
- <primary>getenv</primary>
- </indexterm>
-
- <refnamediv>
- <refname>CCL::GETENV</refname>
- <refpurpose></refpurpose>
- <refclass>Function</refclass>
- </refnamediv>
-
- <refsynopsisdiv>
- <synopsis><function>getenv</function> name =3D> value</synopsis>
- </refsynopsisdiv>
-
- <refsect1>
- <title>Arguments and Values</title>
-
- <variablelist>
- <varlistentry>
- <term>name</term>
-
- <listitem>
- <para>a string which is the name of an existing
- environment variable;
- case-sensitive</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>value</term>
-
- <listitem>
- <para>if there is an environment variable named
- <varname>name</varname>, its value, as a string; if there
- is not, NIL</para>
- </listitem>
- </varlistentry>
- </variablelist>
- </refsect1>
-
- <refsect1>
- <title>Description</title>
-
- <para>
- Looks up the value of the environment variable named by
- <varname>name</varname>, in the OS environment.
- </para>
- </refsect1>
- </refentry>
-
- <refentry id=3D"f_setenv">
- <indexterm zone=3D"f_setenv">
- <primary>setenv</primary>
- </indexterm>
-
- <refnamediv>
- <refname>CCL::SETENV</refname>
- <refpurpose></refpurpose>
- <refclass>Function</refclass>
- </refnamediv>
-
- <refsynopsisdiv>
- <synopsis><function>setenv</function> name value =3D> errno</synopsis>
- </refsynopsisdiv>
-
- <refsect1>
- <title>Arguments and Values</title>
-
- <variablelist>
- <varlistentry>
- <term>name</term>
-
- <listitem>
- <para>a string which is the name of a new or existing
- environment variable;
- case-sensitive</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>value</term>
-
- <listitem>
- <para>a string, to be the new value of the
- environment variable
- named by <varname>name</varname></para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>errno</term>
-
- <listitem>
- <para>zero if the function call completes successfully;
- otherwise, a platform-dependent integer which describes
- the problem</para>
- </listitem>
- </varlistentry>
- </variablelist>
- </refsect1>
-
- <refsect1>
- <title>Description</title>
-
- <para>
- Sets the value of the environment variable named by
- <varname>name</varname>, in the OS environment. If there is
- no such environment
- variable, creates it.
- </para>
- </refsect1>
- </refentry>
-
- <refentry id=3D"f_current-directory-name">
- <indexterm zone=3D"f_current-directory-name">
- <primary>current-directory-name</primary>
- </indexterm>
-
- <refnamediv>
- <refname>CCL::CURRENT-DIRECTORY-NAME</refname>
- <refpurpose></refpurpose>
- <refclass>Function</refclass>
- </refnamediv>
-
- <refsynopsisdiv>
- <synopsis><function>current-directory-name</function>
- =3D> path</synopsis>
- </refsynopsisdiv>
-
- <refsect1>
- <title>Values</title>
-
- <variablelist>
- <varlistentry>
- <term>path</term>
-
- <listitem>
- <para>a string, an absolute pathname in Posix format - with
- directory components separated by slashes</para>
- </listitem>
- </varlistentry>
- </variablelist>
- </refsect1>
-
- <refsect1>
- <title>Description</title>
-
- <para>
- Looks up the current working directory of the OpenMCL process;
- unless it has been changed, this is the directory OpenMCL was
- started in.
- </para>
- </refsect1>
- </refentry>
-
- <refentry id=3D"f_getuid">
- <indexterm zone=3D"f_getuid">
- <primary>getuid</primary>
- </indexterm>
-
- <refnamediv>
- <refname>CCL::GETUID</refname>
- <refpurpose></refpurpose>
- <refclass>Function</refclass>
- </refnamediv>
-
- <refsynopsisdiv>
- <synopsis><function>getuid</function> =3D> uid</synopsis>
- </refsynopsisdiv>
-
- <refsect1>
- <title>Values</title>
-
- <variablelist>
- <varlistentry>
- <term>uid</term>
-
- <listitem>
- <para>a non-negative integer, identifying a specific user
- account as defined in the OS user database</para>
- </listitem>
- </varlistentry>
- </variablelist>
- </refsect1>
-
- <refsect1>
- <title>Description</title>
-
- <para>
- Returns the ("real") user ID of the current user.
- </para>
- </refsect1>
- </refentry>
-
- <refentry id=3D"f_setuid">
- <indexterm zone=3D"f_setuid">
- <primary>setuid</primary>
- </indexterm>
-
- <refnamediv>
- <refname>CCL::SETUID</refname>
- <refpurpose></refpurpose>
- <refclass>Function</refclass>
- </refnamediv>
-
- <refsynopsisdiv>
- <synopsis><function>setuid</function> uid =3D> errno</synopsis>
- </refsynopsisdiv>
-
- <refsect1>
- <title>Arguments and Values</title>
-
- <variablelist>
- <varlistentry>
- <term>uid</term>
-
- <listitem>
- <para>a non-negative integer, identifying a specific user
- account as defined in the OS user database</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>errno</term>
-
- <listitem>
- <para>zero if the function call completes successfully;
- otherwise, a platform-dependent integer which describes
- the problem</para>
- </listitem>
- </varlistentry>
- </variablelist>
- </refsect1>
-
- <refsect1>
- <title>Description</title>
-
- <para>
- Attempts to change the current user ID (both "real" and
- "effective"); fails unless
- the OpenMCL process has super-user privileges or the ID
- given is that of the current user.
- </para>
- </refsect1>
- </refentry>
-
- <refentry id=3D"f_setgid">
- <indexterm zone=3D"f_setgid">
- <primary>setgid</primary>
- </indexterm>
-
- <refnamediv>
- <refname>CCL::SETGID</refname>
- <refpurpose></refpurpose>
- <refclass>Function</refclass>
- </refnamediv>
-
- <refsynopsisdiv>
- <synopsis><function>setgid</function> gid =3D> errno</synopsis>
- </refsynopsisdiv>
-
- <refsect1>
- <title>Arguments and Values</title>
-
- <variablelist>
- <varlistentry>
- <term>gid</term>
-
- <listitem>
- <para>a non-negative integer, identifying a specific
- group as defined in the OS user database</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>errno</term>
-
- <listitem>
- <para>zero if the function call completes successfully;
- otherwise, a platform-dependent integer which describes
- the problem</para>
- </listitem>
- </varlistentry>
- </variablelist>
- </refsect1>
-
- <refsect1>
- <title>Description</title>
-
- <para>
- Attempts to change the current group ID (both "real" and
- "effective"); fails unless
- the OpenMCL process has super-user privileges or the ID
- given is that of a group to which the current user belongs.
- </para>
- </refsect1>
- </refentry>
-
- <refentry id=3D"f_getpid">
- <indexterm zone=3D"f_getpid">
- <primary>getpid</primary>
- </indexterm>
-
- <refnamediv>
- <refname>CCL::GETPID</refname>
- <refpurpose></refpurpose>
- <refclass>Function</refclass>
- </refnamediv>
-
- <refsynopsisdiv>
- <synopsis><function>getpid</function> =3D> pid</synopsis>
- </refsynopsisdiv>
-
- <refsect1>
- <title>Values</title>
-
- <variablelist>
- <varlistentry>
- <term>pid</term>
-
- <listitem>
- <para>a non-negative integer, identifying an OS process</para>
- </listitem>
- </varlistentry>
- </variablelist>
- </refsect1>
-
- <refsect1>
- <title>Description</title>
-
- <para>
- Returns the ID of the OpenMCL OS process.
- </para>
- </refsect1>
- </refentry>
-
- <refentry id=3D"f_get-user-home-dir">
- <indexterm zone=3D"f_get-user-home-dir">
- <primary>get-user-home-dir</primary>
- </indexterm>
-
- <refnamediv>
- <refname>CCL::GET-USER-HOME-DIR</refname>
- <refpurpose></refpurpose>
- <refclass>Function</refclass>
- </refnamediv>
-
- <refsynopsisdiv>
- <synopsis><function>get-user-home-dir</function> =
- uid =3D> path</synopsis>
- </refsynopsisdiv>
-
- <refsect1>
- <title>Values</title>
-
- <variablelist>
- <varlistentry>
- <term>uid</term>
-
- <listitem>
- <para>a non-negative integer, identifying a specific user
- account as defined in the OS user database</para>
- </listitem>
- </varlistentry>
- <varlistentry>
- <term>path</term>
-
- <listitem>
- <para>a string, an absolute pathname in Posix format - with
- directory components separated by slashes; or NIL</para>
- </listitem>
- </varlistentry>
- </variablelist>
- </refsect1>
-
- <refsect1>
- <title>Description</title>
-
- <para>
- Looks up and returns the defined home directory of the user
- identified by <varname>uid</varname>. This value comes from the
- OS user database, not from the <varname>$HOME</varname>
- environment variable. Returns NIL if there is no user with
- the ID <varname>uid</varname>.
- </para>
- </refsect1>
- </refentry>
-
- <refentry id=3D"f_os-command">
- <indexterm zone=3D"f_os-command">
- <primary>os-command</primary>
- </indexterm>
-
- <refnamediv>
- <refname>CCL::OS-COMMAND</refname>
- <refpurpose></refpurpose>
- <refclass>Function</refclass>
- </refnamediv>
-
- <refsynopsisdiv>
- <synopsis><function>os-command</function> command-line
- =3D> exit-code</synopsis>
- </refsynopsisdiv>
-
- <refsect1>
- <title>Values</title>
-
- <variablelist>
- <varlistentry>
- <term>command-line</term>
-
- <listitem><para>a string, obeying all the whitespace and
- escaping
- conventions required by the user's default system shell</para>
- </listitem>
- </varlistentry>
- </variablelist>
- <variablelist>
- <varlistentry>
- <term>exit-code</term>
-
- <listitem><para>a non-negative integer, returned as the exit
- code of a subprocess; zero indicates success</para></listitem>
- </varlistentry>
- </variablelist>
- </refsect1>
-
- <refsect1>
- <title>Description</title>
-
- <para>
- Invokes the Posix function <function>system()</function>, which
- invokes the user's default system shell (such as
- sh or tcsh) as a new process, and has that shell execute
- <varname>command-line</varname>.
- </para>
- =
- <para>
- If the shell was able to find the command specified in
- <varname>command-line</varname>, then <varname>exit-code</varname>
- is the exit code of that command. If not, it is the exit
- code of the shell itself.
- </para>
- </refsect1>
-
- <refsect1>
- <title>Notes</title>
-
- <para>
- By convention, an exit code of 0 indicates success. There are
- also other conventions; unfortunately, they are OS-specific, and
- the portable macros to decode their meaning are implemented
- by the system headers as C preprocessor macros. This means
- that there is no good, automated way to make them available
- to Lisp.
- </para>
- </refsect1>
- </refentry>
-
- <refentry id=3D"m_class">
- <indexterm zone=3D"m_class">
- <primary>@class</primary>
- </indexterm>
- =
- <refnamediv>
- <refname>CCL::@CLASS</refname>
- <refpurpose></refpurpose>
- <refclass>Macro</refclass>
- </refnamediv>
-
- <refsynopsisdiv>
- <synopsis><function>@class</function> class-name</synopsis>
- </refsynopsisdiv>
-
- <refsect1>
- <title>Arguments and Values</title>
-
- <variablelist>
- <varlistentry>
- <term>class-name</term>
-
- <listitem>
- <para>a string which denotes an existing class name, or a
- symbol which can be mapped to such a string via the standard
- name-mapping conventions for class names</para>
- </listitem>
- </varlistentry>
- </variablelist>
- </refsect1>
-
- <refsect1>
- <title>Description</title>
-
- <para>Used to refer to a known ObjC class by name. (Via the use
- LOAD-TIME-VALUE, the results of a class-name -> class lookup
- are cached.)</para>
-
- <para>
- <function>@class</function> is obsolete as of late 2004, because
- find-class now works on ObjC classes. It is described here
- only because some old code still uses it.
- </para>
- </refsect1>
- </refentry>
-
- <refentry id=3D"m_selector">
- <indexterm zone=3D"m_selector">
- <primary>@selector</primary>
- </indexterm>
-
- <refnamediv>
- <refname>CCL::@SELECTOR</refname>
- <refpurpose></refpurpose>
- <refclass>Macro</refclass>
- </refnamediv>
-
- <refsynopsisdiv>
- <synopsis><function>@selector</function> string</synopsis>
- </refsynopsisdiv>
-
- <refsect1>
- <title>Arguments and Values</title>
-
- <variablelist>
- <varlistentry>
- <term>string</term>
-
- <listitem>
- <para>a string constant, used to canonically refer to an
- ObjC method selector</para>
- </listitem>
- </varlistentry>
- </variablelist>
- </refsect1>
-
- <refsect1>
- <title>Description</title>
-
- <para>Used to refer to an ObjC method selector (method name). Uses
- LOAD-TIME-VALUE to cache the result of a string -> selector
- lookup.</para>
- </refsect1>
- </refentry>
-
- <refentry id=3D"m_define-objc-method">
- <indexterm zone=3D"m_define-objc-method">
- <primary>define-objc-method</primary>
- </indexterm>
-
- <refnamediv>
- <refname>CCL::DEFINE-OBJC-METHOD</refname>
- <refpurpose></refpurpose>
- <refclass>Macro</refclass>
- </refnamediv>
-
- <refsynopsisdiv>
- <synopsis><function>define-objc-method</function>
- (selector class-name) &body; body</synopsis>
- </refsynopsisdiv>
-
- <refsect1>
- <title>Arguments and Values</title>
-
- <variablelist>
- <varlistentry>
- <term>selector</term>
-
- <listitem>
- <para>either a string which represents the name of the
- selector or a list which describ+es the method's return
- type, selector components, and argument types (see below.)
- If the first form is used, then the first form in the body
- must be a list which describes the selector's argument
- types and return value type, as per DEFCALLBACK.</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>class-name</term>
-
- <listitem>
- <para>either a string which names an existing ObjC class
- name or a list symbol which can map to such a string via the
- standard name-mapping conventions for class names. (Note
- that the "canonical" lisp class name is such a
- symbol)</para>
- </listitem>
- </varlistentry>
- </variablelist>
- </refsect1>
-
- <refsect1>
- <title>Description</title>
-
- <para>Defines an ObjC-callable method which implements the
- specified message selector for instances of the existing ObjC
- class class-name.</para>
- </refsect1>
- </refentry>
-
- <refentry id=3D"m_define-objc-class-method">
- <indexterm zone=3D"m_define-objc-class-method">
- <primary>define-objc-class-method</primary>
- </indexterm>
-
- <refnamediv>
- <refname>CCL::DEFINE-OBJC-CLASS-METHOD</refname>
- <refpurpose></refpurpose>
- <refclass>Macro</refclass>
- </refnamediv>
-
- <refsynopsisdiv>
- <synopsis><function>define-objc-class-method</function>
- (selector class-name) &body; body</synopsis>
- </refsynopsisdiv>
-
- <refsect1>
- <title>Arguments and Values</title>
-
- <para>As per DEFINE-OBJC-METHOD</para>
- </refsect1>
-
- <refsect1>
- <title>Description</title>
-
- <para>Like DEFINE-OBJC-METHOD, only used to define methods on the
- <emphasis>class</emphasis> named by class-name and on its
- subclasses.</para>
-
- <para>For both DEFINE-OBJC-METHOD and DEFINE-OBJC-CLASS-METHOD, the
- "selector" argument can be a list whose first element is a
- foreign type specifier for the method's return value type and whose
- subsequent elements are either:</para>
-
- <itemizedlist>
- <listitem>
- <para>a non-keyword symbol, which can be mapped to a selector string
- for a parameterless method according to the standard name-mapping
- conventions for method selectors.</para>
- </listitem>
- =
- <listitem>
- <para>a list of alternating keywords and variable/type specifiers,
- where the set of keywords can be mapped to a selector string for a
- parameteriezed method according to the standard name-mapping
- conventions for method selectors and each variable/type-specifier is
- either a variable name (denoting a value of type :ID) or a list whose
- CAR is a variable name and whose CADR is the corresponding
- argument's foreign type specifier.</para>
- </listitem>
- </itemizedlist>
- </refsect1>
- </refentry>
-
- <refentry id=3D"v_alternate-line-terminator">
- <indexterm zone=3D"v_alternate-line-terminator">
- <primary>*alternate-line-terminator*</primary>
- </indexterm>
-
- <refnamediv>
- <refname>CCL:*ALTERNATE-LINE-TERMINATOR*</refname>
- <refpurpose></refpurpose>
- <refclass>Variable</refclass>
- </refnamediv>
-
- <refsect1>
- <title>Description</title>
-
- <para>This variable is currently only used by the standard reader mac=
ro
- function for #\; (single-line comments); that function reads successi=
ve
- characters until EOF, a #\NewLine is read, or a character EQL to the
- value of *alternate-line-terminator* is read. In OpenMCL for Darwin, =
the
- value of this variable is initially #\Return ; in OpenMCL for LinuxPP=
C,
- it's initially NIL.</para>
- =
- <para>Their default treatment by the #\; reader macro is the primary =
way
- in which #\Return and #\Linefeed differ syntactally; by extending the
- #\; reader macro to (conditionally) treat #\Return as a
- comment-terminator, that distinction is eliminated. This seems to make
- LOAD and COMPILE-FILE insensitive to line-termination issues in many
- cases. It could fail in the (hopefully rare) case where a LF-terminat=
ed
- (Unix) text file contains embedded #\Return characters, and this
- mechanism isn't adequate to handle cases where newlines are embed=
ded
- in string constants or other tokens (and presumably should be transla=
ted
- from an external convention to the external one) : it doesn't cha=
nge
- what READ-CHAR or READ-LINE "see", and that may be necessary =
to
- handle some more complicated cases.</para>
- </refsect1>
- </refentry>
-
- <refentry id=3D"k_external-format">
- <indexterm zone=3D"k_external-format">
- <primary>:external-format</primary>
- </indexterm>
-
- <refnamediv>
- <refname>:EXTERNAL-FORMAT</refname>
- <refpurpose></refpurpose>
- <refclass>Keyword Argument</refclass>
- </refnamediv>
-
- <refsect1>
- <title>Description</title>
-
- <para>Per ANSI CL, OpenMCL supports the :EXTERNAL-FORMAT keyword
- argument to the functions OPEN, LOAD, and COMPILE-FILE. This argument=
is
- intended to provide a standard way of providing implementation-depend=
ent
- information about the format of files opened with an element-type of
- CHARACTER. This argument can meaningfully take on the values :DEFAULT
- (the default), :MACOS, :UNIX, or :INFERRED in OpenMCL.</para>
- =
- <para>When defaulted to or specified as :DEFAULT, the format of the f=
ile
- stream is determined by the value of the variable
- CCL:*DEFAULT-EXTERNAL-FORMAT*. See below.</para>
- =
- <para>When specified as :UNIX, all characters are read from and writt=
en
- to files verbatim.</para>
- =
- <para>When specified as :MACOS, all #\Return characters read from the
- file are immediately translated to #\Linefeed (#\Newline); all #\Newl=
ine
- (#\Linefeed) characters are written externally as #\Return characters=
.</para>
- =
- <para>When specified as :INFERRED and the file is open for input, the
- first bufferful of input data is examined; if a #\Return character
- appears in the buffer before the first #\Linefeed, the file stream=
9;s
- external-format is set to :MACOS; otherwise, it is set to :UNIX.</par=
a>
- =
- <para>All other values of :EXTERNAL-FORMAT - and any combinations that
- don't make sense, such as trying to infer the format of a
- newly-created output file stream - are treated as if :UNIX was
- specified. As mentioned above, the :EXTERNAL-FORMAT argument doesn=
9;t
- apply to binary file streams.</para>
- =
- <para>The translation performed when :MACOS is specified or inferred =
has
- a somewhat greater chance of doing the right thing than the
- *alternate-line-terminator* mechanism does; it probably has a somewhat
- greater chance of doing the wrong thing, as well.</para>
- </refsect1>
- </refentry>
-
- <refentry id=3D"v_default-external-format">
- <indexterm zone=3D"v_default-external-format">
- <primary>*default-external-format*</primary>
- </indexterm>
- =
- <refnamediv>
- <refname>CCL:*DEFAULT-EXTERNAL-FORMAT*</refname>
- <refpurpose></refpurpose>
- <refclass>Variable</refclass>
- </refnamediv>
-
- <refsect1>
- <title>Description</title>
-
- <para>The value of this variable is used when :EXTERNAL-FORMAT is
- unspecified or specified as :DEFAULT. It can meaningfully be given any
- of the values :UNIX, :MACOS, or :INFERRED, each of which is interpret=
ed
- as described above.</para>
- =
- <para>Because there's some risk that unsolicited newline translat=
ion
- could have undesirable consequences, the initial value of this variab=
le
- in OpenMCL is :UNIX.</para>
- </refsect1>
- </refentry>
-
- <refentry id=3D"c_ns-lisp-string">
- <indexterm zone=3D"c_ns-lisp-string">
- <primary>ns-lisp-string</primary>
- </indexterm>
-
- <refnamediv>
- <refname>CCL::NS-LISP-STRING</refname>
- <refpurpose></refpurpose>
- <refclass>Class</refclass>
- </refnamediv>
-
- <refsect1>
- <title>Superclasses</title>
-
- <para>NS:NS-STRING</para>
- </refsect1>
-
- <refsect1>
- <title>Initargs</title>
- =
- <variablelist>
- <varlistentry>
- <term>:string</term>
- =
- <listitem>
- <para>
- a Lisp string which is to be the content of
- the newly-created ns-lisp-string.
- </para>
- </listitem>
- </varlistentry>
- </variablelist>
- </refsect1>
-
- <refsect1>
- <title>Description</title>
-
- <para>
- This class
- implements the interface of an NSString, which means that it can
- be passed to any Cocoa or Core Foundation function which expects
- one.
- </para>
-
- <para>
- The string itself is stored on the Lisp heap, which
- means that its memory management is automatic. However, the
- ns-lisp-string object itself is a foreign
- object (that is, it has an objc metaclass), and resides on the
- foreign heap. Therefore, it is necessary to explicitly free
- it, by sending a dealloc message.
- </para>
- </refsect1>
-
- <refsect1>
- <title>Examples</title>
-
- <para>
- You can create an ns-lisp-string with
- <function>make-instance</function>, just like
- any normal Lisp class:
- </para>
-
- <programlisting format=3D"linespecific"
->? (defvar *the-string*
- (make-instance 'ccl::ns-lisp-string
- :string "Hello, Cocoa."))</programlisting>
- =
- <para>
- When you are done with the string, you must explicitly
- deallocate it:
- </para>
-
- <programlisting format=3D"linespecific">? (ccl::send *the-string* 'de=
alloc)</programlisting>
-
- <para>
- You may wish to use an <function>unwind-protect</function>
- form to ensure that this happens:
- </para>
-
- <programlisting format=3D"linespecific"
->(let (*the-string*)
- (unwind-protect (progn (setq *the-string*
- (make-instance 'ccl::ns-lisp-string
- :string "Hello, Cocoa."))
- (format t "~&The string is ~D characters long=
.~%"
- (ccl::send *the-string* 'length)))
- (when *the-string*
- (ccl::send *the-string* 'dealloc))))</programlisting>
- </refsect1>
-
- <refsect1>
- <title>Notes</title>
-
- <para>
- Currently, ns-lisp-string is defined in
- the file ccl/examples/cocoa-backtrace.lisp, which is a
- rather awkward place. It was probably not originally meant
- as a public utility at all. It would be good if it were
- moved someplace else. Use at your own risk.
- </para>
- </refsect1>
- </refentry>
- </sect1>
- </chapter>
-
- <chapter id=3D"Understanding-and-Configuring-the-Garbage-Collector">
- <title>Understanding and Configuring the Garbage Collector</title>
-
- <sect1 id=3D"Heap-space-allocation">
- <title>Heap space allocation</title>
- <para>Release 0.10 or later of OpenMCL uses a different memory
- management scheme than previous versions did. Those earlier
- versions would allocate a block of memory (of specified size) at
- startup and would allocate lisp objects within that block. When
- that block filled with live (non-GCed) objects, the lisp would
- signal a "heap full" condition. The heap size imposed a limit on
- the size of the largest object that could be allocated.</para>
- <para>The new strategy involves reserving a very large (2GB on
- DarwinPPC32, 1GB on LinuxPPC, "very large" on 64-bit
- implementations) block at startup and consuming (and
- relinquishing) its contents as the size of the live lisp heap
- data grows and shrinks. After the initial heap image loads and
- after each full GC, the lisp kernel will try to ensure that a
- specified amount (the "lisp-heap-gc-threshold") of free memory
- is available. The inital value of this kernel variable is 16MB
- on 32-bit implementations and 32MB on 64-bit implementations ;
- it can be manipulated from Lisp (see below.)</para>
- <para>The large reserved memory block consumes very little in
- the way of system resources; memory that's actually committed to
- the lisp heap (live data and the "threshold" area where
- allocation takes place) consumes finite resources (physical
- memory and swap space). The lisp's consumption of those
- resources is proportional to its actual memory usage, which is
- generally a good thing.</para>
- <para>This scheme is much more flexible than the old one, but it
- may also increase the possibility that those resources can
- become exhausted. Neither the new scheme nor the old handles
- that situation gracefully; under the old scheme, a program that
- consumes lots of memory may have run into an artificial limit on
- heap size before exhausting virtual memory.</para> =
-
- <para>The -R or –heap-reserve command-line option can be
- use to limit the size of the reserved block and therefore bound
- heap expansion. Running</para>
- <programlisting>
-> openmcl --heap-reserve 8M
-</programlisting>
- <para>would provide an execution environment that's very similar to
-that provided by earlier OpenMCL versions.</para>
- </sect1>
-
- <sect1 id=3D"The-Ephemeral-GC">
- <title>The Ephemeral GC</title>
- <para>For many programs, the following observations are true to
- a very large degree:</para>
-
- <orderedlist continuation=3D"restarts" inheritnum=3D"ignore">
- <listitem>
- <para>Most heap-allocated objects have very short lifetimes ("are
- ephemeral"): they become inaccessible soon after they're created.</=
para>
- </listitem>
-
- <listitem>
- <para>Most non-ephemeral objects have very long lifetimes: it's
- rarely productive for the GC to consider reclaiming them, since
- it's rarely able to do so. (An object that's survived a large
- number of GCs is likely to survive the next one. That's not always
- true of course, but it's a reasonable heuristic.)</para>
- </listitem>
-
- <listitem>
- <para>It's relatively rare for an old object to be destructively
- modified (via SETF) so that it points to a new one, therefore most
- references to newly-created objects can be found in the stacks and
- registers of active threads. It's not generally necessary to scan
- the entire heap to find references to new objects (or to prove that
- such references don't exists), though it is necessary to keep
- track of the (hopefully exceptional) cases where old objects are
- modified to point at new ones.</para>
- </listitem>
- </orderedlist>
- <orderedlist continuation=3D"restarts" inheritnum=3D"ignore">
- <listitem>
- <para>Most heap-allocated objects have very short lifetimes ("are
- ephemeral"): they become inaccessible soon after they're created.</=
para>
- </listitem>
-
- <listitem>
- <para>Most non-ephemeral objects have very long lifetimes: it's
- rarely productive for the GC to consider reclaiming them, since
- it's rarely able to do so. (An object that's survived a large
- number of GCs is likely to survive the next one. That's not always
- true of course, but it's a reasonable heuristic.)</para>
- </listitem>
-
- <listitem>
- <para>It's relatively rare for an old object to be destructively
- modified (via SETF) so that it points to a new one, therefore most
- references to newly-created objects can be found in the stacks and
- registers of active threads. It's not generally necessary to scan
- the entire heap to find references to new objects (or to prove that
- such references don't exists), though it is necessary to keep
- track of the (hopefully exceptional) cases where old objects are
- modified to point at new ones.</para>
- </listitem>
- </orderedlist>
-
- <para>"Ephemeral" (or "generational") garbage collectors try to
- exploit these observations: by concentrating on frequently
- reclaiming newly-created objects quickly, it's less often
- necessary to do more expensive GCs of the entire heap in order
- to reclaim unreferenced memory. In some environments, the
- pauses associated with such full GCs can be noticable and
- disruptive, and minimizing the frequency (and sometimes the
- duration) of these pauses is probably the EGC's primary goal
- (though there may be other benefits, such as increased locality
- of reference and better paging behavior.) The EGC generally
- leads to slightly longer execution times (and slightly higher,
- amortized GC time), but there are cases where it can improve
- overall performance as well; the nature and degree of its impact
- on performance is highly application-dependant.</para>
- <para>Most EGC strategies (including the one employed by
- OpenMCL) logically or physically divide memory into one or more
- areas of relatively young objects ("generations") and one or
- more areas of old objects. Objects that have survived one or
- more GCs as members of a young generation are promoted (or
- "tenured") into an older generation, where they may or may not
- survive long enough to be promoted to the next generation and
- eventually may become "old" objects that can only be reclaimed
- if a full GC proves that there are no live references to them.
- This filtering process isn't perfect - a certain amount of
- premature tenuring may take place - but it usually works very
- well in practive.</para>
- <para>It's important to note that a GC of the youngest
- generation is typically very fast (perhaps a few milliseconds on
- a modern CPU, depending on various factors), OpenMCL's EGC is
- not concurrent and doesn't offer realtime guarantees.</para>
- <para>OpenMCL's EGC maintains three ephemeral generations; all
- newly created objects are created as members of the youngest
- generation. Each generation has an associated
- <emphasis>threshold</emphasis>, which indicates the number of
- bytes in it and all younger generations that can be allocated
- before a GC is triggered. These GCs will involve the target
- generation and all younger ones (and may therefore cause some
- premature tenuring); since the older generations have larger
- thresholds, they're GCed less frequently and most short-lived
- objects that make it into an older generation tend not to
- survive there very long.</para>
- <para>The EGC can be <emphasis>enabled</emphasis> or
- <emphasis>disabled</emphasis> under program control; under some
- circumstances, it may be enabled but
- <emphasis>inactive</emphasis> (because a full GC is imminent.)
- Since it may be hard to know or predict the consing behavior of
- other threads, the distinction between the "active" and
- "inactive" state isn't very meaningful, especially when native
- threads are involved.</para>
- </sect1>
-
- <sect1 id=3D"GC-Page-reclamation-policy">
- <title>GC Page reclamation policy</title>
- <para>After a full GC finishes, it'll try to ensure that at
- least (LISP-HEAP-GC-THRESHOLD) of virtual memory are available;
- objects will be allocated in this block of memory until it fills
- up, the GC is triggered, and the process repeats itself.</para>
- <para>Many programs reach near stasis in terms of the amount of
- logical memory that's in use after full GC (or run for long
- periods of time in a nearly static state), so the logical
- address range used for consing after the Nth full GC is likely
- to be nearly or entirely identical to the address range used by
- the N+1th full GC.</para>
- <para>By default (and traditionally in OpenMCL), the GC's policy
- is to "release" the pages in this address range: to advise the
- virtual memory system that the pages contain garbage and any
- physical pages associated with them don't need to be swapped out
- to disk before being reused and to (re-)map the logical address
- range so that the pages will be zero-filled by the virtual
- memory system when they're next accessed. This policy is
- intended to reduce the load on the VM system and keep OpenMCL's
- working set to a minimum.</para>
- <para>For some programs (especially those that cons at a very
- high rate), the default policy may be less than ideal: releasing
- pages that're going to be needed almost immediately - and
- zero-fill-faulting them back in, lazily - incurs unnecessary
- overhead. (There's a false economy associated with minimizing
- the size of the working set if it's just going to shoot back up
- again until the next GC.) A policy of "retaining" pages between
- GCs might work better in such an environment.</para>
- <para>Functions described below give the user some control over
- this behavior. An adaptive, feedback-mediated approach might
- yield a better solution.</para>
- </sect1>
-
- <sect1 id=3D"iPure--areas-are-read-only--paged-from-image-file">
- <title>"Pure" areas are read-only, paged from image file</title>
- <para>SAVE-APPLICATION identifies code vectors and the pnames of
- interned symbols and copies these objects to a "pure" area of
- the image file it creates. (The "pure" area accounts for most of
- what the ROOM function reports as "static" space.)</para>
- <para>When the resulting image file is loaded, the pure area of
- the file is now memory-mapped with read-only access. Code and
- pure data are paged in from the image file as needed (and don't
- compete for global virtual memory resources with other memory
- areas.)</para>
- <para>Code-vectors and interned symbol pnames are immutable : it
- is an error to try to change the contents of such an
- object. Previously, that error would have manifested itself in
- some random way. In the new scheme, it'll manifest itself as an
- "unhandled exception" error in the Lisp kernel. The kernel could
- probably be made to detect a spurious, accidental write to
- read-only space and signal a lisp error in that case, but it
- doesn't yet do so.</para>
- <para>The image file should be opened and/or mapped in some mode
- which disallows writing to the memory-mapped regions of the file
- from other processes. I'm not sure of how to do that; writing to
- the file when it's mapped by OpenMCL can have unpredictable and
- unpleasant results. SAVE-APPLICATION will delete its output
- file's directory entry and create a new file; one may need to
- exercise care when using file system utilities (like tar, for
- instance) that might overwrite an existing image file.</para>
- </sect1>
-
- <sect1 id=3D"Weak-Hash-Tables">
- <title>Weak Hash Tables</title>
- <para>In general, a "weak reference" is a reference to an object
- which will not prevent the object from being garbage-collected.
- For example, suppose that you want to keep a list of all the
- objects of a certain type. If you don't take special steps, the
- fact that you have a list of them will mean that the objects are
- always "live", because you can always reference them through the
- list. Therefore, they will never be garbage-collected, and
- their memory will never be reclaimed, even if they are
- referenced nowhere else in the program. You may want this
- behaviour. If you don't, you need weak references.</para>
- <para>OpenMCL supports weak references with "weak hash tables".
- Hash tables may be weak with respect to either their keys or
- their values. To make a hash table with weak keys, invoke
- <literal>make-hash-table</literal> with the option :weak t, or,
- equivalently, :weak :key. To make one with weak values, use
- :weak :value. When the key is weak, the equality test must be
- #'eq (because it wouldn't make sense otherwise).</para>
- <para>When garbage-collection occurs, key-value pairs are
- removed from the hash table if there are no other references to
- the weak element of the pair (key or value).</para>
- <para>In general, weak-key hash tables are useful when you want
- to use the hash to store some extra information about the
- objects you look up in it, while weak-value hash tables are
- useful when you want to use the hash as an index for looking up
- objects.</para>
- <para>If you are experimenting with weak hash tables
- interactively, remember that an object is not dead if it was
- returned by one of the last three interactively-evaluated
- expressions, because of the variables <literal>*</literal>,
- <literal>**</literal>, and <literal>***</literal>. The easy
- workaround is to evaluate some meaningless expression before
- invoking <literal>gc</literal>, to get the object out of the
- repl variables.</para>
- </sect1>
-
- <sect1 id=3D"Garbage-Collection-Dictionary">
- <title>Garbage-Collection Dictionary</title>
-
- <refentry id=3D"f_lisp-heap-gc-threshold">
- <indexterm zone=3D"f_lisp-heap-gc-threshold">
- <primary>lisp-heap-gc-threshold</primary>
- </indexterm>
-
- <refnamediv>
- <refname>LISP-HEAP-GC-THRESHOLD</refname>
- <refpurpose></refpurpose>
- <refclass>Function</refclass>
- </refnamediv>
-
- <refsynopsisdiv>
- <synopsis><function>lisp-heap-gc-threshold</function></synopsis>
- </refsynopsisdiv>
-
- <refsect1>
- <title>Description</title>
-
- <para>Returns the value of the kernel variable that specifies the
- amount of free space to leave in the heap after full GC.</para>
- </refsect1>
- </refentry>
-
- <refentry id=3D"f_set-lisp-heap-gc-threshold">
- <indexterm zone=3D"f_set-lisp-heap-gc-threshold">
- <primary>set-lisp-heap-gc-threshold</primary>
- </indexterm>
-
- <refnamediv>
- <refname>SET-LISP-HEAP-GC-THRESHOLD</refname>
- <refpurpose></refpurpose>
- <refclass>Function</refclass>
- </refnamediv>
-
- <refsynopsisdiv>
- <synopsis>
- <function>lisp-heap-gc-threshold new-threshold</function>
- </synopsis>
- </refsynopsisdiv>
-
- <refsect1>
- <title>Arguments and Values</title>
-
- <variablelist>
- <varlistentry>
- <term>new-value</term>
-
- <listitem>
- <para>The requested new lisp-heap-gc-threshold.</para>
- </listitem>
- </varlistentry>
- </variablelist>
- </refsect1>
-
- <refsect1>
- <title>Description</title>
-
- <para>Sets the value of the kernel variable that specifies the
- amount of free space to leave in the heap after full GC to
- new-value, which should be a non-negative fixnum. Returns the
- value of that kernel variable (which may be somewhat larger than
- what was specified).</para>
- </refsect1>
- </refentry>
-
- <refentry id=3D"f_use-lisp-heap-gc-threshold">
- <indexterm zone=3D"f_use-lisp-heap-gc-threshold">
- <primary>use-lisp-heap-gc-threshold</primary>
- </indexterm>
-
- <refnamediv>
- <refname>USE-LISP-HEAP-GC-THRESHOLD</refname>
- <refpurpose></refpurpose>
- <refclass>Function</refclass>
- </refnamediv>
-
- <refsynopsisdiv>
- <synopsis>
- <function>use-lisp-heap-gc-threshold</function>
- </synopsis>
- </refsynopsisdiv>
-
- <refsect1>
- <title>Description</title>
-
- <para>Tries to grow or shrink lisp's heap space, so that the
- free space is (approximately) equal to the current heap threshold.
- Returns NIL</para>
- </refsect1>
- </refentry>
-
- <refentry id=3D"f_egc">
- <indexterm zone=3D"f_egc">
- <primary>egc</primary>
- </indexterm>
-
- <refnamediv>
- <refname>EGC</refname>
- <refpurpose></refpurpose>
- <refclass>Function</refclass>
- </refnamediv>
-
- <refsynopsisdiv>
- <synopsis><function>egc</function> arg</synopsis>
- </refsynopsisdiv>
-
- <refsect1>
- <title>Arguments and Values</title>
-
- <variablelist>
- <varlistentry>
- <term>arg</term>
-
- <listitem>
- <para>a generalized boolean</para>
- </listitem>
- </varlistentry>
- </variablelist>
- </refsect1>
-
- <refsect1>
- <title>Description</title>
-
- <para>Enables the EGC if arg is non-nil, disables the EGC
- otherwise. Returns the previous enabled status. Although this
- function is thread-safe (in the sense that calls to it are
- serialized), it doesn't make a whole lot of sense to be
- turning the EGC on and off from multiple threads ...</para>
- </refsect1>
- </refentry>
-
- <refentry id=3D"f_egc-enabled-p">
- <indexterm zone=3D"f_egc-enabled-p">
- <primary>egc-enabled-p</primary>
- </indexterm>
-
- <refnamediv>
- <refname>EGC-ENABLED-P</refname>
- <refpurpose></refpurpose>
- <refclass>Function</refclass>
- </refnamediv>
-
- <refsynopsisdiv>
- <synopsis><function>egc-enabled-p</function></synopsis>
- </refsynopsisdiv>
-
- <refsect1>
- <title>Description</title>
-
- <para>Returns T if the EGC was enabled at the time of the call,
- NIL otherwise.</para>
- </refsect1>
- </refentry>
-
- <refentry id=3D"f_egc-active-p">
- <indexterm zone=3D"f_egc-active-p">
- <primary>egc-active-p</primary>
- </indexterm>
-
- <refnamediv>
- <refname>EGC-ACTIVE-P</refname>
- <refpurpose></refpurpose>
- <refclass>Function</refclass>
- </refnamediv>
-
- <refsynopsisdiv>
- <synopsis><function>egc-active-p</function></synopsis>
- </refsynopsisdiv>
-
- <refsect1>
- <title>Description</title>
-
- <para>Returns T if the EGC was active at the time of the call, NIL
- otherwise. Since this is generally a volatile piece of
- information, it's not clear whether this function serves a
- useful purpose when native threads are involved.</para>
- </refsect1>
- </refentry>
-
- <refentry id=3D"f_egc-configuration">
- <indexterm zone=3D"f_egc-configuration">
- <primary>egc-configuration</primary>
- </indexterm>
-
- <refnamediv>
- <refname>EGC-CONFIGURATION</refname>
- <refpurpose></refpurpose>
- <refclass>Function</refclass>
- </refnamediv>
-
- <refsynopsisdiv>
- <synopsis><function>egc-configuration</function></synopsis>
- </refsynopsisdiv>
-
- <refsect1>
- <title>Description</title>
-
- <para>Returns, as multiple values, the sizes in kilobytes of the
- thresholds associated with the youngest ephemeral generation, the
- middle ephemeral generation, and the oldest ephemeral generation</para>
- </refsect1>
- </refentry>
-
- <refentry id=3D"f_configure-gcc">
- <indexterm zone=3D"f_configure-gcc">
- <primary>configure-gcc</primary>
- </indexterm>
-
- <refnamediv>
- <refname>CONFIGURE-GCC</refname>
- <refpurpose></refpurpose>
- <refclass>Function</refclass>
- </refnamediv>
-
- <refsynopsisdiv>
- <synopsis><function>configure-egc</function>
- generation-0-size generation-1-size
- generation-2-size</synopsis>
- </refsynopsisdiv>
-
- <refsect1>
- <title>Arguments and Values</title>
-
- <variablelist>
- <varlistentry>
- <term>generation-0-size</term>
-
- <listitem>
- <para>the requested threshold size of the youngest
- generation, in kilobytes</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>generation-1-size</term>
-
- <listitem>
- <para>the requested threshold size of the middle generation,
- in kilobytes</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>generation-2-size</term>
-
- <listitem>
- <para>the requested threshold size of the oldest generation,
- in kilobytes</para>
- </listitem>
- </varlistentry>
- </variablelist>
- </refsect1>
-
- <refsect1>
- <title>Description</title>
-
- <para>If the EGC is currently disabled, puts the indicated
- threshold sizes in effect and returns T, otherwise, returns NIL.
- (The provided threshold sizes are rounded up to a multiple of
- 64Kbytes in OpenMCL 0.14 and to a multiple of 32KBytes in earlier
- versions.)</para>
- </refsect1>
- </refentry>
-
- <refentry id=3D"f_gc-retain-pages">
- <indexterm zone=3D"f_gc-retain-pages">
- <primary>gc-retain-pages</primary>
- </indexterm>
-
- <refnamediv>
- <refname>GC-RETAIN-PAGES</refname>
- <refpurpose></refpurpose>
- <refclass>Function</refclass>
- </refnamediv>
-
- <refsynopsisdiv>
- <synopsis><function>gc-retain-pages</function> arg</synopsis>
- </refsynopsisdiv>
-
- <refsect1>
- <title>Arguments and Values</title>
-
- <variablelist>
- <varlistentry>
- <term>arg</term>
-
- <listitem>
- <para>a generalized boolean</para>
- </listitem>
- </varlistentry>
- </variablelist>
- </refsect1>
-
- <refsect1>
- <title>Description</title>
-
- <para>Tries to influence the GC to retain/recycle the pages
- allocated between GCs if arg is true, and to release them
- otherwise. This is generally a tradeoff between paging and other
- VM considerations.</para>
- </refsect1>
- </refentry>
-
- <refentry id=3D"f_gc-retaining-pages">
- <indexterm zone=3D"f_gc-retaining-pages">
- <primary>gc-retaining-pages</primary>
- </indexterm>
-
- <refnamediv>
- <refname>GC-RETAINING-PAGES</refname>
- <refpurpose></refpurpose>
- <refclass>Function</refclass>
- </refnamediv>
-
- <refsynopsisdiv>
- <synopsis><function>gc-retaining-pages</function></synopsis>
- </refsynopsisdiv>
-
- <refsect1>
- <title>Description</title>
-
- <para>Returns T if the GC tries to retain pages between full GCs
- and NIL if it's trying to release them to improve VM paging
- performance.</para>
- </refsect1>
- </refentry>
- </sect1>
- </chapter>
-
- <chapter id=3D"Implementation-Details-of-OpenMCL">
- <title>Implementation Details of OpenMCL</title>
- <para>This chapter describes many aspects of OpenMCL's
- implementation as of (roughly) version 1.1. Details vary a bit
- between the three archutectures (PPC32, PPC64, and X86-64)
- currently supported and those details change over time, so the
- definitive reference is the source code (especially some files in
- the ccl/compiler/ directory whose names contain the string "arch"
- and some files in the ccl/lisp-kernel/ directory whose namee
- contain the string "constants".) Hopefully, this chapter will
- make it easier for someone who's interested to read and understand
- the contents of those files.</para>
-
- <sect1 id=3D"Threads-and-exceptions">
- <title>Threads and exceptions</title>
- <para>OpenMCL's threads are "native" (meaning that they're
- scheduled and controlled by the operating system.) Most of the
- implications of this are discussed elsewhere; this section tries
- to describe how threads look from the lisp kernel's perspective
- (and especailly from the GC's point of view.)</para>
- <para>OpenMCL's runtime system tries to use machine-level
- exception mechanisms (conditional traps when available, illegal
- instructions, memory access protection in some cases) to detect
- and handle ... exceptional situations. These situations
- include some TYPE-ERRORs and PROGRAM-ERRORS (notably
- wrong-number-of-args errors), and also include cases like "not
- being able to allocate memory without GCing or obtaining more
- memory from the OS." The general idea is that it's usually
- faster to pay (very occasional) exception-processing overhead
- and figure out what's going on in an exception handler than it
- is to maintain enough state and context to handle an exceptional
- case via a lighter-weight mechanism when that exceptional case
- (by definition) rarely occurs.</para>
- <para>Some emulated execution environments (the Rosetta PPC
- emulator on x86 versions of OSX) don't provide accurate
- exception information to exception handling functions. OpenMCL
- can't run in such environments.</para>
-
- <sect2 id=3D"The-Thread-Context-Record">
- <title>The Thread Context Record</title>
-
- <para>When a lisp thread is first created (or when a thread
- created by foreign code first calls back to lisp), a data
- structure called a Thread Context Record (or TCR) is allocated
- and initialized. On modern versions of Linux and FreeBSD, the
- allocation actually happens via a set of thread-local-storage
- ABI extensions, so a thread's TCR is created when the thread
- is created and dies when the thread dies. (The World's Most
- Advanced Operating System - as Apple's marketing literature
- refers to Darwin - is not very advanced in this regard, and I
- know of no reason to assume that advances will be made in this
- area anytime soon.)</para>
- <para>A TCR contains a few dozen fields (and is therefore a
- few hundred bytes in size.) The fields are mostly
- thread-specific information about the thread's stacks'
- locations and sizes, information about the underlying (POSIX)
- thread, and information about the thread's dynamic binding
- history and pending CATCH/UNWIND-PROTECTs. Some of this
- information could be kept in individual machine registers
- while the thread is running (and the PPC - which has more
- registers available - keeps a few things in registers that the
- X86-64 has to access via the TCR), but it's important to
- remember that the information is thread-specific and can't
- (for instance) be kept in a fixed global memory
- location.</para>
- <para>When lisp code is running, the current thread's TCR is
- kept in a register. On PPC platforms, a general purpose
- register is used; on x86-64, an (otherwise nearly useless)
- segment register works well (prevents the expenditure of a
- more generally useful general- purpose register for this
- purpose.)</para>
- <para>The address of a TCR is aligned in memory in such a way
- that a FIXNUM can be used to represent it. The lisp function
- CCL::%CURRENT-TCR returns the calling thread's TCR as a
- fixnum; actual value of the TCR's address is 4 or 8 times the
- value of this fixnum.</para>
- <para>When the lisp kernel initializes a new TCR, it's added
- to a global list maintained by the kernel; when a thread
- exits, its TCR is removed from this list.</para>
- <para>When a thread calls foreign code, lisp stack pointers
- are saved in its TCR, lisp registers (at least those whose
- value should be preserved across the call) are saved on the
- thread's value stack, and (on x86-64) RSP is switched to the
- control stack. A field in the TCR (tcr.valence) is then set
- to indicate that the thread is running foreigm code, foreign
- argument registers are loaded from a frame on the foreign
- stack, and the foreign function is called. (That's a little
- oversimplified and possibly inaccurate, but the important
- things to note are that the thread "stops following lisp stack
- and register usage conventions" and that it advertises the
- fact that it's done so. Similar transitions in a thread's
- state ("valence") occur when it enters of exits an exception
- handler (which is sort of an OS/hardware-mandated foreign
- function call where the OS thoughtfully saves the thread's
- register state for it beforehand.)</para>
- </sect2>
-
- <sect2 id=3D"Exception-contexts-comma---and-exception-handling-in-ge=
neral">
- <title>Exception contexts, and exception-handling in general</title>
- <para>Unix-like OSes tend to refer to exceptions as "signals";
- the same general mechanism ("signal handling") is used to
- process both asynchronous OS-level events (such as the result
- of the keyboard driver noticing that ^C or ^Z has been
- pressed) and synchronous hardware-level events (like trying to
- execute and illegal instruction or access protected memory.)
- It makes some sense to defer ("block") handling of
- aysnchronous signals so that some critical code sequences
- complete without interruption; since it's generally not
- possible for a thread to proceed after a synchronous exception
- unless and until its state is modified by an exception
- handler, it makes no sense to talk about blocking synchronous
- signals (though some OSes will let you do so and doing so can
- have mysterious effects.)</para>
- <para>On OSX/Darwin, the POSIX signal handling facilities
- coexist with lower-level Mach-based exception handling
- facilities. Unfortunately, the way that this is implemented
- interacts poorly with debugging tools: GDB will generally stop
- whenever the target program encounters a Mach-level exception
- and offers no way to proceed from that point (and let the
- program's POSIX signal handler try to handle the exception);
- Apple's CrashReporter program has had a similar issue and,
- depending on how it's configured, may bombard the user with
- alert dialogs which falsely claim that an application has
- crashed (when in fact the application in question has
- routinely handled a routine exception.) On Darwin/OSX,
- OpenMCL uses Mach thread-level exception handling facilities
- which run before GDB or CrashReporter get a chance to confuse
- themeselves; OpenMCL's Mach exception handling tries to force
- the thread which received a synchronous exception to invoke a
- signal handling function ("as if" signal handling worked more
- usefully under Darwin.) Mach exception handlers run in a
- dedicated thread (which basically does nothing but wait for
- exception messages from the lisp kernel, obtain and modify
- information about the state of threads in which exceptions
- have occurred, and reply to the exception messages with an
- indication that the exception has been handled. The reply
- from a thread-level exception handler keeps the exception from
- being reported to GDB or CrashReporter and avoids the problems
- related to those programs. Since OpenMCL's Mach exception
- handler doesn't claim to handle debugging-related exceptions
- (from breakpoints or single-step operations), it's possible to
- use GDB to debug OpenMCL.</para>
- <para>On platforms where signal handling and debugging don't get i=
n each
-other's way, a signal handler is entered with all signals blocked.
-(This behavior is specified in the call to the sigaction() function
-which established the signal handler.) The signal handler recieves
-three arguments from the OS kernel; the first is an intger which
-identifies the signal, the second is a pointer to an object of
-type "siginfo_t", which may or may not contain a few fields that
-would help to identify the cause of the exception, and the third
-argument is a pointer to a data structure (called a "ucontext"
-or something similar) which contains machine-dependent information
-about the state of the tread at the time that the exception/signal
-occurred. While asynchronous signals are blocked, the signal handler
-stores the pointer to its third argument (the "signal context") in
-a field in the current thread's TCR, sets some bits in another TCR
-field to indicate that the thread is now waiting to handle an
-exception, unblocks asynchronous signals, and waits for a global
-exception lock which serializes exception processing.</para>
- <para>On Darwin, the Mach exception thread creates a signal
- context (and maybe a siginfo_t structure), stores the signal
- context in the thread's TCR, sets the TCR field wich describes
- the thread's state, and arranges that the thread resume
- execution at its signal handling function (with a signal
- handler, possibly NULL siginfo_t, and signal context as
- arguments. When the thread resumes, it waits for the global
- exception lock.</para>
- <para>On x86-64 platforms where signal handing can be used to
- handle synchronous exceptions, there's an additional
- complication: the OS kernel ordinarily allocates the signal
- context and siginfo structures on the stack of the thread
- which received the signal; in practice, that means "wherever
- RSP is pointing." OpenMCL's require that the thread's value
- stack - where RSP is usually pointing while lisp code is
- running - contain only "nodes" (properly tagged lisp objects),
- and scribbling a signal context all over the value stack would
- violate this requirement. To maintain consistency, the
- sigaltstack() mechanism is used to cause the signal to be
- delivered on (and the signal context and siginfo to be
- allocated on) a special stack area (the last few pages of the
- thread's cntrol stack, in practice. When the signal handler
- runs, it (carefully) copies the signal context and siginfo to
- the thread's control stack and makes RSP point into that stack
- before invoking the "real" signal handler. (The effect of
- this hack is that the "real" signal handler always runs on the
- thread's control stack.)</para>
- <para>Once the exception handler has obtained the global
- exception lock, it uses the values of the signal number,
- siginfo_t, and signal context arguments to determine the
- (logical) cause of the exception. Some exceptions may be
- caused by factors that should generate lisp errors or other
- serious conditions (stack overflow); if this is the case, the
- kernel code may release the global exception lock and call out
- to lisp code. (The lisp code in question may need to repeat
- some of the exception decoding process; in particular, it
- needs to be able to interpret register values in the signal
- context that it receives as an argument.)</para>
- <para>In some cases, the lisp kernel exception handler may not
- be able to recover from the exception (this is currently true
- of some types of memory-access fault and is also true of traps
- or illegal instructions that occur during foreign code
- execution. In such cases, the kernel exception handler
- reports the exception as "unhandled", and the kernel debugger
- is invoked.</para>
- <para>If the kernel exception handler identifies the
- exception' cause as being a transient out-of-memory condition
- (indicating that the current thread needs more memory to cons
- in), it tries to make that memory available. In some cases,
- doing so involves invoking the GC.</para>
- </sect2>
-
- <sect2 id=3D"Threads-comma---exceptions-comma---and-the-GC">
- <title>Threads, exceptions, and the GC</title>
- <para>OpenMCL's GC is not concurrent: when the GC is invoked
- in response to an exception in a particular thread, all other
- lisp threads must stop until the GC's work is done. The
- thread that triggered the GC iterates over the global TCR
- list, sending each other thread a distinguished "suspend"
- signal, then iterates over the list again, waiting for a
- per-thread semaphore that indicates that the thread has
- received the "suspend" signal and responded appropriatedly.
- Once all other threads have acknowledged the request to
- suspend themselves, the GC thread can run the GC proper (after
- doing any necessary .) Once the GC's completed its work, the
- thread that invoked the GC iterates over the global TCR list,
- raising a per-thread "resume" semaphore for each other
- thread.</para>
- <para>The signal handler for the asynchronous "suspend" signal
- is entered with all asynchronous signals blocked. It saves
- its signal-context argument in a TCR slot, raises the tcr's
- "suspend" semaphore, then waits on the TCR's "resume"
- semaphore.</para>
- <para>The GC thread has access to the signal contexts of all
- TCRs (including its own) at the time when the thread received
- an exception or acknowledged a request to suspend itself.
- This information (and information about stack areas in the TCR
- itself) allows the GC to identify the "stack locations and
- register contents" that are elements of the GC's root
- set.</para>
- </sect2>
-
- <sect2 id=3D"PC-lusering">
- <title>PC-lusering</title>
- <para>It's not quite accurate to say that OpenMCL's compiler
- and runtime follow precise stack and register usage
- conventions at all times; there are a few exceptions:</para>
-
- <itemizedlist>
- <listitem>
-<para>On both PPC and x86-64 platforms, consing isn't fully atomic.It take=
s at least a few instructions to allocate an object in memory(and slap a he=
ader on it if necesssary); if a thread is interrupted inthe middle of that =
instruction sequence, the new object may or may nothave been created or ful=
ly initialized at the point in time that theinterrupt occurred. (There are=
actually a few different states ofpartial initialization)</para>
-</listitem>
- <listitem>
-<para>On the PPC, the common act of building a lisp control stack frameinv=
olves allocating a four-word frame and storing three register valuesinto th=
at frame. (The fourth word - the back pointer to the previousframe - is au=
tomatically set when the frame is allocated.) The previouscontents of thos=
e three words are unknown (there might have been aforeign stack frame at th=
e same address a few instructions earlier),so interrupting a thread that's =
in the process of initializing aPPC control stack frame isn't GC-safe.</par=
a>
-</listitem>
- <listitem>
-<para>There are similar problems with the initialization of temp stackfram=
es on the PPC. (Allocation and initialization doesn't happenatomically, an=
d the newly allocated stack memory may have undefinedcontents.)</para>
-</listitem>
- <listitem>
-<para>'s write barrier has to be implemented atomically (i.e.,both an inte=
rgenerational store and the update of a correspondingreference bit has to h=
appen without interruption, or neither of theseevents can happen.)</para>
-</listitem>
- <listitem>
-<para>There are a few more similar cases.</para>
-</listitem>
- =
- </itemizedlist>
-
- <para>Fortunately, the number of these non-atomic instruction sequ=
ences is
-small, and fortunately it's fairly easy for the interrupting thread
-to recognize when the interrupted thread is in the middle of such
-a sequence. When this is detected, the interrupting thread modfies
-the state of the interrupted thread (modifying its PC and other
-registers) so that it is no longer in the middle of such a sequenece
-(it's either backed out of it or the remaining instructions are
-emulated.)</para>
- <para>This works because (a) many of the troublesome instruction s=
equences
-are PPC-specific and it's relatively easy to partially disassemble the
-instructions surrounding the interrupted thread's PC on the PPC and
-(b) those instruction sequences are heavily stylized and intended to
-be easily recognized.</para>
- </sect2>
- </sect1>
-
- <sect1 id=3D"Register-usage-and-tagging">
- <title>Register usage and tagging</title>
-
- <sect2 id=3D"Register-usage-and-tagging-overview">
- <title>Overview</title>
- <para>Regardless of other details of its implementation, a
- garbage collector's job is to partition the set of all
- heap-allocated lisp objects (CONSes, STRINGs, INSTANCEs, etc.)
- into two subsets. The first subset contains all objects that
- are transitively referenced from a small set of "root" objects
- (the contents of the stacks and registers of all active
- threads at the time the GC occurs and the values of some
- global variables.) The second subset contains everything
- else: those lisp objects that are not transitively reachable
- from the roots are garbage, and the memory occupied by garbage
- objects can be reclaimed (since the GC has just proven that
- it's impossible to reference them.)</para>
- <para>The set of live, reachable lisp objects basically form
- the nodes of a (usually large) graph, with edges from each
- node A to any other objects (nodes) that object A
- references.</para>
- <para>Some nodes in this graph can never have outgoing edges:
- an array with a specialized numeric or character type usually
- represents its elements in some (possibly more compact)
- specialized way. Some nodes may refer to lisp objects that
- are never allocated in memory (FIXNUMs, CHARACTERs,
- SINGLE-FLOATs on 64-bit platforms ..) This latter class of
- objects are sometimes called "immediates", but that's a little
- confusing because the term "immediate" is sometimes used to
- refer to things that can never be part of the big connectivity
- graph (e.g., the "raw" bits that make up a floating-point
- value, foreign address, or numeric value that needs to be used
- - at least fleetingly - in compiled code.)</para>
- <para>For the GC to be able to build the connectivity graph
- reliably, it's necessary for it to be able to reliably tell
- (a) whether or not a "potential root" - the contents of a
- machine register or stack location - is in fact a node and (b)
- for any node, whether it may have components that refer to
- other nodes.</para>
- <para>There's no reliable way to answer the first question on
- stock hardware. (If everything was a node, as might be the
- case on specially microcoded "lisp machine" hardware, it
- wouldn't even need to be asked.) Since there's no way to just
- look at a machine word (the contents of a machine register or
- stack location) and tell whether or not it's a node or just
- some random non-node value, we have to either adopt and
- enforce strict conventions on register and stack usage or
- tolerate ambiguity.</para>
- <para>"Tolerating ambiguity" is an approach taken by some
- ("conservative") GC schemes; by contrast, OpenMCL's GC is
- "precise", which in this case means that it believes that the
- contents of certain machine registers and stack locations are
- always nodes and that other registers and stack locations are
- never nodes and that these conventions are never violated by
- the compiler or runtime system. The fact that threads are
- preemptively scheduled means that a GC could occur (because of
- activity in some other thread) on any instruction boundary,
- which in turn means that the compiler and runtime system must
- follow precise at all times.</para>
- <para>Once we've decided that a given machine word is a node,
- a describes how the node's value and type are encoded in that
- machine word.</para>
- <para>Most of this - so far - has discussed thigs from the
- GC's very low-level perspective. From a much higher point of
- view, lisp functions accept nodes as arguments, return nodes
- as values, and (usually) perform some operations on those
- arguments in order to produce those results. (In many cases,
- the operations in question involve raw non-node values.)
- Higher-level parts of the lisp type system (functions like
- TYPE-OF and CLASS-OF, etc.) depend on the .</para>
- </sect2>
-
- <sect2 id=3D"pc-locatives-on-the-PPC">
- <title>pc-locatives on the PPC</title>
- <para>On the PPC, there's a third case (besides "node" and
- "immediate" values). As discussed below, a node that denotes
- a memory-allocated lisp object is a biased (tagged) pointer
- -to- that object; it's not generally possible to point -into-
- some composite (multi-element) object (such a pointer would
- not be a node, and the GC would have no way to update the
- pointer if it were to move the underlying object.)</para>
- <para>Such a pointer ("into" the interior of a heap-allocated
- object) is often called a <emphasis>locative</emphasis>; the
- cases where locatives are allowed in OpenMCL mostly involve
- the behavior of function call and return instructions. (To be
- technicaly accurate, the other case also arises on x86-64, but
- that case isn't as user-visible.)</para>
- <para>On the PowerPC (both PPC32 and PPC64), all machine
- instructions are 32 bits wide and all in1struction words are
- allocated on 32-bit boundaries. In PPC OpenMCL, a CODE-VECTOR
- is a specialized type of vector-like object; its elements are
- 32-bit PPC machine instructions. A CODE-VECTOR is an
- attribute of FUNCTION object; a function call involves
- accessing the function's code-vector and jumping to the
- address of its first instruction.</para>
- <para>As each instruction in the code vector sequentially
- executes, the hardware program counter (PC) register advances
- to the address of the next instruction (a locative into the
- code vector); since PPC instructions are always 32 bits wide
- and aligned on 32-bit boundaries, the low two bits of the PC
- are always 0. If the function executes a call (simple call
- instrucions have the mnemonic "bl" on the PPC, which stands
- for "branch and link"), the address of the next instruction
- (also a word-aligned locative into a code-vector) is copied
- into the special- purpose PPC "link register" (lr); a function
- returns to its caller via a "branch to link register" (blr)
- instruction. Some cases of function call and return might
- also use the PPC's "count register" (ctr), and if either the
- lr or ctr needs to be stored in memory it needs to first be
- copied to a general-purpose registers.</para>
- <para>OpenMCL's GC understands that certain registers contain
- these special "pc-locatives" (locatives that point into
- CODE-VECTOR objects); it contains specal support for finding
- the containing CODE-VECTOR object and for adjusting all of
- these "pc-locatives" if the containing object is moved in
- memory. The first part of that - finding the containing
- object - is possible and practical on the PPC because of
- architectural artifcacts (fixed-width instructions and arcana
- of instruction encoding.) It's not possible on x86-64, but
- fortunately not necessary either (though the second part -
- adjusting the PC/RIP when the containing object moves) is both
- necessary and simple.</para>
- </sect2>
-
- <sect2 id=3D"Register-and-stack-usage-conventions">
- <title>Register and stack usage conventions</title>
-
- <sect3 id=3D"Stack-conventions">
- <title>Stack conventions</title>
- <para>On both PPC and X86 platforms, each lisp thread uses 3
- stacks; the ways in which these stacks are used differs
- between the PPC and X86.</para>
- <para>Each thread has:</para>
- <itemizedlist>
- <listitem>
- <para>A "control stack".On both platforms, this is "the
- stack" used by foreign code.On the PPC, it consists of a
- linked list of frameswhere the first word in each frame
- points to the first word in theprevious frame (and the
- outermost frame points to 0.) Some frameson a PPC
- control stack are lisp frames; lisp frames are always 4
- wordsin size and contain (in addition to the back
- pointer to the previousframe) the calling function (a
- node), the return address (a "locative"into the calling
- function's code-vector), and the value to which
- thevalue-stack pointer (see below) should be restored on
- function exit.On the PPC, the GC has to look at
- control-stack frames, identifywhich of those frames are
- lisp frames, and treat the contents ofthe saved function
- slot as a node (and handle the return addresslocative
- specially.)On x86-64, the control stack is used for
- dynamic-extent allocationof immediate objects. Since
- the control stack never contains nodeson x86-64, the GC
- ignores it on that platform.Alignment of the control
- stack follows the ABI conventions of theplatform (at
- least at any point in time where foreign code could
- run.)On PPC, the r1 register always points to the top of
- the current thread'scontrol stack; on x86-64, the RSP
- register points to the top of thecurrent thread's
- control stack when the thread is running foreigncode and
- the address of the top of the control stack is kept in
- thethread's TCR see when not running foreigncode.The
- control stack "grows down."</para>
- </listitem>
- <listitem>
- <para>A "value stack".On both platforms, all values on
- the value stack are nodes (including"tagged return
- addresses" on x86-64.) The value stack is always
- alignedto the native word size; objects are always
- pushed on the value stackusing atomic instructions
- ("stwu"/"stdu" on PPC, "push" on x86-64), sothe contents
- of the value stack between its bottom and top are
- alwaysunambiguously nodes; the compiler usually tries to
- pop or discardnodes from the value stack as soon as
- possible after their last use(as soon as they may have
- become garbage.)On x86-64, the RSP register addresses
- the top of the value stackwhen running lisp code; that
- address is saved in the TCR whenrunning foreign code.On
- the PPC, a dedicated regiter (VSP, currently r15) is
- used toaddress the top of the value stack when running
- lisp code, and theVSP value is saved in the TCR when
- running foreign code.The value stack grows down.</para>
- </listitem>
- <listitem>
- <para>A "temp stack".The temp stack consists of a linked
- list of frames, each of which pointsto the previous temp
- stack frame. The number of native machine wordsin each
- temp stack frame is always even, so the temp stack is
- alignedon a two-word (64- or 128-bit) boundary.The temp
- stack is used for dynamic-extent objects on both
- platforms;on the PPC, it's used for essentially all such
- objects (regardlessof whether or not the objects contain
- nodes); on the x86-64, immediatedynamic-extent objects
- (strings, foreign pointers, etc.) are allocatedon the
- control stack and only node-containing dynamic-extent
- objectsare allocated on the temp stack.Data structures
- used to implement CATCH and UNWIND-PROTECT are stored
- onthe temp stack on both ppc and x86-64.Temp stack
- frames are always doublenode aligned and objects withina
- temp stack frame are aligned on doublenode boundaries.
- The firstword in each frame contains a back pointer to
- the previous frame; onthe PPC, the second word is used
- to indicate to the GC whethe theremaining objects are
- nodes (if the second word is 0) or immediate(otherwise.)
- On x86-64, where temp stack frames always contain
- nodes,the second word is always 0.The temp stack grows
- down. It usually takes several instuctions toallocate
- and safely initialize a temp stack frame that's intended
- tocontain nodes, and the GC has to recognize the case
- where a threadis in the process of allocating and
- initializing a temp stack frameand take care not to
- interpret any uninitialized words in the frameas nodes.
- See .The PPC keeps the current top of the temp stack in
- a dedicated register(TSP, currently r12) when running
- lisp code and saves this register'svalue in the TCR when
- running foreign code. The x86-64 keeps theaddress of
- the top of each thread's temp stack in the thread's
- TCR.</para>
- </listitem>
- </itemizedlist>
- </sect3>
-
- <sect3 id=3D"Register-conventions">
- <title>Register conventions</title>
- <para>If there are a "reasonable" (for some value of
- "reasonable") number or general-purpose registers and the
- instruction set is "reasonably" orthogonal (most
- instructions that operate on GPRs can operate on any GPR),
- then it's possible to statically partition the GPRs into at
- least two sets: "immediate registers" never contain nodes,
- and "node registers" always contain nodes. (On the PPC, a
- few registers are members of a third set of "PC locatives",
- and on both platforms some registers may have dedicated
- roles as stack or heap pointers; the latter class is treated
- as immediates by the GC proper but may be used to help
- determine the bounds of stack and heap memory areas.)</para>
- <para>The ultimate definition of register partitioning is
- hardwired into the GC in functions like "mark_xp()" and
- "forward_xp()", which process the values of some of the
- registers in an exception frame as nodes and may give some
- sort of special treatment to other register values they
- encounter there.)</para>
- <para>On x86-64, the static register partitioning scheme involve=
s:</para>
- <itemizedlist>
- <listitem>
- <para>(only) two "immediate" registers.The RAX and RDX
- registers are used as the implicit operands andresults
- of some extended-precision multiply and divide
- instructionswhich generally involve non-node values;
- since their use in theseinstructions means that they
- can't be guaranteed to contain nodevalues at all times,
- it's natural to put these registers in the"immediate"
- set. RAX is generally given the symbolic name
- "imm0",and RDX is given the symbolic name "imm1"; you
- may see these namesin disassembled code, usually in
- operations involving type checking,array indexing, and
- foreign memory and function access.</para>
- </listitem>
- <listitem>
- <para>(only) two "dedicated" registers.RSP and RBP have
- dedicated functionality dictated by the hardwareand
- calling conventions. (There are a few places where RBP
- istemporarily used as an extra immediate
- register.)</para>
- </listitem>
- <listitem>
- <para>12 "node" registers.All other registers (RBX, RCX,
- RSI, RDI, and R8-R15) are asserted tocontain node values
- at (almost) all times; legacy "string" operationsthat
- implicitly use RSI and/or RDI are not used. Shift and
- rotateintructions which shift/rotate by a variable
- number of bits arerequired by the architecture to use
- the low byte of RCX (the traditionalCL register) as the
- implicit shift count; when it's necessary to keepa
- non-node shift count in the low byte of RCX, the upper 7
- bytes ofthe register are zeroed (so that
- misinterpetation of the immediatevalue in RCX as a node
- will not have negative GC affects. (The GCmight briefly
- treate it as a node, but since it's not pointing
- anywherenear the lisp heap it'll soon lose interest in
- it.)Legacy instructions that use RCX (or some portions
- of it) as a loopcounter can not be used (since such
- instructions might introducenon-node values into
- RCX.)</para>
-</listitem>
- </itemizedlist>
- <para>On the PPC, the static register partitioning scheme involv=
es:</para>
-
- <itemizedlist>
- <listitem>
- <para>6 "immediate" registersRegisters r3-r8 are given
- the symbolic names imm0-imm5. As a RISCarchitecture
- with simpler addressing modes, the PPC probably
- usesimmediate registers a bit more often than the CISC
- x86-64 does, butthey're generally used for the same sort
- of things (type checking,array indexing, FFI,
- etc.)</para>
- </listitem>
- <listitem>
- <para>9 dedicated registers
- <itemizedlist>
- <listitem>
- <para>r0 (symbolic name rzero) always contains the
- value 0 when runninglisp code. Its value is
- sometimes read as 0 when it's used as thebase
- register in a memory address; keeping the value 0
- there issometimes convenient and avoids
- asymmetry.</para>
- </listitem>
- <listitem>
- <para>r1 (symbolic name sp) is the control stack
- pointer, by PPC convention.</para>
- </listitem>
- <listitem>
- <para>r2 is used to hold the current thread's TCR on
- ppc64 systems; it'snot used on ppc32.</para>
- </listitem>
- <listitem>
- <para>r9 and r10 (symbolic names allocptr and
- allocbase) are used to do per-thread memory
- allocation</para>
- </listitem>
- <listitem>
- <para>r11 (symbolic name nargs) contains the number
- of function arguments on entry and the number of
- return values in multiple-value returning
- constructs. It's not used more generally as either
- a node or immediate register because of the way that
- certain trap instruction encodings are
- interpreted.</para>
- </listitem>
- <listitem>
- <para>r12 (symbolic name tsp) holds the top of the current thread's te=
mp stack.</para>
- </listitem>
- <listitem>
- <para>r13 is used to hold the TCR on PPC32 sytems; it's not used on PP=
C64.</para>
- </listitem>
- <listitem>
- <para>r14 (symbolic name loc-pc) is used to copy
- "pc-locative" values between main memory and
- special-purpose PPC registers (LR and CTR) used in
- function-call and return instructions.</para>
- </listitem>
- <listitem>
- <para>r15 (symbolic name vsp) addresses the top of
- the current thread's value stack.</para>
- </listitem>
- <listitem>
- <para>lr and ctr are PPC branch-unit registers used
- in function call and return instructions; they're
- always treated as "pc-locatives", which precludes
- the use of the ctr in some PPC looping
- constructs.</para>
- </listitem>
- =
- </itemizedlist>
- </para>
- </listitem>
- <listitem>
- <para>17 "node" registersr15-r31 are always treated as
- node registers</para>
- </listitem>
- =
- </itemizedlist>
- </sect3>
- </sect2>
-
- <sect2 id=3D"Tagging-scheme">
- <title>Tagging scheme</title>
- <para>OpenMCL always allocates lisp objects on double-node
- (64-bit for 32-bit platforms, 128-bit for 64-bit platforms)
- boundaries; this mean that the low 3 bits (32-bit lisp) or 4
- bits (64-bit lisp) are always 0 and are therefore redundant
- (we only really need to know the upper 29 or 60 bits in order
- to identify the aligned object address.) The extra bits in a
- lisp node can be used to encode at least some information
- about the node's type, and the other 29/60 bits represent
- either an immediate value or a doublenode-aligned memory
- address. The low 3 or 4 bits of a node are called the node's
- "tag bits", and the conventions used to encode type
- information in those tag bits are called a "tagging
- scheme."</para>
- <para>It might be possible to use the same tagging scheme on
- all platforms (at least on all platforms with the same word
- size and/or the same number of available tag bits), but there
- are often some strong reasons for not doing so. These
- arguments tend to be very machine-specific: sometimes, there
- are fairly obvious machine-dependent tricks that can be
- exploited to make common operations on some types of tagged
- objects faster; other times, there are architectural
- restrictions that make it impractical to use certain tags for
- certain types. (On PPC64, the "ld" (load doubleword) and
- "std" (store doubleword) instructions - which load and store a
- GPR operand at the effective address formed by adding the
- value of another GPR operand and a 16-bit constant operand -
- require that the low two bits of that constant operand be 0.
- Since such instructions would typically be used to access the
- fields of things like CONS cells and structures, it's
- desirable that that the tags chosen for CONS cells and
- structures allow the use of these intructions as opposed to
- more expensive alternatives.)</para>
- <para>One architecture-dependent tagging trick that works well
- on all architectures is to use a tag of 0 for FIXNUMs: a
- fixnum basically encodes its value shifted left a few bits and
- keeps those low bits clear. FIXNUM addition, subtraction, and
- binary logical operations can operate directly on the node
- operands, addition and subtraction can exploit hardware-based
- overflow detection, and (in the absence of overflow) the
- hardware result of those operations is a node (fixnum). Some
- other slightly-less-common operations may require a few extra
- instructions, but arithmetic operations on FIXNUMs should be
- as cheap as possible and using a tag of zero for FIXNUMs helps
- to ensure that it will be.</para> =
- <para>If we have N available tag bits (N =3D 3 for 32-bit
- OpenMCL and N =3D 4 for 64-bit OpenMCL), this way of
- representing fixnums with the low M bits forced to 0 works as
- long as M <=3D N. The smaller we make M, the larger the
- values of MOST-POSITIVE-FIXNUM and MOST-NEGATIVE become; the
- larger we make N, the more distinct non-FIXNUM tags become
- available. A reasonable compromise is to choose M =3D N-1; this
- basically yields two distinct FIXNUM tags (one for even
- fixnums, one for odd fixnums), gives 30-bit fixnums on 32-bit
- platforms and 61-bit fixnums on 64-bit platforms, and leaves
- us with 6 or 14 tags to encoded other types.</para>
- <para>Once we get past the assignment of FIXNUM tags, things
- quickly devolve into machine-dependencies. We can fairly
- easily see that we can't directly all other primitive lisp
- object types with only 6 or 14 available tag values; the
- details of how types are encoded vary between the ppc32,
- ppc64, and x86-64 implementations, but there are some general
- common principles:</para>
-
- <itemizedlist>
- <listitem>
- <para>CONS cells always contain exactly 2 elements and are
- usually fairly common.It therefore makes sense to give
- CONS cells their own tag. Unlike thefixnum case - where a
- tag value of 0 had positive implications - theredoesn't
- seem to be any advantage to using any particular value.
- (A longtime ago - in the case of 68K MCL - the CONS tag
- and the order of CAR and CDR in memory were chosen to allow
- smaller, cheaper addressing modes to be used to "cdr down a
- list." That's not a factor on ppc or x86-64,but all
- versions of OpenMCL still store the CDR of a CONS cell
- first in memory. It doesn't matter, but doing it the way
- that the host system did made boostrapping to a new target
- system a little easier.)
- </para>
- </listitem>
- <listitem>
- <para>Any way you look at it, NIL is a bit ... unusual.NIL
- is both a SYMBOL and a LIST (as well as being a canonical
- truth value and probably a few other things.) Its role as
- a LIST is probably much more important to most programs
- than its role as a SYMBOL is:LISTP has to be true of NIL
- and primitives like CAR and CDR do LISTP implicitly when
- safe and want that operation to be fast.There are several
- possible approaches to this; OpenMCL uses two of them. On
- PPC32 and X86-64, NIL is basically a weird CONS cell that
- straddles two doublenodes; the tag of NIL is unique and
- congruent modulo 4 (modulo 8 on 64-bit) with the tag used
- for CONS cells. LISTP is therefore true of any node whose
- low 2 (or 3) bits contain the appropriate tag value (it's
- not otherwise necessary to special-case NIL.)
- SYMBOL accessors (SYMBOL-NAME, SYMBOL-VALUE, SYMBOL-PLIST
- ..) -do- have to special-case NIL (and access the
- components of an internal proxy symbol.) On PPC64 (where
- architectural restrictions dictate the set of tags that can
- be used to access fixed components of an object),
- that approach wasn't practical. NIL is just a
- distinguished SYMBOL,and it just happens to be the case
- that its pname slot and values lots are at the same offsets
- from a tagged pointer as a CONS cell's CDR and CAR would be.
- NIL's pname is set to NIL (SYMBOL-NAME checks for this and
- returns the string "NIL"), and LISTP (and therefore safe
- CAR and CDR) have to check for (OR NULL CONSP).At least in
- the case of CAR and CDR, the fact that the PPC has multiple
- condition-code fields keeps that extra test from
- being prohibitively expensive.</para>
- </listitem>
- <listitem>
- <para>Some objects are immediate.(but not FIXNUMs).This is
- true of CHARACTERs and, on 64-bit platforms,
- SINGLE-FLOATs.It's also true of some nodes used in the
- runtime system (specialvalues used to indicate unbound
- variables and slots, for instance.)On 64-bit platforms,
- SINGLE-FLOATs have their own unique tag (makingthem a
- little easier to recognize; on all platforms, CHARACTERs
- sharea tag with other immediate objects (unbound markers)
- but are easyto recognize (by looking at several of their
- low bits.) The GCtreats any node with an immediate tag
- (and any node with a fixnumtag) as a leaf.</para>
- </listitem>
- <listitem>
- <para>There are some advantages to treating everything
- else - memory-allocated objects that aren't CONS cells -
- uniformly.There are some disadvantages to that uniform
- treatment as well, and the treatment of "memory-allocated
- non-CONS objects" isn't entirely uniformaccross all
- OpenMCL implementations. Let's first pretend that
- the treatment is uniform, then discuss the ways in which it
- isn't.The "uniform approach" is to treat all
- memory-allocated non-CONS objectsas if they were vectors;
- this use of the term is a little looser thanwhat's implied
- by the CL VECTOR type. OpenMCL actually uses the
- term"uvector" to mean "a memory-allocated lisp object
- other than a CONS cell,whose first word is a header which
- describes the object's type andthe number of elements that
- it contains." In this view, a SYMBOL isa UVECTOR, as is a
- STRING, a STANDARD-INSTANCE, a CL array or vector,a
- FUNCTION, and even a DOUBLE-FLOAT.In the PPC
- implementations (where things are a little more
- ... uniform),a single tag value is used to denote any
- uvector; in order to determinesomething more specific
- about the type of the object in question, it'snecessary to
- fetch the low byte of the header word from memory. On
- thex86-64 platform, certain types of uvectors - SYMBOLs
- and FUNCTIONs -are given their own unique tags. The good
- news about the x86-64 approachis that SYMBOLs and
- FUNCTIONs can be recognized without referencingmemory; the
- slightly bad news is that primitive operations that workon
- UVECTOR-tagged objects - like the function CCL:UVREF -
- don't workon SYMBOLs or FUNCTIONs on x86-64 (but -do- work
- on those types of objectsin the PPC ports.)The header word
- which precedes a UVECTOR's data in memory contains 8bits
- of type information in the low byte and either 24 or 56
- bits of"element-count" information in the rest of the
- word. (This is wherethe sometimes-limiting value of 2^24
- for ARRAY-TOTAL-SIZE-LIMIT onPPC32 platforms comes from.)
- The low byte of the header - sometimescalled the uvector's
- subtag - is itself tagged (which means thatthe header is
- tagged.) The (3 or 4) tag bits in the subtag are usedto
- determine whether the uvector's elements are nodes or
- immediates.(A UVECTOR whose elements are nodes is called a
- GVECTOR; a UVECTORwhose elements are immediates is called
- an IVECTOR. This terminologycame from Spice Lisp, which
- was a predecessor of CMUCL.)Even though a uvector header
- is tagged, a header is not a node. There'sno (supported)
- way to get your hands on one in lisp and doing so couldbe
- dangerous. (If the value of a header wound up in a lisp
- noderegister and that register wound up getting pushed on
- a thread's valuestack, the GC might misinterpret that
- situation to mean that therewas a stack-allocated UVECTOR
- on the value stack.)</para>
- </listitem>
- =
- </itemizedlist>
- </sect2>
- </sect1>
-
- <sect1 id=3D"Heap-Allocation">
- <title>Heap Allocation</title> <para>When the OpenMCL kernel
- first starts up, a large contiguous chunk of the process's
- address space is mapped as "anonymous, no access"
- memory. ("Large" means different things in different contexts;
- on LinuxPPC32, it means "about 1 gigabyte", on DarwinPPC32, it
- means "about 2 gigabytes", and on current 64-bit platforms it
- ranges from 128 to 512 gigabytes, depending on OS. These values
- are both defaults and upper limits; the --heap-reserve
- argument can be used to try to reserve less than the
- default.)</para>
- <para>Reserving address space that can't (yet) be read or
- written to doesn't cost much; in particular, it doesn't require
- that correspinding swap space or physical memory be available.
- Marking the address range as being "mapped" helps to ensure that
- other things (result from random calls to malloc(), dynamically
- loaded shared libraries) won't be allocated in this region that
- lisp has reserved for its own heap growth.</para>
- <para>A small portion (around 1/32 on 32-bit platforms and 1/64
- on 64-bit platforms) of that large chunk of address space is
- reserved for GC data structures. Memory pages reserved for
- these data structures are mapped read-write as pages made
- writable in the main portion of the heap.</para>
- <para>The initial heap image is mapped into this reserved
- address space and an additional (LISP-HEAP-GC-THRESHOLD) bytes
- are mapped read-write. GC data structures grow to match the
- amount of GC-able memory in the initial image + the gc
- threshold, and control is transferred to lisp code. Inevitably,
- that code spoils everything and starts consing; there are
- basically three layers of memory allocation that can go
- on.</para>
-
- <sect2 id=3D"Per-thread-object-allocation">
- <title>Per-thread object allocation</title>
- <para>Each lisp thread has a private "reserved memory
- segment"; when a thread starts up, its reserved memory segment
- is empty. PPC ports maintain the highest unallocated addres
- and he lowest allocated address in the current segment in
- registers when running lisp code; on x86-664, these values are
- maintained in the current threads's TCR. (An "empty" heap
- segment is one whose high pointer and low pointer are equal.)
- When a thread is not in the midde of allocating something, the
- low 3 or 4 bits of the high and low pointers are clear (the
- pointers are doublenode-aligned.)</para>
- <para>A thread tries to allocate an object whose physical size
- in bytes is X and whose tag is Y by:</para>
- <orderedlist>
- <listitem>
- <para>decrementing the "high" pointer by (- X Y)</para>
- </listitem>
- <listitem>
- <para>trapping if the high pointer is less than the low
- pointer</para>
- </listitem>
- <listitem>
- <para>using the (tagged) high pointer to initialize the
- object, if necessary</para>
- </listitem>
- <listitem>
- <para>clearing the low bits of the high pointer</para>
- </listitem>
- </orderedlist>
- <para>On PPC32, where the size of a CONS cell is 8 bytes and
- the tag of a CONS cell is 1, machine code which sets the arg_z
- register to the result of doing (CONS arg_y arg_z) looks
- like:</para>
- <programlisting>
- (SUBI ALLOCPTR ALLOCPTR 7) ; decrement the high pointer by (- 8 1)
- (TWLLT ALLOCPTR ALLOCBASE) ; trap if the high pointer is below the ba=
se
- (STW ARG_Z -1 ALLOCPTR) ; set the CDR of the tagged high pointer
- (STW ARG_Y 3 ALLOCPTR) ; set the CAR
- (MR ARG_Z ALLOCPTR) ; arg_z is the new CONS cell
- (RLWINM ALLOCPTR ALLOCPTR 0 0 28) ; clear tag bits
-</programlisting>
- <para>On x86-64, the idea's similar but the implementation is
- different. The high and low pointers to the current thread's
- reserved segment are kept in the TCR, which is addressed by
- the gs segment register. An x86-64 CONS cell is 16 bytes wide
- and has a tag of 3; we canonically use the temp0 register to
- initialize the object</para>
- <programlisting>
- (subq ($ 13) ( (% gs) 216)) ; decrement allocptr
- (movq ( (% gs) 216) (% temp0)); load allocptr into temp0
- (cmpq ( (% gs) 224) (% temp0)) ; compare to allocabase
- (jg L1) ; skip trap
- (uuo-alloc) ; uh, don't skip trap
-L1
- (andb ($ 240) ( (% gs) 216)) ; untag allocptr in the tcr
- (movq (% arg_y) ( 5 (% temp0))) ; set the car
- (movq (% arg_z) ( -3 (% temp0))); set the cdr
- (movq (% temp0) (% arg_z)) ; return the cons
- </programlisting>
- <para>If we don't take the trap (if allocating 8-16 bytes
- doesn't exhaust the thread's reserved memory segment), that's
- a fairly short and simple instruction sequence. If we do take
- the trap, we'll have to do some additional work in order to
- get a new segment for the current thread.</para>
- </sect2>
-
- <sect2 id=3D"Allocation-of-reserved-heap-segments">
- <title>Allocation of reserved heap segments</title>
- <para>After lisp image is first mapped into memory - and after
- each full GC - the lisp kernel ensures that
- (LISP-HEAP-GC-TRESHOLD) additional bytes beyond the current
- end of the heap are mapped read-write.</para>
- <para>If a thread traps while trying to allocate memory, the
- thread goes through the usual exception-handling protocol (to
- ensure that any oher thread that GCs "sees" the state of the
- trapping thread and to serialize exception handling.) When
- the exception handler runs, it determines the nature and size
- of the failed allocation and tries to complete the allocation
- on the thread's behalf (and leave it with a reasonably large
- thread-specific memory segment so that the next small
- allocation is unlikely to trap.</para>
- <para>Depending on the size of the requested segment
- allocation, the number of segment allocations that have
- occurred since the last GC, and the EGC and GC thresholds, the
- segment allocation trap handler may invoke a full or ephemeral
- GC before returning a new segment. It's worth noting that the
- [E]GC is triggered based on the number of and size of these
- segments that've been allocated since the last GC; it doesn't
- have much to do with how "full" each of those per-thread
- segments are. It's possible for a large number of threads to
- do fairly incidental memory allocation and trigger the GC as a
- result; avoiding this involves tuning the per-thread
- allocation quantum and the GC/EGC thresholds
- appropriately.</para>
- </sect2>
-
- <sect2 id=3D"Heap-growth">
- <title>Heap growth</title>
- <para>All OSes on which OpenMCL currently runs use an
- "overcommit" memory allocation strategy by default (though
- some of them provide ways of overriding that default.) What
- this means in general is that the OS doesn't necessarily
- ensure that backing store is available when asked to map pages
- as read-write; it'll often return a success indicator from the
- mapping attempt (mapping the pages as "zero-fill,
- copy-on-write"), and only try to allocate the backing store
- (swap space and/or physical memory) when non-zero contents are
- written to the pages.</para>
- <para>It -sounds- like it'd be better to have the mmap() call
- fail immediately, but it's actually a complicated issue.
- (It's possible that other applications will stop using some
- backing store before lisp code actually touches the pages that
- need it, for instance.) It's also not guaranteed that lisp
- code would be able to "cleanly" signal an out-of-memory
- condition if lisp is ... out of memory</para>
- <para>I don't know that I've ever seen an abrupt out-of-memory fai=
lure that
-wasn't preceeded by several minutes of excessive paging activity. The
-most expedient course in cases like this is to either (a) use less memory
-or (b) get more memory; it's generally hard to use memory that you don't
-have.</para>
- </sect2>
- </sect1>
-
- <sect1 id=3D"GC-details">
- <title>GC details</title>
- <para>The GC uses a Mark/Compact algorithm; its
- execution time is essentially a factor of the amount of live
- data in the heap. (The somewhat better-known Mark/Sweep
- algorithms don't compact the live data but instead traverse the
- garbage to rebuild free-lists; their execution time is therefore
- a factor of the total heap size.)</para>
- <para>As mentioned in , two auxiliary data structures
- (proportional to the size of the lisp heap) are maintained. These ar=
e</para>
- <orderedlist>
- <listitem>
- <para>the markbits bitvector, which contains a bit for
- everydoublenode in the dynamic heap (plus a few extra words
- for alignmentand so that sub-bitvectors can start on word
- boundaries.)</para>
- </listitem>
- <listitem>
- <para>the relocation table, which contains a native word for
- every 32 or 64 doublenodes in the dynamic heap, plus an
- extra word used to keep trackof the end of the heap.</para>
- </listitem>
- </orderedlist>
- <para>The total GC space overhead is therefore on the order of
- 3% (2/64 or 1/32).</para>
- <para>The general algorithm proceeds as follows:</para>
-
- <sect2 id=3D"Mark-phase">
- <title>Mark phase</title>
- <para>Each doublenode in the dynamic heap has a corresponding
- bit in the markbits vector. (For any doublenode in the heap,
- the index of its mark bit is determined by subtracing the
- address of the start of the heap from the address of the
- object and dividing the result by 8 or 16.) The GC knows the
- markbit index of the free pointer, so determining that the
- markbit index of a doubleword address is between the start of
- the heap and the free pointer can be done with a single
- unsigned comparison.</para>
- <para>The markbits of all doublenodes in the dynamic heap are
- zeroed before the mark phase begins. An object is
- <emphasis>marked</emphasis> if the markbits of all of its
- constituent doublewords are set and unmarked otherwise;
- setting an object's markbits involves setting the corrsponding
- markbits of all constituent doublenodes in the object.</para>
- <para>The mark phase traverses each root. If the tag of the
- value of the root indicates that it's a non-immediate node
- whose address lies in the lisp heap, then:</para>
- <orderedlist>
- <listitem>
- <para>If the object is already marked, do nothing.</para>
- </listitem>
- <listitem>
- <para>Set the object's markbit(s).</para>
- </listitem>
- <listitem>
- <para>If the object is an ivector, do nothing further.</para>
- </listitem>
- <listitem>
- <para>If the object is a cons cell, recursively mark its
- car and cdr.</para>
- </listitem>
- <listitem>
- <para>Otherwise, the object is a gvector. Recursively mark
- itselements.</para>
- </listitem>
- </orderedlist>
- <para>Marking an object thus involves ensuring that its mark
- bits are set and then recursively marking any pointers
- contained within the object if the object was originally
- unmarked. If this recursive step was implemented in the
- obvious manner, marking an object would take stack space
- proportional to the length of the pointer chain from some root
- to that object. Rather than storing that pointer chain
- implicitly on the stack (in a series of recursive calls to the
- mark subroutine), the OpenMCL marker uses mixture of recursion
- and a technique called <emphasis>link inversion</emphasis> to
- store the pointer chain in the objects themselves. (Recursion
- tends to be simpler and faster; if a recursive step notes that
- stack space is becoming limited, the link-inversion technique
- is used.)</para>
- <para>Certain types of objects are treated a little specially:</pa=
ra>
- <orderedlist>
- <listitem>
- <para>To support a feature called <emphasis>GCTWA
- <footnote>
- <para>I believe that theacronym comes from MACLISP,
- where it stood for "Garbage Collection ofTruly
- Worthless Atoms".</para>
- </footnote>
- , </emphasis>the vector which contains the
- internalsymbols of the current package is marked on
- entry to the mark phasebut the symbols themselves are
- not marked at this time. Near the endof the mark phase,
- symbols referenced from this vector which are
- nototherwise marked are marked if and only if they're
- somehowdistinguishable from newly created symbols (by
- virtue of their havingfunction bindings, value bindings,
- plists, or other attributes.)</para>
- </listitem>
- <listitem>
- <para>Pools have their first element set to NIL before any
- otherelements are marked.</para>
- </listitem>
- <listitem>
- <para>All hash tables have certain fields (used to cache
- previous results) invalidated.</para>
- </listitem>
- <listitem>
- <para>Weak Hash Tables and other weak objects are put on a
- linkedlist as they're encountered; their contents are only
- retained if there are other (non-weak) references to
- them.</para>
- </listitem>
- </orderedlist>
- <para>At the end of the mark phase, the markbits of all objects wh=
ich
- are transitively reachable from the roots are set and all other markbits
- are clear.</para>
- </sect2>
-
- <sect2 id=3D"Relocation-phase">
- <title>Relocation phase</title>
- <para>The <emphasis>forwarding address</emphasis> of a
- doublenode in the dynamic heap is (<its current address> -
- (size_of_doublenode * <the number of unmarked markbits that
- precede it>)) or alternately (<the base of the heap> +
- (size_of_doublenode * <the number of marked markbits that
- preced it >)). Rather than count the number of preceding
- markbits each time, the relocation table is used to precompute
- an approximation of the forwarding addresses for all
- doublewords. Given this approximate address and a pointer into
- the markbits vector, it's relatively easy to compute the exact
- forwarding address.</para>
- <para>The relocation table contains the forwarding addresses
- of each <emphasis>pagelet</emphasis>, where a pagelet is 256
- bytes (or 32 doublenodes). The forwarding address of the first
- pagelet is the base of the heap. The forwarding address of the
- second pagelet is the sum of the forwarding address of the
- first and 8 bytes for each mark bit set in the first 32-bit
- word in the markbits table. The last entry in the relocation
- table contains the forwarding address that the freepointer
- would have, e.g., the new value of the freepointer after
- compaction.</para>
- <para>In many programs, old objects rarely become garbage and
- new objects often do. When building the relocation table, the
- relocation phase notes the address of the first unmarked
- object in the dynamic heap. Only the area of the heap between
- the first unmarked object and the freepointer needs to be
- compacted; only pointers to this area will need to be
- forwarded (the forwarding address of all other pointers to the
- dynamic heap is the address of that pointer.) Often, the
- first unmarked object is much nearer the free pointer than it
- is to the base of the heap.</para>
- </sect2>
-
- <sect2 id=3D"Forwarding-phase">
- <title>Forwarding phase</title>
- <para>The forwarding phase traverses all roots and the "old"
- part of the dynamic heap (the part between the base of the
- heap and the first unmarked object.) All references to objects
- whose address is between the first unmarked object and the
- free pointer are updated to point to the address the object
- will have after compaction by using the relocation table and
- the markbits vector and interpolating.</para>
- <para>The relocation table entry for the pagelet nearest the
- object is found. If the pagelet's address is less than the
- object's address, the number of set markbits that precede the
- object on the pagelet is used to determine the object's
- address; otherwise, the number of set markbits the follow the
- object on the pagelet is used.</para>
- <para>Since forwarding views the heap as a set of doublewords,
- locatives are (mostly) treated like any other pointers. (The
- basic difference is that locatives may appear to be tagged as
- fixnums, in which case they're treated as word-aligned
- pointers into the object.)</para>
- <para>If the forward phase changes the address of any hash
- table key in a hash table that hashes by address (e.g., an EQ
- hash table), it sets a bit in the hash table's header. The
- hash table code will rehash the hash table's contents if it
- tries to do a lookup on a key in such a table.</para>
- <para>Profiling reveals that about half of the total time
- spent in the GC is spent in the subroutine which determines a
- pointer's forwarding address. Exploiting GCC-specific idioms,
- hand-coding the routine, and inlining calls to it could all be
- expected to improve GC performance.</para>
- </sect2>
-
- <sect2 id=3D"Compact-phase">
- <title>Compact phase</title>
- <para>The compact phase compacts the area between the first
- unmarked object and the freepointer so that it contains only
- marked objects. While doing so, it forwards any pointers it
- finds in the objects it copies.</para>
- <para>When the compact phase is finished, so is the GC (more
- or less): the free pointer and some other data structures are
- updated and control returns to the exception handler that
- invoked the GC. If sufficient memory has been freed to satisfy
- any allocation request that may have triggered the GC, the
- exception handler returns; otherwise, a "seriously low on
- memory" condition is signalled, possibly after releasing a
- small emergency pool of memory.</para>
- </sect2>
- </sect1>
-
- <sect1 id=3D"The-ephemeral-GC">
- <title>The ephemeral GC</title>
- <para>In the OpenMCL memory management scheme, the relative age
- of two objects in the dynamic heap can be determined by their
- addresses: if addresses X and Y are both addresses in the
- dynamic heap, X is younger than Y (X was created more recently
- than Y) if it is nearer to the free pointer (and farther from
- the base of the heap) than Y.</para>
- <para>Ephemeral (or generational) garbage collectors attempt to
- exploit the following assumptions:</para>
- <itemizedlist>
- <listitem>
- <para>most newly created objects become garbage soon after
- they'recreated.</para>
- </listitem>
- <listitem>
- <para>most objects that have already survived several GCs
- are unlikely to ever become garbage.</para>
- </listitem>
- <listitem>
- <para>old objects can only point to newer objects as the
- result of adestructive modification (e.g., via
- SETF.)</para>
- </listitem>
- </itemizedlist>
-
- <para>By concentrating its efforts on (frequently and quickly)
- reclaiming newly created garbage, an ephemeral collector hopes
- to postpone the more costly full GC as long as possible. It's
- important to note that most programs create some long-lived
- garbage, so an EGC can't typically eliminate the need for full
- GC.</para>
- <para>An EGC views each object in the heap as belonging to
- exactly one <emphasis>generation</emphasis>; generations are
- sets of objects that are related to each other by age: some
- generation is the youngest, some the oldest, and there's an age
- relationship between any intervening generations. Objects are
- typically assigned to the youngest generation when first
- allocated; any object that has survived some number of GCs in
- its current generation is promoted (or
- <emphasis>tenured</emphasis>) into an older generation.</para>
- <para>When a generation is GCed, the roots consist of the
- stacks, registers, and global variables as always and also of
- any pointers to objects in that generation from other
- generations. To avoid the need to scan those (often large) other
- generations looking for such intergenerational references, the
- runtime system must note all such intergenerational references
- at the point where they're created (via Setf).<footnote><para>This is
- sometimes called "The Write Barrier": all assignments which
- might result in intergenerational references must be noted, as
- if the other generations were write-protected.</para></footnote> The
- set of pointers that may contain intergenerational references is
- sometimes called <emphasis>the remembered set</emphasis>.</para>
- <para>In OpenMCL's EGC, the heap is organized exactly the same
- as otherwise; "generations" are merely structures which contain
- pointers to regions of the heap (which is already ordered by
- age.) When a generation needs to be GCed, any younger generation
- is incorporated into it; all objects which survive a GC of a
- given generation are promoted into the next older
- generation. The only intergenerational references that can exist
- are therefore those where an old object is modified to contain a
- pointer to a new object.</para>
- <para>The EGC uses exactly the same code as the full GC. When a
- given GC is "ephemeral",</para>
- <itemizedlist>
- <listitem>
- <para>the "base of the heap" used to determine anobject's
- markbit address is the base of the generation
- being collected;</para>
- </listitem>
- <listitem>
- <para>the markbits vector is actually a pointer into the
- middle of the global markbits table; preceding entries in
- this table are used to note doubleword addresses in older
- generations that (may) contain intergenerational
- references;</para>
- </listitem>
- <listitem>
- <para>some steps (notably GCTWA and the handling of weak
- objects) are not performed;</para>
- </listitem>
- <listitem>
- <para>the intergenerational references table is used to
- findadditional roots for the mark and forward phases. If a
- bit is set inthe intergenerational references table, that
- means that thecorresponding doubleword (in some "old"
- generation, insome "earlier" part of the heap) may have had
- a pointerto an object in a younger generation stored into
- it.</para>
- </listitem>
- =
- </itemizedlist>
- <para>The intergenerational references table is maintained
- indirectly: whenever a setf operation that may introduce an
- intergenerational reference occurs, a pointer to the doubleword
- being stored into is pushed onto the <emphasis>memo
- buffer</emphasis>, which is a stack whos top is addressed by the
- memo register. Whenever the memo buffer overflows<tip><para>A
- guard page at the end of the memo buffer simplifies overflow
- detection.</para></tip> when the EGC is active, the handler
- scans the buffer and sets bits in the intergenerational
- references table for each doubleword address it finds in the
- buffer that belongs to some generation other than the youngest;
- the same scan is performed on entry to any ephemeral GC. After
- (possibly) performing this scan, the handler resets the memo
- register to point to the bottom of the memo stack; this means
- that when the EGC is inactive, the memo buffer is constantly
- being filled and emptied for no apparent reason.</para>
- <para>With one exception (the implicit setfs that occur on entry
- to and exit from the binding of a special variable), all setfs
- that might introduce an intergenerational reference must be
- memoized.<tip><para>Note that the implicit setfs that occur when
- initializing an object - as in the case of a call to cons or
- vector - can't introduce intergenerational references, since the
- newly created object is always younger than the objects used to
- initialize it.</para></tip> It's always safe to push any cons
- cell or gvector locative onto the memo stack; it's never safe to
- push anything else.</para>
- <para>Typically, the intergenerational references bitvector is
- sparse: a relatively small number of old locations are stored
- into, although some of them may have been stored into many
- times. The routine that scans the memoization buffer does a lot
- of work and usually does it fairly often; it uses a simple,
- brute-force method but might run faster if it was smarter about
- recognizing addresses that it'd already seen.</para>
- <para>When the EGC mark and forward phases scan the
- intergenerational reference bits, they can clear any bits that
- denote doublewords that definitely do not contain
- intergenerational references.</para>
- </sect1>
-
- <sect1 id=3D"Fasl-files">
- <title>Fasl files</title>
- <para>The information in this section was current in November
- 2004. Saving and loading of Fasl files is implemented in
- xdump/faslenv.lisp, level-0/nfasload.lisp, and lib/nfcomp.lisp.
- The information here is only an overview, which might help when
- reading the source.</para>
- <para>The OpenMCL Fasl format is forked from the old MCL Fasl
- format; there are a few differences, but they are minor. The
- name "nfasload" comes from the fact that this is the so-called
- "new" Fasl system, which was true in 1986 or so. The format has
- held up well, although it would certainly need extensions to
- deal with 64-bit data, and some other modernization might be
- possible.</para>
- <para>A Fasl file begins with a "file header", which contains
- version information and a count of the following "blocks".
- There's typically only one "block" per Fasl file. The blocks
- are part of a mechanism for combining multiple logical files
- into a single physical file, in order to simplify the
- distribution of precompiled programs. (Nobody seems to be doing
- anything interesting with this feature, at the moment, probably
- because it isn't documented.)</para>
- <para>Each block begins with a header for itself, which just
- describes the size of the data that follows.</para>
- <para>The data in each block is treated as a simple stream of
- bytes, which define a bytecode program. The actual bytecodes,
- "fasl operators", are defined in xdump/faslenv.lisp. The
- descriptions in the source file are terse, but, according to
- Gary, "probably accurate".</para>
- <para>Some of the operators are used to create a per-block
- "object table", which is a vector used to keep track of
- previously-loaded objects and simplify references to them. When
- the table is created, an index associated with it is set to
- zero; this is analogous to an array fill-pointer, and allows the
- table to be treated like a stack.</para>
- <para>The low seven bits of each bytecode are used to specify
- the fasl operator; currently, about fifty operators are defined.
- The high byte, when set, indicates that the result of the
- operation should be pushed onto the object table.</para>
- <para>Most bytecodes are followed by operands; the operand data
- is byte-aligned. How many operands there are, and their type,
- depend on the bytecode. Operands can be indices into the object
- table, immediate values, or some combination of these.</para>
- <para>An exception is the bytecode #xFF, which has the symbolic
- name ccl::$faslend; it is used to mark the end of the
- block.</para>
- </sect1>
-
-
-
- <sect1 id=3D"The-Objective-C-Bridge--1-">
- <title>The Objective-C Bridge</title>
-
- <sect2 id=3D"How-OpenMCL-Recognizes-Objective-C-Objects">
- <title>How OpenMCL Recognizes Objective-C Objects</title>
- <para>In most cases, pointers to instances of Objective-C
- classes are recognized as such; the recognition is (and
- probably always will be) slightly heuristic. Basically, any
- pointer that passes basic sanity checks and whose first word
- is a pointer to a known ObjC class is considered to be an
- instance of that class; the Objective-C runtime system would
- reach the same conclusion.</para>
- <para>It's certainly possible that a random pointer to an
- arbitrary memory address could look enough like an ObjC
- instance to fool the lisp runtime system, and it's possible
- that pointers could have their contents change so that
- something that had either been a true ObjC instance (or had
- looked a lot like one) is changed (possibly by virtue of
- having been deallocated.)</para>
- <para>In the first case, we can improve the heuristics
- substantially: we can make stronger assertions that a
- particular pointer is really "of type :ID" when it's a
- parameter to a function declared to take such a pointer as an
- argument or a similarly declared function result; we can be
- more confident of something we obtained via SLOT-VALUE of a
- slot defined to be of type :ID than if we just dug a pointer
- out of memory somewhere.</para>
- <para>The second case is a little more subtle: ObjC memory
- management is based on a reference-counting scheme, and it's
- possible for an object to ... cease to be an object while lisp
- is still referencing it. If we don't want to deal with this
- possibility (and we don't), we'll basically have to ensure
- that the object is not deallocated while lisp is still
- thinking of it as a first-class object. There's some support
- for this in the case of objects created with MAKE-INSTANCE,
- but we may need to give similar treatment to foreign objects
- that are introduced to the lisp runtime in other ways (as
- function arguments, return values, SLOT-VALUE results, etc. as
- well as those instances that're created under lisp
- control.)</para>
- <para>This doesn't all work yet (in fact, not much of it works
- yet); in practice, this has not yet been as much of a problem
- as anticipated, but that may be because existing Cocoa code
- deals primarily with relatively long-lived objects such as
- windows, views, menus, etc.</para>
- </sect2>
-
- <sect2>
- <title>Recommended Reading</title>
-
- <variablelist>
- <varlistentry>
- <term>
- <ulink url=3D"http://developer.apple.com/documentation/Cocoa/">Coco=
a Documentation</ulink>
- </term>
- =
- <listitem>
- <para>
- This is the top page for all of Apple's documentation on
- Cocoa. If you are unfamiliar with Cocoa, it is a good
- place to start.
- </para>
- </listitem>
- </varlistentry>
- <varlistentry>
- <term>
- <ulink url=3D"http://developer.apple.com/documentation/Cocoa/Referenc=
e/Foundation/ObjC_classic/index.html">Foundation Reference for Objective-C<=
/ulink>
- </term>
-
- <listitem>
- <para>
- This is one of the two most important Cocoa references; it
- covers all of the basics, except for GUI programming. This is
- a reference, not a tutorial.
- </para>
- </listitem>
- </varlistentry>
- </variablelist>
- </sect2>
- </sect1>
- </chapter>
-
-
- <chapter id=3D"Modifying-OpenMCL">
- <title>Modifying OpenMCL</title>
-
- <sect1 id=3D"Contributing-Code-Back-to-the-OpenMCL-Project">
- <title>Contributing Code Back to the OpenMCL Project</title>
- <para>This section is a placeholder, added as of August 2004. The
- full text is being written, and will be added as soon as it is
- available.</para>
- </sect1>
-
- <sect1 id=3D"Using-OpenMCL-in--development--and-in--user--mode">
- <title>Using OpenMCL in "development" and in "user" mode</title>
-
- <para>As it's distributed, OpenMCL starts up with *PACKAGE* set
- to the CL-USER package and with most predefined functions and
- methods protected against accidental redefinition. The package
- setting is of course a requirement of ANSI CL, while the
- protection protection is intended to catch certain types of
- programming errors (accidentally redefining a CL or CCL
- function) before those errors have a chance to do much
- damage.</para>
- <para>These settings may make using OpenMCL to develop OpenMCL a
- bit more awkward, since much of that process assumes that the
- CCL package is current (and a primary purpose of that process is
- to redefine some "predefined, builtin functions".) The standard,
- "routine" ways of building OpenMCL from sources (see ) -
- COMPILE-CCL, XCOMPILE-CCL, and XLOAD-LEVEL-0 - bind *PACKAGE* to
- the "CCL" package and enable the redefinition of predefined
- functions; the symbols COMPILE-CCL, XCOMPILE-CCL, and
- XLOAD-LEVEL-0 are additionally now exported from the "CCL"
- package.</para>
- <para>Some other (more ad-hoc) ways of doing development on
- OpenMCL - compiling and/or loading individual files,
- incrementally redefining individual functions - may be awkward
- unless one reverts to the mode of operation which was
- traditionally offered in OpenMCL. (Some OpenMCL source files -
- especially those that comprise the bootstrapping image sources
- and the first few files in the "cold load" sequence - are
- compiled and loaded in the "CCL" package but don't contain
- (IN-PACKAGE "CCL") forms, since IN-PACKAGE doesn't work until
- later in the cold load sequence.)</para>
- <para>The somewhat bizarre behavior of both SET-USER-ENVIRONMENT
- and SET-DEVELOPMENT-ENVIRONMENT with respect to the special
- variables they affect is intended to allow those constructs to
- take effect when the read-eval-print loop next returns to a
- top-level '? ' prompt; the constructs can meaningfully be used
- inside LOAD, for instance (recall that LOAD binds *PACKAGE*),
- though using both constructs within the same LOAD call would
- likely be pretty confusing.</para>
- <para>"user" and "development" are otherwise very generic terms;
- here they're intended to enforce the distinction between "using"
- OpenMCL and "developing" it.</para>
- <para>The initial environment from which OpenMCL images are
- saved is one where (SET-USER-ENVIRONMENT T) has just been
- called; in previous versions, it was effectively as if
- (SET-DEVELOPMENT-ENVIRONMENT T) had just been called.</para>
- <para>Hopefully, most users of OpenMCL can safely ignore these
- issues most of the time. Note that doing (SET-USER-ENVIRONMENT
- T) after loading one's own code (or 3rd-party code) into OpenMCL
- would protect that code (as well as OpenMCL's) from accidental
- redefinition; that may be useful in some cases.</para>
- </sect1>
-
- <sect1 id=3D"Debugging-facilities-in-the-lisp-kernel">
- <title>Debugging facilities in the lisp kernel</title>
- <para> In a perfect world, something like this couldn't
- happen:</para>
- <programlisting>
-Welcome to OpenMCL Version x.y!
-? (defun foo (x)
- (declare (cons x))
- (cdr x))
-FOO
-
-? (foo -1) ;Oops. Too late ...
-Unhandled exception 11 at 0x300e90c8, context->regs at #x7ffff6b8
-Continue/Debugger/eXit <enter>?
-</programlisting>
- <para>As you may have noticed, it's not a perfect world; it's rare
-that the cause (attempting to reference the CDR of -1, and therefore
-accessing unmapped memory near location 0) of this effect (an
-"Unhandled exception ..." message) is so obvious.</para>
- <para>The addresses printed in the message above aren't very useful
-unless you're debugging the kernel with GDB (and they're often
-very useful if you are.)</para>
- <para>Aside from causing an exception that the lisp kernel doesn't
-know how to handle, one can also enter the kernel debugger (more)
-deliberately:</para>
- <programlisting>
-? (defun classify (n)
- (cond ((> n 0) "Greater")
- ((< n 0) "Less")
- (t
- ;;; Sheesh ! What else could it be ?
- (ccl::bug "I give up. How could this happen ?"))))
-CLASSIFY
-
-? (classify 0)
-Bug in OpenMCL system code:
-I give up. How could this happen ?
-? for help
-[12345] OpenMCL kernel debugger:
- </programlisting>
- <para>CCL::BUG isn't quite the right tool for this example (a
- call to BREAK or PRINT might do a better job of clearing up the
- mystery), but it's sometimes helpful when those other tools
- can't be used. The lisp error system notices, for instance, if
- attempts to signal errors themselves cause errors to be
- signaled; this sort of thing can happen if CLOS or the I/O
- system are broken or missing. After some small number of
- recursive errors, the error system gives up and calls
- CCL::BUG.</para>
- <para>If one enters a '?' at the kernel debugger prompt, one
- will see output like:</para>
- <programlisting>
-(S) Find and describe symbol matching specified name
-(B) Show backtrace
-(X) Exit from this debugger, asserting that any exception was handled
-(K) Kill OpenMCL process
-(?) Show this help
-</programlisting>
- <para>CCL::BUG just does an FF-CALL into the lisp kernel. If
- the kernel debugger was invoked because of an unhandled
- exception (such as an illegal memory reference) the OS kernel
- saves the machine state ("context") in a data structure for us,
- and in that case some additional options can be used to display
- the contents of the registers at the point of the
- exception. Another function - CCL::DBG - causes a special
- exception to be generated and enters the lisp kernel debugger
- with a non-null "context":</para>
- <programlisting>
-? (defun classify2 (n)
- (cond ((> n 0) "Greater")
- ((< n 0) "Less")
- (t (dbg n))))
-CLASSIFY2
-
-? (classify2 0)
-Lisp Breakpoint
- While executing: #<Function CLASSIFY2 #x08476cfe>
-? for help
-[12345] OpenMCL kernel debugger: ?
-(G) Set specified GPR to new value
-(A) Advance the program counter by one instruction (use with caution!)
-(D) Describe the current exception in greater detail
-(R) Show raw GPR/SPR register values
-(L) Show Lisp values of tagged registers
-(F) Show FPU registers
-(S) Find and describe symbol matching specified name
-(B) Show backtrace
-(X) Exit from this debugger, asserting that any exception was handled
-(P) Propagate the exception to another handler (debugger or OS)
-(K) Kill OpenMCL process
-(?) Show this help
-</programlisting>
- <para>CCL::DBG takes an argument, whose value is copied into the reg=
ister
-that OpenMCL uses to return a function's primary value (arg_z, which
-is r23 on the PowerPC). If we were to choose the (L) option at this point,
-we'd see a dislay like:</para>
- <programlisting>rnil =3D 0x01836015
-nargs =3D 0
-r16 (fn) =3D #<Function CLASSIFY2 #x30379386>
-r23 (arg_z) =3D 0
-r22 (arg_y) =3D 0
-r21 (arg_x) =3D 0
-r20 (temp0) =3D #<26-element vector subtag =3D 2F @#x303793ee>
-r19 (temp1/next_method_context) =3D 6393788
-r18 (temp2/nfn) =3D #<Function CLASSIFY2 #x30379386>
-r17 (temp3/fname) =3D CLASSIFY2
-r31 (save0) =3D 0
-r30 (save1) =3D *TERMINAL-IO*
-r29 (save2) =3D 0
-r28 (save3) =3D (#<RESTART @#x01867f2e> #<RESTART @#x01867f56>)
-r27 (save4) =3D ()
-r26 (save5) =3D ()
-r25 (save6) =3D ()
-r24 (save7) =3D ()
-</programlisting>
- <para>From this we can conclude that the problematic argument to CLA=
SSIFY2
-was 0 (see r23/arg_z), and that I need to work on a better example.</para>
- <para>The R option shows the values of the ALU (and PPC branch unit)
-registers in hex; the F option shows the values of the FPU registers.</par=
a>
- <para>The (B) option shows a raw stack backtrace; it'll try to
-identify foreign functions as well as lisp functions. (Foreign function
-names are guesses based on the nearest preceding exported symbol.)</para>
- <para>If you ever unexpectedly find yourself in the "lisp kernel
-debugger", the output of the (L) and (B) options are often the most
-helpful things to include in a bug report.</para>
- </sect1>
-
- <sect1 id=3D"Using-AltiVec-in-OpenMCL-LAP-functions">
- <title>Using AltiVec in OpenMCL LAP functions</title>
-
- <sect2 id=3D"Overview--16-">
- <title>Overview</title>
- <para>It's now possible to use AltiVec instructions in PPC LAP
- (assembler) functions.</para>
- <para>The lisp kernel detects the presence or absence of
- AltiVec and preserves AltiVec state on lisp thread switch and
- in response to exceptions, but the implementation doesn't
- otherwise use vector operations.</para>
- <para>This document doesn't document PPC LAP programming in
- general. Ideally, there would be some document that
- did.</para>
- <para>This document does explain AltiVec register-usage
- conventions in OpenMCL and explains the use of some lap macros
- that help to enforce those conventions.</para>
- <para>All of the global symbols described below are exported
- from the CCL package. Note that lap macro names, ppc
- instruction names, and (in most cases) register names are
- treated as strings, so this only applies to functions and
- global variable names.</para>
- <para>Much of the OpenMCL support for AltiVec LAP programming
- is based on work contributed to MCL by Shannon Spires.</para>
- </sect2>
-
- <sect2 id=3D"Register-usage-conventions">
- <title>Register usage conventions</title>
- <para>OpenMCL LAP functions that use AltiVec instructions must
- interoperate with each other and with C functions; that
- suggests that they follow C AltiVec register usage
- conventions. (vr0-vr1 scratch, vr2-vr13 parameters/return
- value, vr14-vr19 temporaries, vr20-vr31 callee-save
- non-volatile registers.)</para>
- <para>The EABI (Embedded Application Binary Interface) used in
- LinuxPPC doesn't ascribe particular significance to the vrsave
- special-purpose register; on other platforms (notably MacOS),
- it's used as a bitmap which indicates to system-level code
- which vector registers contain meaningful values.</para>
- <para>The WITH-ALTIVEC-REGISTERS lapmacro generates code which
- which saves, updates, and restores VRSAVE on platforms where
- this is required (as indicated by the value of the special
- variable which controls this) and ignores VRSAVE on platforms
- that don't require it to be maintained.</para>
- <para>On all PPC platforms, it's necessary to save any non-volatile
-vector registers (vr20 .. vr31) before assigning to them and to restore
-such registers before returning to the caller.</para>
- <para>On platforms that require that VRSAVE be maintained, it's not
-necessary to mention the "use" of vector registers that're
-used as incoming parameters. It's not incorrect to mention their use
-in a WITH-ALTIVEC-REGISTERS form, but it may be unneccessary in many
-interesting cases. One can likewise assume that the caller of any function
-that returns a vector value (in vr2 has already set the apropriate bit in
-VRSAVE to indicate that this register is live. One could therefore write a
-leaf function that added the bytes in vr3 and vr2 and returned the result
-in vr2 as:</para>
- <programlisting>
-(defppclapfunction vaddubs ((y vr3) (z vr2))
- (vaddubs z y z)
- (blr))
-</programlisting>
- <para>When vector registers that aren't incoming parameters are us=
ed
-in a LAP function, WITH-ALTIVEC-REGISTERS takes care of maintaining VRSAVE
-and of saving/restoring any non-volatile vector registers:</para>
- <programlisting>
-(defppclapfunction load-array ((n arg_z))
- (check-nargs 1)
- (with-altivec-registers (vr1 vr2 vr3 vr27) ; Clobbers imm0
- (li imm0 arch::misc-data-offset)
- (lvx vr1 arg_z imm0) ; load MSQ
- (lvsl vr27 arg_z imm0) ; set the permute vector
- (addi imm0 imm0 16) ; address of LSQ
- (lvx vr2 arg_z imm0) ; load LSQ
- (vperm vr3 vr1 vr2 vr27) ; aligned result appears in VR3
- (dbg t)) ; Look at result in some debugger
- (blr))
-</programlisting>
- <para>AltiVec registers are not preserved by CATCH and UNWIND-PROT=
ECT.
-Since AltiVec is only accessible from LAP in OpenMCL and since LAP
-functions rarely use high- level control structures, this should rarely be
-a problem in practice.</para>
- <para>LAP functions which use non-volatile vector registers and wh=
ich call
-(Lisp ?) code which may use CATCH or UNWIND-PROTECT should save those
-vector registers before such a call and restore them on return. This is
-one of the intended uses of the WITH-VECTOR-BUFFER lap macro.</para>
- </sect2>
- </sect1>
-
- <sect1 id=3D"Development-Mode-Dictionary">
- <title>Development-Mode Dictionary</title>
-
- <refentry id=3D"v_warn-if-redefine-kernel">
- <indexterm zone=3D"v_warn-if-redefine-kernel">
- <primary>*warn-if-redefine-kernel</primary>
- </indexterm>
-
- <refnamediv>
- <refname>*WARN-IF-REDEFINE-KERNEL*</refname>
- <refpurpose></refpurpose>
- <refclass>Variable</refclass>
- </refnamediv>
-
- <refsect1>
- <title>Description</title>
-
- <para>When true, attempts to redefine (via DEFUN or DEFMETHOD)
- functions and methods that are marked as being
- "predefined" signal continuable errors.</para>
-
- <para>Note that these are CERRORs, not warnings, and that
- no lisp functions or methods have been defined in the kernel
- in MCL or OpenMCL since 1987 or so.</para>
- </refsect1>
- </refentry>
-
- <refentry id=3D"f_set-development-environment">
- <indexterm zone=3D"f_set-development-environment">
- <primary>set-development-environment</primary>
- </indexterm>
-
- <refnamediv>
- <refname>SET-DEVELOPMENT-ENVIRONMENT</refname>
- <refpurpose></refpurpose>
- <refclass>Function</refclass>
- </refnamediv>
-
- <refsynopsisdiv>
- <synopsis><function>set-development-environment</function>
- &optional;
- unmark-builtin-functions</synopsis>
- </refsynopsisdiv>
-
- <refsect1>
- <title>Description</title>
-
- <para>Arranges that the outermost special bindings of *PACKAGE*
- and *WARN-IF-REDEFINE-KERNEL* restore values of the "CCL"
- package and NIL to these variables, respectively. If the optional
- argument is true, marks all globally defined functions and methods
- as being "not predefined" (this is a fairly expensive
- operation.)</para>
- </refsect1>
- </refentry>
-
- <refentry id=3D"f_set-user-environment">
- <indexterm zone=3D"f_set-user-environment">
- <primary>set-user-environment</primary>
- </indexterm>
-
- <refnamediv>
- <refname>SET-USER-ENVIRONMENT</refname>
- <refpurpose></refpurpose>
- <refclass>Function</refclass>
- </refnamediv>
-
- <refsynopsisdiv>
- <synopsis><function>set-user-environment</function>
- &optional; mark-builtin-functions</synopsis>
- </refsynopsisdiv>
-
- <refsect1>
- <title>Description</title>
-
- <para>Arranges that the outermost special bindings of *PACKAGE*
- and *WARN-IF-REDEFINE-KERNEL* restore values of the
- "CL-USER" package and T to these variables, respectively.
- If the optional argument is true, marks all globally defined
- functions and methods as being "predefined" (this is a
- fairly expensive operation.)</para>
- </refsect1>
- </refentry>
- <refentry id=3D"v_altivec-available">
- <indexterm zone=3D"v_altivec-available">
- <primary>*altivec-available*</primary>
- </indexterm>
-
- <refnamediv>
- <refname>*ALTIVEC-AVAILABLE*</refname>
- <refpurpose></refpurpose>
- <refclass>Variable</refclass>
- </refnamediv>
-
- <refsect1>
- <title>Description</title>
- <para>This variable is intitialized each time an OpenMCL session
- starts based on information provided by the lisp kernel. Its value
- is true if AltiVec is present and false otherwise. This variable
- shouldn't be set by user code.</para>
- </refsect1>
- </refentry>
-
- <refentry id=3D"f_altivec-available-p">
- <indexterm zone=3D"f_altivec-available-p">
- <primary>altivec-available-p</primary>
- </indexterm>
-
- <refnamediv>
- <refname>ALTIVEC-AVAILABLE-P</refname>
- <refpurpose></refpurpose>
- <refclass>Function</refclass>
- </refnamediv>
- =
- <refsynopsisdiv>
- <synopsis><function>altivec-available-p</function></synopsis>
- </refsynopsisdiv>
-
- <refsect1>
- <title>Description</title>
-
- <para>Returns non-NIL if AltiVec is available.</para>
- </refsect1>
- </refentry>
-
- <refentry id=3D"v_altivec-lapmacros-maintain-vrsave-p">
- <indexterm zone=3D"v_altivec-lapmacros-maintain-vrsave-p">
- <primary>*altivec-lapmacros-maintain-vrsave-p*</primary>
- </indexterm>
-
- <refnamediv>
- <refname>*ALTIVEC-LAPMACROS-MAINTAIN-VRSAVE-P*</refname>
- <refpurpose></refpurpose>
- <refclass>Variable</refclass>
- </refnamediv>
- =
- <refsect1>
- <title>Description</title>
-
- <para>Intended to control the expansion of certain lap macros.
- Initialized to NIL on LinuxPPC; initialized to T on platforms
- (such as MacOS X/Darwin) that require that the VRSAVE SPR contain
- a bitmask of active vector registers at all times.</para>
- </refsect1>
- </refentry>
-
- <refentry id=3D"lapm_with-altivec-registers">
- <indexterm zone=3D"lapm_with-altivec-registers">
- <primary>with-altivec-registers</primary>
- </indexterm>
-
- <refnamediv>
- <refname>WITH-ALTIVEC-REGISTERS</refname>
- <refpurpose></refpurpose>
- <refclass>LAP Macro</refclass>
- </refnamediv>
-
- <refsynopsisdiv>
- <synopsis><function>with-altivec-registers</function>
- reglist &body; body</synopsis>
- </refsynopsisdiv>
-
- <refsect1>
- <title>Arguments and Values</title>
-
- <variablelist>
- <varlistentry>
- <term>reglist</term>
-
- <listitem>
- <para>A list of vector register names (vr0 .. vr31).</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>body</term>
-
- <listitem>
- <para>A sequence of PPC LAP instructions.</para>
- </listitem>
- </varlistentry>
- </variablelist>
- </refsect1>
-
- <refsect1>
- <title>Description</title>
-
- <para>Specifies the set of AltiVec registers used in body. If
- *altivec-lapmacros-maintain-vrsave-p* is true when the macro is
- expanded, generates code to save the VRSAVE SPR and updates VRSAVE
- to incude a bitmask generated from the specified register list.
- Generates code which saves any non-volatile vector registers which
- appear in the register list, executes body, and restores the saved
- non-volatile vector registers (and, if
- *altivec-lapmacros-maintain-vrsave-p* is true, restores VRSAVE as
- well. Uses the IMM0 register (r3) as a temporary.</para>
- </refsect1>
- </refentry>
-
- <refentry id=3D"lapm_with-vector-buffer">
- <indexterm zone=3D"lapm_with-vector-buffer">
- <primary>with-vector-buffer</primary>
- </indexterm>
-
- <refnamediv>
- <refname>WITH-VECTOR-BUFFER</refname>
- <refpurpose></refpurpose>
- <refclass>LAP Macro</refclass>
- </refnamediv>
- =
- <refsynopsisdiv>
- <synopsis>with-vector-buffer base n &body; body</synopsis>
- </refsynopsisdiv>
-
- <refsect1>
- <title>Arguments and Values</title>
-
- <variablelist>
- <varlistentry>
- <term>base</term>
-
- <listitem>
- <para>Any available general-purpose register.</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>n</term>
-
- <listitem>
- <para>An integer between 1 and 254, inclusive. (Should
- typically be much, much closer to 1.) Specifies the size of
- the buffer, in 16-byte units.</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>body</term>
-
- <listitem>
- <para>A sequence of PPC LAP instructions.</para>
- </listitem>
- </varlistentry>
- </variablelist>
- </refsect1>
-
- <refsect1>
- <title>Description</title>
- <para>Generates code which allocates a 16-byte aligned buffer
- large enough to contain N vector registers; the GPR base points to
- the lowest address of this buffer. After processing body, the
- buffer will be deallocated. The body should preserve the value of
- base as long as it needs to reference the buffer. It's
- intended that base be used as a base register in stvx and lvx
- instructions within the body.</para>
- </refsect1>
- </refentry>
- </sect1>
- </chapter>
- <index id=3D"Symbol-Index"><title>Symbol Index</title></index>
-</book>
More information about the Openmcl-cvs-notifications
mailing list