Skip to content
Snippets Groups Projects
Commit 19cda0fc authored by Georgy Kirichenko's avatar Georgy Kirichenko Committed by Roman Tsisyk
Browse files

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
parent 441a40c4
No related branches found
No related tags found
No related merge requests found
...@@ -80,7 +80,13 @@ tnt_HMAC_CTX_free ...@@ -80,7 +80,13 @@ tnt_HMAC_CTX_free
# Module API # Module API
_say _say
fiber_attr_new
fiber_attr_delete
fiber_attr_setstacksize
fiber_attr_getstacksize
fiber_self
fiber_new fiber_new
fiber_new_ex
fiber_yield fiber_yield
fiber_start fiber_start
fiber_wakeup fiber_wakeup
......
...@@ -86,11 +86,6 @@ pthread_t main_thread_id; ...@@ -86,11 +86,6 @@ pthread_t main_thread_id;
static size_t page_size; static size_t page_size;
static int stack_direction; static int stack_direction;
enum {
/** The number of pages to use for fiber stack */
FIBER_STACK_PAGES = 16,
};
static void static void
update_last_stack_frame(struct fiber *fiber) update_last_stack_frame(struct fiber *fiber)
{ {
...@@ -102,9 +97,74 @@ 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 static void
fiber_recycle(struct fiber *fiber); fiber_recycle(struct fiber *fiber);
static void
fiber_destroy(struct cord *cord, struct fiber *f);
/** /**
* Transfer control to callee fiber. * Transfer control to callee fiber.
*/ */
...@@ -521,6 +581,12 @@ unregister_fid(struct fiber *fiber) ...@@ -521,6 +581,12 @@ unregister_fid(struct fiber *fiber)
mh_i32ptr_remove(cord()->fiber_registry, &node, NULL); mh_i32ptr_remove(cord()->fiber_registry, &node, NULL);
} }
struct fiber *
fiber_self()
{
return fiber();
}
void void
fiber_gc(void) fiber_gc(void)
{ {
...@@ -549,6 +615,7 @@ fiber_recycle(struct fiber *fiber) ...@@ -549,6 +615,7 @@ fiber_recycle(struct fiber *fiber)
assert(diag_is_empty(&fiber->diag)); assert(diag_is_empty(&fiber->diag));
/* no pending wakeup */ /* no pending wakeup */
assert(rlist_empty(&fiber->state)); assert(rlist_empty(&fiber->state));
bool has_custom_stack = fiber->flags & FIBER_CUSTOM_STACK;
fiber_reset(fiber); fiber_reset(fiber);
fiber->gc.name[0] = '\0'; fiber->gc.name[0] = '\0';
fiber->f = NULL; fiber->f = NULL;
...@@ -556,7 +623,11 @@ fiber_recycle(struct fiber *fiber) ...@@ -556,7 +623,11 @@ fiber_recycle(struct fiber *fiber)
unregister_fid(fiber); unregister_fid(fiber);
fiber->fid = 0; fiber->fid = 0;
region_free(&fiber->gc); 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 static void
...@@ -704,24 +775,18 @@ fiber_stack_destroy(struct fiber *fiber, struct slab_cache *slabc) ...@@ -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 * 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 cord *cord = cord();
struct fiber *fiber = NULL; 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, fiber = rlist_first_entry(&cord->dead,
struct fiber, link); struct fiber, link);
rlist_move_entry(&cord->alive, fiber, link); rlist_move_entry(&cord->alive, fiber, link);
...@@ -735,7 +800,7 @@ fiber_new(const char *name, fiber_func f) ...@@ -735,7 +800,7 @@ fiber_new(const char *name, fiber_func f)
} }
memset(fiber, 0, sizeof(struct fiber)); 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); mempool_free(&cord->fiber_mempool, fiber);
return NULL; return NULL;
} }
...@@ -749,6 +814,7 @@ fiber_new(const char *name, fiber_func f) ...@@ -749,6 +814,7 @@ fiber_new(const char *name, fiber_func f)
rlist_create(&fiber->wake); rlist_create(&fiber->wake);
diag_create(&fiber->diag); diag_create(&fiber->diag);
fiber_reset(fiber); fiber_reset(fiber);
fiber->flags = fiber_attr->flags;
rlist_add_entry(&cord->alive, fiber, link); rlist_add_entry(&cord->alive, fiber, link);
} }
...@@ -762,6 +828,24 @@ fiber_new(const char *name, fiber_func f) ...@@ -762,6 +828,24 @@ fiber_new(const char *name, fiber_func f)
register_fid(fiber); register_fid(fiber);
return 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) ...@@ -770,7 +854,7 @@ fiber_new(const char *name, fiber_func f)
* Sic: cord()->sched needs manual destruction in * Sic: cord()->sched needs manual destruction in
* cord_destroy(). * cord_destroy().
*/ */
void static void
fiber_destroy(struct cord *cord, struct fiber *f) fiber_destroy(struct cord *cord, struct fiber *f)
{ {
if (f == fiber()) { if (f == fiber()) {
......
...@@ -85,6 +85,10 @@ enum { ...@@ -85,6 +85,10 @@ enum {
* the fiber is recycled. * the fiber is recycled.
*/ */
FIBER_IS_DEAD = 1 << 4, 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 FIBER_DEFAULT_FLAGS = FIBER_IS_CANCELLABLE
}; };
...@@ -105,6 +109,47 @@ enum fiber_key { ...@@ -105,6 +109,47 @@ enum fiber_key {
/** \cond public */ /** \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; struct fiber;
/** /**
* Fiber - contains information about fiber * Fiber - contains information about fiber
...@@ -112,6 +157,12 @@ struct fiber; ...@@ -112,6 +157,12 @@ struct fiber;
typedef int (*fiber_func)(va_list); typedef int (*fiber_func)(va_list);
/**
* Return the current fiber
*/
API_EXPORT struct fiber *
fiber_self();
/** /**
* Create a new fiber. * Create a new fiber.
* *
...@@ -131,6 +182,25 @@ typedef int (*fiber_func)(va_list); ...@@ -131,6 +182,25 @@ typedef int (*fiber_func)(va_list);
API_EXPORT struct fiber * API_EXPORT struct fiber *
fiber_new(const char *name, fiber_func f); 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. * Return control to another fiber and wait until it'll be woken.
* *
...@@ -244,6 +314,22 @@ cord_slab_cache(void); ...@@ -244,6 +314,22 @@ cord_slab_cache(void);
/** \endcond public */ /** \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 { struct fiber {
coro_context ctx; coro_context ctx;
/** Coro stack slab. */ /** Coro stack slab. */
......
...@@ -46,6 +46,26 @@ cancel_dead_f(va_list ap) ...@@ -46,6 +46,26 @@ cancel_dead_f(va_list ap)
return 0; 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 static void
fiber_join_test() fiber_join_test()
{ {
...@@ -100,6 +120,22 @@ fiber_join_test() ...@@ -100,6 +120,22 @@ fiber_join_test()
fiber_cancel(fiber); fiber_cancel(fiber);
fiber_join(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(); footer();
} }
......
...@@ -4,4 +4,5 @@ SystemError Failed to allocate 42 bytes in allocator for exception: Cannot alloc ...@@ -4,4 +4,5 @@ SystemError Failed to allocate 42 bytes in allocator for exception: Cannot alloc
# exception propagated # exception propagated
# cancel dead has started # cancel dead has started
# by this time the fiber should be dead already # by this time the fiber should be dead already
# big-stack fiber not crashed
*** fiber_join_test: done *** *** fiber_join_test: done ***
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment