Intermittent hang within For/Next loop

I’m plotting a simple XY graph directly to the main window (not a canvas). The data has been captured via an external device and recorded at a predetermined sample rate (100Hz in this case).

As a useful feature, the user is able to replay the graph in real time. This all seems to work well except that occasionally whilst drawing the graph, the app hangs within the For/Next loop. By “hang” I mean I get the windows blue spinning circle and also the app window moves about 10pixels to the right and down, then moves back again - that bit is weird.

There doesn’t seem to be any regularity about it, but one thing that is consistent is that it hangs between 5-6 seconds into the draw. However, the method does actually end when it should. There are no active timers, so I don’t think it’s that. The code includes a point where the user can ESC the plotting which does still work, so the for/next loop does seem to be still running.

The issue occurs within the IDE and with a built version - so it’s not that.

The Code:

DrawGraphREPLAY

[code] // Get & set the delay number
timeCheckDelay = (1000000/deviceSampleRate) // convert
timeCheckOriginal = Microseconds
timeCheckAccumulator = timeCheckOriginal + timeCheckDelay

//-------graphing

For timeX as Integer = 0 to totalSamples-1

// -------------------------------------------------------------------
forceY = timeX
X1 = (timeX * standardGridX * graphScaleFactorX) + X1setPos
Y1 = (kGraphHeight - recordedGramsArray(forceY) * standardGridY * graphScaleFactorY) + Y1setPos
X2 = ((timeX+1) * standardGridX * graphScaleFactorX) + X2setPos
Y2 = (kGraphHeight - recordedGramsArray(forceY+1) * standardGridY * graphScaleFactorY) + Y2setPos

//Draw the final graph lines
Graphics.Transparency = 25 // overlay on paper image background
Graphics.DrawLine(X1, Y1, X2, Y2)

// Force Exit Replay if Mouse is out of bounds
If MouseX < 220 OR MouseY < 40 OR MouseX > 1000 OR MouseY > 550 Then // Mouse Bounds Invalid
  MsgBox ("Mouse out of range")
  WindowMain.RefreshRect (295,89,668,408)
  Return
End If

// Allow Exit Replay on ESC key
If Keyboard.AsyncKeyDown(&h35) Then // ESC
  MsgBox ("You have forced Replay to end.")
  WindowMain.RefreshRect (295,89,668,408)
  Return
End If

ReplayDELAY // check & delay until correct

Next timeX[/code]

At the end of the for/next loop the delay is called.

ReplayDELAY

[code] timeCheckOriginal = Microseconds

While timeCheckOriginal < timeCheckAccumulator
timeCheckOriginal = Microseconds
Wend

timeCheckAccumulator = timeCheckAccumulator + timeCheckDelay[/code]

The logic part of the code to draw the graph may look messy, but it works fine so it’s not that. Also the timing is spot on.
If it was consistent then I could track it down.

Hopefully someone will see something that I’ve missed.

Cheers.

2016R3 Win7

When the delay occurs and you break into the debugger what is the app doing?

On the Mac you can examine a hanging app in the Activity Viewer. Is there something like this on the Windows side?

Are there threads that could interfere?

Otherwise you can reduce the project until you eliminate the code that causes the problems. Or you add logging statements so that you can see where the code is “hanging”.

You could use a timeOut to prevent the For Next to hang, if at least that is where the issue is :

Dim previousTicks as int64 = Ticks For timeX as Integer = 0 to totalSamples-1 if Ticks = previousTicks + 60 then exit

[quote=381550:@Beatrix Willius]When the delay occurs and you break into the debugger what is the app doing?

On the Mac you can examine a hanging app in the Activity Viewer. Is there something like this on the Windows side?

Are there threads that could interfere?

Otherwise you can reduce the project until you eliminate the code that causes the problems. Or you add logging statements so that you can see where the code is “hanging”.[/quote]

Thanks Beatrix

There are no threads, so that’s not it. The “hang” is only temporary and I already know where it occurs because I can force a break-out by using the ESC key. In other words it MUST be ocurring in the for/next loop.

Also, importantly, the method does end EXACTLY when it should. ie. I have an 11sec test file, when I replay and the “hang” occurs, the graph stops drawing to the window BUT the method ends at 11secs and the following code executes. I’ve also tested other files and if it hangs, it’s always around 5-6secs into the draw. Maybe it’s something to do with Microseconds and Windows?

