Continuing to experiment with Sleep(23) to see where it would excercise a positive effect if not crashing immediately at terminating the second instance on another thread:

 * placed "Sleep()" in "Activity *ActivityManager.cpp::getRootActivity()"

     o doing a "Sleep(23);" before the statement "Activity *oldActivity = 
findActivity();":
         + crashes at terminating the second instance a couple of times, but 
eventually it ran to
           the end, creating 5,000 instances and terminating each on a separate 
thread

     o moving the "Sleep(23);" before the statement "Activity *activityObject =
       createCurrentActivity();":
         + same as above, a crash terminating the second instance on a separate 
thread, re-running
           the program in the same session may yield success in creating and 
terminating 5,000
           instances

 * placed "Sleep()" in "Activity *ActivityManager::createCurrentActivity()"

     o doing a "Sleep(23);" before the statement "Activity *activity = new 
Activity(p, false);":
         + same as above, crashes at terminating the second instance a couple 
of times, but
           eventually it ran to the end, creating 5,000 instances and 
terminating each on a
           separate thread

     o moving the "Sleep(23);" before the statement 
"allActivities->append(activity);":
         + crashed once terminating the second instance on a separate thread, 
re-running the
           compiled program in the same session yields success in creating and 
terminating all
           5,000 instances, running it on new sessions succeeds!

So the Sleep(23) has anĀ  effect while appending the new activity to the allActivities queue in createCurrentActivity(), as if concurrent access to the allActivities queue (a QueueClass) may somehow cause a problem (the test program creates 5,000 interpreter instances and uses them on the main thread to output a dot to stdout and immediately after using it, the instance gets terminated on a separate thread).

Since placing the Sleep(23) right in front of allActivities->append(activity) in createCurrentActivity(), the test program seems to succeed when run in a separate, new session repeatedly.

Any ideas, suggestions?

---rony

P.S.: Enclosed the test program as used for these tests: "nmake Makefile2.windows" and "testTerminate2.exe 1".


6:12


On 05.08.2025 12:51, Rony G. Flatscher wrote:

It seems that the crash can be delayed by a couple of thousand Rexx interpreter instances if adding (Windows) "Sleep(20);" right before "rootActivity = ActivityManager::getRootActivity();" in Interpreter.cpp's "InterpreterInstance *Interpreter::createInterpreterInstance(RexxOption *options)".

Although this is a huge improvement to crashing after three interpreter instance creations after having terminated one interpreter instance on another thread, it does not solve the problem. So a real fix is needed (the problem may occur on any host application that wishes to employ ooRexx scripts as macros, potentially creating and terminating interpreter instances on different threads, and a crashing ooRexx would crash the entire process and hence the host application, which is not acceptable for companies to employ ooRexx as a scripting language for their applications, which would be really a boon for many).

Ultimately, it seems, the crash is linked to invoking "Activity *ActivityManager::createCurrentActivity()" in ActivityManager.cpp. createCurrentActivity() gets invoked via createInterpreterInstance()'s invocation of getRootActivity(). The root cause for the crash seems to be that the interpreter instance gets created sometimes on the wrong Actvity/thread. Not sure why doing a Sleep(20) before invoking getRootActivity() has such a dramatic positive effect.

[The other path to invoking createCurrentActivity() is via "Activity *ActivityManager::attachThread()". Note, the test program does not cause attachThread() to be invoked as it immediately uses the created interpreter instance to create and run a routine and then terminates it on a separate thread.]

Not sure how to proceed. Any ideas, suggestions?

---rony


On 30.07.2025 16:50, Rony G. Flatscher wrote:

Using this e-mail thread to report new insights.

Prolog: first of all, it is a little bit bewitched: sometimes it works, sometimes it doesn't. Then, after many, many hours of all sort of debuggings I arrived at a situation where I could reproduce the crash. And then, after further inspections and debug output edits, the crashes would disappear and it took me again hours to become able to get to a version that would crash in a certain constellation. Because of this I do not dare to edit (make it better readable) the source of the test program as that caused the vanishment of the crash.

