HomeUser Control Panel (unavailable in archive)ForumsTutorialsArt GalleryResourcesMaps

CSSafety Timers

03-15-2008, 04:54 PM#1
chobibo
First and foremost, I would like to thank and acknowledge Vexorian for making CSSafety.
Can someone explain why timers are stacked to make it safe, I'm asking this because I was thinking of applying the technique to groups but I don't know if it's even worth doing it. Here's my code:
Collapse Groups with CSSafety applied:
library GroupsStack

    globals
        private group array G
        private integer N=0
        debug integer StackCount=0
    endglobals

    function NewGroup takes nothing returns group
        if N==0 then
            debug set StackCount=StackCount+1
            debug call BJDebugMsg( "|c00FF0000New Group Created!|r; Total Groups:  "+I2S(StackCount) )
            return CreateGroup()
        endif
        set N=N-1
        debug call BJDebugMsg( "|c0000FF00Using Group in index:  "+I2S( N+1 )+"|r" )
        return G[N]
    endfunction

    function ReleaseGroup takes group g returns nothing
        if N>1 then
            call DestroyGroup(g)
            debug set StackCount=StackCount-1
            debug call BJDebugMsg( "|c00FF0000Limit Reached!|r ; Destroying Group!" )
        else
            debug call BJDebugMsg( "|c000080FFStoring Group in index:  "+I2S( N+1 )+"|r" )
            set G[N]=g
            set N=N+1
        endif
    endfunction
    
        
endlibrary

I want to know if it's a good idea to do something like this or not. Thanks!
03-15-2008, 05:14 PM#2
moyack
All the reasons for time recycling can been seen here

Quote:
Originally Posted by Timers, the Tricks and the Traps by Rising_dusk
What's the most efficient way to use timers?

Timer Recycling
Many advanced users of timers will use a process coined as recycling timers to prevent the need to ever destroy a timer.
Effectively, there is an array of timers where whenever you need a timer, you pull it from the stack.
And that whenever you're done with a timer you return it to the stack.

A bit of code written by our local Technical Director, Vexorian, reveals this in greater detail.
This post can be located here.

NOTE:
This code uses preprocessor help to fall into WC3 syntax.
Rewriting it a little would be necessary for direct use.
Collapse JASS:
//******************************************************************************************
//*
//* CSSafety 14.1
//* ¯¯¯¯¯¯¯¯
//*
//*  Utilities to make things safer. Currently this simply includes a timer recycling
//* Stack. Once you replace CreateTimer with NewTimer and DestroyTimer with ReleaseTimer
//* you no longer have to care about setting timers to null nor about timer related issues
//* with the handle index stack.
//*
//******************************************************************************************

//==========================================================================================
globals
    private timer array T
    private integer N = 0
endglobals
Collapse JASS:
 //==========================================================================================
function NewTimer takes nothing returns timer
    if (N==0) then
        return CreateTimer()
    endif
 set N=N-1
 return T[N]
endfunction
//==========================================================================================
function ReleaseTimer takes timer t returns nothing
    call PauseTimer(t)
    if (N==8191) then
        debug call BJDebugMsg("Warning: Timer stack is full, destroying timer!!")
        //stack is full, the map already has much more troubles than the chance of bug
        call DestroyTimer(t)
    else
        set T[N]=t
        set N=N+1
    endif    
endfunction

There is a great advantage to recycling timers using this method.

Once you replace CreateTimer with NewTimer and DestroyTimer with ReleaseTimer you no longer have to care about setting timers to null nor about timer related issues with the handle index stack.
This means you no longer need to worry about destroying timers or whatnot.
This also eliminates potential bugging of handle indicies.
I recommend it if you think you can follow it!


Alright... So what can go wrong?

The Traps

Timer Nullifying~
This is perhaps one of the most annoying issues with the timer variable.
While the timer is unfathomably useful for jassers all over the world, there is a severe pitfall if it is used in conjunction with game cache storage.
If you nullify a locally declared timer with set SomeTimer = null and you've set handles onto that timer, there is a chance the handles will return null the next time you attempt to recall them.
This bug has in the past and will continue in the future to impede upon the use of timers.
03-15-2008, 05:22 PM#3
Rising_Dusk
You can very easily apply this method to any number of things, from Dummy Unit recycling to group recycling to even trigger recycling. It's handy, and it's really useful if it's something you want to do. Oftentimes, though, for groups you can just use a single global group as a temporary group and then use it wherever you do groupenum -> loop to damage or whatever. That's why it's not commonly seen attributed to groups.

I think the dummy recycling code actually exists inside of the CS, but you'd have to check. It works in an identical fashion, though.

EDIT:
Damned I need to update that tutorial. *Goes to work*
03-15-2008, 05:32 PM#4
chobibo
Thanks Moyack and Rising_Dusk for the helpful information! I think stacks are really useful, I'll be using them more from now on.
03-15-2008, 06:06 PM#5
Rising_Dusk
Well, I updated the tutorial if you're at all interested. It should make more sense and be easier to read now -- And it also makes mention of more vJass applications. It's kind of funny how weird I typed back then... I always hit enter after every sentence. I can see why it got on everyone's nerves!
03-15-2008, 07:48 PM#6
chobibo
Yo Rising_Dusk regarding this statement:
Quote:
Once you replace CreateTimer with NewTimer and DestroyTimer with ReleaseTimer you no longer have to care about setting timers to null nor about timer related issues with the handle index stack.
Does this mean I don't need to null local timer variables which points to the stacked timer? EXAMPLE:
Collapse JASS:
function Test takes nothing returns nothing
     local timer t = NewTimer()
     call TimerStart(t, 10.00, false, null)
     //call ReleaseTimer(t) 
     set t = null // so I wouldn't need to null this variable?
endfunction
Wouldn't this leak a handle Id for each time it is run? Thanks Dusk!
03-15-2008, 08:05 PM#7
uberfoop
You don't release the timer right away. You release it in the callback function when you're done with it. If you release it right away, it just winds up back in the stack. You put it back when you're done with it.

And yes, you don't need to nullify it, because it doesn't have a pointer leak if the handle variable isn't null. Handle variable pointer leaks are only problematic if at the end of a function they're = null.
03-15-2008, 08:17 PM#8
chobibo
Thanks for making that one clear uberfoop, btw the release wasn't intended.
So if a variable points to something like this; local timer t=NewTimer() in which the handle Id wasn't recycled and is still in use, there will be no leak. I think I got a better understaning of variables now thanks uberpoof!
03-15-2008, 08:17 PM#9
Rising_Dusk
Quote:
Wouldn't this leak a handle Id for each time it is run? Thanks Dusk!
The variable isn't being removed, it's being recycled, so its references don't leak.