diff options
author | Simon Marlow <marlowsd@gmail.com> | 2014-04-28 16:55:47 +0100 |
---|---|---|
committer | Simon Marlow <marlowsd@gmail.com> | 2014-05-02 14:49:22 +0100 |
commit | b0534f78a73f972e279eed4447a5687bd6a8308e (patch) | |
tree | 02d52756620bf27b9df9db45c57dacf55f190842 /rts/Threads.c | |
parent | 34db5ccf52ec2a1b5e953c282d0c52a7fc82c02a (diff) | |
download | haskell-b0534f78a73f972e279eed4447a5687bd6a8308e.tar.gz |
Per-thread allocation counters and limits
This tracks the amount of memory allocation by each thread in a
counter stored in the TSO. Optionally, when the counter drops below
zero (it counts down), the thread can be sent an asynchronous
exception: AllocationLimitExceeded. When this happens, given a small
additional limit so that it can handle the exception. See
documentation in GHC.Conc for more details.
Allocation limits are similar to timeouts, but
- timeouts use real time, not CPU time. Allocation limits do not
count anything while the thread is blocked or in foreign code.
- timeouts don't re-trigger if the thread catches the exception,
allocation limits do.
- timeouts can catch non-allocating loops, if you use
-fno-omit-yields. This doesn't work for allocation limits.
I couldn't measure any impact on benchmarks with these changes, even
for nofib/smp.
Diffstat (limited to 'rts/Threads.c')
-rw-r--r-- | rts/Threads.c | 77 |
1 files changed, 29 insertions, 48 deletions
diff --git a/rts/Threads.c b/rts/Threads.c index af4353fc49..b82295284b 100644 --- a/rts/Threads.c +++ b/rts/Threads.c @@ -110,6 +110,8 @@ createThread(Capability *cap, W_ size) tso->stackobj = stack; tso->tot_stack_size = stack->stack_size; + tso->alloc_limit = 0; + tso->trec = NO_TREC; #ifdef PROFILING @@ -164,6 +166,31 @@ rts_getThreadId(StgPtr tso) return ((StgTSO *)tso)->id; } +/* --------------------------------------------------------------------------- + * Getting & setting the thread allocation limit + * ------------------------------------------------------------------------ */ +HsInt64 rts_getThreadAllocationCounter(StgPtr tso) +{ + // NB. doesn't take into account allocation in the current nursery + // block, so it might be off by up to 4k. + return ((StgTSO *)tso)->alloc_limit; +} + +void rts_setThreadAllocationCounter(StgPtr tso, HsInt64 i) +{ + ((StgTSO *)tso)->alloc_limit = i; +} + +void rts_enableThreadAllocationLimit(StgPtr tso) +{ + ((StgTSO *)tso)->flags |= TSO_ALLOC_LIMIT; +} + +void rts_disableThreadAllocationLimit(StgPtr tso) +{ + ((StgTSO *)tso)->flags &= ~TSO_ALLOC_LIMIT; +} + /* ----------------------------------------------------------------------------- Remove a thread from a queue. Fails fatally if the TSO is not on the queue. @@ -524,21 +551,8 @@ threadStackOverflow (Capability *cap, StgTSO *tso) stg_min(tso->stackobj->stack + tso->stackobj->stack_size, tso->stackobj->sp+64))); - if (tso->flags & TSO_BLOCKEX) { - // NB. StackOverflow exceptions must be deferred if the thread is - // inside Control.Exception.mask. See bug #767 and bug #8303. - // This implementation is a minor hack, see Note [Throw to self when masked] - MessageThrowTo *msg = (MessageThrowTo*)allocate(cap, sizeofW(MessageThrowTo)); - SET_HDR(msg, &stg_MSG_THROWTO_info, CCS_SYSTEM); - msg->source = tso; - msg->target = tso; - msg->exception = (StgClosure *)stackOverflow_closure; - blockedThrowTo(cap, tso, msg); - } else { - // Send this thread the StackOverflow exception - throwToSingleThreaded(cap, tso, (StgClosure *)stackOverflow_closure); - return; - } + // Note [Throw to self when masked], also #767 and #8303. + throwToSelf(cap, tso, (StgClosure *)stackOverflow_closure); } @@ -669,39 +683,6 @@ threadStackOverflow (Capability *cap, StgTSO *tso) // IF_DEBUG(scheduler,printTSO(new_tso)); } -/* Note [Throw to self when masked] - * - * When a StackOverflow occurs when the thread is masked, we want to - * defer the exception to when the thread becomes unmasked/hits an - * interruptible point. We already have a mechanism for doing this, - * the blocked_exceptions list, but the use here is a bit unusual, - * because an exception is normally only added to this list upon - * an asynchronous 'throwTo' call (with all of the relevant - * multithreaded nonsense). Morally, a stack overflow should be an - * asynchronous exception sent by a thread to itself, and it should - * have the same semantics. But there are a few key differences: - * - * - If you actually tried to send an asynchronous exception to - * yourself using throwTo, the exception would actually immediately - * be delivered. This is because throwTo itself is considered an - * interruptible point, so the exception is always deliverable. Thus, - * ordinarily, we never end up with a message to onesself in the - * blocked_exceptions queue. - * - * - In the case of a StackOverflow, we don't actually care about the - * wakeup semantics; when an exception is delivered, the thread that - * originally threw the exception should be woken up, since throwTo - * blocks until the exception is successfully thrown. Fortunately, - * it is harmless to wakeup a thread that doesn't actually need waking - * up, e.g. ourselves. - * - * - No synchronization is necessary, because we own the TSO and the - * capability. You can observe this by tracing through the execution - * of throwTo. We skip synchronizing the message and inter-capability - * communication. - * - * We think this doesn't break any invariants, but do be careful! - */ /* --------------------------------------------------------------------------- |