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 ...

_______________________________________________
Oorexx-devel mailing list
Oorexx-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/oorexx-devel

Reply via email to