On Sun, Aug 18, 2013 at 10:59 AM, Art Heimsoth <artstore@artheimsoth.com> wrote:
Is there an explanation on when unguarded should be used on a method,
or when it should not be used? 

Hi Art,

First, I'll give you some areas to read in the ooRexx reference manual and in ooDialog, then I'll try to add a little explanation.

To understand guarded / unguarded you need to know it is related to concurrency.  So, you should read the section Chapter 12 Concurrency to get an idea of how ooRexx implements concurrency.

Guarded / UnGuarded is explained in Chapter 3 Directives, section 3.4 Methods, where guarded/ungurarded is briefly explained in the notes at the end of the section.

To understand how concurrency relates to ooDialog you need to understand events and event handlers in ooDialog.  You should read 3.6 Event Notification Mixin Class, 3.6.1 Connecting Event Handlers, and 3.6.2 Coding Event Handlers.  

In addtion, in Chapter 1 Brief Overview, some basic concepts are explained.  Reading section 1.3.3 Events and section 1.3.14 Window Messages should help.

 
Or alternatively what it does vs what is
done without it?  It doesn’t appear to be a blocking protection on the
method if not present, but does appear to do some sort of serialization
if not present

Basically when a method of an object is guarded no other guarded method of that object can run.  When a method is unguarded it can run even if some other method in the object is executing.  

Nothing is ever as simple as "basically ..."  One exception to the above is that a guarded method can invoke another guarded method in the object and that method will execute.  I.e.:

::class 'example'
::method a 

  -- do stuff
  self~b()
  -- do more stuff
 
::method b
  -- do stuff
  return

So, even though both methods a and b are guarded, when b() is invoked from method a() it executes.
 
– but I am confused on how to figure out when to and
when not to use it on a method. 


Concurrency is not simple, so there are no simplistic statements that will make everything crystal clear.

In general, one use of guarded is to prevent non-deterministic access to the object's variables.  In general I make every method guarded until I see that a method needs to be unguarded.

In ooDialog, I have a general statement: make every event handler unguarded.

To understand this, you need to understand the window processing loop.  The OS sends a message to a window, the window's message processing loop does something in response to the message and returns a value to the OS.  The message processing loop then processes the next message.

The OS stacks the messages up in a queue, the processing loop pulls them out of the queue one by one.

Let's say the user clicks on a button in a dialog, then uses the mouse to move the dialog up 1 pixel.

If, in the window processing loop, the application sleeps for 40 seconds when it processes the button click and then returns a value to the OS.  For 40 seconds, the message to move the dialog up 1 pixel sits in the queue unprocessed.  To the user, it appears that she can not move the dialog with the mouse.

Back to ooDialog specific.   The ooDialog framework starts a window message processing loop, in a separate thread, for every dialog you the programmer create.  Within that processing loop, for every message, the ooDialog framework returns a value to the OS that essentially means: 'do the default for this message', *unless*, you the programmer have connected that message to an event handler.

In the message processing loop, when the ooDialog framework sees a message that has been "connected", the framework invokes the method in the dialog that the programmer connected.

Take this simple dialog:

/* Simple User Dialog to produce a dialog that will appear hung */

  .application~useGlobalConstDir('O')
  .constDir[IDC_PB_ONE] = 100
  .constDir[IDC_PB_TWO] = 110

  dlg = .SimpleDialog~new
  dlg~execute("SHOWTOP", IDI_DLG_OOREXX)

::requires "ooDialog.cls"

::class 'SimpleDialog' subclass UserDialog

::method init
  forward class (super) continue

  self~create(30, 30, 257, 123, "Simple Dialog", "CENTER")

::method defineDialog

  self~createPushButton(IDC_PB_ONE, 10, 10, 50, 14, "", "Push Me Now", 'buttonClick1')
  self~createPushButton(IDC_PB_TWO, 10, 30, 75, 14, "", "Push Me Now Also", 'buttonClick2')
  self~createPushButton(IDOK, 142, 99, 50, 14, "DEFAULT", "Ok")
  self~createPushButton(IDCANCEL, 197, 99, 50, 14, , "Cancel")


::method buttonClick1
  say 'in buttonClick1()'
  j = SysSleep(40)
  say 'leaving buttonClick1()'


::method buttonClick2
  say 'in buttonClick2()'
  j = SysSleep(40)
  say 'leaving buttonClick2()'


::method ok
  say 'in ok()'
  j = SysSleep(40)
  say 'leaving ok()'
  self~ok:super

::method cancel
  say 'in cancel()'
  j = SysSleep(40)
  say 'leaving cancel()'
  self~cancel:super

Run the dialog and click the 'Push Me Now' button immediately.  Then push any other button, nothing will happen.  Typically a user will then start pushing other buttons.  Try to close the dialog by pushing ok or cancel, you won't be able to.  The more times you push the buttons or try to close the dialog, the longer it will be before the program finally ends.

What happens is:  you push the 'Push Me Now' button, the guarded buttonClick1() method starts executing.  You push the 'Push Me Now Also' button, the guarded buttonClick2() is invoked, but can not run until all other guarded methods are not running.

You, the user, get exasperated and push the cancel button.  The guarded cancel() method is invoked by the framework - but it can not run until all other guarded methods are not running.

At this point, the dialog is not going to end for 120 seconds.  The ooRexx interpreter is not going to end until all invoked methods have completed.  The more you try to close the dialog, the worse your situation gets.

Now make all the event handling methods unguarded.  Each time you click a button, you will immediately see the 'in methodName()' message.  This shows that all the event handlers are now running concurrently.

You still won't see the program end immediately when you push the ok or cancel program because you have started all these 40 second threads.  But, if you push 5 buttons pretty quickly you will see the program end in about 40 seconds rather than 200 seconds.

Now, in the guarded version, after you push the first button, you will still be able to move the dialog around with the mouse.  This seems to contradict what I originally said.  The reason for this is that, for button clicks, the ooDialog framework actually invokes the event handler using start() and so the message processing loop immediately returns a value to the OS and continues processing messages.

But try this program:

/* Simple User Dialog to produce a dialog that will appear hung */

  .application~useGlobalConstDir('O')
  .constDir[IDC_PB_ONE] = 100
  .constDir[IDC_PB_TWO] = 110
  .constDir[IDC_EDIT] = 120
  .constDir[IDC_UPD] = 130

  dlg = .SimpleDialog~new
  dlg~execute("SHOWTOP", IDI_DLG_OOREXX)

::requires "ooDialog.cls"

::class 'SimpleDialog' subclass UserDialog

::method init
  forward class (super) continue

  self~create(30, 30, 257, 123, "Simple Dialog", "CENTER")

::method defineDialog

  self~createPushButton(IDC_PB_ONE, 10, 10, 50, 14, "", "Push Me Now", 'buttonClick1')
  self~createPushButton(IDC_PB_TWO, 10, 30, 75, 14, "", "Push Me Now Also", 'buttonClick2')
  self~createEdit(IDC_EDIT, 10, 60, 50, 14)
  self~createUpDown(IDC_UPD, 60, 60, 15, 15, 'WRAP ARROWKEYS AUTOBUDDY SETBUDDYINT')
  self~createPushButton(IDOK, 142, 99, 50, 14, "DEFAULT", "Ok")
  self~createPushButton(IDCANCEL, 197, 99, 50, 14, , "Cancel")

  self~connectUpDownEvent(IDC_UPD, "DELTAPOS", onUPD)

::method onUPD
  say 'in onUPD()'
  j = SysSleep(40)
  say 'leaving onUPD()'
  return .UpDown~deltaPosReply

::method buttonClick1
  say 'in buttonClick1()'
  j = SysSleep(40)
  say 'leaving buttonClick1()'


::method buttonClick2
  say 'in buttonClick2()'
  j = SysSleep(40)
  say 'leaving buttonClick2()'


::method ok
  say 'in ok()'
  j = SysSleep(40)
  say 'leaving ok()'
  self~ok:super

::method cancel
  say 'in cancel()'
  j = SysSleep(40)
  say 'leaving cancel()'
  self~cancel:super

For the UpDown DELTAPOS envent, the ooDialog framework waits for the reply from the event handler.

Run this program - click the 'Push Me Now' button, immediately click an arrow in the UpdDown control, and immediately use the mouse to try and move the window.  You won't be able to.

In Windows 7, the OS will detect that your dialog is not processing messages and start the special "Window is not responding" behavior after about 5 seconds.  This allows the user to move the window and close it.  Without that special behavior things would be truly hung.  But, the way the user can close the dialog is through the Window is not responding dialog that the OS puts up.  Not real user friendly.

Not sure how much help this has been.  But, at least you should be able to see why you usually need make your event handlers unguarded.

--
Mark Miesfeld






 
If it is described somewhere, point me
to it as I did not find it when searching.  Thanks,
  Art
 
 

------------------------------------------------------------------------------
Get 100% visibility into Java/.NET code with AppDynamics Lite!
It's a free troubleshooting tool designed for production.
Get down to code-level detail for bottlenecks, with <2% overhead.
Download for free and get started troubleshooting in minutes.
http://pubads.g.doubleclick.net/gampad/clk?id=48897031&iu=/4140/ostg.clktrk
_______________________________________________
Oorexx-users mailing list
Oorexx-users@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/oorexx-users