wc3campaigns
WC3C Homepage - www.wc3c.netUser Control Panel (Requires Log-In)Engage in discussions with other users and join contests in the WC3C forums!Read one of our many tutorials, ranging in difficulty from beginner to advanced!Show off your artistic talents in the WC3C Gallery!Download quality models, textures, spells (vJASS/JASS), systems, and scripts!Download maps that have passed through our rigorous approval process!

Go Back   Wc3C.net > Tutorials > JASS/AI scripts tutorials
User Name
Password
Register Rules Get Hosted! Chat Pastebin FAQ and Rules Members List Calendar



Reply
 
Thread Tools Search this Thread
Old 04-01-2006, 04:36 AM   #1
PipeDream
Moderator
 
PipeDream's Avatar


Code Moderator
 
Join Date: Feb 2006
Posts: 1,405

Submissions (6)

PipeDream is a glorious beacon of light (463)PipeDream is a glorious beacon of light (463)PipeDream is a glorious beacon of light (463)PipeDream is a glorious beacon of light (463)

Default Local Var Leaks

This tutorial assumes you know JASS and the basics of fixing memory leaks. If you are new to JASS, read Vexorian's tutorial. If you are new to fixing memory leaks, read Blade.dk's tutorial.

This is an attempt to consolidate everything I know about the "local var not nulled leak bug" and provide a reference, as I frequently observe some confusion about it. Disclaimer: I'm not a programmer.

Handles

Units, locations and everything else that isn't a string, real, integer, code or boolean are handles. A handle is a 4B integer with a little type safety so that you don't try things like local location l = GetTriggerPlayer(), which would be meaningless.

The handle indexes an array of structures. Each structure contains a pointer to the unit, location or other object along with a reference count. Whenever you assign a handle to a variable, warcraft finds the structure and increments the reference count. When you reassign that variable, the count is decremented. In theory, when there are no more local or global variables with that small piece of data's address and the object it points to has been removed, it should deallocate itself. Unfortunately, blizzard never finished this functionality. Even worse, they left a bug in: When a variable goes out of scope, that is, when a function that had local variables returns, the reference does not decrement! If you try to destroy or remove a handle, that small piece of data will not be deallocated until the reference count drops to zero. This will never happen if you don't work around that bug.

Whenever something is never going to be destroyed, you do not need to worry about this bug. For example, in an AoS map, the heroes will likely never be removed, so setting to null will not fix any leak. Similarly, multiboards aren't likely to be and players can't be destroyed.

The Work arounds:
Set to Null
Collapse JASS:
function MyTriggerAction takes nothing returns nothing
    local location unitloc = GetUnitLoc(GetTriggerUnit())
    //Do stuff
    call RemoveLocation(unitloc)
    set unitloc = null
endfunction
This is the standard method. It requires just one extra line and is almost always adequate in a trigger action. There are two places where it can be inadequate. One, if your function must return the variable in question. Then, how can you set to null? The other is if you have many ways for your function to return. For each return, you may need identical set to null lines.
Note that it does not matter that you set the variable to null, only that you change its value. Thus, a global var does not need to be nulled, as it will soon be changed which has the same effect as nullification.

Wrapper Functions - discovered by Cubasis
Collapse JASS:
function SomeFunc_helper takes location l returns location
    //Do stuff with l
     return l //Remember to deallocate it later
