malloc -> new
[oweals/gnunet.git] / src / statistics / statistics_api.c
index f4f91538a1509df6964fc0b7def7c496b3b98be0..04e85a07e95323b65d2026990c05d66c836e306b 100644 (file)
@@ -4,7 +4,7 @@
 
      GNUnet is free software; you can redistribute it and/or modify
      it under the terms of the GNU General Public License as published
 
      GNUnet is free software; you can redistribute it and/or modify
      it under the terms of the GNU General Public License as published
-     by the Free Software Foundation; either version 2, or (at your
+     by the Free Software Foundation; either version 3, or (at your
      option) any later version.
 
      GNUnet is distributed in the hope that it will be useful, but
      option) any later version.
 
      GNUnet is distributed in the hope that it will be useful, but
  * @author Christian Grothoff
  */
 #include "platform.h"
  * @author Christian Grothoff
  */
 #include "platform.h"
-#include "gnunet_client_lib.h"
+#include "gnunet_util_lib.h"
 #include "gnunet_constants.h"
 #include "gnunet_constants.h"
-#include "gnunet_container_lib.h"
 #include "gnunet_protocols.h"
 #include "gnunet_protocols.h"
-#include "gnunet_server_lib.h"
 #include "gnunet_statistics_service.h"
 #include "gnunet_statistics_service.h"
-#include "gnunet_strings_lib.h"
 #include "statistics.h"
 
 /**
 #include "statistics.h"
 
 /**
@@ -235,6 +232,16 @@ struct GNUNET_STATISTICS_Handle
    */
   struct GNUNET_TIME_Relative backoff;
 
    */
   struct GNUNET_TIME_Relative backoff;
 
+  /**
+   * Maximum heap size observed so far (if available).
+   */
+  uint64_t peak_heap_size;
+
+  /**
+   * Maximum resident set side observed so far (if available).
+   */
+  uint64_t peak_rss;
+
   /**
    * Size of the 'watches' array.
    */
   /**
    * Size of the 'watches' array.
    */
@@ -254,6 +261,51 @@ struct GNUNET_STATISTICS_Handle
 };
 
 
 };
 
 
+/**
+ * Obtain statistics about this process's memory consumption and
+ * report those as well (if they changed).
+ */
+static void
+update_memory_statistics (struct GNUNET_STATISTICS_Handle *h)
+{
+#if ENABLE_HEAP_STATISTICS
+  uint64_t current_heap_size = 0;
+  uint64_t current_rss = 0;
+
+  if (GNUNET_NO != h->do_destroy)
+    return;
+#if HAVE_MALLINFO
+  {
+    struct mallinfo mi;
+
+    mi = mallinfo();
+    current_heap_size = mi.uordblks + mi.fordblks;
+  }
+#endif
+#if HAVE_GETRUSAGE
+  {
+    struct rusage ru;
+
+    if (0 == getrusage (RUSAGE_SELF, &ru))
+    {
+      current_rss = 1024LL * ru.ru_maxrss;
+    }
+  }
+#endif
+  if (current_heap_size > h->peak_heap_size)
+  {
+    h->peak_heap_size = current_heap_size;
+    GNUNET_STATISTICS_set (h, "# peak heap size", current_heap_size, GNUNET_NO);
+  }
+  if (current_rss > h->peak_rss)
+  {
+    h->peak_rss = current_rss;
+    GNUNET_STATISTICS_set (h, "# peak resident set size", current_rss, GNUNET_NO);
+  }
+#endif
+}
+
+
 /**
  * Schedule the next action to be performed.
  *
 /**
  * Schedule the next action to be performed.
  *
@@ -280,7 +332,9 @@ schedule_watch_request (struct GNUNET_STATISTICS_Handle *h,
   size_t nlen;
   size_t nsize;
 
   size_t nlen;
   size_t nsize;
 
-  GNUNET_assert (h != NULL);
+  GNUNET_assert (NULL != h);
+  GNUNET_assert (NULL != watch);
+
   slen = strlen (watch->subsystem) + 1;
   nlen = strlen (watch->name) + 1;
   nsize = sizeof (struct GNUNET_MessageHeader) + slen + nlen;
   slen = strlen (watch->subsystem) + 1;
   nlen = strlen (watch->name) + 1;
   nsize = sizeof (struct GNUNET_MessageHeader) + slen + nlen;
@@ -289,7 +343,7 @@ schedule_watch_request (struct GNUNET_STATISTICS_Handle *h,
     GNUNET_break (0);
     return;
   }
     GNUNET_break (0);
     return;
   }
-  ai = GNUNET_malloc (sizeof (struct GNUNET_STATISTICS_GetHandle));
+  ai = GNUNET_new (struct GNUNET_STATISTICS_GetHandle);
   ai->sh = h;
   ai->subsystem = GNUNET_strdup (watch->subsystem);
   ai->name = GNUNET_strdup (watch->name);
   ai->sh = h;
   ai->subsystem = GNUNET_strdup (watch->subsystem);
   ai->name = GNUNET_strdup (watch->name);
@@ -327,25 +381,26 @@ static void
 do_disconnect (struct GNUNET_STATISTICS_Handle *h)
 {
   struct GNUNET_STATISTICS_GetHandle *c;
 do_disconnect (struct GNUNET_STATISTICS_Handle *h)
 {
   struct GNUNET_STATISTICS_GetHandle *c;
-  
+
   if (NULL != h->th)
   {
     GNUNET_CLIENT_notify_transmit_ready_cancel (h->th);
     h->th = NULL;
   if (NULL != h->th)
   {
     GNUNET_CLIENT_notify_transmit_ready_cancel (h->th);
     h->th = NULL;
-  } 
-  if (NULL != h->client)
-  {
-    GNUNET_CLIENT_disconnect (h->client, GNUNET_NO);
-    h->client = NULL;
   }
   h->receiving = GNUNET_NO;
   if (NULL != (c = h->current))
   {
     h->current = NULL;
   }
   h->receiving = GNUNET_NO;
   if (NULL != (c = h->current))
   {
     h->current = NULL;
-    if (c->cont != NULL)
+    if ( (NULL != c->cont) &&
+        (GNUNET_YES != c->aborted) )
       c->cont (c->cls, GNUNET_SYSERR);
     free_action_item (c);
   }
       c->cont (c->cls, GNUNET_SYSERR);
     free_action_item (c);
   }
+  if (NULL != h->client)
+  {
+    GNUNET_CLIENT_disconnect (h->client);
+    h->client = NULL;
+  }
 }
 
 
 }
 
 
@@ -353,7 +408,7 @@ do_disconnect (struct GNUNET_STATISTICS_Handle *h)
  * Try to (re)connect to the statistics service.
  *
  * @param h statistics handle to reconnect
  * Try to (re)connect to the statistics service.
  *
  * @param h statistics handle to reconnect
- * @return GNUNET_YES on success, GNUNET_NO on failure.
+ * @return #GNUNET_YES on success, #GNUNET_NO on failure.
  */
 static int
 try_connect (struct GNUNET_STATISTICS_Handle *h)
  */
 static int
 try_connect (struct GNUNET_STATISTICS_Handle *h)
@@ -362,14 +417,14 @@ try_connect (struct GNUNET_STATISTICS_Handle *h)
   struct GNUNET_STATISTICS_GetHandle *gn;
   unsigned int i;
 
   struct GNUNET_STATISTICS_GetHandle *gn;
   unsigned int i;
 
-  if (h->backoff_task != GNUNET_SCHEDULER_NO_TASK)
+  if (GNUNET_SCHEDULER_NO_TASK != h->backoff_task)
     return GNUNET_NO;
     return GNUNET_NO;
-  if (h->client != NULL)
+  if (NULL != h->client)
     return GNUNET_YES;
     return GNUNET_YES;
-  h->client = GNUNET_CLIENT_connect ("statistics", h->cfg);  
-  if (h->client != NULL)
+  h->client = GNUNET_CLIENT_connect ("statistics", h->cfg);
+  if (NULL != h->client)
   {
   {
-    gn = h->action_head; 
+    gn = h->action_head;
     while (NULL != (gh = gn))
     {
       gn = gh->next;
     while (NULL != (gh = gn))
     {
       gn = gh->next;
@@ -378,17 +433,18 @@ try_connect (struct GNUNET_STATISTICS_Handle *h)
        GNUNET_CONTAINER_DLL_remove (h->action_head,
                                     h->action_tail,
                                     gh);
        GNUNET_CONTAINER_DLL_remove (h->action_head,
                                     h->action_tail,
                                     gh);
-       free_action_item (gh);  
+       free_action_item (gh);
       }
     }
     for (i = 0; i < h->watches_size; i++)
       }
     }
     for (i = 0; i < h->watches_size; i++)
-      schedule_watch_request (h, h->watches[i]);
+    {
+      if (NULL != h->watches[i])
+        schedule_watch_request (h, h->watches[i]);
+    }
     return GNUNET_YES;
   }
     return GNUNET_YES;
   }
-#if DEBUG_STATISTICS
   LOG (GNUNET_ERROR_TYPE_DEBUG,
   LOG (GNUNET_ERROR_TYPE_DEBUG,
-       _("Failed to connect to statistics service!\n"));
-#endif
+       "Failed to connect to statistics service!\n");
   return GNUNET_NO;
 }
 
   return GNUNET_NO;
 }
 
@@ -409,6 +465,22 @@ reconnect_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
 }
 
 
 }
 
 
+/**
+ * Task used by 'reconnect_later' to shutdown the handle
+ *
+ * @param cls the statistics handle
+ * @param tc scheduler context
+ */
+static void
+do_destroy (void *cls,
+              const struct GNUNET_SCHEDULER_TaskContext *tc)
+{
+  struct GNUNET_STATISTICS_Handle *h = cls;
+
+  GNUNET_STATISTICS_destroy (h, GNUNET_NO);
+}
+
+
 /**
  * Reconnect at a later time, respecting back-off.
  *
 /**
  * Reconnect at a later time, respecting back-off.
  *
@@ -417,12 +489,32 @@ reconnect_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
 static void
 reconnect_later (struct GNUNET_STATISTICS_Handle *h)
 {
 static void
 reconnect_later (struct GNUNET_STATISTICS_Handle *h)
 {
+  int loss;
+  struct GNUNET_STATISTICS_GetHandle *gh;
+
   GNUNET_assert (GNUNET_SCHEDULER_NO_TASK == h->backoff_task);
   GNUNET_assert (GNUNET_SCHEDULER_NO_TASK == h->backoff_task);
+  if (GNUNET_YES == h->do_destroy)
+  {
+    /* So we are shutting down and the service is not reachable.
+     * Chances are that it's down for good and we are not going to connect to
+     * it anymore.
+     * Give up and don't sync the rest of the data.
+     */
+    loss = GNUNET_NO;
+    for (gh = h->action_head; NULL != gh; gh = gh->next)
+      if ( (gh->make_persistent) && (ACTION_SET == gh->type) )
+       loss = GNUNET_YES;
+    if (GNUNET_YES == loss)
+      GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                 _("Could not save some persistent statistics\n"));
+    h->do_destroy = GNUNET_NO;
+    GNUNET_SCHEDULER_add_continuation (&do_destroy, h,
+                                      GNUNET_SCHEDULER_REASON_PREREQ_DONE);
+    return;
+  }
   h->backoff_task =
     GNUNET_SCHEDULER_add_delayed (h->backoff, &reconnect_task, h);
   h->backoff_task =
     GNUNET_SCHEDULER_add_delayed (h->backoff, &reconnect_task, h);
-  h->backoff = GNUNET_TIME_relative_multiply (h->backoff, 2);
-  h->backoff =
-    GNUNET_TIME_relative_min (h->backoff, GNUNET_CONSTANTS_SERVICE_TIMEOUT);
+  h->backoff = GNUNET_TIME_STD_BACKOFF (h->backoff);
 }
 
 
 }
 
 
