<?xml version="1.0" encoding="UTF-8"?>
<commit>
  <added type="array">
    <added>
      <filename>ext/senatus.lua</filename>
    </added>
    <added>
      <filename>ext/usherette.lua</filename>
    </added>
  </added>
  <modified type="array">
    <modified>
      <diff>@@ -1,3 +1,13 @@
+2009-02-05  Mikio Hirabayashi  &lt;mikio@users.sourceforge.net&gt;
+
+	* ttutil.c (ttservstart, ttservaddtimedhandler): multiple tasks are now supported.
+
+	* ttutil.c (ttsockrecv, ttsockgetint32, ttsockgetint64): performance was improved.
+
+	* ttserver.c (do_extpc): new function.
+
+	- Release: 1.1.14
+
 2009-02-04  Mikio Hirabayashi  &lt;mikio@users.sourceforge.net&gt;
 
 	* tcrdb.c (tcrdbqrysearchout): a bug related to the protocol format was fixed.</diff>
      <filename>ChangeLog</filename>
    </modified>
    <modified>
      <diff>@@ -19,6 +19,7 @@ Contents of the directory tree is below.
   ./             - sources of Tokyo Tyrant
   ./doc/         - manuals and specifications
   ./man/         - manuals for nroff
+  ./ext/         - sample script for the script extension
   ./example/     - sample code of tutorial
   ./lab/         - for test and experiment
 </diff>
      <filename>README</filename>
    </modified>
    <modified>
      <diff>@@ -1,6 +1,6 @@
 #! /bin/sh
 # Guess values for system-dependent variables and create Makefiles.
-# Generated by GNU Autoconf 2.61 for tokyotyrant 1.1.13.
+# Generated by GNU Autoconf 2.61 for tokyotyrant 1.1.14.
 #
 # Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001,
 # 2002, 2003, 2004, 2005, 2006 Free Software Foundation, Inc.
@@ -572,8 +572,8 @@ SHELL=${CONFIG_SHELL-/bin/sh}
 # Identity of this package.
 PACKAGE_NAME='tokyotyrant'
 PACKAGE_TARNAME='tokyotyrant'
-PACKAGE_VERSION='1.1.13'
-PACKAGE_STRING='tokyotyrant 1.1.13'
+PACKAGE_VERSION='1.1.14'
+PACKAGE_STRING='tokyotyrant 1.1.14'
 PACKAGE_BUGREPORT=''
 
 # Factoring default headers for most tests.
@@ -1194,7 +1194,7 @@ if test &quot;$ac_init_help&quot; = &quot;long&quot;; then
   # Omit some internal or obsolete options to make the list less imposing.
   # This message is too long to be a string in the A/UX 3.1 sh.
   cat &lt;&lt;_ACEOF
-\`configure' configures tokyotyrant 1.1.13 to adapt to many kinds of systems.
+\`configure' configures tokyotyrant 1.1.14 to adapt to many kinds of systems.
 
 Usage: $0 [OPTION]... [VAR=VALUE]...
 
@@ -1255,7 +1255,7 @@ fi
 
 if test -n &quot;$ac_init_help&quot;; then
   case $ac_init_help in
-     short | recursive ) echo &quot;Configuration of tokyotyrant 1.1.13:&quot;;;
+     short | recursive ) echo &quot;Configuration of tokyotyrant 1.1.14:&quot;;;
    esac
   cat &lt;&lt;\_ACEOF
 
@@ -1349,7 +1349,7 @@ fi
 test -n &quot;$ac_init_help&quot; &amp;&amp; exit $ac_status
 if $ac_init_version; then
   cat &lt;&lt;\_ACEOF
-tokyotyrant configure 1.1.13
+tokyotyrant configure 1.1.14
 generated by GNU Autoconf 2.61
 
 Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001,
@@ -1363,7 +1363,7 @@ cat &gt;config.log &lt;&lt;_ACEOF
 This file contains any messages produced by compilers while
 running configure, to aid debugging if configure makes a mistake.
 
-It was created by tokyotyrant $as_me 1.1.13, which was
+It was created by tokyotyrant $as_me 1.1.14, which was
 generated by GNU Autoconf 2.61.  Invocation command line was
 
   $ $0 $@
@@ -1719,7 +1719,7 @@ ac_compiler_gnu=$ac_cv_c_compiler_gnu
 
 # Package information
 MYLIBVER=2
-MYLIBREV=4
+MYLIBREV=5
 MYPROTVER=&quot;0.9&quot;
 
 # Targets
@@ -1731,7 +1731,7 @@ MYINITFILES=&quot;ttservctl&quot;
 MYMAN1FILES=&quot;ttserver.1 ttulmgr.1 ttultest.1 tcrtest.1 tcrmttest.1 tcrmgr.1&quot;
 MYMAN3FILES=&quot;ttutil.3 tculog.3 tcrdb.3&quot;
 MYMAN8FILES=&quot;ttservctl.8&quot;
-MYSCREXTFILES=&quot;senatus.lua usherette.lua&quot;
+MYSCREXTFILES=&quot;ext&quot;
 MYDOCUMENTFILES=&quot;COPYING ChangeLog THANKS doc&quot;
 MYPCFILES=&quot;tokyotyrant.pc&quot;
 
@@ -5906,7 +5906,7 @@ exec 6&gt;&amp;1
 # report actual input values of CONFIG_FILES etc. instead of their
 # values after options handling.
 ac_log=&quot;
-This file was extended by tokyotyrant $as_me 1.1.13, which was
+This file was extended by tokyotyrant $as_me 1.1.14, which was
 generated by GNU Autoconf 2.61.  Invocation command line was
 
   CONFIG_FILES    = $CONFIG_FILES
@@ -5949,7 +5949,7 @@ Report bugs to &lt;bug-autoconf@gnu.org&gt;.&quot;
 _ACEOF
 cat &gt;&gt;$CONFIG_STATUS &lt;&lt;_ACEOF
 ac_cs_version=&quot;\\
-tokyotyrant config.status 1.1.13
+tokyotyrant config.status 1.1.14
 configured by $0, generated by GNU Autoconf 2.61,
   with options \\&quot;`echo &quot;$ac_configure_args&quot; | sed 's/^ //; s/[\\&quot;&quot;\`\$]/\\\\&amp;/g'`\\&quot;
 </diff>
      <filename>configure</filename>
    </modified>
    <modified>
      <diff>@@ -7,11 +7,11 @@
 #================================================================
 
 # Package name
-AC_INIT(tokyotyrant, 1.1.13)
+AC_INIT(tokyotyrant, 1.1.14)
 
 # Package information
 MYLIBVER=2
-MYLIBREV=4
+MYLIBREV=5
 MYPROTVER=&quot;0.9&quot;
 
 # Targets
@@ -23,7 +23,7 @@ MYINITFILES=&quot;ttservctl&quot;
 MYMAN1FILES=&quot;ttserver.1 ttulmgr.1 ttultest.1 tcrtest.1 tcrmttest.1 tcrmgr.1&quot;
 MYMAN3FILES=&quot;ttutil.3 tculog.3 tcrdb.3&quot;
 MYMAN8FILES=&quot;ttservctl.8&quot;
-MYSCREXTFILES=&quot;senatus.lua usherette.lua&quot;
+MYSCREXTFILES=&quot;ext&quot;
 MYDOCUMENTFILES=&quot;COPYING ChangeLog THANKS doc&quot;
 MYPCFILES=&quot;tokyotyrant.pc&quot;
 </diff>
      <filename>configure.in</filename>
    </modified>
    <modified>
      <diff>@@ -189,9 +189,12 @@ var {
 
 &lt;h2 id=&quot;introduction&quot;&gt;Introduction&lt;/h2&gt;
 
-&lt;p&gt;Tokyo Tyrant is a package of network interface to the DBM called Tokyo Cabinet.  Though the DBM has high performance, you might bother in case that multiple processes share the same database, or remote processes access the database.  Thus, Tokyo Tyrant is provided for concurrent and remote connections to Tokyo Cabinet.  It is composed of the server process managing a database and its access library for client applications.  The server can embed Lua, a lightweight script language so that you can define arbitrary operations of the database.&lt;/p&gt;
+&lt;p&gt;Tokyo Tyrant is a package of network interface to the DBM called Tokyo Cabinet.  Though the DBM has high performance, you might bother in case that multiple processes share the same database, or remote processes access the database.  Thus, Tokyo Tyrant is provided for concurrent and remote connections to Tokyo Cabinet.  It is composed of the server process managing a database and its access library for client applications.&lt;/p&gt;
+
+&lt;p&gt;The server features high concurrency due to thread-pool modeled implementation and the epoll/kqueue mechanism of the modern Linux/*BSD kernel.  The server and its clients communicate with each other by simple binary protocol on TCP/IP.  Protocols compatible with memcached and HTTP/1.1 are also supported so that almost all principal platforms and programming languages can use Tokyo Tyrant.  High availability and high integrity are also featured due to such mechanisms as hot backup, update logging, and replication.  The server can embed Lua, a lightweight script language so that you can define arbitrary operations of the database.&lt;/p&gt;
+
+&lt;p&gt;Because the server uses the abstract API of Tokyo Cabinet, all of the six APIs: the on-memory hash database API, the on-memory tree database API, the hash API, the B+ tree database API, the fixed-length database API, and the table database API, are available from the client with the common interface.  Moreover, the table extension is provided to use specifidc features of the table database.&lt;/p&gt;
 
-&lt;p&gt;The server features high concurrency due to thread-pool modeled implementation and the epoll/kqueue mechanism of the modern Linux/*BSD kernel.  The server and its clients communicate with each other by simple binary protocol on TCP/IP.  Protocols compatible with memcached and HTTP/1.1 are also supported so that almost all principal platforms and programming languages can use Tokyo Tyrant.  High availability and high integrity are also featured due to such mechanisms as hot backup, update logging, and replication.&lt;/p&gt;
 
 &lt;p&gt;&lt;em&gt;As for now, the server works on Linux, FreeBSD, Mac OS X only.&lt;/em&gt;&lt;/p&gt;
 
@@ -254,12 +257,12 @@ var {
 
 &lt;h2 id=&quot;serverprog&quot;&gt;Server Programs&lt;/h2&gt;
 
-&lt;h3&gt;ttserver&lt;/h3&gt;
+&lt;h3 id=&quot;serverprog_ttserver&quot;&gt;ttserver&lt;/h3&gt;
 
 &lt;p&gt;The command `&lt;code&gt;ttserver&lt;/code&gt;' runs the server managing a database instance.  Because the database is treated by the abstract API of Tokyo Cabinet, you can choose the scheme on start-up of the server.  Supported schema are on-memory hash database, on-memory tree database, hash database, and B+ tree database.  This command is used in the following format.  `&lt;var&gt;dbname&lt;/var&gt;' specifies the database name.  If it is omitted, on-memory hash database is specified.&lt;/p&gt;
 
 &lt;dl class=&quot;api&quot;&gt;
