Adding a test program to check thread reentrancy
authorDaniel Veillard <veillard@redhat.com>
Fri, 14 Aug 2009 06:58:50 +0000 (08:58 +0200)
committerDaniel Veillard <veillard@redhat.com>
Fri, 14 Aug 2009 06:58:50 +0000 (08:58 +0200)
* xsltproc/testThreads.c: based loosely on libxml2 one, checks
  concurrent use of the same stylesheet and extensions reentrancy
* config.h.in configure.in: we need to check for pthreads
* Makefile.am xsltproc/Makefile.am: add the new program and insert
  in make check

Makefile.am
config.h.in
configure.in
xsltproc/Makefile.am
xsltproc/testThreads.c [new file with mode: 0644]

index 839d4e5..60bc051 100644 (file)
@@ -47,12 +47,14 @@ dummy:
 tests: dummy
        @echo '## Running the regression test suite'
        @(cd tests ; $(MAKE) MAKEFLAGS+=--silent tests)
+       @(cd xsltproc ; $(MAKE) MAKEFLAGS+=--silent tests)
        @(if [ "@PYTHON_SUBDIR@" != "" ] ; then cd python ; $(MAKE) MAKEFLAGS+=--silent tests ; fi)
 
 valgrind:
        @echo '## Running the regression tests under Valgrind'
        @echo '## Go get a cup of coffee it is gonna take a while ...'
        @(cd tests ; $(MAKE) CHECKER='valgrind -q' tests)