endfunction
function SomeFunc takes nothing returns location
    return SomeFunc_helper(GetUnitLoc(GetTriggerUnit())
endfunction
This method exploits a curiosity: Function arguments work correctly-the reference count of the handle they point to is decremented when the function returns. Thus, we don't need to set to null, but we have to jump through a hoop to get our variable into a function argument. The downside to this method is the overhead to having an extra function call along with one more line of code.

Integers
Handles are just integers. If we could cast them into an integer and back out somehow, we would be home free, because as long as the handle never goes into a variable of handle type, the reference count won't increment. However, we lose type safety, so we must be careful. Fortunately, type casting was figured out a long time ago through the infamous return bug.
Collapse JASS:
function H2I takes handle h returns integer
    return h
    return 0
endfunction
function SomeFunc takes nothing returns location
    local location unitloc = GetUnitLoc(GetTriggerUnit())
    local integer iunitloc = H2I(unitloc)
    //Do stuff with unitloc
    set unitloc = null
    return iunitloc //Don't forget to remove
    return null
endfunction
Thanks to Anitarf for this example, which combines set to null with the return bug. This works around the main problem with only using set to null by returning an integer instead of the location directly.


Alternatively, there is an old trick for using local variables in GUI: First, declare a point unitloc. Then, at the top of your trigger, add action Custom script - local location udg_unitloc
When you do this, you can select unitloc from the GUI drop down list, but the local version will take preference. However, you can only do this once before something strange happens: Every overwritten local variable will share the same space! For the C heads, it behaves like a union.
Collapse JASS:
function MyTriggerAction takes nothing returns location
    local integer udg_A = H2I(GetUnitLoc(GetTriggerUnit())) //abuse return bug
    local location udg_unitloc
    //Do stuff with udg_unitloc
    return udg_unitloc
endfunction
Or, to save an H2I call:
Collapse JASS:
function MyTriggerAction takes nothing returns location
    local integer udg_A 
    local location udg_unitloc = GetUnitLoc(GetTriggerUnit())
    local integer B = udg_A //abuse global/local bug
    //Do stuff with udg_unitloc
    set udg_unitloc = null
//Choice A - abuse global/local bug
    set udg_A = B
    return udg_unitloc
//Choice B - abuse return bug
//    return B
//    return null
endfunction
Unfortunately using this bug more than once leaks like crazy which defeats the purpose.

Globals and GUI
Since we don't actually need to set a variable to null, but only change its value, if we could use globals for everything, we would also be home free, as long as we make sure to reuse the same space whenever possible. We have global arrays, all we need is to give each scope a private workspace within them. This method is particulary useful for GUI code where, in practice, you can only have one local variable-this method gets you as many as you can pack into the variable editor. Theoretically you could even scope variables inside loops.
Collapse JASS:
//===================================================
//    Temp Memory hack
//  Paste this code into the custom script section
//  Variables needed:
//  integer array udg_Stack_Nodes
//  integer udg_Stack_Heap
//  integer udg_Stack_Top with initial value -1
//  whatever object such as location array udg_tempPoint
//========================================================

function AllocateTemp takes nothing returns integer
    local integer p = udg_Stack_Heap
    set udg_Stack_Heap = udg_Stack_Heap + 1
    if(p > 8191) then
        call BJDebugMsg("Too many active GUI triggers!  Use game cache for a heap")
    endif
    return p
endfunction

function GetTempSpace takes nothing returns integer
    local integer p
    if(udg_Stack_Top == -1) then //Check if stack is empty
        set p = AllocateTemp() //Grab a new cell for our collection
    else //Do a regular stack pop
        set p = udg_Stack_Top
        set udg_Stack_Top = udg_Stack_Nodes[udg_Stack_Top] 
    endif
    return p
endfunction

//Regular stack push
function ReleaseTempSpace takes integer p returns nothing
    set udg_Stack_Nodes[p] = udg_Stack_Top
    set udg_Stack_Top = p
endfunction
Example GUI use:
Trigger:
sometrig
Collapse Events
Player - Player 1 (Red) types a chat message containing p as An exact match
Conditions
Collapse Actions
Custom script: local integer udg_mem = GetTempSpace()
Set tempPoint[mem] = (Target of current camera view)
Wait 5.00 seconds
Cinematic - Ping minimap for (All players) at tempPoint[mem] for 1.00 seconds
Custom script: call RemoveLocation(udg_tempPoint[udg_mem])
Custom script: call ReleaseTempSpace(udg_mem)
Or in JASS:
Collapse JASS:
function PingCamera takes nothing returns nothing
    local integer mem = GetTempSpace()
    set udg_tempPoint[mem] = GetCameraTargetPositionLoc()
    call TriggerSleepAction(5.)
    call PingMinimap(GetLocationX(udg_tempPoint[mem]),GetLocationY(udg_tempPoint[mem]),5.)
    call RemoveLocation(udg_tempPoint(mem))
    call ReleaseTempSpace(mem)
endfunction
Downside: You can only have 8192 simultaneous instances before things fail. This is plenty for GUI, so long as you remember to ReleaseTempSpace()

Caveats
Timers: Attempting to set timers to null will eventually result in catastrophe. Instead, use one of the other methods. The function wrapper method is tried and true:
Collapse JASS:
function callback_core takes timer t returns nothing
//Get handle vars, play with timer
//t does not need to be set to null because it never lives in a local variable
    if(you want) then
         call PauseTimer(t)
         call DestroyTimer(t)
    endif
endfunction
function callback takes nothing returns nothing
    call callback_core(GetExpiredTimer())
endfunction

function inittimer_core takes timer t returns nothing
//Do usual init stuff here
    call TimerStart(t,period,true,function callback)
endfunction
function inittimer takes nothing returns nothing
    call inittimer_core(CreateTimer())
endfunction

The best way to work around the set to null problem is to avoid ever needing to destroy things. Timers, fortunately, are reusable, so we can keep freed ones on a stack just like the multi-locals in gui trick.
Collapse JASS:
globals
    timer array udg_timerstack
    integer udg_ntimer = 0
endglobals

function NewTimer takes nothing returns timer
    if udg_ntimer == 0 then
        return CreateTimer()
    else
        set udg_ntimer = udg_ntimer-1
        return udg_timerstack[udg_ntimer]
    endif
endfunction

function ReleaseTimer takes timer t returns nothing
    if udg_ntimer == 8190 then
        call BJDebugMsg("Warning- you need 8191 timers at once!?")
        call DestroyTimer(t)
    else
        set udg_timerstack[udg_ntimer] = t
        set udg_ntimer = udg_ntimer + 1
    endif
endfunction

Last edited by PipeDream : 12-25-2006 at 03:03 AM.
PipeDream is offline   Reply With Quote
Sponsored Links - Login to hide this ad!
Old 04-01-2006, 05:25 AM   #2
moonliterhythm
User
 
Join Date: Mar 2006
Posts: 71

moonliterhythm has little to show at this moment (8)

Default

thanks this was very educational, and helped me learn a lot more about how to prevent leaks through custom scripting
moonliterhythm is offline   Reply With Quote
Old 04-01-2006, 06:29 AM   #3
Chuckle_Brother
Oh for the sake of fudge
 
Chuckle_Brother's Avatar


Respected User
 
Join Date: Dec 2005
Posts: 782

Submissions (2)

Chuckle_Brother will become famous soon enough (53)Chuckle_Brother will become famous soon enough (53)

Send a message via ICQ to Chuckle_Brother Send a message via AIM to Chuckle_Brother Send a message via MSN to Chuckle_Brother Send a message via Yahoo to Chuckle_Brother
Default

Informative for those trying to understand the WHY of the whole nulling/H2I and back functions.

Really makes me wish they had actually give us this functionality in the first place, true typecasting and an address-of operator would be a heavenly blessing, but thats getting into my huge wishlist of stuff they SHOULD have allowed for.
__________________
"...you play a mean banjo"
Chuckle_Brother is offline   Reply With Quote
Old 04-01-2006, 07:40 AM   #4
vile
n00b map maker
 
Join Date: Jul 2005
Posts: 599

Submissions (5)

vile will become famous soon enough (60)vile will become famous soon enough (60)vile will become famous soon enough (60)

Send a message via ICQ to vile Send a message via AIM to vile Send a message via MSN to vile
Default

Good, I think people should know that using handles with timers and then nulling the timers can cause weird glitches and bugs.
vile is offline   Reply With Quote
Old 04-01-2006, 02:35 PM   #5
emjlr3
Rehabbing
 
emjlr3's Avatar
 
Join Date: Jun 2005
Posts: 1,386

Submissions (14)

emjlr3 is a jewel in the rough (151)emjlr3 is a jewel in the rough (151)

Mapping Contest First Place

Send a message via AIM to emjlr3 Send a message via MSN to emjlr3
Default

im still not clear, do you not null the timers? or any local that is used by the local handle vars? or what?
__________________
emjlr3 is offline   Reply With Quote
Old 04-01-2006, 02:45 PM   #6
vile
n00b map maker
 
Join Date: Jul 2005
Posts: 599

Submissions (5)

vile will become famous soon enough (60)vile will become famous soon enough (60)vile will become famous soon enough (60)

Send a message via ICQ to vile Send a message via AIM to vile Send a message via MSN to vile
Default

You shouldnt null timers that you're using any handles on.
__________________
Check out my maps:
Age of Myths
Deadly Games
Magic & Steel New Project is kicking in!

TEASER TRAILER
vile is offline   Reply With Quote
Old 04-01-2006, 03:04 PM   #7
blu_da_noob
Nonchalant
 
blu_da_noob's Avatar


Respected User
 
Join Date: Mar 2006
Posts: 1,933

Submissions (2)

blu_da_noob is just really nice (398)blu_da_noob is just really nice (398)blu_da_noob is just really nice (398)blu_da_noob is just really nice (398)blu_da_noob is just really nice (398)blu_da_noob is just really nice (398)

[Quicksilver #2] - 2nd Place[Quicksilver#1] 1st place

Send a message via MSN to blu_da_noob
Default

Has anyone been able to figure out the exact conditions that cause the bug when nulling timers with handles attached? Because I've done that plenty of times and never had any problems.
blu_da_noob is offline   Reply With Quote
Old 04-01-2006, 03:21 PM   #8
Anitarf
Procrastination Incarnate


Development Director
 
Join Date: Feb 2004
Posts: 8,190

Submissions (19)

Anitarf has a brilliant future (903)Anitarf has a brilliant future (903)Anitarf has a brilliant future (903)Anitarf has a brilliant future (903)Anitarf has a brilliant future (903)Anitarf has a brilliant future (903)Anitarf has a brilliant future (903)Anitarf has a brilliant future (903)

2008 Spell olympics - Fire - SilverApproved Map: Old School Alliance TacticsHero Contest #2 - 3rd PlaceSpell making session 2 winner

Default

PipeDream, a good tutorial, but the code examples are not appropriate in some cases: when you are explaining how to return a local without a leak, your examples don't return anything, they just destroy the location in the end which makes little sense in the context of what you're explaining.

Edit: Moved to the tutorial submission forum.
__________________
Anitarf is offline   Reply With Quote
Old 04-01-2006, 04:36 PM   #9
vile
n00b map maker
 
Join Date: Jul 2005
Posts: 599

Submissions (5)

vile will become famous soon enough (60)vile will become famous soon enough (60)vile will become famous soon enough (60)

Send a message via ICQ to vile Send a message via AIM to vile Send a message via MSN to vile
Default

blu_da_noob, it happens when nulling the timer, and doing the same on other triggers with other abilities and so. And also, it doesnt happen in the first time, but rather after alot of casting times
__________________
Check out my maps:
Age of Myths
Deadly Games
Magic & Steel New Project is kicking in!

TEASER TRAILER
vile is offline   Reply With Quote
Old 04-01-2006, 05:16 PM   #10
emjlr3
Rehabbing
 
emjlr3's Avatar
 
Join Date: Jun 2005
Posts: 1,386

Submissions (14)

emjlr3 is a jewel in the rough (151)emjlr3 is a jewel in the rough (151)

Mapping Contest First Place

Send a message via AIM to emjlr3 Send a message via MSN to emjlr3
Default

wasnt it found that if you wait a few seconds after the function is ran that it doesnt happen, ex.

Collapse JASS:
call PauseTimer(t)
call FlushHandleLocals(t)
call DestroyTimer(t)
call TriggerSleepAction(5)
set t = null
__________________

Last edited by emjlr3 : 04-01-2006 at 05:16 PM.
emjlr3 is offline   Reply With Quote
Old 04-01-2006, 07:12 PM   #11
vile
n00b map maker
 
Join Date: Jul 2005
Posts: 599

Submissions (5)

vile will become famous soon enough (60)vile will become famous soon enough (60)vile will become famous soon enough (60)

Send a message via ICQ to vile Send a message via AIM to vile Send a message via MSN to vile
Default

was that a question or a fact? *confused*
__________________
Check out my maps:
Age of Myths
Deadly Games
Magic & Steel New Project is kicking in!

TEASER TRAILER
vile is offline   Reply With Quote
Old 04-01-2006, 07:40 PM   #12
Vexorian
Free Software Terrorist
 
Vexorian's Avatar


Technical Director
 
Join Date: Apr 2003
Posts: 14,898

Submissions (37)

Vexorian has a reputation beyond repute (1062)Vexorian has a reputation beyond repute (1062)Vexorian has a reputation beyond repute (1062)Vexorian has a reputation beyond repute (1062)Vexorian has a reputation beyond repute (1062)Vexorian has a reputation beyond repute (1062)Vexorian has a reputation beyond repute (1062)

Hero Contest #3 - 2nd Place

Default

Actually :
- You must pause timers before destroying them.

- If you find bugs with your spell, there is a way to prevent leaks and lose the risk,

Collapse old:
function blahblah takes nothing returns nothing
 local timer t=CreateTimer()

 // ...

 set t=null
endfunction

Collapse new:
function blahblah_sub takes timer t returns nothing
  // ...
endfunction

function blahblah takes nothing returns nothing
   call blahblah_sub(t)
endfunction

It is leak-free and also bug-free.

waiting before setting the timer to null is not bullet proof.


---

remove the *** in the first sample and I'll approve it
__________________
Zoom (requires log in)Wc3 map optimizer 5.0
Someone should fix .wav sound in this thing.
Zoom (requires log in)JassHelper 0.A.2.A
Turns your simple code into something that is complicated enough to work.
Faster != more useful
Vexorian is offline   Reply With Quote
Old 04-01-2006, 07:47 PM   #13
blu_da_noob
Nonchalant
 
blu_da_noob's Avatar


Respected User
 
Join Date: Mar 2006
Posts: 1,933

Submissions (2)

blu_da_noob is just really nice (398)blu_da_noob is just really nice (398)blu_da_noob is just really nice (398)blu_da_noob is just really nice (398)blu_da_noob is just really nice (398)blu_da_noob is just really nice (398)

[Quicksilver #2] - 2nd Place[Quicksilver#1] 1st place

Send a message via MSN to blu_da_noob
Default

Quote:
Originally Posted by vile
blu_da_noob, it happens when nulling the timer, and doing the same on other triggers with other abilities and so. And also, it doesnt happen in the first time, but rather after alot of casting times

How many times is a lot? I have always nulled my timers and never experienced problems, so unless the abilities have be used a few hundred times atleast to experience bugs, I have never had problems with them.


I've never taken any of those measures, Vex, and I've never had any problems. I guess I'll start using them for safety's sake.
blu_da_noob is offline   Reply With Quote
Old 04-01-2006, 07:52 PM   #14
Vexorian
Free Software Terrorist
 
Vexorian's Avatar


Technical Director
 
Join Date: Apr 2003
Posts: 14,898

Submissions (37)

Vexorian has a reputation beyond repute (1062)Vexorian has a reputation beyond repute (1062)Vexorian has a reputation beyond repute (1062)Vexorian has a reputation beyond repute (1062)Vexorian has a reputation beyond repute (1062)Vexorian has a reputation beyond repute (1062)Vexorian has a reputation beyond repute (1062)

Hero Contest #3 - 2nd Place

Default

it is related with the use of game cache and return bug exploiters, I made thousands of spells and have only seen it twice
__________________
Zoom (requires log in)Wc3 map optimizer 5.0
Someone should fix .wav sound in this thing.
Zoom (requires log in)JassHelper 0.A.2.A
Turns your simple code into something that is complicated enough to work.
Faster != more useful
Vexorian is offline   Reply With Quote
Old 04-01-2006, 09:48 PM   #15
PipeDream
Moderator
 
PipeDream's Avatar


Code Moderator
 
Join Date: Feb 2006
Posts: 1,405

Submissions (6)

PipeDream is a glorious beacon of light (463)PipeDream is a glorious beacon of light (463)PipeDream is a glorious beacon of light (463)PipeDream is a glorious beacon of light (463)

Default

Thank you for the feedback. I will add more complete examples.

Vex: thanks for the confirmation on function wrappers

- Removed *** from the first example (Vexorian)
- Changed a couple examples to demonstrate returning with out leaking (Anitarf)

Last edited by PipeDream : 04-02-2006 at 01:13 AM.
PipeDream is offline   Reply With Quote
Reply


Thread Tools Search this Thread
Search this Thread:

Advanced Search

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

vB code is On
Smilies are On
[IMG] code is On
HTML code is Off


All times are GMT. The time now is 05:14 AM.


Affiliates
The Hubb The JASS Vault Clan WEnW Campaign Creations Clan CBS GamesModding Flixreel Videos

Powered by vBulletin (Copyright ©2000 - 2019, Jelsoft Enterprises Ltd).
Hosted by www.OICcam.com
IT Support and Services provided by Executive IT Services