From 19cda0fca86d791cf0fe829be607551cd9284423 Mon Sep 17 00:00:00 2001 From: GeorgyKirichenko <kirichenkoga@gmail.com> Date: Mon, 22 May 2017 18:29:38 +0300 Subject: [PATCH] Add fiber attributes Fiber attributes are a way to specify parameters that is different from the default. When a fiber is created using fiber_new_ex(), an attribute object can be specified to configure custom values for some options. Attributes are specified only at fiber creation time; they cannot be altered while the fiber is being used. Currently only stack_size attribute is supported. Fibers with non-default stack size won't be recycled via fiber pool. API overview: * fiber_new_ex() creates a fiber with custom attributes. * fiber_attr_new()/fiber_attr_delete() creates/destroys attributes. * fiber_attr_setstacksize()/fiber_attr_getstacksize() sets/gets the fiber stack size. * fiber_self() returns current running fiber. All new functions are available from public C API for modules. See #2438 --- extra/exports | 6 ++ src/fiber.c | 126 ++++++++++++++++++++++++++++++++++------- src/fiber.h | 86 ++++++++++++++++++++++++++++ test/unit/fiber.cc | 36 ++++++++++++ test/unit/fiber.result | 1 + 5 files changed, 234 insertions(+), 21 deletions(-) diff --git a/extra/exports b/extra/exports index ffbc4de7d6..22ba8e5341 100644 --- a/extra/exports +++ b/extra/exports @@ -80,7 +80,13 @@ tnt_HMAC_CTX_free # Module API _say +fiber_attr_new +fiber_attr_delete +fiber_attr_setstacksize +fiber_attr_getstacksize +fiber_self fiber_new +fiber_new_ex fiber_yield fiber_start fiber_wakeup diff --git a/src/fiber.c b/src/fiber.c index 9366f563fb..9aa8524298 100644 --- a/src/fiber.c +++ b/src/fiber.c @@ -86,11 +86,6 @@ pthread_t main_thread_id; static size_t page_size; static int stack_direction; -enum { - /** The number of pages to use for fiber stack */ - FIBER_STACK_PAGES = 16, -}; - static void update_last_stack_frame(struct fiber *fiber) { @@ -102,9 +97,74 @@ update_last_stack_frame(struct fiber *fiber) } +enum { + /* The minimum allowable fiber stack size in bytes */ + FIBER_STACK_SIZE_MINIMAL = 16384, + /* Default fiber stack size in bytes */ + FIBER_STACK_SIZE_DEFAULT = 65536 +}; + +/** Default fiber attributes */ +static const struct fiber_attr fiber_attr_default = { + .stack_size = FIBER_STACK_SIZE_DEFAULT, + .flags = FIBER_DEFAULT_FLAGS +}; + +void +fiber_attr_create(struct fiber_attr *fiber_attr) +{ + *fiber_attr = fiber_attr_default; +} + +struct fiber_attr * +fiber_attr_new() +{ + struct fiber_attr *fiber_attr = malloc(sizeof(*fiber_attr)); + if (fiber_attr == NULL) { + diag_set(OutOfMemory, sizeof(*fiber_attr), + "runtime", "fiber attr"); + return NULL; + } + fiber_attr_create(fiber_attr); + return fiber_attr; +} + +void +fiber_attr_delete(struct fiber_attr *fiber_attr) +{ + free(fiber_attr); +} + +int +fiber_attr_setstacksize(struct fiber_attr *fiber_attr, size_t stack_size) +{ + if (stack_size < FIBER_STACK_SIZE_MINIMAL) { + errno = EINVAL; + diag_set(SystemError, "stack size is too small"); + return -1; + } + fiber_attr->stack_size = stack_size; + if (stack_size != FIBER_STACK_SIZE_DEFAULT) { + fiber_attr->flags |= FIBER_CUSTOM_STACK; + } else { + fiber_attr->flags &= ~FIBER_CUSTOM_STACK; + } + return 0; +} + +size_t +fiber_attr_getstacksize(struct fiber_attr *fiber_attr) +{ + return fiber_attr != NULL ? fiber_attr->stack_size : + fiber_attr_default.stack_size; +} + static void fiber_recycle(struct fiber *fiber); +static void +fiber_destroy(struct cord *cord, struct fiber *f); + /** * Transfer control to callee fiber. */ @@ -521,6 +581,12 @@ unregister_fid(struct fiber *fiber) mh_i32ptr_remove(cord()->fiber_registry, &node, NULL); } +struct fiber * +fiber_self() +{ + return fiber(); +} + void fiber_gc(void) { @@ -549,6 +615,7 @@ fiber_recycle(struct fiber *fiber) assert(diag_is_empty(&fiber->diag)); /* no pending wakeup */ assert(rlist_empty(&fiber->state)); + bool has_custom_stack = fiber->flags & FIBER_CUSTOM_STACK; fiber_reset(fiber); fiber->gc.name[0] = '\0'; fiber->f = NULL; @@ -556,7 +623,11 @@ fiber_recycle(struct fiber *fiber) unregister_fid(fiber); fiber->fid = 0; region_free(&fiber->gc); - rlist_move_entry(&cord()->dead, fiber, link); + if (!has_custom_stack) { + rlist_move_entry(&cord()->dead, fiber, link); + } else { + fiber_destroy(cord(), fiber); + } } static void @@ -704,24 +775,18 @@ fiber_stack_destroy(struct fiber *fiber, struct slab_cache *slabc) } } -/** - * Create a new fiber. - * - * Takes a fiber from fiber cache, if it's not empty. - * Can fail only if there is not enough memory for - * the fiber structure or fiber stack. - * - * The created fiber automatically returns itself - * to the fiber cache when its "main" function - * completes. - */ struct fiber * -fiber_new(const char *name, fiber_func f) +fiber_new_ex(const char *name, const struct fiber_attr *fiber_attr, + fiber_func f) { struct cord *cord = cord(); struct fiber *fiber = NULL; + if (fiber_attr == NULL) + fiber_attr = &fiber_attr_default; - if (! rlist_empty(&cord->dead)) { + /* Now we can not reuse fiber if custom attribute was set */ + if (!(fiber_attr->flags & FIBER_CUSTOM_STACK) && + !rlist_empty(&cord->dead)) { fiber = rlist_first_entry(&cord->dead, struct fiber, link); rlist_move_entry(&cord->alive, fiber, link); @@ -735,7 +800,7 @@ fiber_new(const char *name, fiber_func f) } memset(fiber, 0, sizeof(struct fiber)); - if (fiber_stack_create(fiber, FIBER_STACK_PAGES * page_size)) { + if (fiber_stack_create(fiber, fiber_attr->stack_size)) { mempool_free(&cord->fiber_mempool, fiber); return NULL; } @@ -749,6 +814,7 @@ fiber_new(const char *name, fiber_func f) rlist_create(&fiber->wake); diag_create(&fiber->diag); fiber_reset(fiber); + fiber->flags = fiber_attr->flags; rlist_add_entry(&cord->alive, fiber, link); } @@ -762,6 +828,24 @@ fiber_new(const char *name, fiber_func f) register_fid(fiber); return fiber; + +} + +/** + * Create a new fiber. + * + * Takes a fiber from fiber cache, if it's not empty. + * Can fail only if there is not enough memory for + * the fiber structure or fiber stack. + * + * The created fiber automatically returns itself + * to the fiber cache when its "main" function + * completes. + */ +struct fiber * +fiber_new(const char *name, fiber_func f) +{ + return fiber_new_ex(name, NULL, f); } /** @@ -770,7 +854,7 @@ fiber_new(const char *name, fiber_func f) * Sic: cord()->sched needs manual destruction in * cord_destroy(). */ -void +static void fiber_destroy(struct cord *cord, struct fiber *f) { if (f == fiber()) { diff --git a/src/fiber.h b/src/fiber.h index 4398419c9f..b6add8a25d 100644 --- a/src/fiber.h +++ b/src/fiber.h @@ -85,6 +85,10 @@ enum { * the fiber is recycled. */ FIBER_IS_DEAD = 1 << 4, + /** + * This flag is set when fiber uses custom stack size. + */ + FIBER_CUSTOM_STACK = 1 << 5, FIBER_DEFAULT_FLAGS = FIBER_IS_CANCELLABLE }; @@ -105,6 +109,47 @@ enum fiber_key { /** \cond public */ +/** + * Fiber attributes container + */ +struct fiber_attr; + +/** + * Create a new fiber attribute container and initialize it + * with default parameters. + * Can be used for many fibers creation, corresponding fibers + * will not take ownership. + */ +API_EXPORT struct fiber_attr * +fiber_attr_new(); + +/** + * Delete the fiber_attr and free all allocated resources. + * This is safe when fibers created with this attribute still exist. + * + *\param fiber_attr fiber attribute + */ +API_EXPORT void +fiber_attr_delete(struct fiber_attr *fiber_attr); + +/** + * Set stack size for the fiber attribute. + * + * \param fiber_attribute fiber attribute container + * \param stacksize stack size for new fibers + */ +API_EXPORT int +fiber_attr_setstacksize(struct fiber_attr *fiber_attr, size_t stack_size); + +/** + * Get stack size from the fiber attribute. + * + * \param fiber_attribute fiber attribute container or NULL for default + * \retval stack size + */ +API_EXPORT size_t +fiber_attr_getstacksize(struct fiber_attr *fiber_attr); + struct fiber; /** * Fiber - contains information about fiber @@ -112,6 +157,12 @@ struct fiber; typedef int (*fiber_func)(va_list); +/** + * Return the current fiber + */ +API_EXPORT struct fiber * +fiber_self(); + /** * Create a new fiber. * @@ -131,6 +182,25 @@ typedef int (*fiber_func)(va_list); API_EXPORT struct fiber * fiber_new(const char *name, fiber_func f); +/** + * Create a new fiber with defined attributes. + * + * Can fail only if there is not enough memory for + * the fiber structure or fiber stack. + * + * The created fiber automatically returns itself + * to the fiber cache if has default stack size + * when its "main" function completes. + * + * \param name string with fiber name + * \param fiber_attr fiber attributes + * \param fiber_func func for run inside fiber + * + * \sa fiber_start + */ +API_EXPORT struct fiber * +fiber_new_ex(const char *name, const struct fiber_attr *fiber_attr, fiber_func f); + /** * Return control to another fiber and wait until it'll be woken. * @@ -244,6 +314,22 @@ cord_slab_cache(void); /** \endcond public */ +/** + * Fiber attribute container + */ +struct fiber_attr { + /** Fiber stack size. */ + size_t stack_size; + /** Fiber flags. */ + uint32_t flags; +}; + +/** + * Init fiber attr with default values + */ +void +fiber_attr_create(struct fiber_attr *fiber_attr); + struct fiber { coro_context ctx; /** Coro stack slab. */ diff --git a/test/unit/fiber.cc b/test/unit/fiber.cc index b8341c0c8a..eee80df6ce 100644 --- a/test/unit/fiber.cc +++ b/test/unit/fiber.cc @@ -46,6 +46,26 @@ cancel_dead_f(va_list ap) return 0; } +static size_t fiber_stack_size_default; + +static void +stack_expand(void *ptr) +{ + char buf[2048]; + memset(buf, 0, 2048); + long int stack_diff = (long int)(buf - (char *)ptr); + if (abs(stack_diff) < (long int)fiber_stack_size_default) + stack_expand(ptr); +} + +static int +test_stack_f(va_list ap) +{ + char s; + stack_expand(&s); + return 0; +} + static void fiber_join_test() { @@ -100,6 +120,22 @@ fiber_join_test() fiber_cancel(fiber); fiber_join(fiber); + struct fiber_attr *fiber_attr; + fiber_attr = fiber_attr_new(); + fiber_stack_size_default = fiber_attr_getstacksize(fiber_attr); + fiber_attr_setstacksize(fiber_attr, fiber_stack_size_default * 2); + fiber = fiber_new_ex("test_stack", fiber_attr, test_stack_f); + fiber_attr_delete(fiber_attr); + if (fiber == NULL) + diag_raise(); + fiber_set_joinable(fiber, true); + fiber_wakeup(fiber); + /** Let the fiber schedule */ + fiber_wakeup(fiber()); + fiber_yield(); + note("big-stack fiber not crashed"); + fiber_join(fiber); + footer(); } diff --git a/test/unit/fiber.result b/test/unit/fiber.result index 3e8bd039a1..2cd3fbc23d 100644 --- a/test/unit/fiber.result +++ b/test/unit/fiber.result @@ -4,4 +4,5 @@ SystemError Failed to allocate 42 bytes in allocator for exception: Cannot alloc # exception propagated # cancel dead has started # by this time the fiber should be dead already +# big-stack fiber not crashed *** fiber_join_test: done *** -- GitLab