diff options
-rwxr-xr-x | configure | 96 | ||||
-rw-r--r-- | configure.ac | 1 | ||||
-rw-r--r-- | include/asterisk/data_buffer.h | 144 | ||||
-rw-r--r-- | main/asterisk.c | 2 | ||||
-rw-r--r-- | main/data_buffer.c | 314 | ||||
-rw-r--r-- | main/indications.c | 33 | ||||
-rw-r--r-- | res/res_pjsip/pjsip_transport_events.c | 55 | ||||
-rw-r--r-- | tests/test_data_buffer.c | 313 |
8 files changed, 828 insertions, 130 deletions
@@ -25886,102 +25886,6 @@ fi -if test "x${PBX_PJSIP_EVSUB_SET_UAS_TIMEOUT}" != "x1" -a "${USE_PJSIP_EVSUB_SET_UAS_TIMEOUT}" != "no"; then - pbxlibdir="" - # if --with-PJSIP_EVSUB_SET_UAS_TIMEOUT=DIR has been specified, use it. - if test "x${PJSIP_EVSUB_SET_UAS_TIMEOUT_DIR}" != "x"; then - if test -d ${PJSIP_EVSUB_SET_UAS_TIMEOUT_DIR}/lib; then - pbxlibdir="-L${PJSIP_EVSUB_SET_UAS_TIMEOUT_DIR}/lib" - else - pbxlibdir="-L${PJSIP_EVSUB_SET_UAS_TIMEOUT_DIR}" - fi - fi - - ast_ext_lib_check_save_CFLAGS="${CFLAGS}" - CFLAGS="${CFLAGS} $PJPROJECT_CFLAGS" - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for pjsip_evsub_set_uas_timeout in -lpjsip" >&5 -$as_echo_n "checking for pjsip_evsub_set_uas_timeout in -lpjsip... " >&6; } -if ${ac_cv_lib_pjsip_pjsip_evsub_set_uas_timeout+:} false; then : - $as_echo_n "(cached) " >&6 -else - ac_check_lib_save_LIBS=$LIBS -LIBS="-lpjsip ${pbxlibdir} $PJPROJECT_LIB $LIBS" -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -/* Override any GCC internal prototype to avoid an error. - Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -#ifdef __cplusplus -extern "C" -#endif -char pjsip_evsub_set_uas_timeout (); -int -main () -{ -return pjsip_evsub_set_uas_timeout (); - ; - return 0; -} -_ACEOF -if ac_fn_c_try_link "$LINENO"; then : - ac_cv_lib_pjsip_pjsip_evsub_set_uas_timeout=yes -else - ac_cv_lib_pjsip_pjsip_evsub_set_uas_timeout=no -fi -rm -f core conftest.err conftest.$ac_objext \ - conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_pjsip_pjsip_evsub_set_uas_timeout" >&5 -$as_echo "$ac_cv_lib_pjsip_pjsip_evsub_set_uas_timeout" >&6; } -if test "x$ac_cv_lib_pjsip_pjsip_evsub_set_uas_timeout" = xyes; then : - AST_PJSIP_EVSUB_SET_UAS_TIMEOUT_FOUND=yes -else - AST_PJSIP_EVSUB_SET_UAS_TIMEOUT_FOUND=no -fi - - CFLAGS="${ast_ext_lib_check_save_CFLAGS}" - - - # now check for the header. - if test "${AST_PJSIP_EVSUB_SET_UAS_TIMEOUT_FOUND}" = "yes"; then - PJSIP_EVSUB_SET_UAS_TIMEOUT_LIB="${pbxlibdir} -lpjsip $PJPROJECT_LIB" - # if --with-PJSIP_EVSUB_SET_UAS_TIMEOUT=DIR has been specified, use it. - if test "x${PJSIP_EVSUB_SET_UAS_TIMEOUT_DIR}" != "x"; then - PJSIP_EVSUB_SET_UAS_TIMEOUT_INCLUDE="-I${PJSIP_EVSUB_SET_UAS_TIMEOUT_DIR}/include" - fi - PJSIP_EVSUB_SET_UAS_TIMEOUT_INCLUDE="${PJSIP_EVSUB_SET_UAS_TIMEOUT_INCLUDE} $PJPROJECT_CFLAGS" - - # check for the header - ast_ext_lib_check_saved_CPPFLAGS="${CPPFLAGS}" - CPPFLAGS="${CPPFLAGS} ${PJSIP_EVSUB_SET_UAS_TIMEOUT_INCLUDE}" - ac_fn_c_check_header_mongrel "$LINENO" "pjsip.h" "ac_cv_header_pjsip_h" "$ac_includes_default" -if test "x$ac_cv_header_pjsip_h" = xyes; then : - PJSIP_EVSUB_SET_UAS_TIMEOUT_HEADER_FOUND=1 -else - PJSIP_EVSUB_SET_UAS_TIMEOUT_HEADER_FOUND=0 -fi - - - CPPFLAGS="${ast_ext_lib_check_saved_CPPFLAGS}" - - if test "x${PJSIP_EVSUB_SET_UAS_TIMEOUT_HEADER_FOUND}" = "x0" ; then - PJSIP_EVSUB_SET_UAS_TIMEOUT_LIB="" - PJSIP_EVSUB_SET_UAS_TIMEOUT_INCLUDE="" - else - - PBX_PJSIP_EVSUB_SET_UAS_TIMEOUT=1 - cat >>confdefs.h <<_ACEOF -#define HAVE_PJSIP_EVSUB_SET_UAS_TIMEOUT 1 -_ACEOF - - fi - fi -fi - - - if test "x${PBX_PJSIP_TSX_LAYER_FIND_TSX2}" != "x1" -a "${USE_PJSIP_TSX_LAYER_FIND_TSX2}" != "no"; then pbxlibdir="" # if --with-PJSIP_TSX_LAYER_FIND_TSX2=DIR has been specified, use it. diff --git a/configure.ac b/configure.ac index 219f85d0c..e2af23493 100644 --- a/configure.ac +++ b/configure.ac @@ -2303,7 +2303,6 @@ if test "$USE_PJPROJECT" != "no" ; then AST_EXT_LIB_CHECK([PJSIP_EVSUB_GRP_LOCK], [pjsip], [pjsip_evsub_add_ref], [pjsip.h], [$PJPROJECT_LIB], [$PJPROJECT_CFLAGS]) AST_EXT_LIB_CHECK([PJSIP_INV_SESSION_REF], [pjsip], [pjsip_inv_add_ref], [pjsip.h], [$PJPROJECT_LIB], [$PJPROJECT_CFLAGS]) AST_EXT_LIB_CHECK([PJSIP_AUTH_CLT_DEINIT], [pjsip], [pjsip_auth_clt_deinit], [pjsip.h], [$PJPROJECT_LIB], [$PJPROJECT_CFLAGS]) - AST_EXT_LIB_CHECK([PJSIP_EVSUB_SET_UAS_TIMEOUT], [pjsip], [pjsip_evsub_set_uas_timeout], [pjsip.h], [$PJPROJECT_LIB], [$PJPROJECT_CFLAGS]) AST_EXT_LIB_CHECK([PJSIP_TSX_LAYER_FIND_TSX2], [pjsip], [pjsip_tsx_layer_find_tsx2], [pjsip.h], [$PJPROJECT_LIB], [$PJPROJECT_CFLAGS]) fi fi diff --git a/include/asterisk/data_buffer.h b/include/asterisk/data_buffer.h new file mode 100644 index 000000000..dacbaa5e4 --- /dev/null +++ b/include/asterisk/data_buffer.h @@ -0,0 +1,144 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2018, Digium, Inc. + * + * Joshua Colp <jcolp@digium.com> + * Ben Ford <bford@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 Data Buffer API + * + * A data buffer acts as a ring buffer of data. It is given a fixed + * number of data packets to store (which may be dynamically changed). + * Given a number it will store a data packet at that position relative + * to the others. Given a number it will retrieve the given data packet + * if it is present. This is purposely a storage of arbitrary things so + * that it can be used for multiple things. + * + * \author Joshua Colp <jcolp@digium.com> + * \author Ben Ford <bford@digium.com> + */ + +#ifndef _AST_DATA_BUFFER_H_ +#define _AST_DATA_BUFFER_H_ + +/*! + * \brief A buffer of data payloads. + */ +struct ast_data_buffer; + +/*! + * \brief A callback function to free a data payload in a data buffer + * + * \param The data payload + */ +typedef void (*ast_data_buffer_free_callback)(void *data); + +/*! + * \brief Allocate a data buffer + * + * \param free_fn Callback function to free a data payload + * \param size The maximum number of data payloads to contain in the data buffer + * + * \retval non-NULL success + * \retval NULL failure + * + * \note free_fn can be NULL. It is up to the consumer of this API to ensure that memory is + * managed appropriately. + * + * \since 15.4.0 + */ +struct ast_data_buffer *ast_data_buffer_alloc(ast_data_buffer_free_callback free_fn, size_t size); + +/*! + * \brief Resize a data buffer + * + * \param buffer The data buffer + * \param size The new maximum size of the data buffer + * + * \note If the data buffer is shrunk any old data payloads will be freed using the configured callback. + * The data buffer is flexible and can be used for multiple purposes. Therefore it is up to the + * caller of the function to know whether or not a buffer should have its size changed. Increasing + * the size of the buffer may make sense in some scenarios, but shrinking should always be handled + * with caution since data can be lost. + * + * \since 15.4.0 + */ +void ast_data_buffer_resize(struct ast_data_buffer *buffer, size_t size); + +/*! + * \brief Place a data payload at a position in the data buffer + * + * \param buffer The data buffer + * \param pos The position of the data payload + * \param payload The data payload + * + * \retval 0 success + * \retval -1 failure + * + * \note It is up to the consumer of this API to ensure proper memory management of data payloads + * + * \since 15.4.0 + */ +int ast_data_buffer_put(struct ast_data_buffer *buffer, size_t pos, void *payload); + +/*! + * \brief Retrieve a data payload from the data buffer + * + * \param buffer The data buffer + * \param pos The position of the data payload + * + * \retval non-NULL success + * \retval NULL failure + * + * \note This does not remove the data payload from the data buffer. It will be removed when it is displaced. + * + * \since 15.4.0 + */ +void *ast_data_buffer_get(const struct ast_data_buffer *buffer, size_t pos); + +/*! + * \brief Free a data buffer (and all held data payloads) + * + * \param buffer The data buffer + * + * \since 15.4.0 + */ +void ast_data_buffer_free(struct ast_data_buffer *buffer); + +/*! + * \brief Return the number of payloads in a data buffer + * + * \param buffer The data buffer + * + * \retval the number of data payloads + * + * \since 15.4.0 + */ +size_t ast_data_buffer_count(const struct ast_data_buffer *buffer); + +/*! + * \brief Return the maximum number of payloads a data buffer can hold + * + * \param buffer The data buffer + * + * \retval the maximum number of data payloads + * + * \since 15.4.0 + */ +size_t ast_data_buffer_max(const struct ast_data_buffer *buffer); + +#endif /* _AST_DATA_BUFFER_H */ diff --git a/main/asterisk.c b/main/asterisk.c index 36b1b54a1..2e80ffaf6 100644 --- a/main/asterisk.c +++ b/main/asterisk.c @@ -3963,7 +3963,7 @@ int main(int argc, char *argv[]) * * \todo Document these options */ - optind = 0; + optind = 1; while ((c = getopt(argc, argv, getopt_settings)) != -1) { /*!\note Please keep the ordering here to alphabetical, capital letters * first. This will make it easier in the future to select unused diff --git a/main/data_buffer.c b/main/data_buffer.c new file mode 100644 index 000000000..ccbffd22d --- /dev/null +++ b/main/data_buffer.c @@ -0,0 +1,314 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2018, Digium, Inc. + * + * Joshua Colp <jcolp@digium.com> + * Ben Ford <bford@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 Data Buffer API + * + * \author Joshua Colp <jcolp@digium.com> + * \author Ben Ford <bford@digium.com> + */ + +/*** MODULEINFO + <support_level>core</support_level> + ***/ + +#include "asterisk.h" + +#include "asterisk/logger.h" +#include "asterisk/strings.h" +#include "asterisk/data_buffer.h" +#include "asterisk/linkedlists.h" + +/*! + * \brief The number of payloads to increment the cache by + */ +#define CACHED_PAYLOAD_MAX 5 + +/*! + * \brief Payload entry placed inside of the data buffer list + */ +struct data_buffer_payload_entry { + /*! \brief The payload for this position */ + void *payload; + /*! \brief The provided position for this */ + size_t pos; + /*! \brief Linked list information */ + AST_LIST_ENTRY(data_buffer_payload_entry) list; +}; + +/*! + * \brief Data buffer containing fixed number of data payloads + */ +struct ast_data_buffer { + /*! \brief Callback function to free a data payload */ + ast_data_buffer_free_callback free_fn; + /*! \brief A linked list of data payloads */ + AST_LIST_HEAD_NOLOCK(, data_buffer_payload_entry) payloads; + /*! \brief A linked list of unused cached data payloads */ + AST_LIST_HEAD_NOLOCK(, data_buffer_payload_entry) cached_payloads; + /*! \brief The current number of data payloads in the buffer */ + size_t count; + /*! \brief Maximum number of data payloads in the buffer */ + size_t max; + /*! \brief The current number of data payloads in the cache */ + size_t cache_count; +}; + +static void free_fn_do_nothing(void *data) +{ + return; +} + +/*! + * \brief Helper function to allocate a data payload + */ +static struct data_buffer_payload_entry *data_buffer_payload_alloc(void *payload, size_t pos) +{ + struct data_buffer_payload_entry *data_payload; + + data_payload = ast_calloc(1, sizeof(*data_payload)); + if (!data_payload) { + return NULL; + } + + data_payload->payload = payload; + data_payload->pos = pos; + + return data_payload; +} + +/*! + * \brief Helper function that sets the cache to its maximum number of payloads + */ +static void ast_data_buffer_cache_adjust(struct ast_data_buffer *buffer) +{ + int buffer_space; + + ast_assert(buffer != NULL); + + buffer_space = buffer->max - buffer->count; + + if (buffer->cache_count == buffer_space) { + return; + } + + if (buffer->cache_count < buffer_space) { + /* Add payloads to the cache, if able */ + while (buffer->cache_count < CACHED_PAYLOAD_MAX && buffer->cache_count < buffer_space) { + struct data_buffer_payload_entry *buffer_payload; + + buffer_payload = data_buffer_payload_alloc(NULL, -1); + if (buffer_payload) { + AST_LIST_INSERT_TAIL(&buffer->cached_payloads, buffer_payload, list); + buffer->cache_count++; + continue; + } + + ast_log(LOG_ERROR, "Failed to allocate memory to the cache."); + break; + } + } else if (buffer->cache_count > buffer_space) { + /* Remove payloads from the cache */ + while (buffer->cache_count > buffer_space) { + struct data_buffer_payload_entry *buffer_payload; + + buffer_payload = AST_LIST_REMOVE_HEAD(&buffer->cached_payloads, list); + if (buffer_payload) { + ast_free(buffer_payload); + buffer->cache_count--; + continue; + } + + ast_log(LOG_ERROR, "Failed to remove memory from the cache."); + break; + } + } +} + +struct ast_data_buffer *ast_data_buffer_alloc(ast_data_buffer_free_callback free_fn, size_t size) +{ + struct ast_data_buffer *buffer; + + ast_assert(size != 0); + + buffer = ast_calloc(1, sizeof(*buffer)); + if (!buffer) { + return NULL; + } + + AST_LIST_HEAD_INIT_NOLOCK(&buffer->payloads); + AST_LIST_HEAD_INIT_NOLOCK(&buffer->cached_payloads); + + /* If free_fn is NULL, just use free_fn_do_nothing as a default */ + buffer->free_fn = free_fn ? free_fn : free_fn_do_nothing; + buffer->max = size; + + ast_data_buffer_cache_adjust(buffer); + + return buffer; +} + +void ast_data_buffer_resize(struct ast_data_buffer *buffer, size_t size) +{ + struct data_buffer_payload_entry *existing_payload; + + ast_assert(buffer != NULL); + + /* The buffer must have at least a size of 1 */ + ast_assert(size > 0); + + if (buffer->max == size) { + return; + } + + /* If the size is decreasing, some payloads will need to be freed */ + if (buffer->max > size) { + int remove = buffer->max - size; + + AST_LIST_TRAVERSE_SAFE_BEGIN(&buffer->payloads, existing_payload, list) { + if (remove) { + AST_LIST_REMOVE_HEAD(&buffer->payloads, list); + buffer->free_fn(existing_payload->payload); + ast_free(existing_payload); + buffer->count--; + remove--; + continue; + } + break; + } + AST_LIST_TRAVERSE_SAFE_END; + } + + buffer->max = size; + ast_data_buffer_cache_adjust(buffer); +} + +int ast_data_buffer_put(struct ast_data_buffer *buffer, size_t pos, void *payload) +{ + struct data_buffer_payload_entry *buffer_payload = NULL; + struct data_buffer_payload_entry *existing_payload; + int inserted = 0; + + ast_assert(buffer != NULL); + ast_assert(payload != NULL); + + /* If the data buffer has reached its maximum size then the head goes away and + * we will reuse its buffer payload + */ + if (buffer->count == buffer->max) { + buffer_payload = AST_LIST_REMOVE_HEAD(&buffer->payloads, list); + buffer->free_fn(buffer_payload->payload); + buffer->count--; + + /* Update this buffer payload with its new information */ + buffer_payload->payload = payload; + buffer_payload->pos = pos; + } + if (!buffer_payload) { + if (!buffer->cache_count) { + ast_data_buffer_cache_adjust(buffer); + } + buffer_payload = AST_LIST_REMOVE_HEAD(&buffer->cached_payloads, list); + buffer->cache_count--; + + /* Update the payload from the cache with its new information */ + buffer_payload->payload = payload; + buffer_payload->pos = pos; + } + if (!buffer_payload) { + return -1; + } + + /* Given the position find its ideal spot within the buffer */ + AST_LIST_TRAVERSE_SAFE_BEGIN(&buffer->payloads, existing_payload, list) { + /* If it's already in the buffer, drop it */ + if (existing_payload->pos == pos) { + ast_debug(3, "Packet with position %zu is already in buffer. Not inserting.\n", pos); + inserted = -1; + break; + } + + if (existing_payload->pos > pos) { + AST_LIST_INSERT_BEFORE_CURRENT(buffer_payload, list); + inserted = 1; + break; + } + } + AST_LIST_TRAVERSE_SAFE_END; + + if (inserted == -1) { + return 0; + } + + if (!inserted) { + AST_LIST_INSERT_TAIL(&buffer->payloads, buffer_payload, list); + } + + buffer->count++; + + return 0; +} + +void *ast_data_buffer_get(const struct ast_data_buffer *buffer, size_t pos) +{ + struct data_buffer_payload_entry *buffer_payload; + + ast_assert(buffer != NULL); + + AST_LIST_TRAVERSE(&buffer->payloads, buffer_payload, list) { + if (buffer_payload->pos == pos) { + return buffer_payload->payload; + } + } + + return NULL; +} + +void ast_data_buffer_free(struct ast_data_buffer *buffer) +{ + struct data_buffer_payload_entry *buffer_payload; + + ast_assert(buffer != NULL); + + while ((buffer_payload = AST_LIST_REMOVE_HEAD(&buffer->payloads, list))) { + buffer->free_fn(buffer_payload->payload); + ast_free(buffer_payload); + } + + while ((buffer_payload = AST_LIST_REMOVE_HEAD(&buffer->cached_payloads, list))) { + ast_free(buffer_payload); + } + + ast_free(buffer); +} + +size_t ast_data_buffer_count(const struct ast_data_buffer *buffer) +{ + ast_assert(buffer != NULL); + + return buffer->count; +} + +size_t ast_data_buffer_max(const struct ast_data_buffer *buffer) +{ + ast_assert(buffer != NULL); + + return buffer->max; +} diff --git a/main/indications.c b/main/indications.c index 9b0976809..c9f02416f 100644 --- a/main/indications.c +++ b/main/indications.c @@ -632,9 +632,7 @@ static struct ast_tone_zone *ast_tone_zone_alloc(void) static char *complete_country(struct ast_cli_args *a) { - char *res = NULL; struct ao2_iterator i; - int which = 0; size_t wordlen; struct ast_tone_zone *tz; @@ -642,17 +640,17 @@ static char *complete_country(struct ast_cli_args *a) i = ao2_iterator_init(ast_tone_zones, 0); while ((tz = ao2_iterator_next(&i))) { - if (!strncasecmp(a->word, tz->country, wordlen) && ++which > a->n) { - res = ast_strdup(tz->country); - } - tz = ast_tone_zone_unref(tz); - if (res) { - break; + if (!strncasecmp(a->word, tz->country, wordlen)) { + if (ast_cli_completion_add(ast_strdup(tz->country))) { + ast_tone_zone_unref(tz); + break; + } } + ast_tone_zone_unref(tz); } ao2_iterator_destroy(&i); - return res; + return NULL; } static char *handle_cli_indication_add(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) @@ -718,17 +716,17 @@ static char *handle_cli_indication_add(struct ast_cli_entry *e, int cmd, struct static char *complete_indications(struct ast_cli_args *a) { - char *res = NULL; - int which = 0; size_t wordlen; struct ast_tone_zone_sound *ts; - struct ast_tone_zone *tz, tmp_tz = { + struct ast_tone_zone *tz; + struct ast_tone_zone tmp_tz = { .nrringcadence = 0, }; ast_copy_string(tmp_tz.country, a->argv[a->pos - 1], sizeof(tmp_tz.country)); - if (!(tz = ao2_find(ast_tone_zones, &tmp_tz, OBJ_POINTER))) { + tz = ao2_find(ast_tone_zones, &tmp_tz, OBJ_POINTER); + if (!tz) { return NULL; } @@ -736,16 +734,17 @@ static char *complete_indications(struct ast_cli_args *a) ast_tone_zone_lock(tz); AST_LIST_TRAVERSE(&tz->tones, ts, entry) { - if (!strncasecmp(a->word, ts->name, wordlen) && ++which > a->n) { - res = ast_strdup(ts->name); - break; + if (!strncasecmp(a->word, ts->name, wordlen)) { + if (ast_cli_completion_add(ast_strdup(ts->name))) { + break; + } } } ast_tone_zone_unlock(tz); tz = ast_tone_zone_unref(tz); - return res; + return NULL; } static char *handle_cli_indication_remove(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) diff --git a/res/res_pjsip/pjsip_transport_events.c b/res/res_pjsip/pjsip_transport_events.c index c701b8411..cc7b7c077 100644 --- a/res/res_pjsip/pjsip_transport_events.c +++ b/res/res_pjsip/pjsip_transport_events.c @@ -114,6 +114,36 @@ static void transport_monitor_dtor(void *vdoomed) AST_VECTOR_FREE(&monitored->monitors); } +/*! + * \internal + * \brief Do registered callbacks for the transport. + * \since 13.21.0 + * + * \param transports Active transports container + * \param transport Which transport to do callbacks for. + * + * \return Nothing + */ +static void transport_state_do_reg_callbacks(struct ao2_container *transports, pjsip_transport *transport) +{ + struct transport_monitor *monitored; + + monitored = ao2_find(transports, transport->obj_name, OBJ_SEARCH_KEY | OBJ_UNLINK); + if (monitored) { + int idx; + + for (idx = AST_VECTOR_SIZE(&monitored->monitors); idx--;) { + struct transport_monitor_notifier *notifier; + + notifier = AST_VECTOR_GET_ADDR(&monitored->monitors, idx); + ast_debug(3, "running callback %p(%p) for transport %s\n", + notifier->cb, notifier->data, transport->obj_name); + notifier->cb(notifier->data); + } + ao2_ref(monitored, -1); + } +} + /*! \brief Callback invoked when transport state changes occur */ static void transport_state_callback(pjsip_transport *transport, pjsip_transport_state state, const pjsip_transport_state_info *info) @@ -147,6 +177,7 @@ static void transport_state_callback(pjsip_transport *transport, if (!transport->is_shutdown) { pjsip_transport_shutdown(transport); } + transport_state_do_reg_callbacks(transports, transport); break; case PJSIP_TP_STATE_SHUTDOWN: /* @@ -157,23 +188,17 @@ static void transport_state_callback(pjsip_transport *transport, */ transport->is_shutdown = PJ_TRUE; - monitored = ao2_find(transports, transport->obj_name, - OBJ_SEARCH_KEY | OBJ_UNLINK); - if (monitored) { - int idx; - - for (idx = AST_VECTOR_SIZE(&monitored->monitors); idx--;) { - struct transport_monitor_notifier *notifier; - - notifier = AST_VECTOR_GET_ADDR(&monitored->monitors, idx); - ast_debug(3, "running callback %p(%p) for transport %s\n", - notifier->cb, notifier->data, transport->obj_name); - notifier->cb(notifier->data); - } - ao2_ref(monitored, -1); - } + transport_state_do_reg_callbacks(transports, transport); + break; + case PJSIP_TP_STATE_DESTROY: + transport_state_do_reg_callbacks(transports, transport); break; default: + /* + * We have to have a default case because the enum is + * defined by a third-party library. + */ + ast_assert(0); break; } diff --git a/tests/test_data_buffer.c b/tests/test_data_buffer.c new file mode 100644 index 000000000..11fdc7b6f --- /dev/null +++ b/tests/test_data_buffer.c @@ -0,0 +1,313 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2018, Digium, Inc. + * + * Ben Ford <bford@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 Media Stream API Unit Tests + * + * \author Ben Ford <bford@digium.com> + * + */ + +/*** MODULEINFO + <depend>TEST_FRAMEWORK</depend> + <support_level>core</support_level> + ***/ + +#include "asterisk.h" + +#include "asterisk/module.h" +#include "asterisk/test.h" +#include "asterisk/data_buffer.h" + +#define BUFFER_MAX_NOMINAL 10 + +struct mock_payload +{ + int id; +}; + +/* Ensures that RAII_VAR will not trip ast_assert(buffer != NULL) in the callback */ +static void ast_data_buffer_free_wrapper(struct ast_data_buffer *buffer) +{ + if (!buffer) { + return; + } + + ast_data_buffer_free(buffer); +} + +AST_TEST_DEFINE(buffer_create) +{ + RAII_VAR(struct ast_data_buffer *, buffer, NULL, ast_data_buffer_free_wrapper); + + switch (cmd) { + case TEST_INIT: + info->name = "buffer_create"; + info->category = "/main/data_buffer/"; + info->summary = "buffer create unit test"; + info->description = + "Test that creating a data buffer results in a buffer with the expected values"; + return AST_TEST_NOT_RUN; + case TEST_EXECUTE: + break; + } + + buffer = ast_data_buffer_alloc(ast_free_ptr, BUFFER_MAX_NOMINAL); + + ast_test_validate(test, buffer != NULL, + "Failed to create buffer with valid arguments"); + ast_test_validate(test, ast_data_buffer_count(buffer) == 0, + "Newly created buffer does not have the expected payload count"); + ast_test_validate(test, ast_data_buffer_max(buffer) == BUFFER_MAX_NOMINAL, + "Newly created buffer does not have the expected max size"); + + return AST_TEST_PASS; +} + +AST_TEST_DEFINE(buffer_put) +{ + RAII_VAR(struct ast_data_buffer *, buffer, NULL, ast_data_buffer_free_wrapper); + struct mock_payload *payload; + struct mock_payload *fetched_payload; + int ret; + + switch (cmd) { + case TEST_INIT: + info->name = "buffer_put"; + info->category = "/main/data_buffer/"; + info->summary = "buffer put unit test"; + info->description = + "Test that putting payloads in the buffer yields the expected results"; + return AST_TEST_NOT_RUN; + case TEST_EXECUTE: + break; + } + + buffer = ast_data_buffer_alloc(ast_free_ptr, 2); + + ast_test_validate(test, buffer != NULL, + "Failed to create buffer with valid arguments"); + ast_test_validate(test, ast_data_buffer_count(buffer) == 0, + "Newly created buffer is not empty"); + + payload = ast_calloc(1, sizeof(*payload)); + + ast_test_validate(test, payload != NULL, + "Failed to allocate memory for first payload"); + + payload->id = 2; + ret = ast_data_buffer_put(buffer, 2, payload); + + ast_test_validate(test, ret == 0, + "Adding a payload to an empty buffer did not return the expected value"); + ast_test_validate(test, ast_data_buffer_count(buffer) == 1, + "Adding a payload to an empty buffer did not update count to the expected value"); + + fetched_payload = (struct mock_payload *)ast_data_buffer_get(buffer, 2); + + ast_test_validate(test, fetched_payload != NULL, + "Failed to get only payload from buffer given valid arguments"); + + ast_data_buffer_put(buffer, 2, payload); + + ast_test_validate(test, ast_data_buffer_count(buffer) == 1, + "Adding a payload that is already in the buffer should not do anything"); + + payload = ast_calloc(1, sizeof(*payload)); + + ast_test_validate(test, payload != NULL, + "Failed to allocate memory for second payload"); + + payload->id = 1; + ast_data_buffer_put(buffer, 1, payload); + fetched_payload = ast_data_buffer_get(buffer, 1); + + ast_test_validate(test, fetched_payload != NULL, + "Failed to get a payload from buffer given valid arguments"); + ast_test_validate(test, ast_data_buffer_count(buffer) == 2, + "Buffer does not have the expected count after removing a payload"); + ast_test_validate(test, fetched_payload->id == 1, + "Did not get the expected payload from the buffer"); + + payload = ast_calloc(1, sizeof(*payload)); + + ast_test_validate(test, payload != NULL, + "Failed to allocate memory for third payload"); + + payload->id = 3; + ret = ast_data_buffer_put(buffer, 3, payload); + + ast_test_validate(test, ret == 0, + "Failed to replace a payload in the buffer"); + ast_test_validate(test, ast_data_buffer_count(buffer) <= 2, + "Buffer count exceeded the max"); + + fetched_payload = (struct mock_payload *)ast_data_buffer_get(buffer, 3); + + ast_test_validate(test, fetched_payload != NULL, + "Failed to get a payload from buffer at position 3 given valid arguments"); + ast_test_validate(test, fetched_payload->id == 3, + "Did not get the expected payload at position 3 from the buffer"); + + fetched_payload = (struct mock_payload *)ast_data_buffer_get(buffer, 2); + + ast_test_validate(test, fetched_payload != NULL, + "Failed to get a payload from buffer at position 2 given valid arguments"); + ast_test_validate(test, fetched_payload->id == 2, + "Did not get the expected payload at position 2 from the buffer"); + + return AST_TEST_PASS; +} + +AST_TEST_DEFINE(buffer_resize) +{ + RAII_VAR(struct ast_data_buffer *, buffer, NULL, ast_data_buffer_free_wrapper); + + switch (cmd) { + case TEST_INIT: + info->name = "buffer_resize"; + info->category = "/main/data_buffer/"; + info->summary = "buffer resize unit test"; + info->description = + "Tests resizing a data buffer to make sure it has the expected outcome"; + return AST_TEST_NOT_RUN; + case TEST_EXECUTE: + break; + } + + buffer = ast_data_buffer_alloc(ast_free_ptr, BUFFER_MAX_NOMINAL); + + ast_test_validate(test, buffer != NULL, + "Failed to create buffer with valid arguments"); + + ast_data_buffer_resize(buffer, BUFFER_MAX_NOMINAL); + + ast_test_validate(test, ast_data_buffer_max(buffer) == BUFFER_MAX_NOMINAL, + "Trying to resize buffer to same size should not change its max size"); + + ast_data_buffer_resize(buffer, BUFFER_MAX_NOMINAL + 2); + + ast_test_validate(test, ast_data_buffer_max(buffer) == BUFFER_MAX_NOMINAL + 2, + "Increasing buffer size did not return the expected max"); + + ast_data_buffer_resize(buffer, 1); + + ast_test_validate(test, ast_data_buffer_max(buffer) == 1, + "Decreasing buffer size did not return the expected max"); + + return AST_TEST_PASS; +} + +AST_TEST_DEFINE(buffer_nominal) +{ + RAII_VAR(struct ast_data_buffer *, buffer, NULL, ast_data_buffer_free_wrapper); + struct mock_payload *payload; + struct mock_payload *fetched_payload; + int ret; + int i; + + switch (cmd) { + case TEST_INIT: + info->name = "buffer_nominal"; + info->category = "/main/data_buffer/"; + info->summary = "buffer nominal unit test"; + info->description = + "Tests the normal usage of a data buffer to ensure the expected payloads " + "are present after multiple insertions"; + return AST_TEST_NOT_RUN; + case TEST_EXECUTE: + break; + } + + buffer = ast_data_buffer_alloc(ast_free_ptr, BUFFER_MAX_NOMINAL); + + ast_test_validate(test, buffer != NULL, + "Failed to create buffer with valid arguments"); + + for (i = 1; i <= BUFFER_MAX_NOMINAL; i++) { + payload = ast_calloc(1, sizeof(*payload)); + + ast_test_validate(test, payload != NULL, + "Failed to allocate memory for payload %d", i); + + ret = ast_data_buffer_put(buffer, i, payload); + + ast_test_validate(test, ret == 0, + "Failed to add payload %d to buffer", i); + } + + ast_test_validate(test, ast_data_buffer_count(buffer) == BUFFER_MAX_NOMINAL, + "Buffer does not have the expected count after adding payloads"); + + for (i = 1; i <= BUFFER_MAX_NOMINAL; i++) { + fetched_payload = (struct mock_payload *)ast_data_buffer_get(buffer, i); + + ast_test_validate(test, fetched_payload != NULL, + "Failed to get payload at position %d during first loop", i); + } + + for (i = 1; i <= BUFFER_MAX_NOMINAL; i++) { + payload = ast_calloc(1, sizeof(*payload)); + + ast_test_validate(test, payload != NULL, + "Failed to allocate memory for payload %d", i + BUFFER_MAX_NOMINAL); + + ret = ast_data_buffer_put(buffer, i + BUFFER_MAX_NOMINAL, payload); + + ast_test_validate(test, ret == 0, + "Failed to add payload %d to buffer", i + BUFFER_MAX_NOMINAL); + } + + ast_test_validate(test, ast_data_buffer_count(buffer) == BUFFER_MAX_NOMINAL, + "Buffer does not have the expected count after replacing payloads"); + + for (i = 1; i <= BUFFER_MAX_NOMINAL; i++) { + fetched_payload = (struct mock_payload *)ast_data_buffer_get(buffer, i); + + ast_test_validate(test, fetched_payload == NULL, + "Got an unexpected payload at position %d", i); + + fetched_payload = (struct mock_payload *)ast_data_buffer_get(buffer, i + BUFFER_MAX_NOMINAL); + + ast_test_validate(test, fetched_payload != NULL, + "Failed to get payload at position %d during second loop", i + BUFFER_MAX_NOMINAL); + } + + return AST_TEST_PASS; +} + +static int unload_module(void) +{ + AST_TEST_UNREGISTER(buffer_create); + AST_TEST_UNREGISTER(buffer_put); + AST_TEST_UNREGISTER(buffer_resize); + AST_TEST_UNREGISTER(buffer_nominal); + return 0; +} + +static int load_module(void) +{ + AST_TEST_REGISTER(buffer_create); + AST_TEST_REGISTER(buffer_put); + AST_TEST_REGISTER(buffer_resize); + AST_TEST_REGISTER(buffer_nominal); + return AST_MODULE_LOAD_SUCCESS; +} + +AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Data buffer API test module"); |