mirror of
https://github.com/HDFGroup/hdf5.git
synced 2026-09-25 04:09:44 +03:00
Consolidate documentation under docs/ directory (#6310)
* Consolidate documentation under doc/ directory
Move user-facing guides from release_docs/ and doxygen/ into a single
doc/ root. release_docs/ now holds only release artifacts (changelogs,
history, release process, maintainer info).
- git mv release_docs/INSTALL*.md, USING_*.md, README_HPC.md,
BuildSystemNotes.md, AutotoolsToCMakeOptions.md,
HDF5_Library_2.0.0_Migration_Guide.md → doc/
- git mv doxygen/ → doc/doxygen/
- Update CMakeLists.txt: HDF5_DOXYGEN_DIR and add_subdirectory path
- Update CMakeInstallation.cmake: all install paths for moved files
- Update bin/make_vers: hardcoded doxygen/ path substitution
- Update doc/doxygen/CMakeLists.txt: EXAMPLES_DIRECTORY and comments
- Update README.md, CONTRIBUTING.md, SECURITY.md, config/README.md,
release_docs/RELEASE_PROCESS.md: links to moved files
- Update doxygen .dox files: release_docs/ URLs for moved guides
- Rewrite release_docs/README.md for narrowed scope
* Add HDF5_DOCS_DIR variable for doc/ root path
Introduce HDF5_DOCS_DIR = \${HDF5_SOURCE_DIR}/doc so that
CMakeInstallation.cmake and future callers reference the doc/
directory symbolically rather than by hardcoded path.
HDF5_DOXYGEN_DIR is now derived from HDF5_DOCS_DIR.
This commit is contained in:
@@ -0,0 +1,571 @@
|
||||
/** \page thread-safe-lib Thread Safe Library
|
||||
|
||||
Navigate back: \ref index "Main" / \ref TN
|
||||
<hr>
|
||||
|
||||
\section sec_tsafe_compilation Library header files and conditional compilation
|
||||
The following code is placed at the beginning of H5private.h:
|
||||
\code
|
||||
#ifdef H5_HAVE_THREADSAFE
|
||||
#include <pthread.h>
|
||||
#endif
|
||||
\endcode
|
||||
|
||||
<code>H5_HAVE_THREADSAFE</code> is defined when the HDF5 library is
|
||||
compiled with the <em>HDF5_ENABLE_THREADSAFE=ON</em> using CMake. In general,
|
||||
code for the non-threadsafe version of HDF5 library are placed within
|
||||
the <code># else</code> part of the conditional compilation. The exception
|
||||
to this rule are the changes to the <code>FUNC_ENTER</code> (in
|
||||
H5private.h), <code>HRETURN</code> and <code>HRETURN_ERROR</code> (in
|
||||
H5Eprivate.h) macros (see section \ref subsec_tsafe_macro_ret).
|
||||
</p>
|
||||
|
||||
\section sec_tsafe_global Global variables/structures
|
||||
|
||||
\subsection subsec_tsafe_global_var Global library initialization variable
|
||||
In the threadsafe implementation, the global library initialization
|
||||
variable <code>H5_libinit_g</code> is changed to a global structure
|
||||
consisting of the variable with its associated lock (locks are explained
|
||||
in section \ref subsec_tsafe_impl_locks):
|
||||
\code
|
||||
bool H5_libinit_g = FALSE;
|
||||
\endcode
|
||||
|
||||
becomes
|
||||
\code
|
||||
H5_api_t H5_g;
|
||||
\endcode
|
||||
|
||||
where <code>H5_api_t</code> is
|
||||
\code
|
||||
typedef struct H5_api_struct {
|
||||
H5_mutex_t init_lock; // API entrance mutex
|
||||
bool H5_libinit_g;
|
||||
} H5_api_t;
|
||||
\endcode
|
||||
|
||||
All former references to <code>H5_libinit_g</code> in the library are now
|
||||
made using the macro <code>H5_INIT_GLOBAL</code>. If the threadsafe
|
||||
library is to be used, the macro is set to <code>H5_g.H5_libinit_g</code>
|
||||
instead.
|
||||
|
||||
\subsection subsec_tsafe_global_serial Global serialization variable
|
||||
A new global boolean variable <code>H5_allow_concurrent_g</code> is used
|
||||
to determine if multiple threads are allowed to an API call
|
||||
simultaneously. This is set to <code>FALSE</code>.
|
||||
|
||||
All APIs that are allowed to do so have their own local variable that
|
||||
shadows the global variable and is set to <code>TRUE</code>. In phase 1,
|
||||
no such APIs exist.
|
||||
|
||||
It is defined in <code>H5.c</code> as follows:
|
||||
\code
|
||||
bool H5_allow_concurrent_g = FALSE;
|
||||
\endcode
|
||||
|
||||
\subsection subsec_tsafe_global_init Global thread initialization variable
|
||||
The global variable <code>H5_first_init_g</code> of type
|
||||
<code>pthread_once_t</code> is used to allow only the first thread in the
|
||||
application process to call an initialization function using
|
||||
<code>pthread_once</code>. All subsequent calls to
|
||||
<code>pthread_once</code> by any thread are disregarded.
|
||||
|
||||
The call sets up the mutex in the global structure <code>H5_g</code> (see
|
||||
section \ref subsec_tsafe_global_var) via an initialization function
|
||||
<code>H5_first_thread_init</code>. The first thread initialization
|
||||
function is described in section \ref subsec_tsafe_impl_first.
|
||||
|
||||
<code>H5_first_init_g</code> is defined in <code>H5.c</code> as follows:
|
||||
\code
|
||||
pthread_once_t H5_first_init_g = PTHREAD_ONCE_INIT;
|
||||
\endcode
|
||||
|
||||
\subsection subsec_tsafe_global_key Global key for per-thread error stacks
|
||||
A global pthread-managed key <code>H5_errstk_key_g</code> is used to
|
||||
allow pthreads to maintain a separate error stack (of type
|
||||
<code>H5E_t</code>) for each thread. This is defined in <code>H5.c</code>
|
||||
as:
|
||||
\code
|
||||
pthread_key_t H5_errstk_key_g;
|
||||
\endcode
|
||||
|
||||
Error stack management is described in section \ref subsec_tsafe_impl_err.
|
||||
|
||||
\subsection subsec_tsafe_global_cancel Global structure and key for thread cancellation prevention
|
||||
We need to preserve the thread cancellation status of each thread
|
||||
individually by using a key <code>H5_cancel_key_g</code>. The status is
|
||||
preserved using a structure (of type <code>H5_cancel_t</code>) which
|
||||
maintains the cancellability state of the thread before it entered the
|
||||
library and a count (which works very much like the recursive lock
|
||||
counter) which keeps track of the number of API calls the thread makes
|
||||
within the library.
|
||||
|
||||
The structure is defined in <code>H5private.h</code> as:
|
||||
\code
|
||||
// cancellability structure
|
||||
typedef struct H5_cancel_struct {
|
||||
int previous_state;
|
||||
unsigned int cancel_count;
|
||||
} H5_cancel_t;
|
||||
\endcode
|
||||
|
||||
Thread cancellation is described in section \ref subsec_tsafe_impl_cancel.
|
||||
|
||||
\section sec_tsafe_macro Changes to Macro expansions
|
||||
|
||||
\subsection subsec_tsafe_macro_fe Changes to FUNC_ENTER
|
||||
The <code>FUNC_ENTER</code> macro is now extended to include macro calls
|
||||
to initialize first threads, disable cancellability and wraps a lock
|
||||
operation around the checking of the global initialization flag. It
|
||||
should be noted that the cancellability should be disabled before
|
||||
acquiring the lock on the library. Doing so otherwise would allow the
|
||||
possibility that the thread be cancelled just after it has acquired the
|
||||
lock on the library and in that scenario, if the cleanup routines are not
|
||||
properly set, the library would be permanently locked out.
|
||||
|
||||
The additional macro code and new macro definitions can be found in
|
||||
Appendix \ref subsec_tsafe_app_E. The changes are made in <code>H5private.h</code>.
|
||||
|
||||
\subsection subsec_tsafe_macro_ret Changes to HRETURN and HRETURN_ERROR
|
||||
The <code>HRETURN</code> and <code>HRETURN_ERROR</code> macros are the
|
||||
counterparts to the <code>FUNC_ENTER</code> macro described in section
|
||||
\ref subsec_tsafe_macro_fe. <code>FUNC_LEAVE</code> makes a macro call to <code>HRETURN</code>,
|
||||
so it is also covered here.
|
||||
|
||||
The basic changes to these two macros involve adding macro calls to call
|
||||
an unlock operation and re-enable cancellability if necessary. It should
|
||||
be noted that the cancellability should be re-enabled only after the
|
||||
thread has released the lock to the library. The consequence of doing
|
||||
otherwise would be similar to that described in section \ref subsec_tsafe_macro_fe.
|
||||
|
||||
The additional macro code and new macro definitions can be found in
|
||||
Appendix \ref subsec_tsafe_app_E. The changes are made in <code>H5Eprivate.h</code>.
|
||||
|
||||
\section sec_tsafe_impl Implementation of threadsafe functionality
|
||||
|
||||
\subsection subsec_tsafe_impl_locks Recursive Locks
|
||||
A recursive mutex lock m allows a thread t1 to successfully lock m more
|
||||
than once without blocking t1. Another thread t2 will block if t2 tries
|
||||
to lock m while t1 holds the lock to m. If t1 makes k lock calls on m,
|
||||
then it also needs to make k unlock calls on m before it releases the
|
||||
lock.
|
||||
|
||||
Our implementation of recursive locks is built on top of a pthread mutex
|
||||
lock (which is not recursive). It makes use of a pthread condition
|
||||
variable to have unsuccessful threads wait on the mutex. Waiting threads
|
||||
are awaken by a signal from the final unlock call made by the thread
|
||||
holding the lock.
|
||||
|
||||
Recursive locks are defined to be the following type
|
||||
(<code>H5private.h</code>):
|
||||
\code
|
||||
typedef struct H5_mutex_struct {
|
||||
pthread_t owner_thread; // current lock owner
|
||||
pthread_mutex_t atomic_lock; // lock for atomicity of new mechanism
|
||||
pthread_cond_t cond_var; // condition variable
|
||||
unsigned int lock_count;
|
||||
} H5_mutex_t;
|
||||
\endcode
|
||||
|
||||
Detailed implementation code can be found in Appendix \ref subsec_tsafe_app_A. The
|
||||
implementation changes are made in <code>H5TS.c</code>.
|
||||
|
||||
\subsection subsec_tsafe_impl_first First thread initialization
|
||||
Because the mutex lock associated with a recursive lock cannot be
|
||||
statically initialized, a mechanism is required to initialize the
|
||||
recursive lock associated with <code>H5_g</code> so that it can be used
|
||||
for the first time.
|
||||
|
||||
The pthreads library allows this through the pthread_once call which as
|
||||
described in section \ref subsec_tsafe_global_init allows only the first thread accessing the
|
||||
library in an application to initialize <code>H5_g</code>.
|
||||
|
||||
In addition to initializing <code>H5_g</code>, it also initializes the
|
||||
key (see section \ref subsec_tsafe_global_key) for use with per-thread error stacks (see section
|
||||
\ref subsec_tsafe_impl_err).
|
||||
|
||||
The first thread initialization mechanism is implemented as the function
|
||||
call <code>H5_first_thread_init()</code> in <code>H5TS.c</code>. This is
|
||||
described in appendix B.
|
||||
|
||||
\subsection subsec_tsafe_impl_err Per-thread error stack management
|
||||
Pthreads allows individual threads to access dynamic and persistent
|
||||
per-thread data through the use of keys. Each key is associated with
|
||||
a table that maps threads to data items. Keys can be initialized by
|
||||
<code>pthread_key_create()</code> in pthreads (see sections \ref subsec_tsafe_global_key and \ref subsec_tsafe_impl_first).
|
||||
Per-thread data items are accessed using a key through the
|
||||
<code>pthread_getspecific()</code> and <code>pthread_setspecific()</code>
|
||||
calls to read and write to the association table respectively.
|
||||
|
||||
Per-thread error stacks are accessed through the key
|
||||
<code>H5_errstk_key_g</code> which is initialized by the first thread
|
||||
initialization call (see section \ref subsec_tsafe_impl_first).
|
||||
|
||||
In the non-threadsafe version of the library, there is a global stack
|
||||
variable <code>H5E_stack_g[1]</code> which is no longer defined in the
|
||||
threadsafe version. At the same time, the macro call to gain access to
|
||||
the error stack <code>H5E_get_my_stack</code> is changed from:
|
||||
\code
|
||||
#define H5E_get_my_stack() (H5E_stack_g+0)
|
||||
\endcode
|
||||
|
||||
to:
|
||||
\code
|
||||
#define H5E_get_my_stack() H5E_get_stack()
|
||||
\endcode
|
||||
|
||||
where <code>H5E_get_stack()</code> is a surrogate function that does the
|
||||
following operations:
|
||||
<ol>
|
||||
<li>if a thread is attempting to get an error stack for the first
|
||||
time, the error stack is dynamically allocated for the thread and
|
||||
associated with <code>H5_errstk_key_g</code> using
|
||||
<code>pthread_setspecific()</code>. The way we detect if it is the
|
||||
first time is through <code>pthread_getspecific()</code> which
|
||||
returns <code>NULL</code> if no previous value is associated with
|
||||
the thread using the key.</li>
|
||||
|
||||
<li>if <code>pthread_getspecific()</code> returns a non-null value,
|
||||
then that is the pointer to the error stack associated with the
|
||||
thread and the stack can be used as usual.</li>
|
||||
</ol>
|
||||
|
||||
A final change to the error reporting routines is as follows; the current
|
||||
implementation reports errors to always be detected at thread 0. In the
|
||||
threadsafe implementation, this is changed to report the number returned
|
||||
by a call to <code>pthread_self()</code>.
|
||||
|
||||
The change in code (reflected in <code>H5Eprint</code> of file
|
||||
<code>H5E.c</code>) is as follows:
|
||||
\code
|
||||
#ifdef H5_HAVE_THREADSAFE
|
||||
fprintf (stream, "HDF5-DIAG: Error detected in thread %d." ,pthread_self());
|
||||
#else
|
||||
fprintf (stream, "HDF5-DIAG: Error detected in thread 0.");
|
||||
#endif
|
||||
\endcode
|
||||
|
||||
Code for <code>H5E_get_stack()</code> can be found in Appendix \ref subsec_tsafe_app_C. All the
|
||||
above changes were made in <code>H5E.c</code>.
|
||||
|
||||
\subsection subsec_tsafe_impl_cancel Thread Cancellation safety
|
||||
To prevent thread cancellations from killing a thread while it is in the
|
||||
library, we maintain per-thread information about the cancellability
|
||||
status of the thread before it entered the library so that we can restore
|
||||
that same status when the thread leaves the library.
|
||||
|
||||
By <i>enter</i> and <i>leave</i> the library, we mean the points when a
|
||||
thread makes an API call from a user application and the time that API
|
||||
call returns. Other API or callback function calls made from within that
|
||||
API call are considered <i>within</i> the library.
|
||||
|
||||
Because other API calls may be made from within the first API call, we
|
||||
need to maintain a counter to determine which was the first and
|
||||
correspondingly the last return.
|
||||
|
||||
When a thread makes an API call, the macro <code>H5_API_SET_CANCEL</code>
|
||||
calls the worker function <code>H5_cancel_count_inc()</code> which does
|
||||
the following:
|
||||
<ol>
|
||||
<li>if this is the first time the thread has entered the library,
|
||||
a new cancellability structure needs to be assigned to it.</li>
|
||||
<li>if the thread is already within the library when the API call is
|
||||
made, then cancel_count is simply incremented. Otherwise, we set
|
||||
the cancellability state to <code>PTHREAD_CANCEL_DISABLE</code>
|
||||
while storing the previous state into the cancellability structure.
|
||||
<code>cancel_count</code> is also incremented in this case.</li>
|
||||
</ol>
|
||||
|
||||
When a thread leaves an API call, the macro
|
||||
<code>H5_API_UNSET_CANCEL</code> calls the worker function
|
||||
<code>H5_cancel_count_dec()</code> which does the following:
|
||||
<ol>
|
||||
<li>if <code>cancel_count</code> is greater than 1, indicating that the
|
||||
thread is not yet about to leave the library, then
|
||||
<code>cancel_count</code> is simply decremented.</li>
|
||||
<li>otherwise, we reset the cancellability state back to its original
|
||||
state before it entered the library and decrement the count (back
|
||||
to zero).</li>
|
||||
</ol>
|
||||
|
||||
<code>H5_cancel_count_inc</code> and <code>H5_cancel_count_dec</code> are
|
||||
described in Appendix \ref subsec_tsafe_app_D and may be found in <code>H5TS.c</code>.
|
||||
|
||||
\section sec_tsafe_test Test programs
|
||||
Except where stated, all tests involve 16 simultaneous threads that make
|
||||
use of HDF5 API calls without any explicit synchronization typically
|
||||
required in a non-threadsafe environment.
|
||||
|
||||
\subsection subsec_tsafe_test_create Data set create and write
|
||||
The test program sets up 16 threads to simultaneously create 16
|
||||
different datasets named from <i>zero</i> to <i>fifteen</i> for a single
|
||||
file and then writing an integer value into that dataset equal to the
|
||||
dataset's named value.
|
||||
|
||||
The main thread would join with all 16 threads and attempt to match the
|
||||
resulting HDF5 file with expected results - that each dataset contains
|
||||
the correct value (0 for <i>zero</i>, 1 for <i>one</i> etc ...) and all
|
||||
datasets were correctly created.
|
||||
|
||||
The test is implemented in the file <code>ttsafe_dcreate.c</code>.
|
||||
|
||||
\subsection subsec_tsafe_test_err Test on error stack
|
||||
The error stack test is one in which 16 threads simultaneously try to
|
||||
create datasets with the same name. The result, when properly serialized,
|
||||
should be equivalent to 16 attempts to create the dataset with the same
|
||||
name.
|
||||
|
||||
The error stack implementation runs correctly if it reports 15 instances
|
||||
of the dataset name conflict error and finally generates a correct HDF5
|
||||
containing that single dataset. Each thread should report its own stack
|
||||
of errors with a thread number associated with it.
|
||||
|
||||
The test is implemented in the file <code>ttsafe_error.c</code>.
|
||||
|
||||
\subsection subsec_tsafe_test_cancel Test on cancellation safety
|
||||
The main idea in thread cancellation safety is as follows; a child thread
|
||||
is spawned to create and write to a dataset. Following that, it makes a
|
||||
<code>H5Diterate</code> call on that dataset which activates a callback
|
||||
function.
|
||||
|
||||
A deliberate barrier is invoked at the callback function which waits for
|
||||
both the main and child thread to arrive at that point. After that
|
||||
happens, the main thread proceeds to make a thread cancel call on the
|
||||
child thread while the latter sleeps for 3 seconds before proceeding to
|
||||
write a new value to the dataset.
|
||||
|
||||
After the iterate call, the child thread logically proceeds to wait
|
||||
another 3 seconds before writing another newer value to the dataset.
|
||||
|
||||
The test is correct if the main thread manages to read the second value
|
||||
at the end of the test. This means that cancellation did not take place
|
||||
until the end of the iteration call despite of the 3 second wait within
|
||||
the iteration callback and the extra dataset write operation.
|
||||
Furthermore, the cancellation should occur before the child can proceed
|
||||
to write the last value into the dataset.
|
||||
|
||||
\subsection subsec_tsafe_test_attr Test on attribute creation
|
||||
A main thread makes 16 threaded calls to <code>H5Acreate</code> with a
|
||||
generated name for each attribute. Sixteen attributes should be created
|
||||
for the single dataset in random (chronological) order and receive values
|
||||
depending on its generated attribute name (e.g. <i>attrib010</i> would
|
||||
receive the value 10).
|
||||
|
||||
After joining with all child threads, the main thread proceeds to read
|
||||
each attribute by generated name to see if the value tallies. Failure is
|
||||
detected if the attribute name does not exist (meaning they were never
|
||||
created) or if the wrong values were read back.
|
||||
|
||||
\section sec_tsafe_app Appendix
|
||||
|
||||
\subsection subsec_tsafe_app_A Recursive Lock implementation code
|
||||
\code
|
||||
void H5_mutex_init(H5_mutex_t *H5_mutex)
|
||||
{
|
||||
H5_mutex->owner_thread = NULL;
|
||||
pthread_mutex_init(&H5_mutex->atomic_lock, NULL);
|
||||
pthread_cond_init(&H5_mutex->cond_var, NULL);
|
||||
H5_mutex->lock_count = 0;
|
||||
}
|
||||
|
||||
void H5_mutex_lock(H5_mutex_t *H5_mutex)
|
||||
{
|
||||
pthread_mutex_lock(&H5_mutex->atomic_lock);
|
||||
|
||||
if (pthread_equal(pthread_self(), H5_mutex->owner_thread)) {
|
||||
// already owned by self - increment count
|
||||
H5_mutex->lock_count++;
|
||||
}
|
||||
else {
|
||||
if (H5_mutex->owner_thread == NULL) {
|
||||
// no one else has locked it - set owner and grab lock
|
||||
H5_mutex->owner_thread = pthread_self();
|
||||
H5_mutex->lock_count = 1;
|
||||
}
|
||||
else {
|
||||
/* if already locked by someone else */
|
||||
while (1) {
|
||||
pthread_cond_wait(&H5_mutex->cond_var, &H5_mutex->atomic_lock);
|
||||
|
||||
if (H5_mutex->owner_thread == NULL) {
|
||||
H5_mutex->owner_thread = pthread_self();
|
||||
H5_mutex->lock_count = 1;
|
||||
break;
|
||||
} // else do nothing and loop back to wait on condition
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&H5_mutex->atomic_lock);
|
||||
}
|
||||
|
||||
void H5_mutex_unlock(H5_mutex_t *H5_mutex)
|
||||
{
|
||||
pthread_mutex_lock(&H5_mutex->atomic_lock);
|
||||
H5_mutex->lock_count--;
|
||||
|
||||
if (H5_mutex->lock_count == 0) {
|
||||
H5_mutex->owner_thread = NULL;
|
||||
pthread_cond_signal(&H5_mutex->cond_var);
|
||||
}
|
||||
pthread_mutex_unlock(&H5_mutex->atomic_lock);
|
||||
}
|
||||
\endcode
|
||||
|
||||
\subsection subsec_tsafe_app_B First thread initialization
|
||||
\code
|
||||
void H5_first_thread_init(void)
|
||||
{
|
||||
// initialize global API mutex lock
|
||||
H5_g.H5_libinit_g = FALSE;
|
||||
H5_g.init_lock.owner_thread = NULL;
|
||||
pthread_mutex_init(&H5_g.init_lock.atomic_lock, NULL);
|
||||
pthread_cond_init(&H5_g.init_lock.cond_var, NULL);
|
||||
H5_g.init_lock.lock_count = 0;
|
||||
|
||||
// initialize key for thread-specific error stacks
|
||||
pthread_key_create(&H5_errstk_key_g, NULL);
|
||||
|
||||
// initialize key for thread cancellability mechanism
|
||||
pthread_key_create(&H5_cancel_key_g, NULL);
|
||||
}
|
||||
\endcode
|
||||
|
||||
\subsection subsec_tsafe_app_C Per-thread error stack acquisition
|
||||
\code
|
||||
H5E_t *H5E_get_stack(void)
|
||||
{
|
||||
H5E_t *estack;
|
||||
|
||||
if (estack = pthread_getspecific(H5_errstk_key_g)) {
|
||||
return estack;
|
||||
}
|
||||
else {
|
||||
// no associated value with current thread - create one
|
||||
estack = (H5E_t *)malloc(sizeof(H5E_t));
|
||||
pthread_setspecific(H5_errstk_key_g, (void *)estack);
|
||||
return estack;
|
||||
}
|
||||
}
|
||||
\endcode
|
||||
|
||||
\subsection subsec_tsafe_app_D Thread cancellation mechanisms
|
||||
\code
|
||||
void H5_cancel_count_inc(void)
|
||||
{
|
||||
H5_cancel_t *cancel_counter;
|
||||
|
||||
if (cancel_counter = pthread_getspecific(H5_cancel_key_g)) {
|
||||
// do nothing here
|
||||
}
|
||||
else {
|
||||
// first time thread calls library - create new counter and
|
||||
// associate with key
|
||||
cancel_counter = (H5_cancel_t *)malloc(sizeof(H5_cancel_t));
|
||||
cancel_counter->cancel_count = 0;
|
||||
pthread_setspecific(H5_cancel_key_g, (void *)cancel_counter);
|
||||
}
|
||||
|
||||
if (cancel_counter->cancel_count == 0) {
|
||||
/* thread entering library */
|
||||
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &(cancel_counter->previous_state));
|
||||
}
|
||||
|
||||
cancel_counter->cancel_count++;
|
||||
}
|
||||
|
||||
void H5_cancel_count_dec(void)
|
||||
{
|
||||
H5_cancel_t *cancel_counter = pthread_getspecific(H5_cancel_key_g);
|
||||
|
||||
if (cancel_counter->cancel_count == 1)
|
||||
pthread_setcancelstate(cancel_counter->previous_state, NULL);
|
||||
|
||||
cancel_counter->cancel_count--;
|
||||
}
|
||||
\endcode
|
||||
|
||||
\subsection subsec_tsafe_app_E Macro expansion codes
|
||||
|
||||
\subsubsection subsubsec_tsafe_app_E1 <code>FUNC_ENTER</code>
|
||||
\code
|
||||
// Initialize the library \
|
||||
H5_FIRST_THREAD_INIT \
|
||||
H5_API_UNSET_CANCEL \
|
||||
H5_API_LOCK_BEGIN \
|
||||
if (!(H5_INIT_GLOBAL)) { \
|
||||
H5_INIT_GLOBAL = TRUE; \
|
||||
if (H5_init_library() < 0) { \
|
||||
HRETURN_ERROR (H5E_FUNC, H5E_CANTINIT, err, \
|
||||
"library initialization failed"); \
|
||||
} \
|
||||
} \
|
||||
H5_API_LOCK_END \
|
||||
:
|
||||
:
|
||||
:
|
||||
\endcode
|
||||
|
||||
\subsubsection subsubsec_tsafe_app_E2 <code>H5_FIRST_THREAD_INIT</code>
|
||||
\code
|
||||
// Macro for first thread initialization
|
||||
#define H5_FIRST_THREAD_INIT \
|
||||
pthread_once(&H5_first_init_g, H5_first_thread_init);
|
||||
\endcode
|
||||
|
||||
\subsubsection subsubsec_tsafe_app_E3 <code>H5_API_UNSET_CANCEL</code>
|
||||
\code
|
||||
#define H5_API_UNSET_CANCEL \
|
||||
if (H5_IS_API(__func__)) { \
|
||||
H5_cancel_count_inc(); \
|
||||
}
|
||||
\endcode
|
||||
|
||||
\subsubsection subsubsec_tsafe_app_E4 <code>H5_API_LOCK_BEGIN</code>
|
||||
\code
|
||||
#define H5_API_LOCK_BEGIN \
|
||||
if (H5_IS_API(__func__)) { \
|
||||
H5_mutex_lock(&H5_g.init_lock);
|
||||
\endcode
|
||||
|
||||
\subsubsection subsubsec_tsafe_app_E5 <code>H5_API_LOCK_END</code>
|
||||
\code
|
||||
#define H5_API_LOCK_END }
|
||||
\endcode
|
||||
|
||||
\subsubsection subsubsec_tsafe_app_E6 <code>HRETURN</code> and <code>HRETURN_ERROR</code>
|
||||
\code
|
||||
:
|
||||
:
|
||||
H5_API_UNLOCK_BEGIN \
|
||||
H5_API_UNLOCK_END \
|
||||
H5_API_SET_CANCEL \
|
||||
return ret_val; \
|
||||
}
|
||||
\endcode
|
||||
|
||||
\subsubsection subsubsec_tsafe_app_E7 <code>H5_API_UNLOCK_BEGIN</code>
|
||||
\code
|
||||
#define H5_API_UNLOCK_BEGIN \
|
||||
if (H5_IS_API(__func__)) { \
|
||||
H5_mutex_unlock(&H5_g.init_lock);
|
||||
\endcode
|
||||
|
||||
\subsubsection subsubsec_tsafe_app_E8 <code>H5_API_UNLOCK_END</code>
|
||||
\code
|
||||
#define H5_API_UNLOCK_END }
|
||||
</pre>
|
||||
\endcode
|
||||
|
||||
\subsubsection subsubsec_tsafe_app_E9 <code>H5_API_SET_CANCEL</code>
|
||||
\code
|
||||
#define H5_API_SET_CANCEL \
|
||||
if (H5_IS_API(__func__)) { \
|
||||
H5_cancel_count_dec(); \
|
||||
}
|
||||
\endcode
|
||||
|
||||
<hr>
|
||||
Navigate back: \ref index "Main" / \ref TN
|
||||
|
||||
*/
|
||||
Reference in New Issue
Block a user