diff options
Diffstat (limited to 'main/sorcery.c')
-rw-r--r-- | main/sorcery.c | 315 |
1 files changed, 312 insertions, 3 deletions
diff --git a/main/sorcery.c b/main/sorcery.c index d0aee4a92..aa48222dd 100644 --- a/main/sorcery.c +++ b/main/sorcery.c @@ -38,6 +38,9 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/config_options.h" #include "asterisk/netsock2.h" #include "asterisk/module.h" +#include "asterisk/taskprocessor.h" +#include "asterisk/threadpool.h" +#include "asterisk/json.h" /* To prevent DEBUG_FD_LEAKS from interfering with things we undef open and close */ #undef open @@ -52,6 +55,9 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") /*! \brief Maximum length of an object field name */ #define MAX_OBJECT_FIELD 128 +/*! \brief Thread pool for observers */ +static struct ast_threadpool *threadpool; + /*! \brief Structure for registered object type */ struct ast_sorcery_object_type { /*! \brief Unique name of the object type */ @@ -83,6 +89,27 @@ struct ast_sorcery_object_type { /*! \brief Type details */ struct aco_type type; + + /*! \brief Observers */ + struct ao2_container *observers; + + /*! \brief Serializer for observers */ + struct ast_taskprocessor *serializer; +}; + +/*! \brief Structure for registered object type observer */ +struct ast_sorcery_object_type_observer { + /*! \brief Pointer to the observer implementation */ + const struct ast_sorcery_observer *callbacks; +}; + +/*! \brief Structure used for observer invocations */ +struct sorcery_observer_invocation { + /*! \brief Pointer to the object type */ + struct ast_sorcery_object_type *object_type; + + /*! \brief Pointer to the object */ + void *object; }; /*! \brief Structure for registered object field */ @@ -213,9 +240,21 @@ static int sorcery_wizard_cmp(void *obj, void *arg, int flags) int ast_sorcery_init(void) { + struct ast_threadpool_options options = { + .version = AST_THREADPOOL_OPTIONS_VERSION, + .auto_increment = 1, + .max_size = 0, + .idle_timeout = 60, + .initial_size = 0, + }; ast_assert(wizards == NULL); + if (!(threadpool = ast_threadpool_create("Sorcery", NULL, &options))) { + return -1; + } + if (!(wizards = ao2_container_alloc(WIZARD_BUCKETS, sorcery_wizard_hash, sorcery_wizard_cmp))) { + ast_threadpool_shutdown(threadpool); return -1; } @@ -317,6 +356,7 @@ static void sorcery_object_type_destructor(void *obj) ao2_cleanup(object_type->wizards); ao2_cleanup(object_type->fields); + ao2_cleanup(object_type->observers); if (object_type->info) { aco_info_destroy(object_type->info); @@ -324,12 +364,15 @@ static void sorcery_object_type_destructor(void *obj) } ast_free(object_type->file); + + ast_taskprocessor_unreference(object_type->serializer); } /*! \brief Internal function which allocates an object type structure */ static struct ast_sorcery_object_type *sorcery_object_type_alloc(const char *type, const char *module) { struct ast_sorcery_object_type *object_type; + char uuid[AST_UUID_STR_LEN]; if (!(object_type = ao2_alloc(sizeof(*object_type), sorcery_object_type_destructor))) { return NULL; @@ -346,6 +389,11 @@ static struct ast_sorcery_object_type *sorcery_object_type_alloc(const char *typ return NULL; } + if (!(object_type->observers = ao2_container_alloc_options(AO2_ALLOC_OPT_LOCK_RWLOCK, 1, NULL, NULL))) { + ao2_ref(object_type, -1); + return NULL; + } + if (!(object_type->info = ast_calloc(1, sizeof(*object_type->info) + 2 * sizeof(object_type->info->files[0])))) { ao2_ref(object_type, -1); return NULL; @@ -356,6 +404,16 @@ static struct ast_sorcery_object_type *sorcery_object_type_alloc(const char *typ return NULL; } + if (!ast_uuid_generate_str(uuid, sizeof(uuid))) { + ao2_ref(object_type, -1); + return NULL; + } + + if (!(object_type->serializer = ast_threadpool_serializer(uuid, threadpool))) { + ao2_ref(object_type, -1); + return NULL; + } + object_type->info->files[0] = object_type->file; object_type->info->files[1] = NULL; object_type->info->module = module; @@ -597,6 +655,58 @@ static int sorcery_wizard_load(void *obj, void *arg, int flags) return 0; } +/*! \brief Destructor for observer invocation */ +static void sorcery_observer_invocation_destroy(void *obj) +{ + struct sorcery_observer_invocation *invocation = obj; + + ao2_cleanup(invocation->object_type); + ao2_cleanup(invocation->object); +} + +/*! \brief Allocator function for observer invocation */ +static struct sorcery_observer_invocation *sorcery_observer_invocation_alloc(struct ast_sorcery_object_type *object_type, void *object) +{ + struct sorcery_observer_invocation *invocation = ao2_alloc(sizeof(*invocation), sorcery_observer_invocation_destroy); + + if (!invocation) { + return NULL; + } + + ao2_ref(object_type, +1); + invocation->object_type = object_type; + + if (object) { + ao2_ref(object, +1); + invocation->object = object; + } + + return invocation; +} + +/*! \brief Internal callback function which notifies an individual observer that an object type has been loaded */ +static int sorcery_observer_notify_loaded(void *obj, void *arg, int flags) +{ + const struct ast_sorcery_object_type_observer *observer = obj; + + if (observer->callbacks->loaded) { + observer->callbacks->loaded(arg); + } + + return 0; +} + +/*! \brief Internal callback function which notifies observers that an object type has been loaded */ +static int sorcery_observers_notify_loaded(void *data) +{ + struct sorcery_observer_invocation *invocation = data; + + ao2_callback(invocation->object_type->observers, OBJ_NODATA, sorcery_observer_notify_loaded, invocation->object_type->name); + ao2_cleanup(invocation); + + return 0; +} + static int sorcery_object_load(void *obj, void *arg, int flags) { struct ast_sorcery_object_type *type = obj; @@ -605,6 +715,14 @@ static int sorcery_object_load(void *obj, void *arg, int flags) details->type = type->name; ao2_callback(type->wizards, OBJ_NODATA, sorcery_wizard_load, details); + if (ao2_container_count(type->observers)) { + struct sorcery_observer_invocation *invocation = sorcery_observer_invocation_alloc(type, NULL); + + if (invocation && ast_taskprocessor_push(type->serializer, sorcery_observers_notify_loaded, invocation)) { + ao2_cleanup(invocation); + } + } + return 0; } @@ -715,6 +833,68 @@ struct ast_variable *ast_sorcery_objectset_create(const struct ast_sorcery *sorc return fields; } +struct ast_json *ast_sorcery_objectset_json_create(const struct ast_sorcery *sorcery, const void *object) +{ + const struct ast_sorcery_object_details *details = object; + RAII_VAR(struct ast_sorcery_object_type *, object_type, ao2_find(sorcery->types, details->type, OBJ_KEY), ao2_cleanup); + struct ao2_iterator i; + struct ast_sorcery_object_field *object_field; + struct ast_json *json = ast_json_object_create(); + int res = 0; + + if (!object_type || !json) { + return NULL; + } + + i = ao2_iterator_init(object_type->fields, 0); + + for (; (object_field = ao2_iterator_next(&i)) && !res; ao2_ref(object_field, -1)) { + if (object_field->multiple_handler) { + struct ast_variable *tmp = NULL; + struct ast_variable *field; + + if ((res = object_field->multiple_handler(object, &tmp))) { + break; + } + + for (field = tmp; field; field = field->next) { + struct ast_json *value = ast_json_string_create(field->value); + + if (value && ast_json_object_set(json, field->name, value)) { + ast_json_unref(value); + res = -1; + } + } + + ast_variables_destroy(tmp); + } else if (object_field->handler) { + char *buf = NULL; + struct ast_json *value = NULL; + + if ((res = object_field->handler(object, object_field->args, &buf)) || + !(value = ast_json_string_create(buf)) || + ast_json_object_set(json, object_field->name, value)) { + ast_json_unref(value); + res = -1; + } + + ast_free(buf); + } else { + continue; + } + } + + ao2_iterator_destroy(&i); + + /* If any error occurs we destroy the JSON object so a partial objectset is not returned */ + if (res) { + ast_json_unref(json); + json = NULL; + } + + return json; +} + int ast_sorcery_objectset_apply(const struct ast_sorcery *sorcery, void *object, struct ast_variable *objectset) { const struct ast_sorcery_object_details *details = object; @@ -1016,6 +1196,29 @@ static int sorcery_wizard_create(void *obj, void *arg, int flags) return (!object_wizard->caching && !object_wizard->wizard->create(details->sorcery, object_wizard->data, details->obj)) ? CMP_MATCH | CMP_STOP : 0; } +/*! \brief Internal callback function which notifies an individual observer that an object has been created */ +static int sorcery_observer_notify_create(void *obj, void *arg, int flags) +{ + const struct ast_sorcery_object_type_observer *observer = obj; + + if (observer->callbacks->created) { + observer->callbacks->created(arg); + } + + return 0; +} + +/*! \brief Internal callback function which notifies observers that an object has been created */ +static int sorcery_observers_notify_create(void *data) +{ + struct sorcery_observer_invocation *invocation = data; + + ao2_callback(invocation->object_type->observers, OBJ_NODATA, sorcery_observer_notify_create, invocation->object); + ao2_cleanup(invocation); + + return 0; +} + int ast_sorcery_create(const struct ast_sorcery *sorcery, void *object) { const struct ast_sorcery_object_details *details = object; @@ -1030,11 +1233,41 @@ int ast_sorcery_create(const struct ast_sorcery *sorcery, void *object) return -1; } - object_wizard = ao2_callback(object_type->wizards, 0, sorcery_wizard_create, &sdetails); + if ((object_wizard = ao2_callback(object_type->wizards, 0, sorcery_wizard_create, &sdetails)) && + ao2_container_count(object_type->observers)) { + struct sorcery_observer_invocation *invocation = sorcery_observer_invocation_alloc(object_type, object); + + if (invocation && ast_taskprocessor_push(object_type->serializer, sorcery_observers_notify_create, invocation)) { + ao2_cleanup(invocation); + } + } return object_wizard ? 0 : -1; } +/*! \brief Internal callback function which notifies an individual observer that an object has been updated */ +static int sorcery_observer_notify_update(void *obj, void *arg, int flags) +{ + const struct ast_sorcery_object_type_observer *observer = obj; + + if (observer->callbacks->updated) { + observer->callbacks->updated(arg); + } + + return 0; +} + +/*! \brief Internal callback function which notifies observers that an object has been updated */ +static int sorcery_observers_notify_update(void *data) +{ + struct sorcery_observer_invocation *invocation = data; + + ao2_callback(invocation->object_type->observers, OBJ_NODATA, sorcery_observer_notify_update, invocation->object); + ao2_cleanup(invocation); + + return 0; +} + /*! \brief Internal function which returns if a wizard has updated the object */ static int sorcery_wizard_update(void *obj, void *arg, int flags) { @@ -1059,11 +1292,41 @@ int ast_sorcery_update(const struct ast_sorcery *sorcery, void *object) return -1; } - object_wizard = ao2_callback(object_type->wizards, 0, sorcery_wizard_update, &sdetails); + if ((object_wizard = ao2_callback(object_type->wizards, 0, sorcery_wizard_update, &sdetails)) && + ao2_container_count(object_type->observers)) { + struct sorcery_observer_invocation *invocation = sorcery_observer_invocation_alloc(object_type, object); + + if (invocation && ast_taskprocessor_push(object_type->serializer, sorcery_observers_notify_update, invocation)) { + ao2_cleanup(invocation); + } + } return object_wizard ? 0 : -1; } +/*! \brief Internal callback function which notifies an individual observer that an object has been deleted */ +static int sorcery_observer_notify_delete(void *obj, void *arg, int flags) +{ + const struct ast_sorcery_object_type_observer *observer = obj; + + if (observer->callbacks->deleted) { + observer->callbacks->deleted(arg); + } + + return 0; +} + +/*! \brief Internal callback function which notifies observers that an object has been deleted */ +static int sorcery_observers_notify_delete(void *data) +{ + struct sorcery_observer_invocation *invocation = data; + + ao2_callback(invocation->object_type->observers, OBJ_NODATA, sorcery_observer_notify_delete, invocation->object); + ao2_cleanup(invocation); + + return 0; +} + /*! \brief Internal function which returns if a wizard has deleted the object */ static int sorcery_wizard_delete(void *obj, void *arg, int flags) { @@ -1088,7 +1351,14 @@ int ast_sorcery_delete(const struct ast_sorcery *sorcery, void *object) return -1; } - object_wizard = ao2_callback(object_type->wizards, 0, sorcery_wizard_delete, &sdetails); + if ((object_wizard = ao2_callback(object_type->wizards, 0, sorcery_wizard_delete, &sdetails)) && + ao2_container_count(object_type->observers)) { + struct sorcery_observer_invocation *invocation = sorcery_observer_invocation_alloc(object_type, object); + + if (invocation && ast_taskprocessor_push(object_type->serializer, sorcery_observers_notify_delete, invocation)) { + ao2_cleanup(invocation); + } + } return object_wizard ? 0 : -1; } @@ -1109,3 +1379,42 @@ const char *ast_sorcery_object_get_type(const void *object) const struct ast_sorcery_object_details *details = object; return details->type; } + +int ast_sorcery_observer_add(const struct ast_sorcery *sorcery, const char *type, const struct ast_sorcery_observer *callbacks) +{ + RAII_VAR(struct ast_sorcery_object_type *, object_type, ao2_find(sorcery->types, type, OBJ_KEY), ao2_cleanup); + struct ast_sorcery_object_type_observer *observer; + + if (!object_type || !callbacks) { + return -1; + } + + if (!(observer = ao2_alloc(sizeof(*observer), NULL))) { + return -1; + } + + observer->callbacks = callbacks; + ao2_link(object_type->observers, observer); + ao2_ref(observer, -1); + + return 0; +} + +/*! \brief Internal callback function for removing an observer */ +static int sorcery_observer_remove(void *obj, void *arg, int flags) +{ + const struct ast_sorcery_object_type_observer *observer = obj; + + return (observer->callbacks == arg) ? CMP_MATCH | CMP_STOP : 0; +} + +void ast_sorcery_observer_remove(const struct ast_sorcery *sorcery, const char *type, struct ast_sorcery_observer *callbacks) +{ + RAII_VAR(struct ast_sorcery_object_type *, object_type, ao2_find(sorcery->types, type, OBJ_KEY), ao2_cleanup); + + if (!object_type) { + return; + } + + ao2_callback(object_type->observers, OBJ_NODATA | OBJ_UNLINK, sorcery_observer_remove, callbacks); +} |