[quote=381552:@Michel Bujardet]You could use a timeOut to prevent the For Next to hang, if at least that is where the issue is :

Dim previousTicks as int64 = Ticks For timeX as Integer = 0 to totalSamples-1 if Ticks = previousTicks + 60 then exit[/quote]

Thanks Michel, I’m not really sure what you mean. If I exit the code, then the graph won’t draw.

When you say “hang”, I supposed the graph was already drawn when the program continued being busy. You can set the timeout to 11 seconds, if indeed that is sufficient for the graph.

Check Profile in the Project menu. That may give you some insight. Make sure to quit normally the program to get the profile.

Thanks Michel.

The graph could be 1 minute or anything really, so I’m not sure if that would work.

However, I have something positive and a clue to the problem. By trial & error, I’ve managed to reproduce the conditions where the issue occurs.

If the application loses focus, then when selecting the app window again, and clicking replay, it ALWAYS will hang, and will thereafter when replay is selected. This will also occur even if a file has not yet been loaded.

So I thought “Aha - got ya!!” and included WindowMain.SetFocus in the button that starts the replay, but unfortunately this didn’t help.

I think I’m some way into to resolving this, but now I don’t know what to try next.

Also, during the “hang” period, if the ESC key is pressed, and therefore out of the loop, then the next replay works fine. Somehow I have to reset conditions like that when the app loses focus, then regains focus.

I forgot to post this part. This is the code where REPLAY is fired, in the MouseDown Event of a canvas button:

[code] flagGraphReplay = True

// Clear cnvGRAPH window & redraw gridlines
ClearCnvGRAPH
DrawGrid
WindowMain.RefreshRect (295,89,668,408)

// Draw the new GRAPH LIVE
DrawGraphREPLAY

// Clear cnvGRAPH window again & redraw the original graph to extents
ClearCnvGRAPH
DrawGraphToExtents

// Reset focus back to the canvas
cnvGRAPH.SetFocus

// Redraw the cursor
DrawCursorSpecial

flagGraphReplay = False[/code]

Re-engineer your draw routine to be timer based and not loop based and you will solve your problems. You or Xojo isn’t yielding enough time to process the event queue which is causing the “hangs” or thread off your replay, have it render to a temp picture and have a timer on the UI to draw that picture at certain intervals to the UI.

Thanks Julian.

I went down the path of using microseconds because Xojo’s timers do not have enough resolution. So therefore the ReplayDELAY routine IS the timer.

I already have a picture object that renders the graph then displays in a canvas. The “replay” option however draws to the main window for reasons that are too complicated to discuss at this point.

Your idea of creating a thread seems promising but sounds like a lot of work when I’m almost there.

Everything works perfectly as expected, until the user selects another window or application that causes my application to lose focus. Apart from that, all is fine - works perfect. I’ve tested on files that are 1000 samples per second and all good.

I could have a message that alerts the user to ensure they don’t ever select any other application whilst running mine, otherwise the “replay” function will not work if the recording is over 5 seconds etc… But that would be very poor programming - even I’m not that lazy!! :slight_smile:

Surely there is another way?.. I’m almost there.

[EDIT] Also important to point out that the “Replay” function is a “Novelty” and not a requirement, the idea was conceived during the process of “what my program should/must do” phase and was under the heading as “Nice to have”. The timing of the replay does not have to be perfect because it is just a “simulated” replay of what the original recording was and has no bearing or weight to the overall accuracy.

However, now that it has been implemented it would be good have it working correctly. I’m not going to discard it even though it’s pretty much just “fairy dust”.

Steve, what you are doing is NOT a timer. It is a loop. And loops hijack the app’s CPU.

What is the point in requiring ultra precise timer if you end up with a hung app ?

To replace a For Next loop by a timer, you can do that in the Action event of that multiple timer :

[code]Static timeX as integer
If timeX < totalSample-1 then
// do your drawing
timeX = timeX + 1
else
me.mode = Timer.Modeoff
timeX = 0
end if

//For timeX as Integer = 0 to totalSamples-1[/code]

In my case I have no issue with the loop hijacking the CPU. The user is reviewing the Graph in “pretend” real time. Therefore nothing else needs to happen except to ESC from the redraw if they have no interest.

[quote]What is the point in requiring ultra precise timer if you end up with a hung app ?[/quote]

