ECL's default heap (1GB on 32-bit or 4GB on 64-bit) can sometimes
authorMatthew Mondor <mmondor@pulsar-zone.net>
Thu, 27 Aug 2015 18:08:06 +0000 (14:08 -0400)
committerMatthew Mondor <mmondor@pulsar-zone.net>
Thu, 27 Aug 2015 18:20:05 +0000 (14:20 -0400)
be higher than the OS's process rlimit is, especially the soft limit.
Consider this default heap, as well as any user-specified heap size
as a target, and adapt as necessary the heap size according to the
soft and hard rlimits if any.
We attempt to grow the soft limit if possible to reach the target,
but as necessary lower the heap size.
Otherwise, when ECL experiences allocation errors and attempts to
report these, needing even more allocations to do so, it can end
up locked in a busy loop.  It's better to gracefully report that the
heap should be grown instead.

src/c/main.d
src/c/stacks.d
src/configure
src/configure.ac
src/h/internal.h

index ce605ae..be7272b 100755 (executable)
@@ -44,6 +44,9 @@
 #include <ecl/cache.h>
 #include <ecl/internal.h>
 #include <ecl/ecl-inl.h>
+#ifdef HAVE_GETRLIMIT
+#include <sys/resource.h>
+#endif
 
 
 #include "ecl_features.h"
@@ -60,6 +63,66 @@ const char *ecl_self;
 
 /************************ GLOBAL INITIALIZATION ***********************/
 
+
+/* HEAP */
+
+/* 10MB extra for ECL itself */
+#define HEAP_GAP (10 * 1024 * 1024)
+
+#if ECL_FIXNUM_BITS <= 32
+#define HEAP_SIZE_DEFAULT (1024 * 1024 * 1024)
+#else
+#define HEAP_SIZE_DEFAULT (4096 * 1024 * 1024)
+#endif
+
+/*
+ * If the target heap size (1GB on 32-bit, 4GB otherwise) exceeds
+ * the soft process data size rlimit, attempt to grow the soft limit.
+ * If the hard limit doesn't allow it, reduce the target heap size
+ * to the soft limit.  The heap will be smaller than expected, but ECL will
+ * be able to report allocation errors gracefully rather than busy-looping
+ * attempting to allocate even more room to report the error and encountering
+ * even more allocation errors.
+ */
+size_t
+fix_heap_size(size_t target)
+{
+#ifdef HAVE_SETRLIMIT
+        struct rlimit rlp;
+
+        if (getrlimit(RLIMIT_DATA, &rlp) != 0) {
+                /* Cannot evaluate, keep target */
+                return target;
+        }
+        /* Soft limit too low? */
+        if (target + HEAP_GAP > rlp.rlim_cur) {
+                size_t missing = target + HEAP_GAP - rlp.rlim_cur;
+
+                /* Hard limit too low to reach target? */
+                if (rlp.rlim_cur + missing > rlp.rlim_max) {
+                        /* XXX We could error immediately here instead */
+                        return (rlp.rlim_max - HEAP_GAP);
+                }
+
+                if (rlp.rlim_cur + missing < rlp.rlim_max) {
+                        /* Attempt to grow soft limit */
+                        rlp.rlim_cur += missing;
+                        if (setrlimit(RLIMIT_DATA, &rlp) == 0)
+                                return target;
+                        else {
+                                /* XXX We could error immediately instead */
+                                return (rlp.rlim_cur - HEAP_GAP);
+                        }
+                } else {
+                        /* XXX We could error immediately instead */
+                        return (rlp.rlim_cur - HEAP_GAP);
+                }
+        }
+#endif
+        return target;
+}
+
+
 static int ARGC;
 static char **ARGV;
 cl_fixnum ecl_option_values[ECL_OPT_LIMIT+1] = {
@@ -88,11 +151,7 @@ cl_fixnum ecl_option_values[ECL_OPT_LIMIT+1] = {
         128*sizeof(cl_index)*1024, /* ECL_OPT_C_STACK_SIZE */
         4*sizeof(cl_index)*1024, /* ECL_OPT_C_STACK_SAFETY_AREA */
         1,              /* ECL_OPT_SIGALTSTACK_SIZE */
-#if ECL_FIXNUM_BITS <= 32
-        1024*1024*1024, /* ECL_OPT_HEAP_SIZE */
-#else
-        4024*1024*1024, /* ECL_OPT_HEAP_SIZE */
-#endif
+        0,              /* ECL_OPT_HEAP_SIZE (set below in cl_boot()) */
         1024*1024,      /* ECL_OPT_HEAP_SAFETY_AREA */
         0,              /* ECL_OPT_THREAD_INTERRUPT_SIGNAL */
         1,              /* ECL_OPT_SET_GMP_MEMORY_FUNCTIONS */
@@ -489,6 +548,9 @@ cl_boot(int argc, char **argv)
         int i;
         cl_env_ptr env;
 
+        ecl_option_values[ECL_OPT_HEAP_SIZE] =
+            fix_heap_size(HEAP_SIZE_DEFAULT);
+
         i = ecl_option_values[ECL_OPT_BOOTED];
         if (i) {
                 if (i < 0) {
index 2ff99fc..59c7058 100644 (file)
@@ -633,7 +633,7 @@ si_set_limit(cl_object type, cl_object limit)
         } else if (type == @'ext::lisp-stack')
                 ecl_stack_set_size(env, the_size);
         else
-                _ecl_set_max_heap_size(the_size);
+                _ecl_set_max_heap_size(fix_heap_size(the_size));
 
         return si_get_limit(type);
 }
index c9f64e4..416d79d 100755 (executable)
@@ -9193,6 +9193,19 @@ fi
 done
 
 
+for ac_func in getrlimit setrlimit
+do :
+  as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
+ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var"
+if eval test \"x\$"$as_ac_var"\" = x"yes"; then :
+  cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+done
+
+
 
 for ac_func in expf logf sqrtf cosf sinf tanf sinhf coshf tanhf \
                floorf ceilf fabsf frexpf ldexpf log1p log1pf log1pl \
index 9ab7397..94169dd 100644 (file)
@@ -710,6 +710,8 @@ AC_CHECK_FUNCS( [nanosleep alarm times select setenv putenv] \
                 [lstat mkstemp sigprocmask isatty tzset] \
                [gettimeofday getrusage] )
 
+AC_CHECK_FUNCS( [getrlimit setrlimit] )
+
 dnl AC_CHECK_FUNCS( [ feenableexcept ] )
 
 AC_CHECK_FUNCS( [expf logf sqrtf cosf sinf tanf sinhf coshf tanhf] \
index d7b64f7..67da9fd 100755 (executable)
@@ -53,6 +53,8 @@ extern void init_lib_LSP(cl_object);
 extern cl_env_ptr _ecl_alloc_env(cl_env_ptr parent);
 extern void _ecl_dealloc_env(cl_env_ptr);
 
+extern size_t fix_heap_size(size_t target);
+
 /* alloc.d/alloc_2.d */
 
 #ifdef GBC_BOEHM