Subject: make-fresh-user-module procedure leaks memory

From: Chris Vine <[email-masked]>
Date: Sun, 30 Nov 2014 23:28:34 +0000

The make-fresh-user-module procedure leaks memory in guile-2.0.11 as
demonstrated by the attached test case.  This test case should be
invoked either with the "shared" or "fresh" option.  If invoked with
the "fresh" option, it will call make-fresh-user-module on each
iteration through the loop.  On my 32-bit machine it will steadily
accumulate a memory leak before running out of memory on consuming
approximately 2.2G memory, after about 180,000 iterations.  If called
with the "shared" option, it will accumulate no additional memory while
executing, and will execute normally to the end of its iterations.

The question which might be asked is "Would any sane person ever want
to invoke the make-fresh-user-module procedure more than a few times
in a practical program?".  The answer to this question is "Yes", if
guile is being used as an extension framework for a C or C++ program,
and it executes guile extensions as individual tasks, and it is
necessary that top levels should not to be shared.  The execution of
tasks concurrently is one such case, but there can be many cases where
isolated top levels are desirable for tasks executed serially also.

Test case:

----------------------------- snip -----------------------------

/* compile with 'gcc -O2 -Wall `pkg-config --cflags --libs guile-2.0` -o test-guile' */

#include <unistd.h>
#include <libguile.h>
#include <stdio.h>
#include <string.h>

int fresh;

void* func (void* data)
{

  switch (fresh)
    {
      case 0:
	scm_c_eval_string("");
	break;
      default:
	scm_c_eval_string("(set-current-module (make-fresh-user-module))");
    }

  return NULL;
}

int main (int argc, char *argv[])
{

  int count;

  if (argc != 2
      || (strcmp (argv[1], "shared") &&
	  strcmp (argv[1], "fresh")))
    {
      puts ("Usage: test-guile shared | fresh");
      exit (1);
    }

  if (!strcmp (argv[1], "fresh"))
    {
      puts("Invoking make-fresh-user-module");
      fresh = 1;
    }
  else
    puts("Using shared top level");

  for (count = 0; count < 256000; ++count)
    {
      scm_with_guile(func, NULL);

      if (!(count % 100)) {
	printf("%d ", count);
	fflush(stdout);
      }

      usleep(1);
    }

  puts("");
  return 0;
}