Yes, that is a very good point Michel. There really is no point in having “precise” timing on the redraw. I guess it comes down to the “value” of including the whole scenario in the first place. When the graph is redrawn in “real time” there is no real value in just looking at it. It is a “novelty” factor and could be excluded.

Nevertheless, after reviewing how my program works, and doing some more thinking, I’ve come to the same conclusion as I always have, in that there IS an actual “real” benefit to the “replay” function and it’s existence does make sense.

At this point (meaning late on a Sunday Night) I can’t comprehend any coding. I will however, tomorrow and/or beyond, post some images of my software where it will become more clear what I’m doing, and why.

Cheers.

If you’re playing back some real time recording bear in mind that you’re most probably only going to be displaying it at a max of 60fps due to the screen refresh rate limitations of the hardware you’re rending on (I’m not considering monitors at >60Hz here).

That means you have 1000ms / 60 or 16.67ms to render the plot of the line since your last render which should be plenty of time.

You might have taken 1000 data samples during this 16.67ms so you just need to render those in between each “tick” of the timer. This is the fastest speed you need to play back at because the user wont actually be seeing the data change if you update faster than this in a super tight loop.

The side effect of rendering it like this will allow your app to actually process other things and not lock up as you’re experiencing.

Obviously, you do :slight_smile:

Thanks Julian. I already know that. It is not the point.

The point is that it all works perfect until the main window has lost focus. The “simulated replay” does not have to be perfect because how the heck would you ever know? or care anyway.

Some of the recordings and/or imported files could be around 40,000 samples per second. That’s why I used microseconds. I haven’t tested this part yet, but I see no reason why the graph should not redraw as a “mock real time” example.

The issue thus far is not how the graph is represented, but why the method is interrupted. I’m not just saying when the method is run, I’m saying losing focus causes the method not to run as expected EVEN WHEN THE METHOD HAS NOT BEEN CALLED.

I really appreciate the efforts that you guys have put into this, but I think there is a mis-understanding and a communication issue on my part.

I’ll do a test at 40k and see where that leads to.

No, it works sometimes, with some data, on your machine, in some cases…

It’s likely that in other situations (slower machine, more data, etc.) it’s going to fail much more often.

As others have pointed out, the design is wrong - you are locking up the CPU in a tight loop drawing and then pausing within that loop in response to an event.

The way to engineer it is inside-out and make it event-driven, something like this:

Canvas1.MouseDown
    Timer1.Mode = Timer.ModeMultiple

Timer1.Action
    RefreshData
    Canvas1.invalidate // this will cause the canvas to refresh
    
Canvas1.Paint
   g.draw backingPicture,0,0

RefreshData
    [code to update the backingPicture from data]
    [ do not put any time-delaying loops here]

Thanks everyone.

I’ve done a test at 44.1kHz, that is 44 thousand and 100 samples per second. The test was for 1 second.

That is a lot of data for my software to cope with. The upshot is that the “Replay” does lag, The “Replay” does work, but instead of 1 second, I’d say 1.5 over that same period.

I thought (wrongly) that there was some magical “super dooper” fast method.

The above solutions look like the way to go.

btw. I’m not drawing into a canvas, but on the window itself.

Steve, you obviously do not realize that drawing at 44.1KHz is completely and utterly a waste of CPU, and moreover a hardware impossibility. With the best of GPU, such a refresh rate is well over reasonable.

There are reasons why TV is using a 60 Hz refresh rate, and even the wildest games seldom go over 100 Hz.

Contrary to what you assume, the “super dooper” exists, and it is called slowing down display. And doing it in a timer.

But it is apparent that you refuse to take into account any of the suggestions that were made to you in this thread by several of the best Xojo programmers. So much so, I wonder why you even started it in the first place. If your approach worked so perfectly, it would not hang, would it ?

No need to reply, I know once again you would rebuff any suggestion. Good luck.

That’s what the OS does when it detects that your app is in a tight loop and is not responding to the OS any more. Go with what Michel and Michael have suggested.

BTW, contrary to Mac, when a Windows window loses focus, its execution stops…

And, setting focus to the window does just the same as Clearfocus. If you want the window to come to the front, use

window1.Show

A simple way of preventing the window to lose focus would simply be to make it a floating window.

I don’t think so.

Just tested, indeed execution does not stop.