changeset 673:a8effb6159c2

Variable timeouts and client keep-alive
author Adam Chlipala <adamc@hcoop.net>
date Tue, 24 Mar 2009 15:35:46 -0400
parents df6eb58de040
children fab5998b840e
files lib/js/urweb.js src/c/urweb.c src/cjr_print.sig src/cjr_print.sml src/compiler.sig src/compiler.sml src/demo.sml tests/channel.urp
diffstat 8 files changed, 76 insertions(+), 30 deletions(-) [+]
line wrap: on
line diff
--- a/lib/js/urweb.js	Tue Mar 24 15:05:28 2009 -0400
+++ b/lib/js/urweb.js	Tue Mar 24 15:35:46 2009 -0400
@@ -141,6 +141,7 @@
 var client_id = 0;
 var client_pass = 0;
 var url_prefix = "/";
+var timeout = 60;
 
 function getXHR(uri)
 {
@@ -234,8 +235,18 @@
 function listener() {
   var uri = path_join(url_prefix, ".msgs");
   var xhr = getXHR();
-  var orsc = function() {
+  var tid, orsc, onTimeout;
+
+  var connect = function () {
+    xhr.onreadystatechange = orsc;
+    tid = window.setTimeout(onTimeout, timeout * 500);
+    requestUri(xhr, uri);
+  }
+
+  orsc = function() {
     if (xhr.readyState == 4) {
+      window.clearTimeout(tid);
+
       var isok = false;
 
       try {
@@ -271,8 +282,7 @@
           }
         }
 
-        xhr.onreadystatechange = orsc;
-        requestUri(xhr, uri);
+        connect();
       }
       else {
         try {
@@ -282,8 +292,12 @@
     }
   };
 
-  xhr.onreadystatechange = orsc;
-  requestUri(xhr, uri);
+  onTimeout = function() {
+    xhr.abort();
+    connect();
+  };
+
+  connect();
 }
 
 function rv(chn, parse, k) {
--- a/src/c/urweb.c	Tue Mar 24 15:05:28 2009 -0400
+++ b/src/c/urweb.c	Tue Mar 24 15:35:46 2009 -0400
@@ -220,10 +220,8 @@
   }
 
   if (c->data.used.sock != -1) {
-    pthread_mutex_unlock(&c->data.used.lock);
-    close(sock);
-    fprintf(stderr, "Duplicate client connection (%d)\n", (int)id);
-    return;
+    close(c->data.used.sock);
+    c->data.used.sock = -1;
   }
 
   c->data.used.last_contact = time(NULL);
@@ -288,11 +286,13 @@
   }
 }
 
-void uw_prune_clients(time_t timeout) {
+extern int uw_timeout;
+
+void uw_prune_clients() {
   size_t i;
   time_t cutoff;
 
-  cutoff = time(NULL) - timeout;
+  cutoff = time(NULL) - uw_timeout;
 
   pthread_mutex_lock(&clients_mutex);
 
@@ -507,6 +507,8 @@
   size_t n_deltas;
   channel_delta *deltas;
 
+  int timeout;
+
   char error_message[ERROR_BUF_LEN];
 };
 
@@ -541,6 +543,8 @@
   ctx->n_deltas = 0;
   ctx->deltas = malloc(0);
 
+  ctx->timeout = uw_timeout;
+
   return ctx;
 }
 
@@ -834,10 +838,15 @@
     int pass;
     client *c = uw_new_client(&pass);
 
-    char *r = uw_malloc(ctx, strlen(ctx->script_header) + 56 + 2 * INTS_MAX + buf_used(&ctx->script)
+    char *r = uw_malloc(ctx, strlen(ctx->script_header) + 65 + 3 * INTS_MAX + buf_used(&ctx->script)
                         + strlen(ctx->url_prefix));
-    sprintf(r, "%s<script>client_id=%d;client_pass=%d;url_prefix=\"%s\";%s</script>",
-            ctx->script_header, (int)c->id, c->data.used.pass, ctx->url_prefix, ctx->script.start);
+    sprintf(r, "%s<script>client_id=%d;client_pass=%d;url_prefix=\"%s\";timeout=%d;%s</script>",
+            ctx->script_header,
+            (int)c->id,
+            c->data.used.pass,
+            ctx->url_prefix,
+            ctx->timeout,
+            ctx->script.start);
     return r;
   }
 }
