mirror of
https://github.com/asterisk/asterisk.git
synced 2025-11-19 00:00:09 +00:00
Merge "sorcery: Add support for object staleness"
This commit is contained in:
@@ -312,6 +312,9 @@ struct ast_sorcery_wizard {
|
|||||||
|
|
||||||
/*! \brief Callback for closing a wizard */
|
/*! \brief Callback for closing a wizard */
|
||||||
void (*close)(void *data);
|
void (*close)(void *data);
|
||||||
|
|
||||||
|
/* \brief Callback for whether or not the wizard believes the object is stale */
|
||||||
|
int (*is_stale)(const struct ast_sorcery *sorcery, void *data, void *object);
|
||||||
};
|
};
|
||||||
|
|
||||||
/*! \brief Interface for a sorcery object type observer */
|
/*! \brief Interface for a sorcery object type observer */
|
||||||
@@ -1201,6 +1204,20 @@ int ast_sorcery_update(const struct ast_sorcery *sorcery, void *object);
|
|||||||
*/
|
*/
|
||||||
int ast_sorcery_delete(const struct ast_sorcery *sorcery, void *object);
|
int ast_sorcery_delete(const struct ast_sorcery *sorcery, void *object);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Determine if a sorcery object is stale with respect to its backing datastore
|
||||||
|
* \since 14.0.0
|
||||||
|
*
|
||||||
|
* This function will query the wizard(s) backing the particular sorcery object to
|
||||||
|
* determine if the in-memory object is now stale. No action is taken to update
|
||||||
|
* the object. Callers of this function may use one of the ast_sorcery_retrieve
|
||||||
|
* functions to obtain a new instance of the object if desired.
|
||||||
|
*
|
||||||
|
* \retval 0 the object is not stale
|
||||||
|
* \retval 1 the object is stale
|
||||||
|
*/
|
||||||
|
int ast_sorcery_is_stale(const struct ast_sorcery *sorcery, void *object);
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \brief Decrease the reference count of a sorcery structure
|
* \brief Decrease the reference count of a sorcery structure
|
||||||
*
|
*
|
||||||
@@ -1217,6 +1234,16 @@ void ast_sorcery_unref(struct ast_sorcery *sorcery);
|
|||||||
*/
|
*/
|
||||||
const char *ast_sorcery_object_get_id(const void *object);
|
const char *ast_sorcery_object_get_id(const void *object);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \since 14.0.0
|
||||||
|
* \brief Get when the socery object was created
|
||||||
|
*
|
||||||
|
* \param object Pointer to a sorcery object
|
||||||
|
*
|
||||||
|
* \retval The time when the object was created
|
||||||
|
*/
|
||||||
|
const struct timeval ast_sorcery_object_get_created(const void *object);
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \brief Get the type of a sorcery object
|
* \brief Get the type of a sorcery object
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -129,6 +129,9 @@ struct ast_sorcery_object {
|
|||||||
|
|
||||||
/*! \brief Extended object fields */
|
/*! \brief Extended object fields */
|
||||||
struct ast_variable *extended;
|
struct ast_variable *extended;
|
||||||
|
|
||||||
|
/*! \brief Time that the object was created */
|
||||||
|
struct timeval created;
|
||||||
};
|
};
|
||||||
|
|
||||||
/*! \brief Structure for registered object type */
|
/*! \brief Structure for registered object type */
|
||||||
@@ -1734,6 +1737,7 @@ void *ast_sorcery_alloc(const struct ast_sorcery *sorcery, const char *type, con
|
|||||||
details->object->id = ast_strdup(id);
|
details->object->id = ast_strdup(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
details->object->created = ast_tvnow();
|
||||||
ast_copy_string(details->object->type, type, sizeof(details->object->type));
|
ast_copy_string(details->object->type, type, sizeof(details->object->type));
|
||||||
|
|
||||||
if (aco_set_defaults(&object_type->type, id, details)) {
|
if (aco_set_defaults(&object_type->type, id, details)) {
|
||||||
@@ -2143,6 +2147,35 @@ int ast_sorcery_delete(const struct ast_sorcery *sorcery, void *object)
|
|||||||
return object_wizard ? 0 : -1;
|
return object_wizard ? 0 : -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int ast_sorcery_is_stale(const struct ast_sorcery *sorcery, void *object)
|
||||||
|
{
|
||||||
|
const struct ast_sorcery_object_details *details = object;
|
||||||
|
RAII_VAR(struct ast_sorcery_object_type *, object_type, ao2_find(sorcery->types, details->object->type, OBJ_KEY), ao2_cleanup);
|
||||||
|
struct ast_sorcery_object_wizard *found_wizard;
|
||||||
|
int res = 0;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (!object_type) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
AST_VECTOR_RW_RDLOCK(&object_type->wizards);
|
||||||
|
for (i = 0; i < AST_VECTOR_SIZE(&object_type->wizards); i++) {
|
||||||
|
found_wizard = AST_VECTOR_GET(&object_type->wizards, i);
|
||||||
|
|
||||||
|
if (found_wizard->wizard->callbacks.is_stale) {
|
||||||
|
res |= found_wizard->wizard->callbacks.is_stale(sorcery, found_wizard->data, object);
|
||||||
|
ast_debug(5, "After calling wizard '%s', object '%s' is %s\n",
|
||||||
|
found_wizard->wizard->callbacks.name,
|
||||||
|
ast_sorcery_object_get_id(object),
|
||||||
|
res ? "stale" : "not stale");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
AST_VECTOR_RW_UNLOCK(&object_type->wizards);
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
void ast_sorcery_unref(struct ast_sorcery *sorcery)
|
void ast_sorcery_unref(struct ast_sorcery *sorcery)
|
||||||
{
|
{
|
||||||
if (sorcery) {
|
if (sorcery) {
|
||||||
@@ -2161,6 +2194,12 @@ const char *ast_sorcery_object_get_id(const void *object)
|
|||||||
return details->object->id;
|
return details->object->id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const struct timeval ast_sorcery_object_get_created(const void *object)
|
||||||
|
{
|
||||||
|
const struct ast_sorcery_object_details *details = object;
|
||||||
|
return details->object->created;
|
||||||
|
}
|
||||||
|
|
||||||
const char *ast_sorcery_object_get_type(const void *object)
|
const char *ast_sorcery_object_get_type(const void *object)
|
||||||
{
|
{
|
||||||
const struct ast_sorcery_object_details *details = object;
|
const struct ast_sorcery_object_details *details = object;
|
||||||
|
|||||||
@@ -138,6 +138,9 @@ struct sorcery_test_caching {
|
|||||||
/*! \brief Whether the object has been deleted from the cache or not */
|
/*! \brief Whether the object has been deleted from the cache or not */
|
||||||
unsigned int deleted:1;
|
unsigned int deleted:1;
|
||||||
|
|
||||||
|
/*! \brief Whether the object is stale or not */
|
||||||
|
unsigned int is_stale:1;
|
||||||
|
|
||||||
/*! \brief Object to return when asked */
|
/*! \brief Object to return when asked */
|
||||||
struct test_sorcery_object object;
|
struct test_sorcery_object object;
|
||||||
};
|
};
|
||||||
@@ -217,6 +220,12 @@ static int sorcery_test_delete(const struct ast_sorcery *sorcery, void *data, vo
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int sorcery_test_is_stale(const struct ast_sorcery *sorcery, void *data, void *object)
|
||||||
|
{
|
||||||
|
cache.is_stale = 1;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
/*! \brief Dummy sorcery wizards, not actually used so we only populate the name and nothing else */
|
/*! \brief Dummy sorcery wizards, not actually used so we only populate the name and nothing else */
|
||||||
static struct ast_sorcery_wizard test_wizard = {
|
static struct ast_sorcery_wizard test_wizard = {
|
||||||
.name = "test",
|
.name = "test",
|
||||||
@@ -234,6 +243,7 @@ static struct ast_sorcery_wizard test_wizard2 = {
|
|||||||
.retrieve_id = sorcery_test_retrieve_id,
|
.retrieve_id = sorcery_test_retrieve_id,
|
||||||
.update = sorcery_test_update,
|
.update = sorcery_test_update,
|
||||||
.delete = sorcery_test_delete,
|
.delete = sorcery_test_delete,
|
||||||
|
.is_stale = sorcery_test_is_stale,
|
||||||
};
|
};
|
||||||
|
|
||||||
static void sorcery_observer_created(const void *object)
|
static void sorcery_observer_created(const void *object)
|
||||||
@@ -2223,6 +2233,84 @@ AST_TEST_DEFINE(object_delete_uncreated)
|
|||||||
return AST_TEST_PASS;
|
return AST_TEST_PASS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AST_TEST_DEFINE(object_is_stale)
|
||||||
|
{
|
||||||
|
RAII_VAR(struct ast_sorcery *, sorcery, NULL, ast_sorcery_unref);
|
||||||
|
RAII_VAR(struct ast_sorcery_wizard *, wizard1, &test_wizard, ast_sorcery_wizard_unregister);
|
||||||
|
RAII_VAR(struct ast_sorcery_wizard *, wizard2, &test_wizard2, ast_sorcery_wizard_unregister);
|
||||||
|
RAII_VAR(struct test_sorcery_object *, obj1, NULL, ao2_cleanup);
|
||||||
|
RAII_VAR(struct test_sorcery_object *, obj2, NULL, ao2_cleanup);
|
||||||
|
|
||||||
|
switch (cmd) {
|
||||||
|
case TEST_INIT:
|
||||||
|
info->name = "object_is_stale";
|
||||||
|
info->category = "/main/sorcery/";
|
||||||
|
info->summary = "sorcery object staleness unit test";
|
||||||
|
info->description =
|
||||||
|
"Test whether sorcery will query a wizard correctly if asked\n"
|
||||||
|
"if an object is stale.";
|
||||||
|
return AST_TEST_NOT_RUN;
|
||||||
|
case TEST_EXECUTE:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ast_sorcery_wizard_register(&test_wizard)) {
|
||||||
|
ast_test_status_update(test, "Failed to register a perfectly valid sorcery wizard\n");
|
||||||
|
return AST_TEST_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ast_sorcery_wizard_register(&test_wizard2)) {
|
||||||
|
ast_test_status_update(test, "Failed to register a perfectly valid sorcery wizard\n");
|
||||||
|
return AST_TEST_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(sorcery = ast_sorcery_open())) {
|
||||||
|
ast_test_status_update(test, "Failed to open sorcery structure\n");
|
||||||
|
return AST_TEST_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((ast_sorcery_apply_default(sorcery, "test", "test", NULL) != AST_SORCERY_APPLY_SUCCESS) ||
|
||||||
|
ast_sorcery_internal_object_register(sorcery, "test", test_sorcery_object_alloc, NULL, NULL)) {
|
||||||
|
return AST_TEST_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_sorcery_object_field_register_nodoc(sorcery, "test", "bob", "5", OPT_UINT_T, 0, FLDSET(struct test_sorcery_object, bob));
|
||||||
|
ast_sorcery_object_field_register_nodoc(sorcery, "test", "joe", "10", OPT_UINT_T, 0, FLDSET(struct test_sorcery_object, joe));
|
||||||
|
ast_sorcery_object_field_register_custom_nodoc(sorcery, "test", "jim", "444", jim_handler, NULL, jim_vl, 0, 0);
|
||||||
|
ast_sorcery_object_field_register_custom_nodoc(sorcery, "test", "jack", "888,999", jack_handler, jack_str, NULL, 0, 0);
|
||||||
|
|
||||||
|
|
||||||
|
if ((ast_sorcery_apply_default(sorcery, "test2", "test2", "test2data") != AST_SORCERY_APPLY_SUCCESS) ||
|
||||||
|
ast_sorcery_internal_object_register(sorcery, "test2", test_sorcery_object_alloc, NULL, NULL)) {
|
||||||
|
return AST_TEST_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_sorcery_object_field_register_nodoc(sorcery, "test2", "bob", "5", OPT_UINT_T, 0, FLDSET(struct test_sorcery_object, bob));
|
||||||
|
ast_sorcery_object_field_register_nodoc(sorcery, "test2", "joe", "10", OPT_UINT_T, 0, FLDSET(struct test_sorcery_object, joe));
|
||||||
|
ast_sorcery_object_field_register_custom_nodoc(sorcery, "test2", "jim", "444", jim_handler, NULL, jim_vl, 0, 0);
|
||||||
|
ast_sorcery_object_field_register_custom_nodoc(sorcery, "test2", "jack", "888,999", jack_handler, jack_str, NULL, 0, 0);
|
||||||
|
|
||||||
|
|
||||||
|
if (!(obj1 = ast_sorcery_alloc(sorcery, "test", "blah"))) {
|
||||||
|
ast_test_status_update(test, "Failed to allocate a known object type\n");
|
||||||
|
return AST_TEST_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(obj2 = ast_sorcery_alloc(sorcery, "test2", "blah"))) {
|
||||||
|
ast_test_status_update(test, "Failed to allocate a known object type\n");
|
||||||
|
return AST_TEST_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The 'test' wizard has no is_stale callback */
|
||||||
|
ast_test_validate(test, ast_sorcery_is_stale(sorcery, obj1) == 0);
|
||||||
|
|
||||||
|
/* The 'test2' wizard should return stale */
|
||||||
|
ast_test_validate(test, ast_sorcery_is_stale(sorcery, obj2) == 1);
|
||||||
|
ast_test_validate(test, cache.is_stale == 1);
|
||||||
|
|
||||||
|
return AST_TEST_PASS;
|
||||||
|
}
|
||||||
|
|
||||||
AST_TEST_DEFINE(caching_wizard_behavior)
|
AST_TEST_DEFINE(caching_wizard_behavior)
|
||||||
{
|
{
|
||||||
struct ast_flags flags = { CONFIG_FLAG_NOCACHE };
|
struct ast_flags flags = { CONFIG_FLAG_NOCACHE };
|
||||||
@@ -3505,6 +3593,7 @@ static int unload_module(void)
|
|||||||
AST_TEST_UNREGISTER(object_update_uncreated);
|
AST_TEST_UNREGISTER(object_update_uncreated);
|
||||||
AST_TEST_UNREGISTER(object_delete);
|
AST_TEST_UNREGISTER(object_delete);
|
||||||
AST_TEST_UNREGISTER(object_delete_uncreated);
|
AST_TEST_UNREGISTER(object_delete_uncreated);
|
||||||
|
AST_TEST_UNREGISTER(object_is_stale);
|
||||||
AST_TEST_UNREGISTER(caching_wizard_behavior);
|
AST_TEST_UNREGISTER(caching_wizard_behavior);
|
||||||
AST_TEST_UNREGISTER(object_type_observer);
|
AST_TEST_UNREGISTER(object_type_observer);
|
||||||
AST_TEST_UNREGISTER(configuration_file_wizard);
|
AST_TEST_UNREGISTER(configuration_file_wizard);
|
||||||
@@ -3561,6 +3650,7 @@ static int load_module(void)
|
|||||||
AST_TEST_REGISTER(object_update_uncreated);
|
AST_TEST_REGISTER(object_update_uncreated);
|
||||||
AST_TEST_REGISTER(object_delete);
|
AST_TEST_REGISTER(object_delete);
|
||||||
AST_TEST_REGISTER(object_delete_uncreated);
|
AST_TEST_REGISTER(object_delete_uncreated);
|
||||||
|
AST_TEST_REGISTER(object_is_stale);
|
||||||
AST_TEST_REGISTER(caching_wizard_behavior);
|
AST_TEST_REGISTER(caching_wizard_behavior);
|
||||||
AST_TEST_REGISTER(object_type_observer);
|
AST_TEST_REGISTER(object_type_observer);
|
||||||
AST_TEST_REGISTER(configuration_file_wizard);
|
AST_TEST_REGISTER(configuration_file_wizard);
|
||||||
|
|||||||
Reference in New Issue
Block a user