Basic LISP Techniques - Part4
Basic LISP Techniques - Part4
Basic LISP Techniques - Part4
Interfaces
(excl.osi:command-output "ls")
When invoked in this fashion, this function returns the the Standard Output from the shell
command as a string. Alternatively, you can specify that the output go to another location,
such as a file, or a variable defined inside the CL session.
When using excl.osi:command-output, you can also specify whether CL should wait
for the shell command to complete, or should simply go about its business and let the shell
command complete asynchronously.
55
56 CHAPTER 4. INTERFACES
as if they were defined natively in CL. This technique requires a bit more setup work than
using simple shell commands with excl.osi:command-output, but it will provide better
performance and more transparent operation once it is set up.
the client with a normal CL Command Prompt (for security, it will by default only accept
connections from the local machine).
4.7 Multiprocessing
Multiprocessing allows your application to carry on separate tasks virtually simultaneously.
Virtually, because on a single-processor machine, only one operation can physically be hap-
pening at any one time. However, there are situations when you want your program to
appear to be executing separate execution threads, or processes, simultaneously. Web serv-
ing is a good example. As described in Section 4.9, you may be running your CL application
as a webserver. This means that multiple users, on different machines in different parts of
your company or even around the world, may be sending requests to your application in
the form of web browser (client) HTTP commands. If one user’s request takes an especially
long time to process, you probably do not want all your other users to have to wait for a
response while that one request is being processed.
Multiprocessing addresses such problems by allowing your CL application to continue
servicing other requests, while “simultaneously” completing that long computation.
Allegro Multiprocessing is covered in the document multiprocessing.htm which ships
with all editions of Allegro CL.
(defvar *count* 0)
(mp:process-run-function
(list :name "counting to a million in background"
:priority -10)
#’(lambda() (dotimes (n 1000000) (setq *count* n))))
This starts a thread named “counting to a million in background” with a relative priority
of -10. Notice that after you call this function, your toplevel prompt returns immediately.
The toplevel Read-Eval-Print loop is continuing on its merry way, while your new process is
running in the background, counting to a million (actually, 999999). While the background
process is running, you can sample the value of count at any time:
CL-USER(16): *count*
424120
CL-USER(17): *count*
593783
CL-USER(18): *count*
679401
CL-USER(19): *count*
765012
Note that on a fast processor, you may need to increase the count value in this example to
be able to take samples before the code completes.
Now, run the background function within the context of seizing this lock:
60 CHAPTER 4. INTERFACES
CL-USER(21): (mp:process-run-function
(list :name "counting to a million in background"
:priority -10)
#’(lambda()
(mp:with-process-lock (count-lock)
(dotimes (n 1000000) (setq count n)))))
#<MULTIPROCESSING:PROCESS counting to a million in background @ #x5a0332a>
You now can adopt a policy that any piece of code wishing to modify the value of count must
first seize the count-lock. This will ensure that nothing will interfere with the counting
process. If you do the following while the counting process is running:
you will notice that execution will block until the counting process is finished. This is
because the count-lock is already seized, and execution is waiting for this lock to be
released before proceeding.
It is usually a good idea to make your process locks as fine-grained as possible, to avoid
making processes wait unecessarily.
From your toplevel prompt, you can monitor which processes are running in the current
CL image with the toplevel :proc command:
CL-USER(28): :proc
P Bix Dis Sec dSec Priority State Process Name, Whostate, Arrest
* 7 1 2 2.0 -10 runnable counting to a million in background
* 1 8 28 0.1 0 runnable Initial Lisp Listener
* 3 0 0 0.0 0 waiting Connect to Emacs daemon, waiting for input
* 4 0 0 0.0 0 inactive Run Bar Process
* 6 0 3 0.0 0 waiting Editor Server, waiting for input
See the multiprocessing.htm for details on each column in this report, but essentially
this is showing the state of each process currently in the system. This example shows that
Allegro CL uses separate threads for maintaining the connection to the Emacs text editor.
As we will see in Section 4.9, the AllegroServe webserver automatically creates and
manages separate threads to deal with simultaneous web connections.
4.8.1 ODBC
ODBC, or Open Database Connectivity, is a standard means of connecting to a relational
database based on SQL (Structured Query Language, the industry standard Data Definition
and Data Manipulation language for relational databases). Virtually all popular database
systems can be connected to using ODBC. On Windows these facilities are usually free
of charge; on Unix they may or may not be free. In any case, ODBC is a good solution
to consider if you want to connect your application to several different relational database
systems without writing and maintaining separate pieces of code specific to each.
ODBC uses the concept of data sources, each with a name. There are operating system
dependent ways to set up data sources on your machine to point to actual physical databases,
which may be housed on the local machine or remote. Assuming we have a data source
named “login” with a table named “USER,” connecting to and querying the database can
be as simple as the following:
As with most CL database interfaces, the ODBC interface returns all SQL queries with
multiple values: The first (and main) return value is a list of lists, corresponding to the
rows in the returned query. The second return value is a list of field names, corresponding
to the values in each of the first lists.
Because ODBC by definition must retain portability among database systems, it is
generally unable to take full advantage of database-specific features and performance opti-
mizations.
4.8.2 MySQL
MySQL is a popular relational database which is available both in commercially supported
and free, open-source form. While you can connect to a MySQL database using ODBC,
Allegro CL also provides a direct connection to MySQL, which performs faster and is easier
to set up than the ODBC connection. From the application level, its use is simple enough
that an application using the MySQL Direct interface could be converted to use ODBC
without major rework, should that become necessary. Therefore, if you know your Allegro
CL application will be using MySQL for the immediate future, we recommend using the
MySQL Direct interface rather than ODBC.
Here is the same example from above, using the MySQL Direct interface instead of the
ODBC one:
:database "login"))
#<DBI.MYSQL:MYSQL connected to localhost/3306 @ #x5bd114a>
CL-USER(51): (dbi.mysql:sql "select * from USER" :db handle)
((1 "dcooper8" "bhaga<<" "Dave" #:|null| "Cooper" #:|null| ...)
(7 "awolven" #:|null| #:|null| #:|null| #:|null| #:|null| ...))
("GEN_ID" "LOGIN" "PASSWORD" "FIRST_NAME" "MIDDLE_NAME"
"LAST_NAME" "COMPANY" "ADDRESS_1" "ADDRESS_2" "CITY" ...)
• The functions are in the dbi.mysql package rather than the dbi package.
• Since there is no need to set up separate data sources for MySQL Direct, we connect
to a MySQL instance on the local machine directly by specifying a user, password,
and database name.
• NULL values in the return query are represented with the special symbol #:|null|
rather than with NIL as in ODBC.
(require :aserve)
(use-package :net.aserve)
(use-package :net.html.generator)
(use-package :net.aserve.client)
With AllegroServe, you can map website addresses to actual functions which will run in
response to a request. Here is a simple example:
Now, whenever a web visitor goes to the /hello.html URI on our server, she will receive
in response a page with the simple text “Hello World.” More dynamic examples would of
course be more interesting, and we present some in the next chapter.
Note the use of the html operator in the previous example. This is a macro from the
HTMLgen package which ships with Allegroserve. HTMLgen allows convenient generation
of dynamic, well-structured HTML from a lisp-like source format. Please see the Alle-
groServe and HTMLgen documentation at http://opensource.franz.com/aserve/index.html
for details on both of these.
2. the response code (e.g. 200 for a normal response, 404 for a “Page Not Found,” etc.)
For more complex client requests which return structured HTML, an HTML parser is
available. For example, you might create a news agent by sending an HTTP request to a
news site, then parsing the HTML to create convenient list structures from which you can
elicit the text of the news stories. Please see http://opensource.franz.com/xmlutils/index.html
for details on this parser.
64 CHAPTER 4. INTERFACES
4.11 Email
Email was the first “killer app” of the Internet, and arguably continues in that role to this
day. Despite threats from viruses and unsolicited commercial email, many people in the
modern world still depend on email for their daily communication. CL provides an excellent
environment for automating email processing. Not coincidentally, in the next chapter we
present an example of using CL to perform text classification on email, an effective means
for filtering unwanted and unsolicited commercial bulk email.
(require :imap)
(use-package :net.post-office)
Here is an example of using the POP client interface. First, you must create a connection
handle to the POP server:
tables
hash, 45
test-expression forms
for cond, 37
threads, 1
toplevel, 19
turning off evaluation, 22
Typing
Dynamic, 22
union, 34
Unix, 44
variables
global, 25
special, 25
webserver, 62
With-open-file, 45