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 Oorexx-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/oorexx-devel