diff --git a/lang/perl/README.cgi-bin b/lang/perl/README.cgi-bin
new file mode 100644
index 0000000..1d04076
--- /dev/null
+++ b/lang/perl/README.cgi-bin
@@ -0,0 +1,22 @@
+This should go in a sites-enabled file, inside a VirtualHost:
+
+ ScriptAlias /stasis-cgi/ /home/sears/stasis/lang/perl/cgi-bin/
+
+ AllowOverride None
+ Options +ExecCGI -MultiViews +SymLinksIfOwnerMatch
+ Order allow,deny
+ Allow from all
+
+
+
+
+This is the contents of my /etc/apache2/mods-enabled/fastcgi.conf
+
+
+ AddHandler fastcgi-script .fcgi
+ #FastCgiWrapper /usr/lib/apache2/suexec
+ FastCgiIpcDir /var/lib/apache2/fastcgi
+ FastCgiConfig -maxClassProcesses 1 -maxProcesses 1 -processSlack 1
+
+
+Note that the fastcgi scripts are currently hardcoded to run in ~sears/stasis/www-data2
diff --git a/lang/perl/README.modperl b/lang/perl/README.modperl
new file mode 100644
index 0000000..65d6621
--- /dev/null
+++ b/lang/perl/README.modperl
@@ -0,0 +1,25 @@
+This goes in apache.conf:
+
+
+ StartServers 1
+# Only want 1 server; this is a correctness constraint for stasis.
+ ServerLimit 1
+ MinSpareThreads 25
+ MaxSpareThreads 75
+ ThreadLimit 150
+ ThreadsPerChild 150
+ MaxClients 150
+ MaxRequestsPerChild 0
+
+
+
+This goes in the sites-enabled/???:
+
+ PerlRequire /home/sears/stasis/lang/perl/apache/inc/startup.pl
+
+
+ SetHandler perl-script
+ PerlResponseHandler StasisWeb::Web
+
+
+Note that the modapache scripts are currently hardcoded to look in ~sears/stasis/www-data
diff --git a/lang/perl/apache/StasisWeb/Web.pm b/lang/perl/apache/StasisWeb/Web.pm
new file mode 100644
index 0000000..cccff37
--- /dev/null
+++ b/lang/perl/apache/StasisWeb/Web.pm
@@ -0,0 +1,34 @@
+#file:Stasis/Web.pm
+#----------------------
+package StasisWeb::Web;
+
+use threads::shared;
+my $thelock :shared;
+
+use strict;
+use warnings;
+use Apache2::RequestRec ();
+use Apache2::RequestIO ();
+use Apache2::Const -compile => qw(OK);
+
+sub handler {
+ my $r = shift;
+ $r->content_type('text/html');
+ print "
Stasis
" . `pwd`;
+ my $xid = Stasis::Tbegin();
+ warn "a\n";
+ my %h;
+ tie %h, 'Stasis::Hash', $xid, Stasis::ROOT_RID();
+
+ $h{foo}++;
+
+ print ("$xid $h{foo}\n");
+
+ Stasis::Tcommit($xid);
+ warn "b\n"; $| = 1;
+
+ print "\n";
+
+ return Apache2::Const::OK;
+}
+1;
diff --git a/lang/perl/apache/inc/startup.pl b/lang/perl/apache/inc/startup.pl
new file mode 100755
index 0000000..ac70559
--- /dev/null
+++ b/lang/perl/apache/inc/startup.pl
@@ -0,0 +1,51 @@
+use threads;
+use threads::shared;
+
+use strict;
+BEGIN {
+ my $thelock :shared;
+
+ $ENV{STASIS_DIR} = '/home/sears/stasis';
+
+ $ENV{STASIS_LOCK} = $thelock;
+}
+use lib ($ENV{STASIS_DIR}."/lang/perl/apache/");
+push @INC, "$ENV{STASIS_DIR}/lang/perl/";
+
+use Inline (Config =>
+ DIRECTORY => "$ENV{STASIS_DIR}/www-data/",
+ );
+use Stasis;
+
+# XXX Ideally, the rest of this would go in a post_config handler, but
+# I can't get that to work...
+
+my $data_dir = "$ENV{STASIS_DIR}/www-data/";
+
+{
+ my $d = `date`;
+ chomp $d;
+ warn "$d: Starting Stasis in $data_dir\n";
+ chdir $data_dir
+ || die "Could not chdir to $data_dir, which I must own!";
+
+ Stasis::Tinit();
+ $d = `date`;
+ chomp $d;
+ warn "$d: Stasis recovery complete\n";
+ my $xid = Stasis::Tbegin();
+ if(Stasis::TrecordType($xid, Stasis::ROOT_RID()) == Stasis::INVALID_SLOT()) {
+ $d = `date`;
+ chomp $d;
+ warn "$d: Bootstrapping new Stasis store.\n";
+
+ Stasis::ThashCreate($xid);
+ }
+ Stasis::Tcommit($xid);
+
+ $d = `date`;
+ chomp $d;
+ warn "$d: Stasis initialization complete.\n";
+}
+
+1;
diff --git a/lang/perl/cgi-bin/test.fcgi b/lang/perl/cgi-bin/test.fcgi
new file mode 100755
index 0000000..36a9530
--- /dev/null
+++ b/lang/perl/cgi-bin/test.fcgi
@@ -0,0 +1,92 @@
+#!/usr/bin/perl -w
+
+use strict;
+
+BEGIN {
+ $ENV{STASIS_DIR} = '/home/sears/stasis';
+ $ENV{STASIS_INLINE_DIRECTORY} = '/home/sears/stasis/www-data2';
+ $ENV{PATH} = '/usr/local/bin:/usr/bin:/bin';
+# $ENV{LANG} = 'en_US.UTF-8';
+# $ENV{SHELL} = '/bin/sh';
+
+# warn `env`;
+ chdir "$ENV{STASIS_DIR}/www-data2" || die;
+# warn "begin succeeded";
+ push @INC, "$ENV{STASIS_DIR}/lang/perl";
+}
+
+use threads;
+use threads::shared;
+
+use Stasis;
+
+use IO::Handle;
+use FCGI;
+
+my $count = 0;
+my $tot :shared = 0;
+my $concurrent :shared = 0;
+
+my $in = new IO::Handle;
+my $out = new IO::Handle;
+my $err = new IO::Handle;
+
+my $num_procs = 25;
+
+
+Stasis::Tinit();
+my $xid = Stasis::Tbegin();
+if(Stasis::TrecordType($xid, Stasis::ROOT_RID()) == Stasis::INVALID_SLOT()) {
+ warn "Creating new database\n";
+ Stasis::ThashCreate($xid);
+}
+Stasis::Tcommit($xid);
+warn "Stasis bootstrap completed; accepting FastCGI requests\n";
+
+my @thrs;
+for(my $i = 0; $i < $num_procs; $i++) {
+ push @thrs, threads->create(\&event_loop, $i);
+}
+sub event_loop {
+ my $i = shift;
+# warn "Event loop $i started\n";
+ my $request = FCGI::Request($in, $out, $err);
+ while($request->Accept() >= 0) {
+ my $s;
+ my $tot2;
+ { lock $concurrent; $concurrent ++; }
+ {
+ lock($tot);
+ $tot ++;
+ $s = $tot;
+ my $xid = Stasis::Tbegin();
+
+ my %h;
+ tie %h, 'Stasis::Hash', $xid, Stasis::ROOT_RID();
+
+ $h{foo}++;
+
+ $tot2 = $h{foo};
+
+ Stasis::TsoftCommit($xid);
+ }
+ # Release lock before waiting for disk force.
+ Stasis::TforceCommits();
+
+ print $out "Content-type: text/html\r\n\r\n" . "$count of $s ($tot2) concurrent: $concurrent";
+ $count ++;
+ { lock $concurrent; warn "concurr: $concurrent\n"; $concurrent --; }
+# print $err "Event loop $i accepted request\n";
+ }
+# warn "Event loop $i done\n";
+}
+
+warn "Done spawning worker threads\n";
+
+foreach my $t (@thrs) {
+ $t->join();
+}
+
+Stasis::Tdeinit();
+
+warn "Stasis cleanly shut down\n";