So enclosed you will find the patch "getInterpreterInfosWithIds.diff" that produces debug information for interpreter instance creations and terminations. The enclosed program "testTerminate2.cpp" can be compiled with "nmake Makefile2.windows" and produces "testTerminate2.exe".

What is the purpose of testTerminate2.cpp?

  * It creates an interpreter instance and uses it to display the ooRexx 
version and then
    terminates it, followed by a loop which gets carried out five times in 
which a Rexx
    interpreter instance gets created, and a little Rexx program
    ("tid=.context~thread;.error~charOut('<{tid='tid'}>');") gets executed on 
the main thread.

  * Depending on the command line arguments the termination of each Rexx 
interpreter instance in
    the loop occurs:
      o (case 1) on the main thread, if no arguments are supplied: this works 
flawlessly

      o on a separate thread, if one or two arguments are supplied: this may 
crash or succeed, if

          + (case 2) one argument gets supplied: this crashes in "bool
            InterpreterInstance::terminate()" after invoking the statement:
            "memoryObject.collectAndUninit(Interpreter::lastInstance());"

          + (case 3) two arguments get supplied: this works MOST of the time, 
and if it crashes
            it does so as in (case 2) above

With the enclosed version I have not yet managed to crash (case 3) which seems to be just a matter of time/luck.

However with (case 2) I get the reported crash. The difference between (case 2) and (case 3), which both terminate the Rexx interpreter instance on a separate thread, is "harmless", as the only difference in (case 3) is that it carries out a "fflush(stderr)" before terminating the instance (not sure why that has such a significant effect in this case).

Here the output of running the three cases:

  * (case 1) output:
    G:\tmp\orx\bugs\crashOnTerminate20250729>rxenv testTerminate2.exe
    *** main() - entered testTerminate2.main() ***

    
<IntInst::inititalize#175/this=I1/thread=T1><IntInst::terminate#529\this=I1\thread=T1>

    <IntInst::inititalize#175/this=I2/thread=T1>Created interpreter instance 
version=5.2.0 language level=6.05
    ... terminating this interpreter instance 
...<IntInst::terminate#529\this=I2\thread=T1>

    
<IntInst::inititalize#175/this=I3/thread=T1><IntInst::terminate#529\this=I3\thread=T1>
      done.

    There is [1] argument, therefore:
             testTerminate2.exe: creating total of [5] interpreters, running 
[tid=.context~thread;.error~charOut('<{tid='tid'}>');]
                     terminating on SAME thread


    
<IntInst::inititalize#175/this=I4/thread=T1><{tid=1}><IntInst::terminate#529\this=I4\thread=T1>

    
<IntInst::inititalize#175/*this=I5/thread=T1*><IntInst::terminate#529\this=I5\thread=T1>

    
<IntInst::inititalize#175/this=I6/thread=T1><{tid=1}><IntInst::terminate#529\this=I6\thread=T1>

    
<IntInst::inititalize#175/*this=I7/thread=T1*><IntInst::terminate#529\this=I7\thread=T1>

    
<IntInst::inititalize#175/this=I8/thread=T1><{tid=1}><IntInst::terminate#529\this=I8\thread=T1>

    
<IntInst::inititalize#175/*this=I9/thread=T1*><IntInst::terminate#529\this=I9\thread=T1>

    
<IntInst::inititalize#175/this=I10/thread=T1><{tid=1}><IntInst::terminate#529\this=I10\thread=T1>

    
<IntInst::inititalize#175/*this=I11/thread=T1*><IntInst::terminate#529\this=I11\thread=T1>

    
<IntInst::inititalize#175/this=I12/thread=T1><{tid=1}><IntInst::terminate#529\this=I12\thread=T1>

    
<IntInst::inititalize#175/*this=I13/thread=T1*><IntInst::terminate#529\this=I13\thread=T1>

    ---
    *** main() - done: created [5] instances, ran 
[tid=.context~thread;.error~charOut('<{tid='tid'}>');], each terminated on the 
SAME thread
    ---
    *** main() - exiting testTerminate2.main() after Sleep(500); ***


  * (case 2) output:
    G:\tmp\orx\bugs\crashOnTerminate20250729>rxenv testTerminate2.exe 1
    *** main() - entered testTerminate2.main() ***

    
<IntInst::inititalize#175/this=I1/thread=T1><IntInst::terminate#529\this=I1\thread=T1>

    <IntInst::inititalize#175/this=I2/thread=T1>Created interpreter instance 
version=5.2.0 language level=6.05
    ... terminating this interpreter instance 
...<IntInst::terminate#529\this=I2\thread=T1>

    
<IntInst::inititalize#175/this=I3/thread=T1><IntInst::terminate#529\this=I3\thread=T1>
      done.

    There are [2] arguments, therefore:
             testTerminate2.exe: creating total of [5] interpreters, running 
[tid=.context~thread;.error~charOut('<{tid='tid'}>');]
                     terminating on SEPARATE thread


    <IntInst::inititalize#175/this=I4/thread=*T1*><{tid=1}>
    Loop # [1], created thread [0000000000000210] for terminating instance ...
    <IntInst::terminate#529\this=I4\thread=*T2*>

    