-&lt;dt&gt;&lt;code&gt;ttserver [-host &lt;var&gt;name&lt;/var&gt;] [-port &lt;var&gt;num&lt;/var&gt;] [-th&lt;var&gt;num&lt;/var&gt; &lt;var&gt;num&lt;/var&gt;] [-tout &lt;var&gt;num&lt;/var&gt;] [-dmn] [-pid &lt;var&gt;path&lt;/var&gt;] [-log &lt;var&gt;path&lt;/var&gt;] [-ld|-le] [-ulog &lt;var&gt;path&lt;/var&gt;] [-ulim &lt;var&gt;num&lt;/var&gt;] [-uas] [-sid &lt;var&gt;num&lt;/var&gt;] [-mhost &lt;var&gt;name&lt;/var&gt;] [-mport &lt;var&gt;num&lt;/var&gt;] [-rts &lt;var&gt;path&lt;/var&gt;] [-ext &lt;var&gt;path&lt;/var&gt;] [-mask &lt;var&gt;expr&lt;/var&gt;] [&lt;var&gt;dbname&lt;/var&gt;]&lt;/code&gt;&lt;/dt&gt;
+&lt;dt&gt;&lt;code&gt;ttserver [-host &lt;var&gt;name&lt;/var&gt;] [-port &lt;var&gt;num&lt;/var&gt;] [-th&lt;var&gt;num&lt;/var&gt; &lt;var&gt;num&lt;/var&gt;] [-tout &lt;var&gt;num&lt;/var&gt;] [-dmn] [-pid &lt;var&gt;path&lt;/var&gt;] [-log &lt;var&gt;path&lt;/var&gt;] [-ld|-le] [-ulog &lt;var&gt;path&lt;/var&gt;] [-ulim &lt;var&gt;num&lt;/var&gt;] [-uas] [-sid &lt;var&gt;num&lt;/var&gt;] [-mhost &lt;var&gt;name&lt;/var&gt;] [-mport &lt;var&gt;num&lt;/var&gt;] [-rts &lt;var&gt;path&lt;/var&gt;] [-ext &lt;var&gt;path&lt;/var&gt;] [-extpc &lt;var&gt;name&lt;/var&gt; &lt;var&gt;period&lt;/var&gt;] [-mask &lt;var&gt;expr&lt;/var&gt;] [&lt;var&gt;dbname&lt;/var&gt;]&lt;/code&gt;&lt;/dt&gt;
 &lt;/dl&gt;
 
 &lt;p&gt;Options feature the following.&lt;/p&gt;
