diff --git a/configs/stasis.conf.sample b/configs/stasis.conf.sample new file mode 100644 index 0000000000000000000000000000000000000000..c6466b489c99e631e4c96ca03adb56939bb31a76 --- /dev/null +++ b/configs/stasis.conf.sample @@ -0,0 +1,8 @@ +[threadpool] +;initial_size = 0 ; Initial size of the threadpool + +;idle_timeout_sec = 20 ; Number of seconds a thread should be idle before dying +; ; 0 means threads never time out + +;max_size = 200 ; Maximum number of threads in the threadpool +; ; 0 means no limit to the threads in the threadpool diff --git a/configs/stasis_core.conf.sample b/configs/stasis_core.conf.sample deleted file mode 100644 index 840b13ae1cd4d208f003437e3198f9653ad3578a..0000000000000000000000000000000000000000 --- a/configs/stasis_core.conf.sample +++ /dev/null @@ -1,6 +0,0 @@ -[general] - -[threadpool] -;initial_size = 0 ; Initial size of the threadpool -;idle_timeout = 20 ; Number of seconds a thread should be idle before dying -;max_size = 200 ; Maximum number of threads in the threadpool diff --git a/include/asterisk/stasis.h b/include/asterisk/stasis.h index fd51cf51da374b4293a29ae1fe6ef088e25ca717..fd60724c08e5d683cb54987b28136c46a3f36db2 100644 --- a/include/asterisk/stasis.h +++ b/include/asterisk/stasis.h @@ -809,15 +809,19 @@ void stasis_log_bad_type_access(const char *name); /*! @{ */ /*! - * \brief Initialize the Stasis subsystem + * \brief Initialize the Stasis subsystem. * \return 0 on success. * \return Non-zero on error. * \since 12 */ int stasis_init(void); +/*! @} */ + +/*! @{ */ + /*! - * \private + * \internal * \brief called by stasis_init() for cache initialization. * \return 0 on success. * \return Non-zero on error. @@ -825,6 +829,25 @@ int stasis_init(void); */ int stasis_cache_init(void); +/*! + * \internal + * \brief called by stasis_init for config initialization. + * \return 0 on success. + * \return Non-zero on error. + * \since 12 + */ +int stasis_config_init(void); + +struct ast_threadpool_options; + +/*! + * \internal + * \brief Retrieves the Stasis threadpool configuration. + * \param[out] threadpool_options Filled with Stasis threadpool options. + */ +void stasis_config_get_threadpool_options( + struct ast_threadpool_options *threadpool_options); + /*! @} */ /*! diff --git a/main/asterisk.c b/main/asterisk.c index c53652aafd1889f30683cb32353e6de4e4e52f6b..c1ce2c0f187a32bfd070a7b744c525bcce49f9f8 100644 --- a/main/asterisk.c +++ b/main/asterisk.c @@ -4202,6 +4202,13 @@ int main(int argc, char *argv[]) exit(1); } +#ifdef AST_XML_DOCS + /* Load XML documentation. */ + ast_xmldoc_load_documentation(); +#endif + + aco_init(); + if (stasis_init()) { printf("Stasis initialization failed.\n%s", term_quit()); exit(1); @@ -4261,13 +4268,6 @@ int main(int argc, char *argv[]) exit(1); } -#ifdef AST_XML_DOCS - /* Load XML documentation. */ - ast_xmldoc_load_documentation(); -#endif - - aco_init(); - if (app_init()) { printf("App core initialization failed.\n%s", term_quit()); exit(1); diff --git a/main/stasis.c b/main/stasis.c index 406a1bb254b23cd778a9227921237398b14b9386..64f77e309145dcddbfcc6e37867c6b8433944950 100644 --- a/main/stasis.c +++ b/main/stasis.c @@ -642,24 +642,25 @@ int stasis_init(void) { int cache_init; - /* XXX Should this be configurable? */ - struct ast_threadpool_options opts = { - .version = AST_THREADPOOL_OPTIONS_VERSION, - .idle_timeout = 20, - .auto_increment = 1, - .initial_size = 0, - .max_size = 200 - }; + struct ast_threadpool_options opts; /* Be sure the types are cleaned up after the message bus */ ast_register_cleanup(stasis_cleanup); ast_register_atexit(stasis_exit); + if (stasis_config_init() != 0) { + ast_log(LOG_ERROR, "Stasis configuration failed\n"); + return -1; + } + if (pool) { ast_log(LOG_ERROR, "Stasis double-initialized\n"); return -1; } + stasis_config_get_threadpool_options(&opts); + ast_debug(3, "Creating Stasis threadpool: initial_size = %d, max_size = %d, idle_timeout_secs = %d\n", + opts.initial_size, opts.max_size, opts.idle_timeout); pool = ast_threadpool_create("stasis-core", NULL, &opts); if (!pool) { ast_log(LOG_ERROR, "Stasis threadpool allocation failed\n"); diff --git a/main/stasis_config.c b/main/stasis_config.c new file mode 100644 index 0000000000000000000000000000000000000000..f20d61977a1529e8c0259a0d2d907edd0268b199 --- /dev/null +++ b/main/stasis_config.c @@ -0,0 +1,192 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2013, Digium, Inc. + * + * David M. Lee, II <dlee@digium.com> + * + * See http://www.asterisk.org for more information about + * the Asterisk project. Please do not directly contact + * any of the maintainers of this project for assistance; + * the project provides a web site, mailing lists and IRC + * channels for your use. + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + */ + +/*! \file + * + * \brief Stasis Message Bus configuration API. + * + * \author David M. Lee, II <dlee@digium.com> + */ + +/*** MODULEINFO + <support_level>core</support_level> + ***/ + +#include "asterisk.h" + +ASTERISK_FILE_VERSION(__FILE__, "$Revision$") + +#include "asterisk/config_options.h" +#include "asterisk/stasis.h" +#include "asterisk/threadpool.h" + +#include <limits.h> + +/*** DOCUMENTATION + <configInfo name="stasis" language="en_US"> + <synopsis>Stasis message bus configuration.</synopsis> + <configFile name="stasis.conf"> + <configObject name="threadpool"> + <synopsis>Threadpool configuration.</synopsis> + <configOption name="initial_size" default="0"> + <synopsis>Initial number of threads in the message bus threadpool.</synopsis> + </configOption> + <configOption name="idle_timeout_sec" default="20"> + <synopsis>Number of seconds for an idle thread to be disposed of.</synopsis> + </configOption> + <configOption name="max_size" default="200"> + <synopsis>Maximum number of threads in the threadpool.</synopsis> + </configOption> + </configObject> + </configFile> + </configInfo> + ***/ + +/*! \brief Locking container for safe configuration access. */ +static AO2_GLOBAL_OBJ_STATIC(confs); + +struct stasis_threadpool_conf { + int initial_size; + int idle_timeout_sec; + int max_size; +}; + +struct stasis_conf { + struct stasis_threadpool_conf *threadpool; +}; + +/*! \brief Mapping of the stasis http conf struct's globals to the + * threadpool context in the config file. */ +static struct aco_type threadpool_option = { + .type = ACO_GLOBAL, + .name = "threadpool", + .item_offset = offsetof(struct stasis_conf, threadpool), + .category = "^threadpool$", + .category_match = ACO_WHITELIST, +}; + +static struct aco_type *threadpool_options[] = ACO_TYPES(&threadpool_option); + +#define CONF_FILENAME "stasis.conf" + +/*! \brief The conf file that's processed for the module. */ +static struct aco_file conf_file = { + /*! The config file name. */ + .filename = CONF_FILENAME, + /*! The mapping object types to be processed. */ + .types = ACO_TYPES(&threadpool_option), +}; + +static void conf_dtor(void *obj) +{ + struct stasis_conf *conf = obj; + + ao2_cleanup(conf->threadpool); + conf->threadpool = NULL; +} + +static void *conf_alloc(void) +{ + RAII_VAR(struct stasis_conf *, conf, NULL, ao2_cleanup); + + conf = ao2_alloc_options(sizeof(*conf), conf_dtor, + AO2_ALLOC_OPT_LOCK_NOLOCK); + if (!conf) { + return NULL; + } + + conf->threadpool = ao2_alloc_options(sizeof(*conf->threadpool), NULL, + AO2_ALLOC_OPT_LOCK_NOLOCK); + if (!conf->threadpool) { + return NULL; + } + + aco_set_defaults(&threadpool_option, "threadpool", conf->threadpool); + + ao2_ref(conf, +1); + return conf; +} + +CONFIG_INFO_CORE("stasis", cfg_info, confs, conf_alloc, + .files = ACO_FILES(&conf_file)); + +void stasis_config_get_threadpool_options( + struct ast_threadpool_options *threadpool_options) +{ + RAII_VAR(struct stasis_conf *, conf, NULL, ao2_cleanup); + + conf = ao2_global_obj_ref(confs); + + ast_assert(conf && conf->threadpool); + + { + struct ast_threadpool_options newopts = { + .version = AST_THREADPOOL_OPTIONS_VERSION, + .initial_size = conf->threadpool->initial_size, + .auto_increment = 1, + .idle_timeout = conf->threadpool->idle_timeout_sec, + .max_size = conf->threadpool->max_size, + }; + + *threadpool_options = newopts; + } +} + +/*! \brief Load (or reload) configuration. */ +static int process_config(int reload) +{ + switch (aco_process_config(&cfg_info, reload)) { + case ACO_PROCESS_ERROR: + return -1; + case ACO_PROCESS_OK: + case ACO_PROCESS_UNCHANGED: + break; + } + + return 0; +} + +static void config_exit(void) +{ + aco_info_destroy(&cfg_info); +} + +int stasis_config_init(void) +{ + if (aco_info_init(&cfg_info)) { + aco_info_destroy(&cfg_info); + return -1; + } + + ast_register_atexit(config_exit); + + /* threadpool section */ + aco_option_register(&cfg_info, "initial_size", ACO_EXACT, + threadpool_options, "0", OPT_INT_T, PARSE_IN_RANGE, + FLDSET(struct stasis_threadpool_conf, initial_size), 0, + INT_MAX); + aco_option_register(&cfg_info, "idle_timeout_sec", ACO_EXACT, + threadpool_options, "20", OPT_INT_T, PARSE_IN_RANGE, + FLDSET(struct stasis_threadpool_conf, idle_timeout_sec), 0, + INT_MAX); + aco_option_register(&cfg_info, "max_size", ACO_EXACT, + threadpool_options, "200", OPT_INT_T, PARSE_IN_RANGE, + FLDSET(struct stasis_threadpool_conf, max_size), 0, INT_MAX); + + return process_config(0); +}