Local variable nullification & the native CreateUnit

XeRo13g

New Member
Reaction score
3
It's a couple of days now, that I've began learning Jass. I don't mind that is time consuming as long as things are done in the best, most effective and correct way, else what is the point using it? I'm a total noob, so please bear with me.

While trying to learn by improving/correcting my triggers, I've reached to this command:

Code:
call CreateNUnitsAtLoc

Then I realized that:
1. It uses locations when I was in fact using reals and I had to convert the reals to points, then destroy the points, nullify them and so on.

2. The above function calls several others...Adding the unit to groups, saving it for later use as LastCreatedUnit etc...

SO, since I'm taking a shot on a Hero siege map that spawns thousands and thousands of units while continuously struggling to improve the speed of everything even slightly, it's only reasonable to try and use the native:

Code:
native  CreateUnit  takes player id, integer unitid, real x, real y, real face returns unit
set u=CreateUnit(Player(8),udg_Hero_Type[ri],x,y,180 )

-I had to use a local variable (u) for the unit, so it can be used instead of LastCreatedUnit. If there is another way, please share.
-I was surprised to see that the native worked, even though udg_Hero_Type is not a unit but unit-type variable.

I'll paste the code, in case someone spots something I should know. Excuse my ignorance on writing/pasting code, I tried to make it as much readable as possible.


Code:
function Trig_Random_Mode_Actions takes nothing returns nothing
    local player p=GetOwningPlayer(GetSoldUnit())
    local integer pn=GetConvertedPlayerId(p)
    local unit u
    local integer i=1
    local integer ri
    local real x
    local real y
    local effect sfx1
    if ( Trig_Random_Mode_Func005C() ) then
        call ClearSelectionForPlayer( p )
        set udg_PickHeroCheck[pn] = (udg_PickHeroCheck[pn]-1)
           set x=9023.8
           set y=-7354.8+( 1154.50 * ( I2R(pn) - 1 ) )
        call CreateUnit(p,'n00W',x,y,180 )
        call AddUnitToStock(udg_Portal_Chance[pn], 'h00O', 1, 1)
        call AddUnitToStock( udg_Portal_Chance[pn],'h00M', 1, 1 )
        call AddUnitToStock( udg_Portal_Chance[pn],'h00N', 1, 1 )   
        call SetUnitAnimation( udg_Portal_Chance[pn], "birth" )
	call CameraSetupApplyForPlayer( true, udg_CameraPick[pn], p, 2.00 )
	set x=9281
	set y=-7870+(1151.00 * ( I2R(pn) - 1 ) ) 
    		loop
       		 exitwhen i > 3
          		set ri=GetRandomInt(100*i+1, 100*i+1+udg_Count[100*i])
          		set y=y+255
         		set sfx1=AddSpecialEffect("Abilities\\Spells\\Human\\Resurrect\\ResurrectCaster.mdl", x,y)
           		call DestroyEffect(sfx1) 
            		set u=CreateUnit(Player(8),udg_Hero_Type[ri],x,y,180 )
			set udg_Hero_Type[pn+i*10] = udg_Hero_Type[ri] 
                        set udg_Temp_Units[100*i+pn] = u
			set u=null
			set sfx1=null
         	 set i = i + 1
   		 endloop
        call TriggerSleepAction( 1 )
        call SelectUnitForPlayerSingle( udg_Portal_Chance[pn],p )
    else
        call ClearSelectionForPlayer( p )
        call DisplayTextToPlayer(p,0,0, "TRIGSTR_014")
    endif
set p=null
endfunction

Now, here it comes the problem.
QUESTION 1. It seems logical that I should nullify the (u) local variable each time is used in the loop...
or it's ok if I just null it at the end just like the (p) variable?

Note: I know that this can be used directly:
Code:
set udg_Temp_Units[100*i+pn]=CreateUnit(Player(8),udg_Hero_Type[ri],x,y,180 )
to avoid the nullification of the local variable(u) but no matter that in this case the unit needs to be stored, in most other cases, it doesn't

You must never nullify local variables BEFORE you destroyed what they contained, or else you'll never be able to remove that.
An example of that error is like this:
Code:
        Custom script:   set Exploding_Unit = null
        Custom script:   call RemoveUnit( Exploding_Unit )

