bugGNU Octave - Bugs: bug #52804, "Interruptible" and...

 
 

bug #52804: "Interruptible" and "BusyAction" properties

Submitter:  Rik <rik5>
Submitted:  Thu 04 Jan 2018 05:00:05 AM UTC
   
 
Category:  Plotting Severity:  3 - Normal
Priority:  5 - Normal Item Group:  Matlab Compatibility
Status:  Fixed Assigned to:  None
Originator Name:  Open/Closed:  * Closed
Release:  * dev Operating System:  * GNU/Linux
Fixed Release:  None Planned Release:  None
* Mandatory Fields

Add a New Comment Rich Markup
   

Jump to the original submission

Mon 22 Jan 2018 07:00:51 AM UTC, comment #14: 

I checked in your cset here http://hg.savannah.gnu.org/hgweb/octave/rev/58dd3c2d5db8.  I did change some of the wording of the documentation, but it is just a difficult topic to explain successfully.

Rik <rik5>
Group administrator
Sun 21 Jan 2018 08:50:35 PM UTC, comment #13: 

I attached an updated patch that should compile this time (sorry!), in which I added some documentation (don't hesitate to rephrase) and also modified _get_frame_ to be ML compatible. I attached also the two callback functions I used for testing:

  • cb.m is a lengthy callbacks that periodically executes "drawnow", "getframe", "pause", "waitfor" or "figure" and writes into appdata. It may also be used to test that deletefcn, resizefcn are never queued.
  • cb2.m also writes into appdata each time it is called.



clf
set (gca, 'buttondownfcn', @cb, 'interruptible', 'on')
hui = uicontrol ('style', 'pushbutton', 'callback', 'disp (''button'')', 'busyaction', 'queue')


With the patch Octave works like Matlab again (I hope), the only difference now is that I am aware of it :-).

