What’s the best way to determine if a Timer is going to fire, and if not, reset it so that it does fire again, but, also to avoid re-starting it if it’s already going.
The problem I’m running into is that, within a Timer.Action event, Timer.Mode = Timer.ModeSingle.
So this code will sometimes work, sometimes not:
if mTimer.Mode = Timer.ModeOFF then
mTimer.Mode = Timer.ModeSingle // cause the timer to fire again
else
// the timer is already running, so we Don't want to reset it in this case
end if
I’m not sure I understand the problem, so let me restate it with made-up numbers to see if I have it right.
You have a Timer whose period is set to 1000 ms. At some point that Timer is started.
With 200 ms left to go, some code needs to make sure the Timer is going to fire, but if it just sets Mode to ModeSingle, it will push the next firing time out to 1000 ms, even though you want to ensure it fires at its scheduled time 200 ms from that point. Is that it?
If so, I’m not sure why if Timer1.Mode <> Timer.ModeSingle doesn’t work consistently. Do you have an idea as to why that might fail?
As an alternative, create the Timer as a property and use AddHandler for its action. When you need to start the Timer, create it and set its Period and Mode. When it’s done, remove the handler and set the property to nil. The logic is, if the Timer exists, it’s running.
Sub MaybeStartTimer()
if MyTimer is nil then
MyTimer = new Timer
AddHandler MyTimer.Action, AddressOf MyTimerAction
MyTimer.Period = somePeriod
MyTimer.Mode = Timer.ModeSingle
end if
End Sub
Sub MyTimerAction (sender As Timer)
// The code you need it to do
MyTimer.Mode = Timer.ModeOff
RemoveHandler MyTimer.Action, AddressOf MyTimerAction
MyTimer = nil
End Sub
Kem, you have it right. “Fire a timer, but if it’s already set to go, don’t delay it.”
In my testing, I see that inside a Timer.Action event, Timer.Mode = ModeSingle. Your suggestion about forcing ModeOff is a good one, but this seems like it shouldn’t be that hard, so I was wondering if I was doing it wrong.
[quote=216098:@Michael Diehr]What’s the best way to determine if a Timer is going to fire, and if not, reset it so that it does fire again, but, also to avoid re-starting it if it’s already going.
The problem I’m running into is that, within a Timer.Action event, Timer.Mode = Timer.ModeSingle.
So this code will sometimes work, sometimes not:
if mTimer.Mode = Timer.ModeOFF then
mTimer.Mode = Timer.ModeSingle // cause the timer to fire again
else
// the timer is already running, so we Don't want to reset it in this case
end if
[/quote]
Your code should work just fine as it is. Where do you place it ?
The other thing I don’t understand is why he is worried about the mode of a timer inside the action event of the timer. Inside the action event, the timer has fired. It gets reset when the action event finishes.
John asks [quote]The other thing I don’t understand is why he is worried about the mode of a timer inside the action event of the timer. Inside the action event, the timer has fired. It gets reset when the action event finishes[/quote]
The answer is that in a complex app, there are often multiple code pathways : A method “FooBar()” may not be aware of (and, indeed, via good design principles for OOP, should not care) how it was called.
The problem is not generally that this bug is hard to deal with in a toy example, but rather it’s a subtle bug that crops up in real-world use. My code which worked fine on Win32 and Carbon started behaving oddly in Cocoa builds, and it wasn’t until just now that I figured out why…
[quote=216286:@Michael Diehr]And… further testing does not show the issue in the latest 2015 R3 beta builds, so perhaps this is an issue that was fixed at some point?
Im using 2014R2.1 where I clearly see the bug [/quote]
That is why it did not show here. Just move to a more recent version, then. Maybe 2.4 won’t show the issue.
If you absolutely need to use 2.1, you could subclass the timer and set a flag when the timer starts. Maybe using a computed property shadowing mode.
You could also use Xojo.Core.Date.Nanoseconds stored upon start so you can check the time remaining to fire by comparing to Period.
Yeah, I’m not sure where the problem is. It doesn’t appear to be in the most recent version.
I just tried a very simple project. A Window and a Timer. The timer is mode multiple and set to execute every second (Default settings). I have one line in the timer action event:
If me.Mode = Timer.ModeSingle Then Break
The app just happily runs.
Now I just tried changing the timer to a single mode and it still didn’t break. So then I changed the code to:
if mTimer.mode = Timer.modeOFF then
mTimer.mode = 1
mTimer.reset //Technically, this is a redundancy as changing the mode *should* reset the timer, but perhaps this addition will help.
end if
Make sure that the period property is set in the inspector or through an Open event or the like. You do not want period = nil.