FlightControl e98814e8d4 Progress
Fixed that schedulers, which are not attached to an object, after
garbage collect were removed also from the scheduler dispatcher. The
scheduler dispatcher now attaches the schedulers to 2 different tables.
- A weak table ObjectSchedulers, which contains scheduler attached to
objects. When the object is removed, the scheduler is removed also.
- A weak table with PersistentSchedulers, which contains schedulers not
attached to objects. These schedulers are persistent, even after garbage
collection.

I hope this now makes the circle round...
2016-12-13 12:29:28 +01:00

173 lines
5.3 KiB
Lua

--- This module contains the TIMER class.
--
-- ===
--
-- Takes care of scheduled function dispatching for defined in MOOSE classes.
--
-- This function is complicated.
--
-- ===
--
-- ===
--
-- ### Contributions: -
-- ### Authors: FlightControl : Design & Programming
--
-- @module Timer
--- The TIMER structure
-- @type TIMER
TIMER = {
ClassName = "TIMER",
CallID = 0,
}
function TIMER:New()
local self = BASE:Inherit( self, BASE:New() )
self:F3()
return self
end
--- Add a Schedule to the ScheduleDispatcher.
-- The development of this method was really tidy.
-- It is constructed as such that a garbage collection is executed on the weak tables, when the Scheduler is nillified.
-- Nothing of this code should be modified without testing it thoroughly.
-- @param #TIMER self
-- @param Core.Scheduler#SCHEDULER Scheduler
function TIMER:AddSchedule( Scheduler, ScheduleFunction, ScheduleArguments, Start, Repeat, Randomize, Stop )
self:F( { Scheduler, ScheduleFunction, ScheduleArguments, Start, Repeat, Randomize, Stop } )
self.CallID = self.CallID + 1
-- Initialize the ObjectSchedulers array, which is a weakly coupled table.
-- If the object used as the key is nil, then the garbage collector will remove the item from the Functions array.
self.PersistentSchedulers = self.PersistentSchedulers or {}
-- Initialize the ObjectSchedulers array, which is a weakly coupled table.
-- If the object used as the key is nil, then the garbage collector will remove the item from the Functions array.
self.ObjectSchedulers = self.ObjectSchedulers or setmetatable( {}, { __mode = "v" } )
if Scheduler.TimeEventObject then
self.ObjectSchedulers[self.CallID] = Scheduler
self:T3( { self.CallID, self.ObjectSchedulers[self.CallID] } )
else
self.PersistentSchedulers[self.CallID] = Scheduler
self:T3( { self.CallID, self.PersistentSchedulers[self.CallID] } )
end
self.Schedule = self.Schedule or setmetatable( {}, { __mode = "k" } )
self.Schedule[Scheduler] = {}
self.Schedule[Scheduler][self.CallID] = {}
self.Schedule[Scheduler][self.CallID].Function = ScheduleFunction
self.Schedule[Scheduler][self.CallID].Arguments = ScheduleArguments
self.Schedule[Scheduler][self.CallID].StartTime = timer.getTime() + ( Start or 0 )
self.Schedule[Scheduler][self.CallID].Start = Start + .001
self.Schedule[Scheduler][self.CallID].Repeat = Repeat
self.Schedule[Scheduler][self.CallID].Randomize = Randomize
self.Schedule[Scheduler][self.CallID].Stop = Stop
self:T3( self.Schedule[Scheduler][self.CallID] )
self.Schedule[Scheduler][self.CallID].CallHandler = function( CallID )
self:F3( CallID )
local ErrorHandler = function( errmsg )
env.info( "Error in timer function: " .. errmsg )
if debug ~= nil then
env.info( debug.traceback() )
end
return errmsg
end
local Scheduler = self.ObjectSchedulers[CallID]
if not Scheduler then
Scheduler = self.PersistentSchedulers[CallID]
end
self:T3( { Scheduler = Scheduler } )
if Scheduler then
local Schedule = self.Schedule[Scheduler][CallID]
self:T3( { Schedule = Schedule } )
local ScheduleObject = Scheduler.TimeEventObject
local ScheduleFunction = Schedule.Function
local ScheduleArguments = Schedule.Arguments
local Start = Schedule.Start
local Repeat = Schedule.Repeat or 0
local Randomize = Schedule.Randomize or 0
local Stop = Schedule.Stop or 0
local ScheduleID = Schedule.ScheduleID
local Status, Result
if ScheduleObject then
local function Timer()
return ScheduleFunction( ScheduleObject, unpack( ScheduleArguments ) )
end
Status, Result = xpcall( Timer, ErrorHandler )
else
local function Timer()
return ScheduleFunction( unpack( ScheduleArguments ) )
end
Status, Result = xpcall( Timer, ErrorHandler )
end
local CurrentTime = timer.getTime()
local StartTime = CurrentTime + Start
if Status and (( Result == nil ) or ( Result and Result ~= false ) ) then
if Repeat ~= 0 and ( Stop == 0 ) or ( Stop ~= 0 and CurrentTime <= StartTime + Stop ) then
local ScheduleTime =
CurrentTime +
Repeat +
math.random(
- ( Randomize * Repeat / 2 ),
( Randomize * Repeat / 2 )
) +
0.01
self:T( { ScheduleArguments, "Repeat:", CurrentTime, ScheduleTime } )
return ScheduleTime -- returns the next time the function needs to be called.
else
timer.removeFunction( ScheduleID )
ScheduleID = nil
end
else
timer.removeFunction( ScheduleID )
ScheduleID = nil
end
else
self:E( "Scheduled obscolete call ..." )
end
return nil
end
self.Schedule[Scheduler][self.CallID].ScheduleID = timer.scheduleFunction(
self.Schedule[Scheduler][self.CallID].CallHandler,
self.CallID,
timer.getTime() + self.Schedule[Scheduler][self.CallID].Start
)
return self.CallID
end
function TIMER:RemoveSchedule( CallID )
self:F( CallID )
local Schedule = self.ObjectSchedulers[CallID]
if Schedule then
local ScheduleID = Schedule.ScheduleID
timer.removeFunction( ScheduleID )
ScheduleID = nil
Schedule = nil
end
end