diff --git a/doxygen/dox/Overview.dox b/doxygen/dox/Overview.dox
index c84ce0a9a60..1f68f7c50a3 100644
--- a/doxygen/dox/Overview.dox
+++ b/doxygen/dox/Overview.dox
@@ -9,19 +9,19 @@ the entire HDF5 ecosystem in one place, and you should also consult the document
sets of the many outstanding community projects.
For a first contact with HDF5, the best place is to have a look at the \link
-GettingStarted getting started\endlink page that shows you how to write and
+GettingStarted getting started \endlink page that shows you how to write and
compile your first program with HDF5.
The \b main \b documentation is organized by documentation flavor. Most
technical documentation consists to varying degrees of information related to
tasks, concepts, or reference material. As its title
-suggests, the \link RM Reference Manual\endlink is 100% reference material,
+suggests, the \link RM Reference Manual \endlink is 100% reference material,
while the \link Cookbook \endlink is focused on tasks. The different guide-type
documents cover a mix of tasks, concepts, and reference, to help a specific
audience succeed.
\par Offline reading
- You can download it as an archive for offline reading.
+ You can download it as an archive for offline reading.
\par ToDo List
There is plenty of unfinished business.
diff --git a/doxygen/dox/Specifications.dox b/doxygen/dox/Specifications.dox
index 42f06d23099..7807fa3ba11 100644
--- a/doxygen/dox/Specifications.dox
+++ b/doxygen/dox/Specifications.dox
@@ -17,7 +17,7 @@
\li \ref IMG
\li \ref TBL
-\li
+\li
HDF5 Dimension Scale Specification
*/
diff --git a/doxygen/dox/TechnicalNotes.dox b/doxygen/dox/TechnicalNotes.dox
index 1737b60ab0b..3ea6af63a25 100644
--- a/doxygen/dox/TechnicalNotes.dox
+++ b/doxygen/dox/TechnicalNotes.dox
@@ -4,10 +4,10 @@
\li \ref APPDBG
\li \ref FMTDISC
\li \ref FILEIMGOPS
-\li \ref FILTER
+\li \ref subsubsec_dataset_transfer_filter
\li \ref IOFLOW
\li \ref TNMDC
-\li \ref MT
+\li \ref thread-safe-lib
\li \ref SWMR
\li \ref VDS
\li \ref RELVERSION
@@ -17,12 +17,6 @@
*/
-/** \page MT HDF5 Thread Safe library
-
-\htmlinclude ThreadSafeLibrary.html
-
-*/
-
/** \page IOFLOW HDF5 Raw I/O Flow Notes
\htmlinclude IOFlow.html
@@ -53,12 +47,6 @@
*/
-/** \page FILTER HDF5 Filters
-
-\htmlinclude Filters.html
-
-*/
-
/** \page APPDBG Debugging HDF5 Applications
\htmlinclude DebuggingHDF5Applications.html
@@ -67,13 +55,262 @@
/** \page SWMR Introduction to Single-Writer/Multiple-Reader (SWMR)
-\htmlinclude intro_SWMR.html
+\section sec_swmr_intro Introduction to SWMR
+The Single-Writer / Multiple-Reader (SWMR) feature enables multiple processes to read an HDF5 file
+while it is being written to (by a single process) without using locks or requiring communication between processes.
+
+
+All communication between processes must be performed via the HDF5 file. The HDF5 file under SWMR access must
+reside on a system that complies with POSIX write() semantics.
+
+The basic engineering challenge for this to work was to ensure that the readers of an HDF5 file always
+see a coherent (though possibly not up to date) HDF5 file.
+
+The issue is that when writing data there is information in the metadata cache in addition to the physical file on disk:
+
+
+However, the readers can only see the state contained in the physical file:
+
+
+The SWMR solution implements dependencies on when the metadata can be flushed to the file. This ensures that metadata cache
+flush operations occur in the proper order, so that there will never be internal file pointers in the physical file
+that point to invalid (unflushed) file addresses.
+
+A beneficial side effect of using SWMR access is better fault tolerance. It is more difficult to corrupt a file when using SWMR.
+
+\subsection subsec_swmr_doc Documentation
+\subsubsection subsubsec_swmr_doc_guide User Guide
+SWMR User Guide
+
+\subsubsection subsubsec_swmr_doc_apis HDF5 Library APIs
+
+- #H5Fstart_swmr_write — Enables SWMR writing mode for a file
+- #H5DOappend — Appends data to a dataset along a specified dimension
+- #H5Pset_object_flush_cb — Sets a callback function to invoke when an object flush occurs in the file
+- #H5Pget_object_flush_cb — Retrieves the object flush property values from the file access property list
+- #H5Odisable_mdc_flushes — Prevents metadata entries for an HDF5 object from being flushed from the metadata cache to storage
+- #H5Oenable_mdc_flushes — Enables flushing of dirty metadata entries from a file’s metadata cache
+- #H5Oare_mdc_flushes_disabled — Determines if an HDF5 object has had flushes of metadata entries disabled
+
+
+\subsubsection subsubsec_swmr_doc_tools Tools
+\li h5watch — Outputs new records appended to a dataset as the dataset grows
+\li h5format_convert — Converts the layout format version and chunked indexing types of datasets created with
+HDF5-1.10 so that applications built with HDF5-1.8 can access them
+\li h5clear — Clears superblock status_flags field, removes metadata cache image, prints EOA and EOF, or sets EOA of a file
+
+\subsubsection subsubsec_swmr_doc_design Design Documents
+
+\subsection subsec_swmr_model Programming Model
+Please be aware that the SWMR feature requires that an HDF5 file be created with the latest file format. See
+#H5Pset_libver_bounds for more information.
+
+To use SWMR follow the the general programming model for creating and accessing HDF5 files and objects along with the steps described below.
+
+\subsubsection subsubsec_swmr_model_writer SWMR Writer
+The SWMR writer either opens an existing file and objects or creates them as follows.
+
+Open an existing file:
+Call #H5Fopen using the #H5F_ACC_SWMR_WRITE flag.
+Begin writing datasets.
+Periodically flush data.
+
+Create a new file:
+Call #H5Fcreate using the latest file format.
+Create groups, datasets and attributes, and then close the attributes.
+Call #H5Fstart_swmr_write to start SWMR access to the file.
+Periodically flush data.
+
+Example Code:
+Create the file using the latest file format property:
+\code
+ fapl = H5Pcreate (H5P_FILE_ACCESS);
+ status = H5Pset_libver_bounds (fapl, H5F_LIBVER_LATEST, H5F_LIBVER_LATEST);
+ fid = H5Fcreate (filename, H5F_ACC_TRUNC, H5P_DEFAULT, fapl);
+ // Create objects (files, datasets, ...).
+ // Close any attributes and named datatype objects.
+ // Groups and datasets may remain open before starting SWMR access to them.
+
+ // Start SWMR access to the file:
+ status = H5Fstart_swmr_write (fid);
+
+ // Reopen the datasets and then start writing, periodically flushing data:
+ status = H5Dwrite (dset_id, ...);
+ status = H5Dflush (dset_id);
+\endcode
+
+\subsubsection subsubsec_swmr_model_reader SWMR Reader
+The SWMR reader must continually poll for new data:
+
+Call #H5Fopen using the #H5F_ACC_SWMR_READ flag.
+Poll, checking the size of the dataset to see if there is new data available for reading.
+Read new data, if any.
+
+Example Code:
+\code
+ // Open the file using the SWMR read flag:
+ fid = H5Fopen (filename, H5F_ACC_RDONLY | H5F_ACC_SWMR_READ, H5P_DEFAULT);
+ // Open the dataset and then repeatedly poll the dataset, by getting the dimensions, reading new data, and refreshing:
+ dset_id = H5Dopen (...);
+ space_id = H5Dget_space (...);
+ while (...) {
+ status = H5Dread (dset_id, ...);
+ status = H5Drefresh (dset_id);
+ space_id = H5Dget_space (...);
+ }
+\endcode
+
+\subsection subsec_swmr_scope Limitations and Scope
+An HDF5 file under SWMR access must reside on a system that complies with POSIX write()
+semantics. It is also limited in scope as follows.
+
+The writer process is only allowed to modify raw data of existing datasets by;
+Appending data along any unlimited dimension.
+Modifying existing data
+The following operations are not allowed (and the corresponding HDF5 files will fail)
+\li The writer cannot add new objects to the file.
+\li The writer cannot delete objects in the file.
+\li The writer cannot modify or append data with variable length, string or region reference datatypes.
+\li File space recycling is not allowed. As a result the size of a file modified by a SWMR writer may be larger than a file modified by a non-SWMR writer.
+
+\subsection subsec_swmr_tools Tools for Working with SWMR
+Two new tools, h5watch and h5clear, are available for use with SWMR. The other HDF5 utilities have also been modified to recognize SWMR
+\li The h5watch tool allows a user to monitor the growth of a dataset.
+\li The h5clear tool clears the status flags in the superblock of an HDF5 file.
+\li The rest of the HDF5 tools will exit gracefully but not work with SWMR otherwise.
+
+\subsection subsec_swmr_example Programming Example
+A good example of using SWMR is included with the HDF5 tests in the source code. You can run it while reading
+the file it creates. If you then interrupt the application and reader and look at the resulting file, you will
+see that the file is still valid. Follow these steps:
+\li Download the HDF5 source code to a local directory on a filesystem (that complies with POSIX write() semantics).
+Build the software. No special configuration options are needed to use SWMR.
+\li Invoke two command terminal windows. In one window go into the bin directory of the built binaries.
+In the other window go into the test directory of the HDF5-1.10 source code that was just built.
+\li In the window in the test directory compile and run use_append_chunk.c. The example writes a three
+dimensional dataset by planes (with chunks of size 1 x 256 x 256).
+\li In the other window (in the bin directory) run h5watch on the file created by
+use_append_chunk.c (use_append_chunk.h5). It should be run while use_append_chunk is executing and you
+will see valid data displayed with h5watch.
+\li Interrupt use_append_chunk while it is running, and stop h5watch.
+\li Use h5clear to clear the status flags in the superblock of the HDF5 file (use_append_chunk.h5).
+\li View the file with h5dump. You will see that it is a valid file even though the application did not
+close properly. It will contain data up to the point that it was interrupted.
*/
/** \page VDS Introduction to the Virtual Dataset - VDS
-\htmlinclude intro_VDS.html
+\section sec_vds_intro Introduction to VDS
+The HDF5 Virtual Dataset (VDS) feature enables users to access data in a collection of HDF5 files as a
+single HDF5 dataset and to use the HDF5 APIs to work with that dataset.
+
+For example, your data may be collected into four files:
+
+
+You can map the datasets in the four files into a single VDS that can be accessed just like any other dataset:
+
+
+The mapping between a VDS and the HDF5 source datasets is persistent and transparent to an application. If a source
+file is missing the fill value will be displayed.
+
+See the Virtual (VDS) Documentation for complete details regarding the VDS feature.
+
+The VDS feature was implemented using hyperslab selection (#H5Sselect_hyperslab). See the tutorial on
+Reading From or Writing to a Subset of a Dataset for more information on selecting hyperslabs.
+
+\subsection subsec_vds_intro_model Programming Model
+To create a Virtual Dataset you simply follow the HDF5 programming model and add a few additional API calls
+to map the source code datasets to the VDS.
+
+Following are the steps for creating a Virtual Dataset:
+\li Create the source datasets that will comprise the VDS
+\li Create the VDS: ‐ Define a datatype and dataspace (can be unlimited)
+\li Define the dataset creation property list (including fill value)
+\li (Repeat for each source dataset) Map elements from the source dataset to elements of the VDS
+\li Select elements in the source dataset (source selection)
+\li Select elements in the virtual dataset (destination selection)
+\li Map destination selections to source selections (see Functions for Working with a VDS)
+\li Call H5Dcreate using the properties defined above
+\li Access the VDS as a regular HDF5 dataset
+\li Close the VDS when finished
+
+Functions for Working with a VDS
+The #H5Pset_virtual API sets the mapping between virtual and source datasets. This is a dataset creation property list.
+Using this API will change the layout of the dataset to #H5D_VIRTUAL. As with specifying any dataset creation property
+list, an instance of the property list is created, modified, passed into the dataset creation call and then closed:
+\code
+ dcpl = H5Pcreate (H5P_DATASET_CREATE);
+ src_space = H5screate_simple ...
+ status = H5Sselect_hyperslab (space, ...
+ status = H5Pset_virtual (dcpl, space, SRC_FILE[i], SRC_DATASET[i], src_space);
+ dset = H5Dcreate2 (file, DATASET, H5T_NATIVE_INT, space, H5P_DEFAULT, dcpl, H5P_DEFAULT);
+ status = H5Pclose (dcpl);
+\endcode
+
+There are several other APIs introduced with Virtual Datasets, including query functions. For details
+see the complete list of HDF5 library APIs that support Virtual Datasets.
+
+Limitations
+This feature was introduced in HDF5-1.10.
+
+The number of source datasets is unlimited. However, there is a limit on the size of each source dataset.
+
+\subsection subsec_vds_intro_examples Programming Examples
+Example 1
+This example creates three HDF5 files, each with a one-dimensional dataset of 6 elements. The datasets in these files
+are the source datasets that are then used to create a 4 x 6 Virtual Dataset with a fill value of -1. The first three
+rows of the VDS are mapped to the data from the three source datasets as shown below:
+
+
+In this example the three source datasets are mapped to the VDS with this code:
+\code>
+src_space = H5Screate_simple (RANK1, dims, NULL);
+for (i = 0; i < 3; i++) {
+ start[0] = (hsize_t)i;
+ // Select i-th row in the virtual dataset; selection in the source datasets is the same.
+ status = H5Sselect_hyperslab (space, H5S_SELECT_SET, start, NULL, count, block);
+ status = H5Pset_virtual (dcpl, space, SRC_FILE[i], SRC_DATASET[i], src_space);
+}
+endcode>
+
+After the VDS is created and closed, it is reopened. The property list is then queried to determine the
+layout of the dataset and its mappings, and the data in the VDS is read and printed.
+
+This example is in the HDF5 source code and can be obtained from here:
+C Example
+For details on compiling an HDF5 application: [ Compiling HDF5 Applications ]
+
+Example 2
+This example shows how to use a C-style printf statement for specifying multiple source datasets as one virtual
+dataset. Only one mapping is required. In other words only one #H5Pset_virtual call is needed to map multiple datasets.
+It creates a 2-dimensional unlimited VDS. Then it re-opens the file, makes queries, and reads the virtual dataset.
+
+The source datasets are specified as A-0, A-1, A-2, and A-3. These are mapped to the virtual dataset with one call:
+\code
+status = H5Pset_virtual (dcpl, vspace, SRCFILE, "A-%b", src_space);
+\endcode
+
+The %b indicates that the block count of the selection in the dimension should be used.
+
+C Example
+For details on compiling an HDF5 application: [ Compiling HDF5 Applications ]
+
+Using h5dump with a VDS
+The h5dump utility can be used to view a VDS. The h5dump output for a VDS looks exactly like that for any other dataset.
+If h5dump cannot find a source dataset then the fill value will be displayed.
+
+You can determine that a dataset is a VDS by looking at its properties with
+\code
+ h5dump -p
+\endcode
+ It will display each source dataset mapping, beginning with Mapping 0. Below is an excerpt of the output of
+\code
+ h5dump -p
+\endcode
+on the vds.h5 file created in Example 1.You can see that the entire source file a.h5 is mapped to the first row of the VDS dataset.
+
+
*/
diff --git a/doxygen/examples/ThreadSafeLibrary.html b/doxygen/dox/ThreadSafe.dox
similarity index 62%
rename from doxygen/examples/ThreadSafeLibrary.html
rename to doxygen/dox/ThreadSafe.dox
index 5824dc6feac..73365120527 100644
--- a/doxygen/examples/ThreadSafeLibrary.html
+++ b/doxygen/dox/ThreadSafe.dox
@@ -1,155 +1,95 @@
-
-
-
- Thread Safe Library
-
+/** \page thread-safe-lib Thread Safe Library
-1. Library header files and conditional compilation
-
-
+\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>
+ #include
#endif
-
-
+\endcode
-
H5_HAVE_THREADSAFE
is defined when the HDF5 library is
-compiled with the --enable-threadsafe configuration option. In general,
+compiled with the --enable-threadsafe configuration option using autotools or
+HDF5_ENABLE_THREADSAFE=ON using CMake. In general,
code for the non-threadsafe version of HDF5 library are placed within
-the #else
part of the conditional compilation. The exception
+the # else
part of the conditional compilation. The exception
to this rule are the changes to the FUNC_ENTER
(in
H5private.h), HRETURN
and HRETURN_ERROR
(in
-H5Eprivate.h) macros (see section 3.2).
+H5Eprivate.h) macros (see section \ref subsec_tsafe_macro_ret).
+\section sec_tsafe_global Global variables/structures
-2. Global variables/structures
-
-2.1 Global library initialization variable
-
-
+\subsection subsec_tsafe_global_var Global library initialization variable
In the threadsafe implementation, the global library initialization
variable H5_libinit_g
is changed to a global structure
consisting of the variable with its associated lock (locks are explained
-in section 4.1):
-
-
-
-
+in section \ref subsec_tsafe_impl_locks):
+\code
hbool_t H5_libinit_g = FALSE;
-
-
+\endcode
-
becomes
-
-
-
-
+\code
H5_api_t H5_g;
-
-
+\endcode
-
where H5_api_t
is
-
-
-
-
+\code
typedef struct H5_api_struct {
- H5_mutex_t init_lock; /* API entrance mutex */
+ H5_mutex_t init_lock; // API entrance mutex
hbool_t H5_libinit_g;
} H5_api_t;
-
-
+\endcode
-
All former references to H5_libinit_g
in the library are now
made using the macro H5_INIT_GLOBAL
. If the threadsafe
library is to be used, the macro is set to H5_g.H5_libinit_g
instead.
-
-2.2 Global serialization variable
-
-
+\subsection subsec_tsafe_global_serial Global serialization variable
A new global boolean variable H5_allow_concurrent_g
is used
to determine if multiple threads are allowed to an API call
simultaneously. This is set to FALSE
.
-
-
All APIs that are allowed to do so have their own local variable that
shadows the global variable and is set to TRUE
. In phase 1,
no such APIs exist.
-
-
It is defined in H5.c
as follows:
-
-
-
-
+\code
hbool_t H5_allow_concurrent_g = FALSE;
-
-
-
-2.3 Global thread initialization variable
+\endcode
-
+\subsection subsec_tsafe_global_init Global thread initialization variable
The global variable H5_first_init_g
of type
pthread_once_t
is used to allow only the first thread in the
application process to call an initialization function using
pthread_once
. All subsequent calls to
pthread_once
by any thread are disregarded.
-
-
The call sets up the mutex in the global structure H5_g
(see
-section 3.1) via an initialization function
+section \ref subsec_tsafe_global_var) via an initialization function
H5_first_thread_init
. The first thread initialization
-function is described in section 4.2.
-
+function is described in section \ref subsec_tsafe_impl_first.
-
H5_first_init_g
is defined in H5.c
as follows:
-
-
-
-
+\code
pthread_once_t H5_first_init_g = PTHREAD_ONCE_INIT;
-
-
-
-2.4 Global key for per-thread error stacks
+\endcode
-
+\subsection subsec_tsafe_global_key Global key for per-thread error stacks
A global pthread-managed key H5_errstk_key_g
is used to
allow pthreads to maintain a separate error stack (of type
H5E_t
) for each thread. This is defined in H5.c
as:
-
-
-
-
+\code
pthread_key_t H5_errstk_key_g;
-
-
-
-
-Error stack management is described in section 4.3.
-
+\endcode
-2.5 Global structure and key for thread cancellation prevention
+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 H5_cancel_key_g
. The status is
preserved using a structure (of type H5_cancel_t
) which
@@ -157,32 +97,21 @@
2.5 Global structure and key for thread cancellation prevention
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 H5private.h
as:
-
-
-
-
- /* cancellability structure */
+\code
+ // cancellability structure
typedef struct H5_cancel_struct {
int previous_state;
unsigned int cancel_count;
} H5_cancel_t;
-
-
-
-
-Thread cancellation is described in section 4.4.
-
+\endcode
+Thread cancellation is described in section \ref subsec_tsafe_impl_cancel.
-3. Changes to Macro expansions
+\section sec_tsafe_macro Changes to Macro expansions
-3.1 Changes to FUNC_ENTER
-
-
+\subsection subsec_tsafe_macro_fe Changes to FUNC_ENTER
The FUNC_ENTER
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
@@ -191,458 +120,345 @@
3.1 Changes to FUNC_ENTER
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 E.1 to E.5. The changes are made in H5private.h
.
-
-
-3.2 Changes to HRETURN and HRETURN_ERROR
+Appendix \ref subsec_tsafe_app_E. The changes are made in H5private.h
.
-
+\subsection subsec_tsafe_macro_ret Changes to HRETURN and HRETURN_ERROR
The HRETURN
and HRETURN_ERROR
macros are the
counterparts to the FUNC_ENTER
macro described in section
-3.1. FUNC_LEAVE
makes a macro call to HRETURN
,
+\ref subsec_tsafe_macro_fe. FUNC_LEAVE
makes a macro call to HRETURN
,
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 3.1.
-
+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 E.9 to E.9. The changes are made in H5Eprivate.h
.
-
-
-4. Implementation of threadsafe functionality
+Appendix \ref subsec_tsafe_app_E. The changes are made in H5Eprivate.h
.
-4.1 Recursive Locks
+\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
(H5private.h
):
-
-
-
-
+\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 */
+ 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 A. The
+Detailed implementation code can be found in Appendix \ref subsec_tsafe_app_A. The
implementation changes are made in H5TS.c
.
-
-
-4.2 First thread initialization
-
+\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 H5_g
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 3.3 allows only the first thread accessing the
+described in section \ref subsec_tsafe_global_init allows only the first thread accessing the
library in an application to initialize H5_g
.
-
-
In addition to initializing H5_g
, it also initializes the
-key (see section 3.4) for use with per-thread error stacks (see section
-4.3).
-
+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 H5_first_thread_init()
in H5TS.c
. This is
described in appendix B.
-
-4.3 Per-thread error stack management
-
-
+\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
-pthread_key_create()
in pthreads (see sections 3.4 and 4.2).
+pthread_key_create()
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
pthread_getspecific()
and pthread_setspecific()
calls to read and write to the association table respectively.
-
-
Per-thread error stacks are accessed through the key
H5_errstk_key_g
which is initialized by the first thread
-initialization call (see section 4.2).
-
+initialization call (see section \ref subsec_tsafe_impl_first).
-
In the non-threadsafe version of the library, there is a global stack
variable H5E_stack_g[1]
which is no longer defined in the
threadsafe version. At the same time, the macro call to gain access to
the error stack H5E_get_my_stack
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 H5E_get_stack()
is a surrogate function that does the
following operations:
-
-
- - 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
H5_errstk_key_g
using
- pthread_setspecific()
. The way we detect if it is the
- first time is through pthread_getspecific()
which
- returns NULL
if no previous value is associated with
- the thread using the key.
-
- - if
pthread_getspecific()
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.
+- 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
H5_errstk_key_g
using
+pthread_setspecific()
. The way we detect if it is the
+first time is through pthread_getspecific()
which
+returns NULL
if no previous value is associated with
+the thread using the key.
+
+- if
pthread_getspecific()
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.
-
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 pthread_self()
.
-
-
The change in code (reflected in H5Eprint
of file
H5E.c
) is as follows:
-
-
-
-
+\code
#ifdef H5_HAVE_THREADSAFE
- fprintf (stream, "HDF5-DIAG: Error detected in thread %d."
- ,pthread_self());
+ 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 H5E_get_stack()
can be found in Appendix C. All the
+Code for H5E_get_stack()
can be found in Appendix \ref subsec_tsafe_app_C. All the
above changes were made in H5E.c
.
-
-
-4.4 Thread Cancellation safety
-
+\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 enter and leave 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 within 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 H5_API_SET_CANCEL
calls the worker function H5_cancel_count_inc()
which does
the following:
-
-
- - if this is the first time the thread has entered the library,
- a new cancellability structure needs to be assigned to it.
- - 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
PTHREAD_CANCEL_DISABLE
- while storing the previous state into the cancellability structure.
- cancel_count
is also incremented in this case.
+- if this is the first time the thread has entered the library,
+a new cancellability structure needs to be assigned to it.
+- 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
PTHREAD_CANCEL_DISABLE
+while storing the previous state into the cancellability structure.
+cancel_count
is also incremented in this case.
-
When a thread leaves an API call, the macro
H5_API_UNSET_CANCEL
calls the worker function
H5_cancel_count_dec()
which does the following:
-
-
- - if
cancel_count
is greater than 1, indicating that the
- thread is not yet about to leave the library, then
- cancel_count
is simply decremented.
- - otherwise, we reset the cancellability state back to its original
- state before it entered the library and decrement the count (back
- to zero).
+- if
cancel_count
is greater than 1, indicating that the
+thread is not yet about to leave the library, then
+cancel_count
is simply decremented.
+- otherwise, we reset the cancellability state back to its original
+state before it entered the library and decrement the count (back
+to zero).
-
H5_cancel_count_inc
and H5_cancel_count_dec
are
-described in Appendix D and may be found in H5TS.c
.
-
+described in Appendix \ref subsec_tsafe_app_D and may be found in H5TS.c
.
-5. Test programs
-
-
+\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.
-
-5.1 Data set create and write
-
-
+\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 zero to fifteen 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 zero, 1 for one etc ...) and all
datasets were correctly created.
-
-
The test is implemented in the file ttsafe_dcreate.c
.
-
-
-5.2 Test on error stack
-
+\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 ttsafe_error.c
.
-
-
-5.3 Test on cancellation safety
-
+\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
H5Diterate
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.
-
-5.4 Test on attribute creation
-
-
+\subsection subsec_tsafe_test_attr Test on attribute creation
A main thread makes 16 threaded calls to H5Acreate
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. attrib010 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.
-
-A. Recursive Lock implementation code
+\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->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);
+ 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);
+ 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);
}
-
-
-
-B. First thread initialization
+\endcode
-
-
+\subsection subsec_tsafe_app_B First thread initialization
+\code
void H5_first_thread_init(void)
{
- /* initialize global API mutex lock */
+ // 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 */
+ // initialize key for thread-specific error stacks
pthread_key_create(&H5_errstk_key_g, NULL);
- /* initialize key for thread cancellability mechanism */
+ // initialize key for thread cancellability mechanism
pthread_key_create(&H5_cancel_key_g, NULL);
}
-
-
-
+\endcode
-C. Per-thread error stack acquisition
-
-
-
+\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;
+ 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
-D. Thread cancellation mechanisms
-
-
-
+\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
- */
+ // 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);
@@ -650,8 +466,7 @@ D. Thread cancellation mechanisms
if (cancel_counter->cancel_count == 0) {
/* thread entering library */
- pthread_setcancelstate(PTHREAD_CANCEL_DISABLE,
- &(cancel_counter->previous_state));
+ pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &(cancel_counter->previous_state));
}
cancel_counter->cancel_count++;
@@ -666,22 +481,19 @@ D. Thread cancellation mechanisms
cancel_counter->cancel_count--;
}
-
-
+\endcode
-E. Macro expansion codes
+\subsection subsec_tsafe_app_E Macro expansion codes
-E.1 FUNC_ENTER
-
-
-
- /* Initialize the library */ \
+\subsubsection subsubsec_tsafe_app_E1 FUNC_ENTER
+\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) { \
+ if (H5_init_library() < 0) { \
HRETURN_ERROR (H5E_FUNC, H5E_CANTINIT, err, \
"library initialization failed"); \
} \
@@ -690,56 +502,37 @@ E.1 FUNC_ENTER
:
:
:
-
-
+\endcode
-E.2 H5_FIRST_THREAD_INIT
-
-
-
- /* Macro for first thread initialization */
+\subsubsection subsubsec_tsafe_app_E2 H5_FIRST_THREAD_INIT
+\code
+ // Macro for first thread initialization
#define H5_FIRST_THREAD_INIT \
pthread_once(&H5_first_init_g, H5_first_thread_init);
-
-
-
-
-E.3 H5_API_UNSET_CANCEL
+\endcode
-
-
+\subsubsection subsubsec_tsafe_app_E3 H5_API_UNSET_CANCEL
+\code
#define H5_API_UNSET_CANCEL \
if (H5_IS_API(__func__)) { \
H5_cancel_count_inc(); \
}
-
-
-
-
-E.4 H5_API_LOCK_BEGIN
+\endcode
-
-
+\subsubsection subsubsec_tsafe_app_E4 H5_API_LOCK_BEGIN
+\code
#define H5_API_LOCK_BEGIN \
if (H5_IS_API(__func__)) { \
H5_mutex_lock(&H5_g.init_lock);
-
-
-
+\endcode
-E.5 H5_API_LOCK_END
-
-
-
+\subsubsection subsubsec_tsafe_app_E5 H5_API_LOCK_END
+\code
#define H5_API_LOCK_END }
-
-
-
-
-E.6 HRETURN
and HRETURN_ERROR
+\endcode
-
-
+\subsubsection subsubsec_tsafe_app_E6 HRETURN
and HRETURN_ERROR
+\code
:
:
H5_API_UNLOCK_BEGIN \
@@ -747,41 +540,27 @@ E.6 HRETURN
and HRETURN_ERROR
H5_API_SET_CANCEL \
return ret_val; \
}
-
-
-
-E.7 H5_API_UNLOCK_BEGIN
+\endcode
-
-
+\subsubsection subsubsec_tsafe_app_E7 H5_API_UNLOCK_BEGIN
+\code
#define H5_API_UNLOCK_BEGIN \
if (H5_IS_API(__func__)) { \
H5_mutex_unlock(&H5_g.init_lock);
-
-
-
-E.8 H5_API_UNLOCK_END
+\endcode
-
-
+\subsubsection subsubsec_tsafe_app_E8 H5_API_UNLOCK_END
+\code
#define H5_API_UNLOCK_END }
-
+\endcode
-
-E.9 H5_API_SET_CANCEL
-
-
-
+\subsubsection subsubsec_tsafe_app_E9 H5_API_SET_CANCEL
+\code
#define H5_API_SET_CANCEL \
if (H5_IS_API(__func__)) { \
H5_cancel_count_dec(); \
}
-
-
-
-By Chee Wai Lee
-By Bill Wendling
+\endcode
-
-