diff src/c/urweb.c @ 672:df6eb58de040

Fix some AJAX annoyances
author Adam Chlipala <adamc@hcoop.net>
date Tue, 24 Mar 2009 15:05:28 -0400
parents 729e65db2e2f
children a8effb6159c2
line wrap: on
line diff
--- a/src/c/urweb.c	Tue Mar 24 14:44:45 2009 -0400
+++ b/src/c/urweb.c	Tue Mar 24 15:05:28 2009 -0400
@@ -77,16 +77,22 @@
 }
 
 static void buf_append(buf *b, const char *s, size_t len) {
-  buf_check(b, len);
+  buf_check(b, len+1);
   memcpy(b->front, s, len);
   b->front += len;
+  *b->front = 0;
 }
 
 
-// Persistent client state
+// Persistent state types
 
 typedef enum { UNUSED, USED } usage;
 
+typedef struct channel_list {
+  struct channel *data;
+  struct channel_list *next;
+} channel_list;
+
 typedef struct client {
   size_t id;
   usage mode;
@@ -99,10 +105,32 @@
       int sock;
       time_t last_contact;
       unsigned refcount;
+      channel_list *channels;
     } used;
   } data;
 } client;
 
+typedef struct client_list {
+  client *data;
+  struct client_list *next;
+} client_list;
+
+typedef struct channel {
+  size_t id;
+  usage mode;
+  union {
+    struct channel *next;
+    struct {
+      pthread_mutex_t lock;
+      client_list *clients;
+      unsigned refcount;
+    } used;
+  } data;
+} channel;
+
+
+// Persistent client state
+
 static client **clients, *clients_free;
 static size_t n_clients;
 
@@ -130,8 +158,9 @@
   c->data.used.pass = rand();
   c->data.used.sock = -1;
   c->data.used.last_contact = time(NULL);
+  buf_init(&c->data.used.msgs, 0);
   c->data.used.refcount = 0;
-  buf_init(&c->data.used.msgs, 0);
+  c->data.used.channels = NULL;
 
   pthread_mutex_unlock(&clients_mutex);
 
@@ -210,17 +239,53 @@
   pthread_mutex_unlock(&c->data.used.lock);
 }
 
+
 static void uw_free_client(client *c) {
+  channel_list *chs;
+
   printf("Freeing client %d\n", c->id);
 
-  if (c->mode == USED && c->data.used.sock != -1)
-    close(c->data.used.sock);
+  if (c->mode == USED) {
+    pthread_mutex_lock(&c->data.used.lock);
 
-  pthread_mutex_destroy(&c->data.used.lock);
-  buf_free(&c->data.used.msgs);
-  c->mode = UNUSED;
-  c->data.next = clients_free;
-  clients_free = c;
+    for (chs = c->data.used.channels; chs; ) {
+      client_list *prev, *cs;
+      
+      channel *ch = chs->data;
+      channel_list *tmp = chs->next;
+      free(chs);
+      chs = tmp;
+
+      pthread_mutex_lock(&ch->data.used.lock);
+      for (prev = NULL, cs = ch->data.used.clients; cs; ) {
+        if (cs->data == c) {
+          client_list *tmp = cs->next;
+          free(cs);
+          cs = tmp;
+          if (prev)
+            prev->next = cs;
+          else
+            ch->data.used.clients = cs;
+        }
+        else {
+          prev = cs;
+          cs = cs->next;
+        }
+      }
+      pthread_mutex_unlock(&ch->data.used.lock);
+    }
+
+    if (c->data.used.sock != -1)
+      close(c->data.used.sock);
+
+    pthread_mutex_unlock(&c->data.used.lock);
+    pthread_mutex_destroy(&c->data.used.lock);
+    buf_free(&c->data.used.msgs);
+    c->mode = UNUSED;
+
+    c->data.next = clients_free;
+    clients_free = c;
+  }
 }
 
 void uw_prune_clients(time_t timeout) {
@@ -243,23 +308,6 @@
 
 // Persistent channel state
 
-typedef struct client_list {
-  client *data;
-  struct client_list *next;
-} client_list;
-
-typedef struct channel {
-  size_t id;
-  usage mode;
-  union {
-    struct channel *next;
-    struct {
-      pthread_mutex_t lock;
-      client_list *clients;
-      unsigned refcount;
-    } used;
-  } data;
-} channel;
 
 static channel **channels, *channels_free;
 static size_t n_channels;