+       @(cd xsltproc ; $(MAKE) CHECKER='valgrind -q' tests)
 
 cleanup:
        -@(find . -name .\#\* -exec rm {} \;)
index 7b8783d..b3e2dd5 100644 (file)
@@ -42,6 +42,9 @@
 /* Define to 1 if you have the <inttypes.h> header file. */
 #undef HAVE_INTTYPES_H
 
+/* Define if pthread library is there (-lpthread) */
+#undef HAVE_LIBPTHREAD
+
 /* Define to 1 if you have the <locale.h> header file. */
 #undef HAVE_LOCALE_H
 
@@ -66,6 +69,9 @@
 /* Define to 1 if you have the `printf' function. */
 #undef HAVE_PRINTF
 
+/* Define if <pthread.h> is there */
+#undef HAVE_PTHREAD_H
+
 /* Define to 1 if you have the `snprintf' function. */
 #undef HAVE_SNPRINTF
 
 /* Define to 1 if you have the `_stat' function. */
 #undef HAVE__STAT
 
+/* Define to the sub-directory in which libtool stores uninstalled libraries.
+   */
+#undef LT_OBJDIR
+
 /* Name of package */
 #undef PACKAGE
 
 /* Define to 1 if you have the ANSI C header files. */
 #undef STDC_HEADERS
 
-/* Version number of package */
-#undef VERSION
-
+/* Enable extensions on AIX 3, Interix.  */
+#ifndef _ALL_SOURCE
+# undef _ALL_SOURCE
+#endif
 /* Enable GNU extensions on systems that have them.  */
 #ifndef _GNU_SOURCE
 # undef _GNU_SOURCE
 #endif
+/* Enable threading extensions on Solaris.  */
+#ifndef _POSIX_PTHREAD_SEMANTICS
+# undef _POSIX_PTHREAD_SEMANTICS
+#endif
+/* Enable extensions on HP NonStop.  */
+#ifndef _TANDEM_SOURCE
+# undef _TANDEM_SOURCE
+#endif
+/* Enable general extensions on Solaris.  */
+#ifndef __EXTENSIONS__
+# undef __EXTENSIONS__
+#endif
+
+
+/* Version number of package */
+#undef VERSION
+
+/* Define to 1 if on MINIX. */
+#undef _MINIX
+
+/* Define to 2 if the system does not provide POSIX.1 features except with
+   this defined. */
+#undef _POSIX_1_SOURCE
+
+/* Define to 1 if you need to in order for `stat' and other things to work. */
+#undef _POSIX_SOURCE
 
 /* Using the Win32 Socket implementation */
 #undef _WINSOCKAPI_
index 92edb86..60f189f 100644 (file)
@@ -119,6 +119,12 @@ AM_PROG_LIBTOOL
 
 AC_CHECK_HEADERS(sys/types.h sys/time.h stdlib.h unistd.h string.h)
 
+dnl Look for pthread.h, needed for testThreads
+AC_CHECK_HEADER(pthread.h,
+    AC_CHECK_LIB(pthread, pthread_join,[
+       AC_DEFINE([HAVE_LIBPTHREAD], [], [Define if pthread library is there (-lpthread)])
+       AC_DEFINE([HAVE_PTHREAD_H], [], [Define if <pthread.h> is there]) ]))
+
 dnl
 dnl Detect supported locale
 dnl
index 6555358..7eb01e4 100644 (file)
@@ -5,12 +5,19 @@ INCLUDES = -I$(top_srcdir) -I$(top_srcdir)/libxslt -I$(top_srcdir)/libexslt \
 EXTRA_PROGRAMS=
 bin_PROGRAMS = xsltproc $(XSLTPROCDV)
 
+noinst_PROGRAMS=testThreads
+
 AM_CFLAGS = $(LIBGCRYPT_CFLAGS)
 
 xsltproc_SOURCES = xsltproc.c
 xsltproc_LDFLAGS = 
 xsltproc_DEPENDENCIES = $(DEPS)
 
+testThreads_SOURCES=testThreads.c
+testThreads_LDFLAGS =
+testThreads_DEPENDENCIES = $(DEPS)
+testThreads_LDADD= $(LDADDS)
+
 DEPS = $(top_builddir)/libxslt/libxslt.la \
        $(top_builddir)/libexslt/libexslt.la 
 
@@ -24,3 +31,6 @@ xsltproc_LDADD = $(LIBGCRYPT_LIBS) $(LDADDS)
 xsltproc.dv: xsltproc.o
        $(CC) $(CFLAGS) -o xsltproc xsltproc.o ../libexslt/.libs/libexslt.a ../libxslt/.libs/libxslt.a @LIBXML_LIBS@ $(EXTRA_LIBS) $(LIBGCRYPT_LIBS)
 
+tests: testThreads
+       @echo '## Running testThreads'
+       @($(CHECKER) ./testThreads ; grep "MORY ALLO" .memdump  | grep -v "MEMORY ALLOCATED : 0" || true)
diff --git a/xsltproc/testThreads.c b/xsltproc/testThreads.c
new file mode 100644 (file)
index 0000000..6c00ef4
--- /dev/null
@@ -0,0 +1,263 @@
+/**
+ * testThreads.c: testing of heavilly multithreaded concurrent accesses
+ *
+ * See Copyright for the status of this software.
+ *
+ * daniel@veillard.com
+ */
+
+/*
+ * TODO: extend it to allow giving the stylesheets/input as filenames on the
+ *       command line to test specifics, also add exslt
+ */
+
+#include "config.h"
+#include <stdlib.h>
+#include <stdio.h>
+
+#define _REENTRANT
+#include <libxml/xmlversion.h>
+
+#if defined(LIBXML_THREAD_ENABLED) && defined(HAVE_PTHREAD_H)
+
+#include <libxml/globals.h>
+#include <libxml/threads.h>
+#include <libxml/parser.h>
+#include <libxml/catalog.h>
+#include <libxml/xpathInternals.h>
+#include <libxslt/xslt.h>
+#include <libxslt/xsltInternals.h>
+#include <libxslt/transform.h>
+#include <libxslt/xsltutils.h>
+#include <libxslt/extensions.h>
+#include <pthread.h>
+#include <string.h>
+#if !defined(_MSC_VER)
+#include <unistd.h>
+#endif
+#include <assert.h>
+
+#define        MAX_ARGC        20
+
+static pthread_t tid[MAX_ARGC];
+
+#define EXT_NS BAD_CAST "http://foo.org"
+#define EXT_DATA "bar"
+
+const char *stylesheet = "<xsl:stylesheet version='1.0' \
+xmlns:xsl='http://www.w3.org/1999/XSL/Transform' \
+xmlns:foo='http://foo.org' \
+extension-element-prefixes='foo'>\
+<xsl:template match='text()'>\
+Success <xsl:value-of select='foo:foo()'/>\
+</xsl:template>\
+</xsl:stylesheet>\
+";
+
+int init = 0;
+
+const char *doc = "<doc>Failed</doc>";
+const char *expect = "<?xml version=\"1.0\"?>\nSuccess foo\n";
+
+static void fooFunction(xmlXPathParserContextPtr ctxt,
+                        int nargs ATTRIBUTE_UNUSED) {
+    xmlXPathReturnString(ctxt, xmlStrdup(BAD_CAST "foo"));
+}
+
+static
+void * registerFooExtensions(ATTRIBUTE_UNUSED xsltTransformContextPtr ctxt,
+                             ATTRIBUTE_UNUSED const xmlChar *URI) {
+    xsltRegisterExtModuleFunction(BAD_CAST "foo", EXT_NS, fooFunction);
+    return((void *)EXT_DATA);
+}
+
+static
+void shutdownFooExtensions(xsltTransformContextPtr ctxt ATTRIBUTE_UNUSED,
+                           const xmlChar *URI, void *data) {
+    const char *str = (const char *) data;
+    if (!xmlStrEqual(URI, EXT_NS)) {
+        fprintf(stderr, "Mismatch in extensions shutdown URI");
+    }
+    if (!xmlStrEqual(BAD_CAST str, BAD_CAST EXT_DATA)) {
+        fprintf(stderr, "Mismatch in extensions shutdown DATA");
+    }
+}
+
+static void registerFooModule(void) {
+    xsltRegisterExtModule(EXT_NS, registerFooExtensions, shutdownFooExtensions);
+}
+
+static void *
+threadRoutine1(void *data)
+{
+    xmlDocPtr input;
+    xmlDocPtr style;
+    xmlDocPtr res;
+    xmlChar *result;
+    int len;
+    xsltStylesheetPtr cur;
+    int id = (int)(unsigned long) data;
+
+    input = xmlReadMemory(doc, strlen(doc), "doc.xml", NULL, 0);
+    if (input == NULL) {
+        fprintf(stderr, "Thread id %d failed to parse input\n", id);
+        exit(1);
+    }
+    style = xmlReadMemory(stylesheet, strlen(stylesheet), "doc.xsl", NULL, 0);
+    if (style == NULL) {
+        fprintf(stderr, "Thread id %d failed to parse stylesheet\n", id);
+        exit(1);
+    }
+    cur = xsltParseStylesheetDoc(style);
+    if (cur == NULL) {
+        fprintf(stderr, "Thread id %d failed to compile stylesheet\n", id);
+        exit(1);
+    }
+    res = xsltApplyStylesheet(cur, input, NULL);
+    if (res == NULL) {
+        fprintf(stderr, "Thread id %d failed to apply stylesheet\n", id);
+        exit(1);
+    }
+    if (xsltSaveResultToString(&result, &len, res, cur) < 0) {
+        fprintf(stderr, "Thread id %d failed to output result\n", id);
+        exit(1);
+    }
+    if (!xmlStrEqual(BAD_CAST expect, result)) {
+        fprintf(stderr, "Thread id %d output not conform\n", id);
+        exit(1);
+    }
+    xsltFreeStylesheet(cur);
+    xmlFreeDoc(input);
+    xmlFreeDoc(res);
+    xmlFree(result);
+    return(0);
+}
+
+static void *
+threadRoutine2(void *data)
+{
+    xmlDocPtr input;
+    xmlDocPtr res;
+    xmlChar *result;
+    int len;
+    xsltStylesheetPtr cur = (xsltStylesheetPtr) data;
+
+    if (cur == NULL) {
+        fprintf(stderr, "Thread failed to get the stylesheet\n");
+        exit(1);
+    }
+    input = xmlReadMemory(doc, strlen(doc), "doc.xml", NULL, 0);
+    if (input == NULL) {
+        fprintf(stderr, "Thread failed to parse input\n");
+        exit(1);
+    }
+    res = xsltApplyStylesheet(cur, input, NULL);
+    if (res == NULL) {
+        fprintf(stderr, "Thread failed to apply stylesheet\n");
+        exit(1);
+    }
+    if (xsltSaveResultToString(&result, &len, res, cur) < 0) {
+        fprintf(stderr, "Thread failed to output result\n");
+        exit(1);
+    }
+    if (!xmlStrEqual(BAD_CAST expect, result)) {
+        fprintf(stderr, "Thread output not conform\n");
+        exit(1);
+    }
+    xmlFreeDoc(input);
+    xmlFreeDoc(res);
+    xmlFree(result);
+    return(0);
+}
+int
+main(void)
+{
+    unsigned int i, repeat;
+    unsigned int num_threads = 8;
+    void *results[MAX_ARGC];
+    int ret;
+
+    xmlInitParser();
+
+    registerFooModule();
+
+    /*
+     * First pass each thread has its own version of the stylesheet
+     * each of them will initialize and shutdown the extension
+     */
+    for (repeat = 0;repeat < 500;repeat++) {
+       for (i = 0; i < num_threads; i++) {
+           results[i] = NULL;
+           tid[i] = (pthread_t) -1;
+       }
+
+       for (i = 0; i < num_threads; i++) {
+           ret = pthread_create(&tid[i], NULL, threadRoutine1,
+                                 (void *) (unsigned long) i);
+           if (ret != 0) {
+               perror("pthread_create");
+               exit(1);
+           }
+       }
+       for (i = 0; i < num_threads; i++) {
+           ret = pthread_join(tid[i], &results[i]);
+           if (ret != 0) {
+               perror("pthread_join");
+               exit(1);
+           }
+       }
+    }
+
+    /*
+     * Second pass all threads share the same stylesheet instance
+     * look for transformation clashes
+     */
+    for (repeat = 0;repeat < 500;repeat++) {
+        xmlDocPtr style;
+        xsltStylesheetPtr cur;
+
+        style = xmlReadMemory(stylesheet, strlen(stylesheet), "doc.xsl",
+                               NULL, 0);
+        if (style == NULL) {
+            fprintf(stderr, "Main failed to parse stylesheet\n");
+            exit(1);
+        }
+        cur = xsltParseStylesheetDoc(style);
+        if (cur == NULL) {
+            fprintf(stderr, "Main failed to compile stylesheet\n");
+            exit(1);
+        }
+       for (i = 0; i < num_threads; i++) {
+           results[i] = NULL;
+           tid[i] = (pthread_t) -1;
+       }
+
+       for (i = 0; i < num_threads; i++) {
+           ret = pthread_create(&tid[i], NULL, threadRoutine2, (void *) cur);
+           if (ret != 0) {
+               perror("pthread_create");
+               exit(1);
+           }
+       }
+       for (i = 0; i < num_threads; i++) {
+           ret = pthread_join(tid[i], &results[i]);
+           if (ret != 0) {
+               perror("pthread_join");
+               exit(1);
+           }
+       }
+        xsltFreeStylesheet(cur);
+    }
+    xsltCleanupGlobals();
+    xmlCleanupParser();
+    xmlMemoryDump();
+    return (0);
+}
+#else /* !LIBXML_THREADS_ENABLED | !HAVE_PTHREAD_H */
+int
+main(void)
+{
+    fprintf(stderr, "libxml was not compiled with thread\n");
+    return (0);
+}
+#endif