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
[email protected]
https://lists.sourceforge.net/lists/listinfo/oorexx-devel