[asterisk-commits] sorcery: Add support for object staleness (asterisk[master])

SVN commits to the Asterisk project asterisk-commits at lists.digium.com
Sat Jul 11 10:46:52 CDT 2015


Matt Jordan has submitted this change and it was merged.

Change subject: sorcery: Add support for object staleness
......................................................................


sorcery: Add support for object staleness

This patch enhances the sorcery API to allow for sorcery wizards to
determine if an object is stale. This includes the following:

* Sorcery objects now have a timestamp that is set on creation. Since
  sorcery objects are immutable, this can be used by sorcery wizards to
  determine if an object is stale.

* A new API call has been added, ast_sorcery_is_stale. This API call
  queries the wizards associated with the object, calling a new callback
  function 'is_stale'. Note that if a wizard does not support the new
  callback, objects are always assumed to not be stale.

* Unit tests have been added that cover the new API call.

Change-Id: Ica93c6a4e8a06c0376ea43e00cf702920b806064
---
M include/asterisk/sorcery.h
M main/sorcery.c
M tests/test_sorcery.c
3 files changed, 156 insertions(+), 0 deletions(-)

Approvals:
  Anonymous Coward #1000019: Verified
  Matt Jordan: Looks good to me, approved
  Joshua Colp: Looks good to me, but someone else must approve



diff --git a/include/asterisk/sorcery.h b/include/asterisk/sorcery.h
index 0bd22a6..d681ebb 100644
--- a/include/asterisk/sorcery.h
+++ b/include/asterisk/sorcery.h
@@ -312,6 +312,9 @@
 
 	/*! \brief Callback for closing a wizard */
 	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 */
@@ -1202,6 +1205,20 @@
 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
  *
  * \param sorcery Pointer to a sorcery structure
@@ -1218,6 +1235,16 @@
 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
  *
  * \param object Pointer to a sorcery object
diff --git a/main/sorcery.c b/main/sorcery.c
index 20b3d6b..fbbd146 100644
--- a/main/sorcery.c
+++ b/main/sorcery.c
@@ -129,6 +129,9 @@
 
 	/*! \brief Extended object fields */
 	struct ast_variable *extended;
+
+	/*! \brief Time that the object was created */
+	struct timeval created;
 };
 
 /*! \brief Structure for registered object type */
@@ -1734,6 +1737,7 @@
 		details->object->id = ast_strdup(id);
 	}
 
+	details->object->created = ast_tvnow();
 	ast_copy_string(details->object->type, type, sizeof(details->object->type));
 
 	if (aco_set_defaults(&object_type->type, id, details)) {
@@ -2143,6 +2147,35 @@
 	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)
 {
 	if (sorcery) {
@@ -2161,6 +2194,12 @@
 	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 struct ast_sorcery_object_details *details = object;
diff --git a/tests/test_sorcery.c b/tests/test_sorcery.c
index b6b3d09..5d96422 100644
--- a/tests/test_sorcery.c
+++ b/tests/test_sorcery.c
@@ -138,6 +138,9 @@
 	/*! \brief Whether the object has been deleted from the cache or not */
 	unsigned int deleted:1;
 
+	/*! \brief Whether the object is stale or not */
+	unsigned int is_stale:1;
+
 	/*! \brief Object to return when asked */
 	struct test_sorcery_object object;
 };
@@ -217,6 +220,12 @@
 	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 */
 static struct ast_sorcery_wizard test_wizard = {
 	.name = "test",
@@ -234,6 +243,7 @@
 	.retrieve_id = sorcery_test_retrieve_id,
 	.update = sorcery_test_update,
 	.delete = sorcery_test_delete,
+	.is_stale = sorcery_test_is_stale,
 };
 
 static void sorcery_observer_created(const void *object)
@@ -2223,6 +2233,84 @@
 	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)
 {
 	struct ast_flags flags = { CONFIG_FLAG_NOCACHE };
@@ -3505,6 +3593,7 @@
 	AST_TEST_UNREGISTER(object_update_uncreated);
 	AST_TEST_UNREGISTER(object_delete);
 	AST_TEST_UNREGISTER(object_delete_uncreated);
+	AST_TEST_UNREGISTER(object_is_stale);
 	AST_TEST_UNREGISTER(caching_wizard_behavior);
 	AST_TEST_UNREGISTER(object_type_observer);
 	AST_TEST_UNREGISTER(configuration_file_wizard);
@@ -3561,6 +3650,7 @@
 	AST_TEST_REGISTER(object_update_uncreated);
 	AST_TEST_REGISTER(object_delete);
 	AST_TEST_REGISTER(object_delete_uncreated);
+	AST_TEST_REGISTER(object_is_stale);
 	AST_TEST_REGISTER(caching_wizard_behavior);
 	AST_TEST_REGISTER(object_type_observer);
 	AST_TEST_REGISTER(configuration_file_wizard);

-- 
To view, visit https://gerrit.asterisk.org/781
To unsubscribe, visit https://gerrit.asterisk.org/settings

Gerrit-MessageType: merged
Gerrit-Change-Id: Ica93c6a4e8a06c0376ea43e00cf702920b806064
Gerrit-PatchSet: 2
Gerrit-Project: asterisk
Gerrit-Branch: master
Gerrit-Owner: Matt Jordan <mjordan at digium.com>
Gerrit-Reviewer: Anonymous Coward #1000019
Gerrit-Reviewer: Joshua Colp <jcolp at digium.com>
Gerrit-Reviewer: Matt Jordan <mjordan at digium.com>



More information about the asterisk-commits mailing list