[module] - Tiny module to create periodic tickers

Share your advanced PureBasic knowledge/code with the community.
miso
Enthusiast
Enthusiast
Posts: 466
Joined: Sat Oct 21, 2023 4:06 pm
Location: Hungary

[module] - Tiny module to create periodic tickers

Post by miso »

Plain and simple, modularized for easy implementation.

Code: Select all

;-===MODULE DESCRIPTION===
;Tiny module to create periodic tickers
;Created with PB 6.20 Beta 1
;Author:      :miso
;Creation date:2025.01.31
;Purpose      :for periodic tasks like sending a keepalive packet to push through nat or change frame of an animation, etc
;Scope        :all platforms, but tested only on Windows 7 64 bit
;Permissions  :777, do what you want, (author takes no responsibilities nor any claims)

EnableExplicit
;-===MODULE DECLARATION===
DeclareModule ticker
  ;-===CONSTANTS===
  #TICKS_FOREVER = -10
  #MAXIMUM_ALLOWED_TICKERS = 255
  
  ;-===STRUCTURES===
  Structure tickerstructure
    timeout.i
    lasttick.i
    current_tick_count.i
    target_tick_count.i
    alive.i
  EndStructure

  ;-===PUBLIC PROCEDURE DECLARATIONS===
  Declare.i create(ID.i,ticktime_ms.i,number_of_ticks.i=#TICKS_FOREVER)
  Declare.i triggered(ID.i)
  Declare.i kill(ID.i)
EndDeclareModule

;-===MODULE===
Module ticker
  ;-===PRIVATE GLOBALS===
  Global Dim tickers.tickerstructure(#MAXIMUM_ALLOWED_TICKERS)
  ;-===PUBLIC PROCEDURES===
  
  ;***************************************
  ;Create a new ticker
  ;***************************************
  Procedure.i create(ID.i,ticktime_ms.i,number_of_ticks.i=#TICKS_FOREVER)
    If ID.i>=0 And ID.i<=#MAXIMUM_ALLOWED_TICKERS
      With tickers(ID.i)
        \lasttick.i = ElapsedMilliseconds()
        \timeout.i = ticktime_ms.i
        \current_tick_count.i = 0
        \alive.i = #True
        \target_tick_count = number_of_ticks.i
      EndWith
      ProcedureReturn #True
    EndIf
    ProcedureReturn #False
  EndProcedure
  
  ;***************************************
  ;Returns the triggered status of a ticker
  ;***************************************
  Procedure.i triggered(ID.i)
    If ID.i<0 Or ID.i>#MAXIMUM_ALLOWED_TICKERS
      Debug Str(ID.i) + " not exists, cant be checked"
      ProcedureReturn #False
    EndIf
    
    If tickers(ID.i)\alive <> #True
      ProcedureReturn #False
    EndIf
    
    If ElapsedMilliseconds()-tickers(ID.i)\lasttick<tickers(ID.i)\timeout
      ProcedureReturn #False
    EndIf
    
    tickers(ID.i)\current_tick_count + 1
    tickers(ID.i)\lasttick = ElapsedMilliseconds()
    If tickers(ID.i)\current_tick_count = tickers(ID.i)\target_tick_count
      tickers(ID.i)\alive = #False
    EndIf
    
    ProcedureReturn #True
  EndProcedure
  
  ;***************************************
  ;Sets a ticker to be dead
  ;***************************************
  Procedure.i Kill(ID.i)
    If ID.i<0 Or ID.i>#MAXIMUM_ALLOWED_TICKERS
      ProcedureReturn #False
    EndIf
    tickers(ID.i)\alive = #False
    ProcedureReturn #True
  EndProcedure
EndModule

;-===EXAMPLE USAGE===
OpenConsole("Periodic Tickers")
ticker::create(0,5000)   ;this ticker can proc any number of times with 5000 ms delay
ticker::create(1,1000,2) ;This can proc only twice, then stops
ticker::create(2,200)    ;this ticker can proc any number of times with 200 ms delay

Repeat
  If ticker::triggered(1) : PrintN("Tiktak") : EndIf
  If ticker::triggered(2) : PrintN("Tak")    : EndIf
Until ticker::triggered(0)
ticker::kill(0) ;not needed, just showcase for example
End


User avatar
STARGÅTE
Addict
Addict
Posts: 2228
Joined: Thu Jan 10, 2008 1:30 pm
Location: Germany, Glienicke
Contact:

Re: [module] - Tiny module to create periodic tickers

Post by STARGÅTE »

The module serves its purpose but it is inaccurate in time when the loop do also other tasks.

When you ask for the trigger with "ElapsedMilliseconds() - tickers(ID.i)\lasttick < tickers(ID.i)\timeout",
usually the time difference between lasttick and ElapsedMilliseconds() is larger then the timeout itself, when the trigger is activated.
However, later you set tickers(ID.i)\lasttick = ElapsedMilliseconds(), which meas, all triggers are slightly longer than then timeout and you accumulate an error.
It is more accurate to set tickers(ID.i)\lasttick = tickers(ID.i)\lasttick + tickers(ID.i)\timeout
and check also for multiple ticks between two calls of triggered(), when the program has some other delays.
PB 6.01 ― Win 10, 21H2 ― Ryzen 9 3900X, 32 GB ― NVIDIA GeForce RTX 3080 ― Vivaldi 6.0 ― www.unionbytes.de
Lizard - Script language for symbolic calculations and moreTypeface - Sprite-based font include/module
miso
Enthusiast
Enthusiast
Posts: 466
Joined: Sat Oct 21, 2023 4:06 pm
Location: Hungary

Re: [module] - Tiny module to create periodic tickers

Post by miso »

You are absolutely right, it is not for tasks that are perfect time sensitive. Sending a keepalive packet that is delayed a little would not make any difference.
It is more accurate to set tickers(ID.i)\lasttick = tickers(ID.i)\lasttick + tickers(ID.i)\timeout
Your insight is good again, as usual. If you use this in a threaded environment, it would be better in your way. However, in a non threaded environment if the main task took long, that way you can trigger the tickers multiple time one after another. That would not be better.

Everything depends on what it is used for. Your insight is appreciated and valuable though.
Post Reply