@@ -444,9 +536,7 @@ process_statistics_value_message (struct GNUNET_STATISTICS_Handle *h,
 
   if (h->current->aborted)
   {
 
   if (h->current->aborted)
   {
-#if DEBUG_STATISTICS
     LOG (GNUNET_ERROR_TYPE_DEBUG, "Iteration was aborted, ignoring VALUE\n");
     LOG (GNUNET_ERROR_TYPE_DEBUG, "Iteration was aborted, ignoring VALUE\n");
-#endif
     return GNUNET_OK;           /* don't bother */
   }
   size = ntohs (msg->size);
     return GNUNET_OK;           /* don't bother */
   }
   size = ntohs (msg->size);
@@ -464,25 +554,19 @@ process_statistics_value_message (struct GNUNET_STATISTICS_Handle *h,
     GNUNET_break (0);
     return GNUNET_SYSERR;
   }
     GNUNET_break (0);
     return GNUNET_SYSERR;
   }
-#if DEBUG_STATISTICS
   LOG (GNUNET_ERROR_TYPE_DEBUG, "Received valid statistic on `%s:%s': %llu\n",
        service, name, GNUNET_ntohll (smsg->value));
   LOG (GNUNET_ERROR_TYPE_DEBUG, "Received valid statistic on `%s:%s': %llu\n",
        service, name, GNUNET_ntohll (smsg->value));
-#endif
   if (GNUNET_OK !=
       h->current->proc (h->current->cls, service, name,
                         GNUNET_ntohll (smsg->value),
                         0 !=
                         (ntohl (smsg->uid) & GNUNET_STATISTICS_PERSIST_BIT)))
   {
   if (GNUNET_OK !=
       h->current->proc (h->current->cls, service, name,
                         GNUNET_ntohll (smsg->value),
                         0 !=
                         (ntohl (smsg->uid) & GNUNET_STATISTICS_PERSIST_BIT)))
   {
-#if DEBUG_STATISTICS
     LOG (GNUNET_ERROR_TYPE_DEBUG,
          "Processing of remaining statistics aborted by client.\n");
     LOG (GNUNET_ERROR_TYPE_DEBUG,
          "Processing of remaining statistics aborted by client.\n");
-#endif
     h->current->aborted = GNUNET_YES;
   }
     h->current->aborted = GNUNET_YES;
   }
-#if DEBUG_STATISTICS
   LOG (GNUNET_ERROR_TYPE_DEBUG, "VALUE processed successfully\n");
   LOG (GNUNET_ERROR_TYPE_DEBUG, "VALUE processed successfully\n");
-#endif
   return GNUNET_OK;
 }
 
   return GNUNET_OK;
 }
 
@@ -492,7 +576,8 @@ process_statistics_value_message (struct GNUNET_STATISTICS_Handle *h,
  *
  * @param h statistics handle
  * @param msg the watch value message
  *
  * @param h statistics handle
  * @param msg the watch value message
- * @return GNUNET_OK if the message was well-formed, GNUNET_SYSERR if not
+ * @return GNUNET_OK if the message was well-formed, GNUNET_SYSERR if not,
+ *         GNUNET_NO if this watch has been cancelled
  */
 static int
 process_watch_value (struct GNUNET_STATISTICS_Handle *h,
  */
 static int
 process_watch_value (struct GNUNET_STATISTICS_Handle *h,
@@ -516,6 +601,8 @@ process_watch_value (struct GNUNET_STATISTICS_Handle *h,
     return GNUNET_SYSERR;
   }
   w = h->watches[wid];
     return GNUNET_SYSERR;
   }
   w = h->watches[wid];
+  if (NULL == w)
+    return GNUNET_NO;
   (void) w->proc (w->proc_cls, w->subsystem, w->name,
                   GNUNET_ntohll (wvm->value),
                   0 != (ntohl (wvm->flags) & GNUNET_STATISTICS_PERSIST_BIT));
   (void) w->proc (w->proc_cls, w->subsystem, w->name,
                   GNUNET_ntohll (wvm->value),
                   0 != (ntohl (wvm->flags) & GNUNET_STATISTICS_PERSIST_BIT));
@@ -523,6 +610,16 @@ process_watch_value (struct GNUNET_STATISTICS_Handle *h,
 }
 
 
 }
 
 
+static void
+destroy_task (void *cls,
+             const struct GNUNET_SCHEDULER_TaskContext *tc)
+{
+  struct GNUNET_STATISTICS_Handle *h = cls;
+
+  GNUNET_STATISTICS_destroy (h, GNUNET_NO);
+}
+
+
 /**
  * Function called with messages from stats service.
  *
 /**
  * Function called with messages from stats service.
  *
@@ -534,24 +631,33 @@ receive_stats (void *cls, const struct GNUNET_MessageHeader *msg)
 {
   struct GNUNET_STATISTICS_Handle *h = cls;
   struct GNUNET_STATISTICS_GetHandle *c;
 {
   struct GNUNET_STATISTICS_Handle *h = cls;
   struct GNUNET_STATISTICS_GetHandle *c;
+  int ret;
 
 
-  if (msg == NULL)
+  if (NULL == msg)
   {
   {
-#if DEBUG_STATISTICS
     LOG (GNUNET_ERROR_TYPE_DEBUG | GNUNET_ERROR_TYPE_BULK,
          "Error receiving statistics from service, is the service running?\n");
     LOG (GNUNET_ERROR_TYPE_DEBUG | GNUNET_ERROR_TYPE_BULK,
          "Error receiving statistics from service, is the service running?\n");
-#endif
     do_disconnect (h);
     reconnect_later (h);
     return;
   }
   switch (ntohs (msg->type))
   {
     do_disconnect (h);
     reconnect_later (h);
     return;
   }
   switch (ntohs (msg->type))
   {
+  case GNUNET_MESSAGE_TYPE_TEST:
+    if (GNUNET_SYSERR != h->do_destroy)
+    {
+      /* not in shutdown, why do we get 'TEST'? */
+      GNUNET_break (0);
+      do_disconnect (h);
+      reconnect_later (h);
+      return;
+    }
+    h->do_destroy = GNUNET_NO;
+    GNUNET_SCHEDULER_add_continuation (&destroy_task, h,
+                                      GNUNET_SCHEDULER_REASON_PREREQ_DONE);
+    break;
   case GNUNET_MESSAGE_TYPE_STATISTICS_END:
   case GNUNET_MESSAGE_TYPE_STATISTICS_END:
-#if DEBUG_STATISTICS
     LOG (GNUNET_ERROR_TYPE_DEBUG, "Received end of statistics marker\n");
     LOG (GNUNET_ERROR_TYPE_DEBUG, "Received end of statistics marker\n");
-#endif
     if (NULL == (c = h->current))
     {
       GNUNET_break (0);
     if (NULL == (c = h->current))
     {
       GNUNET_break (0);
@@ -568,10 +674,10 @@ receive_stats (void *cls, const struct GNUNET_MessageHeader *msg)
     else
     {
       h->receiving = GNUNET_NO;
     else
     {
       h->receiving = GNUNET_NO;
-    }    
+    }
     h->current = NULL;
     schedule_action (h);
     h->current = NULL;
     schedule_action (h);
-    if (c->cont != NULL)
+    if (NULL != c->cont)
       c->cont (c->cls, GNUNET_OK);
     free_action_item (c);
     return;
       c->cont (c->cls, GNUNET_OK);
     free_action_item (c);
     return;
@@ -580,23 +686,23 @@ receive_stats (void *cls, const struct GNUNET_MessageHeader *msg)
     {
       do_disconnect (h);
       reconnect_later (h);
     {
       do_disconnect (h);
       reconnect_later (h);
-      return;     
+      return;
     }
     /* finally, look for more! */
     }
     /* finally, look for more! */
-#if DEBUG_STATISTICS
     LOG (GNUNET_ERROR_TYPE_DEBUG,
         "Processing VALUE done, now reading more\n");
     LOG (GNUNET_ERROR_TYPE_DEBUG,
         "Processing VALUE done, now reading more\n");
-#endif
     GNUNET_CLIENT_receive (h->client, &receive_stats, h,
                           GNUNET_TIME_absolute_get_remaining (h->
                                                               current->timeout));
     h->backoff = GNUNET_TIME_UNIT_MILLISECONDS;
     return;
   case GNUNET_MESSAGE_TYPE_STATISTICS_WATCH_VALUE:
     GNUNET_CLIENT_receive (h->client, &receive_stats, h,
                           GNUNET_TIME_absolute_get_remaining (h->
                                                               current->timeout));
     h->backoff = GNUNET_TIME_UNIT_MILLISECONDS;
     return;
   case GNUNET_MESSAGE_TYPE_STATISTICS_WATCH_VALUE:
-    if (GNUNET_OK != 
-       process_watch_value (h, msg))
+    if (GNUNET_OK !=
+       (ret = process_watch_value (h, msg)))
     {
       do_disconnect (h);
     {
       do_disconnect (h);
+      if (GNUNET_NO == ret)
+       h->backoff = GNUNET_TIME_UNIT_MILLISECONDS;
       reconnect_later (h);
       return;
     }
       reconnect_later (h);
       return;
     }
@@ -604,7 +710,7 @@ receive_stats (void *cls, const struct GNUNET_MessageHeader *msg)
     GNUNET_assert (h->watches_size > 0);
     GNUNET_CLIENT_receive (h->client, &receive_stats, h,
                           GNUNET_TIME_UNIT_FOREVER_REL);
     GNUNET_assert (h->watches_size > 0);
     GNUNET_CLIENT_receive (h->client, &receive_stats, h,
                           GNUNET_TIME_UNIT_FOREVER_REL);
-    return;    
+    return;
   default:
     GNUNET_break (0);
     do_disconnect (h);
   default:
     GNUNET_break (0);
     do_disconnect (h);
@@ -633,13 +739,11 @@ transmit_get (struct GNUNET_STATISTICS_Handle *handle, size_t size, void *buf)
   uint16_t msize;
 
   GNUNET_assert (NULL != (c = handle->current));
   uint16_t msize;
 
   GNUNET_assert (NULL != (c = handle->current));
-  if (buf == NULL)
+  if (NULL == buf)
   {
     /* timeout / error */
   {
     /* timeout / error */
-#if DEBUG_STATISTICS
     LOG (GNUNET_ERROR_TYPE_DEBUG,
          "Transmission of request for statistics failed!\n");
     LOG (GNUNET_ERROR_TYPE_DEBUG,
          "Transmission of request for statistics failed!\n");
-#endif  
     do_disconnect (handle);
     reconnect_later (handle);
     return 0;
     do_disconnect (handle);
     reconnect_later (handle);
     return 0;
@@ -657,10 +761,8 @@ transmit_get (struct GNUNET_STATISTICS_Handle *handle, size_t size, void *buf)
                                              c->name));
   if (GNUNET_YES != handle->receiving)
   {
                                              c->name));
   if (GNUNET_YES != handle->receiving)
   {
-#if DEBUG_STATISTICS
     LOG (GNUNET_ERROR_TYPE_DEBUG,
          "Transmission of GET done, now reading response\n");
     LOG (GNUNET_ERROR_TYPE_DEBUG,
          "Transmission of GET done, now reading response\n");
-#endif
     handle->receiving = GNUNET_YES;
     GNUNET_CLIENT_receive (handle->client, &receive_stats, handle,
                            GNUNET_TIME_absolute_get_remaining (c->timeout));
     handle->receiving = GNUNET_YES;
     GNUNET_CLIENT_receive (handle->client, &receive_stats, handle,
                            GNUNET_TIME_absolute_get_remaining (c->timeout));
@@ -686,21 +788,17 @@ transmit_watch (struct GNUNET_STATISTICS_Handle *handle, size_t size, void *buf)
   size_t slen2;
   uint16_t msize;
 
   size_t slen2;
   uint16_t msize;
 
-  if (buf == NULL)
+  if (NULL == buf)
   {
     /* timeout / error */
   {
     /* timeout / error */
-#if DEBUG_STATISTICS
     LOG (GNUNET_ERROR_TYPE_DEBUG,
          "Transmission of request for statistics failed!\n");
     LOG (GNUNET_ERROR_TYPE_DEBUG,
          "Transmission of request for statistics failed!\n");
-#endif
     do_disconnect (handle);
     reconnect_later (handle);
     return 0;
   }
     do_disconnect (handle);
     reconnect_later (handle);
     return 0;
   }
-#if DEBUG_STATISTICS
   LOG (GNUNET_ERROR_TYPE_DEBUG, "Transmitting watch request for `%s'\n",
        handle->current->name);
   LOG (GNUNET_ERROR_TYPE_DEBUG, "Transmitting watch request for `%s'\n",
        handle->current->name);
-#endif
   slen1 = strlen (handle->current->subsystem) + 1;
   slen2 = strlen (handle->current->name) + 1;
   msize = slen1 + slen2 + sizeof (struct GNUNET_MessageHeader);
   slen1 = strlen (handle->current->subsystem) + 1;
   slen2 = strlen (handle->current->name) + 1;
   msize = slen1 + slen2 + sizeof (struct GNUNET_MessageHeader);
@@ -773,6 +871,7 @@ transmit_set (struct GNUNET_STATISTICS_Handle *handle, size_t size, void *buf)
   GNUNET_assert (NULL == handle->current->cont);
   free_action_item (handle->current);
   handle->current = NULL;
   GNUNET_assert (NULL == handle->current->cont);
   free_action_item (handle->current);
   handle->current = NULL;
+  update_memory_statistics (handle);
   return nsize;
 }
 
   return nsize;
 }
 