--- a/src/cjr_print.sig	Tue Mar 24 15:05:28 2009 -0400
+++ b/src/cjr_print.sig	Tue Mar 24 15:35:46 2009 -0400
@@ -36,4 +36,6 @@
     val p_sql : CjrEnv.env -> Cjr.file Print.printer
 
     val debug : bool ref
+
+    val timeout : int ref
 end
--- a/src/cjr_print.sml	Tue Mar 24 15:05:28 2009 -0400
+++ b/src/cjr_print.sml	Tue Mar 24 15:35:46 2009 -0400
@@ -1124,6 +1124,8 @@
         urlify' IS.empty 0 t
     end
 
+val timeout = ref 0
+
 fun p_exp' par env (e, loc) =
     case e of
         EPrim p => Prim.p_t_GCC p
@@ -2688,6 +2690,10 @@
              string (Int.toString (SM.foldl Int.max 0 fnums + 1)),
              string ";",
              newline,
+             string "int uw_timeout = ",
+             string (Int.toString (!timeout)),
+             string ";",
+             newline,
              newline,
              string "int uw_input_num(char *name) {",
              newline,
--- a/src/compiler.sig	Tue Mar 24 15:05:28 2009 -0400
+++ b/src/compiler.sig	Tue Mar 24 15:35:46 2009 -0400
@@ -36,7 +36,8 @@
          exe : string,
          sql : string option,
          debug : bool,
-         profile : bool
+         profile : bool,
+         timeout : int
     }
     val compile : string -> unit
     val compileC : {cname : string, oname : string, ename : string, libs : string, profile : bool} -> unit
--- a/src/compiler.sml	Tue Mar 24 15:05:28 2009 -0400
+++ b/src/compiler.sml	Tue Mar 24 15:35:46 2009 -0400
@@ -42,7 +42,8 @@
      exe : string,
      sql : string option,
      debug : bool,
-     profile : bool
+     profile : bool,
+     timeout : int
 }
 
 type ('src, 'dst) phase = {
@@ -200,7 +201,7 @@
               handle LrParser.ParseError => [],
      print = SourcePrint.p_file}    
 
-fun p_job {prefix, database, exe, sql, sources, debug, profile} =
+fun p_job {prefix, database, exe, sql, sources, debug, profile, timeout} =
     let
         open Print.PD
         open Print
@@ -223,6 +224,10 @@
              case sql of
                  NONE => string "No SQL file."
                | SOME sql => string ("SQL fle: " ^ sql),
+             newline,
+             string "Timeout: ",
+             string (Int.toString timeout),
+             newline,
              string "Sources:",
              p_list string sources,
              newline]
@@ -265,7 +270,7 @@
                               readSources acc
                           end
 
