I have been getting a warning about AceTimer for my addon having a lot of live timers... I have revised the AceTImer code and it seems that when I cancel a timer the timre.delay gets to 0, but the handle in the table ramains... so when I schedule a new timer a new handle is created and so on. It seems the pice of code on the OnUpdate event that resets the handles is not doing it correctly.
This could be solved if you con create and then enable or disable the timmer using the handle.
You seem to be doing it wrong. Here's example code:
This creates a timer, to call the function self:UpdatePartyGUIDs() after 0.5 seconds, but only if a timer to do it doesn't already exist.
if not timers.UpdatePartyGUIDs then
timers.UpdatePartyGUIDs = self:ScheduleTimer("UpdatePartyGUIDs", 0.5)
end
Now this is a one-shot timer, its not a repeating timer. When it fires, the handle no longer becomes valid, you need to nil out the invalid timer to it.
function Omen:UpdatePartyGUIDs()
timers.UpdatePartyGUIDs = nil
-- blah blah rest of code
end
Now, the function UpdatePartyGUIDs() can be called from many places, not just from a timer. So we cancel the timer if it exists (that is we cancel the timer if some other code calls it ahead of the timer), and also have it not error on a invalid timer handle by sending it "true" in the second argument.
function Omen:UpdatePartyGUIDs()
if timers.UpdatePartyGUIDs then
self:CancelTimer(timers.UpdatePartyGUIDs, true)
timers.UpdatePartyGUIDs = nil
end
-- blah blah rest of code
end
We extensively tested the usage of AceTimer, especially with alot of timers and handles. Its true that the timer does infact not get removed instantly, however it will be removed when its time is reached.
So unless you have alot of timers with a rather "long" time (> 10 seconds) and create alot of new timers in that period, it should never be an issue. If you should have the case of alot of long one-shot timers, maybe you should rethink your design a bit :)
Here is what I do. DHUD2 displays player buffs that are about to expire. In the PLAYER_AURA_CHANGED event I do a loop through the players buffs.
local name, _, texture, _, _, duration, expirationTime, isMine = UnitAura("player", i, "HELPFUL")
if name then
if (duration < 1800) or ( duration >= 1800 and p.pBuffsFilter) then
local currentTime = GetTime()
local timeLeft = expirationTime - currentTime
if timeLeft > 0 and timeLeft <= timeFilter then
....(show de buff icon)....
elseif timeLeft > 0 then
local newtimeToEvent = timeLeft - timeFilter
if newtimeToEvent < timeToEvent or timeToEvent == 0 then
timeToEvent = newtimeToEvent
DHUD2:CancelTimer(DHUD2.auraHandle, true)
DHUD2.auraHandle = self:ScheduleTimer(DHUD2.AuraTimer, timeToEvent-1)
end
end
This code gets called again from the timer handler function. If there is a new buff who's expiration is longer than the filter a new timer is created. I probably should do it out of the loop cause where it is at the moment I know various timers can be schedualed and canceled (I have tried it and gotten the same behaviour).
That is why I asked if you could just rescheduale a timer if you all ready had a handle, cause I only really need 1 timmer but be able to change its expiration time.
Yeah, this caught me by surprise too. It works differently than ace2. I'm not sure why you would ever want to scedule 100 timers for 1 second all at once, but it will let you do that vs just resetting the time on 1 timer 100 times, then it will complain about it later.
function module:OnPlayerDataChanged(Name)
self:OnPlayerDataChangedThrottled(Name)
-- This code just leaks resources
-- self:CancelTimer(self.timerPlayerData, true)
-- self.timerPlayerData = self:ScheduleTimer("OnPlayerDataChangedThrottled", 1, Name)
end
There's something fishy about mixing repeating timers with one shot timers and trying to start stop the repeating ones.
I realize this is an old thread but this quote helped me work on a solution for myself
Its true that the timer does infact not get removed instantly, however it will be removed when its time is reached.
My code essentially has a ResetProcessing method which looks like this. The goal is to Stop and Restart Processing. Processing just means the timer is running:
function FullThrottle:ResetProcessing()
FullThrottle:StopProcessing()
FullThrottle:StartProcessing()
end
StopProcessing() looked like this:
function FullThrottle:StopProcessing()
--cancel the calculation timer
self:CancelTimer(FullThrottle.CalculationTimer, true)
FullThrottle.CalculationTimer = nil
end
And StartProcessing() looked like this:
function FullThrottle:StartProcessing()
FullThrottle.CalculationTimer = self:ScheduleRepeatingTimer("DoCalculations", FullThrottle.CalculationInterval, nil)
end
I should mention, as I think it's related, that I do have a second "one shot" timer which gets triggered when you mouse over the frame and fires off an animation about 1 second later. And only when both of these are "running" can I reproduce the problem I'm about to describe. To cause the problem, I simply have to mouse over my frame to trigger the mouse event that fires the one shot trigger.
While it was VERY intermittent, sometimes the repeating timer callback wouldn't get called any more after calling ResetProcessing(). In other words, the timer wasn't getting initialized as expected.
The one thing I know for sure is that when the problem DOES happen, the creation of my FullThrottle.CalculationTimer will NOT be nil (a table representing the string exists), no errors are thrown, but a later call to self:TimeLeft(FullThrottle.CalculationTimer) will result in the "no such timer registered" error.
I think that's ultimately the bug. I have searched my code extensively for the issue, I'm sure the problem is in how I'm using AceTimer (not AceTimer itself) but I could not locate where I am doing things wrong. I did however find a solution that solves the problem. When canceling the repeating timer, if I convert it to a one time timer (which makes the handle valid) then things work as expected. I fear this is more of a hack for something I've done wrong than anything.
I'm posting my "hack" in case it help shed light on any possibility that there is in fact a bug with AceTimer's processing of it's timers:
function FullThrottle:KillRepeatingTimer(timer)
if(timer) then
self:CancelTimer(timer, true)
timer = self:ScheduleTimer("DoCalculations", 0.1, nil) --convert to one shot timer
self:CancelTimer(timer, true) --cancel again, we don't care if it ran or not
timer = nil
end
end
function FullThrottle:ResetProcessing()
FullThrottle:StopProcessing()
FullThrottle:StartProcessing()
end
function FullThrottle:StopProcessing()
FullThrottle:KillRepeatingTimer(FullThrottle.CalculationTimer)
end
function FullThrottle:StartProcessing()
FullThrottle.CalculationTimer = self:ScheduleRepeatingTimer("DoCalculations", FullThrottle.CalculationInterval, nil)
end
Rollback Post to RevisionRollBack
To post a comment, please login or register a new account.
I have been getting a warning about AceTimer for my addon having a lot of live timers... I have revised the AceTImer code and it seems that when I cancel a timer the timre.delay gets to 0, but the handle in the table ramains... so when I schedule a new timer a new handle is created and so on. It seems the pice of code on the OnUpdate event that resets the handles is not doing it correctly.
This could be solved if you con create and then enable or disable the timmer using the handle.
At the moment is there a work arround this issue?
This creates a timer, to call the function self:UpdatePartyGUIDs() after 0.5 seconds, but only if a timer to do it doesn't already exist.
Now this is a one-shot timer, its not a repeating timer. When it fires, the handle no longer becomes valid, you need to nil out the invalid timer to it.
Now, the function UpdatePartyGUIDs() can be called from many places, not just from a timer. So we cancel the timer if it exists (that is we cancel the timer if some other code calls it ahead of the timer), and also have it not error on a invalid timer handle by sending it "true" in the second argument.
So unless you have alot of timers with a rather "long" time (> 10 seconds) and create alot of new timers in that period, it should never be an issue. If you should have the case of alot of long one-shot timers, maybe you should rethink your design a bit :)
This code gets called again from the timer handler function. If there is a new buff who's expiration is longer than the filter a new timer is created. I probably should do it out of the loop cause where it is at the moment I know various timers can be schedualed and canceled (I have tried it and gotten the same behaviour).
That is why I asked if you could just rescheduale a timer if you all ready had a handle, cause I only really need 1 timmer but be able to change its expiration time.
I realize this is an old thread but this quote helped me work on a solution for myself
My code essentially has a ResetProcessing method which looks like this. The goal is to Stop and Restart Processing. Processing just means the timer is running:
StopProcessing() looked like this:
And StartProcessing() looked like this:
I should mention, as I think it's related, that I do have a second "one shot" timer which gets triggered when you mouse over the frame and fires off an animation about 1 second later. And only when both of these are "running" can I reproduce the problem I'm about to describe. To cause the problem, I simply have to mouse over my frame to trigger the mouse event that fires the one shot trigger.
While it was VERY intermittent, sometimes the repeating timer callback wouldn't get called any more after calling ResetProcessing(). In other words, the timer wasn't getting initialized as expected.
The one thing I know for sure is that when the problem DOES happen, the creation of my FullThrottle.CalculationTimer will NOT be nil (a table representing the string exists), no errors are thrown, but a later call to self:TimeLeft(FullThrottle.CalculationTimer) will result in the "no such timer registered" error.
I think that's ultimately the bug. I have searched my code extensively for the issue, I'm sure the problem is in how I'm using AceTimer (not AceTimer itself) but I could not locate where I am doing things wrong. I did however find a solution that solves the problem. When canceling the repeating timer, if I convert it to a one time timer (which makes the handle valid) then things work as expected. I fear this is more of a hack for something I've done wrong than anything.
I'm posting my "hack" in case it help shed light on any possibility that there is in fact a bug with AceTimer's processing of it's timers: