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 &quot;I&quot; (Indivdual) 
that the arguments
    *                     are listed individually, &quot;A&quot; (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 &quot;I&quot; (Indivdual) 
that the arguments
    *                     are listed individually, &quot;A&quot; (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

Reply via email to