<IntInst::inititalize#175/*this=I5/thread=T2*><IntInst::terminate#529\this=I5\thread=*T2*>

    <IntInst::inititalize#175/this=I6/thread=T1><{tid=1}>
    Loop # [2], created thread [0000000000000214] for terminating instance ...
    <IntInst::terminate#529\this=I6\thread=T3>

    G:\tmp\orx\bugs\crashOnTerminate20250729>/** CRASH ** in 
memoryObject.collectAndUninit(Interpreter::lastInstance()); /
  * (case 3) output:
    G:\tmp\orx\bugs\crashOnTerminate20250729>rxenv testTerminate2.exe 1 1
    *** main() - entered testTerminate2.main() ***

    
<IntInst::inititalize#175/this=I1/thread=T1><IntInst::terminate#529\this=I1\thread=T1>

    <IntInst::inititalize#175/this=I2/thread=T1>Created interpreter instance 
version=5.2.0 language level=6.05
    ... terminating this interpreter instance 
...<IntInst::terminate#529\this=I2\thread=T1>

    
<IntInst::inititalize#175/this=I3/thread=T1><IntInst::terminate#529\this=I3\thread=T1>
      done.

    There are [3] arguments, therefore:
             testTerminate2.exe: creating total of [5] interpreters, running 
[tid=.context~thread;.error~charOut('<{tid='tid'}>');]
                     terminating on SEPARATE thread (doing a 'fflush(stderr)' 
in termination thread)


    <IntInst::inititalize#175/this=I4/thread=T1><{tid=1}>
    Loop # [1], created thread [0000000000000210] for terminating instance ...

    
<IntInst::inititalize#175/this=I5/thread=T1><IntInst::terminate#529\this=I4\thread=T2>
    <{tid=1}>
    Loop # [2], created thread [000000000000022C] for terminating instance ...

    
<IntInst::inititalize#175/this=I6/thread=T1><IntInst::terminate#529\this=I5\thread=T3>
    <{tid=1}>
    Loop # [3], created thread [0000000000000218] for terminating instance ...

    
<IntInst::inititalize#175/this=I7/thread=T1><IntInst::terminate#529\this=I6\thread=T4>
    <{tid=1}>
    Loop # [4], created thread [0000000000000224] for terminating instance ...

    
<IntInst::inititalize#175/this=I8/thread=T1><IntInst::terminate#529\this=I7\thread=T5>
    <{tid=1}>
    Loop # [5], created thread [0000000000000258] for terminating instance ...

    ---
    *** main() - done: created [5] instances, ran 
[tid=.context~thread;.error~charOut('<{tid='tid'}>');], each terminated on 
SEPARATE threads
    ---
    <IntInst::terminate#529\this=I8\thread=*T6*>

    
<IntInst::inititalize#175/*this=I9/thread=T6*><IntInst::terminate#529\this=I9\thread=*T6*>
    *** main() - exiting testTerminate2.main() after Sleep(500); ***


The creation of the Rexx interpreter instances by the test program occurs on thread T1 on which the Rexx program gets carried out (producing the string "<{tid=1}>").

(case 1) creates an interpreter in the loop and terminates it on the same thread. Creating and terminating another interpreter instance thereafter works without a crash.

The difference between (case 2) which causes a crash and (case 3) seems to be the fact, that (case 2) shuts down with I5 on T2, but continues to create a new interpreter I6 on T1, terminates it on T3 and there the crash occurs (in the memoryObject.collectAndUninit() call branch).

(case 3) on the other hand does not shut down the interpreter, except after the last created interpreter I8 got terminated on T6, causing I9 on T6 to shut down, which works without a crash in this case.

Maybe these observation help shed light on what might be causing the crash.

---rony


On 23.12.2023 18:03, Rony G. Flatscher wrote:

Since last August a crash got reported in <https://sourceforge.net/p/oorexx/bugs/1872/> which can be easily reproduced by following the directions there. Using the supplied simple C++-only program will work flawlessly if invoked simply as

    testTerminate.exe

it will run flawlessly using 250 Rexx interpreter instances to execute a simple script (issuing a single dot on stdout) and then terminate it on the same thread.

... cut ...

#/*----------------------------------------------------------------------------*/
#/*                                                                            
*/
#/* Copyright (c) 2008-2014 Rexx Language Association. All rights reserved.    
*/
#/*                                                                            
*/
#/* This program and the accompanying materials are made available under       
*/
#/* the terms of the Common Public License v1.0 which accompanies this         
*/
#/* distribution. A copy is also available at the following address:           
*/
#/* https://www.oorexx.org/license.html                                        
*/
#/*                                                                            
*/
#/* Redistribution and use in source and binary forms, with or                 
*/
#/* without modification, are permitted provided that the following            
*/
#/* conditions are met:                                                        
*/
#/*                                                                            
*/
#/* Redistributions of source code must retain the above copyright             
*/
#/* notice, this list of conditions and the following disclaimer.              
*/
#/* 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.   
*/
#/*                                                                            
*/
#/* Neither the name of Rexx Language Association nor the names                
*/
#/* of its contributors may be used to endorse or promote products             
*/
#/* derived from this software without specific prior written permission.      
*/
#/*                                                                            
*/
#/* THIS SOFTWARE IS PROVIDED BY THE COPYright HOLDERS AND CONTRIBUTORS        
*/
#/* "AS IS" AND ANY EXPRESS 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 COPYright   
*/
#/* OWNER OR 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 is a Visual C++, nMake compatible make file.
#
# The compiler needs to be able to find the ooRexx native API headers and
# libraries. If REXX_HOME is set on the system, the correct directory will be
# automatically added to the LIB and INCLUDE environment variables by this make
# file.
#
# Otherwise, either uncomment the next line and define the correct REXX_HOME, or
# be sure the LIB and INCLUDE environment variables allow the compiler to find
# the ooRexx native API headers and libraries.
#
# On a build system for ooRexx, you can set REXX_HOME to point to Win32Dbg or
# Win32Rel, as appropriate, in your build directory.  For this to work, you
# will have to have built the interpreter.

#REXX_HOME = C:\work.ooRexx\wc\main\Win32Dbg   # For example

# Define RELEASE on the command line, or here, to build a non-debug version.  By
# default debug versions are built.  I.e. nMake RELEASE=1 /F Makefile
#RELEASE = 1


