Committed with [r12999]: <http://sourceforge.net/p/oorexx/code-0/12999>.
---rony
On 27.07.2025 15:18, Rony G. Flatscher wrote:
On 27.07.2025 15:04, Rick McGuire wrote:
Actually, your version has a serious flaw in it. The line
+ InstanceDetacher instDet = InstanceDetacher(tmpInstance.instance);
uses a copy constructor, so two instances of an InstanceDetacher get created,
which means the destructor gets run twice. The first probably gets run before
the uninit call. Change this to
InstanceDetacher instDet(tmpInstance.instance);
and everything will work out okay.
Oops, thank you, Rick!
Refactored, how about:
Index: interpreter/runtime/Interpreter.cpp
===================================================================
--- interpreter/runtime/Interpreter.cpp (revision 12998)
+++ interpreter/runtime/Interpreter.cpp (working copy)
@@ -235,7 +235,9 @@
// to shut down so that the package manager can unload
// the libraries (it needs to pass a RexxThreadContext
// pointer out to package unloaders, if they are defined)
- InstanceBlock instance;
+ InstanceBlock tmpInstance;
+ InstanceAttacherDetacher instAttDet(tmpInstance.instance);
+
// run whatever uninits we can before we start releasing the
libraries
memoryObject.lastChanceUninit();
@@ -487,6 +489,30 @@
/**
+ * Make sure that attachThread() and detachThread() get carried out for the
+ * supplied InterpreterInstance.
+ *
+ * @param i InterpreterInstance
+ */
+InstanceAttacherDetacher::InstanceAttacherDetacher(InterpreterInstance *i)
+{
+ // Get an instance.
+ instance = i;
+ instance->InterpreterInstance::attachThread();
+}
+
+/**
+ * Carry out detachThread() for the instance, if going out of scope.
+ */
+InstanceAttacherDetacher::~InstanceAttacherDetacher()
+{
+ // detach from the instance
+ instance->InterpreterInstance::detachThread();
+}
+
+
+
+/**
* Decode a condition directory into a easier to use
* structure form for a native code user. This breaks
* the directory into its component pieces, including
Index: interpreter/runtime/Interpreter.hpp
===================================================================
--- interpreter/runtime/Interpreter.hpp (revision 12998)
+++ interpreter/runtime/Interpreter.hpp (working copy)
@@ -248,4 +248,14 @@
};
+class InstanceAttacherDetacher
+{
+public:
+ InstanceAttacherDetacher(InterpreterInstance *i);
+ ~InstanceAttacherDetacher();
+
+ InterpreterInstance *instance; // interpreter instance
+};
+
+
#endif
Any comments, suggestions?
---rony
On Sun, Jul 27, 2025 at 8:59 AM Rick McGuire <object.r...@gmail.com> wrote:
I would have put the attachThread() inside the constructor, but that works.
Rick
On Sun, Jul 27, 2025 at 8:49 AM Rony G. Flatscher <rony.flatsc...@wu.ac.at>
wrote:
On 27.07.2025 13:24, Rick McGuire wrote:
If an exception happens, the detachThread() never happens, which would
leave the
interpreter in an inconsistent state. The best fix would be to write a
small class with
a destructor method that will do the detach thread when the variable
leaves the scope.
Thank you, Rick.
How about:
Index: interpreter/runtime/Interpreter.cpp
===================================================================
--- interpreter/runtime/Interpreter.cpp (revision 12998)
+++ interpreter/runtime/Interpreter.cpp (working copy)
@@ -235,7 +235,10 @@
// to shut down so that the package manager can unload
// the libraries (it needs to pass a RexxThreadContext
// pointer out to package unloaders, if they are defined)
- InstanceBlock instance;
+ InstanceBlock tmpInstance;
+ tmpInstance.instance->InterpreterInstance::attachThread();
+ InstanceDetacher instDet =
InstanceDetacher(tmpInstance.instance);
+
// run whatever uninits we can before we start releasing
the libraries
memoryObject.lastChanceUninit();
@@ -487,6 +490,28 @@
/**
+ * Make sure that detachThread() gets carried out for the supplied
InterpreterInstance.
+ *
+ * @param i InterpreterInstance
+ */
+InstanceDetacher::InstanceDetacher(InterpreterInstance *i)
+{
+ // Get an instance.
+ instance = i;
+}
+
+/**
+ * Carry out detachThread() for the instance, if going out of
scope.
+ */
+InstanceDetacher::~InstanceDetacher()
+{
+ // detach from the instance
+ instance->InterpreterInstance::detachThread();
+}
+
+
+
+/**
* Decode a condition directory into a easier to use
* structure form for a native code user. This breaks
* the directory into its component pieces, including
Index: interpreter/runtime/Interpreter.hpp
===================================================================
--- interpreter/runtime/Interpreter.hpp (revision 12998)
+++ interpreter/runtime/Interpreter.hpp (working copy)
@@ -248,4 +248,14 @@
};
+class InstanceDetacher
+{
+public:
+ InstanceDetacher(InterpreterInstance *i);
+ ~InstanceDetacher();
+
+ InterpreterInstance *instance; // interpreter instance
+};
+
+
#endif
Any comments, suggestions?
---rony
On Sun, Jul 27, 2025 at 6:51 AM Rony G. Flatscher
<rony.flatsc...@wu.ac.at> wrote:
It seems that the following patch fixes the reported crash:
Index: interpreter/runtime/Interpreter.cpp
===================================================================
--- interpreter/runtime/Interpreter.cpp (revision 12998)
+++ interpreter/runtime/Interpreter.cpp (working copy)
@@ -235,11 +235,15 @@
// to shut down so that the package manager can unload
// the libraries (it needs to pass a RexxThreadContext
// pointer out to package unloaders, if they are
defined)
- InstanceBlock instance;
+ InstanceBlock tmpInstance;
+
tmpInstance.instance->InterpreterInstance::attachThread();
+
// run whatever uninits we can before we start
releasing the libraries
memoryObject.lastChanceUninit();
PackageManager::unload();
+
+
tmpInstance.instance->InterpreterInstance::detachThread();
}
catch (ActivityException)
{
Any comments, suggestions?
---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/>
<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.
However if supplying an argument to that test program (source got
uploaded with
#1872 as well), then the termination of the Rexx interpreter
instance occurs on a
separate thread and will cause the crash upon terminating the
second Rexx
interpreter instance on a separate thread:
testTerminate.exe
anyArgumentSufficesToRunTheRIIOnSeparateThreadAndCauseTheCrash
The relevant Windows code is:
... cut ...
//
from<https://stackoverflow.com/questions/1981459/using-threads-in-c-on-windows-simple-example>
<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>
<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;
// uncommenting the following statement, makes it run
*// fprintf(stderr, "ThreadFunc(), arrived: terminating
instance [%p]\n",
instance);fflush(stderr); *
// Now wait for the interpreter to terminate and we are
done.
instance->Terminate();
// fprintf(stderr, "ThreadFunc(), leaving: terminating instance
[%p]\n", instance);fflush(stderr);
return 0;
... cut ...
if (argc>1) // if argument given terminate on separate
thread
{
void *data = interpreter;
HANDLE thread = CreateThread(NULL, 0, ThreadFunc,
data, 0, NULL);
if (thread)
{
fprintf(stderr, "Created [%d], thread [%p] for
terminating instance [%p]\n", i, thread, interpreter);fflush(stderr);
}
}
else // terminate on main thread
{
// Now wait for the interpreter to terminate and
we are done.
interpreter->Terminate();
}
... cut ...
Uncommenting the blue fprintf statement makes the crash go away
*most* of the time
such that all 250 Rexx interpreter instances can be terminated on a
separate
thread. Reducing that to just fflush(stderr) makes it go away
*most* of the time as
well (whereas using fflush(stdout) instead does not have any
effect. So accessing
stderr in that thread somehow influences the behaviour).
---
Originally this crash occurred in the context of BSF4ooRexx and I
reported it in
<https://sourceforge.net/p/oorexx/feature-requests/806/>
<https://sourceforge.net/p/oorexx/feature-requests/806/>. It seems
that Rick
thought it had to do with the code in BSF4ooRexx and not with
ooRexx. Eventually
this thread led to a new, small C++ (supplied with #1872 above)
only (only creates
a RII, runs a simple Rexx script, terminates the RII) showcase in
which no external
dependency could be made responsible for the crash, such that it
becomes clear that
the cause lies in ooRexx itself.
As the crash occurs rather early, if using "testTerminate.exe
someArg" I had hoped
that some of the C++ gurus would be able to locate (and hopfeully
fix) the cause
relatively quickly (realizing that multithreaded bugs can be
extremely hard to
trace down and to fix). Unfortunately, this has not happened to
this day (after
almost 16 months). As this is a showstopper and inhibits using this
great ooRexx
feature, namely being able to create any number of different Rexx
interpreter
instances (each having its own .local) in a process and terminate
them.
Therefore I kindly request any of the C++ experts to look into this
matter if
possible. Maybe the upcoming vacation time allows for a few time
slots to use for
analyzing, finding and hopefully fixing the cause of this
showstopper crash!
---rony
P.S.: Have simplified the test code a little bit, also allowing the
test program to
run with all three reported scenarios; it is enclosed as (also
slightly stripped
down) "testTerminate2.cpp" and "Makefile2.windows" ("nmake /f
Makefil2.windows"
will create testTerminate2.exe) which after successful compilation
can be invoked as:
* testTerminate2.exe
o no arguments: will create and terminate all 250 Rexx
interpreter instances
(RII) on the same (main) thread without any crash
* testTerminate2.exe oneArg
o any single argument will cause the crash to always occur
while terminating
the *second* RII on a separate thread (as if terminating
the previous RII
on a separate thread left something in an unbalanced state)
* testTerminate2.exe oneArg secondArg
o any two arguments will cause fflush(stderr) in the
terminating thread to be
issued and as a result most of the time all 250 Rexx
interpreter instances
can be terminated on a separate thread without a crash;
running it a couple
of times may however cause that crash to occur at different
RIIs
Terminate() will kick off the garbage collector and the crash seems
to occur during
its run.
P.P.S.: Would it be possible to have the termination of a RII
carried out on the
same thread as its creation (which seems to run stably) by ooRexx
itself (e.g.
having a specific thread to create and to terminate RIIs)?
This may need a function to learn about whether a RII is currently
running/blocked
and only terminate it on the creation thread if not in use so not
to block the
creation thread. Maybe also (an asynchroneous?) a function to force
termination of
a RII would be helpful in such a scenario, maybe with an option
that indicates
whether running/blocking Rexx code on that RII should be forcefully
halted or not.
_______________________________________________
Oorexx-devel mailing list
Oorexx-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/oorexx-devel