summaryrefslogtreecommitdiffstats
path: root/test/tskiplist.c
diff options
context:
space:
mode:
authorNeil Fortner <nfortne2@hdfgroup.org>2015-07-20 22:21:36 (GMT)
committerNeil Fortner <nfortne2@hdfgroup.org>2015-07-20 22:21:36 (GMT)
commitf550bc0cea1da09002118fe8dc4455b18fc26717 (patch)
treec397791fb1b551b6965fda53da307c385d559081 /test/tskiplist.c
parent74e4cd4d2146b61b9d0179e5e1dc8c9048be2e19 (diff)
downloadhdf5-f550bc0cea1da09002118fe8dc4455b18fc26717.zip
hdf5-f550bc0cea1da09002118fe8dc4455b18fc26717.tar.gz
hdf5-f550bc0cea1da09002118fe8dc4455b18fc26717.tar.bz2
[svn-r27415] Fix potential error with H5I_clear_type which could occur when a callback closed
a different ID in the same type. Added a new skiplist routine, H5SL_try_free_safe, which iterates over items, freeing some of them, and which intercepts and defers attempts to remove from the list outside of the main iteration. Changed H5I_clear_type to use this function. Tested: jam, koala, ostrich (h5committest); ummon
Diffstat (limited to 'test/tskiplist.c')
-rw-r--r--test/tskiplist.c203
1 files changed, 203 insertions, 0 deletions
diff --git a/test/tskiplist.c b/test/tskiplist.c
index 07e63fd..62c5f8e 100644
--- a/test/tskiplist.c
+++ b/test/tskiplist.c
@@ -1171,6 +1171,208 @@ test_skiplist_free(void)
/****************************************************************
**
+** test_skiplist_try_free_safe(): Test H5SL (skip list) code.
+** Tests 'try_free_safe' operation in skip lists.
+**
+****************************************************************/
+/* Macro definitions */
+#define TEST_TFS_MAX_NOBJS 100
+#define TEST_TFS_MIN_NOBJS 5
+#define TEST_TFS_NITER 50
+
+/* Structure to hold the list of objects */
+typedef struct {
+ H5SL_t *slist; /* Skiplist holding the objects */
+ struct test_tfs_obj_t *list; /* Linear list of objects */
+ int nobjs; /* Number of objects in list */
+ int nobjs_rem; /* Number of objects in list that have not been freed */
+} test_tfs_list_t;
+
+/* Structure for an object */
+typedef struct test_tfs_obj_t {
+ int idx; /* Index (key) for this object */
+ int nfrees; /* Number of times this object has been freed */
+} test_tfs_obj_t;
+
+/* op_data struct for H5SL_iterate() */
+typedef struct test_tfs_it_ud_t {
+ test_tfs_list_t *obj_list; /* List of objects */
+ int last_idx; /* Index of last object visited in iteration */
+ int ncalls; /* Number of times this callback was called */
+} test_tfs_it_ud_t;
+
+/* iterate callback */
+static herr_t test_tfs_iter(void *_obj, void *key, void *_udata) {
+ test_tfs_obj_t *obj = (test_tfs_obj_t *)_obj;
+ test_tfs_it_ud_t *udata = (test_tfs_it_ud_t *)_udata;
+
+ /* Check consistency */
+ VERIFY((void *)&obj->idx, key, "obj->idx");
+ VERIFY(obj, &udata->obj_list->list[obj->idx], "obj_list->list[obj->idx]");
+
+ /* Increment number of calls */
+ udata->ncalls++;
+
+ /* Verify we were given the correct object */
+ do {
+ udata->last_idx++;
+ } while(udata->obj_list->list[udata->last_idx].nfrees != 0);
+ VERIFY(udata->last_idx, obj->idx, "H5SL_iterate");
+
+ return 0;
+} /* end test_tfs_iter() */
+
+/* try_free_safe callback */
+static htri_t test_tfs_free(void *_obj, void *key, void *_obj_list) {
+ test_tfs_obj_t *obj = (test_tfs_obj_t *)_obj;
+ test_tfs_list_t *obj_list = (test_tfs_list_t *)_obj_list;
+ test_tfs_it_ud_t iter_ud;
+ int nrems, rem_idx, i, j;
+ test_tfs_obj_t *obj_ret;
+ herr_t ret; /* return value */
+ htri_t ret_value;
+
+ /* Check consistency */
+ VERIFY((void *)&obj->idx, key, "obj->idx");
+ VERIFY(obj, &obj_list->list[obj->idx], "obj_list->list[obj->idx]");
+
+ /* Mark this object as freed (to make sure it isn't recursively freed, that
+ * is not something we support, we will undo this if we decide later not to
+ * free the object) */
+ obj->nfrees++;
+ obj_list->nobjs_rem--;
+
+ /* Decide how many objects to remove */
+ nrems = (int)(HDrandom() % (long)3);
+
+ /* Remove objects */
+ for(i = 0; i < nrems; i++)
+ /* Check nobjs_rem */
+ if(obj_list->nobjs_rem > 0) {
+ /* Remove a random object from the list */
+ rem_idx = (int)(HDrandom() % (long)obj_list->nobjs_rem);
+
+ /* Scan the list, finding the rem_idx'th object that has not been
+ * freed */
+ for(j = 0; j < obj_list->nobjs; j++)
+ if(obj_list->list[j].nfrees == 0) {
+ if(rem_idx == 0)
+ break;
+ else
+ rem_idx--;
+ } /* end if */
+ if(j == obj_list->nobjs)
+ ERROR("invalid obj_list");
+ else {
+ /* Remove the object */
+ obj_ret = (test_tfs_obj_t *)H5SL_remove(obj_list->slist, &j);
+ CHECK(obj_ret, NULL, "H5SL_remove");
+ obj_ret->nfrees++;
+ obj_list->nobjs_rem--;
+ } /* end else */
+ } /* end if */
+
+ /* Mark this object as not freed so we know to expect it in the iterate call
+ */
+ obj->nfrees--;
+ obj_list->nobjs_rem++;
+
+ /* Iterate over skip list (maybe) */
+ if(HDrandom() % (long)5) {
+ iter_ud.obj_list = obj_list;
+ iter_ud.last_idx = -1;
+ iter_ud.ncalls = 0;
+ ret = H5SL_iterate(obj_list->slist, test_tfs_iter, &iter_ud);
+ CHECK(ret, FAIL, "H5SL_iterate");
+ VERIFY(iter_ud.ncalls, obj_list->nobjs_rem, "H5SL_iterate");
+ } /* end if */
+
+ /* Verify nobjs_rem is non-negative */
+ if(obj_list->nobjs_rem < 0)
+ ERROR("invalid nobjs_rem");
+
+ /* Decide whether this object should be freed */
+ if(HDrandom() % (long)2) {
+ /* Free the object */
+ ret_value = TRUE;
+ obj->nfrees++;
+ obj_list->nobjs_rem--;
+ } /* end if */
+ else
+ /* Do not free the object */
+ ret_value = FALSE;
+
+ return ret_value;
+} /* end test_tfs_free() */
+
+/* Test function */
+static void
+test_skiplist_try_free_safe(void)
+{
+ test_tfs_list_t obj_list;
+ test_tfs_obj_t list[TEST_TFS_MAX_NOBJS];
+ int i, j;
+ int nobjs_found;
+ hsize_t count;
+ herr_t ret; /* Generic return value */
+
+ /* Output message about test being performed */
+ MESSAGE(7, ("Testing Skip List 'Try Free Safe' Operation\n"));
+
+ /* Create a skip list */
+ obj_list.slist = H5SL_create(H5SL_TYPE_INT, NULL);
+ CHECK(obj_list.slist, NULL, "H5SL_create");
+
+ /* Init obj_list.list */
+ obj_list.list = list;
+ for(j = 0; j < TEST_TFS_MAX_NOBJS; j++)
+ list[j].idx = j;
+
+ for(i = 0; i < TEST_TFS_NITER; i++) {
+ /* Build object list */
+ obj_list.nobjs = obj_list.nobjs_rem = (int)(TEST_TFS_MIN_NOBJS + (HDrandom() % (long)(TEST_TFS_MAX_NOBJS - TEST_TFS_MIN_NOBJS + 1)));
+ for(j = 0; j < obj_list.nobjs; j++) {
+ list[j].nfrees = 0;
+ ret = H5SL_insert(obj_list.slist, &list[j], &list[j].idx);
+ CHECK(ret, FAIL, "H5SL_insert");
+ } /* end for */
+
+ /* Call H5S_try_free_safe() - free most of the items in the skip list in
+ * a safe manner */
+ ret = H5SL_try_free_safe(obj_list.slist, test_tfs_free, &obj_list);
+ CHECK(ret, FAIL, "H5SL_try_free_safe");
+
+ /* Verify list */
+ nobjs_found = 0;
+ for(j = 0; j < obj_list.nobjs; j++)
+ if(list[j].nfrees == 0)
+ nobjs_found++;
+ else
+ VERIFY(list[j].nfrees, (long)1, "list[j].nfrees");
+
+ /* Verify number of objects */
+ VERIFY(obj_list.nobjs_rem, nobjs_found, "obj_list.nobjs_rem");
+ count = H5SL_count(obj_list.slist);
+ VERIFY(count, (size_t)nobjs_found, "H5SL_count");
+
+ /* Release the skip list, forcibly freeing all nodes (will not make
+ * callbacks) */
+ ret = H5SL_release(obj_list.slist);
+ CHECK(ret, FAIL, "H5SL_release");
+
+ /* Verify number of objects is 0 */
+ count = H5SL_count(obj_list.slist);
+ VERIFY(count, (size_t)0, "H5SL_count");
+ } /* end for */
+
+ /* Close the skip list */
+ ret = H5SL_close(obj_list.slist);
+ CHECK(ret, FAIL, "H5SL_close");
+
+} /* end test_skiplist_try_free_safe() */
+
+/****************************************************************
+**
** test_skiplist_less(): Test H5SL (skip list) code.
** Tests 'less' operation in skip lists.
**
@@ -1577,6 +1779,7 @@ test_skiplist(void)
test_skiplist_add(); /* Test 'add' operation */
test_skiplist_destroy(); /* Test 'destroy' operation */
test_skiplist_free(); /* Test 'free' operation */
+ test_skiplist_try_free_safe(); /* Test 'try_free_safe' operation */
test_skiplist_less(); /* Test 'less' operation */
test_skiplist_greater(); /* Test 'greater' operation */
test_skiplist_below(); /* Test 'below' operation */