This quote is taken from a tutorial I've been reading and it has greatly confused me.
I don't want the (u) unit to be instantly removed, that is why I store it at a global variable.
Generally, leak handling in GUI is different from that of JASS and I fail to deeply understand it yet, since I haven't found a tutorial that explains everything in detail. They tend to give an example and make reference of what else is similar.

QUESTION 2. Following through the functions after call CreateNUnitsAtLoc, I didn't see any nullification of the unit saved in bj's or any destroying of the bj_groups but I guess, there must be something for it internally... Is there?

QUESTION 3. If there is, can we directly use:
Code:
 set bj_lastCreatedUnit = CreateUnit(Player(8),udg_Hero_Type[ri],x,y,180 )
And avoid creating a local, or nullifying it?
Thanks for taking the time to read all this, I would appreciate any feedback on what asked or not.
 

tooltiperror

Super Moderator
Reaction score
231
>to avoid the nullification of the local variable(u) but no matter that in this case the unit needs to be stored, in most other cases, it doesn't
Let's say Exploding_Unit points to a unit (a unit variable is just its handle id) which is 0x13A72B. You set Exploding_Unit to null, and now it points to 0x0. Then you do RemoveUnit(Exploding_Unit)... you just killed an invalid unit.

>I didn't see any nullification of the unit saved in bj's or any destroying of the bj_groups but I guess, there must be something for it internally... Is there?
The bj_ variables are globals, and you don't have to null globals. And since the groups are reused so much, they're not destroyed.

>QUESTION 3. If there is, can we directly use:
Yes, you can. But it's ugly.
 

XeRo13g

New Member
Reaction score
3
Let's say Exploding_Unit points to a unit (a unit variable is just its handle id) which is 0x13A72B. You set Exploding_Unit to null, and now it points to 0x0. Then you do RemoveUnit(Exploding_Unit)... you just killed an invalid unit.

-If the local (u) isn't nulled before the loop sets it to another unit, it will leak, no?
-In the example of the Exploding_Unit, what if the unit is nulled and then killed at a later time by expiration, pick all units in area, or by another unit... Is there a problem with that?

The bj_ variables are globals, and you don't have to null globals. And since the groups are reused so much, they're not destroyed.

>QUESTION 3. If there is, can we directly use:
Yes, you can. But it's ugly.

I've missed that bj are globals, that makes sense. In any case, creating a unit through those bj's seem too much work for nothing, unless I'm missing something here.
Thanks for the clarification, it really helped!
 

Ayanami

칼리
Reaction score
288
For the case of units, reassigning the unit variable to another unit will not cause a leak, unlike locations. All you need to do is to null it at the end of your code.
 

XeRo13g

New Member
Reaction score
3
1. No
2. No
3. bjs are useless

These are the answers for the original post, or the post above yours?!

For the case of units, reassigning the unit variable to another unit will not cause a leak, unlike locations. All you need to do is to null it at the end of your code.

It's nice that local unit variables act much like global unit vars, let alone the nullification at the end. I wonder if it's the same for local player variables and I wouldn't mind if someone confirmed this.
Thanks Ayanami.
 

Laiev

Hey Listen!!
Reaction score
188
>>These are the answers for the original post, or the post above yours?!
Original :)
 

XeRo13g

New Member
Reaction score
3
QUESTION 1. It seems logical that I should nullify the (u) local variable each time is used in the loop...
or it's ok if I just null it at the end just like the (p) variable?

So...
Code:
if Laiev==true then
  if Ayanami==true then
     answer is Yes
     Dirac is False
  else
     Dirac is True
     answer is No
  endif
 set self=confused
endif
 

inevit4ble

Well-Known Member
Reaction score
38
As I understand it, reassigning a value before destroying/nulling it would leave a handle in memory without a reference. I would think it needs to be nulled before reset
 

XeRo13g

New Member
Reaction score
3
As I understand it, reassigning a value before destroying/nulling it would leave a handle in memory without a reference. I would think it needs to be nulled before reset

It seems to be a general confusion about this matter and this extends to other types of variables like Strings. I've read in a couple of tutorials that you must set the string variable to "" in an effort to nullify it, while most other posts/tutorials say that strings do not need to be nullified.
Code:
call DisplayTextToPlayer(Player(0),0,0, "Message")
There is also the case where you may avoid using any kind of variable, not sure if that leaves a leak.
 

PurgeandFire

zxcvmkgdfg
Reaction score
509
It seems to be a general confusion about this matter and this extends to other types of variables