!IF DEFINED(REXX_HOME)
INCLUDE = $(INCLUDE);$(REXX_HOME)\api
LIB = $(LIB);$(REXX_HOME)\api
!ENDIF

REXX_LIBS = rexx.lib rexxapi.lib

WARNINGFLAGS = /W3 /D_CRT_SECURE_NO_DEPRECATE /D_CRT_NONSTDC_NO_DEPRECATE

!IF DEFINED(RELEASE)

OOREXX_CFLAGS = /nologo /EHsc /O2 /Gs /FAcs /MT $(WARNINGFLAGS) /c
EXE_LFLAGS = /nologo /SUBSYSTEM:Console $(REXX_LIBS) user32.lib comdlg32.lib 
gdi32.lib kernel32.lib
DLL_LFLAGS = /nologo /SUBSYSTEM:Windows $(REXX_LIBS) /DLL

!ELSE

OOREXX_CFLAGS =  /nologo /EHsc /Zi /Od /MTd $(WARNINGFLAGS) /c
EXE_LFLAGS = /nologo /DEBUG -debugtype:cv /SUBSYSTEM:Console $(REXX_LIBS) 
user32.lib comdlg32.lib gdi32.lib kernel32.lib
DLL_LFLAGS = /nologo /DEBUG -debugtype:cv /SUBSYSTEM:Windows $(REXX_LIBS) /DLL

!ENDIF

# What we want to build.
all: testTerminate2.exe

testTerminate2.obj: testTerminate2.cpp
  cl $(OOREXX_CFLAGS) testTerminate2.cpp

testTerminate2.exe: testTerminate2.obj
    link $(EXE_LFLAGS) testTerminate2.obj bufferoverflowu.lib -out:$(@B).exe

clean:
        del *.exe *.dll *.obj *.ilk *.pdb *.lib *.exp *.suo *.cod 1>nul 2>&1

// Based on: stackOverflow.cpp ooRexx sample


/**
 * A simple example that creates an instance of the interpreter and uses that
 * instance to execute a Rexx routine defined in this program.
 *
 * Both routines cause an infinite recursion.  One version of the routine traps
 * any error, the other does not.  This shows how to handle conditions raised
 * inside the interpreter execution, i.e., outside of your native code
 * execution.
 *
 * To have the second routine execute, specify any argument on the command line.
 * For example:
 *
 *   stackOverflow 1
 *
 * With no argument, the first routine is used:
 *
 *   starckOverflow
 */

#include "oorexxapi.h"
#include <stdio.h>
#include <string.h>

// #include "SysThread.hpp"

#if defined(_WIN32)
#define _CDECL __cdecl
#else
#define _CDECL
#endif


bool bDebug=false; // true; // false;  // if true, then fprintf() output gets 
shown

/* Prototypes for several simple helper functions that demonstrate usage of some
 * of the native C++ APIs. The functions themselves are at the bottom of the
 * file.
 */
bool checkForCondition(RexxThreadContext *c, bool clear);
void printInterpreterVersion(RexxInstance *);
int  mainArgc   = -1;
int  totLoops   =  5000; // 1000; // 500; // 5; // 250;
// char rexxCode[] = "tid=.context~thread;.error~charOut('<{tid='tid'}>');";   
// Rexx code to execute on each interpreter instance
// char rexxCode[] = "nl='0d0a'x;.error~charOut('    
[***_I'.context~interpreter'/T'.context~thread'_***]'nl);";   // Rexx code to 
execute on each interpreter instance

char rexxCode[] = ".error~charOut('.');";


struct LoopData {
    RexxInstance *instance;
    int          loopNr;
};

// from 
<https://stackoverflow.com/questions/1981459/using-threads-in-c-on-windows-simple-example>,
// also 
<https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-createthread>
// #include <windows.h>

