In App Designed app: When does a function called within another function spawn a new thread?

14 views (last 30 days)
I'm having SO much trouble! I think maybe when I call a function from within another function or callback it's going into a new thread.
I want it to finish the function before moving on. Is the best way to do that to make the function return a (dummy) value?
Since there is nothing to return but completion do I need to declare
function finish = MyFunction(app);
does finish need a value assigned at the end?
or just short cut and call
fin = MyFunction %#ok suppress mLint about unused return value?
  12 Comments
Gavin
Gavin on 18 Sep 2024
Thanks for your help guys. Yes I suppose I need more (mutex?) flags to be sure the next trial doesn't start.
I've actually tried similar things (as well as inserting strategic coder.inline('never'); but I'm still having trouble. Could be I put the IsRunning flag in the wrong place so I'll try it again as @Mario Malic outlined above
I called the help desk and outlined my various issues and they (Jack Meehan is lead) are working on it.
I'll let you know when it's resolved but if the details are too much I'll probably just give you the case number (if that's something you can access) if you are interested.
Thanks again.
Gavin
Gavin on 18 Sep 2024
I just heard from Jack with some suggestions to try. The trial running flag thing didn't work on the first attempt. I need to figure out more places to check it and not start new trials even if they will return immediately. Doing it mindlessly, turning off app.TrialRunning in the EndOfTrial wrapup routine cause a complete hang of App Designer and the program requiring CTRL-ALT-DEL
I've got a few more things to try.

Sign in to comment.

Accepted Answer

Gavin
Gavin on 23 Sep 2024
See where @Umar laid it out very well my related issue and provided several solutions to that part of the problem
At issue here was that I was calling the "main line" function ManageResults() from the end of the callback to handle the USB input.
Insightfully, @Umar said above
However, if you re-enter the function that contains a breakpoint while it’s paused (for instance, if the function is called again due to an event), it will hit the same breakpoint again each time it reaches that point in code execution. This could indeed lead to what seems like "piling up" of breakpoints if you continuously trigger the function before resolving the initial breakpoint.
and that is indeed what was happening. It looked like results weren't being processed with a break because of the recursion.
The solution I finally figured out, recalling what I would do in C, and any real time program with interrupts, is that one has to separate the callback function from the completion routine. So the callback just puts the Pico results into a simple circular buffer and then ManageResults() is in it's own loop taking care of whatever is in the buffer and waiting (patiently) to process as needed until the End of trial signal arrives.
As my mother used to say "onward and upward"
  1 Comment
Umar
Umar on 24 Sep 2024
Hi @Gavin,
Thank you for sharing your insights regarding the callback function management and the related issues you encountered. I appreciate your detailed explanation of how my feedback helped clarify the situation.
It is indeed crucial to understand the implications of re-entering a function with active breakpoints, as this can lead to unintended complications in code execution. Your approach to separate the callback function from the completion routine is commendable. Utilizing a circular buffer for managing results effectively allows for smoother processing without getting caught in recursion, thereby enhancing system stability. I am glad to hear that you found a solution that resonates with your experience in C programming and real-time systems.
It reflects a thoughtful application of concepts across different programming paradigms.
If there are any further developments or additional assistance you require, please do not hesitate to reach out.

Sign in to comment.

More Answers (1)

Gavin
Gavin on 20 Sep 2024
I had several confounding issues for example that RunTrial() just starts a trial then returns; so waiting until its really finished in the right spot was important.
@Umar was very helpful in understanding more about callbacks. It is unfortunate that the debugger with dbstack command doesn't help as it can only show the one thread that the breakpoint is in. Is there a way to get info about all the running threads in a running app from the command window?
One remaining question is about pause(1); if I start like this
function RunTrial(app,trlNum) % Don't run until Trial is complete
while app.TrialRunning
pause(1);
AddDebugText(app,"Trial still running");
end
% Continue with setting up the next trial
will the events (USB) keep being processed so the trial can actually finish? (Seems so)
What about if I am running the debbugger in AD? It seems that too much gets halted when I hit a breakpoint or should it be just that thread.
What if I re-enter the function that is paused? Will it hit the same breakpoint again (if the path through the routine with the break is hit) and pile up breaks? I think that is what was confusing me. I'd hit stop and it would just seem to sit at the same point until I stopped again.
I'd need to set a breakpoint that is "out of the way" so the later passes can keep running if needed.
Phew! What a tangle I created!
Thanks for all your help.
  1 Comment
Umar
Umar on 20 Sep 2024

Hi @Gavin,

First, let me Thankyou for your compliment, @Umar was very helpful in understanding more about callbacks. After reviewing your recent posted comments, please see my response below.

Let me address your first query, “Is there a way to get info about all the running threads in a running app from the command window?”

Please refer to documentation provided below.

https://www.mathworks.com/matlabcentral/answers/2130561-display-active-threads-that-the-app-uses

Now addressing your query regarding, “One remaining question is about pause(1); if I start like this

function RunTrial(app,trlNum)  % Don't run until Trial is     complete
while app.TrialRunning
  pause(1);
  AddDebugText(app,"Trial still running");  
end
% Continue with setting up the next trial

will the events (USB) keep being processed so the trial can actually finish? (Seems so)What about if I am running the debbugger in AD? It seems that too much gets halted when I hit a breakpoint or should it be just that thread. What if I re-enter the function that is paused? Will it hit the same breakpoint again (if the path through the routine with the break is hit) and pile up breaks? I think that is what was confusing me. I'd hit stop and it would just seem to sit at the same point until I stopped again.I'd need to set a breakpoint that is "out of the way" so the later passes can keep running if needed.”

Please see my detailed response to your second query.

Behavior of pause(1) in Your Code

After reviewing the pause function documentation provided at the link below

https://www.mathworks.com/help/matlab/ref/pause.html

So, analyzing your provided code snippets, when you will use pause(1); inside your RunTrial function, MATLAB will indeed halt execution of that particular thread for 1 second. However, it's important to note that while this thread is paused, other threads (including event handling threads) can continue to process events. This means that USB events or other asynchronous tasks should still be handled, allowing your trial to complete even if the main thread is temporarily inactive. For instance, if you have an event listener set up for USB input, it should still receive and process data during the pause period.

Debugging with Breakpoints

When debugging with breakpoints in MATLAB, only the thread where the breakpoint is hit will be halted. This means that if your application is multi-threaded, other threads can continue executing unless they are also stopped by hitting a breakpoint. However, if you re-enter the function that contains a breakpoint while it’s paused (for instance, if the function is called again due to an event), it will hit the same breakpoint again each time it reaches that point in code execution. This could indeed lead to what seems like "piling up" of breakpoints if you continuously trigger the function before resolving the initial breakpoint.

Setting Breakpoints Strategically

To avoid confusion with multiple breakpoints being hit, you may consider setting conditional breakpoints or placing breakpoints at points in your code where they won't be hit frequently during normal operation. For example, you could place breakpoints at the beginning of a new trial setup rather than within the loop that processes trials. Additionally, using dbstop if error can help catch errors without halting execution unnecessarily on every iteration.

I would advise when you debug complex applications, wrap code sections in try-catch blocks because it can help manage unexpected errors without completely halting execution flow.

Please let me know if you have any further questions for us.

Sign in to comment.

Categories

Find more on Properties in Help Center and File Exchange

Products


Release

R2023b

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!