summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAustin Seipp <aseipp@pobox.com>2013-09-08 02:08:45 -0500
committerAustin Seipp <aseipp@pobox.com>2013-09-08 03:55:06 -0500
commitd85044f6b201eae0a9e453b89c0433608e0778f0 (patch)
tree2ef70aed38ac9ee9b0c7dc3f1cdf8a8e79091c11
parentc73d372bfebb5acee45e196d4e8694b656c7fd82 (diff)
downloadhaskell-d85044f6b201eae0a9e453b89c0433608e0778f0.tar.gz
Default to infinite stack size (#8189)
When servicing a stack overflows, only throw an exception to the given thread if the user explicitly set a max stack size, using +RTS -K. Otherwise just service it normally and grow the stack. In case we actually run out of *heap* (stack chuncks are allocated on the heap), then we need to bail by calling the stackOverflow() hook and exit immediately. Authored-by: Ben Gamari <bgamari.foss@gmail.com> Signed-off-by: Austin Seipp <aseipp@pobox.com>
-rw-r--r--docs/users_guide/runtime_control.xml11
-rw-r--r--includes/Rts.h3
-rw-r--r--includes/rts/Constants.h2
-rw-r--r--includes/rts/storage/GC.h13
-rw-r--r--rts/RtsFlags.c4
-rw-r--r--rts/RtsUtils.c6
-rw-r--r--rts/Schedule.c2
-rw-r--r--rts/Threads.c10
-rw-r--r--rts/sm/Storage.c55
9 files changed, 73 insertions, 33 deletions
diff --git a/docs/users_guide/runtime_control.xml b/docs/users_guide/runtime_control.xml
index 3e3ae3f7d2..8044d9ccb3 100644
--- a/docs/users_guide/runtime_control.xml
+++ b/docs/users_guide/runtime_control.xml
@@ -676,10 +676,13 @@ $ ./prog -f +RTS -H32m -S -RTS -h foo bar
<indexterm><primary>stack, maximum size</primary></indexterm>
</term>
<listitem>
- <para>&lsqb;Default: 8M&rsqb; Set the maximum stack size for
- an individual thread to <replaceable>size</replaceable>
- bytes. If the thread attempts to exceed this limit, it will
- be send the <literal>StackOverflow</literal> exception.
+ <para>
+ &lsqb;Default: infinite&rsqb; Set the maximum stack
+ size for an individual thread to
+ <replaceable>size</replaceable> bytes. A setting of zero
+ implies no maximum stack size limit. If the thread
+ attempts to exceed this limit, it will be sent the
+ <literal>StackOverflow</literal> exception.
</para>
<para>
This option is there mainly to stop the program eating up
diff --git a/includes/Rts.h b/includes/Rts.h
index 122637c465..ee10a1044b 100644
--- a/includes/Rts.h
+++ b/includes/Rts.h
@@ -250,7 +250,7 @@ void getWin32ProgArgv(int *argc, wchar_t **argv[]);
void setWin32ProgArgv(int argc, wchar_t *argv[]);
#endif
-void stackOverflow(void);
+void stackOverflow(StgTSO* tso);
void stg_exit(int n) GNU_ATTRIBUTE(__noreturn__);
@@ -268,6 +268,7 @@ int stg_sig_install (int, int, void *);
#define EXIT_INTERRUPTED 252
#define EXIT_HEAPOVERFLOW 251
#define EXIT_KILLED 250
+#define EXIT_STACKOVERFLOW 249
/* -----------------------------------------------------------------------------
Miscellaneous garbage
diff --git a/includes/rts/Constants.h b/includes/rts/Constants.h
index 494abe2b22..deb550ee97 100644
--- a/includes/rts/Constants.h
+++ b/includes/rts/Constants.h
@@ -234,7 +234,7 @@
* stopped for one reason or another. See typedef StgThreadReturnCode
* in TSO.h.
*/
-#define HeapOverflow 1 /* might also be StackOverflow */
+#define HeapOverflow 1
#define StackOverflow 2
#define ThreadYielding 3
#define ThreadBlocked 4
diff --git a/includes/rts/storage/GC.h b/includes/rts/storage/GC.h
index fb5e21e832..07bc65128f 100644
--- a/includes/rts/storage/GC.h
+++ b/includes/rts/storage/GC.h
@@ -130,9 +130,15 @@ extern generation * oldest_gen;
StgPtr allocate(Capability *cap, W_ n)
Allocates memory from the nursery in
- the current Capability. This can be
- done without taking a global lock,
- unlike allocate().
+ the current Capability. This can be
+ done without taking a global lock,
+ unlike allocate(). In the event of a
+ heap overflow the program will be
+ terminated.
+
+ StgPtr allocateFail(Capability *cap, W_ n)
+ Similar to allocate() but returns NULL
+ in the event of a heap overflow.
StgPtr allocatePinned(Capability *cap, W_ n)
Allocates a chunk of contiguous store
@@ -154,6 +160,7 @@ extern generation * oldest_gen;
-------------------------------------------------------------------------- */
StgPtr allocate ( Capability *cap, W_ n );
+StgPtr allocateFail ( Capability *cap, W_ n );
StgPtr allocatePinned ( Capability *cap, W_ n );
/* memory allocator for executable memory */
diff --git a/rts/RtsFlags.c b/rts/RtsFlags.c
index 1e541a0201..46a8462dca 100644
--- a/rts/RtsFlags.c
+++ b/rts/RtsFlags.c
@@ -94,7 +94,7 @@ void initRtsFlagsDefaults(void)
RtsFlags.GcFlags.statsFile = NULL;
RtsFlags.GcFlags.giveStats = NO_GC_STATS;
- RtsFlags.GcFlags.maxStkSize = (8 * 1024 * 1024) / sizeof(W_);
+ RtsFlags.GcFlags.maxStkSize = 0; /* off by default */
RtsFlags.GcFlags.initialStkSize = 1024 / sizeof(W_);
RtsFlags.GcFlags.stkChunkSize = (32 * 1024) / sizeof(W_);
RtsFlags.GcFlags.stkChunkBufferSize = (1 * 1024) / sizeof(W_);
@@ -233,7 +233,7 @@ usage_text[] = {
" -? Prints this message and exits; the program is not executed",
" --info Print information about the RTS used by this program",
"",
-" -K<size> Sets the maximum stack size (default 8M) Egs: -K32k -K512k",
+" -K<size> Sets the maximum stack size (defaults to infinite) Egs: -K32k -K512k",
" -ki<size> Sets the initial thread stack size (default 1k) Egs: -ki4k -ki2m",
" -kc<size> Sets the stack chunk size (default 32k)",
" -kb<size> Sets the stack chunk buffer size (default 1k)",
diff --git a/rts/RtsUtils.c b/rts/RtsUtils.c
index cb9002c361..604c7eed61 100644
--- a/rts/RtsUtils.c
+++ b/rts/RtsUtils.c
@@ -114,12 +114,12 @@ stgFree(void* p)
-------------------------------------------------------------------------- */
void
-stackOverflow(void)
+stackOverflow(StgTSO* tso)
{
- StackOverflowHook(RtsFlags.GcFlags.maxStkSize * sizeof(W_));
+ StackOverflowHook(tso->tot_stack_size * sizeof(W_));
#if defined(TICKY_TICKY)
- if (RtsFlags.TickyFlags.showTickyStats) PrintTickyInfo();
+ if (RtsFlags.TickyFlags.showTickyStats) PrintTickyInfo();
#endif
}
diff --git a/rts/Schedule.c b/rts/Schedule.c
index 07ebec62b1..20077b1d53 100644
--- a/rts/Schedule.c
+++ b/rts/Schedule.c
@@ -393,7 +393,7 @@ schedule (Capability *initialCapability, Task *task)
run_thread:
- // CurrentTSO is the thread to run. t might be different if we
+ // CurrentTSO is the thread to run. It might be different if we
// loop back to run_thread, so make sure to set CurrentTSO after
// that.
cap->r.rCurrentTSO = t;
diff --git a/rts/Threads.c b/rts/Threads.c
index 14fb7e872c..90d3dfd115 100644
--- a/rts/Threads.c
+++ b/rts/Threads.c
@@ -499,7 +499,8 @@ threadStackOverflow (Capability *cap, StgTSO *tso)
IF_DEBUG(sanity,checkTSO(tso));
- if (tso->tot_stack_size >= RtsFlags.GcFlags.maxStkSize
+ if ( (RtsFlags.GcFlags.maxStkSize > 0) // don't throw if we have infinite stack
+ && (tso->tot_stack_size >= RtsFlags.GcFlags.maxStkSize)
&& !(tso->flags & TSO_BLOCKEX)) {
// NB. never raise a StackOverflow exception if the thread is
// inside Control.Exceptino.block. It is impractical to protect
@@ -575,7 +576,12 @@ threadStackOverflow (Capability *cap, StgTSO *tso)
"allocating new stack chunk of size %d bytes",
chunk_size * sizeof(W_));
- new_stack = (StgStack*) allocate(cap, chunk_size);
+ new_stack = (StgStack*) allocateFail(cap, chunk_size);
+ if (new_stack == NULL) {
+ // We've really run out of memory in the heap, so die.
+ stackOverflow(tso);
+ stg_exit(EXIT_STACKOVERFLOW);
+ }
SET_HDR(new_stack, &stg_STACK_info, old_stack->header.prof.ccs);
TICK_ALLOC_STACK(chunk_size);
diff --git a/rts/sm/Storage.c b/rts/sm/Storage.c
index b575fc3e52..df088132e3 100644
--- a/rts/sm/Storage.c
+++ b/rts/sm/Storage.c
@@ -624,27 +624,27 @@ move_STACK (StgStack *src, StgStack *dest)
}
/* -----------------------------------------------------------------------------
- allocate()
+ allocateFail()
This allocates memory in the current thread - it is intended for
- use primarily from STG-land where we have a Capability. It is
- better than allocate() because it doesn't require taking the
- sm_mutex lock in the common case.
+ use primarily from STG-land where we have a Capability.
Memory is allocated directly from the nursery if possible (but not
from the current nursery block, so as not to interfere with
Hp/HpLim).
+
+ We return NULL in the event of a heap overflow.
-------------------------------------------------------------------------- */
StgPtr
-allocate (Capability *cap, W_ n)
+allocateFail (Capability *cap, W_ n)
{
bdescr *bd;
StgPtr p;
TICK_ALLOC_HEAP_NOCTR(WDS(n));
CCS_ALLOC(cap->r.rCCCS,n);
-
+
if (n >= LARGE_OBJECT_THRESHOLD/sizeof(W_)) {
W_ req_blocks = (W_)BLOCK_ROUND_UP(n*sizeof(W_)) / BLOCK_SIZE;
@@ -655,14 +655,7 @@ allocate (Capability *cap, W_ n)
req_blocks >= HS_INT32_MAX) // avoid overflow when
// calling allocGroup() below
{
- heapOverflow();
- // heapOverflow() doesn't exit (see #2592), but we aren't
- // in a position to do a clean shutdown here: we
- // either have to allocate the memory or exit now.
- // Allocating the memory would be bad, because the user
- // has requested that we not exceed maxHeapSize, so we
- // just exit.
- stg_exit(EXIT_HEAPOVERFLOW);
+ return NULL; // heap overflow
}
ACQUIRE_SM_LOCK
@@ -682,12 +675,12 @@ allocate (Capability *cap, W_ n)
bd = cap->r.rCurrentAlloc;
if (bd == NULL || bd->free + n > bd->start + BLOCK_SIZE_W) {
-
+
// The CurrentAlloc block is full, we need to find another
// one. First, we try taking the next block from the
// nursery:
bd = cap->r.rCurrentNursery->link;
-
+
if (bd == NULL || bd->free + n > bd->start + BLOCK_SIZE_W) {
// The nursery is empty, or the next block is already
// full: allocate a fresh block (we can't fail here).
@@ -720,6 +713,36 @@ allocate (Capability *cap, W_ n)
return p;
}
+/* -----------------------------------------------------------------------------
+ allocate()
+
+ This allocates memory in the current thread - it is intended for
+ use primarily from STG-land where we have a Capability.
+
+ Memory is allocated directly from the nursery if possible (but not
+ from the current nursery block, so as not to interfere with
+ Hp/HpLim).
+
+ We crash with a HeapOverflow when the allocation fails.
+ -------------------------------------------------------------------------- */
+
+StgPtr
+allocate (Capability *cap, W_ n)
+{
+ StgPtr p = allocateFail(cap, n);
+ if (p == NULL) {
+ heapOverflow();
+ // heapOverflow() doesn't exit (see #2592), but we aren't
+ // in a position to do a clean shutdown here: we
+ // either have to allocate the memory or exit now.
+ // Allocating the memory would be bad, because the user
+ // has requested that we not exceed maxHeapSize, so we
+ // just exit.
+ stg_exit(EXIT_HEAPOVERFLOW);
+ }
+ return p;
+}
+
/* ---------------------------------------------------------------------------
Allocate a fixed/pinned object.