@@ -828,9 +927,12 @@ GNUNET_STATISTICS_create (const char *subsystem,
 {
   struct GNUNET_STATISTICS_Handle *ret;
 
 {
   struct GNUNET_STATISTICS_Handle *ret;
 
-  GNUNET_assert (subsystem != NULL);
-  GNUNET_assert (cfg != NULL);
-  ret = GNUNET_malloc (sizeof (struct GNUNET_STATISTICS_Handle));
+  if (GNUNET_YES ==
+      GNUNET_CONFIGURATION_get_value_yesno (cfg, "statistics", "DISABLE"))
+    return NULL;
+  GNUNET_assert (NULL != subsystem);
+  GNUNET_assert (NULL != cfg);
+  ret = GNUNET_new (struct GNUNET_STATISTICS_Handle);
   ret->cfg = cfg;
   ret->subsystem = GNUNET_strdup (subsystem);
   ret->backoff = GNUNET_TIME_UNIT_MILLISECONDS;
   ret->cfg = cfg;
   ret->subsystem = GNUNET_strdup (subsystem);
   ret->backoff = GNUNET_TIME_UNIT_MILLISECONDS;
@@ -851,19 +953,22 @@ GNUNET_STATISTICS_destroy (struct GNUNET_STATISTICS_Handle *h, int sync_first)
 {
   struct GNUNET_STATISTICS_GetHandle *pos;
   struct GNUNET_STATISTICS_GetHandle *next;
 {
   struct GNUNET_STATISTICS_GetHandle *pos;
   struct GNUNET_STATISTICS_GetHandle *next;
-  struct GNUNET_STATISTICS_GetHandle *prev;
   struct GNUNET_TIME_Relative timeout;
   int i;
 
   struct GNUNET_TIME_Relative timeout;
   int i;
 
-  if (h == NULL)
+  if (NULL == h)
     return;
     return;
+  GNUNET_assert (GNUNET_NO == h->do_destroy); // Don't call twice.
   if (GNUNET_SCHEDULER_NO_TASK != h->backoff_task)
   if (GNUNET_SCHEDULER_NO_TASK != h->backoff_task)
+  {
     GNUNET_SCHEDULER_cancel (h->backoff_task);
     GNUNET_SCHEDULER_cancel (h->backoff_task);
+    h->backoff_task = GNUNET_SCHEDULER_NO_TASK;
+  }
   if (sync_first)
   {
   if (sync_first)
   {
-    if (h->current != NULL)
+    if (NULL != h->current)
     {
     {
-      if (h->current->type == ACTION_GET)
+      if (ACTION_GET == h->current->type)
       {
         GNUNET_CLIENT_notify_transmit_ready_cancel (h->th);
         h->th = NULL;
       {
         GNUNET_CLIENT_notify_transmit_ready_cancel (h->th);
         h->th = NULL;
@@ -871,61 +976,36 @@ GNUNET_STATISTICS_destroy (struct GNUNET_STATISTICS_Handle *h, int sync_first)
         h->current = NULL;
       }
     }
         h->current = NULL;
       }
     }
-    pos = h->action_head;
-    prev = NULL;
-    while (pos != NULL)
+    next = h->action_head;
+    while (NULL != (pos = next))
     {
       next = pos->next;
     {
       next = pos->next;
-      if (pos->type == ACTION_GET)
+      if (ACTION_GET == pos->type)
       {
       {
-        if (prev == NULL)
-          h->action_head = next;
-        else
-          prev->next = next;
+       GNUNET_CONTAINER_DLL_remove (h->action_head,
+                                    h->action_tail,
+                                    pos);
         free_action_item (pos);
       }
         free_action_item (pos);
       }
-      else
-      {
-        prev = pos;
-      }
-      pos = next;
-    }
-    h->action_tail = prev;
-    if (h->current == NULL)
-    {
-      h->current = h->action_head;
-      if (h->action_head != NULL)
-      {
-        h->action_head = h->action_head->next;
-        if (h->action_head == NULL)
-          h->action_tail = NULL;
-      }
     }
     }
+    if ( (NULL == h->current) &&
+        (NULL != (h->current = h->action_head)) )
+      GNUNET_CONTAINER_DLL_remove (h->action_head,
+                                  h->action_tail,
+                                  h->current);
     h->do_destroy = GNUNET_YES;
     h->do_destroy = GNUNET_YES;
-    if ((h->current != NULL) && (h->th == NULL))
+    if ((NULL != h->current) && (NULL == h->th) &&
+       (NULL != h->client))
     {
     {
-      if (NULL == h->client)
-      {
-       /* instant-connect (regardless of back-off) to submit final value */
-       if (GNUNET_SCHEDULER_NO_TASK != h->backoff_task)
-       {
-         GNUNET_SCHEDULER_cancel (h->backoff_task);
-         h->backoff_task = GNUNET_SCHEDULER_NO_TASK;
-       }
-       h->client = GNUNET_CLIENT_connect ("statistics", h->cfg);
-      }
-      if (NULL != h->client)
-      {
-       timeout = GNUNET_TIME_absolute_get_remaining (h->current->timeout);
-       h->th =
-          GNUNET_CLIENT_notify_transmit_ready (h->client, h->current->msize,
-                                               timeout, GNUNET_YES,
-                                               &transmit_action, h);
-       GNUNET_assert (NULL != h->th);
-      }
+      timeout = GNUNET_TIME_absolute_get_remaining (h->current->timeout);
+      h->th =
+       GNUNET_CLIENT_notify_transmit_ready (h->client, h->current->msize,
+                                            timeout, GNUNET_YES,
+                                            &transmit_action, h);
+      GNUNET_assert (NULL != h->th);
     }
     }
-    if (h->th != NULL)
-      return;
+    if (NULL != h->th)
+      return; /* do not finish destruction just yet */
   }
   while (NULL != (pos = h->action_head))
   {
   }
   while (NULL != (pos = h->action_head))
   {
@@ -938,7 +1018,7 @@ GNUNET_STATISTICS_destroy (struct GNUNET_STATISTICS_Handle *h, int sync_first)
   for (i = 0; i < h->watches_size; i++)
   {
     if (NULL == h->watches[i])
   for (i = 0; i < h->watches_size; i++)
   {
     if (NULL == h->watches[i])
-      continue; 
+      continue;
     GNUNET_free (h->watches[i]->subsystem);
     GNUNET_free (h->watches[i]->name);
     GNUNET_free (h->watches[i]);
     GNUNET_free (h->watches[i]->subsystem);
     GNUNET_free (h->watches[i]->name);
     GNUNET_free (h->watches[i]);
@@ -949,6 +1029,47 @@ GNUNET_STATISTICS_destroy (struct GNUNET_STATISTICS_Handle *h, int sync_first)
 }
 
 
 }
 
 
+/**
+ * Function called to transmit TEST message to service to
+ * confirm that the service has received all of our 'SET'
+ * messages (during statistics disconnect/shutdown).
+ *
+ * @param cls the 'struct GNUNET_STATISTICS_Handle'
+ * @param size how many bytes can we write to buf
+ * @param buf where to write requests to the service
+ * @return number of bytes written to buf
+ */
+static size_t
+transmit_test_on_shutdown (void *cls,
+                          size_t size,
+                          void *buf)
+{
+  struct GNUNET_STATISTICS_Handle *h = cls;
+  struct GNUNET_MessageHeader hdr;
+
+  h->th = NULL;
+  if (NULL == buf)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+               _("Failed to receive acknowledgement from statistics service, some statistics might have been lost!\n"));
+    h->do_destroy = GNUNET_NO;
+    GNUNET_SCHEDULER_add_continuation (&destroy_task, h,
+                                      GNUNET_SCHEDULER_REASON_PREREQ_DONE);
+    return 0;
+  }
+  hdr.type = htons (GNUNET_MESSAGE_TYPE_TEST);
+  hdr.size = htons (sizeof (struct GNUNET_MessageHeader));
+  memcpy (buf, &hdr, sizeof (hdr));
+  if (GNUNET_YES != h->receiving)
+  {
+    h->receiving = GNUNET_YES;
+    GNUNET_CLIENT_receive (h->client, &receive_stats, h,
+                           GNUNET_TIME_UNIT_FOREVER_REL);
+  }
+  return sizeof (struct GNUNET_MessageHeader);
+}
+
+
 /**
  * Schedule the next action to be performed.
  *
 /**
  * Schedule the next action to be performed.
  *
@@ -959,8 +1080,8 @@ schedule_action (struct GNUNET_STATISTICS_Handle *h)
 {
   struct GNUNET_TIME_Relative timeout;
 
 {
   struct GNUNET_TIME_Relative timeout;
 
-  if ( (h->th != NULL) ||
-       (h->backoff_task != GNUNET_SCHEDULER_NO_TASK) )
+  if ( (NULL != h->th) ||
+       (GNUNET_SCHEDULER_NO_TASK != h->backoff_task) )
     return;                     /* action already pending */
   if (GNUNET_YES != try_connect (h))
   {
     return;                     /* action already pending */
   if (GNUNET_YES != try_connect (h))
   {
@@ -973,10 +1094,14 @@ schedule_action (struct GNUNET_STATISTICS_Handle *h)
   h->current = h->action_head;
   if (NULL == h->current)
   {
   h->current = h->action_head;
   if (NULL == h->current)
   {
-    if (h->do_destroy)
+    if (GNUNET_YES == h->do_destroy)
     {
     {
-      h->do_destroy = GNUNET_NO;
-      GNUNET_STATISTICS_destroy (h, GNUNET_YES);
+      h->do_destroy = GNUNET_SYSERR; /* in 'TEST' mode */
+      h->th = GNUNET_CLIENT_notify_transmit_ready (h->client,
+                                                  sizeof (struct GNUNET_MessageHeader),
+                                                  SET_TRANSMIT_TIMEOUT,
+                                                  GNUNET_NO,
+                                                  &transmit_test_on_shutdown, h);
     }
     return;
   }
     }
     return;
   }
@@ -988,10 +1113,8 @@ schedule_action (struct GNUNET_STATISTICS_Handle *h)
                                             timeout, GNUNET_YES,
                                             &transmit_action, h)))
   {
                                             timeout, GNUNET_YES,
                                             &transmit_action, h)))
   {
-#if DEBUG_STATISTICS
     LOG (GNUNET_ERROR_TYPE_DEBUG,
          "Failed to transmit request to statistics service.\n");
     LOG (GNUNET_ERROR_TYPE_DEBUG,
          "Failed to transmit request to statistics service.\n");
-#endif
     do_disconnect (h);
     reconnect_later (h);
   }
     do_disconnect (h);
     reconnect_later (h);
   }
@@ -1007,6 +1130,7 @@ schedule_action (struct GNUNET_STATISTICS_Handle *h)
  * @param timeout after how long should we give up (and call
  *        cont with an error code)?
  * @param cont continuation to call when done (can be NULL)
  * @param timeout after how long should we give up (and call
  *        cont with an error code)?
  * @param cont continuation to call when done (can be NULL)
+ *        This callback CANNOT destroy the statistics handle in the same call.
  * @param proc function to call on each value
  * @param cls closure for cont and proc
  * @return NULL on error
  * @param proc function to call on each value
  * @param cls closure for cont and proc
  * @return NULL on error
@@ -1024,17 +1148,17 @@ GNUNET_STATISTICS_get (struct GNUNET_STATISTICS_Handle *handle,
 
   if (NULL == handle)
     return NULL;
 
   if (NULL == handle)
     return NULL;
-  GNUNET_assert (proc != NULL);
+  GNUNET_assert (NULL != proc);
   GNUNET_assert (GNUNET_NO == handle->do_destroy);
   GNUNET_assert (GNUNET_NO == handle->do_destroy);
-  if (subsystem == NULL)
+  if (NULL == subsystem)
     subsystem = "";
     subsystem = "";
-  if (name == NULL)
+  if (NULL == name)
     name = "";
   slen1 = strlen (subsystem) + 1;
   slen2 = strlen (name) + 1;
   GNUNET_assert (slen1 + slen2 + sizeof (struct GNUNET_MessageHeader) <
                  GNUNET_SERVER_MAX_MESSAGE_SIZE);
     name = "";
   slen1 = strlen (subsystem) + 1;
   slen2 = strlen (name) + 1;
   GNUNET_assert (slen1 + slen2 + sizeof (struct GNUNET_MessageHeader) <
                  GNUNET_SERVER_MAX_MESSAGE_SIZE);
-  ai = GNUNET_malloc (sizeof (struct GNUNET_STATISTICS_GetHandle));
+  ai = GNUNET_new (struct GNUNET_STATISTICS_GetHandle);
   ai->sh = handle;
   ai->subsystem = GNUNET_strdup (subsystem);
   ai->name = GNUNET_strdup (name);
   ai->sh = handle;
   ai->subsystem = GNUNET_strdup (subsystem);
   ai->name = GNUNET_strdup (name);
@@ -1062,6 +1186,7 @@ GNUNET_STATISTICS_get_cancel (struct GNUNET_STATISTICS_GetHandle *gh)
 {
   if (NULL == gh)
     return;
 {
   if (NULL == gh)
     return;
+  gh->cont = NULL;
   if (gh->sh->current == gh)
   {
     gh->aborted = GNUNET_YES;
   if (gh->sh->current == gh)
   {
     gh->aborted = GNUNET_YES;
@@ -1078,8 +1203,6 @@ GNUNET_STATISTICS_get_cancel (struct GNUNET_STATISTICS_GetHandle *gh)
 
 /**
  * Watch statistics from the peer (be notified whenever they change).
 
 /**
  * Watch statistics from the peer (be notified whenever they change).
- * Note that the only way to cancel a "watch" request is to destroy
- * the statistics handle given as the first argument to this call.
  *
  * @param handle identification of the statistics service
  * @param subsystem limit to the specified subsystem, never NULL
  *
  * @param handle identification of the statistics service
  * @param subsystem limit to the specified subsystem, never NULL
@@ -1095,9 +1218,9 @@ GNUNET_STATISTICS_watch (struct GNUNET_STATISTICS_Handle *handle,
 {
   struct GNUNET_STATISTICS_WatchEntry *w;
 
 {
   struct GNUNET_STATISTICS_WatchEntry *w;
 
-  if (handle == NULL)
+  if (NULL == handle)
     return GNUNET_SYSERR;
     return GNUNET_SYSERR;
-  w = GNUNET_malloc (sizeof (struct GNUNET_STATISTICS_WatchEntry));
+  w = GNUNET_new (struct GNUNET_STATISTICS_WatchEntry);
   w->subsystem = GNUNET_strdup (subsystem);
   w->name = GNUNET_strdup (name);
   w->proc = proc;
   w->subsystem = GNUNET_strdup (subsystem);
   w->name = GNUNET_strdup (name);
   w->proc = proc;
@@ -1108,6 +1231,48 @@ GNUNET_STATISTICS_watch (struct GNUNET_STATISTICS_Handle *handle,
 }
 
 
 }
 
 
+/**
+ * Stop watching statistics from the peer.
+ *
+ * @param handle identification of the statistics service
+ * @param subsystem limit to the specified subsystem, never NULL
+ * @param name name of the statistic value, never NULL
+ * @param proc function to call on each value
+ * @param proc_cls closure for proc
+ * @return GNUNET_OK on success, GNUNET_SYSERR on error (no such watch)
+ */
+int
+GNUNET_STATISTICS_watch_cancel (struct GNUNET_STATISTICS_Handle *handle,
+                               const char *subsystem, const char *name,
+                               GNUNET_STATISTICS_Iterator proc, void *proc_cls)
+{
+  struct GNUNET_STATISTICS_WatchEntry *w;
+  unsigned int i;
+
+  if (NULL == handle)
+    return GNUNET_SYSERR;
+  for (i=0;i<handle->watches_size;i++)
+  {
+    w = handle->watches[i];
+    if (NULL == w)
+      continue;
+    if ( (w->proc == proc) &&
+        (w->proc_cls == proc_cls) &&
+        (0 == strcmp (w->name, name)) &&
+        (0 == strcmp (w->subsystem, subsystem)) )
+    {
+      GNUNET_free (w->name);
+      GNUNET_free (w->subsystem);
+      GNUNET_free (w);
+      handle->watches[i] = NULL;
+      return GNUNET_OK;
+    }
+  }
+  return GNUNET_SYSERR;
+}
+
+
+
 /**
  * Queue a request to change a statistic.
  *
 /**
  * Queue a request to change a statistic.
  *
@@ -1127,8 +1292,8 @@ add_setter_action (struct GNUNET_STATISTICS_Handle *h, const char *name,
   size_t nsize;
   int64_t delta;
 
   size_t nsize;
   int64_t delta;
 
-  GNUNET_assert (h != NULL);
-  GNUNET_assert (name != NULL);
+  GNUNET_assert (NULL != h);
+  GNUNET_assert (NULL != name);
   slen = strlen (h->subsystem) + 1;
   nlen = strlen (name) + 1;
   nsize = sizeof (struct GNUNET_STATISTICS_SetMessage) + slen + nlen;
   slen = strlen (h->subsystem) + 1;
   nlen = strlen (name) + 1;
   nsize = sizeof (struct GNUNET_STATISTICS_SetMessage) + slen + nlen;
@@ -1137,16 +1302,16 @@ add_setter_action (struct GNUNET_STATISTICS_Handle *h, const char *name,
     GNUNET_break (0);
     return;
   }
     GNUNET_break (0);
     return;
   }
-  for (ai = h->action_head; ai != NULL; ai = ai->next)
+  for (ai = h->action_head; NULL != ai; ai = ai->next)
   {
     if (! ( (0 == strcmp (ai->subsystem, h->subsystem)) &&
   {
     if (! ( (0 == strcmp (ai->subsystem, h->subsystem)) &&
-           (0 == strcmp (ai->name, name)) && 
-           ( (ai->type == ACTION_UPDATE) ||
-             (ai->type == ACTION_SET) ) ) )
+           (0 == strcmp (ai->name, name)) &&
+           ( (ACTION_UPDATE == ai->type) ||
+             (ACTION_SET == ai->type) ) ) )
       continue;
       continue;
-    if (ai->type == ACTION_SET)
+    if (ACTION_SET == ai->type)
     {
     {
-      if (type == ACTION_UPDATE)
+      if (ACTION_UPDATE == type)
       {
        delta = (int64_t) value;
        if (delta > 0)
       {
        delta = (int64_t) value;
        if (delta > 0)
@@ -1171,7 +1336,7 @@ add_setter_action (struct GNUNET_STATISTICS_Handle *h, const char *name,
     }
     else
     {
     }
     else
     {
-      if (type == ACTION_UPDATE)
+      if (ACTION_UPDATE == type)
       {
        /* make delta cummulative */
        delta = (int64_t) value;
       {
        /* make delta cummulative */
        delta = (int64_t) value;
@@ -1186,10 +1351,10 @@ add_setter_action (struct GNUNET_STATISTICS_Handle *h, const char *name,
     }
     ai->timeout = GNUNET_TIME_relative_to_absolute (SET_TRANSMIT_TIMEOUT);
     ai->make_persistent = make_persistent;
     }
     ai->timeout = GNUNET_TIME_relative_to_absolute (SET_TRANSMIT_TIMEOUT);
     ai->make_persistent = make_persistent;
-    return;  
+    return;
   }
   /* no existing entry matches, create a fresh one */
   }
   /* no existing entry matches, create a fresh one */
-  ai = GNUNET_malloc (sizeof (struct GNUNET_STATISTICS_GetHandle));
+  ai = GNUNET_new (struct GNUNET_STATISTICS_GetHandle);
   ai->sh = h;
   ai->subsystem = GNUNET_strdup (h->subsystem);
   ai->name = GNUNET_strdup (name);
   ai->sh = h;
   ai->subsystem = GNUNET_strdup (h->subsystem);
   ai->name = GNUNET_strdup (name);
@@ -1217,7 +1382,7 @@ void
 GNUNET_STATISTICS_set (struct GNUNET_STATISTICS_Handle *handle,
                        const char *name, uint64_t value, int make_persistent)
 {
 GNUNET_STATISTICS_set (struct GNUNET_STATISTICS_Handle *handle,
                        const char *name, uint64_t value, int make_persistent)
 {
-  if (handle == NULL)
+  if (NULL == handle)
     return;
   GNUNET_assert (GNUNET_NO == handle->do_destroy);
   add_setter_action (handle, name, make_persistent, value, ACTION_SET);
     return;
   GNUNET_assert (GNUNET_NO == handle->do_destroy);
   add_setter_action (handle, name, make_persistent, value, ACTION_SET);
@@ -1237,9 +1402,9 @@ void
 GNUNET_STATISTICS_update (struct GNUNET_STATISTICS_Handle *handle,
                           const char *name, int64_t delta, int make_persistent)
 {
 GNUNET_STATISTICS_update (struct GNUNET_STATISTICS_Handle *handle,
                           const char *name, int64_t delta, int make_persistent)
 {
-  if (handle == NULL)
+  if (NULL == handle)
     return;
     return;
-  if (delta == 0)
+  if (0 == delta)
     return;
   GNUNET_assert (GNUNET_NO == handle->do_destroy);
   add_setter_action (handle, name, make_persistent, (uint64_t) delta,
     return;
   GNUNET_assert (GNUNET_NO == handle->do_destroy);
   add_setter_action (handle, name, make_persistent, (uint64_t) delta,