unsigned long WINAPI threadMain (void *param)
#endif
{
    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 (gRunInfo.numParses == 0 || thInfo->fParses < gRunInfo.numParses) {
            thInfo->fInProgress = true;

            if (thParser == 0)
                thParser = new ThreadParser;

            docNum++;

            if (docNum >= gRunInfo.numInputFiles)
                docNum = 0;

            InFileInfo *fInfo = &gRunInfo.files[docNum];

            if (gRunInfo.verbose )
                printf("Thread #%d: parse %d starting file %s\n", thInfo->fThreadNum, thInfo->fParses, fInfo->fileName);

            int checkSum = 0;

            checkSum = thParser->parse(docNum);

            // For the case where we skip the preparse we will have nothing to
            // compare the first parse's results to ... so if this looks like first
            // parse move the checkSum back into the gRunInfo data for this file.

            if (gRunInfo.files[docNum].checkSum == 0) {
                gRunInfo.files[docNum].checkSum = checkSum;
            }
            else if (checkSum != gRunInfo.files[docNum].checkSum) {
                if (checkSum == 0) {
                    // parse returns 0 if there was an error so do this to get the real
                    // checksum value
                    checkSum = thParser->getCheckSum();
                }
                fprintf(stderr, "\nThread %d: Parse Check sum error on file  \"%s\" for parse # %d.  Expected %x,  got %x\n",
                    thInfo->fThreadNum, fInfo->fileName, thInfo->fParses, fInfo->checkSum, checkSum);

	            double totalParsesCompleted = 0;
                for (int threadNum=0; threadNum < gRunInfo.numThreads; threadNum++) {
                    totalParsesCompleted += gThreadInfo[threadNum].fParses;
                }
                fprintf(stderr, "Total number of parses completed is %f.\n", totalParsesCompleted);

                // Revisit - let the loop continue to run?
                int secondTryCheckSum = thParser->reCheck();
                fprintf(stderr, "   Retry checksum is %x\n", secondTryCheckSum);
                if (gRunInfo.dumpOnErr && gRunInfo.dom) {
                    thParser->domPrint();
                }
                fflush(stdout);
                fflush(stderr);
                clearFileInfoMemory();
                exit(-1);
            }

            if (gRunInfo.reuseParser == false) {
                delete thParser;
                thParser = 0;
            }

            thInfo->fHeartBeat = true;
            thInfo->fParses++;
            thInfo->fInProgress = false;
        }
        else {
            ThreadFuncs::Sleep(1000);
        }
    }
    delete thParser;
#ifdef HAVE_PTHREAD
	return;
}
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;
    }


    /** Grammar caching thread testing */
    // Initialize memory manger and grammar pool
    // set doInitialParse to true so that the first parse will cache the
    // grammar and it'll be used in subsequent parses
    
    if (gRunInfo.doSchema == true && gRunInfo.doNamespaces == true && gRunInfo.doGrammarCaching == true) {
        gpMemMgr = new MemoryManagerImpl();
        gp = new XMLGrammarPoolImpl(gpMemMgr);
        gRunInfo.doInitialParse = true;
    }    

    //
    // If we will be parsing from memory, read each of the input files
    //  into memory now.
    //
    ReadFilesIntoMemory();

    // Initialize checksums to zero so we can check first parse and if
    // zero then we need to move first parse's checksum into array. This
    // is for the cse where we skip the initial parse.
    for (int n = 0; n < gRunInfo.numInputFiles; n++)
    {
        gRunInfo.files[n].checkSum = 0;
    }

    if (gRunInfo.doInitialParse)
    {
    //
    // 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 occurred while initially parsing %s\n",
                    fileName);
                errors = true;
            };

            gRunInfo.files[n].checkSum = cksum;
            if (gRunInfo.verbose )
                printf("%x\n", cksum);
            if (gRunInfo.dumpOnErr && errors && gRunInfo.dom) {
                mainParser->domPrint();
            }

        }
        delete mainParser;

        if (errors) {
            fprintf(stderr, "Quitting due to error incurred during initial parse\n");
            clearFileInfoMemory();
            return 1;
        }
    }

    //
    //  Fire off the requested number of parallel threads
    //

    if (gRunInfo.numThreads == 0) {
        clearFileInfoMemory();
        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]);
    }
        
    if (gRunInfo.numParses)
    {
        bool notDone;
        while (true)
        {
            ThreadFuncs::Sleep(1000);            
            notDone = false;
           
            for (threadNum = 0; threadNum < gRunInfo.numThreads; threadNum++) {
                if (gThreadInfo[threadNum].fParses < gRunInfo.numParses)                                    
                    notDone = true;
            }
            if (notDone == false) {                
                break;
            }
        }
    }
    else
    {
        //
        //  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 = '+';                
                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.

    //
    //  Make sure all threads are done before terminate
    //
    for (threadNum=0; threadNum < gRunInfo.numThreads; threadNum++) {
        while (gThreadInfo[threadNum].fInProgress == true) {
            ThreadFuncs::Sleep(1000);
        }
        if (gRunInfo.verbose)
            printf("Thread #%d: is finished.\n", threadNum);
    }

    //
    //  We are done!   Count the number of parse and terminate the program
    //
    double totalParsesCompleted = 0;
    for (threadNum=0; threadNum < gRunInfo.numThreads; threadNum++)
    {
        totalParsesCompleted += gThreadInfo[threadNum].fParses;
        // printf("%f   ", totalParsesCompleted);
    }

    if (gRunInfo.quiet == false) {
        if (gRunInfo.numParses) {
            printf("\n%8.0f total parses were completed.\n", totalParsesCompleted);
        }
        else {
            double parsesPerMinute = totalParsesCompleted / (double(gRunInfo.totalTime) / double(60));
            printf("\n%8.2f parses per minute.\n", parsesPerMinute);
        }
    }
    
    // delete grammar pool and memory manager
    if (gp) {
        delete gp;    
        delete gpMemMgr;    
    }

    XMLPlatformUtils::Terminate();

    clearFileInfoMemory();

    delete [] gThreadInfo;

    printf("Test Run Successfully\n");

    return 0;
}