@@ -282,6 +285,7 @@ var {
 &lt;li&gt;&lt;code&gt;-mport &lt;var&gt;num&lt;/var&gt;&lt;/code&gt; : specify the port number of the replication master server.&lt;/li&gt;
 &lt;li&gt;&lt;code&gt;-rts &lt;var&gt;path&lt;/var&gt;&lt;/code&gt; : specify the replication time stamp file.&lt;/li&gt;
 &lt;li&gt;&lt;code&gt;-ext &lt;var&gt;path&lt;/var&gt;&lt;/code&gt; : specify the script language extension file.&lt;/li&gt;
+&lt;li&gt;&lt;code&gt;-extpc &lt;var&gt;name&lt;/var&gt; &lt;var&gt;period&lt;/var&gt;&lt;/code&gt; : specify the function name and the calling period of a periodic command.&lt;/li&gt;
 &lt;li&gt;&lt;code&gt;-mask &lt;var&gt;expr&lt;/var&gt;&lt;/code&gt; : specify the names of forbidden commands.&lt;/li&gt;
 &lt;li&gt;&lt;code&gt;-unmask &lt;var&gt;expr&lt;/var&gt;&lt;/code&gt; : specify the names of allowed commands.&lt;/li&gt;
 &lt;/ul&gt;
@@ -292,7 +296,7 @@ var {
 
 &lt;p&gt;The command mask expression is a list of command names separated by &quot;,&quot;.  For example, &quot;out,vanish,copy&quot; means a set of &quot;out&quot;, &quot;vanish&quot;, and &quot;copy&quot;.  Commands of the memcached compatible protocol and the HTTP compatible protocol are also forbidden or allowed, related by the mask of each original command.  Moreover, there are meta expressions.  &quot;all&quot; means all commands.  &quot;allorg&quot; means all commands of the original binary protocol.  &quot;allmc&quot; means all commands of the memcached compatible protocol.  &quot;allhttp&quot; means all commands of the HTTP compatible protocol.  &quot;allread&quot; is the abbreviation of `get', `mget', `vsiz', `iterinit', `iternext', `fwmkeys', `rnum', `size', and `stat'.  &quot;allwrite&quot; is the abbreviation of `put', `putkeep', `putcat', `putshl', `putnr', `out', `addint', `adddouble', `vanish', and `misc'.  &quot;allmanage&quot; is the abbreviation of `sync', `copy', `restore', and `setmst'.  &quot;repl&quot; means replication as master.  &quot;slave&quot; means replication as slave.&lt;/p&gt;
 
-&lt;h3&gt;ttservctl&lt;/h3&gt;
+&lt;h3 id=&quot;serverprog_ttservctl&quot;&gt;ttservctl&lt;/h3&gt;
 
 &lt;p&gt;The command `&lt;code&gt;ttservctl&lt;/code&gt;' is the startup script of the server.  It can be called by the RC script of the bootstrap process of the operating system.  This command is used in the following format.&lt;/p&gt;
 
@@ -309,7 +313,7 @@ var {
 
 &lt;p&gt;The database is placed as &quot;/var/ttserver/casket.tch&quot;.  The log and related files are also placed in &quot;/var/ttserver&quot;.  This command returns 0 on success, another on failure.&lt;/p&gt;
 
-&lt;h3&gt;ttulmgr&lt;/h3&gt;
+&lt;h3 id=&quot;serverprog_ttulmgr&quot;&gt;ttulmgr&lt;/h3&gt;
 
 &lt;p&gt;The command `&lt;code&gt;ttulmgr&lt;/code&gt;' is the utility to export and import the update log.  It is useful to filter the update log with such text utilities as `&lt;code&gt;grep&lt;/code&gt;' and `&lt;code&gt;sed&lt;/code&gt;'.  This command is used in the following format.  `&lt;var&gt;upath&lt;/var&gt;' specifies the update log directory.&lt;/p&gt;
 
@@ -333,7 +337,7 @@ var {
 
 &lt;h2 id=&quot;clientprog&quot;&gt;Client Programs&lt;/h2&gt;
 
-&lt;h3&gt;tcrtest&lt;/h3&gt;
+&lt;h3 id=&quot;clientprog_tcrtest&quot;&gt;tcrtest&lt;/h3&gt;
 
 &lt;p&gt;The command `&lt;code&gt;tcrtest&lt;/code&gt;' is a utility for facility test and performance test.  This command is used in the following format.  `&lt;var&gt;host&lt;/var&gt;' specifies the host name of the server.  `&lt;var&gt;rnum&lt;/var&gt;' specifies the number of iterations.&lt;/p&gt;
 
@@ -372,7 +376,7 @@ var {
 
 &lt;p&gt;If the port number is not more than 0, UNIX domain socket is used and the path of the socket file is specified by the host parameter.  This command returns 0 on success, another on failure.&lt;/p&gt;
 
-&lt;h3&gt;tcrmttest&lt;/h3&gt;
+&lt;h3 id=&quot;clientprog_tcrmttest&quot;&gt;tcrmttest&lt;/h3&gt;
 
 &lt;p&gt;The command `&lt;code&gt;tcrmttest&lt;/code&gt;' is a utility for facility test under multi-thread situation.  This command is used in the following format.  `&lt;var&gt;host&lt;/var&gt;' specifies the host name of the server.  `&lt;var&gt;rnum&lt;/var&gt;' specifies the number of iterations.&lt;/p&gt;
 
@@ -399,7 +403,7 @@ var {
 
 &lt;p&gt;If the port number is not more than 0, UNIX domain socket is used and the path of the socket file is specified by the host parameter.  This command returns 0 on success, another on failure.&lt;/p&gt;
 
-&lt;h3&gt;tcrmgr&lt;/h3&gt;
+&lt;h3 id=&quot;clientprog_tcrmgr&quot;&gt;tcrmgr&lt;/h3&gt;
 
 &lt;p&gt;The command `&lt;code&gt;tcrmgr&lt;/code&gt;' is a utility for test and debugging of the remote database API and its applications.  `&lt;var&gt;host&lt;/var&gt;' specifies the host name of the server.  `&lt;var&gt;key&lt;/var&gt;' specifies the key of a record.  `&lt;var&gt;value&lt;/var&gt;' specifies the value of a record.  `&lt;var&gt;func&lt;/var&gt; specifies the name of the function.  `&lt;var&gt;arg&lt;/var&gt;' specifies the arguments of the function.  `&lt;var&gt;dpath&lt;/var&gt;' specifies the destination file.  `&lt;var&gt;file&lt;/var&gt;' specifies the input file.  `&lt;var&gt;upath&lt;/var&gt;' specifies the update log directory.  `&lt;var&gt;mhost&lt;/var&gt;' specifies the host name of the replication master.  `&lt;var&gt;url&lt;/var&gt;' specifies the target URL.&lt;/p&gt;
 
@@ -475,7 +479,7 @@ var {
 
 &lt;p&gt;Remote database is a set of interfaces to use an abstract database of Tokyo Cabinet, mediated by a server of Tokyo Tyrant.  See `&lt;code&gt;tcrdb.h&lt;/code&gt;' for entire specification.&lt;/p&gt;
 
-&lt;h3&gt;Description&lt;/h3&gt;
+&lt;h3 id=&quot;clientprog_description&quot;&gt;Description&lt;/h3&gt;
 
 &lt;p&gt;To use the remote database API, include `&lt;code&gt;tcrdb.h&lt;/code&gt;' and related standard header files.  Usually, write the following description near the front of a source file.&lt;/p&gt;
 
@@ -490,7 +494,7 @@ var {
 
 &lt;p&gt;Before operations to store or retrieve records, it is necessary to connect the remote database object to the server.  The function `&lt;code&gt;tcrdbopen&lt;/code&gt;' is used to open a database connection and the function `&lt;code&gt;tcrdbclose&lt;/code&gt;' is used to close the connection.&lt;/p&gt;
 
-&lt;h3&gt;API&lt;/h3&gt;
+&lt;h3 id=&quot;tcrdbapi_api&quot;&gt;API&lt;/h3&gt;
 
 &lt;p&gt;The function `tcrdberrmsg' is used in order to get the message string corresponding to an error code.&lt;/p&gt;
 
@@ -919,7 +923,7 @@ var {
 &lt;dd&gt;Because the object of the return value is created with the function `tclistnew', it should be deleted with the function `tclistdel' when it is no longer in use.&lt;/dd&gt;
 &lt;/dl&gt;
 
-&lt;h3&gt;API of the Table Extension&lt;/h3&gt;
+&lt;h3 id=&quot;tcrdbapi_apitbl&quot;&gt;API of the Table Extension&lt;/h3&gt;
 
 &lt;p&gt;The function `tcrdbtblput' is used in order to store a record into a remote database object.&lt;/p&gt;
 
@@ -1075,7 +1079,7 @@ var {
 &lt;dd&gt;Because the object of the return value is created with the function `tcmapnew', it should be deleted with the function `tcmapdel' when it is no longer in use.&lt;/dd&gt;
 &lt;/dl&gt;
 
-&lt;h3&gt;Example Code&lt;/h3&gt;
+&lt;h3 id=&quot;tcrdbapi_example&quot;&gt;Example Code&lt;/h3&gt;
 
 &lt;p&gt;The following code is an example to use a remote database.&lt;/p&gt;
 
@@ -1130,7 +1134,90 @@ int main(int argc, char **argv){
 }
 &lt;/pre&gt;
 
-&lt;h3&gt;How to Use the Library&lt;/h3&gt;
+&lt;p&gt;The following code is an example to use a remote database with the table extension.&lt;/p&gt;
+
+&lt;pre&gt;#include &amp;lt;tcrdb.h&amp;gt;
+#include &amp;lt;stdlib.h&amp;gt;
+#include &amp;lt;stdbool.h&amp;gt;
+#include &amp;lt;stdint.h&amp;gt;
+
+int main(int argc, char **argv){
+
+  TCRDB *rdb;
+  int ecode, pksiz, i, rsiz;
+  char pkbuf[256];
+  const char *rbuf, *name;
+  TCMAP *cols;
+  RDBQRY *qry;
+  TCLIST *res;
+
+  /* create the object */
+  rdb = tcrdbnew();
+
+  /* connect to the server */
+  if(!tcrdbopen(rdb, &quot;localhost&quot;, 1978)){
+    ecode = tcrdbecode(rdb);
+    fprintf(stderr, &quot;open error: %s\n&quot;, tcrdberrmsg(ecode));
+  }
+
+  /* store a record */
+  pksiz = sprintf(pkbuf, &quot;%ld&quot;, (long)tcrdbtblgenuid(rdb));
+  cols = tcmapnew3(&quot;name&quot;, &quot;mikio&quot;, &quot;age&quot;, &quot;30&quot;, &quot;lang&quot;, &quot;ja,en,c&quot;, NULL);
+  if(!tcrdbtblput(rdb, pkbuf, pksiz, cols)){
+    ecode = tcrdbecode(rdb);
+    fprintf(stderr, &quot;put error: %s\n&quot;, tcrdberrmsg(ecode));
+  }
+  tcmapdel(cols);
+
+  /* store a record in a naive way */
+  pksiz = sprintf(pkbuf, &quot;12345&quot;);
+  cols = tcmapnew();
+  tcmapput2(cols, &quot;name&quot;, &quot;falcon&quot;);
+  tcmapput2(cols, &quot;age&quot;, &quot;31&quot;);
+  tcmapput2(cols, &quot;lang&quot;, &quot;ja&quot;);
+  if(!tcrdbtblput(rdb, pkbuf, pksiz, cols)){
+    ecode = tcrdbecode(rdb);
+    fprintf(stderr, &quot;put error: %s\n&quot;, tcrdberrmsg(ecode));
+  }
+  tcmapdel(cols);
+
+  /* search for records */
+  qry = tcrdbqrynew(rdb);
+  tcrdbqryaddcond(qry, &quot;age&quot;, RDBQCNUMGE, &quot;20&quot;);
+  tcrdbqryaddcond(qry, &quot;lang&quot;, RDBQCSTROR, &quot;ja,en&quot;);
+  tcrdbqrysetorder(qry, &quot;name&quot;, RDBQOSTRASC);
+  tcrdbqrysetmax(qry, 10);
+  res = tcrdbqrysearch(qry);
+  for(i = 0; i &amp;lt; tclistnum(res); i++){
+    rbuf = tclistval(res, i, &amp;amp;rsiz);
+    cols = tcrdbtblget(rdb, rbuf, rsiz);
+    if(cols){
+      printf(&quot;%s&quot;, rbuf);
+      tcmapiterinit(cols);
+      while((name = tcmapiternext2(cols)) != NULL){
+        printf(&quot;\t%s\t%s&quot;, name, tcmapget2(cols, name));
+      }
+      printf(&quot;\n&quot;);
+      tcmapdel(cols);
+    }
+  }
+  tclistdel(res);
+  tcrdbqrydel(qry);
+
+  /* close the connection */
+  if(!tcrdbclose(rdb)){
+    ecode = tcrdbecode(rdb);
+    fprintf(stderr, &quot;close error: %s\n&quot;, tcrdberrmsg(ecode));
+  }
+
+  /* delete the object */
+  tcrdbdel(rdb);
+
+  return 0;
+}
+&lt;/pre&gt;
+
+&lt;h3 id=&quot;tcrdbapi_uselib&quot;&gt;How to Use the Library&lt;/h3&gt;
 
 &lt;p&gt;The API of C is available by programs conforming to the C89 (ANSI C) standard or the C99 standard.  As the header files of Tokyo Tyrant are provided as `&lt;code&gt;tcrdb.h&lt;/code&gt;', applications should include it to use the API.  As the library is provided as `&lt;code&gt;libtokyotyrant.a&lt;/code&gt;' and `&lt;code&gt;libtokyotyrant.so&lt;/code&gt;' and they depends `&lt;code&gt;libtokyocabinet.so&lt;/code&gt;', `&lt;code&gt;libz.so&lt;/code&gt;', `&lt;code&gt;libbz2.so&lt;/code&gt;', `&lt;code&gt;libresolv.so&lt;/code&gt;', `&lt;code&gt;libnsl.so&lt;/code&gt;', `&lt;code&gt;libdl.so&lt;/code&gt;', `&lt;code&gt;librt.so&lt;/code&gt;', `&lt;code&gt;libpthread.so&lt;/code&gt;', `&lt;code&gt;libm.so&lt;/code&gt;', and `&lt;code&gt;libc.so&lt;/code&gt;', linker options `&lt;code&gt;-ltokyotyrant&lt;/code&gt;', `&lt;code&gt;-ltokyocabinet&lt;/code&gt;', `&lt;code&gt;-lz&lt;/code&gt;', `&lt;code&gt;-lbz2&lt;/code&gt;', `&lt;code&gt;-lresolv&lt;/code&gt;', `&lt;code&gt;-lnsl&lt;/code&gt;', `&lt;code&gt;-ldl&lt;/code&gt;', `&lt;code&gt;-lrt&lt;/code&gt;', `&lt;code&gt;-lpthread&lt;/code&gt;', `&lt;code&gt;-lm&lt;/code&gt;', and `&lt;code&gt;-lc&lt;/code&gt;' are required for build command.  A typical build command is the following.&lt;/p&gt;
 
@@ -1147,7 +1234,7 @@ int main(int argc, char **argv){
 
 &lt;p&gt;The database server can starts reading a Lua script file by the `&lt;code&gt;-ext&lt;/code&gt;' option.  Clients can call functions defined in the script file by the `&lt;code&gt;tcrdbext&lt;/code&gt;' function of the remote database API.&lt;/p&gt;
 
-&lt;h3&gt;User-defined Functions&lt;/h3&gt;
+&lt;h3 id=&quot;luaext_userfunc&quot;&gt;User-defined Functions&lt;/h3&gt;
 
 &lt;p&gt;You can define some arbitrary functions in the script file.  Each function receives two string parameters of the key and the value.  The return value is sent back to the client.  If the function returns `nil', the server send the error code to the client.&lt;/p&gt;
 
@@ -1155,7 +1242,7 @@ int main(int argc, char **argv){
 
 &lt;p&gt;Note that instances of Lua interpreter are handled separately by each native thread. Because global variables of Lua are not useful to share some data among native threads or sessions, shared data should be handled in the database or by the stash functions.&lt;/p&gt;
 
-&lt;h3&gt;Built-in Functions&lt;/h3&gt;
+&lt;h3 id=&quot;luaext_builtinfunc&quot;&gt;Built-in Functions&lt;/h3&gt;
 
 &lt;p&gt;The following build-in functions for database operations are available in user defined functions.  The type of `key' and `value' parameters should be string or number.  If number is given, it is converted as decimal string.&lt;/p&gt;
 
@@ -1318,7 +1405,7 @@ int main(int argc, char **argv){
 
 &lt;p&gt;The global variable `&lt;code&gt;_version&lt;/code&gt;' contains the version information of the server.  The global variable `&lt;code&gt;_pid&lt;/code&gt;' contains the process ID.  The global variable `&lt;code&gt;_sid&lt;/code&gt;' contains the server ID.  The global variable `&lt;code&gt;_thnum&lt;/code&gt;' contains the number of native threads.  The global variable `&lt;code&gt;_thid&lt;/code&gt;' contains the ID number of each native thread.&lt;/p&gt;
 
-&lt;h3&gt;Example Code&lt;/h3&gt;
+&lt;h3 id=&quot;luaext_example&quot;&gt;Example Code&lt;/h3&gt;
 
 &lt;p&gt;The following code is an example to increment the record value and store as a decimal number string.  This function should be called with the record locking option to ensure atomicity.&lt;/p&gt;
 
@@ -1678,7 +1765,7 @@ end
 
 &lt;h2 id=&quot;tutorial&quot;&gt;Tutorial&lt;/h2&gt;
 
-&lt;h3&gt;Basic Use&lt;/h3&gt;
+&lt;h3 id=&quot;tutorial_basic&quot;&gt;Basic Use&lt;/h3&gt;
 
 &lt;p&gt;After installation of Tokyo Tyrant, you can start the server immediately by executing the command `&lt;code&gt;ttserver&lt;/code&gt;' in the terminal.  By default, the server listens to the port 1978 and serves as the accessor of an on-memory hash database, which is useful to store cache data.&lt;/p&gt;
 
@@ -1733,7 +1820,7 @@ end
 &lt;pre&gt;[terminal-1]$ rm casket.tch
 &lt;/pre&gt;
 
-&lt;h3&gt;Daemon&lt;/h3&gt;
+&lt;h3 id=&quot;tutorial_daemon&quot;&gt;Daemon&lt;/h3&gt;
 
 &lt;p&gt;To run the server as a daemon process, specify the option `&lt;code&gt;-dmn&lt;/code&gt;'.  Moreover, the option `&lt;code&gt;-pid&lt;/code&gt;' should be specified to record the process ID into a file.  Note that the current directory of the daemon process is changed to the root directory.  So, the file path parameter should be expressed as the absolute path.&lt;/p&gt;
 
@@ -1752,7 +1839,7 @@ end
 
 &lt;p&gt;By default, the database file and the related files are placed under `&lt;code&gt;/var/ttserver&lt;/code&gt;'.  Because `&lt;code&gt;ttservctl&lt;/code&gt;' is a tiny shell script, copy and edit it for your purpose.  Also, it is suitable to install the modified script into `&lt;code&gt;/etc/init.d&lt;/code&gt;' and set symbolic links from `&lt;code&gt;/etc/rc3.d/S98ttserver&lt;/code&gt;' and `&lt;code&gt;/etc/rc5.d/S98ttserver&lt;/code&gt;'.&lt;/p&gt;
 
-&lt;h3&gt;Backup and Recovery&lt;/h3&gt;
+&lt;h3 id=&quot;tutorial_backup&quot;&gt;Backup and Recovery&lt;/h3&gt;
 
 &lt;p&gt;Let's run the server again to continue this tutorial.&lt;/p&gt;
 
@@ -1792,7 +1879,7 @@ end
 &lt;pre&gt;[terminal-1]$ rm casket.tch backup.tch
 &lt;/pre&gt;
 
-&lt;h3&gt;Update Log&lt;/h3&gt;
+&lt;h3 id=&quot;tutorial_ulog&quot;&gt;Update Log&lt;/h3&gt;
 
 &lt;p&gt;Let's run the server with update logging enabled.  The option `&lt;code&gt;-ulog&lt;/code&gt;' specifies the directory to contain the update log files.&lt;/p&gt;
 
@@ -1834,7 +1921,7 @@ end
 &lt;pre&gt;[terminal-1]$ rm -rf casket.tch ulog ulog-back
 &lt;/pre&gt;
 
-&lt;h3&gt;Replication&lt;/h3&gt;
+&lt;h3 id=&quot;tutorial_replication&quot;&gt;Replication&lt;/h3&gt;
 
 &lt;p&gt;Replication is a mechanism to synchronize two or more database servers for high availability and high integrity.  The replication source server is called &quot;master&quot; and each destination server is called &quot;slave&quot;.  Replication requires the following preconditions.&lt;/p&gt;
 
@@ -1905,7 +1992,7 @@ end
 
 &lt;p&gt;Tokyo Tyrant supports &quot;dual master&quot; replication which realizes higher availability.  To do it, run two servers which replicate each other.&lt;/p&gt;
 
-&lt;h3&gt;Setting Replication on Demand&lt;/h3&gt;
+&lt;h3 id=&quot;tutorial_repondemand&quot;&gt;Setting Replication on Demand&lt;/h3&gt;
 
 &lt;p&gt;You can set replication of the running database service without any downtime.  First, prepare the following script for backup operation and save it as &quot;ttbackup.sh&quot; with executable permission (0755).&lt;/p&gt;
 
@@ -1975,7 +2062,7 @@ cp -f &quot;$srcpath&quot; &quot;$destpath&quot;
 [terminal-2]$ rm -rf casket-2.tch ulog-2 2.rts
 &lt;/pre&gt;
 
-&lt;h3&gt;Tuning&lt;/h3&gt;
+&lt;h3 id=&quot;tutorial_tuning&quot;&gt;Tuning&lt;/h3&gt;
 
 &lt;p&gt;If you use a hash database, set the tuning parameter &quot;#bnum=&lt;var&gt;xxx&lt;/var&gt;&quot; to improve performance.  It specifies the bucket number and should be more than the number of record to be stored.&lt;/p&gt;
 
@@ -1983,7 +2070,7 @@ cp -f &quot;$srcpath&quot; &quot;$destpath&quot;
 
 &lt;p&gt;If huge number of clients access the server, make sure the limit number of file descriptors per process is cleared.  By default on most systems, it is set as 1024.  If so, use `&lt;code&gt;ulimit&lt;/code&gt;' to clear it.&lt;/p&gt;
 
-&lt;h3&gt;Lua Extension&lt;/h3&gt;
+&lt;h3 id=&quot;tutorial_luaext&quot;&gt;Lua Extension&lt;/h3&gt;
 
 &lt;p&gt;If you want more complex database operations than existing ones, use the Lua extension.  For example, prepare the following script and save it as &quot;test.lua&quot;.  There is a function &quot;fibonacci&quot; which returns the Fibonacci number of a number of the key.&lt;/p&gt;
 
@@ -2037,9 +2124,9 @@ end
 [terminal-2]$ tcrmgr ext localhost fibnext
 &lt;/pre&gt;
 
-&lt;p&gt;As you see, the called function receives two string parameters of the key and the value.  The return value is sent back to the client.  You can use such built-in functions for database operations as &quot;_put&quot;, &quot;_out&quot;, &quot;_get&quot;, and so on.  There is a sample file `&lt;code&gt;/usr/local/share/tokyotyrant/senatus.lua&lt;/code&gt;'.&lt;/p&gt;
+&lt;p&gt;As you see, the called function receives two string parameters of the key and the value.  The return value is sent back to the client.  You can use such built-in functions for database operations as &quot;_put&quot;, &quot;_out&quot;, &quot;_get&quot;, and so on.  There is a sample file `&lt;code&gt;ext/senatus.lua&lt;/code&gt;'.&lt;/p&gt;
 
-&lt;h3&gt;Using memcached Client&lt;/h3&gt;
+&lt;h3 id=&quot;tutorial_memcached&quot;&gt;Using memcached Client&lt;/h3&gt;
 
 &lt;p&gt;This section describes how to use a memcached client library of Perl (Cache::Memcached) with Tokyo Tyrant.  Run the server of Tokyo Tyrant as usual.  And, the following script is a typical example.&lt;/p&gt;
 
@@ -2063,7 +2150,7 @@ printf(&quot;three: %s\n&quot;, $val-&amp;gt;{three});
 $memd-&amp;gt;delete('one');
 &lt;/pre&gt;
 
-&lt;h3&gt;Using HTTP Client&lt;/h3&gt;
+&lt;h3 id=&quot;tutorial_http&quot;&gt;Using HTTP Client&lt;/h3&gt;
 
 &lt;p&gt;This section describes how to use an HTTP client library of Perl (LWP::UserAgent) with Tokyo Tyrant.  Run the server of Tokyo Tyrant as usual.  And, the following script is a typical example.&lt;/p&gt;
 
@@ -2097,6 +2184,49 @@ if($res-&amp;gt;is_success()){
 }
 &lt;/pre&gt;
 
+&lt;h3 id=&quot;tutorial_expcache&quot;&gt;Persistent but Expirable Cache&lt;/h3&gt;
+
+&lt;p&gt;If you want to cache data like session information for your Web application but want to avoid data loss because of the server crash, using Tokyo Tyrant can be the solution, that is to say, &quot;persistent&quot; expirable cache.  It requires the following preconditions.&lt;/p&gt;
+
+&lt;ul&gt;
+&lt;li&gt;The server must open a table database.&lt;/li&gt;
+&lt;li&gt;Clients should store each record with an expiration date column.&lt;/li&gt;
+&lt;li&gt;The database should have an index for the expiration date column.&lt;/li&gt;
+&lt;li&gt;The server must periodically call the user defined function provided through the Lua extension.&lt;/li&gt;
+&lt;/ul&gt;
+
+&lt;p&gt;First, prepare the following script for expiration and save it as &quot;ttexpire.lua&quot;.  It will expire records where the value of the &quot;x&quot; column exceeds the current date.&lt;/p&gt;
+
+&lt;pre&gt;function expire()
+   local args = {};
+   local cdate = string.format(&quot;%d&quot;, _time())
+   table.insert(args, &quot;addcond\0x\0NUMLE\0&quot; .. cdate)
+   table.insert(args, &quot;out&quot;)
+   local res = _misc(&quot;search&quot;, args)
+   if not res then
+      _log(&quot;expiration was failed&quot;)
+   end
+end
+&lt;/pre&gt;
+
+&lt;p&gt;Start the server by opening a table database which has the index for the &quot;x&quot; column, and by scheduling it to call the expiration function per second.&lt;/p&gt;
+
+&lt;pre&gt;[terminal-1]$ ttserver -ext ttexpire.lua -extpc expire 1.0 &quot;casket.tct#idx=x:dec&quot;
+&lt;/pre&gt;
+
+&lt;p&gt;Store test records from another terminal.&lt;/p&gt;
+
+&lt;pre&gt;[terminal-2]$ now=`date +%s`
+for((i=1;i&amp;lt;=60;i++)); do
+  tcrmgr put -sep '|' localhost &quot;$i&quot; &quot;x|$((now+i))&quot;
+done
+&lt;/pre&gt;
+
+&lt;p&gt;You can confirm that the records are being removed by expiration.&lt;/p&gt;
+
+&lt;pre&gt;[terminal-2]$ tcrmgr list -pv -sep '|' localhost
+&lt;/pre&gt;
+
 &lt;hr /&gt;
 
 &lt;h2 id=&quot;license&quot;&gt;License&lt;/h2&gt;</diff>
      <filename>doc/index.html</filename>
    </modified>
    <modified>
      <diff>@@ -5,11 +5,11 @@ ttserver \- the server of Tokyo Tyrant
 
 .SH DESCRIPTION
 .PP
-The command `\fBttserver\fR' runs the server managing a database instance.  Because the database is treated by the abstract API of Tokyo Cabinet, you can choose the scheme on start\-up of the server.  Supported schema are on\-memory database, hash database, and B+ tree database.  This command is used in the following format.  `\fIdbname\fR' specifies the database name.  If it is omitted, on\-memory database is specified.
+The command `\fBttserver\fR' runs the server managing a database instance.  Because the database is treated by the abstract API of Tokyo Cabinet, you can choose the scheme on start\-up of the server.  Supported schema are on\-memory hash database, on\-memory tree database, hash database, and B+ tree database.  This command is used in the following format.  `\fIdbname\fR' specifies the database name.  If it is omitted, on\-memory hash database is specified.
 .PP
 .RS
 .br
-\fBttserver \fR[\fB\-host \fIname\fB\fR]\fB \fR[\fB\-port \fInum\fB\fR]\fB \fR[\fB\-th\fInum\fB \fInum\fB\fR]\fB \fR[\fB\-tout \fInum\fB\fR]\fB \fR[\fB\-dmn\fR]\fB \fR[\fB\-pid \fIpath\fB\fR]\fB \fR[\fB\-log \fIpath\fB\fR]\fB \fR[\fB\-ld\fR|\fB\-le\fR]\fB \fR[\fB\-ulog \fIpath\fB\fR]\fB \fR[\fB\-ulim \fInum\fB\fR]\fB \fR[\fB\-uas\fR]\fB \fR[\fB\-sid \fInum\fB\fR]\fB \fR[\fB\-mhost \fIname\fB\fR]\fB \fR[\fB\-mport \fInum\fB\fR]\fB \fR[\fB\-rts \fIpath\fB\fR]\fB \fR[\fB\-ext \fIpath\fB\fR]\fB \fR[\fB\fIdbname\fB\fR]\fB\fR
+\fBttserver \fR[\fB\-host \fIname\fB\fR]\fB \fR[\fB\-port \fInum\fB\fR]\fB \fR[\fB\-th\fInum\fB \fInum\fB\fR]\fB \fR[\fB\-tout \fInum\fB\fR]\fB \fR[\fB\-dmn\fR]\fB \fR[\fB\-pid \fIpath\fB\fR]\fB \fR[\fB\-log \fIpath\fB\fR]\fB \fR[\fB\-ld\fR|\fB\-le\fR]\fB \fR[\fB\-ulog \fIpath\fB\fR]\fB \fR[\fB\-ulim \fInum\fB\fR]\fB \fR[\fB\-uas\fR]\fB \fR[\fB\-sid \fInum\fB\fR]\fB \fR[\fB\-mhost \fIname\fB\fR]\fB \fR[\fB\-mport \fInum\fB\fR]\fB \fR[\fB\-rts \fIpath\fB\fR]\fB \fR[\fB\-ext \fIpath\fB\fR]\fB \fR[\fB\-extpc \fIname\fB \fIperiod\fB\fR]\fB \fR[\fB\-mask \fIexpr\fB\fR]\fB \fR[\fB\fIdbname\fB\fR]\fB\fR
 .RE
 .PP
 Options feature the following.
@@ -49,6 +49,12 @@ Options feature the following.
 .br
 \fB\-ext \fIpath\fR\fR : specify the script language extension file.
 .br
+\fB\-extpc \fIname\fR \fIperiod\fR\fR : specify the function name and the calling period of a periodic command.
+.br
+\fB\-mask \fIexpr\fR\fR : specify the names of forbidden commands.
+.br
+\fB\-unmask \fIexpr\fR\fR : specify the names of allowed commands.
+.br
 .RE
 .PP
 To terminate the server normally, send SIGINT or SIGTERM to the process.  It is okay to press Ctrl\-C on the controlling terminal.  To restart the server, send SIGHUP to the process.  If the port number is not more than 0, UNIX domain socket is used and the path of the socket file is specified by the host parameter.  This command returns 0 on success, another on failure.</diff>
      <filename>man/ttserver.1</filename>
    </modified>
    <modified>
      <diff>@@ -1317,7 +1317,7 @@ static int proctable(const char *host, int port, int cnum, int rnum){
       int type = types[myrand(sizeof(types) / sizeof(*types))];
       tcrdbqrysetorder(qry, name, type);
     }
-    if(myrand(3) != 0) tcrdbqrysetmax(qry, myrand(i));
+    tcrdbqrysetmax(qry, myrand(10));
     TCLIST *res = tcrdbqrysearch(qry);
     tclistdel(res);
     tcrdbqrydel(qry);</diff>
      <filename>tcrtest.c</filename>
    </modified>
    <modified>
      <diff>@@ -79,6 +79,15 @@ typedef struct {                         // type of structure of master synchron
   bool recon;
 } REPLARG;
 
+typedef struct {                         // type of structure of periodic command
+  const char *name;
+  TCADB *adb;
+  TCULOG *ulog;
+  uint32_t sid;
+  REPLARG *sarg;
+  void *scrext;
+} EXTPCARG;
+
 typedef struct {                         // type of structure of task opaque object
   uint64_t mask;
   TCADB *adb;
@@ -108,9 +117,10 @@ static int proc(const char *dbname, const char *host, int port, int thnum, int t
                 bool dmn, const char *pidpath, const char *logpath,
                 const char *ulogpath, uint64_t ulim, bool uas, uint32_t sid,
                 const char *mhost, int mport, const char *rtspath, const char *extpath,
-                uint64_t mask);
+                const TCLIST *extpcs, uint64_t mask);
 static void do_log(int level, const char *msg, void *opq);
 static void do_slave(void *opq);
+static void do_extpc(void *opq);
 static void do_task(TTSOCK *sock, void *opq, TTREQ *req);
 static int tokenize(char *str, char **tokens, int max);
 static uint32_t recmtxidx(const char *kbuf, int ksiz);
@@ -169,6 +179,7 @@ int main(int argc, char **argv){
   char *mhost = NULL;
   char *rtspath = NULL;
   char *extpath = NULL;
+  TCLIST *extpcs = NULL;
   int port = DEFPORT;
   int thnum = DEFTHNUM;
   int tout = 0;
@@ -227,6 +238,12 @@ int main(int argc, char **argv){
       } else if(!strcmp(argv[i], &quot;-ext&quot;)){
         if(++i &gt;= argc) usage();
         extpath = argv[i];
+      } else if(!strcmp(argv[i], &quot;-extpc&quot;)){
+        if(!extpcs) extpcs = tclistnew2(1);
+        if(++i &gt;= argc) usage();
+        tclistpush2(extpcs, argv[i]);
+        if(++i &gt;= argc) usage();
+        tclistpush2(extpcs, argv[i]);
       } else if(!strcmp(argv[i], &quot;-mask&quot;)){
         if(++i &gt;= argc) usage();
         mask |= getcmdmask(argv[i]);
@@ -263,8 +280,9 @@ int main(int argc, char **argv){
   if(!rtspath) rtspath = DEFRTSPATH;
   g_serv = ttservnew();
   int rv = proc(dbname, host, port, thnum, tout, dmn, pidpath, logpath,
-                ulogpath, ulim, uas, sid, mhost, mport, rtspath, extpath, mask);
+                ulogpath, ulim, uas, sid, mhost, mport, rtspath, extpath, extpcs, mask);
   ttservdel(g_serv);
+  if(extpcs) tclistdel(extpcs);
   return rv;
 }
 
@@ -276,8 +294,8 @@ static void usage(void){
   fprintf(stderr, &quot;usage:\n&quot;);
   fprintf(stderr, &quot;  %s [-host name] [-port num] [-thnum num] [-tout num]&quot;
           &quot; [-dmn] [-pid path] [-log path] [-ld|-le] [-ulog path] [-ulim num] [-uas] [-sid num]&quot;
-          &quot; [-mhost name] [-mport num] [-rts path] [-ext path] [-mask expr] [-unmask expr]&quot;
-          &quot; [dbname]\n&quot;, g_progname);
+          &quot; [-mhost name] [-mport num] [-rts path] [-ext path] [-extpc name period]&quot;
+          &quot; [-mask expr] [-unmask expr] [dbname]\n&quot;, g_progname);
   fprintf(stderr, &quot;\n&quot;);
   exit(1);
 }
@@ -382,7 +400,7 @@ static int proc(const char *dbname, const char *host, int port, int thnum, int t
                 bool dmn, const char *pidpath, const char *logpath,
                 const char *ulogpath, uint64_t ulim, bool uas, uint32_t sid,
                 const char *mhost, int mport, const char *rtspath, const char *extpath,
-                uint64_t mask){
+                const TCLIST *extpcs, uint64_t mask){
   LOGARG larg;
   larg.fd = 1;
   ttservsetloghandler(g_serv, do_log, &amp;larg);
@@ -516,7 +534,31 @@ static int proc(const char *dbname, const char *host, int port, int thnum, int t
   sarg.sid = sid;
   sarg.fail = false;
   sarg.recon = false;
-  if(!(mask &amp; TTMSKSLAVE)) ttservsettimedhandler(g_serv, 1.0, do_slave, &amp;sarg);
+  if(!(mask &amp; TTMSKSLAVE)) ttservaddtimedhandler(g_serv, 1.0, do_slave, &amp;sarg);
+  EXTPCARG *pcargs = NULL;
+  int pcnum = 0;
+  if(extpath &amp;&amp; extpcs){
+    pcnum = tclistnum(extpcs) / 2;
+    pcargs = tcmalloc(sizeof(*pcargs) * pcnum);
+    for(int i = 0; i &lt; pcnum; i++){
+      const char *name = tclistval2(extpcs, i * 2);
+      double period = tcatof(tclistval2(extpcs, i * 2 + 1));
+      EXTPCARG *pcarg = pcargs + i;
+      pcarg-&gt;name = name;
+      pcarg-&gt;adb = adb;
+      pcarg-&gt;ulog = ulog;
+      pcarg-&gt;sid = sid;
+      pcarg-&gt;sarg = &amp;sarg;
+      pcarg-&gt;scrext = scrextnew(thnum, thnum + i, extpath, adb, ulog, sid, scrstash,
+                                scrlcks, RECMTXNUM, do_log);
+      if(pcarg-&gt;scrext){
+        if(*name &amp;&amp; period &gt; 0) ttservaddtimedhandler(g_serv, period, do_extpc, pcarg);
+      } else {
+        err = true;
+        ttservlog(g_serv, TTLOGERROR, &quot;scrextnew failed&quot;);
+      }
+    }
+  }
   TASKARG targ;
   targ.mask = mask;
   targ.adb = adb;
@@ -552,6 +594,16 @@ static int proc(const char *dbname, const char *host, int port, int thnum, int t
     }
     if(!ttservstart(g_serv)) err = true;
   } while(g_restart);
+  if(pcargs){
+    for(int i = 0; i &lt; pcnum; i++){
+      EXTPCARG *pcarg = pcargs + i;
+      if(pcarg-&gt;scrext &amp;&amp; !scrextdel(pcarg-&gt;scrext)){
+        err = true;
+        ttservlog(g_serv, TTLOGERROR, &quot;scrextdel failed&quot;);
+      }
+    }
+    tcfree(pcargs);
+  }
   for(int i = 0; i &lt; RECMTXNUM; i++){
     if(pthread_mutex_destroy(targ.rmtxs + i) != 0)
       ttservlog(g_serv, TTLOGERROR, &quot;pthread_mutex_destroy failed&quot;);
@@ -669,6 +721,17 @@ static void do_slave(void *opq){
 }
 
 
+/* perform an extension command */
+static void do_extpc(void *opq){
+  EXTPCARG *arg = (EXTPCARG *)opq;
+  const char *name = arg-&gt;name;
+  void *scr = arg-&gt;scrext;
+  int xsiz;
+  char *xbuf = scrextcallmethod(scr, name, &quot;&quot;, 0, &quot;&quot;, 0, &amp;xsiz);
+  tcfree(xbuf);
+}
+
+
 /* handle a task and dispatch it */
 static void do_task(TTSOCK *sock, void *opq, TTREQ *req){
   TASKARG *arg = (TASKARG *)opq;</diff>
      <filename>ttserver.c</filename>
    </modified>
    <modified>
      <diff>@@ -387,6 +387,11 @@ bool ttsockprintf(TTSOCK *sock, const char *format, ...){
 /* Receive data by a socket. */
 bool ttsockrecv(TTSOCK *sock, char *buf, int size){
   assert(sock &amp;&amp; buf &amp;&amp; size &gt;= 0);
+  if(sock-&gt;rp + size &lt;= sock-&gt;ep){
+    memcpy(buf, sock-&gt;rp, size);
+    sock-&gt;rp += size;
+    return true;
+  }
   bool err = false;
   char *wp = buf;
   while(size &gt; 0){
@@ -460,26 +465,18 @@ bool ttsockgets(TTSOCK *sock, char *buf, int size){
 /* Receive an 32-bit integer by a socket. */
 uint32_t ttsockgetint32(TTSOCK *sock){
   assert(sock);
-  uint32_t num = ttsockgetc(sock) &lt;&lt; 24;
-  num |= ttsockgetc(sock) &lt;&lt; 16;
-  num |= ttsockgetc(sock) &lt;&lt; 8;
-  num |= ttsockgetc(sock);
-  return num;
+  uint32_t num;
+  ttsockrecv(sock, (char *)&amp;num, sizeof(num));
+  return TTNTOHL(num);
 }
 
 
 /* Receive an 64-bit integer by a socket. */
 uint64_t ttsockgetint64(TTSOCK *sock){
   assert(sock);
-  uint64_t num = (uint64_t)ttsockgetc(sock) &lt;&lt; 56;
-  num |= (uint64_t)ttsockgetc(sock) &lt;&lt; 48;
-  num |= (uint64_t)ttsockgetc(sock) &lt;&lt; 40;
-  num |= (uint64_t)ttsockgetc(sock) &lt;&lt; 32;
-  num |= (uint64_t)ttsockgetc(sock) &lt;&lt; 24;
-  num |= (uint64_t)ttsockgetc(sock) &lt;&lt; 16;
-  num |= (uint64_t)ttsockgetc(sock) &lt;&lt; 8;
-  num |= (uint64_t)ttsockgetc(sock);
-  return num;
+  uint64_t num;
+  ttsockrecv(sock, (char *)&amp;num, sizeof(num));
+  return TTNTOHLL(num);
 }
 
 
@@ -743,14 +740,14 @@ TTSERV *ttservnew(void){
   serv-&gt;queue = tclistnew();
   if(pthread_mutex_init(&amp;serv-&gt;qmtx, NULL) != 0) tcmyfatal(&quot;pthread_mutex_init failed&quot;);
   if(pthread_cond_init(&amp;serv-&gt;qcnd, NULL) != 0) tcmyfatal(&quot;pthread_cond_init failed&quot;);
+  if(pthread_mutex_init(&amp;serv-&gt;tmtx, NULL) != 0) tcmyfatal(&quot;pthread_mutex_init failed&quot;);
+  if(pthread_cond_init(&amp;serv-&gt;tcnd, NULL) != 0) tcmyfatal(&quot;pthread_cond_init failed&quot;);
   serv-&gt;thnum = TTDEFTHNUM;
   serv-&gt;timeout = 0;
   serv-&gt;term = false;
   serv-&gt;do_log = NULL;
   serv-&gt;opq_log = NULL;
-  serv-&gt;freq_timed = 0.0;
-  serv-&gt;do_timed = NULL;
-  serv-&gt;opq_timed = NULL;
+  serv-&gt;timernum = 0;
   serv-&gt;do_task = NULL;
   serv-&gt;opq_task = NULL;
   return serv;
@@ -760,8 +757,10 @@ TTSERV *ttservnew(void){
 /* Delete a server object. */
 void ttservdel(TTSERV *serv){
   assert(serv);
-  pthread_mutex_destroy(&amp;serv-&gt;qmtx);
+  pthread_cond_destroy(&amp;serv-&gt;tcnd);
+  pthread_mutex_destroy(&amp;serv-&gt;tmtx);
   pthread_cond_destroy(&amp;serv-&gt;qcnd);
+  pthread_mutex_destroy(&amp;serv-&gt;qmtx);
   tclistdel(serv-&gt;queue);
   tcfree(serv);
 }
@@ -806,12 +805,15 @@ void ttservsetloghandler(TTSERV *serv, void (*do_log)(int, const char *, void *)
 }
 
 
-/* Set the timed handler of a server object. */
-void ttservsettimedhandler(TTSERV *serv, double freq, void (*do_timed)(void *), void *opq){
+/* Add a timed handler to a server object. */
+void ttservaddtimedhandler(TTSERV *serv, double freq, void (*do_timed)(void *), void *opq){
   assert(serv &amp;&amp; freq &gt;= 0.0 &amp;&amp; do_timed);
-  serv-&gt;freq_timed = freq;
-  serv-&gt;do_timed = do_timed;
-  serv-&gt;opq_timed = opq;
+  if(serv-&gt;timernum &gt;= TTTIMERMAX - 1) return;
+  TTTIMER *timer = serv-&gt;timers + serv-&gt;timernum;
+  timer-&gt;freq_timed = freq;
+  timer-&gt;do_timed = do_timed;
+  timer-&gt;opq_timed = opq;
+  serv-&gt;timernum++;
 }
 
 
@@ -848,13 +850,13 @@ bool ttservstart(TTSERV *serv){
   }
   ttservlog(serv, TTLOGSYSTEM, &quot;service started: %d&quot;, getpid());
   bool err = false;
-  TTTIMER timer;
-  timer.alive = false;
-  timer.serv = serv;
-  if(serv-&gt;do_timed){
-    if(pthread_create(&amp;timer.thid, NULL, ttservtimer, &amp;timer) == 0){
-      ttservlog(serv, TTLOGINFO, &quot;timer thread started&quot;);
-      timer.alive = true;
+  for(int i = 0; i &lt; serv-&gt;timernum; i++){
+    TTTIMER *timer = serv-&gt;timers + i;
+    timer-&gt;alive = false;
+    timer-&gt;serv = serv;
+    if(pthread_create(&amp;(timer-&gt;thid), NULL, ttservtimer, timer) == 0){
+      ttservlog(serv, TTLOGINFO, &quot;timer thread %d started&quot;, i + 1);
+      timer-&gt;alive = true;
     } else {
       ttservlog(serv, TTLOGERROR, &quot;pthread_create (ttservtimer) failed&quot;);
       err = true;
@@ -975,6 +977,10 @@ bool ttservstart(TTSERV *serv){
     err = true;
     ttservlog(serv, TTLOGERROR, &quot;pthread_cond_broadcast failed&quot;);
   }
+  if(pthread_cond_broadcast(&amp;serv-&gt;tcnd) != 0){
+    err = true;
+    ttservlog(serv, TTLOGERROR, &quot;pthread_cond_broadcast failed&quot;);
+  }
   usleep(TTWAITWORKER * 1000);
   for(int i = 0; i &lt; thnum; i++){
     if(!reqs[i].alive) continue;
@@ -992,12 +998,14 @@ bool ttservstart(TTSERV *serv){
   if(tclistnum(serv-&gt;queue) &gt; 0)
     ttservlog(serv, TTLOGINFO, &quot;%d requests discarded&quot;, tclistnum(serv-&gt;queue));
   tclistclear(serv-&gt;queue);
-  if(timer.alive){
+  for(int i = 0; i &lt; serv-&gt;timernum; i++){
+    TTTIMER *timer = serv-&gt;timers + i;
+    if(!timer-&gt;alive) continue;
     void *rv;
-    if(pthread_cancel(timer.thid) == 0)
-      ttservlog(serv, TTLOGINFO, &quot;timer thread was canceled&quot;);
-    if(pthread_join(timer.thid, &amp;rv) == 0){
-      ttservlog(serv, TTLOGINFO, &quot;timer thread finished&quot;);
+    if(pthread_cancel(timer-&gt;thid) == 0)
+      ttservlog(serv, TTLOGINFO, &quot;timer thread %d was canceled&quot;, i + 1);
+    if(pthread_join(timer-&gt;thid, &amp;rv) == 0){
+      ttservlog(serv, TTLOGINFO, &quot;timer thread %d finished&quot;, i + 1);
       if(rv &amp;&amp; rv != PTHREAD_CANCELED) err = true;
     } else {
       err = true;
@@ -1062,9 +1070,40 @@ static void *ttservtimer(void *argp){
     ttservlog(serv, TTLOGERROR, &quot;pthread_setcancelstate failed&quot;);
   }
   usleep(100000);
+  double freqi;
+  double freqd = modf(timer-&gt;freq_timed, &amp;freqi);
   while(!serv-&gt;term){
-    serv-&gt;do_timed(serv-&gt;opq_timed);
-    usleep(serv-&gt;freq_timed * 1000000);
+    if(pthread_mutex_lock(&amp;serv-&gt;tmtx) == 0){
+      struct timeval tv;
+      struct timespec ts;
+      if(gettimeofday(&amp;tv, NULL) == 0){
+        ts.tv_sec = tv.tv_sec + (int)freqi;
+        ts.tv_nsec = tv.tv_usec * 1000 + freqd * 1000000000;
+        if(ts.tv_nsec &gt;= 1000000000){
+          ts.tv_nsec -= 1000000000;
+          ts.tv_sec++;
+        }
+      } else {
+        ts.tv_sec = (1ULL &lt;&lt; (sizeof(time_t) * 8 - 1)) - 1;
+        ts.tv_nsec = 0;
+      }
+      int code = pthread_cond_timedwait(&amp;serv-&gt;tcnd, &amp;serv-&gt;tmtx, &amp;ts);
+      if(code == 0 || code == ETIMEDOUT || code == EINTR){
+        if(pthread_mutex_unlock(&amp;serv-&gt;tmtx) != 0){
+          err = true;
+          ttservlog(serv, TTLOGERROR, &quot;pthread_mutex_unlock failed&quot;);
+          break;
+        }
+        if(code != 0) timer-&gt;do_timed(timer-&gt;opq_timed);
+      } else {
+        pthread_mutex_unlock(&amp;serv-&gt;tmtx);
+        err = true;
+        ttservlog(serv, TTLOGERROR, &quot;pthread_cond_timedwait failed&quot;);
+      }
+    } else {
+      err = true;
+      ttservlog(serv, TTLOGERROR, &quot;pthread_mutex_lock failed&quot;);
+    }
   }
   return err ? &quot;error&quot; : NULL;
 }</diff>
      <filename>ttutil.c</filename>
    </modified>
    <modified>
      <diff>@@ -274,10 +274,15 @@ double ttunpackdouble(const char *buf);
 #define TTCMDMISC      0x90              /* ID of misc command */
 #define TTCMDREPL      0xa0              /* ID of repl command */
 
+#define TTTIMERMAX     8                 /* maximum number of timers */
+
 typedef struct _TTTIMER {                /* type of structure for a timer */
   pthread_t thid;                        /* thread ID */
   bool alive;                            /* alive flag */
   struct _TTSERV *serv;                  /* server object */
+  double freq_timed;                     /* frequency of timed handler */
+  void (*do_timed)(void *);              /* call back function for timed handler */
+  void *opq_timed;                       /* opaque pointer for timed handler */
 } TTTIMER;
 
 typedef struct _TTREQ {                  /* type of structure for a server */
@@ -297,14 +302,15 @@ typedef struct _TTSERV {                 /* type of structure for a server */
   TCLIST *queue;                         /* queue of requests */
   pthread_mutex_t qmtx;                  /* mutex for the queue */
   pthread_cond_t qcnd;                   /* condition variable for the queue */
+  pthread_mutex_t tmtx;                  /* mutex for the timer */
+  pthread_cond_t tcnd;                   /* condition variable for the timer */
   int thnum;                             /* number of threads */
   double timeout;                        /* timeout milliseconds of each task */
   bool term;                             /* terminate flag */
   void (*do_log)(int, const char *, void *);  /* call back function for logging */
   void *opq_log;                         /* opaque pointer for logging */
-  double freq_timed;                     /* frequency of timed handler */
-  void (*do_timed)(void *);              /* call back function for timed handler */
-  void *opq_timed;                       /* opaque pointer for timed handler */
+  TTTIMER timers[TTTIMERMAX];            /* timer objects */
+  int timernum;                          /* number of timer objects */
   void (*do_task)(TTSOCK *, void *, TTREQ *req);  /* call back function for task */
   void *opq_task;                        /* opaque pointer for task */
 } TTSERV;
@@ -354,13 +360,13 @@ void ttservtune(TTSERV *serv, int thnum, double timeout);
 void ttservsetloghandler(TTSERV *serv, void (*do_log)(int, const char *, void *), void *opq);
 
 
-/* Set the timed handler of a server object.
+/* Add a timed handler to a server object.
    `serv' specifies the server object.
    `freq' specifies the frequency of execution in seconds.
    `do_timed' specifies the pointer to a function to do with a event.  Its parameter is the
    opaque pointer.
    `opq' specifies the opaque pointer to be passed to the handler.  It can be `NULL'. */
-void ttservsettimedhandler(TTSERV *serv, double freq, void (*do_timed)(void *), void *opq);
+void ttservaddtimedhandler(TTSERV *serv, double freq, void (*do_timed)(void *), void *opq);
 
 
 /* Set the response handler of a server object.
@@ -404,8 +410,8 @@ bool ttserviskilled(TTSERV *serv);
  *************************************************************************************************/
 
 
-#define _TT_VERSION    &quot;1.1.13&quot;
-#define _TT_LIBVER     204
+#define _TT_VERSION    &quot;1.1.14&quot;
+#define _TT_LIBVER     205
 #define _TT_PROTVER    &quot;0.9&quot;
 
 </diff>
      <filename>ttutil.h</filename>
    </modified>
  </modified>
  <removed type="array"/>
  <parents type="array">
    <parent>
      <id>cc296d2e4f68ceb40626252ac265d296a2d3b19c</id>
    </parent>
  </parents>
  <author>
    <name>Bob Ippolito</name>
    <email>bob@redivi.com</email>
  </author>
  <url>http://github.com/etrepum/tokyo-tyrant/commit/9d36d5d6e027e4b054e180873155a590519e587d</url>
  <id>9d36d5d6e027e4b054e180873155a590519e587d</id>
  <committed-date>2009-02-08T20:18:50-08:00</committed-date>
  <authored-date>2009-02-08T20:18:50-08:00</authored-date>
  <message>sync with tokyotyrant-1.1.14.tar.gz</message>
  <tree>e4a695a157e28a3a3d13fd737f864f670ba6ed61</tree>
  <committer>
    <name>Bob Ippolito</name>
    <email>bob@redivi.com</email>
  </committer>
</commit>
