- Joined
- Mar 29, 2016
- Messages
- 688
This system uses one timer to manage all your timer needs (that means all timeout). It bundles all codes that will expire at the same time using Nestharus' Trigger (This provides some advantages namely, better performance compared to coupling codes using native trigger, and it also allows you to remove a code from a currently executing/expiring timer - which doesn't work correctly when using timer + native trigger).
Script
v1.0
- First Release
Script
JASS:
library MasterTimer /*
*/uses /*
*/Trigger /* https://github.com/nestharus/JASS/blob/master/jass/Systems/Trigger/script.j
*/Table /* https://www.hiveworkshop.com/threads/snippet-new-table.188084/
*/ListT /* https://github.com/nestharus/JASS/blob/master/jass/Data%20Structures/ListT/script.j
*/StaticUniqueList /* https://github.com/nestharus/JASS/blob/master/jass/Data%20Structures/StaticUniqueList/script.j
*/ErrorMessage /* https://github.com/nestharus/JASS/blob/master/jass/Systems/ErrorMessage/main.j
*///! novjass
|-------------|
| Description |
|-------------|
/*
This system uses a single timer to handle all timeouts. It does it by binding all
the codes that will expire at the same time into a bucket. Each bucket will have
its own Trigger and a remaining time. The timer runs as a one-shot timer to execute
the bucket with the least remaining time and then it resets the time of that bucket
to its timeout/period while the remaining time of the other buckets are updated.
Then it proceeds to run the next bucket with the least remaining time and this
process is repeated over again.
*/
|-----|
| API |
|-----|
struct MasterTimer/*
*/static method operator [] takes real timeout returns MasterTimer/*
- Returns a timeout instance for registration
*/method clear takes nothing returns nothing/*
- Clears all registered codes and Triggers from this timeout
*/method register takes code c returns nothing/*
*/method unregister takes code c returns nothing/*
- Registers/Unregisters a code to a timeout instance
*/method registerTrigger takes Trigger whichTrigger returns nothing/*
*/method unregisterTrigger takes Trigger whichTrigger returns nothing/*
- Registers/Unregisters a Trigger to a timeout instance
*/module MasterTimer/* (Optional)
- Implement this module inside your struct below your static methods
named 'periodic' and 'period'
Interfaces (Optional):
*/static method period takes nothing returns real/*
- Must return the desired periodic method execution timeout
- If not found, the timeout would be set to 0.03125 by default
*/static method periodic takes nothing returns nothing/*
- The periodic method
Methods (Provided by the Module):
*/static method startPeriodic takes nothing returns nothing/*
- Starts the periodic execution
*/static method stopPeriodic takes nothing returns nothing/*
- Stops the periodic execution
*/
|---------|
| Example |
|---------|
call MasterTimer[0.03125].register(function Periodic)
call MasterTimer[0.03125].unregister(function Periodic)
//! endnovjass
globals
/*============================================================*/
/* SYSTEM CONFIGURATION */
/*============================================================*/
/*
The number of decimal places that will be considered
for the timeout */
private constant integer TIMEOUT_PRECISION = 5
/*
When looking for a bucket to register the code, the
system will check if there is any that will expire at
the same time as the input code. This value is the
maximum allowance for the difference in their expiration.
If the system can't find any existing bucket that meets
this condition, a new bucket will be created */
private constant real EXPIRATION_OFFSET_TOLERANCE = 0.05
/*============================================================*/
/* SYSTEM CONFIGURATION */
/*============================================================*/
endglobals
private keyword Init
private struct Bucket extends array
Trigger trigger
real timeout
real remaining
integer count
readonly static TableArray bucket
private static TableArray condition
implement ListT
method getBucket takes nothing returns thistype
local thistype node = this.first
loop
exitwhen node == 0
if node.remaining < EXPIRATION_OFFSET_TOLERANCE then
set node.count = node.count + 1
return node
endif
set node = node.next
endloop
set node = this.enqueue()
set node.count = 1
set node.timeout = this.timeout
set node.remaining = node.timeout
set node.trigger = Trigger.create(false)
return node
endmethod
method removeFromBucket takes integer index returns nothing
local thistype node = bucket[this][index]
call bucket[this].remove(index)
call condition[this].remove(index)
set node.count = node.count - 1
if node.count == 0 then
call node.remove()
call node.trigger.destroy()
endif
endmethod
method register takes boolexpr expr returns nothing
local thistype node = this.getBucket()
local integer id = GetHandleId(expr)
set bucket[this][id] = node
set condition[this][id] = node.trigger.register(expr)
endmethod
method unregister takes boolexpr expr returns nothing
local integer id = GetHandleId(expr)
call TriggerCondition(condition[this][id]).destroy()
call this.removeFromBucket(id)
endmethod
method registerTrigger takes Trigger trig returns nothing
local thistype node = this.getBucket()
set bucket[this][trig] = node
set condition[this][trig] = node.trigger.reference(trig)
endmethod
method unregisterTrigger takes Trigger trig returns nothing
call TriggerReference(condition[this][trig]).destroy()
call this.removeFromBucket(trig)
endmethod
private static method init takes nothing returns nothing
set bucket = TableArray[0x2000]
set condition = TableArray[0x2000]
endmethod
implement Init
endstruct
private struct PeriodList extends array
private static timer timer = CreateTimer()
implement StaticUniqueList
private static method periodic takes nothing returns nothing
local real timeout = TimerGetTimeout(timer)
local real leastRemaining = 0.00
local thistype this = first
local Bucket node
loop
exitwhen this == sentinel
set node = Bucket(this).first
loop
exitwhen node == 0
set node.remaining = node.remaining - timeout
if node.remaining == 0.00 then
set node.remaining = node.timeout
call node.trigger.fire()
endif
if leastRemaining == 0.00 or node.remaining < leastRemaining then
set leastRemaining = node.remaining
endif
set node = node.next
endloop
set this = this.next
endloop
call TimerStart(timer, leastRemaining, false, function thistype.periodic)
endmethod
method register takes nothing returns nothing
if not (Bucket(this).first == 0) and (Bucket(this).last == Bucket(this).first) then
call enqueue(this)
if first == this or Bucket(this).timeout < TimerGetRemaining(timer) then
call TimerStart(timer, Bucket(this).timeout, false, function thistype.periodic)
endif
endif
endmethod
method unregister takes nothing returns nothing
if Bucket(this).first == 0 then
call this.remove()
if first == sentinel then
call PauseTimer(timer)
endif
endif
endmethod
endstruct
struct MasterTimer extends array
debug private static TableArray registered
private static Table bucket
private static real factor
static method operator [] takes real timeout returns thistype
local integer id = R2I(timeout*factor)
local Bucket this = bucket[id]
if this == 0 then
set this = Bucket.create()
set this.timeout = timeout
set bucket[id] = this
endif
return this
endmethod
method register takes code c returns nothing
debug call ThrowError(this == 0, "MasterTimer", "register()", "thistype", 0, "Attempted to use a null instance")
debug call ThrowError(c == null, "MasterTimer", "register()", "thistype", this, "Attempted to register a null code")
debug call ThrowError(registered[this].boolean[GetHandleId(Filter(c))], "MasterTimer", "register()", "thistype", this, "Attempted to register an already registered code")
debug set registered[this].boolean[GetHandleId(Filter(c))] = true
call Bucket(this).register(Filter(c))
call PeriodList(this).register()
endmethod
method unregister takes code c returns nothing
debug call ThrowError(this == 0, "MasterTimer", "unregister()", "thistype", 0, "Attempted to use a null instance")
debug call ThrowError(c == null, "MasterTimer", "unregister()", "thistype", this, "Attempted to unregister a null code")
debug call ThrowError(not registered[this].boolean[GetHandleId(Filter(c))], "MasterTimer", "unregister()", "thistype", this, "Attempted to unregister an unregistered code")
debug call registered[this].boolean.remove(GetHandleId(Filter(c)))
call Bucket(this).unregister(Filter(c))
call PeriodList(this).unregister()
endmethod
method registerTrigger takes Trigger trig returns nothing
debug call ThrowError(this == 0, "MasterTimer", "registerTrigger()", "thistype", 0, "Attempted to use a null instance")
debug call ThrowError(trig == 0, "MasterTimer", "registerTrigger()", "thistype", this, "Attempted to register a null Trigger")
debug call ThrowError(registered[this].boolean[trig], "MasterTimer", "registerTrigger()", "thistype", this, "Attempted to register an already registered Trigger")
debug set registered[this].boolean[trig] = true
call Bucket(this).registerTrigger(trig)
call PeriodList(this).register()
endmethod
method unregisterTrigger takes Trigger trig returns nothing
debug call ThrowError(this == 0, "MasterTimer", "unregisterTrigger()", "thistype", 0, "Attempted to use a null instance")
debug call ThrowError(trig == 0, "MasterTimer", "unregisterTrigger()", "thistype", this, "Attempted to unregister a null Trigger")
debug call ThrowError(not registered[this].boolean[trig], "MasterTimer", "unregisterTrigger()", "thistype", this, "Attempted to unregister an unregistered Trigger")
debug call registered[this].boolean.remove(trig)
call Bucket(this).unregisterTrigger(trig)
call PeriodList(this).unregister()
endmethod
method clear takes nothing returns nothing
local Bucket node = Bucket(this).first
debug call ThrowWarning(this == node, "MasterTimer", "clear()", "thistype", this, "Attempted to clear an empty instance")
debug call ThrowError(this == 0, "MasterTimer", "clear()", "thistype", 0, "Attempted to clear a null instance")
loop
exitwhen node == 0
set node.count = 0
call node.trigger.destroy()
call node.remove()
set node = node.next
endloop
call PeriodList(this).unregister()
call Bucket(this).destroy()
call bucket.remove(R2I(Bucket(this).timeout*factor))
call Bucket.bucket[this].flush()
debug call registered[this].flush()
endmethod
private static method init takes nothing returns nothing
debug set registered = TableArray[0x2000]
set bucket = Table.create()
set factor = Pow(10, TIMEOUT_PRECISION)
endmethod
implement Init
endstruct
private module Init
private static method onInit takes nothing returns nothing
call init()
endmethod
endmodule
module MasterTimer
static if thistype.periodic.exists then
static method startPeriodic takes nothing returns nothing
static if thistype.period.exists then
call MasterTimer[period()].register(function thistype.periodic)
else
call MasterTimer[0.031250000].register(function thistype.periodic)
endif
endmethod
static method stopPeriodic takes nothing returns nothing
static if thistype.period.exists then
call MasterTimer[period()].unregister(function thistype.periodic)
else
call MasterTimer[0.031250000].unregister(function thistype.periodic)
endif
endmethod
endif
endmodule
endlibrary
v1.0
- First Release
Last edited: