diff --git a/tests/IThreadTest/IThreadTest.cpp b/tests/IThreadTest/IThreadTest.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..f3f3bb15e03fcda3a3e4ef967c5d7f620283587a
--- /dev/null
+++ b/tests/IThreadTest/IThreadTest.cpp
@@ -0,0 +1,1032 @@
+/*
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 1999-2000 The Apache Software Foundation.  All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. The end-user documentation included with the redistribution,
+ *    if any, must include the following acknowledgment:
+ *       "This product includes software developed by the
+ *        Apache Software Foundation (http://www.apache.org/)."
+ *    Alternately, this acknowledgment may appear in the software itself,
+ *    if and wherever such third-party acknowledgments normally appear.
+ *
+ * 4. The names "Xerces" and "Apache Software Foundation" must
+ *    not be used to endorse or promote products derived from this
+ *    software without prior written permission. For written
+ *    permission, please contact apache\@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache",
+ *    nor may "Apache" appear in their name, without prior written
+ *    permission of the Apache Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation, and was
+ * originally based on software copyright (c) 1999, International
+ * Business Machines, Inc., http://www.ibm.com .  For more information
+ * on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ */
+
+/*
+* $Id$
+*/
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <parsers/SAXParser.hpp>
+#include <parsers/IDOMParser.hpp>
+#include <util/PlatformUtils.hpp>
+#include <sax/HandlerBase.hpp>
+#include <framework/MemBufInputSource.hpp>
+
+#include <idom/IDOM.hpp>
+
+
+
+//------------------------------------------------------------------------------
+//
+//   Windows specific code for starting threads
+//
+//------------------------------------------------------------------------------
+#ifdef PLATFORM_WIN32
+
+#include "Windows.h"
+#include "process.h"
+
+
+
+typedef DWORD (WINAPI *ThreadFunc)(void *);
+
+class ThreadFuncs           // This class isolates OS dependent threading
+{                           //   functions from the rest of ThreadTest program.
+public:
+    static void Sleep(int millis) {::Sleep(millis);};
+    static void startThread(ThreadFunc, void *param);
+};
+
+void ThreadFuncs::startThread(ThreadFunc func, void *param)
+{
+#ifdef noway
+    unsigned long x;
+    x = _beginthread(func, 0x10000, param);
+    if (x == -1)
+    {
+        fprintf(stderr, "Error starting thread.  Errno = %d\n", errno);
+        exit(-1);
+    }
+#endif
+    HANDLE  tHandle;
+    DWORD   threadID;   
+
+    tHandle = CreateThread(0,          // Security Attributes,
+                           0x10000,    // Stack Size,
+                           func,       // Starting Address.
+                           param,      // Parmeters
+                           0,          // Creation Flags,
+                           &threadID); // Thread ID (Can not be null on 95/98)
+
+    if (tHandle == 0)
+    {
+        fprintf(stderr, "Error starting thread.  Errno = %d\n", errno);
+        exit(-1);
+    }
+
+    // Set the priority of the working threads low, so that the UI of the running system will
+    //   remain responsive.
+    SetThreadPriority(tHandle, THREAD_PRIORITY_IDLE);
+}
+
+
+#elif defined (AIX) || defined(SOLARIS) || defined(LINUX) || defined(HPUX) || defined (OS390)
+#include <pthread.h>
+#include <unistd.h>
+#include <errno.h>
+
+
+//------------------------------------------------------------------------------
+//
+//   UNIX specific code for starting threads
+//
+//------------------------------------------------------------------------------
+
+#ifdef OS390
+extern "C" {
+#endif
+
+
+typedef void (*ThreadFunc)(void *);
+typedef void *(*pthreadfunc)(void *);
+
+class ThreadFuncs           // This class isolates OS dependent threading
+{                           //   functions from the rest of ThreadTest program.
+public:
+    static void Sleep(int millis);
+    static void startThread(ThreadFunc, void *param);
+};
+
+void ThreadFuncs::Sleep(int millis)
+{
+   int seconds = millis/1000;
+   if (seconds <= 0) seconds = 1;
+   ::sleep(seconds);
+}
+
+
+void ThreadFuncs::startThread(ThreadFunc func, void *param)
+{
+    unsigned long x;
+
+    pthread_t tId;
+    //thread_t tId;
+#if defined(_HP_UX) && defined(XML_USE_DCE)
+    x = pthread_create( &tId, pthread_attr_default,  (pthreadfunc)func,  param);
+#else
+    pthread_attr_t attr;
+    pthread_attr_init(&attr);
+    x = pthread_create( &tId, &attr,  (pthreadfunc)func,  param);
+#endif
+    if (x == -1)
+    {
+        fprintf(stderr, "Error starting thread.  Errno = %d\n", errno);
+        exit(-1);
+    }
+
+}
+#ifdef OS390
+}
+#endif
+#else
+#error This platform is not supported
+#endif
+
+
+
+//------------------------------------------------------------------------------
+//
+//  struct InFileInfo   One of these structs will be set up for each file listed
+//                      on the command line.  Once set, the data is unchanging
+//                      and can safely be referenced by the test threads without
+//                      use of synchronization.
+//
+//------------------------------------------------------------------------------
+struct InFileInfo
+{
+    char    *fileName;
+    XMLCh   *uFileName;      // When doing an in-memory parse, avoid transcoding file name
+                             //    each time through.
+    char    *fileContent;    // If doing an in-memory parse, this field points
+                             //   to an allocated string containing the entire file
+                             //   contents.  Otherwise it's 0.
+    size_t  fileSize;        // The file length.  Only initialized when doing
+                             //   an in-memory test.
+    int     checkSum;        // The XML checksum.  Set up by the main thread for
+                             //   each file before the worker threads are started.
+};
+
+//------------------------------------------------------------------------------
+//
+//  struct runInfo     Holds the info extracted from the command line.
+//                     There is only one of these, and it is static, and
+//                     unchanging once the command line has been parsed.
+//                     During the test, the threads will access this info without
+//                     any synchronization.
+//
+//------------------------------------------------------------------------------
+const int MAXINFILES = 25;
+struct RunInfo
+{
+    bool        quiet;
+    bool        verbose;
+    bool        stopNow;
+    int         numThreads;
+    bool        validating;
+    bool        dom;
+    bool        reuseParser;
+    bool        inMemory;
+    bool        dumpOnErr;
+    int         totalTime;
+    int         numInputFiles;
+    InFileInfo  files[MAXINFILES];
+};
+
+
+//------------------------------------------------------------------------------
+//
+//  struct threadInfo  Holds information specific to an individual thread.
+//                     One of these is set up for each thread in the test.
+//                     The main program monitors the threads by looking
+//                     at the status stored in these structs.
+//
+//------------------------------------------------------------------------------
+struct ThreadInfo
+{
+    bool    fHeartBeat;            // Set true by the thread each time it finishes
+                                   //   parsing a file.
+    unsigned int     fParses;      // Number of parses completed.
+    int              fThreadNum;   // Identifying number for this thread.
+    ThreadInfo() {
+        fHeartBeat = false;
+        fParses = 0;
+        fThreadNum = -1;
+    }
+};
+
+
+//
+//------------------------------------------------------------------------------
+//
+//  Global Data
+//
+//------------------------------------------------------------------------------
+RunInfo         gRunInfo;
+ThreadInfo      *gThreadInfo;
+
+
+
+//------------------------------------------------------------------------------
+//
+//  class ThreadParser   Bundles together a SAX parser and the SAX handlers
+//                       and contains the API that the rest of this test
+//                       program uses for creating parsers and doing parsing.
+//
+//                       Multiple instances of this class can operate concurrently
+//                       in different threads.
+//
+//-------------------------------------------------------------------------------
+class ThreadParser: public HandlerBase
+{
+private:
+    int             fCheckSum;
+    SAXParser*      fSAXParser;
+    IDOMParser*     fDOMParser;
+    IDOM_Document * fDoc;
+
+
+public:                               //  This is the API used by the rest of the test program
+    ThreadParser();
+    ~ThreadParser();
+
+    int parse(int fileNum);           // Parse the specified file.  fileNum is an index
+                                      //   into the gRunInfo.files array.
+                                      //  return the XML checksum, or
+                                      //  0 if a parse error occured.
+
+    int reCheck();                    // Try to compute the checksum again.
+                                      //  for DOM, re-walk the tree.
+                                      //  for SAX, can't do, just return previous value.
+
+    void domPrint(const IDOM_Node *node); // Dump out the contents of a node,
+    void domPrint();                  //   including any children.  Default (no param)
+                                      //   version dumps the entire document.
+
+private:
+    ThreadParser(const ThreadParser &); // No copy constructor
+    const ThreadParser & operator =(const ThreadParser &); // No assignment.
+
+    void  addToCheckSum(const XMLCh *chars, int len=-1);
+    void  domCheckSum(const IDOM_Node *);
+
+
+public:                               // Not really public,
+                                      //  These are the SAX call-back functions
+                                      //  that this class implements.
+    void startElement(const XMLCh* const name, AttributeList& attributes);
+    void characters(const XMLCh* const chars, const unsigned int length) {
+        addToCheckSum(chars, length);};
+    void ignorableWhitespace(const XMLCh* const chars, const unsigned int length) {
+        addToCheckSum(chars, length);};
+    void resetDocument() {};
+
+    void warning(const SAXParseException& exception)     {
+        fprintf(stderr, "*** Warning ");
+        throw;};
+
+    void error(const SAXParseException& exception)       {
+        fprintf(stderr, "*** Error ");
+        throw;};
+
+    void fatalError(const SAXParseException& exception)  {
+        fprintf(stderr, "***** Fatal error ");
+        throw;};
+};
+
+
+
+//
+//  ThreadParser constructor.  Invoked by the threads of the test program
+//                              to create parsers.
+//
+ThreadParser::ThreadParser()
+{
+    fSAXParser = 0;
+    fDOMParser = 0;
+    fDoc       = 0;
+    if (gRunInfo.dom) {
+        // Set up to use a DOM parser
+        fDOMParser = new IDOMParser;
+        fDOMParser->setDoValidation(gRunInfo.validating);
+        fDOMParser->setErrorHandler(this);
+    }
+    else
+    {
+        // Set up to use a SAX parser.
+        fSAXParser = new SAXParser;
+        fSAXParser->setDoValidation(gRunInfo.validating);
+        fSAXParser->setDocumentHandler(this);
+        fSAXParser->setErrorHandler(this);
+    }
+
+}
+
+
+
+ThreadParser::~ThreadParser()
+{
+    delete fDoc;
+    delete fSAXParser;
+    delete fDOMParser;
+}
+
+
+
+
+//------------------------------------------------------------------------
+//
+//  parse   - This is the method that is invoked by the rest of
+//            the test program to actually parse an XML file.
+//
+//------------------------------------------------------------------------
+int ThreadParser::parse(int fileNum)
+{
+    MemBufInputSource *mbis = 0;
+    InFileInfo        *fInfo = &gRunInfo.files[fileNum];
+    delete fDoc;
+    fDoc = 0;
+
+    fCheckSum = 0;
+
+    if (gRunInfo.inMemory) {
+        mbis = new  MemBufInputSource((const XMLByte *) fInfo->fileContent,
+                                       fInfo->fileSize,
+                                       fInfo->uFileName,
+                                       false);
+    }
+
+    try
+    {
+        if (gRunInfo.dom) {
+            // Do a DOM parse
+            if (gRunInfo.inMemory)
+                fDOMParser->parse(*mbis);
+            else
+                fDOMParser->parse(fInfo->fileName);
+            fDoc = fDOMParser->getDocument();
+            domCheckSum(fDoc);
+        }
+        else
+        {
+            // Do a SAX parse
+            if (gRunInfo.inMemory)
+                fSAXParser->parse(*mbis);
+            else
+                fSAXParser->parse(fInfo->fileName);
+        }
+    }
+
+    catch (const XMLException& e)
+    {
+        char *exceptionMessage = XMLString::transcode(e.getMessage());
+        fprintf(stderr, " during parsing: %s \n Exception message is: %s \n",
+            fInfo->fileName, exceptionMessage);
+        delete exceptionMessage;
+    }
+
+    delete mbis;
+    return fCheckSum;
+}
+
+
+//
+//  addToCheckSum - private function, used within ThreadParser in
+//                  computing the checksum of the XML file.
+//
+//                  Unichar Strings to be added to the checksum
+//                  can either be null terminated (omit len param, which
+//                  will then default to -1), or provide an explicit
+//                  length.
+//
+void ThreadParser::addToCheckSum(const XMLCh *chars, int len)
+{
+    if (len = -1)
+    {
+        // Null terminated string.
+        while (*chars != 0)
+        {
+            fCheckSum = fCheckSum*5 + *chars;
+            chars++;
+        }
+    }
+    else
+    {
+        // String with character count.
+        int i;
+        for (i=0; i<len; i++)
+            fCheckSum = fCheckSum*5 + chars[i];
+    }
+}
+
+
+//
+// startElement - our SAX handler callback function for element starts.
+//                update the document checksum with the element name
+//                and any attribute names and values.
+//
+void ThreadParser::startElement(const XMLCh *const name, AttributeList &attributes)
+{
+    addToCheckSum(name);
+
+    int n = attributes.getLength();
+    int i;
+    for (i=0; i<n; i++)
+    {
+        const XMLCh *attNam = attributes.getName(i);
+        addToCheckSum(attNam);
+        const XMLCh *attVal = attributes.getValue(i);
+        addToCheckSum(attVal);
+    }
+}
+
+
+//
+// domCheckSum  -  Compute the check sum for a DOM node.
+//                 Works recursively - initially called with a document node.
+//
+void ThreadParser::domCheckSum(const IDOM_Node *node)
+{
+    const XMLCh        *s;
+    IDOM_Node          *child;
+    IDOM_NamedNodeMap  *attributes;
+
+    switch (node->getNodeType() )
+    {
+    case IDOM_Node::ELEMENT_NODE:
+        {
+            s = node->getNodeName();   // the element name
+
+            attributes = node->getAttributes();  // Element's attributes
+            int numAttributes = attributes->getLength();
+            int i;
+            for (i=0; i<numAttributes; i++)
+                domCheckSum(attributes->item(i));
+
+            addToCheckSum(s);          // Content and Children
+            for (child=node->getFirstChild(); child!=0; child=child->getNextSibling())
+                domCheckSum(child);
+
+            break;
+        }
+
+
+    case IDOM_Node::ATTRIBUTE_NODE:
+        {
+            s = node->getNodeName();  // The attribute name
+            addToCheckSum(s);
+            s = node->getNodeValue();  // The attribute value
+            if (s != 0)
+                addToCheckSum(s);
+            break;
+        }
+
+
+    case IDOM_Node::TEXT_NODE:
+    case IDOM_Node::CDATA_SECTION_NODE:
+        {
+            s = node->getNodeValue();
+            addToCheckSum(s);
+            break;
+        }
+
+    case IDOM_Node::ENTITY_REFERENCE_NODE:
+    case IDOM_Node::DOCUMENT_NODE:
+        {
+            // For entity references and the document, nothing is dirctly
+            //  added to the checksum, but we do want to process the chidren nodes.
+            //
+            for (child=node->getFirstChild(); child!=0; child=child->getNextSibling())
+                domCheckSum(child);
+            break;
+        }
+    }
+}
+
+
+//
+// Recompute the checksum.  Meaningful only for DOM, will tell us whether
+//  a failure is transient, or whether the DOM data is permanently corrupted.
+//
+int ThreadParser::reCheck()
+{
+    if (gRunInfo.dom) {
+        fCheckSum = 0;
+        domCheckSum(fDoc);
+    }
+    return fCheckSum;
+}
+
+//
+// domPrint  -  Dump the contents of a DOM node.
+//              For debugging failures, when all else fails.
+//                 Works recursively - initially called with a document node.
+//
+void ThreadParser::domPrint()
+{
+    printf("Begin DOMPrint ...\n");
+    if (gRunInfo.dom)
+        domPrint(fDOMParser->getDocument());
+    printf("End DOMPrint\n");
+}
+
+
+static void printString(const XMLCh *str)
+{
+    char *s = XMLString::transcode(str);
+    printf("%s", s);
+    delete s;
+}
+
+
+void ThreadParser::domPrint(const IDOM_Node *node)
+{
+
+    IDOM_Node          *child;
+    IDOM_NamedNodeMap  *attributes;
+
+    switch (node->getNodeType() )
+    {
+    case IDOM_Node::ELEMENT_NODE:
+        {
+            printf("<");
+            printString(node->getNodeName());   // the element name
+
+            attributes = node->getAttributes();  // Element's attributes
+            int numAttributes = attributes->getLength();
+            int i;
+            for (i=0; i<numAttributes; i++) {
+                domPrint(attributes->item(i));
+            }
+            printf(">");
+
+            for (child=node->getFirstChild(); child!=0; child=child->getNextSibling())
+                domPrint(child);
+
+            printf("</");
+            printString(node->getNodeName());
+            printf(">");
+            break;
+        }
+
+
+    case IDOM_Node::ATTRIBUTE_NODE:
+        {
+            printf(" ");
+            printString(node->getNodeName());   // The attribute name
+            printf("= \"");
+            printString(node->getNodeValue());  // The attribute value
+            printf("\"");
+            break;
+        }
+
+
+    case IDOM_Node::TEXT_NODE:
+    case IDOM_Node::CDATA_SECTION_NODE:
+        {
+            printString(node->getNodeValue());
+            break;
+        }
+
+    case IDOM_Node::ENTITY_REFERENCE_NODE:
+    case IDOM_Node::DOCUMENT_NODE:
+        {
+            // For entity references and the document, nothing is dirctly
+            //  printed, but we do want to process the chidren nodes.
+            //
+            for (child=node->getFirstChild(); child!=0; child=child->getNextSibling())
+                domPrint(child);
+            break;
+        }
+    }
+}
+
+
+
+
+//----------------------------------------------------------------------
+//
+//   parseCommandLine   Read through the command line, and save all
+//                      of the options in the gRunInfo struct.
+//
+//                      Display the usage message if the command line
+//                      is no good.
+//
+//                      Probably ought to be a member function of RunInfo.
+//
+//----------------------------------------------------------------------
+
+void parseCommandLine(int argc, char **argv)
+{
+    gRunInfo.quiet = false;               // Set up defaults for run.
+    gRunInfo.verbose = false;
+    gRunInfo.numThreads = 2;
+    gRunInfo.validating = false;
+    gRunInfo.dom = false;
+    gRunInfo.reuseParser = false;
+    gRunInfo.inMemory = false;
+    gRunInfo.dumpOnErr = false;
+    gRunInfo.totalTime = 0;
+    gRunInfo.numInputFiles = 0;
+
+    try             // Use exceptions for command line syntax errors.
+    {
+        int argnum = 1;
+        while (argnum < argc)
+        {
+            if (strcmp(argv[argnum], "-quiet") == 0)
+                gRunInfo.quiet = true;
+            else if (strcmp(argv[argnum], "-verbose") == 0)
+                gRunInfo.verbose = true;
+            else if (strcmp(argv[argnum], "-v") == 0)
+                gRunInfo.validating = true;
+            else if (strcmp(argv[argnum], "-dom") == 0)
+                gRunInfo.dom = true;
+            else if (strcmp(argv[argnum], "-reuse") == 0)
+                gRunInfo.reuseParser = true;
+            else if (strcmp(argv[argnum], "-dump") == 0)
+                gRunInfo.dumpOnErr = true;
+            else if (strcmp(argv[argnum], "-mem") == 0)
+                gRunInfo.inMemory = true;
+            else if (strcmp(argv[argnum], "-threads") == 0)
+            {
+                ++argnum;
+                if (argnum >= argc)
+                    throw 1;
+                gRunInfo.numThreads = atoi(argv[argnum]);
+                if (gRunInfo.numThreads < 0)
+                    throw 1;
+            }
+            else if (strcmp(argv[argnum], "-time") == 0)
+            {
+                ++argnum;
+                if (argnum >= argc)
+                    throw 1;
+                gRunInfo.totalTime = atoi(argv[argnum]);
+                if (gRunInfo.numThreads < 1)
+                    throw 1;
+            }
+            else  if (argv[argnum][0] == '-')
+            {
+                fprintf(stderr, "Unrecognized command line option.  Scanning \"%s\"\n",
+                    argv[argnum]);
+                throw 1;
+            }
+            else
+            {
+                gRunInfo.numInputFiles++;
+                if (gRunInfo.numInputFiles >= MAXINFILES)
+                {
+                    fprintf(stderr, "Too many input files.  Limit is %d\n", MAXINFILES);
+                    throw 1;
+                }
+                gRunInfo.files[gRunInfo.numInputFiles-1].fileName = argv[argnum];
+            }
+            argnum++;
+        }
+
+        // We've made it through the command line.
+        //  Verify that at least one input file to be parsed was specified.
+        if (gRunInfo.numInputFiles == 0)
+        {
+            fprintf(stderr, "No input XML file specified on command line.\n");
+            throw 1;
+        };
+
+
+    }
+    catch (int)
+    {
+        fprintf(stderr, "usage:  threadtest [-v] [-threads nnn] [-time nnn] [-quiet] [-verbose] xmlfile...\n"
+            "     -v             Use validating parser.  Non-validating is default. \n"
+            "     -dom           Use a DOM parser.  Default is SAX. \n"
+            "     -quiet         Suppress periodic status display. \n"
+            "     -verbose       Display extra messages. \n"
+            "     -reuse         Retain and reuse parser.  Default creates new for each parse.\n"
+            "     -threads nnn   Number of threads.  Default is 2. \n"
+            "     -time nnn      Total time to run, in seconds.  Default is forever.\n"
+            "     -dump          Dump DOM tree on error.\n"
+            "     -mem           Read files into memory once only, and parse them from there.\n"
+            );
+        exit(1);
+    }
+}
+
+
+//---------------------------------------------------------------------------
+//
+//   ReadFilesIntoMemory   For use when parsing from memory rather than
+//                          reading the files each time, here is the code that
+//                          reads the files into local memory buffers.
+//
+//                          This function is only called once, from the main
+//                          thread, before all of the worker threads are started.
+//
+//---------------------------------------------------------------------------
+void ReadFilesIntoMemory()
+{
+    int     fileNum;
+    FILE    *fileF;
+    size_t  t;
+
+    if (gRunInfo.inMemory)
+    {
+        for (fileNum = 0; fileNum <gRunInfo.numInputFiles; fileNum++)
+        {
+            InFileInfo *fInfo = &gRunInfo.files[fileNum];
+            fInfo->uFileName = XMLString::transcode(fInfo->fileName);
+            fileF = fopen( fInfo->fileName, "rb" );
+            if (fileF == 0) {
+                fprintf(stderr, "Can not open file \"%s\".\n", fInfo->fileName);
+                exit(-1);
+            }
+            fseek(fileF, 0, SEEK_END);
+            fInfo->fileSize = ftell(fileF);
+            fseek(fileF, 0, SEEK_SET);
+            fInfo->fileContent = new char[fInfo->fileSize + 1];
+            t = fread(fInfo->fileContent, 1, fInfo->fileSize, fileF);
+            if (t != fInfo->fileSize) {
+                fprintf(stderr, "Error reading file \"%s\".\n", fInfo->fileName);
+                exit(-1);
+            }
+            fclose(fileF);
+            fInfo->fileContent[fInfo->fileSize] = 0;
+        }
+    }
+}
+
+
+
+//----------------------------------------------------------------------
+//
+//  threadMain   The main function for each of the swarm of test threads.
+//               Run in an infinite loop, parsing each of the documents
+//               given on the command line in turn.
+//
+//----------------------------------------------------------------------
+
+#ifdef OS390
+extern "C" {
+#endif
+
+unsigned long WINAPI threadMain (void *param)
+{
+    ThreadInfo   *thInfo = (ThreadInfo *)param;
+    ThreadParser *thParser = 0;
+
+    if (gRunInfo.verbose)
+        printf("Thread #%d: starting\n", thInfo->fThreadNum);
+
+    int docNum = gRunInfo.numInputFiles;
+
+    //
+    // Each time through this loop, one file will be parsed and its checksum
+    // computed and compared with the precomputed value for that file.
+    //
+    while (gRunInfo.stopNow == false)
+    {
+
+        if (thParser == 0)
+            thParser = new ThreadParser;
+
+        docNum++;
+
+        if (docNum >= gRunInfo.numInputFiles)
+            docNum = 0;
+
+        InFileInfo *fInfo = &gRunInfo.files[docNum];
+
+        if (gRunInfo.verbose )
+            printf("Thread #%d: starting file %s\n", thInfo->fThreadNum, fInfo->fileName);
+
+
+        int checkSum = 0;
+        checkSum = thParser->parse(docNum);
+
+        if (checkSum != gRunInfo.files[docNum].checkSum)
+        {
+            fprintf(stderr, "\nThread %d: Parse Check sum error on file  \"%s\".  Expected %x,  got %x\n",
+                thInfo->fThreadNum, fInfo->fileName, fInfo->checkSum, checkSum);
+
+            // Revisit - let the loop continue to run?
+            int secondTryCheckSum = thParser->reCheck();
+            fprintf(stderr, "   Retry checksum is %x\n", secondTryCheckSum);
+            if (gRunInfo.dumpOnErr)
+                thParser->domPrint();
+            fflush(stdout);
+            exit(-1);
+        }
+
+        if (gRunInfo.reuseParser == false)
+        {
+            delete thParser;
+            thParser = 0;
+        }
+
+        thInfo->fHeartBeat = true;
+        thInfo->fParses++;
+    }
+
+    delete thParser;
+    return 0;
+}
+
+#ifdef OS390
+}
+#endif
+
+
+
+
+//----------------------------------------------------------------------
+//
+//   main
+//
+//----------------------------------------------------------------------
+
+int main (int argc, char **argv)
+{
+
+
+    parseCommandLine(argc, argv);
+
+    //
+    // Initialize the XML system.
+    //
+    try
+    {
+         XMLPlatformUtils::Initialize();
+    }
+    catch (...)
+    {
+        fprintf(stderr, "Exception from XMLPlatfromUtils::Initialize.\n");
+        return 1;
+    }
+
+
+    //
+    // If we will be parsing from memory, read each of the input files
+    //  into memory now.
+    //
+    ReadFilesIntoMemory();
+
+
+    //
+    // While we are still single threaded, parse each of the documents
+    //  once, to check for errors, and to note the checksum.
+    // Blow off the rest of the test if there are errors.
+    //
+    ThreadParser *mainParser = new ThreadParser;
+    int     n;
+    bool    errors = false;
+    int     cksum;
+
+
+    for (n = 0; n < gRunInfo.numInputFiles; n++)
+    {
+        char *fileName = gRunInfo.files[n].fileName;
+        if (gRunInfo.verbose)
+            printf("%s checksum is ", fileName);
+
+        cksum = mainParser->parse(n);
+
+        if (cksum == 0)
+        {
+            fprintf(stderr, "An error occured while initially parsing %s\n",
+                fileName);
+            errors = true;
+        };
+
+        gRunInfo.files[n].checkSum = cksum;
+        if (gRunInfo.verbose )
+            printf("%x\n", cksum);
+        if (gRunInfo.dumpOnErr && errors)
+            mainParser->domPrint();
+
+        delete mainParser;
+    }
+    if (errors)
+        exit(1);
+
+    //
+    //  Fire off the requested number of parallel threads
+    //
+
+    if (gRunInfo.numThreads == 0)
+        exit(0);
+
+    gThreadInfo = new ThreadInfo[gRunInfo.numThreads];
+
+    int threadNum;
+    for (threadNum=0; threadNum < gRunInfo.numThreads; threadNum++)
+    {
+        gThreadInfo[threadNum].fThreadNum = threadNum;
+        ThreadFuncs::startThread(threadMain, &gThreadInfo[threadNum]);
+    }
+
+    //
+    //  Loop, watching the heartbeat of the worker threads.
+    //    Each second, display "+" when all threads have completed a parse
+    //                 display "." if some thread hasn't since previous "+"
+    //
+
+    unsigned long startTime = XMLPlatformUtils::getCurrentMillis();
+    int elapsedSeconds = 0;
+    while (gRunInfo.totalTime == 0 || gRunInfo.totalTime > elapsedSeconds)
+    {
+        ThreadFuncs::Sleep(1000);
+        if (gRunInfo.quiet == false && gRunInfo.verbose == false)
+        {
+            char c = '+';
+            int threadNum;
+            for (threadNum=0; threadNum < gRunInfo.numThreads; threadNum++)
+            {
+                if (gThreadInfo[threadNum].fHeartBeat == false)
+                {
+                    c = '.';
+                    break;
+                };
+            }
+            fputc(c, stdout);
+            fflush(stdout);
+            if (c == '+')
+                for (threadNum=0; threadNum < gRunInfo.numThreads; threadNum++)
+                    gThreadInfo[threadNum].fHeartBeat = false;
+        }
+        elapsedSeconds = (XMLPlatformUtils::getCurrentMillis() - startTime) / 1000;
+    };
+
+    //
+    //  Time's up, we are done.  (We only get here if this was a timed run)
+    //  Tally up the total number of parses completed by each of the threads.
+    //
+    gRunInfo.stopNow = true;      // set flag, which will cause worker threads to stop.
+
+    double totalParsesCompleted = 0;
+    for (threadNum=0; threadNum < gRunInfo.numThreads; threadNum++)
+    {
+        totalParsesCompleted += gThreadInfo[threadNum].fParses;
+        // printf("%f   ", totalParsesCompleted);
+    }
+
+    double parsesPerMinute = totalParsesCompleted / (double(gRunInfo.totalTime) / double(60));
+    printf("\n%8.1f parses per minute.", parsesPerMinute);
+
+    //  The threads are still running; we just return
+    //   and leave it to the operating sytem to kill them.
+    //
+    ThreadFuncs::Sleep(2000);   // Give the worker threads a chance to finish.
+                                // This is mainly to give us a clean shutdown for leak testing.
+
+    XMLPlatformUtils::Terminate();   // To Do - all hell will probably break loose if some
+                                     //    threads are still going.
+
+    delete gThreadInfo;
+    return 0;
+}
+
+
diff --git a/tests/IThreadTest/Makefile.in b/tests/IThreadTest/Makefile.in
new file mode 100644
index 0000000000000000000000000000000000000000..957a2004daccf62501690fb1adeca5f90870dc11
--- /dev/null
+++ b/tests/IThreadTest/Makefile.in
@@ -0,0 +1,115 @@
+#
+# The Apache Software License, Version 1.1
+# 
+# Copyright (c) 1999-2000 The Apache Software Foundation.  All rights
+# reserved.
+# 
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 
+# 1. Redistributions of source code must retain the above copyright
+#    notice, this list of conditions and the following disclaimer. 
+# 
+# 2. Redistributions in binary form must reproduce the above copyright
+#    notice, this list of conditions and the following disclaimer in
+#    the documentation and/or other materials provided with the
+#    distribution.
+# 
+# 3. The end-user documentation included with the redistribution,
+#    if any, must include the following acknowledgment:  
+#       "This product includes software developed by the
+#        Apache Software Foundation (http://www.apache.org/)."
+#    Alternately, this acknowledgment may appear in the software itself,
+#    if and wherever such third-party acknowledgments normally appear.
+# 
+# 4. The names "Xerces" and "Apache Software Foundation" must
+#    not be used to endorse or promote products derived from this
+#    software without prior written permission. For written 
+#    permission, please contact apache\@apache.org.
+# 
+# 5. Products derived from this software may not be called "Apache",
+#    nor may "Apache" appear in their name, without prior written
+#    permission of the Apache Software Foundation.
+# 
+# THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+# WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+# ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+# OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+# ====================================================================
+# 
+# This software consists of voluntary contributions made by many
+# individuals on behalf of the Apache Software Foundation, and was
+# originally based on software copyright (c) 1999, International
+# Business Machines, Inc., http://www.ibm.com .  For more information
+# on the Apache Software Foundation, please see
+# <http://www.apache.org/>.
+# 
+#
+# $Id$
+#
+
+###################################################################
+#                    IMPORTANT NOTE                               #
+###################################################################
+# If you are going to do the OS390BATCH build, make sure you have #
+# the OS390BATCH environment variable set.                        #
+#                                                                 #
+#   export OS390BATCH=1                                           #
+#                                                                 #
+###################################################################
+
+PLATFORM = @platform@
+COMPILER = @compiler@
+CXXFLAGS = @cxxflags@
+CFLAGS = @cflags@
+PREFIX = @prefix@
+OSVER = @osver@
+LIBS = @libs@
+CC = @compiler@
+
+include ../../version.incl
+include ../Makefile.incl
+
+APP_NAME=ThreadTest
+APP_DIR=ThreadTest
+
+OUTDIR= ${XERCESCROOT}/tests/${APP_DIR}
+EXEC=	${XERCESCROOT}/bin
+OBJS=	${OUTDIR}/ThreadTest.o
+SRC=	${XERCESCROOT}/tests/${APP_DIR}
+HEADER_FILES=
+
+## OS390BATCH
+ifeq (${OS390BATCH},1)
+BATCH_TARGET= "//'${LOADMOD}(THREDTST)'"
+all: makedir ${BATCH_TARGET}
+else
+all: makedir ${EXEC}/${APP_NAME}
+endif
+
+makedir:
+	-mkdir -p $(OUTDIR)
+
+${EXEC}/${APP_NAME}: ${OBJS}
+	${LINK} ${PLATFORM_LIB_LINK_OPTIONS} ${OBJS} -o $@ ${LIBRARY_SEARCH_PATHS} ${LIBRARY_NAMES} ${EXTRA_LINK_OPTIONS} ${LIBS}
+
+${BATCH_TARGET}: ${OBJS}
+	${LINK} ${PLATFORM_LIB_LINK_OPTIONS} ${OBJS} -o $@ ${LIBRARY_SEARCH_PATHS} ${LIBRARY_NAMES} ${EXTRA_LINK_OPTIONS} 
+
+$(OUTDIR)/ThreadTest.o: ${SRC}/ThreadTest.cpp ${HEADER_FILES}
+	${CC} ${CMP} $(INCLUDES) -o $(OUTDIR)/ThreadTest.o ${SRC}/ThreadTest.cpp
+
+clean:
+	rm -f ${OBJS} ${EXEC}/${APP_NAME}
+
+distclean:	clean
+	rm -f Makefile