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