Add code to allow for late memory cleanup.
authorWerner Koch <wk@gnupg.org>
Fri, 28 Dec 2012 18:26:59 +0000 (19:26 +0100)
committerWerner Koch <wk@gnupg.org>
Wed, 20 Mar 2013 08:50:17 +0000 (09:50 +0100)
* common/init.c (mem_cleanup_item_t): New.
(run_mem_cleanup): New.
(_init_common_subsystems): Add an atexit for it.
(register_mem_cleanup_func): New.

* g10/kbnode.c (cleanup_registered): New.
(release_unused_nodes): New.
(alloc_node): Call register_mem_cleanup_func.
--

It is often time consuming to figure out whether still allocated
memory at process termination is fine (e.g. a cache) or a problem.  To
help for that register_mem_cleanup_func may now be used to cleanup
such memory.  The run time of the program will be longer; if that
turns out to be a problem we can change the code to only run in
debugging mode.

common/init.c
common/init.h
g10/kbnode.c

index e00b9b3..8a0b6a8 100644 (file)
 
 #include "util.h"
 
+/* This object is used to register memory cleanup functions.
+   Technically they are not needed but they can avoid frequent
+   questions about un-released memory.  Note that we use the system
+   malloc and not any wrappers.  */
+struct mem_cleanup_item_s;
+typedef struct mem_cleanup_item_s *mem_cleanup_item_t;
+
+struct mem_cleanup_item_s
+{
+  mem_cleanup_item_t next;
+  void (*func) (void);
+};
+
+static mem_cleanup_item_t mem_cleanup_list;
+
 
 /* The default error source of the application.  This is different
    from GPG_ERR_SOURCE_DEFAULT in that it does not depend on the
@@ -65,6 +80,36 @@ sleep_on_exit (void)
 #endif /*HAVE_W32CE_SYSTEM*/
 
 
+static void
+run_mem_cleanup (void)
+{
+  mem_cleanup_item_t next;
+
+  while (mem_cleanup_list)
+    {
+      next = mem_cleanup_list->next;
+      mem_cleanup_list->func ();
+      free (mem_cleanup_list);
+      mem_cleanup_list = next;
+    }
+}
+
+
+void
+register_mem_cleanup_func (void (*func)(void))
+{
+  mem_cleanup_item_t item;
+
+  item = malloc (sizeof *item);
+  if (item)
+    {
+      item->func = func;
+      item->next = mem_cleanup_list;
+      mem_cleanup_list = item;
+    }
+}
+
+
 /* If STRING is not NULL write string to es_stdout or es_stderr.  MODE
    must be 1 or 2.  If STRING is NULL flush the respective stream.  */
 static int
@@ -100,6 +145,8 @@ _init_common_subsystems (gpg_err_source_t errsource, int *argcp, char ***argvp)
   /* Store the error source in a gloabl variable. */
   default_errsource = errsource;
 
+  atexit (run_mem_cleanup);
+
   /* Try to auto set the character set.  */
   set_native_charset (NULL);
 
index 633ffac..eea2eb1 100644 (file)
@@ -36,6 +36,8 @@
 # error GPG_ERR_SOURCE_DEFAULT has default value
 #endif
 
+void register_mem_cleanup_func (void (*func)(void));
+
 void _init_common_subsystems (gpg_err_source_t errsource,
                               int *argcp, char ***argvp);
 #define init_common_subsystems(a,b)                             \
index 1a8b91e..d490740 100644 (file)
 
 #define USE_UNUSED_NODES 1
 
+static int cleanup_registered;
 static KBNODE unused_nodes;
 
-static KBNODE
-alloc_node(void)
+#if USE_UNUSED_NODES
+static void
+release_unused_nodes (void)
 {
-    KBNODE n;
+  while (unused_nodes)
+    {
+      kbnode_t next = unused_nodes->next;
+      xfree (unused_nodes);
+      unused_nodes = next;
+    }
+}
+#endif /*USE_UNUSED_NODES*/
 
-    n = unused_nodes;
-    if( n )
-       unused_nodes = n->next;
-    else
-       n = xmalloc( sizeof *n );
-    n->next = NULL;
-    n->pkt = NULL;
-    n->flag = 0;
-    n->private_flag=0;
-    n->recno = 0;
-    return n;
+
+static kbnode_t
+alloc_node (void)
+{
+  kbnode_t n;
+
+  n = unused_nodes;
+  if (n)
+    unused_nodes = n->next;
+  else
+    {
+      if (!cleanup_registered)
+        {
+          cleanup_registered = 1;
+          register_mem_cleanup_func (release_unused_nodes);
+        }
+      n = xmalloc (sizeof *n);
+    }
+  n->next = NULL;
+  n->pkt = NULL;
+  n->flag = 0;
+  n->private_flag=0;
+  n->recno = 0;
+  return n;
 }
 
 static void
 free_node( KBNODE n )
 {
-    if( n ) {
+  if (n)
+    {
 #if USE_UNUSED_NODES
-       n->next = unused_nodes;
-       unused_nodes = n;
+      n->next = unused_nodes;
+      unused_nodes = n;
 #else
-       xfree( n );
+      xfree (n);
 #endif
     }
 }