I’d like to welcome guest blogger Malcolm Lidierth of King’s College London. Malcolm is well known in the Matlab-Java community for his jcontrol utility. Some months ago, I mentioned his work on another File Exchange contribution, MUtilities when I discussed transparent Matlab figure windows. Today, Malcolm discusses one of his lesser-known but extremely important isMultipleCall utility.
Every now and again, a very simple bit of code turns out to be more useful than the author initially imagined. Something I have repeatedly used is the isMultipleCall function which I posted to MATLAB Central’s File Exchange a year or so ago.
The isMultipleCall function uses fully-documented pure-MATLAB to extend the control that can be achieved over callbacks.
Here was the problem: I had a modular system built in MATLAB which allowed third-party developers to add their own plugins. I wanted a mechanism to force the dismissal (“bail-out”) of a callback even when the Interruptible property of the parent object was set to ‘on’. Such callback re-entrancy issues are common for rapidly-firing events, and debugging and fixing them is usually not easy.
The callback’s dismissal code would need to be fast because it might be called many dozens of times, e.g. in a WindowButtonMotion callback. An obvious approach was to check the function call stack using MATLAB’s dbstack function. Although, at first, this seemed likely to be too slow, profiling showed it was not – taking < 40µsec per call – and within a WindowButtonMotion callback in a real GUI, I could not perceive any slowing of the code.
Here is the function:
function flag=isMultipleCall() flag = false; % Get the stack s = dbstack(); if numel(s)< =2 % Stack too short for a multiple call return end % How many calls to the calling function are in the stack? names = {s(:).name}; TF = strcmp(s(2).name,names); count = sum(TF); if count>1 % More than 1 flag = true; end end |
With isMultipleCall invoked from another function (see note below), dbstack will return a structure with a minimum of 2 elements – the first relating to isMultipleCall itself and the second to the calling function. So with numel(s) <= 2, there can be no multiple calls and we can return false immediately thus saving time in doing any further testing. For numel(s) > 2 we simply check to see whether the calling functions referenced in s(2) appears anywhere else on the stack. If it does, then we return true; otherwise false.
Then, in our callback code we simply use:
if isMultipleCall(); return; end |
If this line is placed first in the callback function code, it essentially mimics the behavior that you might expect after setting the Interruptible property of the event firing object to ‘off’. Adding a drawnow() at the end of the callback will ensure that any waiting callbacks in the queue are dismissed:
function MyCallback(hObj, EventData) % Quick bail-out if callback code is called before another has ended if isMultipleCall(); return; end ... % do some actual callback work here drawnow(); end |
There are several ways in which isMultipleCall can extend the standard MALAB functionality. First, by moving isMultipleCall reference from the first line of the callback we can create both an interruptible and an uninteruptible code block, e.g.
function MyCallback(hObj, EventData) %Code Block 1 ... if isMultipleCall(); return; end %Code Block 2 ... drawnow(); end |
Second, as isMultipleCall controls the callbacks – not the objects that trigger them – we can individually control the callbacks of objects which fire multiple events. That is particularly useful with Java components, which gives a third extension – isMultipleCall can be used in any function: not just the callbacks of standard MATLAB components, but also of Java or COM components.
Finally, as the callback, not the object is being controlled, we can control a callback that may be shared between multiple objects e.g. a menu component and a toolbar button.
Not bad for 13 lines of code.
Note: isMultipleCall must be called from a function, not from a string in the callback property.
Do you have any other favorite mechanism for controlling callback re-entrancy? If so, please post a comment.
My approach is instead of calling
inside of callback, use following syntax:
where function
is responsible for interrupting if called multiple times and always caching last varargin so that it is not getting lost.
6 lines 🙂
@Arda – that’s certainly faster by about 10-fold, but would be less easy to maintain e.g. with multiple return points. Perhaps the challenge should be to achieve this with less than one line in the callback!
@Malcolm, it was just a joke. Sorry for the misunderstanding. Yet the code i wrote above does not work as i suspected..
@Arda
Your code worked for me in a WindowButtonMotionFcn relacing …doSomething with
That example simulates the effect of setting ‘Interruptible’ to ‘off’ and ‘BusyAction’ to ‘cancel’ – but via the callback rather than the object. If isMultipleCall() took up a large proportion of the execution time, an approach like this would be far better – the processor overhead for your code is tiny. Another tack is to clear the callback property at the start and reset it at the end of the callback routine – but if any error or ^C occurs while its cleared how would you reset it?
@Malcolm, you cannot clear the callback property value from within an executing callback, AFAIK. At least, it didn’t work a few releases ago and since it made sense I did not try again lately. The property appears to change, but then reverts to its previous value when the callback ends.
@Yair
That surprises me. You certainly can with a figure WIndowButtonMotion callback The code below runs just once after using set(figH, ‘WindowButtonMotionFcn’, @MyCallback).
I also used a solution almost similar to Arda’s (using a persistent variable). After reading this post I opted for the isMultipleCall() because, like Malcolm very very wisely says, I had problems when internal callback errors (which were not fatal in my case) or Ctrl+C stopped the callback that I was controlling for recursive call. In this case the callback could not be run again unless I explicitly cleared all variables.
By the way, if the handle has properties ‘BusyAction’ and ‘Interruptable’ (like figure windows, uicontrols, etc.), setting ‘cancel’ and ‘off’ is always a choice. That should do the same…
This utility is very interesting, but due to the use of ‘dbstack’ it wouldn’t work in compiled applications.
Do you know of any easy workaround for that?
Thank you very much
@Michele – you could store a temporary flag in a persistent variable or the handle’s ApplicationData/UserData, or a variant of these. For example:
@Yair – Thank you very much for the quick reply, I’ll definitely give it a try.
This is very interesting. But how would you handle the case when you don’t want to bail out of the callback if it’s already executing, but you want to wait instead for the first instance to finish? The obvious solution of putting a polling loop does not work because the loop will keep Matlab from finishing the execution of the first instance. I can’t seem to find a solution to that problem!
Thanks!
@Rafael – if the callback’ed object supports the BusyAction and Interruptable properties, then setting these would be the easiest choice. Otherwise, you could preserve the callback parameters in some persistent data struct (e.g., a cell array) and set a timer object to run the callback function programmatically in a second or two.
Thanks a lot for the answer @Yair!
I’m not sure your solution of using a timer to re-execute the function will work for me. I have thought of that solution for a while, but let’s say you have two things that must happen in a particular order, and only the first one is the one that must be re-executed by the timer. How do I hold the execution of the second process until the first one has for sure executed? I get back to the polling loop problem! Would I have to convert all of my processes into timers that are constantly firing to check if it’s OK to execute in the right order? My whole system would be come a nightmare if I have to do that!
This has been a really useful post and has really helped out a Matlab GUI that I have.
I’m confused on one count though, and I’m sure this is just my lack of understanding of how Matlab runs behind the scenes. Why does this not work with a figure’s ResizeFcn callback? I would really like to have a custom resize, but it needs to be safe against re-entrancy. Currently, I’d handle this with a timer, but I’d rather not have to use that.
Is there a way to get this to work with the ResizeFcn?
Thanks.
Is there a reason why this function was removed from the Matlab File Exchange?
I prefer a simplified DBSTACK solution: