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