Well, for the first matter, I hope I can straighten things up. Let's say you have this:
JASS:
function Test takes nothing returns nothing
    local location L = Location(0,2)
    call RemoveLocation(L)
    set L = Location(0,64)
    call RemoveLocation(L)
    set L = null
endfunction

The above function does not leak. Everything is destroyed and the fact that you didn't null the variable before resetting it does not matter. This is how the process of recycling handles works:
1) First, the variable is removed or destroyed.
2) Then it checks if any local variables are pointing to it. If there are any, then it's handle ID does not get recycled until there are no local variables pointing to it. (handle ID's can be found by using [ljass]GetHandleId[/ljass]) If there are not any, then it recycles the handle ID, and that ID becomes open for use by other handles to be made in the future.

Locals can only point to one value. (excluding arrays) So if it was pointing to handle A, and then you make it point to handle B, then it is no longer pointing to handle A. Thus, the handle ID can be safely recycled if the variable gets destroyed, since nothing is pointing to it.

That's why reassigning it won't leak a handle ID as long as you null at the end of the function. :D

-------

like Strings. I've read in a couple of tutorials that you must set the string variable to "" in an effort to nullify it, while most other posts/tutorials say that strings do not need to be nullified.
Code:
call DisplayTextToPlayer(Player(0),0,0, "Message")
There is also the case where you may avoid using any kind of variable, not sure if that leaves a leak.

Strings are special. The way Wc3 handles them is that they store them into a string table that contains all of the strings of the map. Every new string that has not been used before gets entered into that table. So setting a string variable to null or "" won't matter, since it is going to be entered into the string table anyway. Once a string is in the table, it never gets removed until the map ends, so there is nothing you can do about the slight memory leak of strings. However, it usually isn't something to worry about unless you have a save-load system used constantly or if you are performing a lot of string-related actions. :)
 

XeRo13g

New Member
Reaction score
3
That was truly enlightening!! My sincere gratitude.-

Thanks to everyone for taking the time to read/reply it really helped! (+rep for what it's worth)
 

Troll-Brain

You can change this now in User CP.
Reaction score
85
Actually there is still the local bug explained here

Though now the return bug is "fixed" and the local shadowing doesn't work anymore, indeed in the case that a local has the same name of a global variable, the global one is used.

Even more even globals can make a leak, explained here, but since it's only a little memory leak (and not an handle id one) you don't have to worry about it.
 

XeRo13g

New Member
Reaction score
3
k

Truth be told... Reading these threads, got me more confused than ever.

This is what I figured out by checking those threads and I might very well misunderstood.

-Local AND Global handles need to be nulled immediately after destroying them.
-If a local handle is destroyed+nulled and then resused in the same trigger, it leaks.

So, for example, if you want several special effects, the same number of local effect variables should be used, destroyed and nulled.

I will try to read them again after a good night sleep.
Thank you for sharing Troll-Brain!!
 

Dirac

22710180
Reaction score
147
-Local AND Global handles need to be nulled immediately after destroying them.
-If a local handle is destroyed+nulled and then resused in the same trigger, it leaks.
You got everything wrong.

1.- Locals have to be nulled before the function ends, otherwise they will leak, globals don't
2.- No, but you only have to null them before the function ends
 

Troll-Brain

You can change this now in User CP.
Reaction score
85
1) Technically globals leak if you don't null them in the same thread you destroy them, but until you reuse them it will be only a really minor memory leak, and not an handle id one which is worse.
So just assume that they don't leak, or null them when you destroy the associated handle if you care about it.

2) locals parameters (function blablabla takes ...) are not concerned by the bug, you don't have to null them, even after destroying them.

3) local variables are concerned by the bug , you have to null them, but in case a function returns an handle local variable it's better to return a global one like that, because else you will have an handle id leak, example (it's a stupid one) :

JASS:
function Test takes unit u returns unit
   set u = CreateUnit(Player(0),'hpea',0,0,0)
   return u // it won't leak because local parameters are not concerned by the bug
endfunction

function Test takes nothing returns unit
   local unit u = CreateUnit(Player(0),'hpea',0,0,0)
   return u // bad , when the unit will be removed his handle id won't be recycled
endfunction