(file #43000, file #43001, file #43002)

Pantxo Diribarne <pantxo>
Group Member
Fri 19 Jan 2018 11:18:35 PM UTC, comment #12: 

The patch applies cleanly but when I try to compile I get an error.


libinterp/corefcn/graphics.cc: In member function ‘void gh_manager::do_execute_callback(const graphics_handle&, const octave_value&, const octave_value&)’:
libinterp/corefcn/graphics.cc:10323:15: error: ‘go’ was not declared in this scope
           if (go)


This is true.  Previously "go" was declared in the function, rather than in its own scoping block, so it was available until the function scope was removed.

Rik <rik5>
Group administrator
Fri 19 Jan 2018 03:22:32 PM UTC, comment #11: 

Ok, I just realized that the "if (true)" block creates a scope. When quitting the block, the "guard" variable is deleted and the the mutex is thus unlocked. This doesn't have anything to do with  a race condition.
The attached cset fixes the issue for me (I could have used "gh_manager::unlock ()" but I would have been left with unused variables) and reverts the previous cset for listeners.

(file #42974)

Pantxo Diribarne <pantxo>
Group Member
Fri 19 Jan 2018 02:15:59 PM UTC, comment #10: 

@Rik: can you confirm my observations from comment #8 and #9? It turns out to be due to be a race condition so you may not notice the regression on your system.
In this cset http://hg.savannah.gnu.org/hgweb/octave/rev/41cf6ee90cb6 I removed a "if (true)" block. I am surprised that adding it back "fixes" the regression (I though the compiler would simply eliminate the if condition in such situation): I can reliably run the example from comment #8.

Pantxo Diribarne <pantxo>
Group Member
Thu 18 Jan 2018 07:48:02 PM UTC, comment #9: 

Actually this is a regression, Octave 4.2.1 behaves exactly like Matlab on my system and the message from the axes callback is displayed every 5s, as expected.

Pantxo Diribarne <pantxo>
Group Member
Thu 18 Jan 2018 12:42:47 PM UTC, comment #8: 

Here is a much better test (the cb.m function is attached) that I could check in ML:


clf
set (gca, 'buttondownfcn', @cb, 'interruptible', 'on')
hui = uicontrol ('style', 'pushbutton', 'callback', 'disp (''button'')', 'busyaction', 'queue')


First click inside the axes to trigger the execution of the lengthy axes callback. Then click multiple times on the button to see what happens.
Here is what I observe in Octave

  • whatever the value of "interruptible" and "busyaction", the uicontrol callbacks are always executed after the axes buttondownfcn has returned.
  • the messages only appear onscreen once all the callbacks have been executed.
  • clicking the button while the axes callback is executing doesn't visually look like the button reacts, even though the click is actually taken into account



In Matlab:

  • interruptible->on, busyaction->queue: uicontrol callbacks are executed each time drawnow is called (if you have clicked 3 times the button in  5 seconds then you see 3 "button" between 2 "processing queued callbacks")
  • interruptible->off, busyaction->queue: uicontrol callbacks are all executed after the axes callback as returned (drawnow has no effect)
  • interruptible->off, busyaction->cancel: uicontrol callbacks are not executed



(file #42953)

Pantxo Diribarne <pantxo>
Group Member
Thu 18 Jan 2018 11:00:23 AM UTC, comment #7: 


>> The question now is does "Interruptible" and "BusyAction" work for the XXXFcn correctly


I guess that the answer is no: I don't know that Octave's interpreter can be interrupted in the middle of the evaluation of a program (except for debugging). To my knowledge, there is no such thing as a signal saying the interpreter it should interrupt its operations and evaluate something else before proceeding (it would be useful though, so that one can "pause" a lengthy operation and resume it later).
The following shows that callbacks are always queued and eventually executed when drawnow is called.


plot (1:10)
hui = uicontrol ("style", "pushbutton", "callback", "disp ('button')")
tic ()
while (1)
  if (toc > 5)
    tic ()
    disp ("processing queued callbacks")
    drawnow ()
  endif
endwhile


Try to click multiple times on the button and you'll see that the messages appear all at once, when drawnow is called in the main program.

Pantxo Diribarne <pantxo>
Group Member
Thu 11 Jan 2018 07:57:06 PM UTC, comment #6: 

Here is a list of callback functions that Matlab recognizes: http://www.mathworks.com/help/matlab/creating_guis/write-callbacks-using-the-programmatic-workflow.html

You are right that these are either all of the type "XXXFcn" such as "ButtonDownFcn" or all of the type "Callback" such as "ClickCallback".

I agree that we should not apply these properties to listeners.

The question now is does "Interruptible" and "BusyAction" work for the XXXFcn correctly?

And two, if you revert the code do listeners always run through to completion or can they be interrupted by other property listeners?  I don't think listener's should be able to be interrupted.

Rik <rik5>
Group administrator
Thu 11 Jan 2018 10:11:51 AM UTC, comment #5: 

@Rik: the behavior you describe is ridiculously complicated for property listeners.

In fact I am now pretty sure "interruptible" should not apply to property listener callbacks:

  • all the examples provided in the ML doc only refer to regular callbacks ("XXXfcn", or "callback" properties), which are almost all triggered by user interaction, thus from the GUI thread
  • property listerners are a more general concept in ML [1] since you can apply them to any classdef handle object, with or wo an "interruptible" property.


I am in favor of backing out the cset.

[1] https://fr.mathworks.com/help/matlab/ref/handle.addlistener.html?requestedDomain=true

Pantxo Diribarne <pantxo>
Group Member
Fri 05 Jan 2018 10:08:05 PM UTC, comment #4: 

@Pantxo: Duh, you're quite right.  The BusyAction property is only for the interrupting callback.  I did have another question, however.  As I read the documentation, even when "Interruptible" is "on" an interrupting callback is not executed immediately. 


When an object’s Interruptible property is set to 'on', its callback can be interrupted at the next occurrence of one of these commands: drawnow, figure, getframe, waitfor, or pause.

*    If the running callback contains one of these commands, then MATLAB stops the execution of the running callback and executes the interrupting callback. MATLAB resumes executing the running callback when the interrupting callback completes.

*    If the running callback does not contain one of these commands, then MATLAB finishes executing the callback without interruption.


If I understand that correctly, most callbacks will execute all the way through because they are short and don't contain one of the five listed commands.

A sample test case is attached as tst_interrupt4.m



close all;
graphics_toolkit qt

hl = line ();

lsn1 = @() disp ("line listener");

function lsn2 (h, evt, hl)
  disp ("begin axes listener");

  set (hl, "linewidth", get (h, "linewidth"));

  a = 1;
  a += 2;

  disp ("before drawnow");
  drawnow ();  % Should now run queued interrupts

  a += 3;

  disp ("end axes listener");
endfunction

addlistener (hl, "linewidth", lsn1);
addlistener (gca, "linewidth", {@lsn2, hl});

disp ("Interruptible : ON");
set (gca, "linewidth", 2);
drawnow ();

disp ("\nInterruptible : OFF, BusyAction : QUEUE");
set (gca, "interruptible", "off");
set (gca, "linewidth", 3);
drawnow ();

disp ("\nInterruptible : OFF, BusyAction : CANCEL");
set (hl, "busyaction", "cancel");
set (gca, "linewidth", 4);
drawnow ();


The results under Octave show that any interrupting callback is executed immediately.


Interruptible : ON
begin axes listener
line listener
before drawnow
end axes listener

Interruptible : OFF, BusyAction : QUEUE
begin axes listener
before drawnow
end axes listener
line listener

Interruptible : OFF, BusyAction : CANCEL
begin axes listener
before drawnow
end axes listener



Rik <rik5>
Group administrator
Fri 05 Jan 2018 09:29:47 AM UTC, comment #3: 

@Rik: the busyaction property that is taken into acount is the one of the interrupt*ing* callback object, as stated in the ML doc

>> ...BusyAction property (of the object owning the interrupting callback)...


so your extended example should be


close all;
graphics_toolkit qt

hl = line ();

lsn = @() disp ("line listener");

function lsn2 (h, evt, hl)
  disp ("begin axes listener");
  set (hl, "linewidth", get (h, "linewidth"));
  disp ("end axes listener");
endfunction

addlistener (gca, "linewidth", {@lsn2, hl});
addlistener (hl, "linewidth", lsn);

disp ("Interruptible : ON");
set (gca, "linewidth", 2);
drawnow ();

disp ("\nInterruptible : OFF, BusyAction : QUEUE");
set (gca, "interruptible", "off");
set (gca, "linewidth", 3);
drawnow ();

disp ("\nInterruptible : OFF, BusyAction : CANCEL");
set (hl, "busyaction", "cancel");
set (gca, "linewidth", 4);
drawnow ();


Pantxo Diribarne <pantxo>
Group Member
Fri 05 Jan 2018 03:51:57 AM UTC, comment #2: 

This is important so I pushed the cset here (http://hg.savannah.gnu.org/hgweb/octave/rev/a56d283ff18a).

However, I still don't think it is handling a BusyAction of Cancel correctly.  In my understanding, the interrupting callback is simply discarded and never executed.  However, in Octave Queue and Cancel produce the same results.  I modified your test script to create tst_interrupt2.m which is attached.


close all;
graphics_toolkit qt

hl = line ();

lsn = @() disp ("line listener");

function lsn2 (h, evt, hl)
  disp ("begin axes listener");
  set (hl, "linewidth", get (h, "linewidth"));
  disp ("end axes listener");
endfunction

addlistener (gca, "linewidth", {@lsn2, hl});
addlistener (hl, "linewidth", lsn);

disp ("Interruptible : ON");
set (gca, "linewidth", 2);
drawnow ();

disp ("\nInterruptible : OFF, BusyAction : QUEUE");
set (gca, "interruptible", "off");
set (gca, "linewidth", 3);
drawnow ();

disp ("\nInterruptible : OFF, BusyAction : CANCEL");
set (gca, "busyaction", "cancel");
set (gca, "linewidth", 4);
drawnow ();


The results are


tst_interrupt2
Interruptible : ON
begin axes listener
line listener
end axes listener

Interruptible : OFF, BusyAction : QUEUE
begin axes listener
end axes listener
line listener

Interruptible : OFF, BusyAction : CANCEL
begin axes listener
end axes listener
line listener


The BusyAction property doesn't seem to make a difference.  I believe this is because there may be a way to execute a callback directly after a property is modified in this routine


void
callback_property::execute (const octave_value& data) const
{
  octave::unwind_protect frame;

  // We are executing a callback function, so allow handles that have
  // their handlevisibility property set to "callback" to be visible.

  frame.add_method (executing_callbacks, &callback_props::erase, this);

  if (! executing_callbacks.contains (this))
    {
      executing_callbacks.insert (this);

      if (callback.is_defined () && ! callback.isempty ())
        gh_manager::execute_callback (get_parent (), callback, data);
    }
}


This documentation was useful on how the property should behave: https://www.mathworks.com/help/matlab/creating_guis/callback-sequencing-and-interruption.html.



(file #42840)

Rik <rik5>
Group administrator
Thu 04 Jan 2018 02:43:45 PM UTC, comment #1: 

The attached cset fixes the issue for me. My test code was


hl = line ();

lsn = @() disp ("line listener");
function lsn2 (h, evt, hl)
  disp ("begin axes listener")
  set (hl, "linewidth", get (h, "linewidth"))
  disp ("end axes listener")
endfunction

addlistener (gca, "linewidth", {@lsn2, hl})
addlistener (hl, "linewidth", lsn)

set (gca, "linewidth", 2)
set (gca, "interruptible", "off")
set (gca, "linewidth", 3)



(file #42825)

Pantxo Diribarne <pantxo>
Group Member
Thu 04 Jan 2018 05:00:05 AM UTC, original submission:  

It's kind of hard to say, but I don't think Octave is implementing the "Interruptible" and "BusyAction" properties.

It would be useful if someone could run the attached script tst_callback_interrupt.m script.


close all;

function cb_tag (hax, ~)
  fprintf (2, 'cb_tag: callback executed\n');
%  fflush (2);
endfunction

function cb_userdata (hax, ~)
  fprintf (2, 'cb_userdata: callback started\n');
%  fflush (2);
  set (hax, 'tag', 'foo');
  fprintf (2, 'cb_userdata: called set() once\n');
%  fflush (2);
  set (hax, 'tag', 'bar');
  fprintf (2, 'cb_userdata: called set() twice\n');
%  fflush (2);
  set (hax, 'tag', '');
  fprintf (2, 'cb_userdata: called set() thrice\n');
%  fflush (2);
endfunction

hf = figure ();
hax = gca ();
set (hax, 'interruptible', 'off');
addlistener (hax, 'tag', @cb_tag);
addlistener (hax, 'userdata', @cb_userdata);

set (hax, 'busyaction', 'queue');

fprintf (2, ['\n' '"busyaction" = "queue"' '\n']);
fprintf (2, ['changing "userdata"' '\n']);
set (hax, 'userdata', 5);

set (hax, 'busyaction', 'cancel');

fprintf (2, ['\n' '"busyaction" = "cancel"' '\n']);
fprintf (2, ['changing "userdata"' '\n']);
set (hax, 'userdata', 0);


Under Octave, this produces:


"busyaction" = "queue"
changing "userdata"
cb_userdata: callback started
cb_tag: callback executed
cb_userdata: called set() once
cb_tag: callback executed
cb_userdata: called set() twice
cb_tag: callback executed
cb_userdata: called set() thrice

"busyaction" = "cancel"
changing "userdata"
cb_userdata: callback started
cb_tag: callback executed
cb_userdata: called set() once
cb_tag: callback executed
cb_userdata: called set() twice
cb_tag: callback executed
cb_userdata: called set() thrice


First, with "interruptible" turned off I don't think the cb_tag callback should be executed during the running of cb_userdata.  But even if that is iscorrect, why is there no difference when the "BusyAction" property is changed?

Rik <rik5>
Group administrator

 

(Note: upload size limit is set to 16384 kB, after insertion of the required escape characters.)

Attach Files:
   
   
Comment:
   

Attached Files
file #43001:  cb.m added by pantxo (957B - text/x-objcsrc)
file #43002:  cb2.m added by pantxo (178B - text/x-objcsrc)
file #42974:  revert_busyaction.patch added by pantxo (2KiB - text/x-patch)
file #42953:  cb.m added by pantxo (190B - text/x-objcsrc)
file #42840:  tst_interrupt2.m added by rik5 (637B - text/x-matlab)
file #42825:  interupt_callback.patch added by pantxo (2KiB - text/x-patch)
file #42818:  tst_callback_interrupt.m added by rik5 (958B - text/x-matlab)

 

Depends on the following items: None found

Items that depend on this one: None found

 

Carbon-Copy List
  • -email is unavailable- added by pantxo (Updated the item)
  • -email is unavailable- added by rik5 (Submitted the item)
  •  

    There are 0 votes so far. Votes easily highlight which items people would like to see resolved in priority, independently of the priority of the item set by tracker managers.

    Only group members can vote.

     

    Follow 12 latest changes.

    Date Changed by Updated Field Previous Value => Replaced by
    2018-01-22 rik5 StatusIn Progress Fixed
        Open/ClosedOpen Closed
    2018-01-21 pantxo Attached File- Added revert_document_busyaction.patch, #43000
        Attached File- Added cb.m, #43001
        Attached File- Added cb2.m, #43002
    2018-01-19 pantxo Attached File- Added revert_busyaction.patch, #42974
    2018-01-18 pantxo Attached File- Added cb.m, #42953
    2018-01-11 rik5 StatusPatch Submitted In Progress
    2018-01-05 rik5 Attached File- Added tst_interrupt2.m, #42840
    2018-01-04 pantxo Attached File- Added interupt_callback.patch, #42825
        StatusNone Patch Submitted
    2018-01-04 rik5 Attached File- Added tst_callback_interrupt.m, #42818

    Back to the top

    Powered by Savane 3.12.
    Corresponding source code