-                  fun finish (prefix, database, exe, sql, debug, profile, sources) =
+                  fun finish (prefix, database, exe, sql, debug, profile, timeout, sources) =
                       {prefix = Option.getOpt (prefix, "/"),
                        database = database,
                        exe = Option.getOpt (exe, OS.Path.joinBaseExt {base = OS.Path.base filename,
@@ -273,12 +278,13 @@
                        sql = sql,
                        debug = debug,
                        profile = profile,
+                       timeout = Option.getOpt (timeout, 60),
                        sources = sources}
 
-                  fun read (prefix, database, exe, sql, debug, profile) =
+                  fun read (prefix, database, exe, sql, debug, profile, timeout) =
                       case TextIO.inputLine inf of
-                          NONE => finish (prefix, database, exe, sql, debug, profile, [])
-                        | SOME "\n" => finish (prefix, database, exe, sql, debug, profile, readSources [])
+                          NONE => finish (prefix, database, exe, sql, debug, profile, timeout, [])
+                        | SOME "\n" => finish (prefix, database, exe, sql, debug, profile, timeout, readSources [])
                         | SOME line =>
                           let
                               val (cmd, arg) = Substring.splitl (fn x => not (Char.isSpace x)) (Substring.full line)
@@ -290,32 +296,38 @@
                                   (case prefix of
                                        NONE => ()
                                      | SOME _ => ErrorMsg.error "Duplicate 'prefix' directive";
-                                   read (SOME arg, database, exe, sql, debug, profile))
+                                   read (SOME arg, database, exe, sql, debug, profile, timeout))
                                 | "database" =>
                                   (case database of
                                        NONE => ()
                                      | SOME _ => ErrorMsg.error "Duplicate 'database' directive";
-                                   read (prefix, SOME arg, exe, sql, debug, profile))
+                                   read (prefix, SOME arg, exe, sql, debug, profile, timeout))
                                 | "exe" =>
                                   (case exe of
                                        NONE => ()
                                      | SOME _ => ErrorMsg.error "Duplicate 'exe' directive";
-                                   read (prefix, database, SOME (relify arg), sql, debug, profile))
+                                   read (prefix, database, SOME (relify arg), sql, debug, profile, timeout))
                                 | "sql" =>
                                   (case sql of
                                        NONE => ()
                                      | SOME _ => ErrorMsg.error "Duplicate 'sql' directive";
-                                   read (prefix, database, exe, SOME (relify arg), debug, profile))
-                                | "debug" => read (prefix, database, exe, sql, true, profile)
-                                | "profile" => read (prefix, database, exe, sql, debug, true)
+                                   read (prefix, database, exe, SOME (relify arg), debug, profile, timeout))
+                                | "debug" => read (prefix, database, exe, sql, true, profile, timeout)
+                                | "profile" => read (prefix, database, exe, sql, debug, true, timeout)
+                                | "timeout" =>
+                                  (case timeout of
+                                       NONE => ()
+                                     | SOME _ => ErrorMsg.error "Duplicate 'timeout' directive";
+                                   read (prefix, database, exe, sql, debug, profile, SOME (valOf (Int.fromString arg))))
                                 | _ => (ErrorMsg.error ("Unrecognized command '" ^ cmd ^ "'");
-                                        read (prefix, database, exe, sql, debug, profile))
+                                        read (prefix, database, exe, sql, debug, profile, timeout))
                           end
 
-                  val job = read (NONE, NONE, NONE, NONE, false, false)
+                  val job = read (NONE, NONE, NONE, NONE, false, false, NONE)
               in
                   TextIO.closeIn inf;
                   Monoize.urlPrefix := #prefix job;
+                  CjrPrint.timeout := #timeout job;
                   job
               end,
     print = p_job
@@ -598,7 +610,7 @@
         else if not (OS.Process.isSuccess (OS.Process.system link)) then
             print "C linking failed\n"
         else
-            print "Success\n"
+            ()
     end
 
 fun compile job =
--- a/src/demo.sml	Tue Mar 24 15:05:28 2009 -0400
+++ b/src/demo.sml	Tue Mar 24 15:35:46 2009 -0400
@@ -93,6 +93,7 @@
             sql = SOME (OS.Path.joinDirFile {dir = dirname,
                                              file = "demo.sql"}),
             debug = false,
+            timeout = Int.max (#timeout combined, #timeout urp),
             profile = false
         }
 
--- a/tests/channel.urp	Tue Mar 24 15:05:28 2009 -0400
+++ b/tests/channel.urp	Tue Mar 24 15:35:46 2009 -0400
@@ -1,3 +1,4 @@
 debug
+timeout 10
 
 channel