summaryrefslogtreecommitdiffstats
path: root/test
diff options
context:
space:
mode:
authorChee-Wai Lee <cwlee@ncsa.uiuc.edu>2000-05-18 19:13:33 (GMT)
committerChee-Wai Lee <cwlee@ncsa.uiuc.edu>2000-05-18 19:13:33 (GMT)
commite26f4e5eed2e219597d4bfa85925840c84dd64db (patch)
tree281871beb7e12d1a2dd90e3bdd49eaa2bdf19fd8 /test
parentbc520e88b4ad3b175c0aeca2c90e021956676533 (diff)
downloadhdf5-e26f4e5eed2e219597d4bfa85925840c84dd64db.zip
hdf5-e26f4e5eed2e219597d4bfa85925840c84dd64db.tar.gz
hdf5-e26f4e5eed2e219597d4bfa85925840c84dd64db.tar.bz2
[svn-r2264] Added Thread-safe feature. This is the phase 1 implementation
that all HDF5 API functions are protected by a mutex lock. Basically, serialized all API calls. To use it, use configure --enable-threadsafe --with-pthread
Diffstat (limited to 'test')
-rw-r--r--test/Makefile.in20
-rw-r--r--test/ttsafe.c334
-rw-r--r--test/ttsafe.h141
-rw-r--r--test/ttsafe_acreate.c166
-rw-r--r--test/ttsafe_cancel.c203
-rw-r--r--test/ttsafe_dcreate.c191
-rw-r--r--test/ttsafe_error.c179
7 files changed, 1231 insertions, 3 deletions
diff --git a/test/Makefile.in b/test/Makefile.in
index 2f0912d..eba7699 100644
--- a/test/Makefile.in
+++ b/test/Makefile.in
@@ -16,10 +16,12 @@ CPPFLAGS=-I. -I$(srcdir) -I../src -I$(top_srcdir)/src @CPPFLAGS@
## These are our main targets. They should be listed in the order to be
## executed, generally most specific tests to least specific tests.
RUNTEST=$(LT_RUN)
+
TEST_PROGS=testhdf5 lheap ohdr stab gheap hyperslab istore bittests dtypes \
dsets cmpd_dset extend external links unlink big mtime fillval mount \
flush1 flush2 enum gass_write gass_read gass_append dpss_write \
- dpss_read srb_write srb_append srb_read
+ dpss_read srb_write srb_append srb_read ttsafe
+
TIMINGS=iopipe chunk ragged overhead
## The libh5test.a library provides common support code for the tests. We link
@@ -46,25 +48,28 @@ MOSTLYCLEAN=cmpd_dset.h5 dataset.h5 extend.h5 istore.h5 tfile1.h5 tfile2.h5 \
big.data big[0-9][0-9][0-9][0-9][0-9].h5 dtypes1.h5 dtypes2.h5 \
tattr.h5 tselect.h5 mtime.h5 ragged.h5 unlink.h5 overhead.h5 \
fillval_[0-9].h5 fillval.raw mount_[0-9].h5 trefer[12].h5 \
- tvltypes.h5 tvlstr.h5 flush.h5 enum1.h5 titerate.h5
+ tvltypes.h5 tvlstr.h5 flush.h5 enum1.h5 titerate.h5 ttsafe.h5
CLEAN=$(TIMINGS)
## Source and object files for programs... The TEST_SRC list contains all the
## source files and is used for things like dependencies, archiving, etc. The
## other source lists are for the individual tests, the files of which may
## overlap with other tests.
+
TEST_SRC=big.c bittests.c chunk.c cmpd_dset.c dsets.c dtypes.c extend.c \
external.c fillval.c flush1.c flush2.c gheap.c h5test.c hyperslab.c \
iopipe.c istore.c lheap.c links.c mount.c mtime.c ohdr.c overhead.c \
ragged.c stab.c tattr.c testhdf5.c tfile.c th5s.c titerate.c tmeta.c \
trefer.c tselect.c ttbbt.c tvltypes.c tvlstr.c unlink.c enum.c \
+ ttsafe.c ttsafe_dcreate.c ttsafe_error.c ttsafe_cancel.c \
+ ttsafe_acreate.c \
gass_write.c gass_read.c gass_append.c dpss_read.c dpss_write.c \
srb_read.c srb_write.c srb_append.c
TEST_OBJ=$(TEST_SRC:.c=.lo)
## Private header files (not to be installed)...
-PRIVATE_HDR=testhdf5.h
+PRIVATE_HDR=testhdf5.h ttsafe.h
## Additional targets
.PHONY: timings _timings
@@ -78,7 +83,12 @@ timings _timings: $(TIMINGS)
## How to build the tests... They all depend on the test and hdf5 libraries.
$(TEST_PROGS): $(LIB) $(LIBHDF5)
+
TESTHDF5_OBJ=testhdf5.lo tattr.lo tfile.lo titerate.lo tmeta.lo trefer.lo tselect.lo ttbbt.lo tvltypes.lo tvlstr.lo th5s.lo
+
+TTS_OBJ=ttsafe.lo ttsafe_dcreate.lo ttsafe_error.lo ttsafe_cancel.lo \
+ ttsafe_acreate.lo
+
testhdf5: $(TESTHDF5_OBJ)
@$(LT_LINK_EXE) $(CFLAGS) -o $@ $(TESTHDF5_OBJ) $(LIB) $(LIBHDF5) $(LDFLAGS) $(LIBS)
@@ -157,6 +167,9 @@ flush2: flush2.lo
enum: enum.lo
@$(LT_LINK_EXE) $(CFLAGS) -o $@ enum.lo $(LIB) $(LIBHDF5) $(LDFLAGS) $(LIBS)
+ttsafe: $(TTS_OBJ)
+ @$(LT_LINK_EXE) $(CFLAGS) -o $@ $(TTS_OBJ) $(LIB) $(LIBHDF5) $(LDFLAGS) $(LIBS)
+
gass_write: gass_write.lo
@$(LT_LINK_EXE) $(CFLAGS) -o $@ gass_write.lo $(LIB) $(LIBHDF5) $(LDFLAGS) $(LIBS)
@@ -182,3 +195,4 @@ srb_append: srb_append.lo
@$(LT_LINK_EXE) $(CFLAGS) -o $@ srb_append.lo $(LIB) $(LIBHDF5) $(LDFLAGS) $(LIBS)
@CONCLUDE@
+
diff --git a/test/ttsafe.c b/test/ttsafe.c
new file mode 100644
index 0000000..cf87752
--- /dev/null
+++ b/test/ttsafe.c
@@ -0,0 +1,334 @@
+/****************************************************************************
+ * NCSA HDF *
+ * Software Development Group *
+ * National Center for Supercomputing Applications *
+ * University of Illinois at Urbana-Champaign *
+ * 605 E. Springfield, Champaign IL 61820 *
+ * *
+ * For conditions of distribution and use, see the accompanying *
+ * hdf/COPYING file. *
+ * *
+ ****************************************************************************/
+
+#ifdef RCSID
+static char RcsId[] = "@(#)$Revision$";
+#endif
+
+/* $Id$ */
+
+/*
+ FILE
+ ttsafe.c - HDF5 threadsafe testing framework main file.
+
+ REMARKS
+ General test wrapper for HDF5 library thread safety test programs
+
+ DESIGN
+ Each test function should be implemented as function having no
+ parameters and returning void (i.e. no return value). They should be put
+ into the list of InitTest() calls in main() below. Functions which depend
+ on other functionality should be placed below the InitTest() call for the
+ base functionality testing.
+ Each test module should include ttsafe.h and define a unique set of
+ names for test files they create.
+
+ BUGS/LIMITATIONS
+
+ EXPORTED ROUTINES/VARIABLES:
+ Two variables are exported: num_errs, and Verbosity.
+
+ */
+
+#if defined __MWERKS__
+#include <console.h>
+#endif
+
+#include <stdarg.h>
+
+#define MAXNUMOFTESTS 50
+#define HDF5_TEST_MASTER
+
+#define MAX_NUM_NAME 1000
+#define NAME_OFFSET 6 /* offset for "name<num>" */
+
+/* Internal Variables */
+static int Index = 0;
+
+/* Global variables */
+int num_errs = 0;
+int Verbosity;
+
+#include <ttsafe.h>
+
+#ifndef H5_HAVE_THREADSAFE
+int main(void)
+{
+ printf("Test skipped because THREADSAFE not enabled\n");
+ return 0;
+}
+#else
+/* ANY new test needs to have a prototype in tproto.h */
+struct TestStruct {
+ int NumErrors;
+ char Description[64];
+ int SkipFlag;
+ char Name[16];
+ void (*Call) (void);
+ void (*Cleanup) (void);
+} Test[MAXNUMOFTESTS];
+
+static void InitTest(const char *TheName, void (*TheCall) (void), void (*Cleanup) (void), const char *TheDescr);
+static void usage(void);
+
+static void
+InitTest(const char *TheName, void (*TheCall) (void), void (*Cleanup) (void), const char *TheDescr)
+{
+ if (Index >= MAXNUMOFTESTS) {
+ print_func("Uh-oh, too many tests added, increase MAXNUMOFTEST!\n");
+ exit(-1);
+ } /* end if */
+ HDstrcpy(Test[Index].Description, TheDescr);
+ HDstrcpy(Test[Index].Name, TheName);
+ Test[Index].Call = TheCall;
+ Test[Index].Cleanup = Cleanup;
+ Test[Index].NumErrors = -1;
+ Test[Index].SkipFlag = 0;
+ Index++;
+}
+
+static void
+usage(void)
+{
+ intn i;
+
+ print_func("Usage: testhdf5 [-v[erbose] (l[ow]|m[edium]|h[igh]|0-10)] \n");
+ print_func(" [-[e]x[clude] name+] \n");
+ print_func(" [-o[nly] name+] \n");
+ print_func(" [-b[egin] name] \n");
+ print_func(" [-s[ummary]] \n");
+ print_func(" [-c[leanoff]] \n");
+ print_func(" [-n[ocaching]] \n");
+ print_func(" [-h[elp]] \n");
+ print_func("\n\n");
+ print_func("verbose controls the amount of information displayed\n");
+ print_func("exclude to exclude tests by name\n");
+ print_func("only to name tests which should be run\n");
+ print_func("begin start at the name of the test givin\n");
+ print_func("summary prints a summary of test results at the end\n");
+ print_func("cleanoff does not delete *.hdf files after execution of tests\n");
+ print_func("nocaching do not turn on low-level DD caching\n");
+ print_func("help print out this information\n");
+ print_func("\n\n");
+ print_func("This program currently tests the following: \n\n");
+ print_func("%16s %s\n", "Name", "Description");
+ print_func("%16s %s\n", "----", "-----------");
+ for (i = 0; i < Index; i++)
+ print_func("%16s %s\n", Test[i].Name, Test[i].Description);
+ print_func("\n\n");
+} /* end usage() */
+
+/*
+ * This routine is designed to provide equivalent functionality to 'printf'
+ * and allow easy replacement for environments which don't have stdin/stdout
+ * available. (i.e. Windows & the Mac)
+ */
+int
+print_func(const char *format,...)
+{
+ va_list arglist;
+ int ret_value;
+
+ va_start(arglist, format);
+ ret_value = vprintf(format, arglist);
+ va_end(arglist);
+ return (ret_value);
+}
+
+char* gen_name(int value) {
+ char* temp;
+ int i, length;
+
+ length = num_digits(MAX_NUM_NAME-1);
+ temp = (char *)malloc((NAME_OFFSET+length+1)*sizeof(char));
+ temp = strcpy(temp, "attrib");
+ temp[NAME_OFFSET+length] = '\0';
+
+ for (i=length-1;i>=0;i--) {
+ temp[NAME_OFFSET+i] = (char)((int)'0' + value%10);
+ value = value/10;
+ }
+
+ return temp;
+}
+
+/* pre-condition: num must be a non-negative number */
+int num_digits(int num) {
+ int i=0;
+
+ if (num == 0)
+ return 1;
+ while (num > 0) {
+ num = num/10;
+ i++;
+ }
+ return i;
+}
+
+int
+main(int argc, char *argv[])
+{
+ int CLLoop; /* Command Line Loop */
+ int Loop, Loop1;
+ int Summary = 0;
+ int CleanUp = 1;
+ int Cache = 1;
+ uintn major, minor, release;
+
+#if defined __MWERKS__
+ argc = ccommand(&argv);
+#endif
+
+#if !(defined MAC || defined __MWERKS__ || defined SYMANTEC_C)
+ /* Un-buffer the stdout and stderr */
+ setbuf(stderr, NULL);
+ setbuf(stdout, NULL);
+#endif
+
+ /*
+ * Turn off automatic error reporting since we do it ourselves. Besides,
+ * half the functions this test calls are private, so automatic error
+ * reporting wouldn't do much good since it's triggered at the API layer.
+ */
+ H5Eset_auto (NULL, NULL);
+
+ /* Tests are generally arranged from least to most complexity... */
+ InitTest("dcreate", tts_dcreate, cleanup_dcreate, "multi-dataset creation");
+ InitTest("error", tts_error, cleanup_error, "per-thread error stacks");
+ InitTest("cancel", tts_cancel, cleanup_cancel, "Thread cancellation safety test");
+ InitTest("acreate", tts_acreate, cleanup_acreate, "multi-attribute creation");
+
+ Verbosity = 4; /* Default Verbosity is Low */
+ H5get_libversion(&major, &minor, &release);
+
+ print_func("\nFor help use: testhdf5 -help\n");
+ print_func("Linked with hdf5 version %u.%u release %u\n",
+ (unsigned)major, (unsigned)minor, (unsigned)release);
+ for (CLLoop = 1; CLLoop < argc; CLLoop++) {
+ if ((argc > CLLoop + 1) && ((HDstrcmp(argv[CLLoop], "-verbose") == 0) ||
+ (HDstrcmp(argv[CLLoop], "-v") == 0))) {
+ if (argv[CLLoop + 1][0] == 'l')
+ Verbosity = 4;
+ else if (argv[CLLoop + 1][0] == 'm')
+ Verbosity = 6;
+ else if (argv[CLLoop + 1][0] == 'h')
+ Verbosity = 10;
+ else
+ Verbosity = atoi(argv[CLLoop + 1]);
+ } /* end if */
+ if ((argc > CLLoop) && ((HDstrcmp(argv[CLLoop], "-summary") == 0) ||
+ (HDstrcmp(argv[CLLoop], "-s") == 0)))
+ Summary = 1;
+
+ if ((argc > CLLoop) && ((HDstrcmp(argv[CLLoop], "-help") == 0) ||
+ (HDstrcmp(argv[CLLoop], "-h") == 0))) {
+ usage();
+ exit(0);
+ }
+ if ((argc > CLLoop) && ((HDstrcmp(argv[CLLoop], "-cleanoff") == 0) ||
+ (HDstrcmp(argv[CLLoop], "-c") == 0)))
+ CleanUp = 0;
+
+ if ((argc > CLLoop) && ((HDstrcmp(argv[CLLoop], "-nocache") == 0) ||
+ (HDstrcmp(argv[CLLoop], "-n") == 0))) {
+ Cache = 0;
+ printf ("Cache = %d\n", Cache);
+ }
+
+ if ((argc > CLLoop + 1) && ((HDstrcmp(argv[CLLoop], "-exclude") == 0) ||
+ (HDstrcmp(argv[CLLoop], "-x") == 0))) {
+ Loop = CLLoop + 1;
+ while ((Loop < argc) && (argv[Loop][0] != '-')) {
+ for (Loop1 = 0; Loop1 < Index; Loop1++)
+ if (HDstrcmp(argv[Loop], Test[Loop1].Name) == 0)
+ Test[Loop1].SkipFlag = 1;
+ Loop++;
+ } /* end while */
+ } /* end if */
+ if ((argc > CLLoop + 1) && ((HDstrcmp(argv[CLLoop], "-begin") == 0) ||
+ (HDstrcmp(argv[CLLoop], "-b") == 0))) {
+ Loop = CLLoop + 1;
+ while ((Loop < argc) && (argv[Loop][0] != '-')) {
+ for (Loop1 = 0; Loop1 < Index; Loop1++) {
+ if (HDstrcmp(argv[Loop], Test[Loop1].Name) != 0)
+ Test[Loop1].SkipFlag = 1;
+ if (HDstrcmp(argv[Loop], Test[Loop1].Name) == 0)
+ Loop1 = Index;
+ } /* end for */
+ Loop++;
+ } /* end while */
+ } /* end if */
+ if ((argc > CLLoop + 1) && ((HDstrcmp(argv[CLLoop], "-only") == 0) ||
+ (HDstrcmp(argv[CLLoop], "-o") == 0))) {
+ for (Loop = 0; Loop < Index; Loop++)
+ Test[Loop].SkipFlag = 1;
+ Loop = CLLoop + 1;
+ while ((Loop < argc) && (argv[Loop][0] != '-')) {
+ for (Loop1 = 0; Loop1 < Index; Loop1++)
+ if (HDstrcmp(argv[Loop], Test[Loop1].Name) == 0)
+ Test[Loop1].SkipFlag = 0;
+ Loop++;
+ } /* end while */
+ } /* end if */
+ } /* end for */
+
+#ifdef NOT_YET
+ if (Cache) /* turn on caching, unless we were instucted not to */
+ Hcache(CACHE_ALL_FILES, TRUE);
+#endif /* NOT_YET */
+
+ for (Loop = 0; Loop < Index; Loop++) {
+ if (Test[Loop].SkipFlag) {
+ MESSAGE(2, ("Skipping -- %s \n", Test[Loop].Description));
+ } else {
+ MESSAGE(2, ("Testing -- %s (%s) \n", Test[Loop].Description,
+ Test[Loop].Name));
+ MESSAGE(5, ("===============================================\n"));
+ Test[Loop].NumErrors = num_errs;
+ (*Test[Loop].Call) ();
+ Test[Loop].NumErrors = num_errs - Test[Loop].NumErrors;
+ MESSAGE(5, ("===============================================\n"));
+ MESSAGE(5, ("There were %d errors detected.\n\n", (int) Test[Loop].NumErrors));
+ } /* end else */
+ } /* end for */
+
+ MESSAGE(2, ("\n\n"))
+ if (num_errs)
+ print_func("!!! %d Error(s) were detected !!!\n\n", (int) num_errs);
+ else
+ print_func("All tests were successful. \n\n");
+
+ if (Summary) {
+ print_func("Summary of Test Results:\n");
+ print_func("Name of Test Errors Description of Test\n");
+ print_func("---------------- ------ --------------------------------------\n");
+
+ for (Loop = 0; Loop < Index; Loop++) {
+ if (Test[Loop].NumErrors == -1)
+ print_func("%16s %6s %s\n", Test[Loop].Name, "N/A", Test[Loop].Description);
+ else
+ print_func("%16s %6d %s\n", Test[Loop].Name, (int) Test[Loop].NumErrors,
+ Test[Loop].Description);
+ } /* end for */
+ print_func("\n\n");
+ } /* end if */
+ if (CleanUp && !getenv("HDF5_NOCLEANUP")) {
+ MESSAGE(2, ("\nCleaning Up temp files...\n\n"));
+
+ /* call individual cleanup routines in each source module */
+ for (Loop = 0; Loop < Index; Loop++)
+ if (!Test[Loop].SkipFlag && Test[Loop].Cleanup!=NULL)
+ (*Test[Loop].Cleanup) ();
+ }
+ return (num_errs);
+} /* end main() */
+#endif /*H5_HAVE_THREADSAFE*/
diff --git a/test/ttsafe.h b/test/ttsafe.h
new file mode 100644
index 0000000..2359764
--- /dev/null
+++ b/test/ttsafe.h
@@ -0,0 +1,141 @@
+/****************************************************************************
+ * NCSA HDF *
+ * Software Development Group *
+ * National Center for Supercomputing Applications *
+ * University of Illinois at Urbana-Champaign *
+ * 605 E. Springfield, Champaign IL 61820 *
+ * *
+ * For conditions of distribution and use, see the accompanying *
+ * hdf/COPYING file. *
+ * *
+ ****************************************************************************/
+
+/* $Id$ */
+
+/*
+ * This header file contains information required for testing the HDF5 library.
+ */
+
+#ifndef HDF5TEST_H
+#define HDF5TEST_H
+
+#include <hdf5.h>
+#include <string.h>
+
+/*
+ * Include required headers. This file tests internal library functions,
+ * so we include the private headers here.
+ */
+#include <H5private.h>
+#include <H5Eprivate.h>
+
+#ifdef H5_HAVE_THREADSAFE
+/* Include pthread library for threadsafe tests */
+#include <pthread.h>
+
+extern int num_errs;
+extern int Verbosity;
+
+/* Use %ld to print the value because long should cover most cases. */
+/* Used to make certain a return value _is_not_ a value */
+#define CHECK(ret, val, where) do { \
+ if (Verbosity>9) print_func(" Call to routine: %15s at line %4d " \
+ "in %s returned %ld \n", \
+ where, (int)__LINE__, __FILE__, \
+ (long)ret); \
+ if (ret == val) { \
+ print_func("*** UNEXPECTED RETURN from %s is %ld at line %4d " \
+ "in %s\n", where, (long)ret, (int)__LINE__, __FILE__); \
+ num_errs++; \
+ H5Eprint (stdout); \
+ } \
+ H5Eclear(); \
+} while(0)
+
+#define CHECK_I(ret,where) { \
+ if (Verbosity>9) { \
+ print_func(" Call to routine: %15s at line %4d in %s returned %ld\n", \
+ (where), (int)__LINE__, __FILE__, (long)(ret)); \
+ } \
+ if ((ret)<0) { \
+ print_func ("*** UNEXPECTED RETURN from %s is %ld line %4d in %s\n", \
+ (where), (long)(ret), (int)__LINE__, __FILE__); \
+ H5Eprint (stdout); \
+ num_errs++; \
+ } \
+ H5Eclear (); \
+}
+
+#define CHECK_PTR(ret,where) { \
+ if (Verbosity>9) { \
+ print_func(" Call to routine: %15s at line %4d in %s returned %p\n", \
+ (where), (int)__LINE__, __FILE__, (ret)); \
+ } \
+ if (!(ret)) { \
+ print_func ("*** UNEXPECTED RETURN from %s is NULL line %4d in %s\n", \
+ (where), (int)__LINE__, __FILE__); \
+ H5Eprint (stdout); \
+ num_errs++; \
+ } \
+ H5Eclear (); \
+}
+
+/* Used to make certain a return value _is_ a value */
+#define VERIFY(x, val, where) do { \
+ if (Verbosity>9) { \
+ print_func(" Call to routine: %15s at line %4d in %s had value " \
+ "%ld \n", where, (int)__LINE__, __FILE__, (long)x); \
+ } \
+ if (x != val) { \
+ print_func("*** UNEXPECTED VALUE from %s is %ld at line %4d " \
+ "in %s\n", where, (long)x, (int)__LINE__, __FILE__); \
+ H5Eprint (stdout); \
+ num_errs++; \
+ } \
+ H5Eclear(); \
+} while(0)
+
+/* Used to document process through a test and to check for errors */
+#define RESULT(ret,func) do { \
+ if (Verbosity>8) { \
+ print_func(" Call to routine: %15s at line %4d in %s returned " \
+ "%ld\n", func, (int)__LINE__, __FILE__, (long)ret); \
+ } \
+ if (Verbosity>9) HEprint(stdout, 0); \
+ if (ret == FAIL) { \
+ print_func("*** UNEXPECTED RETURN from %s is %ld at line %4d " \
+ "in %s\n", func, (long)ret, (int)__LINE__, __FILE__); \
+ H5Eprint (stdout); \
+ num_errs++; \
+ } \
+ H5Eclear(); \
+} while(0)
+
+/* Used to document process through a test */
+#define MESSAGE(V,A) {if (Verbosity>(V)) print_func A;}
+
+/* definitions for command strings */
+#define VERBOSITY_STR "Verbosity"
+#define SKIP_STR "Skip"
+#define TEST_STR "Test"
+#define CLEAN_STR "Cleanup"
+
+/* Prototypes for the support routines */
+int print_func(const char *,...);
+extern char* gen_name(int);
+extern int num_digits(int);
+
+/* Prototypes for the test routines */
+void tts_dcreate(void);
+void tts_error(void);
+void tts_cancel(void);
+void tts_acreate(void);
+
+/* Prototypes for the cleanup routines */
+void cleanup_dcreate(void);
+void cleanup_error(void);
+void cleanup_cancel(void);
+void cleanup_acreate(void);
+
+#endif /* H5_HAVE_THREADSAFE */
+#endif /* HDF5_TESTH */
diff --git a/test/ttsafe_acreate.c b/test/ttsafe_acreate.c
new file mode 100644
index 0000000..ca94470
--- /dev/null
+++ b/test/ttsafe_acreate.c
@@ -0,0 +1,166 @@
+/********************************************************************
+ *
+ * Testing for thread safety in H5A (dataset attribute) library
+ * operations. -- Threaded program --
+ * ------------------------------------------------------------------
+ *
+ * Plan: Attempt to break H5Acreate by making many simultaneous create
+ * calls.
+ *
+ * Claim: N calls to H5Acreate should create N attributes for a dataset
+ * if threadsafe. If some unprotected shared data exists for the
+ * dataset (eg, a count of the number of attributes in the
+ * dataset), there is a small chance that consecutive reads occur
+ * before a write to that shared variable.
+ *
+ * Created: Oct 5 1999
+ * Programmer: Chee Wai LEE
+ *
+ * Modification History
+ * --------------------
+ * May 15 2000 - incorporated into library tests (Chee Wai LEE)
+ *
+ ********************************************************************/
+
+#include "ttsafe.h"
+
+#ifndef H5_HAVE_THREADSAFE
+static int dummy; /* just to create a non-empty object file */
+#else
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#define FILE "ttsafe.h5"
+#define DATASETNAME "IntData"
+#define NUM_THREADS 16
+
+void *tts_acreate_thread(void *);
+
+typedef struct acreate_data_struct {
+ hid_t dataset;
+ hid_t datatype;
+ hid_t dataspace;
+ int current_index;
+} ttsafe_name_data_t;
+
+void tts_acreate(void) {
+
+ /* Pthread definitions
+ */
+ pthread_t threads[NUM_THREADS];
+
+ /* HDF5 data definitions
+ */
+ hid_t file, dataset;
+ hid_t dataspace, datatype;
+ hid_t attribute;
+ hsize_t dimsf[1]; /* dataset dimensions */
+
+ int data; /* data to write */
+ int buffer, ret;
+
+ int i;
+ ttsafe_name_data_t *attrib_data;
+
+ /* create a hdf5 file using H5F_ACC_TRUNC access,
+ * default file creation plist and default file
+ * access plist
+ */
+ file = H5Fcreate(FILE, H5F_ACC_TRUNC, H5P_DEFAULT, H5P_DEFAULT);
+
+ /* create a simple dataspace for the dataset
+ */
+ dimsf[0] = 1;
+ dataspace = H5Screate_simple(1,dimsf,NULL);
+
+ /* define datatype for the data using native little endian integers
+ */
+ datatype = H5Tcopy(H5T_NATIVE_INT);
+ H5Tset_order(datatype, H5T_ORDER_LE);
+
+ /* create a new dataset within the file
+ */
+ dataset = H5Dcreate(file, DATASETNAME, datatype, dataspace,
+ H5P_DEFAULT);
+
+ /* initialize data for dataset and write value to dataset
+ */
+ data = NUM_THREADS;
+ H5Dwrite(dataset, H5T_NATIVE_INT, H5S_ALL, H5S_ALL,
+ H5P_DEFAULT, &data);
+
+ /* simultaneously create a large number of attributes to be
+ associated with the dataset
+ */
+
+ for (i=0;i<NUM_THREADS;i++) {
+ attrib_data = malloc(sizeof(ttsafe_name_data_t));
+ attrib_data->dataset = dataset;
+ attrib_data->datatype = datatype;
+ attrib_data->dataspace = dataspace;
+ attrib_data->current_index = i;
+ pthread_create(&threads[i],NULL,tts_acreate_thread,attrib_data);
+ }
+
+ for (i=0;i<NUM_THREADS;i++) {
+ pthread_join(threads[i],NULL);
+ }
+
+ /* verify the correctness of the test */
+ for (i=0; i<NUM_THREADS; i++) {
+ attribute = H5Aopen_name(dataset,gen_name(i));
+ if (attribute < 0) {
+ fprintf(stderr,"unable to open appropriate attribute. Test failed!\n");
+ } else {
+ ret = H5Aread(attribute, H5T_NATIVE_INT, &buffer);
+ if ((ret < 0) || (buffer != i)) {
+ fprintf(stderr,"wrong data values. Test failed!\n");
+ }
+ H5Aclose(attribute);
+ }
+ }
+
+ /* close remaining resources
+ */
+ H5Sclose(dataspace);
+ H5Tclose(datatype);
+ H5Dclose(dataset);
+ H5Fclose(file);
+}
+
+void *tts_acreate_thread(void *client_data) {
+
+ hid_t attribute;
+ hsize_t dimsf[1]; /* dataset dimensions */
+
+ char *attribute_name;
+ int *attribute_data; /* data for attributes */
+ int i;
+
+ ttsafe_name_data_t *attrib_data = (ttsafe_name_data_t *)client_data;
+
+ /* create attribute
+ */
+ attribute_name = gen_name(attrib_data->current_index);
+ attribute = H5Acreate(attrib_data->dataset,
+ attribute_name,
+ attrib_data->datatype,
+ attrib_data->dataspace,
+ H5P_DEFAULT);
+
+ /* Write data to the attribute
+ */
+ attribute_data = malloc(sizeof(int));
+ *attribute_data = attrib_data->current_index;
+ H5Awrite(attribute,H5T_NATIVE_INT,attribute_data);
+ H5Aclose(attribute);
+
+ return NULL;
+}
+
+void cleanup_acreate(void) {
+}
+
+#endif /*H5_HAVE_THREADSAFE*/
+
diff --git a/test/ttsafe_cancel.c b/test/ttsafe_cancel.c
new file mode 100644
index 0000000..4e893e9
--- /dev/null
+++ b/test/ttsafe_cancel.c
@@ -0,0 +1,203 @@
+/********************************************************************
+ *
+ * Testing thread safety. Thread Cancellation safety
+ * -------------------------------------------------
+ *
+ * The main thread spawns a child to perform a series of dataset writes
+ * to a hdf5 file. The main thread and child thread synchronizes within
+ * a callback function called during a H5Diterate call afterwhich the
+ * main thread attempts to cancel the child thread.
+ *
+ * The cancellation should only work after the child thread has safely
+ * left the H5Diterate call.
+ *
+ * Temporary files generated:
+ * ttsafe.h5
+ *
+ * HDF5 APIs exercised in thread:
+ * H5Screate_simple, H5Tcopy, H5Tset_order, H5Dcreate, H5Dclose,
+ * H5Dwrite, H5Dread, H5Diterate, H5Tclose, H5Sclose.
+ *
+ * Created: May 15 2000
+ * Programmer: Chee Wai LEE
+ *
+ * Modification History
+ * --------------------
+ *
+ ********************************************************************/
+#include "ttsafe.h"
+
+#ifndef H5_HAVE_THREADSAFE
+static int dummy; /* just to create a non-empty object file */
+#else
+
+#define FILE "ttsafe.h5"
+#define DATASETNAME "commonname"
+
+void *tts_cancel_thread(void *);
+void tts_cancel_barrier(void);
+herr_t tts_cancel_callback(void *, hid_t, hsize_t, hssize_t *, void *);
+void cancellation_cleanup(void *);
+
+hid_t cancel_file;
+typedef struct cleanup_struct {
+ hid_t dataset;
+ hid_t datatype;
+ hid_t dataspace;
+} cancel_cleanup_t;
+
+pthread_t childthread;
+pthread_mutex_t mutex;
+pthread_cond_t cond;
+
+void tts_cancel(void) {
+
+ pthread_attr_t attribute;
+ hid_t dataset;
+
+ int buffer;
+
+ /* make thread scheduling global */
+ pthread_attr_init(&attribute);
+ pthread_attr_setscope(&attribute, PTHREAD_SCOPE_SYSTEM);
+
+ /* create a hdf5 file using H5F_ACC_TRUNC access,
+ * default file creation plist and default file
+ * access plist
+ */
+ cancel_file = H5Fcreate(FILE, H5F_ACC_TRUNC, H5P_DEFAULT, H5P_DEFAULT);
+
+ pthread_create(&childthread, &attribute, tts_cancel_thread, NULL);
+
+ tts_cancel_barrier();
+ pthread_cancel(childthread);
+
+ dataset = H5Dopen(cancel_file, DATASETNAME);
+ H5Dread(dataset, H5T_NATIVE_INT, H5S_ALL, H5S_ALL, H5P_DEFAULT,
+ &buffer);
+
+ if (buffer == 11) {
+ /* do nothing */
+ } else {
+ fprintf(stderr,
+ "operation unsuccessful with value at %d instead of 11\n",
+ buffer);
+ }
+
+ H5Dclose(dataset);
+ H5Fclose(cancel_file);
+}
+
+void *tts_cancel_thread(void *arg) {
+
+ int datavalue;
+ int *buffer;
+ hid_t dataspace, datatype, dataset;
+ hsize_t dimsf[1]; /* dataset dimensions */
+
+ cancel_cleanup_t *cleanup_structure;
+
+ /* define dataspace for dataset
+ */
+ dimsf[0] = 1;
+ dataspace = H5Screate_simple(1,dimsf,NULL);
+
+ /* define datatype for the data using native little endian integers
+ */
+ datatype = H5Tcopy(H5T_NATIVE_INT);
+ H5Tset_order(datatype, H5T_ORDER_LE);
+
+ /* create a new dataset within the file
+ */
+ dataset = H5Dcreate(cancel_file, DATASETNAME, datatype, dataspace,
+ H5P_DEFAULT);
+
+ /* If thread is cancelled, make cleanup call */
+ cleanup_structure = (cancel_cleanup_t*)malloc(sizeof(cancel_cleanup_t));
+ cleanup_structure->dataset = dataset;
+ cleanup_structure->datatype = datatype;
+ cleanup_structure->dataspace = dataspace;
+ pthread_cleanup_push(cancellation_cleanup, cleanup_structure);
+
+ datavalue = 1;
+ H5Dwrite(dataset, H5T_NATIVE_INT, H5S_ALL, H5S_ALL, H5P_DEFAULT,
+ &datavalue);
+
+ buffer = malloc(sizeof(int));
+ H5Dread(dataset, H5T_NATIVE_INT, H5S_ALL, H5S_ALL, H5P_DEFAULT,
+ buffer);
+ H5Diterate(buffer, H5T_NATIVE_INT, dataspace, tts_cancel_callback,
+ &dataset);
+
+ sleep(3);
+
+ datavalue = 100;
+ H5Dwrite(dataset, H5T_NATIVE_INT, H5S_ALL, H5S_ALL, H5P_DEFAULT,
+ &datavalue);
+ H5Dclose(dataset);
+ H5Tclose(datatype);
+ H5Sclose(dataspace);
+
+ /* required by pthreads. the argument 0 pops the stack but does not
+ execute the cleanup routine.
+ */
+ pthread_cleanup_pop(0);
+
+ return (NULL);
+}
+
+herr_t tts_cancel_callback(void *elem, hid_t type_id, hsize_t ndim,
+ hssize_t *point, void *operator_data) {
+ int value = *(int *)elem;
+ hid_t dataset = *(hid_t *)operator_data;
+
+ tts_cancel_barrier();
+ sleep(3);
+
+ if (value != 1) {
+ fprintf(stderr,"Error! Element value should be 1 and not %d\n", value);
+ return(-1);
+ }
+
+ value += 10;
+ H5Dwrite(dataset, H5T_NATIVE_INT, H5S_ALL, H5S_ALL, H5P_DEFAULT,
+ &value);
+
+ return (0);
+}
+
+/* need to perform the dataset, datatype and dataspace close that was
+ never performed because of thread cancellation
+*/
+void cancellation_cleanup(void *arg) {
+ cancel_cleanup_t *cleanup_structure = (cancel_cleanup_t *)arg;
+ H5Dclose(cleanup_structure->dataset);
+ H5Tclose(cleanup_structure->datatype);
+ H5Sclose(cleanup_structure->dataspace);
+ /* retained for debugging */
+ /* printf("cancellation noted, cleaning up ... \n"); */
+}
+
+/*
+ artificial (and specific to this test) barrier to keep track of whether
+ both the main and child threads have reached a point in the program.
+*/
+void tts_cancel_barrier() {
+
+ static int count = 2;
+
+ pthread_mutex_lock(&mutex);
+ if (count != 1) {
+ count--;
+ pthread_cond_wait(&cond, &mutex);
+ } else {
+ pthread_cond_signal(&cond);
+ }
+ pthread_mutex_unlock(&mutex);
+
+}
+
+void cleanup_cancel() {
+ H5close();
+}
+#endif /*H5_HAVE_THREADSAFE*/
diff --git a/test/ttsafe_dcreate.c b/test/ttsafe_dcreate.c
new file mode 100644
index 0000000..cc7d6e6
--- /dev/null
+++ b/test/ttsafe_dcreate.c
@@ -0,0 +1,191 @@
+/********************************************************************
+ *
+ * Testing thread safety in dataset creation in the HDF5 library
+ * -------------------------------------------------------------
+ *
+ * Set of tests to run multiple threads so that each creates a different
+ * dataset. This is likely to cause race-conditions if run in a non
+ * threadsafe environment.
+ *
+ * Temporary files generated:
+ * ttsafe.h5
+ *
+ * HDF5 APIs exercised in thread:
+ * H5Screate_simple, H5Tcopy, H5Tset_order, H5Dcreate, H5Dwrite, H5Dclose,
+ * H5Tclose, H5Sclose.
+ *
+ * Created: Apr 28 2000
+ * Programmer: Chee Wai LEE
+ *
+ * Modification History
+ * --------------------
+ *
+ ********************************************************************/
+#include "ttsafe.h"
+
+#ifndef H5_HAVE_THREADSAFE
+static int dummy; /* just to create a non-empty object file */
+#else
+
+#define FILE "ttsafe.h5"
+#define DATASETNAME_LENGTH 10
+#define NUM_THREAD 16
+
+void *tts_dcreate_creator(void *);
+
+/*
+ **********************************************************************
+ * Thread safe test - multiple dataset creation
+ **********************************************************************
+ */
+void tts_dcreate(void) {
+
+ /* Pthread definitions
+ */
+ pthread_t threads[NUM_THREAD];
+
+ /* HDF5 data definitions
+ */
+ hid_t file, dataset, datatype;
+
+ int datavalue;
+ int i;
+
+ typedef struct thread_info {
+ int id;
+ hid_t file;
+ char *dsetname;
+ } thread_info;
+
+ thread_info *thread_out;
+
+ char *dsetname[NUM_THREAD];
+ pthread_attr_t attribute;
+
+ /* set pthread attribute to perform global scheduling */
+ pthread_attr_init(&attribute);
+ pthread_attr_setscope(&attribute, PTHREAD_SCOPE_SYSTEM);
+
+ /* set individual dataset names (rather than generated the names
+ automatically)
+ */
+
+ for (i=0;i<NUM_THREAD;i++) {
+ dsetname[i] = (char *)malloc(sizeof(char)*DATASETNAME_LENGTH);
+ }
+ dsetname[0] = "zero";
+ dsetname[1] = "one";
+ dsetname[2] = "two";
+ dsetname[3] = "three";
+ dsetname[4] = "four";
+ dsetname[5] = "five";
+ dsetname[6] = "six";
+ dsetname[7] = "seven";
+ dsetname[8] = "eight";
+ dsetname[9] = "nine";
+ dsetname[10] = "ten";
+ dsetname[11] = "eleven";
+ dsetname[12] = "twelve";
+ dsetname[13] = "thirteen";
+ dsetname[14] = "fourteen";
+ dsetname[15] = "fifteen";
+
+ /* create a hdf5 file using H5F_ACC_TRUNC access,
+ * default file creation plist and default file
+ * access plist
+ */
+ file = H5Fcreate(FILE, H5F_ACC_TRUNC, H5P_DEFAULT, H5P_DEFAULT);
+
+ /* simultaneously create a large number of datasets within the file
+ */
+ for (i=0;i<NUM_THREAD;i++) {
+ thread_out = (thread_info *)malloc(sizeof(thread_info));
+ thread_out->id = i;
+ thread_out->file = file;
+ thread_out->dsetname = dsetname[i];
+ pthread_create(&threads[i],NULL,tts_dcreate_creator,thread_out);
+ }
+
+ for (i=0;i<NUM_THREAD;i++) {
+ pthread_join(threads[i],NULL);
+ }
+
+ /* compare data to see if it is written correctly
+ */
+
+ /* define datatype for the data using native little endian integers
+ */
+ datatype = H5Tcopy(H5T_NATIVE_INT);
+
+ for (i=0;i<NUM_THREAD;i++) {
+ if ((dataset = H5Dopen(file,dsetname[i])) < 0) {
+ fprintf(stderr,"Dataset name not found - test failed\n");
+ H5Fclose(file);
+ return;
+ } else {
+ H5Dread(dataset, datatype, H5S_ALL, H5S_ALL, H5P_DEFAULT, &datavalue);
+ if (datavalue != i) {
+ fprintf(stderr,
+ "Wrong value read %d for dataset name %s - test failed\n",
+ datavalue, dsetname[i]);
+ H5Dclose(dataset);
+ H5Fclose(file);
+ return;
+ }
+ H5Dclose(dataset);
+ }
+ }
+ /* close remaining resources
+ */
+ H5Fclose(file);
+
+}
+
+void *tts_dcreate_creator(void *thread_data) {
+
+ hid_t dataspace, datatype, dataset;
+ hsize_t dimsf[1]; /* dataset dimensions */
+
+ struct thread_info {
+ int id;
+ hid_t file;
+ char *dsetname;
+ } thread_in;
+
+ thread_in.dsetname = (char *)malloc(sizeof(char)*DATASETNAME_LENGTH);
+ thread_in = *((struct thread_info *)thread_data);
+
+ /* define dataspace for dataset
+ */
+ dimsf[0] = 1;
+ dataspace = H5Screate_simple(1,dimsf,NULL);
+
+ /* define datatype for the data using native little endian integers
+ */
+ datatype = H5Tcopy(H5T_NATIVE_INT);
+ H5Tset_order(datatype, H5T_ORDER_LE);
+
+ /* create a new dataset within the file
+ */
+ dataset = H5Dcreate(thread_in.file, thread_in.dsetname,
+ datatype, dataspace,
+ H5P_DEFAULT);
+
+ /* initialize data for dataset and write value to dataset
+ */
+ H5Dwrite(dataset, H5T_NATIVE_INT, H5S_ALL, H5S_ALL,
+ H5P_DEFAULT, &thread_in.id);
+
+ /* close dataset, datatype and dataspace resources
+ */
+ H5Dclose(dataset);
+ H5Tclose(datatype);
+ H5Sclose(dataspace);
+
+ return NULL;
+}
+
+void cleanup_dcreate(void) {
+ H5close();
+}
+#endif /*H5_HAVE_THREADSAFE*/
diff --git a/test/ttsafe_error.c b/test/ttsafe_error.c
new file mode 100644
index 0000000..1dafd23
--- /dev/null
+++ b/test/ttsafe_error.c
@@ -0,0 +1,179 @@
+/********************************************************************
+ *
+ * Testing thread safety. Deliberate per-thread errors to test error stack
+ * -----------------------------------------------------------------------
+ *
+ * Create 16 multiple threads to create datasets with the same name. The
+ * library should respond with 15 equivalent error stack printouts (one for
+ * each bad thread). The final hdf5 file should be a valid file with one
+ * entry.
+ *
+ * Temporary files generated:
+ * ttsafe.h5
+ *
+ * HDF5 APIs exercised in thread:
+ * H5Screate_simple, H5Tcopy, H5Tset_order, H5Dcreate, H5Dclose,
+ * H5Tclose, H5Sclose.
+ *
+ * Created: Apr 28 2000
+ * Programmer: Chee Wai LEE
+ *
+ * Modification History
+ * --------------------
+ *
+ ********************************************************************/
+#include "ttsafe.h"
+
+#ifndef H5_HAVE_THREADSAFE
+static int dummy; /* just to create a non-empty object file */
+#else
+
+#define NUM_THREAD 16
+#define FILE "ttsafe.h5"
+/* Having a common dataset name is an error */
+#define DATASETNAME "commonname"
+#define EXPECTED_ERROR_DEPTH 3
+#define WRITE_NUMBER 37
+
+herr_t error_callback(void *);
+herr_t walk_error_callback(int, H5E_error_t *, void *);
+void *tts_error_thread(void *);
+hid_t error_file;
+
+typedef struct err_num_struct {
+ int maj_num;
+ int min_num;
+} err_num_t;
+
+err_num_t expected[] = {{15, 23}, {15, 23}, {10, 32}};
+int error_flag = 0;
+int error_count = 0;
+
+pthread_mutex_t error_mutex;
+
+void tts_error(void) {
+
+ int i;
+
+ pthread_t threads[NUM_THREAD];
+ pthread_attr_t attribute;
+
+ hid_t dataset;
+ int value;
+
+ H5E_auto_t old_error_cb;
+ void *old_error_client_data;
+
+ /* set up mutex for global count of errors */
+ pthread_mutex_init(&error_mutex, NULL);
+
+ /* preserve previous error stack handler */
+ H5Eget_auto(&old_error_cb, &old_error_client_data);
+
+ /* set our own auto error stack handler */
+ H5Eset_auto(error_callback, NULL);
+
+ /* make thread scheduling global */
+ pthread_attr_init(&attribute);
+ pthread_attr_setscope(&attribute, PTHREAD_SCOPE_SYSTEM);
+
+ /* create a hdf5 file using H5F_ACC_TRUNC access,
+ * default file creation plist and default file
+ * access plist
+ */
+ error_file = H5Fcreate(FILE, H5F_ACC_TRUNC, H5P_DEFAULT, H5P_DEFAULT);
+
+ for (i=0;i<NUM_THREAD;i++) {
+ pthread_create(&threads[i], &attribute, tts_error_thread, NULL);
+ }
+
+ for (i=0;i<NUM_THREAD;i++) {
+ pthread_join(threads[i],NULL);
+ }
+
+ if (error_flag) {
+ fprintf(stderr, "Threads reporting different error values!\n");
+ }
+
+ if (error_count != NUM_THREAD - 1) {
+ fprintf(stderr, "Error: %d threads failed instead of %d\n",
+ error_count, NUM_THREAD-1);
+ }
+
+ dataset = H5Dopen(error_file, DATASETNAME);
+ H5Dread(dataset, H5T_NATIVE_INT, H5S_ALL, H5S_ALL, H5P_DEFAULT, &value);
+
+ if (value != WRITE_NUMBER) {
+ fprintf(stderr,
+ "Error: Successful thread wrote value %d instead of %d\n",
+ value, WRITE_NUMBER);
+ }
+
+ H5Dclose(dataset);
+ H5Fclose(error_file);
+
+ /* turn our error stack handler off */
+ H5Eset_auto(old_error_cb, old_error_client_data);
+}
+
+void *tts_error_thread(void *arg) {
+
+ hid_t dataspace, datatype, dataset;
+ hsize_t dimsf[1]; /* dataset dimensions */
+ int value;
+
+ /* define dataspace for dataset
+ */
+ dimsf[0] = 1;
+ dataspace = H5Screate_simple(1,dimsf,NULL);
+
+ /* define datatype for the data using native little endian integers
+ */
+ datatype = H5Tcopy(H5T_NATIVE_INT);
+ H5Tset_order(datatype, H5T_ORDER_LE);
+
+ /* create a new dataset within the file
+ */
+ dataset = H5Dcreate(error_file, DATASETNAME, datatype, dataspace,
+ H5P_DEFAULT);
+ if (dataset >= 0) { /* not an error */
+ value = WRITE_NUMBER;
+ H5Dwrite(dataset, H5T_NATIVE_INT, H5S_ALL, H5S_ALL, H5P_DEFAULT, &value);
+
+ H5Dclose(dataset);
+ }
+ H5Tclose(datatype);
+ H5Sclose(dataspace);
+ return (NULL);
+}
+
+void cleanup_error() {
+}
+
+herr_t error_callback(void *client_data) {
+ pthread_mutex_lock(&error_mutex);
+ error_count++;
+ pthread_mutex_unlock(&error_mutex);
+ return (H5Ewalk(H5E_WALK_DOWNWARD, walk_error_callback, NULL));
+}
+
+herr_t walk_error_callback(int n, H5E_error_t *err_desc,
+ void *client_data) {
+ int maj_num, min_num;
+
+ if (err_desc) {
+ maj_num = err_desc->maj_num;
+ min_num = err_desc->min_num;
+
+ if (n < EXPECTED_ERROR_DEPTH) {
+ if (maj_num == expected[n].maj_num &&
+ min_num == expected[n].min_num) {
+ return SUCCEED;
+ }
+ }
+ }
+ error_flag = -1;
+ return SUCCEED;
+}
+
+#endif /*H5_HAVE_THREADSAFE*/