@@ -33,6 +33,88 @@ static void * oomHandler(size_t requested)
3333 throw std::bad_alloc ();
3434}
3535
36+ static size_t getFreeMem ()
37+ {
38+ /* On Linux, use the `MemAvailable` or `MemFree` fields from
39+ /proc/cpuinfo. */
40+ # ifdef __linux__
41+ {
42+ std::unordered_map<std::string, std::string> fields;
43+ for (auto & line :
44+ tokenizeString<std::vector<std::string>>(readFile (std::filesystem::path (" /proc/meminfo" )), " \n " )) {
45+ auto colon = line.find (' :' );
46+ if (colon == line.npos )
47+ continue ;
48+ fields.emplace (line.substr (0 , colon), trim (line.substr (colon + 1 )));
49+ }
50+
51+ auto i = fields.find (" MemAvailable" );
52+ if (i == fields.end ())
53+ i = fields.find (" MemFree" );
54+ if (i != fields.end ()) {
55+ auto kb = tokenizeString<std::vector<std::string>>(i->second , " " );
56+ if (kb.size () == 2 && kb[1 ] == " kB" )
57+ return string2Int<size_t >(kb[0 ]).value_or (0 ) * 1024 ;
58+ }
59+ }
60+ # endif
61+
62+ /* On non-Linux systems, conservatively assume that 25% of memory is free. */
63+ long pageSize = sysconf (_SC_PAGESIZE);
64+ long pages = sysconf (_SC_PHYS_PAGES);
65+ if (pageSize > 0 && pages > 0 )
66+ return (static_cast <size_t >(pageSize) * static_cast <size_t >(pages)) / 4 ;
67+ return 0 ;
68+ }
69+
70+ /* *
71+ * When a thread goes into a coroutine, we lose its original sp until
72+ * control flow returns to the thread. This causes Boehm GC to crash
73+ * since it will scan memory between the coroutine's sp and the
74+ * original stack base of the thread. Therefore, we detect when the
75+ * current sp is outside of the original thread stack and push the
76+ * entire thread stack instead, as an approximation.
77+ *
78+ * This is not optimal, because it causes the stack below sp to be
79+ * scanned. However, we usually we don't have active coroutines during
80+ * evaluation, so this is acceptable.
81+ *
82+ * Note that we don't scan coroutine stacks. It's currently assumed
83+ * that we don't have GC roots in coroutines.
84+ */
85+ void fixupBoehmStackPointer (void ** sp_ptr, void * _pthread_id)
86+ {
87+ void *& sp = *sp_ptr;
88+ auto pthread_id = reinterpret_cast <pthread_t >(_pthread_id);
89+ size_t osStackSize;
90+ char * osStackHi;
91+ char * osStackLo;
92+
93+ # ifdef __APPLE__
94+ osStackSize = pthread_get_stacksize_np (pthread_id);
95+ osStackHi = (char *) pthread_get_stackaddr_np (pthread_id);
96+ osStackLo = osStackHi - osStackSize;
97+ # else
98+ pthread_attr_t pattr;
99+ if (pthread_attr_init (&pattr))
100+ throw Error (" fixupBoehmStackPointer: pthread_attr_init failed" );
101+ # ifdef HAVE_PTHREAD_GETATTR_NP
102+ if (pthread_getattr_np (pthread_id, &pattr))
103+ throw Error (" fixupBoehmStackPointer: pthread_getattr_np failed" );
104+ # else
105+ # error "Need `pthread_attr_get_np`"
106+ # endif
107+ if (pthread_attr_getstack (&pattr, (void **) &osStackLo, &osStackSize))
108+ throw Error (" fixupBoehmStackPointer: pthread_attr_getstack failed" );
109+ if (pthread_attr_destroy (&pattr))
110+ throw Error (" fixupBoehmStackPointer: pthread_attr_destroy failed" );
111+ osStackHi = osStackLo + osStackSize;
112+ # endif
113+
114+ if (sp >= osStackHi || sp < osStackLo) // sp is outside the os stack
115+ sp = osStackLo;
116+ }
117+
36118static inline void initGCReal ()
37119{
38120 /* Initialise the Boehm garbage collector. */
@@ -63,8 +145,11 @@ static inline void initGCReal()
63145
64146 GC_set_oom_fn (oomHandler);
65147
66- /* Set the initial heap size to something fairly big (25% of
67- physical RAM, up to a maximum of 384 MiB) so that in most cases
148+ GC_set_sp_corrector (&fixupBoehmStackPointer);
149+ assert (GC_get_sp_corrector ());
150+
151+ /* Set the initial heap size to something fairly big (80% of
152+ free RAM, up to a maximum of 4 GiB) so that in most cases
68153 we don't need to garbage collect at all. (Collection has a
69154 fairly significant overhead.) The heap size can be overridden
70155 through libgc's GC_INITIAL_HEAP_SIZE environment variable. We
@@ -75,15 +160,10 @@ static inline void initGCReal()
75160 if (!getEnv (" GC_INITIAL_HEAP_SIZE" )) {
76161 size_t size = 32 * 1024 * 1024 ;
77162# if HAVE_SYSCONF && defined(_SC_PAGESIZE) && defined(_SC_PHYS_PAGES)
78- size_t maxSize = 384 * 1024 * 1024 ;
79- long pageSize = sysconf (_SC_PAGESIZE);
80- long pages = sysconf (_SC_PHYS_PAGES);
81- if (pageSize != -1 )
82- size = (pageSize * pages) / 4 ; // 25% of RAM
83- if (size > maxSize)
84- size = maxSize;
163+ size_t maxSize = 4ULL * 1024 * 1024 * 1024 ;
164+ auto free = getFreeMem ();
165+ size = std::max (size, std::min ((size_t ) (free * 0.5 ), maxSize));
85166# endif
86- debug (" setting initial heap size to %1% bytes" , size);
87167 GC_expand_hp (size);
88168 }
89169}
0 commit comments