To keep the best of both worlds I propose to allow to activate this particular
feature (defaulting
methods to unguarded if the first statement neither starts with EXPOSE nor with
USE LOCAL) with a
new option "unguarded" on the options directive, i.e.:
::options unguarded
Enclosed please find the intended patch (tested) with request for comment.
Will update the documentation (rexxref) and the method directive test unit
accordingly.
---rony
P.S.: With the patch applied the following program:
say 'OPTIONS UNGUARDED set ...'
do i=1 to 5
m=.Test1~method("M"i)
say "m"i"~isGuarded:" m~isGuarded
end
::options unguarded
::class Test1
::method m1
say 'm1'
::method m2
expose a
::method m3
use local
::method m4 guarded
::method m5 unguarded
will yield the following (expected) output:
OPTIONS UNGUARDED set ...
m1~isGuarded: 0
m2~isGuarded: 1
m3~isGuarded: 1
m4~isGuarded: 1
m5~isGuarded: 0
Without the new "unguarded" subdirective of the options directive in effect
method m1 will be
guarded by default (the original behavior).
On 07.08.2021 18:28, Rony G. Flatscher wrote:
>
> Sleeping and thinking more over it I would suggest to remove this feature
> altogether! The major
> reasing being that the Rexx philosophy (and ooRexx by extension) should make
> coding as easy as
> possible for programmers.
>
> The default for methods (and related) has always been "guarded" such that one
> would not have to
> write "guarded" to the method directive. With that default only in very rare
> cases (in
> multithreaded scenarios) would one have to write "unguarded" to a method
> directive. And if doing
> so one must have an understanding of multithreading and the ramifications if
> someone would want a
> method to run unguarded.
>
> Compare this to the current situation with this new feature on: in order to
> fix BSF.CLSÂ all of a
> sudden I had to add the keyword "guarded" to every method to gain the default
> behaviour for ooRexx
> < 5.0 and thereby re-enabling correct execution of GUI nutshell examples.
>
> Ad feature: originally it was thought to be helpful to programmers by saving
> the programmers to
> write "unguarded" to a method directive for a method that is known to be safe
> in unguarded mode
> thinking that methods that have no direct access to the attribute pool (i.e.
> the method routine
> would not start with "expose" or "use local") qualify for unguarded execution
> not thinking about
> scenarios where this is not enough.
>
> To make it easy for the programmer (i.e. not having to know additional,
> sometimes quite subtle,
> concepts that play a role in this context) I would be in a strong favor to
> leave the default
> "guarded" in place. Then either remove this new feature altogether or make it
> an explicit option a
> programmer has to state ("::OPTIONS" with a new pair "guarded|unguarded").
>
> What opinions have others? Do you concur?
>
> ---rony
>
>
> On 06.08.2021 15:09, Rony G. Flatscher wrote:
>>
>> Background: In order for ooRexx programmers getting acquainted with
>> BSF4ooRexx/Java quickly there
>> are numerous nutshell examples in "bsf4rexx/samples". Most of these nutshell
>> examples stem from
>> observations of students over time who should get help by demonstrating them
>> how to achieve
>> something of interest with these (mostly brief) nutshell examples.
>>
>> One interesting problem has been the interaction from ooRexx with GUI
>> objects which must be
>> carried out on the GUI threads in Java (the "awt thread" or the "JavaFX
>> Application thread").
>> Although they got the necessary information about the architecture and what
>> to do in ordert to
>> become able to send messages on GUI threads, they kept running into
>> problems, losing a lot of
>> time (even months because they could not get it working in more complex
>> programs).
>>
>> To make a long story short, I came up with a message based solution, that
>> was very easy to
>> understand and to employ for them. None of the students ran into the GUI
>> thread problems since then.
>>
>> The solution is an ooRexx class for awt (the Java "abstract windows
>> toolkit") named .AwtGuiThread
>> and for JavaFX (a powerful GUI system) .FxGuiThread, both subclassing a
>> common superclass
>> .AbstractGuiThread. These classes allow one to send the ooRexx message
>> runLater[Latest](GUIreceiver, messageName, arguments) which get queued and
>> dispatched on the GUI
>> thread later.
>>
>> The nutshell examples
>> "bsf4rexx/samples/3-090_update_awtSwing_GUI-from-non-GUI-thread.rxj" and
>> "bsf4rexx/samples/JavaFX/javafx_update_GUI-from-non-GUI-thread.rxj"
>> demonstrate how to employ
>> this infrastructure. They have been working for years without a problem.
>>
>> While working on BSF4ooRexx I stumbled over an error (not having run those
>> two examples for quite
>> some time) which seems to indicate that ooRexx now creates an error when
>> being used from
>> different threads:
>>
>>
>> F:\work\svn\bsf4oorexx\trunk\bsf4oorexx\samples>3-090_update_awtSwing_GUI-from-non-GUI-thread.rxj
>> screenSize: [java.awt.Dimension[width=1920,height=1080]]
>> winSize : [java.awt.Dimension[width=800,height=200]]
>> xPos=[560] yPos=[440]
>> a REXXEVENTHANDLER::actionPerformed - starting Rexx thread
>> The SOME_REXX_CLASS class::updateGuiFromRexxThread - just arrived, GUI
>> thread: 23808
>> The SOME_REXX_CLASS class::updateGuiFromRexxThread - now running on
>> thread: 7428
>> *-* Compiled method "DELETE" with scope "Queue".
>> 5727 *-* msgQueue~delete(idx) -- delete the guiMsg object
>> 5637 *-* forward message "REMOVEMESSAGE" continue -- remove all GUI
>> messages of the same name targeted to the same object
>> 207 *-* .AwtGuiThread~runLaterLatest(label, "setText", "i", str)
>> Error 93 running
>> F:\work\svn\bsf4oorexx\trunk\bsf4oorexx\samples\3-090_update_awtSwing_GUI-from-non-GUI-thread.rxj
>> line 207: Incorrect
>> call to method.
>> Error 93.966: Incorrect queue index "1".
>> a REXXEVENTHANDLER::windowClosing - release lock ('closeApp=.true')
>> which will allow a blocked 'waitForExit' method to resume and return
>>
>> F:\work\svn\bsf4oorexx\trunk\bsf4oorexx\samples>
>>
>> and
>>
>>
>> F:\work\svn\bsf4oorexx\trunk\bsf4oorexx\samples\JavaFX>javafx_update_GUI-from-non-GUI-thread.rxj
>> a REXXBUTTONHANDLER::handle - starting Rexx thread
>> The SOME_REXX_CLASS class::updateGuiFromRexxThread - just arrived, GUI
>> thread: 24244
>> The SOME_REXX_CLASS class::updateGuiFromRexxThread - now running on
>> thread: 14124
>> *-* Compiled method "DELETE" with scope "Queue".
>> 5727 *-* msgQueue~delete(idx) -- delete the guiMsg object
>> 5637 *-* forward message "REMOVEMESSAGE" continue -- remove all GUI
>> messages of the same name targeted to the same object
>> 194 *-* .FxGuiThread~runLaterLatest(label, "setText", "i", str)
>> Error 93 running
>> F:\work\svn\bsf4oorexx\trunk\bsf4oorexx\samples\JavaFX\javafx_update_GUI-from-non-GUI-thread.rxj
>> line 194: Incorrect call to method.
>> Error 93.966: Incorrect queue index "1".
>>
>> The ooRexx code where the error occurs looks like, lines # 5637 and # 5727
>> are highlighted in
>> green and bold:
>>
>> /*
>> -------------------------------------------------------------------------------------------------
>> */
>> -- method replaces existing target: this way only the latest sent
>> message will get executed!
>> /** This class method allows to define a Rexx message with its arguments
>> that should
>> * be processed on the GUI thread. Each invocation will create a new
>> <code>GUIMessage</code>
>> * from the supplied arguments and returns it for further inspection in
>> addition to queueing
>> * it for later execution on the GUI thread. Unlike
>> <code>runLater</code> this method will
>> * first remove any queued messages with the same target and the same
>> message name, before
>> * queueing this message.
>> *
>> * @param target the target object to receive a message on the GUI
>> thread
>> * @param messageName the message name to send to the target on the GUI
>> thread
>> * @param indicator optional; indicates with "I" (Indivdual)
>> that the arguments
>> * are listed individually, "A" (Array)
>> indicates that the fourth
>> * argument is an array containing the arguments to
>> supply with the
>> * message on the GUI thread
>> *
>> * @return GUIMessage a GUI message object (modelled after ooRexx'
>> <code>Message</code> class
>> * that allows one to inspect the state of the
>> message (process completed,
>> * fetching a possible result, determining whether an
>> error occurred and
>> * inspecting it)
>> */
>> ::method runLaterLatest class
>> use strict arg target, messageName, ...
>>
>> signal on syntax
>> if self~JavaGuiUtilityClz=.nil then self~setup -- make sure
>> attributes are initialized
>>
>> *forward message "REMOVEMESSAGE" continue -- remove all GUI messages
>> of the same name
>> targeted to the same object*
>> guiMsg=result -- fetch returned GUIMessage object
>> msgQueue=self~msgQueue -- get message queue
>> msgQueue~queue(guiMsg) -- now enqueue current (latest) guiMsg
>>
>> if self~waitingToRun=.false then -- "runLater" not invoked yet?
>> do
>> self~waitingToRun=.true
>> self~invokeOnGuiThread -- make sure Java will dispatch Rexx
>> message on GUI thread
>> end
>> return guiMsg -- allows caller to get a hold of a possible
>> return value
>>
>> syntax:
>> raise propagate
>>
>> ... cut ...
>>
>> /*
>> -------------------------------------------------------------------------------------------------
>> */
>> -- method removes all GUI message objects
>> /** This private class method removes all GUIMessage objects from the
>> message queue that will be processed
>> * later when Java call backs on the GUI thread, that have the supplied
>> target and the supplied
>> * message name.
>> *
>> * @param target the target object to receive a message on the GUI
>> thread
>> * @param messageName the message name to send to the target on the GUI
>> thread
>> * @param indicator optional; indicates with "I" (Indivdual)
>> that the arguments
>> * are listed individually, "A" (Array)
>> indicates that the fourth
>> * argument is an array containing the arguments to
>> supply with the
>> * message on the GUI thread
>> *
>> * @return GUIMessage a GUI message object (modelled after ooRexx'
>> <code>Message</code> class
>> * that allows one to inspect the state of the
>> message (process completed,
>> * fetching a possible result, determining whether an
>> error occurred and
>> * inspecting it)
>> */
>> ::method removeMessage class private
>> use strict arg target, messageName, ...
>>
>> signal on syntax
>> if self~JavaGuiUtilityClz=.nil then self~setup -- make sure
>> attributes are initialized
>>
>> -- remove all occurrences of messages with the same messagename
>> and the same target
>> msgQueue=self~msgQueue -- get message queue
>> idx=msgQueue~first -- get index of the first item in the
>> queue, if any
>> do while idx<>.nil -- a valid index in hand ?
>> tmpItem=msgQueue~at(idx) -- fetch item, a GuiMessage
>> -- same target, same messagename?
>> if tmpItem~target=target,
>> tmpItem~messageName~caselessEquals(messagename) then
>> *msgQueue~delete(idx) -- delete the guiMsg object***
>> idx=msgQueue~next(idx) -- get index of next item in the queue
>> end
>>
>> guiMsg=self~createGuiMessage(arg(1,"array")) -- create and return GUI
>> message object
>> return guiMsg -- allows caller to get a hold of a possible
>> return value
>>
>> syntax:
>> raise propagate
>>
>> None of the methods of these three classes are marked as unguarded such that
>> in the past if one
>> method executes no other can do so in parallel, inhibiting concurrent access
>> of the queue.
>>
>> However, I recall that there was a change in ooRexx 5.0 that if methods do
>> not have an expose
>> keyword statement than unlike earlier versions of ooRexx such expose-less
>> methods will be made
>> unguarded.
>>
>> Yet, as in this real use case this change may bring incompatibilities for
>> existing programs
>> (BSF4ooRexx has been around for more than 10 years). The problem lies in
>> getting and using the
>> queue in different threads because of this (at the time of devising the
>> solution unforeseen) as
>> no default blocking of methods takes place.
>>
>> To double-check I added manually "guarded" to all methods of the classes
>> that play together here
>> and doing so, made the nutshell programs work again.
>>
>> ---
>>
>> The reason I bring this forward is twofold:
>>
>> * the change may break existing programs and programmers may be clueless
>> as to why that happens
>> and therefore not know how to solve it,
>> * it should be discussed whether keeping this change, but if so it needs
>> to be prominently
>> communicated as the cause may be too subtle for many, especially if
>> using ooRexx code for
>> which they do not have the source:
>> o one solution may be to have a new OPTION unguared|guarded
>> determining how expose-less
>> methods should be treated; this way this default to unguarded
>> behavior needs to be
>> activated explicitly making the programmer aware of it, yet older
>> programs still would
>> function with the default to guarded behavior that was in place in
>> erarlier versions of
>> ooRexx,
>> o another solution would be to not only analyze whether the expose
>> statement exists, but in
>> the case where it does not exist also analyze whether messages to
>> self occur and if so
>> leave the method guarded; however this bears the problem that one
>> could use an object
>> that refers to self such that the problem might surface nevertheless.
>>
>> Any thoughts, ideas, suggestions, conclusions?
>>
>> ---rony
>>
Index: interpreter/classes/PackageClass.hpp
===================================================================
--- interpreter/classes/PackageClass.hpp (revision 12284)
+++ interpreter/classes/PackageClass.hpp (working copy)
@@ -181,6 +181,8 @@
inline void enableProlog() { packageSettings.enableProlog(); }
inline void disableProlog() { packageSettings.disableProlog(); }
inline bool isPrologEnabled() { return packageSettings.isPrologEnabled()
&& initCode != OREF_NULL; }
+ inline void setUnguarded() { packageSettings.setUnguarded(); }
+ inline bool isUnguarded() { return packageSettings.isUnguarded(); }
inline RoutineClass *getMain() { return (RoutineClass *)mainExecutable; }
RexxString *getTrace();
Index: interpreter/execution/PackageSetting.hpp
===================================================================
--- interpreter/execution/PackageSetting.hpp (revision 12284)
+++ interpreter/execution/PackageSetting.hpp (working copy)
@@ -63,6 +63,7 @@
LostdigitsSyntax,
NostringSyntax,
NotreadySyntax,
+ Unguarded,
} PackageFlags;
@@ -83,6 +84,7 @@
traceSettings.setDefault();
}
+
inline void setDigits(size_t d) { numericSettings.setDigits(d); }
inline size_t getDigits() const { return numericSettings.getDigits(); }
inline void setForm(bool f) { numericSettings.setForm(f); }
@@ -114,6 +116,8 @@
inline void enableProlog() { packageOptions[NoProlog] = false; }
inline void disableProlog() { packageOptions[NoProlog] = true; }
inline bool isPrologEnabled() { return !packageOptions[NoProlog]; }
+ inline void setUnguarded() { packageOptions[Unguarded] = true; }
+ inline bool isUnguarded() { return packageOptions[Unguarded]; }
NumericSettings numericSettings; // the package numeric settings
TraceSetting traceSettings; // the package trace setting
Index: interpreter/parser/DirectiveParser.cpp
===================================================================
--- interpreter/parser/DirectiveParser.cpp (revision 12284)
+++ interpreter/parser/DirectiveParser.cpp (working copy)
@@ -919,6 +919,7 @@
// there is any opportunity to protect the object.
Protected<RexxCode> code = translateBlock();
+ // If ::OPTIONS UNGUARDED is stated then:
// While the general default for methods is GUARDED, for plain methods
// (no ATTRIBUTE, ABSTRACT, DELEGATE, or EXTERNAL) we choose a more
// convenient default based on whether the method really requires the
@@ -926,11 +927,14 @@
// EXPOSE or USE LOCAL, else it is UNGUARDED.
if (guardFlag == DEFAULT_GUARD)
{
- RexxInstruction *first = code->getFirstInstruction();
- if (first == NULL ||
- (!first->isType(KEYWORD_EXPOSE) &&
!first->isType(KEYWORD_USE_LOCAL)))
+ if (package->isUnguarded())
{
- guardFlag = UNGUARDED_METHOD;
+ RexxInstruction *first = code->getFirstInstruction();
+ if (first == NULL ||
+ (!first->isType(KEYWORD_EXPOSE) &&
!first->isType(KEYWORD_USE_LOCAL)))
+ {
+ guardFlag = UNGUARDED_METHOD;
+ }
}
}
@@ -1322,6 +1326,14 @@
break;
}
+ // ::OPTIONS UNGUARDED
+ case SUBDIRECTIVE_UNGUARDED:
+ {
+ // this option is just the keyword...default methods to
unguarded if not EXPOSE or USE LOCAL
+ package->setUnguarded();
+ break;
+ }
+
// invalid keyword
default:
syntaxError(Error_Invalid_subkeyword_options, token);
_______________________________________________
Oorexx-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/oorexx-devel