From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Return-Path: Message-Id: <20190302130034.125890957@gmail.com> Date: Sat, 02 Mar 2019 15:55:28 +0300 From: Cyrill Gorcunov Subject: [rfc 4/4] core/fiber: Shrink stack when recycling References: <20190302125524.279852704@gmail.com> MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Disposition: inline; filename=fiber-stack-wmark-dyna-2 To: tarantool-patches@freelists.org Cc: gorcunov@gmail.com, vdavydov.dev@gmail.com List-ID: Currently we've a static mark in stack to detect the situation where its usage is close to exhausting. We can move forward and try to shrink stack everytime it is recycling. For this sake we add wmark_addr pointing into a fiber and put it next to the last page on the stack. On recycle we test if mark has been modified. If it is still present we wimply shrink stack by one page and continue this way until stack usage is balanced. Once we met first overwrite we stop tracking and freeze the mark to not longer do any access assuming stack won't get bigger. Note that "stack is close to overflow" is still present since it is separate mechanism. Closes #3418 --- src/lib/core/fiber.c | 65 +++++++++++++++++++++++++++++++++++++++++++-------- src/lib/core/fiber.h | 2 + 2 files changed, 57 insertions(+), 10 deletions(-) Index: tarantool.git/src/lib/core/fiber.c =================================================================== --- tarantool.git.orig/src/lib/core/fiber.c +++ tarantool.git/src/lib/core/fiber.c @@ -124,6 +124,9 @@ static const uint64_t poison_pool[] = { #define POISON_GAP (128 + sizeof(poison_pool[0])) #define POISON_OFF (POISON_GAP / sizeof(poison_pool[0])) +#define wmark_freeze(_pp) do { *((uintptr_t *)(_pp)) |= (uintptr_t)1; } while (0) +#define wmark_frozen(_p) ((uintptr_t)(_p) & (uintptr_t)1) + static void fiber_wmark_recycle(struct fiber *fiber); void @@ -763,6 +766,31 @@ stack_put_wmark(void *addr) } static void +stack_shrink(struct fiber *fiber) +{ + void *hi, *lo; + + if (stack_direction < 0) { + hi = fiber->stack + fiber->stack_size; + lo = fiber->stack_wmark_ofl + page_size; + } else { + hi = fiber->stack_wmark_ofl - page_size; + lo = fiber->stack - fiber->stack_size; + } + + if (fiber->stack_wmark <= lo || + fiber->stack_wmark >= hi) + return; + + madvise(fiber->stack_wmark, page_size, MADV_DONTNEED); + if (stack_direction < 0) + fiber->stack_wmark += page_size; + else + fiber->stack_wmark -= page_size; + stack_put_wmark(fiber); +} + +static void fiber_wmark_recycle(struct fiber *fiber) { static bool overflow_warned = false; @@ -772,20 +800,30 @@ fiber_wmark_recycle(struct fiber *fiber) /* * We are watching for stack overflow in one shot way: - * firstly to not spam a user with messages and secondly - * to relax system from additional operations while our - * watermark is not dynamic. + * to not spam a user with messages. */ - if (overflow_warned) - return; - - if (!stack_has_wmark(fiber->stack_wmark_ofl)) { - if (!overflow_warned) { + if (!overflow_warned) { + if (!stack_has_wmark(fiber->stack_wmark_ofl)) { say_warn("fiber %d is close to a stack limit", fiber->name, fiber->fid); overflow_warned = true; } } + + if (wmark_frozen(fiber->stack_wmark)) + return; + + /* + * On recycle we're trying to shrink stack + * as much as we can until first mark overwrite + * detected, then we simply freeze watermark and + * assume the stack is balanced and won't change + * much in future. + */ + if (!stack_has_wmark(fiber->stack_wmark)) + wmark_freeze(&fiber->stack_wmark); + else + stack_shrink(fiber); } static void @@ -797,6 +835,7 @@ fiber_wmark_init(struct fiber *fiber) */ if (fiber->flags & FIBER_CUSTOM_STACK) { fiber->stack_wmark_ofl = NULL; + fiber->stack_wmark = NULL; return; } @@ -805,11 +844,15 @@ fiber_wmark_init(struct fiber *fiber) * to catch if we're getting close to * stack exhausting. */ - if (stack_direction < 0) + if (stack_direction < 0) { fiber->stack_wmark_ofl = fiber->stack; - else + fiber->stack_wmark = fiber->stack_wmark_ofl + page_size; + } else { fiber->stack_wmark_ofl = fiber->stack + fiber->stack_size - page_size; + fiber->stack_wmark = fiber->stack_wmark_ofl - page_size; + } stack_put_wmark(fiber->stack_wmark_ofl); + stack_put_wmark(fiber->stack_wmark); } static int @@ -1030,9 +1073,11 @@ cord_create(struct cord *cord, const cha tt_pthread_attr_getstack(cord->id, &cord->sched.stack, &cord->sched.stack_size); cord->sched.stack_wmark_ofl = cord->sched.stack; + cord->sched.stack_wmark = cord->sched.stack; #else cord->sched.stack = NULL; cord->sched.stack_wmark_ofl = NULL; + cord->sched.stack_wmark = NULL; cord->sched.stack_size = 0; #endif } Index: tarantool.git/src/lib/core/fiber.h =================================================================== --- tarantool.git.orig/src/lib/core/fiber.h +++ tarantool.git/src/lib/core/fiber.h @@ -348,6 +348,8 @@ struct fiber { struct slab *stack_slab; /** Coro stack addr. */ void *stack; + /** Stack watermark addr. */ + void *stack_wmark; /** Stack watermark addr for overflow detection. */ void *stack_wmark_ofl; /** Coro stack size. */