DWORD WINAPI ThreadFunc(void* data) {
   // Do stuff.  This will be the first function called on the new thread.
   // When this function returns, the thread goes away.  See MSDN for more 
details.

    // RexxInstance *instance = (RexxInstance *) data;
    LoopData *ld = (LoopData *) data;

    if (mainArgc==3)        // fflush(stderr) suffices (must be stderr !)
    {
        // the following block, makes it run MOST of the time
        fflush(stderr);
    }

    // Now wait for the interpreter to terminate and we are done.
    // fprintf(stderr, "/***T***\\");fflush(stderr);
    // instance->Terminate();
    if (bDebug==true) fprintf(stdout,"*** ... a) in thread for L[%d] about to 
Terminate() in TID=[%ld] ...\n",ld->loopNr,
        (DWORD) GetCurrentThreadId() );

    ld->instance->Terminate();

    if (bDebug==true)
    {
        fprintf(stdout,"*** ... b) in thread for L[%d] AFTER Terminate() in 
TID=[%ld]\n",ld->loopNr,
                (DWORD) GetCurrentThreadId()
        );fflush(stdout);
    }
    // fprintf(stderr, "\\***T***/");fflush(stderr);

    return 0;
}

int _CDECL main(int argc, char **argv)
{
    if (bDebug==true)
    {
        fprintf(stderr, "*** main() - entered testTerminate2.main() ***\n");
        fflush(stderr);
    }
    mainArgc        = argc;

    // if (argc>1) { sscanf(argv[1], "%d", &totLoops);}  // just keep it for 
reference

    // show interpreter version
    RexxInstance *interpreter0;
    RexxThreadContext *threadContext0;
    RexxOption *options0 = NULL;
    if ( RexxCreateInterpreter(&interpreter0, &threadContext0, options0) == 0 )
    {
        fprintf(stderr, "Failed to create interpreter, aborting.\n");
        fflush(stderr);
        exit(-1);
    }
    printInterpreterVersion(interpreter0);
    if (bDebug==true)
    {
        fprintf(stderr, "... terminating this interpreter instance ...");
        fflush(stderr);
    }

    interpreter0->Terminate();

//    if (bDebug==true)
    {
        fflush(stderr);
        fprintf(stderr, " done.\n");

        fprintf(stderr, "\nThere %s [%d] argument%s, therefore: \n", (argc==1 ? 
"is": "are"), argc, (argc==1 ? "" : "s"));
        fprintf(stderr, "\t%s: creating total of [%d] interpreters, running 
[%s] \n",
                  argv[0], totLoops, rexxCode);

        fprintf(stderr, "\t\tterminating on %s thread%s\n\n",
                  (argc==1? "SAME" : "SEPARATE"), (argc==3? " (doing a 
'fflush(stderr)' in termination thread)" : "" ) );

        fprintf(stderr, "----------------------------------->: \n\n");

        fflush(stderr);
    }

    for (int i=1;i<=totLoops;i++)
    {
        if (bDebug==true)
        {
            fprintf(stderr, "\n--> Loop # [%d]\n",i);
            fflush(stderr);
        }

        // These are the arguments to RexxCreateInterpreter().  An array of any
        // number of Rexx options can be passed in, but for this example we do 
not
        // need any options.  So, we use NULL.
        RexxInstance *interpreter;
        RexxThreadContext *threadContext;
        RexxOption *options = NULL;

        if (bDebug==true)
        {
            fprintf(stderr," \n*** RexxInterpreter L[%d], about to create in 
TID=[%d]\n",i,(DWORD) GetCurrentThreadId());
            fflush(stderr);
        }

        if ( RexxCreateInterpreter(&interpreter, &threadContext, options) == 0 )
        {
            if (bDebug==true)
            {
                fprintf(stderr, "Failed to create interpreter, aborting.\n");
                fflush(stderr);
            }
            exit(1);
        }

        // printInterpreterVersion(interpreter);
        if (bDebug==true)
        {
            fprintf(stderr," \n*** RexxInterpreter L[%d] created in TID=[%d], 
interpreter=[%p], threadContext=[%p]\n", (DWORD) GetCurrentThreadId(), i, 
interpreter, threadContext);
            fflush(stderr);
        }

        // Create a routine object from the source code.
        RexxRoutineObject obj = threadContext->NewRoutine("testTerminate", 
rexxCode, strlen(rexxCode));
        if ( obj == NULL )
        {
            checkForCondition(threadContext, true);
            fprintf(stderr,"Error creating routine object, aborting\n");
            fflush(stderr);
            exit(1);
        }

        // Execute the routine.
        if (bDebug==true)
        {
            fprintf(stderr,"*** ... a) threadContext->CallRoutine(...) 
TID=[%d]\n",(DWORD) GetCurrentThreadId());
            fflush(stderr);
        }

        RexxObjectPtr result = threadContext->CallRoutine(obj, NULL);

        if (bDebug==true)
        {
            fprintf(stderr,"*** ... b) threadContext->CallRoutine(...) 
TID=[%d], result=[%p]\n",(DWORD) GetCurrentThreadId(),  result);
            fflush(stderr);
        }

        // Check for a condition raised during CallRoutine().
        checkForCondition(threadContext, true);

        if (argc>1) // if argument given terminate on separate thread
        {
            LoopData ld = { interpreter, i };
            // void *data = interpreter;
            // HANDLE thread = CreateThread(NULL, 0, ThreadFunc, data, 0, NULL);
            HANDLE thread = CreateThread(NULL, 0, ThreadFunc, (void *) &ld, 0, 
NULL);
            if (thread && bDebug==true)
            {
                // fprintf(stderr, "Created # [%d], thread [%p] for terminating 
instance [%p]\n", i, thread, interpreter);fflush(stderr);
                fprintf(stderr, "\n<-- Loop # [%d], created thread [%p] for 
terminating instance ...\n", i, thread);
                fflush(stderr);
            }
            else    // oops, an error occurred!
            {
                DWORD errnum = GetLastError();
                if (bDebug==true)
                {
                    fprintf(stderr, "ERROR at RII # [%d], thread [%p] for 
terminating instance [%p]: GetLastError()=[%d]\n", i, thread, interpreter, 
errnum);
                    fflush(stderr);
                }

                // from: interpreter\platform\windows\SysRexxUtil.cpp-1228
                char *errmsg=NULL;
                if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | 
FORMAT_MESSAGE_FROM_SYSTEM, NULL, errnum, 0, (LPSTR)&errmsg, 64, NULL) == 0)
                {
                    if (bDebug==true)
                    {
                        fprintf(stderr, "             # [%d], thread [%p]: no 
information about error via FormatMessage()\n", i, thread);
                        fflush(stderr);
                    }
                }
                else
                {                               /* succeeded                  */
                    if (bDebug==true)
                    {
                        fprintf(stderr, "             # [%d], thread [%p]: 
FormatMessage()=[%s]\n", i, thread, errmsg);
                        fflush(stderr);
                    }
                    LocalFree(errmsg);
                }
            }
        }
        else        // terminate on main thread
        {
            // Now wait for the interpreter to terminate and we are done.
            if (bDebug==true)
            {
                fprintf(stderr,"*** 1) L[%d] interpreter->Terminate() ...\n",i);
                fflush(stderr);
            }

            interpreter->Terminate();

            if (bDebug==true)
            {
                fprintf(stderr,"*** 2) L[%d] interpreter->Terminate().\n",i);
                fflush(stderr);
            }
        }

    }
    if (bDebug==true)
    {
        fprintf(stderr, "\n\n<-----------------------------------: \n");
        fprintf(stderr, "\n---\n*** main() - done: created [%d] instances, ran 
[%s], each terminated on %s thread%s\n---\n",
               totLoops, rexxCode, (argc==1? "the SAME" : "SEPARATE"), 
(argc==1?"":"s"));
        fflush(stderr);
    }

    Sleep(500); // wait to make sure that all instances in all threads can be 
terminated
    if (bDebug==true)
    {
        fprintf(stderr, "*** main() - exiting testTerminate2.main() after 
Sleep(500); ***\n");
        fflush(stderr);
    }

    return 0;
}

/**
 * Below are several helper functions that demonstrate how to use some of the
 * different C++ native APIs.
 */

/**
 * Given an interpreter instance, prints out the interpreter version and
 * language version.  The documentation in the ooRexx programming guide explains
 * the byte encoding of the version numbers.
 */
void printInterpreterVersion(RexxInstance *interpreter)
{
    wholenumber_t ver  = interpreter->InterpreterVersion();
    wholenumber_t lang = interpreter->LanguageLevel();
    if (bDebug==true)
    {
        fprintf(stderr, "Created interpreter instance version=%lld.%lld.%lld 
language level=%lld.%02lld\n",
               (ver & 0xff0000) >> 16, (ver & 0x00ff00) >> 8, ver & 0x0000ff, 
(lang & 0xff00) >> 8, lang & 0x00ff);
        fflush(stderr);
    }

}


/**
 * Given a condition object, extracts and returns as a whole number the subcode
 * of the condition.
 */
inline wholenumber_t conditionSubCode(RexxCondition *condition)
{
    return (condition->code - (condition->rc * 1000));
}


/**
 * Outputs the typical condition message.  For example:
 *
 *      4 *-* say dt~number
 * Error 97 running C:\work\qTest.rex line 4:  Object method not found
 * Error 97.1:  Object "a DateTime" does not understand message "NUMBER"
 *
 * @param c          The thread context we are operating in.
 * @param condObj    The condition information object.  The object returned from
 *                   the C++ API GetConditionInfo()
 * @param condition  The RexxCondition struct.  The filled in struct from the
 *                   C++ API DecodeConditionInfo().
 *
 * @assumes  There is a condition and that condObj and condition are valid.
 */
void standardConditionMsg(RexxThreadContext *c, RexxDirectoryObject condObj, 
RexxCondition *condition)
{
    RexxObjectPtr list = c->SendMessage0(condObj, "TRACEBACK");
    if ( list != NULLOBJECT )
    {
        RexxArrayObject a = (RexxArrayObject)c->SendMessage0(list, "ALLITEMS");
        if ( a != NULLOBJECT )
        {
            size_t count = c->ArrayItems(a);
            for ( size_t i = 1; i <= count; i++ )
            {
                RexxObjectPtr o = c->ArrayAt(a, i);
                if ( o != NULLOBJECT )
                {
                    printf("%s\n", c->ObjectToStringValue(o));
                }
            }
        }
    }
    printf("Error %zd running %s line %zd: %s\n", condition->rc, 
c->CString(condition->program),
           condition->position, c->CString(condition->errortext));

    printf("Error %zd.%03zd:  %s\n", condition->rc, 
conditionSubCode(condition), c->CString(condition->message));
    fflush(stdout);
}


/**
 * Given a thread context, checks for a raised condition, and prints out the
 * standard condition message if there is a condition.
 *
 * @param c      Thread context we are operating in.
 * @param clear  Whether to clear the condition or not.
 *
 * @return True if there was a condition, otherwise false.
 */
bool checkForCondition(RexxThreadContext *c, bool clear)
{
    if ( c->CheckCondition() )
    {
        RexxCondition condition;
        RexxDirectoryObject condObj = c->GetConditionInfo();

        if ( condObj != NULLOBJECT )
        {
            c->DecodeConditionInfo(condObj, &condition);
            standardConditionMsg(c, condObj, &condition);

            if ( clear )
            {
                c->ClearCondition();
            }
            return true;
        }
    }
    return false;
}
_______________________________________________
Oorexx-devel mailing list
Oorexx-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/oorexx-devel

Reply via email to