summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorQuincey Koziol <koziol@lbl.gov>2020-12-01 20:24:32 (GMT)
committerGitHub <noreply@github.com>2020-12-01 20:24:32 (GMT)
commit7950dca75c353b5a8fe607b12f5195b3cfec76f4 (patch)
treeb2b96d0ccb7bd77a8e45239757c128e7c6617d8f
parent436221cc1851643729fca9ff15f16aa0edad79e0 (diff)
downloadhdf5-7950dca75c353b5a8fe607b12f5195b3cfec76f4.zip
hdf5-7950dca75c353b5a8fe607b12f5195b3cfec76f4.tar.gz
hdf5-7950dca75c353b5a8fe607b12f5195b3cfec76f4.tar.bz2
Add H5atclose and H5is_library_terminating routines. (#139)
* Add H5atclose and H5is_library_terminating routines. * Add brief Doxygen comment for H5_atclose_func_t typedef. * Added /*out*/ flag to parameter list, for the tracing script. * Update doxygen comment for H5atclose with additional detail. * Return FAIL for H5is_library_threadsafe and H5is_library_terminating when their parameters are NULL.
-rw-r--r--src/H5.c125
-rw-r--r--src/H5public.h48
-rw-r--r--test/tmisc.c114
3 files changed, 279 insertions, 8 deletions
diff --git a/src/H5.c b/src/H5.c
index 03a8714..e5583be 100644
--- a/src/H5.c
+++ b/src/H5.c
@@ -44,6 +44,13 @@
/* Package Typedefs */
/********************/
+/* Node for list of 'atclose' routines to invoke at library shutdown */
+typedef struct H5_atclose_node_t {
+ H5_atclose_func_t func; /* Function to invoke */
+ void * ctx; /* Context to pass to function */
+ struct H5_atclose_node_t *next; /* Pointer to next node in list */
+} H5_atclose_node_t;
+
/********************/
/* Local Prototypes */
/********************/
@@ -88,6 +95,12 @@ H5_debug_t H5_debug_g; /* debugging info */
/* Local Variables */
/*******************/
+/* Linked list of registered 'atclose' functions to invoke at library shutdown */
+static H5_atclose_node_t *H5_atclose_head = NULL;
+
+/* Declare a free list to manage the H5_atclose_node_t struct */
+H5FL_DEFINE_STATIC(H5_atclose_node_t);
+
/*--------------------------------------------------------------------------
NAME
H5__init_package -- Initialize interface-specific information
@@ -305,6 +318,28 @@ H5_term_library(void)
/* Check if we should display error output */
(void)H5Eget_auto2(H5E_DEFAULT, &func, NULL);
+ /* Iterate over the list of 'atclose' callbacks that have been registered */
+ if (H5_atclose_head) {
+ H5_atclose_node_t *curr_atclose; /* Current 'atclose' node */
+
+ /* Iterate over all 'atclose' nodes, making callbacks */
+ curr_atclose = H5_atclose_head;
+ while (curr_atclose) {
+ H5_atclose_node_t *tmp_atclose; /* Temporary pointer to 'atclose' node */
+
+ /* Invoke callback, providing context */
+ (*curr_atclose->func)(curr_atclose->ctx);
+
+ /* Advance to next node and free this one */
+ tmp_atclose = curr_atclose;
+ curr_atclose = curr_atclose->next;
+ H5FL_FREE(H5_atclose_node_t, tmp_atclose);
+ } /* end while */
+
+ /* Reset list head, in case library is re-initialized */
+ H5_atclose_head = NULL;
+ } /* end if */
+
/*
* Terminate each interface. The termination functions return a positive
* value if they do something that might affect some other interface in a
@@ -971,6 +1006,45 @@ done:
} /* end H5open() */
/*-------------------------------------------------------------------------
+ * Function: H5atclose
+ *
+ * Purpose: Register a callback for the library to invoke when it's
+ * closing. Callbacks are invoked in LIFO order.
+ *
+ * Return: Non-negative on success/Negative on failure
+ *
+ *-------------------------------------------------------------------------
+ */
+herr_t
+H5atclose(H5_atclose_func_t func, void *ctx)
+{
+ H5_atclose_node_t *new_atclose; /* New 'atclose' node */
+ herr_t ret_value = SUCCEED; /* Return value */
+
+ FUNC_ENTER_API(FAIL)
+ H5TRACE2("e", "Hc*x", func, ctx);
+
+ /* Check arguments */
+ if (NULL == func)
+ HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "NULL func pointer")
+
+ /* Allocate space for the 'atclose' node */
+ if (NULL == (new_atclose = H5FL_MALLOC(H5_atclose_node_t)))
+ HGOTO_ERROR(H5E_RESOURCE, H5E_CANTALLOC, FAIL, "can't allocate 'atclose' node")
+
+ /* Set up 'atclose' node */
+ new_atclose->func = func;
+ new_atclose->ctx = ctx;
+
+ /* Connector to linked-list of 'atclose' nodes */
+ new_atclose->next = H5_atclose_head;
+ H5_atclose_head = new_atclose;
+
+done:
+ FUNC_LEAVE_API(ret_value)
+} /* end H5atclose() */
+
+/*-------------------------------------------------------------------------
* Function: H5close
*
* Purpose: Terminate the library and release all resources.
@@ -1107,23 +1181,58 @@ H5free_memory(void *mem)
*-------------------------------------------------------------------------
*/
herr_t
-H5is_library_threadsafe(hbool_t *is_ts)
+H5is_library_threadsafe(hbool_t *is_ts /*out*/)
{
- FUNC_ENTER_API_NOINIT
- H5TRACE1("e", "*b", is_ts);
+ herr_t ret_value = SUCCEED; /* Return value */
- HDassert(is_ts);
+ FUNC_ENTER_API_NOINIT
+ H5TRACE1("e", "x", is_ts);
- /* At this time, it is impossible for this to fail. */
+ if(is_ts) {
#ifdef H5_HAVE_THREADSAFE
- *is_ts = TRUE;
+ *is_ts = TRUE;
#else /* H5_HAVE_THREADSAFE */
- *is_ts = FALSE;
+ *is_ts = FALSE;
#endif /* H5_HAVE_THREADSAFE */
+ }
+ else
+ ret_value = FAIL;
- FUNC_LEAVE_API_NOINIT(SUCCEED)
+ FUNC_LEAVE_API_NOINIT(ret_value)
} /* end H5is_library_threadsafe() */
+/*-------------------------------------------------------------------------
+ * Function: H5is_library_terminating
+ *
+ * Purpose: Checks to see if the library is shutting down.
+ *
+ * Note: Useful for plugins to detect when the library is terminating.
+ * For example, a VOL connector could check if a "file close"
+ * callback was the result of the library shutdown process, or
+ * an API action from the application.
+ *
+ * Return: SUCCEED/FAIL
+ *
+ *-------------------------------------------------------------------------
+ */
+herr_t
+H5is_library_terminating(hbool_t *is_terminating /*out*/)
+{
+ herr_t ret_value = SUCCEED; /* Return value */
+
+ FUNC_ENTER_API_NOINIT
+ H5TRACE1("e", "x", is_terminating);
+
+ HDassert(is_terminating);
+
+ if(is_terminating)
+ *is_terminating = H5_TERM_GLOBAL;
+ else
+ ret_value = FAIL;
+
+ FUNC_LEAVE_API_NOINIT(ret_value)
+} /* end H5is_library_terminating() */
+
#if defined(H5_HAVE_THREADSAFE) && defined(H5_BUILT_AS_DYNAMIC_LIB) && defined(H5_HAVE_WIN32_API) && \
defined(H5_HAVE_WIN_THREADS)
/*-------------------------------------------------------------------------
diff --git a/src/H5public.h b/src/H5public.h
index 20a7dc1..6252778 100644
--- a/src/H5public.h
+++ b/src/H5public.h
@@ -386,6 +386,11 @@ typedef struct H5_alloc_stats_t {
size_t peak_alloc_blocks_count; /**< Peak # of blocks allocated */
} H5_alloc_stats_t;
+/**
+ * Library shutdown callback, used by H5atclose().
+ */
+typedef void (*H5_atclose_func_t)(void *ctx);
+
/* Functions in H5.c */
/**
* \ingroup H5
@@ -405,6 +410,33 @@ typedef struct H5_alloc_stats_t {
H5_DLL herr_t H5open(void);
/**
* \ingroup H5
+ * \brief Registers a callback for the library to invoke when it's closing.
+ * \param[in] func The function pointer to invoke
+ * \param[in] ctx Context to pass to \p func when invoked
+ * \return \herr_t
+ *
+ * \details H5atclose() registers a callback that the HDF5 library will invoke
+ * when closing. The full capabilities of the HDF5 library are
+ * available to callbacks invoked through this mechanism, library
+ * shutdown will only begin in earnest when all callbacks have been
+ * invoked and have returned.
+ *
+ * Registered callbacks are invoked in LIFO order, similar to the
+ * Standard C 'atexit' routine. For example, if 'func1' is registered,
+ * then 'func2', when the library is closing 'func2' will
+ * be invoked first, then 'func1'.
+ *
+ * The \p ctx pointer will be passed to \p func when it's invoked.
+ * NULL is allowed for \p ctx.
+ *
+ * If the HDF5 library is initialized and closed more than once, the
+ * \p func callback must be registered within each open/close cycle.
+ *
+ * \since 1.12.1
+ */
+H5_DLL herr_t H5atclose(H5_atclose_func_t func, void *ctx);
+/**
+ * \ingroup H5
* \brief Flushes all data to disk, closes all open objects, and releases memory
* \return \herr_t
*
@@ -613,6 +645,22 @@ H5_DLL herr_t H5get_libversion(unsigned *majnum, unsigned *minnum, unsigned *rel
H5_DLL herr_t H5check_version(unsigned majnum, unsigned minnum, unsigned relnum);
/**
* \ingroup H5
+ * \brief Checks whether the HDF5 library is closing.
+ * \param[out] is_terminating Flag indicating whether library is shutting down
+ * \return \herr_t
+ *
+ * \details H5is_library_terminating() queries whether the HDF5 library is in
+ * the process of shutting down. The \p is_terminating flag will only
+ * be set to TRUE after shutdown starts, it will be FALSE before the
+ * library has been initialized, while the library is initialized, and
+ * after it has been closed. The value of \p is_terminating is
+ * undefined if this routine fails.
+ *
+ * \since 1.12.1
+ */
+H5_DLL herr_t H5is_library_terminating(hbool_t *is_terminating);
+/**
+ * \ingroup H5
* \brief Determines whether the HDF5 library was built with the thread-safety
* feature enabled
*
diff --git a/test/tmisc.c b/test/tmisc.c
index 40a1df5..51b2e89 100644
--- a/test/tmisc.c
+++ b/test/tmisc.c
@@ -5752,6 +5752,119 @@ test_misc35(void)
} /* end test_misc35() */
+/* Context to pass to 'atclose' callbacks */
+static int test_misc36_context;
+
+/* 'atclose' callbacks for test_misc36 */
+static void
+test_misc36_cb1(void *_ctx)
+{
+ int *ctx = (int *)_ctx; /* Set up context pointer */
+ hbool_t is_terminating; /* Flag indicating the library is terminating */
+ herr_t ret; /* Return value */
+
+ /* Check whether the library thinks it's terminating */
+ is_terminating = FALSE;
+ ret = H5is_library_terminating(&is_terminating);
+ CHECK(ret, FAIL, "H5is_library_terminating");
+ VERIFY(is_terminating, TRUE, "H5is_library_terminating");
+
+ /* Verify correct ordering for 'atclose' callbacks */
+ if(0 != *ctx)
+ HDabort();
+
+ /* Update context value */
+ *ctx = 1;
+}
+
+static void
+test_misc36_cb2(void *_ctx)
+{
+ int *ctx = (int *)_ctx; /* Set up context pointer */
+ hbool_t is_terminating; /* Flag indicating the library is terminating */
+ herr_t ret; /* Return value */
+
+ /* Check whether the library thinks it's terminating */
+ is_terminating = FALSE;
+ ret = H5is_library_terminating(&is_terminating);
+ CHECK(ret, FAIL, "H5is_library_terminating");
+ VERIFY(is_terminating, TRUE, "H5is_library_terminating");
+
+ /* Verify correct ordering for 'atclose' callbacks */
+ if(1 != *ctx)
+ HDabort();
+
+ /* Update context value */
+ *ctx = 2;
+}
+
+/****************************************************************
+**
+** test_misc36(): Exercise H5atclose and H5is_library_terminating
+**
+****************************************************************/
+static void
+test_misc36(void)
+{
+ hbool_t is_terminating; /* Flag indicating the library is terminating */
+ herr_t ret; /* Return value */
+
+ /* Output message about test being performed */
+ MESSAGE(5, ("H5atclose and H5is_library_terminating API calls"));
+
+ /* Check whether the library thinks it's terminating */
+ is_terminating = TRUE;
+ ret = H5is_library_terminating(&is_terminating);
+ CHECK(ret, FAIL, "H5is_library_terminating");
+ VERIFY(is_terminating, FALSE, "H5is_library_terminating");
+
+ /* Shut the library down */
+ test_misc36_context = 0;
+ H5close();
+
+ /* Check whether the library thinks it's terminating */
+ is_terminating = TRUE;
+ ret = H5is_library_terminating(&is_terminating);
+ CHECK(ret, FAIL, "H5is_library_terminating");
+ VERIFY(is_terminating, FALSE, "H5is_library_terminating");
+
+ /* Check the close context was not changed */
+ VERIFY(test_misc36_context, 0, "H5atclose");
+
+ /* Restart the library */
+ H5open();
+
+ /* Check whether the library thinks it's terminating */
+ is_terminating = TRUE;
+ ret = H5is_library_terminating(&is_terminating);
+ CHECK(ret, FAIL, "H5is_library_terminating");
+ VERIFY(is_terminating, FALSE, "H5is_library_terminating");
+
+ /* Register the 'atclose' callbacks */
+ /* (Note that these will be called in reverse order, which is checked) */
+ ret = H5atclose(&test_misc36_cb2, &test_misc36_context);
+ CHECK(ret, FAIL, "H5atclose");
+ ret = H5atclose(&test_misc36_cb1, &test_misc36_context);
+ CHECK(ret, FAIL, "H5atclose");
+
+ /* Shut the library down */
+ test_misc36_context = 0;
+ H5close();
+
+ /* Check the close context was changed correctly */
+ VERIFY(test_misc36_context, 2, "H5atclose");
+
+ /* Restart the library */
+ H5open();
+
+ /* Close the library again */
+ test_misc36_context = 0;
+ H5close();
+
+ /* Check the close context was not changed */
+ VERIFY(test_misc36_context, 0, "H5atclose");
+} /* end test_misc36() */
+
/****************************************************************
**
** test_misc(): Main misc. test routine.
@@ -5803,6 +5916,7 @@ test_misc(void)
test_misc33(); /* Test to verify that H5HL_offset_into() returns error if offset exceeds heap block */
test_misc34(); /* Test behavior of 0 and NULL in H5MM API calls */
test_misc35(); /* Test behavior of free-list & allocation statistics API calls */
+ test_misc36(); /* Exercise H5atclose and H5is_library_terminating */
} /* test_misc() */