function Test takes nothing returns unit
   local unit u = CreateUnit(Player(0),'hpea',0,0,0)
   set bj_lastCreatedUnit = u // it&#039;s a bj global (<b>b</b>lizzard <b>j</b>ass used by GUI, but it will also work with any other unit global)
   set u = null // good , when the unit will be removed his handle id will be recycled
   return bj_lastCreatedUnit // good, since it&#039;s a global variable until it&#039;s re-used later or sooner all unit handle id will be recycled
endfunction


In conclusion if you are still confused just care about local variables (the second example), it's not worth null the other ones.
 

XeRo13g

New Member
Reaction score
3
Not sure why I got the impression that re-using variables after nullification, leak. It's pretty bad when you don't have knowledge of the mechanics behind coding, so I'll have to re-read this whole thing again.

I think, your example gave me a vague idea of what is going on. Thanks again.
 

tooltiperror

Super Moderator
Reaction score
231
That makes no sense :)

Let me try to explain it. Imagine memory as a big array.

JASS:

[ ]	[ ]	[ ]	[ ]	[ ]	[ ]	[ ]
0x01	0x02	0x03	0x04	0x05	0x06	0x07

Each number beneath represents the location in memory it is at. Let's say we create a unit, it is now stored in memory. Keep in mind units are just Handle Ids (numbers)

JASS:

CreateUnit(...)  // Create a unit

					// The unit is now in memory here
[ ]	[ ]	[ ]	[ ]	[ ]	[0x100001]	[ ]
0x01	0x02	0x03	0x04	0x05	0x06		0x07


Now a unit variable stores a unit in it. But it doesn't actually. Because you see, a unit is a number in memory. A unit variable is a number in memory. The data where the variable is pointing is the unit it is set to.

JASS:

local unit bill = CreateUnit(...)  // &quot;Bill&quot; is now the value of the new unit, it is located at 0x03

[ ]	[ ]	[0x06]	[ ]	[ ]	[0x100001]	[ ]
0x01	0x02	0x03	0x04	0x05	0x06		0x07


So now, bill (aka 0x03) is just "pointing" at 0x06. We call it pointing, because it is like [ljass]bill[/ljass] is showing us (or pointing to) where 0x03 is.

Now let's get into locations. When you do [LJASS]Location(0,0)[/LJASS] it creates a new location.

JASS:

local location loc       // &quot;loc&quot; is stored at 0x05
			 // loc points to nothing (0x0) by default
[ ]	[ ]	[ ]	[ ]	[0x0]	[]	[ ]
0x01	0x02	0x03	0x04	0x05	0x06	0x07

THEN

set loc = Location(0,0)  // New location is created at 0x01,
			 // it ia a Handle Id like units are,
			 // loc will was assigned to point to 0x01
[0x100001]	[ ]	[ ]	[ ]	[0x01]	[]	[ ]
0x01		0x02	0x03	0x04	0x05	0x06	0x07

THEN
set loc = Location(0,0)  // New location is created at 0x02,
			 // but memory at 0x01 is STILL THERE
[0x100001]	[0x100002]	[ ]	[ ]	[0x02]	[]	[ ]
0x01		0x02		0x03	0x04	0x05	0x06	0x07


See the problem? The memory slots keep filling up, but they're not destroyed.
 

Sgqvur

FullOfUltimateTruthsAndEt ernalPrinciples, i.e shi
Reaction score
62
Questions: Does local arrays leak and do they require nulling?

JASS:
function funcy takes nothing returns nothing
    local array locs
    set locs[0] = Location(0, 0)
    set locs[1] = Location(1, 1)
    set locs[2] = Location(2, 2)
    set locs[3] = Location(3, 3)

    // return (1. Will the array deallocte the objects[locations] it holds? if the functions returns at this point)

    call RemoveLocation(locs[0])
    call RemoveLocation(locs[1])
    call RemoveLocation(locs[2])
    call RemoveLocation(locs[3])

    // 2. are those required?
    set locs[0] = null
    set locs[1] = null
    set locs[2] = null
    set locs[3] = null
endfunction


Edit:
Any theories why Blizzard hasn't fixed this bug after some many patches?
Is reference counting really really that hard to do right?
Or is it that the reference counting can't be fixed with a patch (requires recompilation of the game[.dll])?
 
General chit-chat
Help Users
  • No one is chatting at the moment.

      The Helper Discord

      Members online

      No members online now.

      Affiliates

      Hive Workshop NUON Dome World Editor Tutorials

      Network Sponsors

      Apex Steel Pipe - Buys and sells Steel Pipe.
      Top