<p>George Joseph has uploaded this change for <strong>review</strong>.</p><p><a href="https://gerrit.asterisk.org/10612">View Change</a></p><pre style="font-family: monospace,monospace; white-space: pre-wrap;">vector: Add options for alternate memory management<br><br>The vector implementation has been using the ast_calloc, ast_free,<br>and ast_malloc functions for memory management but there are<br>situations where the vector may be used to process error or<br>backtrace information where an error in the ast_ MM functions<br>could cause a recursive error situation.<br><br>* Added an ast_calloc_ptr function that, regardless of MALLOC_DEBUG,<br> always points to a real function and not a macro.<br><br>* Added an ast_std_strdup function that always calls the libc strdup<br> function regardless of MALLOC_DEBUG. Will be needed for a<br> follow-on commit.<br><br>* Added 3 virtual function pointers for calloc, free and element<br> cleanup to the vector structures. AST_VECTOR_INIT defaults these<br> to ast_calloc_ptr, ast_free_ptr and NULL respectively. This<br> preserves existing behavior.<br><br>* All places in vector.h that had called the ast_ functions<br> directly now use the virtual function pointers instead.<br><br>* A new macro AST_VECTOR_INIT_MM_FN() allows the caller to specify<br> the 3 new functions. The element cleanup function is optional.<br><br>* Two new macros AST_VECTOR_CLEANUP and AST_VECTOR_PTR_CLEANUP will<br> use the 3 virtual functions to...<br> 1. Cleanup all elements in the vector (provided the element<br> cleanup function was specified in AST_VECTOR_INIT_MM_FN)<br> 2. Use the free virtual function to free the internal "elems"<br> array.<br> 3. For AST_VECTOR_PTR_CLEANUP, use the free virtual function to<br> free the externally allocated vector structure itself.<br><br>* cli.c was using a calloc'd vector without having called<br> AST_VECTOR_INIT() which used to be effectively safe but was never<br> technically correct. It now calls AST_VECTOR_INIT.<br><br>* optional_api.c was using a vector without having called<br> AST_VECTOR_INIT() on it. Needed to add init() and atexit()<br> routines to do the work.<br><br>Example where a function must return a vector that doesn't use the<br>ast_ memory management functions. Error checking omitted for<br>brevity.<br><br>struct ast_vector_string *get_somevector()<br>{<br> struct ast_vector_string *strings = ast_std_malloc(sizeof(*strings));<br> AST_VECTOR_INIT_MM_FN(strings, 0, ast_std_calloc, ast_std_free,<br> ast_std_free);<br> AST_VECTOR_APPEND(strings, ast_std_strdup(somestring1);<br> AST_VECTOR_APPEND(strings, ast_std_strdup(somestring2);<br> AST_VECTOR_APPEND(strings, ast_std_strdup(somestring3);<br> return strings;<br>}<br><br>In this case, the ast_std_calloc function will be used to allocate<br>the vector's elements array.<br><br>void myfunc()<br>{<br> int i;<br> struct ast_vector_string *strings = get_somevector();<br> for (i = 0; i < AST_VECTOR_SIZE(strings); i++) {<br> AST_LOG(LOG_ERROR, "%s\n", AST_VECTOR_GET(strings, i));<br> }<br> AST_VECTOR_PTR_CLEANUP(strings);<br>}<br><br>Notice that myfunc() doesn't have to worry about how either<br>the vector itself or the elements were allocated. It just<br>calls AST_VECTOR_PTR_CLEANUP() and that macro uses ast_std_free<br>to free the elements, the internal "elems" array, and the vector<br>structure itself.<br><br>Change-Id: I0d871dfed9ce215fd11aa03e7d6cfaf385b55597<br>---<br>M include/asterisk/_private.h<br>M include/asterisk/astmm.h<br>M include/asterisk/vector.h<br>M main/asterisk.c<br>M main/astmm.c<br>M main/cli.c<br>M main/optional_api.c<br>7 files changed, 145 insertions(+), 11 deletions(-)<br><br></pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;">git pull ssh://gerrit.asterisk.org:29418/asterisk refs/changes/12/10612/1</pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;"><span>diff --git a/include/asterisk/_private.h b/include/asterisk/_private.h</span><br><span>index d954768..71f5a5b 100644</span><br><span>--- a/include/asterisk/_private.h</span><br><span>+++ b/include/asterisk/_private.h</span><br><span>@@ -56,6 +56,7 @@</span><br><span> void ast_msg_shutdown(void); /*!< Provided by message.c */</span><br><span> int aco_init(void); /*!< Provided by config_options.c */</span><br><span> int dns_core_init(void); /*!< Provided by dns_core.c */</span><br><span style="color: hsl(120, 100%, 40%);">+int ast_optional_api_init(void); /*!< Provided by optional_api.c */</span><br><span> </span><br><span> /*!</span><br><span> * \brief Initialize malloc debug phase 1.</span><br><span>diff --git a/include/asterisk/astmm.h b/include/asterisk/astmm.h</span><br><span>index e1f91dd..8b66fca 100644</span><br><span>--- a/include/asterisk/astmm.h</span><br><span>+++ b/include/asterisk/astmm.h</span><br><span>@@ -34,6 +34,7 @@</span><br><span> void *ast_std_malloc(size_t size) attribute_malloc;</span><br><span> void *ast_std_calloc(size_t nmemb, size_t size) attribute_malloc;</span><br><span> void *ast_std_realloc(void *ptr, size_t size);</span><br><span style="color: hsl(120, 100%, 40%);">+char *ast_std_strdup(const char *ptr);</span><br><span> void ast_std_free(void *ptr);</span><br><span> </span><br><span> /*!</span><br><span>@@ -44,6 +45,14 @@</span><br><span> */</span><br><span> void ast_free_ptr(void *ptr);</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief calloc() wrapper</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * ast_calloc_ptr should be used when a function pointer for calloc() needs to be passed</span><br><span style="color: hsl(120, 100%, 40%);">+ * as the argument to a function. Otherwise, astmm will cause seg faults.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+void *ast_calloc_ptr(size_t nmemb, size_t size);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> void *__ast_calloc(size_t nmemb, size_t size, const char *file, int lineno, const char *func) attribute_malloc;</span><br><span> void *__ast_calloc_cache(size_t nmemb, size_t size, const char *file, int lineno, const char *func) attribute_malloc;</span><br><span> void *__ast_malloc(size_t size, const char *file, int lineno, const char *func) attribute_malloc;</span><br><span>diff --git a/include/asterisk/vector.h b/include/asterisk/vector.h</span><br><span>index d1b2973..61ddcc3 100644</span><br><span>--- a/include/asterisk/vector.h</span><br><span>+++ b/include/asterisk/vector.h</span><br><span>@@ -35,6 +35,9 @@</span><br><span> * \since 12</span><br><span> */</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+typedef void *(*_calloc_fn)(size_t count, size_t len);</span><br><span style="color: hsl(120, 100%, 40%);">+typedef void (*_free_fn)(void *ptr);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> /*!</span><br><span> * \brief Define a vector structure</span><br><span> *</span><br><span>@@ -46,6 +49,9 @@</span><br><span> type *elems; \</span><br><span> size_t max; \</span><br><span> size_t current; \</span><br><span style="color: hsl(120, 100%, 40%);">+ _calloc_fn calloc_fn; \</span><br><span style="color: hsl(120, 100%, 40%);">+ _free_fn free_fn; \</span><br><span style="color: hsl(120, 100%, 40%);">+ _free_fn elem_cleanup_fn; \</span><br><span> }</span><br><span> </span><br><span> /*! \brief Integer vector definition */</span><br><span>@@ -96,6 +102,9 @@</span><br><span> size_t max; \</span><br><span> size_t current; \</span><br><span> ast_rwlock_t lock; \</span><br><span style="color: hsl(120, 100%, 40%);">+ _calloc_fn calloc_fn; \</span><br><span style="color: hsl(120, 100%, 40%);">+ _free_fn free_fn; \</span><br><span style="color: hsl(120, 100%, 40%);">+ _free_fn elem_cleanup_fn; \</span><br><span> }</span><br><span> </span><br><span> /*!</span><br><span>@@ -113,7 +122,44 @@</span><br><span> #define AST_VECTOR_INIT(vec, size) ({ \</span><br><span> size_t __size = (size); \</span><br><span> size_t alloc_size = __size * sizeof(*((vec)->elems)); \</span><br><span style="color: hsl(0, 100%, 40%);">- (vec)->elems = alloc_size ? ast_calloc(1, alloc_size) : NULL; \</span><br><span style="color: hsl(120, 100%, 40%);">+ (vec)->calloc_fn = (ast_calloc_ptr); \</span><br><span style="color: hsl(120, 100%, 40%);">+ (vec)->free_fn = (ast_free_ptr); \</span><br><span style="color: hsl(120, 100%, 40%);">+ (vec)->elem_cleanup_fn = NULL; \</span><br><span style="color: hsl(120, 100%, 40%);">+ (vec)->elems = alloc_size ? (vec)->calloc_fn(1, alloc_size) : NULL; \</span><br><span style="color: hsl(120, 100%, 40%);">+ (vec)->current = 0; \</span><br><span style="color: hsl(120, 100%, 40%);">+ if ((vec)->elems) { \</span><br><span style="color: hsl(120, 100%, 40%);">+ (vec)->max = __size; \</span><br><span style="color: hsl(120, 100%, 40%);">+ } else { \</span><br><span style="color: hsl(120, 100%, 40%);">+ (vec)->max = 0; \</span><br><span style="color: hsl(120, 100%, 40%);">+ } \</span><br><span style="color: hsl(120, 100%, 40%);">+ (alloc_size == 0 || (vec)->elems != NULL) ? 0 : -1; \</span><br><span style="color: hsl(120, 100%, 40%);">+})</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Initialize a vector with specified memory management functions</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * If \a size is 0, then no space will be allocated until the vector is</span><br><span style="color: hsl(120, 100%, 40%);">+ * appended to.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * This macro is primarily intended for use by functions that return vectors.</span><br><span style="color: hsl(120, 100%, 40%);">+ * It enables them to hide the details of memory management and element cleanup.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param vec Vector to initialize.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param size Initial size of the vector.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param calloc_fn The zero-fill allocation function.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param free_fn The free function.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param elem_cleanup_fn An optional element cleanup function.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \return 0 on success.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \return Non-zero on failure.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+#define AST_VECTOR_INIT_MM_FN(vec, size, __calloc_fn, __free_fn, __elem_cleanup_fn) ({ \</span><br><span style="color: hsl(120, 100%, 40%);">+ size_t __size = (size); \</span><br><span style="color: hsl(120, 100%, 40%);">+ size_t alloc_size = __size * sizeof(*((vec)->elems)); \</span><br><span style="color: hsl(120, 100%, 40%);">+ (vec)->calloc_fn = (__calloc_fn); \</span><br><span style="color: hsl(120, 100%, 40%);">+ (vec)->free_fn = (__free_fn); \</span><br><span style="color: hsl(120, 100%, 40%);">+ (vec)->elem_cleanup_fn = (__elem_cleanup_fn); \</span><br><span style="color: hsl(120, 100%, 40%);">+ (vec)->elems = alloc_size ? (vec)->calloc_fn(1, alloc_size) : NULL; \</span><br><span> (vec)->current = 0; \</span><br><span> if ((vec)->elems) { \</span><br><span> (vec)->max = __size; \</span><br><span>@@ -172,7 +218,9 @@</span><br><span> * \param vec Vector to deallocate.</span><br><span> */</span><br><span> #define AST_VECTOR_FREE(vec) do { \</span><br><span style="color: hsl(0, 100%, 40%);">- ast_free((vec)->elems); \</span><br><span style="color: hsl(120, 100%, 40%);">+ if ((vec)->free_fn) { \</span><br><span style="color: hsl(120, 100%, 40%);">+ (vec)->free_fn((vec)->elems); \</span><br><span style="color: hsl(120, 100%, 40%);">+ } \</span><br><span> (vec)->elems = NULL; \</span><br><span> (vec)->max = 0; \</span><br><span> (vec)->current = 0; \</span><br><span>@@ -187,8 +235,45 @@</span><br><span> * \param vec Pointer to a malloc'd vector structure.</span><br><span> */</span><br><span> #define AST_VECTOR_PTR_FREE(vec) do { \</span><br><span style="color: hsl(120, 100%, 40%);">+ _free_fn ff = (vec)->free_fn; \</span><br><span> AST_VECTOR_FREE(vec); \</span><br><span style="color: hsl(0, 100%, 40%);">- ast_free(vec); \</span><br><span style="color: hsl(120, 100%, 40%);">+ if (ff) { \</span><br><span style="color: hsl(120, 100%, 40%);">+ ff(vec); \</span><br><span style="color: hsl(120, 100%, 40%);">+ } \</span><br><span style="color: hsl(120, 100%, 40%);">+} while (0)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Completely deallocates this vector using the element cleanup function.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * If no element_cleanup_fn was supplied when this vector was initialized, a simple</span><br><span style="color: hsl(120, 100%, 40%);">+ * AST_VECTOR_FREE is done.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param vec Pointer to a vector structure.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+#define AST_VECTOR_CLEANUP(vec) do { \</span><br><span style="color: hsl(120, 100%, 40%);">+ size_t __idx; \</span><br><span style="color: hsl(120, 100%, 40%);">+ if ((vec)->elem_cleanup_fn) { \</span><br><span style="color: hsl(120, 100%, 40%);">+ for (__idx = 0; __idx < (vec)->current; __idx++) { \</span><br><span style="color: hsl(120, 100%, 40%);">+ (vec)->elem_cleanup_fn((vec)->elems[__idx]); \</span><br><span style="color: hsl(120, 100%, 40%);">+ } \</span><br><span style="color: hsl(120, 100%, 40%);">+ } \</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_VECTOR_FREE(vec); \</span><br><span style="color: hsl(120, 100%, 40%);">+} while (0)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Completely deallocates this malloc'd vector using the element cleanup function.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * If no element_cleanup_fn was supplied when this vector was initialized, a simple</span><br><span style="color: hsl(120, 100%, 40%);">+ * AST_VECTOR_PTR_FREE is done.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param vec Pointer to a malloc'd vector structure.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+#define AST_VECTOR_PTR_CLEANUP(vec) do { \</span><br><span style="color: hsl(120, 100%, 40%);">+ _free_fn ff = (vec)->free_fn; \</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_VECTOR_CLEANUP(vec); \</span><br><span style="color: hsl(120, 100%, 40%);">+ if (ff) { \</span><br><span style="color: hsl(120, 100%, 40%);">+ ff(vec); \</span><br><span style="color: hsl(120, 100%, 40%);">+ } \</span><br><span> } while (0)</span><br><span> </span><br><span> /*!</span><br><span>@@ -213,8 +298,11 @@</span><br><span> * \param vec Pointer to a malloc'd vector structure.</span><br><span> */</span><br><span> #define AST_VECTOR_RW_PTR_FREE(vec) do { \</span><br><span style="color: hsl(120, 100%, 40%);">+ _free_fn ff = (vec)->free_fn; \</span><br><span> AST_VECTOR_RW_FREE(vec); \</span><br><span style="color: hsl(0, 100%, 40%);">- ast_free(vec); \</span><br><span style="color: hsl(120, 100%, 40%);">+ if (ff) { \</span><br><span style="color: hsl(120, 100%, 40%);">+ ff(vec); \</span><br><span style="color: hsl(120, 100%, 40%);">+ } \</span><br><span> } while(0)</span><br><span> </span><br><span> /*!</span><br><span>@@ -225,7 +313,7 @@</span><br><span> do { \</span><br><span> if ((idx) >= (vec)->max) { \</span><br><span> size_t new_max = ((idx) + 1) * 2; \</span><br><span style="color: hsl(0, 100%, 40%);">- typeof((vec)->elems) new_elems = ast_calloc(1, \</span><br><span style="color: hsl(120, 100%, 40%);">+ typeof((vec)->elems) new_elems = (vec)->calloc_fn(1, \</span><br><span> new_max * sizeof(*new_elems)); \</span><br><span> if (new_elems) { \</span><br><span> if ((vec)->elems) { \</span><br><span>@@ -744,6 +832,8 @@</span><br><span> * elements in a new vector</span><br><span> *</span><br><span> * This macro basically provides a filtered clone.</span><br><span style="color: hsl(120, 100%, 40%);">+ * The memory management functions used for the clone will be the same as the</span><br><span style="color: hsl(120, 100%, 40%);">+ * source vector.</span><br><span> *</span><br><span> * \param vec Vector to operate on.</span><br><span> * \param callback A callback that takes at least 1 argument (the element)</span><br><span>@@ -791,12 +881,13 @@</span><br><span> size_t idx; \</span><br><span> typeof((vec)) new_vec; \</span><br><span> do { \</span><br><span style="color: hsl(0, 100%, 40%);">- new_vec = ast_malloc(sizeof(*new_vec)); \</span><br><span style="color: hsl(120, 100%, 40%);">+ new_vec = (vec)->calloc_fn(1, sizeof(*new_vec)); \</span><br><span> if (!new_vec) { \</span><br><span> break; \</span><br><span> } \</span><br><span style="color: hsl(0, 100%, 40%);">- if (AST_VECTOR_INIT(new_vec, AST_VECTOR_SIZE((vec))) != 0) { \</span><br><span style="color: hsl(0, 100%, 40%);">- ast_free(new_vec); \</span><br><span style="color: hsl(120, 100%, 40%);">+ if (AST_VECTOR_INIT_MM_FN(new_vec, AST_VECTOR_SIZE((vec)), (vec)->calloc_fn, \</span><br><span style="color: hsl(120, 100%, 40%);">+ (vec)->free_fn, (vec)->elem_cleanup_fn) != 0) { \</span><br><span style="color: hsl(120, 100%, 40%);">+ (vec)->free_fn(new_vec); \</span><br><span> new_vec = NULL; \</span><br><span> break; \</span><br><span> } \</span><br><span>diff --git a/main/asterisk.c b/main/asterisk.c</span><br><span>index 3354724..28c6321 100644</span><br><span>--- a/main/asterisk.c</span><br><span>+++ b/main/asterisk.c</span><br><span>@@ -4108,6 +4108,7 @@</span><br><span> </span><br><span> ast_autoservice_init();</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+ check_init(ast_optional_api_init(), "Optional API");</span><br><span> check_init(ast_timing_init(), "Timing");</span><br><span> check_init(ast_ssl_init(), "SSL");</span><br><span> read_pjproject_startup_options();</span><br><span>diff --git a/main/astmm.c b/main/astmm.c</span><br><span>index c845ee7..76c1a5f 100644</span><br><span>--- a/main/astmm.c</span><br><span>+++ b/main/astmm.c</span><br><span>@@ -1752,6 +1752,11 @@</span><br><span> return realloc(ptr, size);</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+char *ast_std_strdup(const char *ptr)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ return strdup(ptr);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> void ast_std_free(void *ptr)</span><br><span> {</span><br><span> free(ptr);</span><br><span>@@ -1761,3 +1766,8 @@</span><br><span> {</span><br><span> ast_free(ptr);</span><br><span> }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+void *ast_calloc_ptr(size_t nmemb, size_t size)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ return ast_calloc(nmemb, size);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span>diff --git a/main/cli.c b/main/cli.c</span><br><span>index cf51d0d..1331dea 100644</span><br><span>--- a/main/cli.c</span><br><span>+++ b/main/cli.c</span><br><span>@@ -2534,7 +2534,7 @@</span><br><span> char *retstr, *prevstr;</span><br><span> size_t max_equal;</span><br><span> size_t which = 0;</span><br><span style="color: hsl(0, 100%, 40%);">- struct ast_vector_string *vec = ast_calloc(1, sizeof(*vec));</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_vector_string *vec = ast_malloc(sizeof(*vec));</span><br><span> </span><br><span> /* Recursion into this function is a coding error. */</span><br><span> ast_assert(!ast_threadstorage_get_ptr(&completion_storage));</span><br><span>@@ -2543,6 +2543,8 @@</span><br><span> return NULL;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+ AST_VECTOR_INIT(vec, 0);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> if (ast_threadstorage_set_ptr(&completion_storage, vec)) {</span><br><span> ast_log(LOG_ERROR, "Failed to initialize threadstorage for completion.\n");</span><br><span> ast_free(vec);</span><br><span>@@ -2607,8 +2609,7 @@</span><br><span> return vec;</span><br><span> </span><br><span> vector_cleanup:</span><br><span style="color: hsl(0, 100%, 40%);">- AST_VECTOR_CALLBACK_VOID(vec, ast_free);</span><br><span style="color: hsl(0, 100%, 40%);">- AST_VECTOR_PTR_FREE(vec);</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_VECTOR_PTR_CLEANUP(vec);</span><br><span> </span><br><span> return NULL;</span><br><span> }</span><br><span>diff --git a/main/optional_api.c b/main/optional_api.c</span><br><span>index d63129c..75e0b22 100644</span><br><span>--- a/main/optional_api.c</span><br><span>+++ b/main/optional_api.c</span><br><span>@@ -18,6 +18,7 @@</span><br><span> </span><br><span> #include "asterisk.h"</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/_private.h"</span><br><span> #include "asterisk/optional_api.h"</span><br><span> #include "asterisk/utils.h"</span><br><span> #include "asterisk/vector.h"</span><br><span>@@ -143,6 +144,7 @@</span><br><span> return NULL;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+ AST_VECTOR_INIT(&api->users, 0);</span><br><span> strcpy(api->symname, symname); /* SAFE */</span><br><span> </span><br><span> return api;</span><br><span>@@ -267,4 +269,23 @@</span><br><span> }</span><br><span> }</span><br><span> }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> #endif /* defined(OPTIONAL_API) */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void optional_api_shutdown(void)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+#ifdef OPTIONAL_API</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_VECTOR_CLEANUP(&apis);</span><br><span style="color: hsl(120, 100%, 40%);">+#endif</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+int ast_optional_api_init(void)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+#ifdef OPTIONAL_API</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_VECTOR_INIT(&apis, 0);</span><br><span style="color: hsl(120, 100%, 40%);">+#endif</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_register_cleanup(optional_api_shutdown);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span></span><br></pre><p>To view, visit <a href="https://gerrit.asterisk.org/10612">change 10612</a>. To unsubscribe, or for help writing mail filters, visit <a href="https://gerrit.asterisk.org/settings">settings</a>.</p><div itemscope itemtype="http://schema.org/EmailMessage"><div itemscope itemprop="action" itemtype="http://schema.org/ViewAction"><link itemprop="url" href="https://gerrit.asterisk.org/10612"/><meta itemprop="name" content="View Change"/></div></div>
<div style="display:none"> Gerrit-Project: asterisk </div>
<div style="display:none"> Gerrit-Branch: 16 </div>
<div style="display:none"> Gerrit-MessageType: newchange </div>
<div style="display:none"> Gerrit-Change-Id: I0d871dfed9ce215fd11aa03e7d6cfaf385b55597 </div>
<div style="display:none"> Gerrit-Change-Number: 10612 </div>
<div style="display:none"> Gerrit-PatchSet: 1 </div>
<div style="display:none"> Gerrit-Owner: George Joseph <gjoseph@digium.com> </div>