• 🏆 Texturing Contest #33 is OPEN! Contestants must re-texture a SD unit model found in-game (Warcraft 3 Classic), recreating the unit into a peaceful NPC version. 🔗Click here to enter!
  • 🏆 Hive's 6th HD Modeling Contest: Mechanical is now open! Design and model a mechanical creature, mechanized animal, a futuristic robotic being, or anything else your imagination can tinker with! 📅 Submissions close on June 30, 2024. Don't miss this opportunity to let your creativity shine! Enter now and show us your mechanical masterpiece! 🔗 Click here to enter!

[JASS] Nulling local variables that are terurned.

Status
Not open for further replies.
Level 24
Joined
Aug 1, 2013
Messages
4,657
Hi all.

I got this bad feeling when I do not null a local agent in functions... that are used 33 times a second for every object of that system... could be like 3300 per second for example.
JASS:
function GetUnit takes nothing returns unit
	local unit u = AUnit()
	
	return u
endfunction

But
1. Is it necessary to null returned values?

2. Is this a good solution for it?
JASS:
function GetUnit takes nothing returns unit
	local unit u = AUnit()
	
	set udg_TempUnit = u
	set u = null
	return udg_TempUnit
endfunction

3. Do local variables that are given as an argument also have to be nulled?
JASS:
function SetUnit takes unit whichUnit returns nothing
	set whichUnit = null
endfunction

EDIT: Yes these functions are really stupid and don't do shit but that is not what it is about.
EDIT: And yes I spelled the title wrong... it should be "Nulling local variables that are returned."
 
Level 22
Joined
Sep 24, 2005
Messages
4,821
IIRC, parametric variables don't leak... so:

JASS:
function Poop takes unit u returns unit
 return u
endfunction

is better than:

JASS:
function Poop takes nothing returns unit
 local unit u=SomeUnit
 set udg_Unit=u
 set u=null 
 return udg_Unit
endfunction

Also, Troll-Brain (with help from thehelper guys, there's a thread there I'm just too lazy to pull it up) did some experiments with global variables too and they found out that it leaks too, not references though...
 
Level 24
Joined
Aug 1, 2013
Messages
4,657
IIRC, parametric variables don't leak... so:

JASS:
function Poop takes unit u returns unit
 return u
endfunction

is better than:

JASS:
function Poop takes nothing returns unit
 local unit u=SomeUnit
 set udg_Unit=u
 set u=null 
 return udg_Unit
endfunction

If it is better because the first one does not create a new unit then you get the question wrong.
The question was which of the first two were better.
The third JASS block is a separate question...

Also, Troll-Brain (with help from thehelper guys, there's a thread there I'm just too lazy to pull it up) did some experiments with global variables too and they found out that it leaks too, not references though...
Global variables are pointers right? So what can it leak if it is not a reference?
 
Level 22
Joined
Sep 24, 2005
Messages
4,821
If it is better because the first one does not create a new unit then you get the question wrong.
Nope. It's better because it doesn't suffer the global variable leak.

Global variables are pointers right? So what can it leak if it is not a reference?
Because the references gets recycled and the memory consumption (according to their test) does not drop.

EDIT: To answer the questions:

1. Yes, they leak too.
2. Nope.
3. Nope.
 
Level 24
Joined
Aug 1, 2013
Messages
4,657
"the memory consumption (according to their test) does not drop."
I knoe that in JAVA not-used-stuff/removed-stuff is stored in the Garbagecollector.
That one does nothing until something decides that it is garbage day.
And that happens when something wants to have a spot in the memory but it is too full.
I thought that C++ does not do that by itself though.

Maybe... I hope, that that is the same as in WC3.

EDIT: I am ussually not so afraid of local variable leaks... especcially when it can only happen once per game.
But I hereby replace the basic attacks of units... Ranged units that is.
They create a missile and I have to do stuff with that unit. So I use some functions but they do return an agent 2 times in the loop for each missile. so 67 times a second for each existing missile... can get real nasty if I got a single leak.
 
Level 23
Joined
Apr 16, 2012
Messages
4,041
it is useless to reassign the local variable into global one and return it, at that point you dont even need to return it.

Parameters themselves do not leak when not nulled.

There is no in-built garbage collector in C++, since users are strongly advised to not use low level memory managment until needed(special overloads for new operator for fast allocations of small blocks, or pre-allocating)

Even if the variable leaks, I would consider it nonsense to reassign it to global variable, since what you leak is essentially minimal(a integer in a pool, which is basically nothing).


From what we know about the memory layout of wc3, not everything is pointer. Namely, integer, code, real and boolean are all stored as 4 byte values, while strings still create copies when passed to functions. Only handle and derivates are pointers.

Globals do leak the same thing unnulled globals do, 4 byte handleid on pointer types, nothing on nonpointer types(including string afaik). This is because while the game can and indeed does remove the object itself from existance, since there is still variable pointing to it, its handleid cannot be recycled, because if it got recycled to some other type, you would effectively typecast the object to another one, and calling KillUnit on lightning would have some fancy results most likely.

My impression though is, that when you reassign global variable to store something else, the handleid should be freed, since the game has the knowledge of this happening("OOO, this handleid is being unpointed from this and this variable, so lower its ref-count, and if it is 0, woops here we go, clear the shit out of it!"), but when locals fall out of scope, the game seems to not care about them unpointing, and the game still consinders the soon to be nonexistant variables to point to it, which is silly imo, but thats how we think it is, or at least I think it is
 

Wrda

Spell Reviewer
Level 26
Joined
Nov 18, 2012
Messages
1,894
Ok, let me see if I got this...:
1. It is said that global variables don't leak(reals, integers, units, players).
2. Now you say that some people did some experiments and global variables do leak.
3. I test a map with 200 units and with a trigger that changes a "unit" variable to a different unit in game every 0.01 seconds for 20 minutes. After that what I see? No lag in game, no black screen.
So you could explain me the statement number 2, is this some kind of joke?
 
Level 23
Joined
Apr 16, 2012
Messages
4,041
leak does not need to lower your fps, it most of the time does not even do that.

Leak means you unreference memory that will not be destroyed, increasing your memory usage. Fps drops result from not removing special effects and stuff like that, and this is not leak per se
 

Wrda

Spell Reviewer
Level 26
Joined
Nov 18, 2012
Messages
1,894
leak does not need to lower your fps, it most of the time does not even do that.
I know, only locations, special effects and other stuff do that.

Leak means you unreference memory that will not be destroyed, increasing your memory usage. Fps drops result from not removing special effects and stuff like that, and this is not leak per se
As far as I know, leaks tend to increase the time of "black screen", you know, after the game ends or when a player leaves. And my tests revealed that there is no black screen even if a global unit variable is constantly changing value every 0.01 seconds for 20 mins
 
Level 23
Joined
Apr 16, 2012
Messages
4,041
as I said, I myself think that when you reassign global variable the refcount on previous object is lowered, and locals leak only because when they fall out of scope(function quits running) there is no refcount decrement.

This is my speculation tho, nothing confirmed, but it is technically possible(from technological point of view)
 
Level 22
Joined
Sep 24, 2005
Messages
4,821
Ok, let me see if I got this...:
1. It is said that global variables don't leak(reals, integers, units, players).
2. Now you say that some people did some experiments and global variables do leak.
3. I test a map with 200 units and with a trigger that changes a "unit" variable to a different unit in game every 0.01 seconds for 20 minutes. After that what I see? No lag in game, no black screen.
So you could explain me the statement number 2, is this some kind of joke?

1. They will leak if the object they point to gets removed and they remain to point at the said object.

2. Yes, here.

3. a. There won't be any lag or "black screen" unless you've reached a significant number of leaks with regards to your system's memory. b.You're doing it wrong. Try doing it like how it is done on the link.
 

Wrda

Spell Reviewer
Level 26
Joined
Nov 18, 2012
Messages
1,894
1. Let's say that a unit is removed from game and a variable's value "X" was that unit, how is supposed that variable still pointing to a removed object? It's value after removing the unit should be "null", not the unit, that looks nonsense.
2. It still doesn't clarify if we have to do only in this order:
JASS:
   set G1=CreateGroup()
   call DestroyGroup(G1)
   set G1=null
or
JASS:
   set G1=CreateGroup()
   set G1=null
   call DestroyGroup(G1)
 
Level 24
Joined
Aug 1, 2013
Messages
4,657
JASS:
   set G1=CreateGroup()
   set G1=null
   call DestroyGroup(G1)

That is bull shit.

Set variable to <group>
set variable to <null>
destroy variable (destroy <null>)

If you have
Set variable to <group>
destroy variable (destroy <group>)
set variable to null

You would do it right.

It is the same as a road sign.
You make a city and call it <city>
You make a sign to lead people to your city. (<city> take the left here)
Then you remove <city> (yea you remove a complete city)
The sign still exists.
So you have to clear the text in the sign.
 
Level 23
Joined
Apr 16, 2012
Messages
4,041
JASS:
function myF takes nothing returns nothing
    local unit u = CreateUnit(Player(0), 'hfoo', 0, 0, 0)
    call KillUnit(u)
    call RemoveUnit(u)
    
    if u == null then
        call BJDebugMsg("null")
    else
        call BJDebugMsg("not null")
    endif
endfunction

//===========================================================================
function InitTrig_Untitled_Trigger_002 takes nothing returns nothing
    call myF()
endfunction

prints not null.

This is because the interpreter does not change what given handle points to from natives, ever.

Basically you removed the unit, but the local variable still points there, hence the memory cant be freed, because if you try to call SetUnitFacing(u, 5) and the memory was already removed from RAM, you would get segfault(trying to access memory that was not allocated to your process) if lucky, if not you would possibly allow for security holes, like chaning stuff in wc3's memory that should not be changed.

The second question:
Lets see what you do:

You first create a group in the memory, take the memory address where the group is stored, and say to G1 "can you please point to this? Thank you", and so G1 will point to the memory allocated for that group(not the group itself, so if you pass G1 into function, you only pass the memory address it is stored inside, so calling DestroyGroup from given function will destroy group stored inside G1 as well).

Then you try to call function DestroyGroup with a group stored where the G1 points to. The game takes that data and does something to it(may call constructor if it is C++ code, etc). The variable G1 is not updated, so it still points to that memory address, so you cant deallocate it(I can give you reason why if you want).

Then you say to G1 "Ok, all's fine, now point to nothing", and so G1 changes the memory address it should point to to null. In programming languages null is usually defined as memory address at 0(0x00000000 or on 64bit architecture 0x0000000000000000), which is kind of special memory address(please note that depending on the OS as well as the architecture, it could point to anything).

Under the hood, before the interpreter changes what G1 points to, it goes to the object G1 pointed to, and says: "Ok, listen, this dude is no longer pointing his giant finger at you, so we can say that there is one less dude pointing at you." Then if there are 0 dudes pointing into that memory address, the interpreter goes "ok, listen, noone has any interest in you anymore, so you can go fuck yourself now", and so it goes.

The second code:

We create and store the pointer as per usual.
But then you actually say to G1 to point to null, while the group being pointed at still lives, even if it ref-count goes down by 1(to 0 in this case), but Jass has no garbage collector, so there is noone going over the allocated memory looking at ref-count and removing the blocks that have 0 ref-count(this is also not safe to do, since the variable can be still used, like units, you dont want units disappearing from your map just because no variable points to them).

So now you have some unreachable memory address which holds the group, and G1 pointing into null.

Now you call DestroyGroup(G1), and what you actually are trying to do is destroy a group stored inside "null" memory address, and for security reasons, there is a null check inside the code, so nothing happens.

After that G1 still points to null, and the last created group is lost forever(until wc3 closes).

So as you can see, the first alternative is the better one, the second is a group leak.
 

Wrda

Spell Reviewer
Level 26
Joined
Nov 18, 2012
Messages
1,894
It is the same as a road sign.
You make a city and call it <city>
You make a sign to lead people to your city. (<city> take the left here)
Then you remove <city> (yea you remove a complete city)
The sign still exists.
So you have to clear the text in the sign.
Well, best comparison I've seen.

Summing up, that is completely bullshit. All spells and systems should be set to pending because "SOMEONE" after thousands of years discovered the mistery behind the leaks...
 
Level 24
Joined
Aug 1, 2013
Messages
4,657
Summing up, that is completely bullshit. All spells and systems should be set to pending because "SOMEONE" after thousands of years discovered the mistery behind the leaks...

JASS:
    set whichTimer = null
    set newLocation = null
    set oldLocation = null
    set whichUnit = null
endfunction

Does that looks familiar?

"set <handle> = null"
"endfunction"

That is removing the leaks.

The big question of this thread was.
Does the value also leak if it is returned?

JASS:
    return whichUnit
    set whichUnit = null
endfunction
That is bull shit because "set whichUnit = null" is never called because it is after a return statement.

JASS:
    set whichUnit = null
    return whichUnit
endfunction
This is even more bullshit because you null the value and then tell people that they need that value (null).

But it turns out that it doesn't leak (enough). So it can be used like this:

JASS:
    return whichUnit
endfunction
 
Level 24
Joined
Aug 1, 2013
Messages
4,657
Well... you don't want me to begin on what could have been better if Blizzard would have thought about that.
And I am not the only one.

Just don't try to make a system with buffs, cooldowns or constant movement.
Once you do, you will think differently, but I don't want to ruin your view on Blizzard.
 
Level 29
Joined
Oct 24, 2012
Messages
6,543
But it turns out that it doesn't leak (enough). So it can be used like this:

JASS:
    return whichUnit
endfunction

The above does leak.

The only time it does not leak is if the variable whichUnit is a parameter of the function it is in.

Example: Does not leak because the parameter is used. (blizzard must have handled parameters nulling variables before they fall out of scope)
JASS:
function Test takes unit whichUnit returns unit
    // Do actions here.
    return whichUnit
endfunction

Example: Does leak because the local is not nulled. (blizzard did not handle parameters nulling variables before they fall out of scope)
JASS:
function Test takes nothing returns unit
    local unit whichUnit = GetTriggerUnit()
    // Do actions here.
    return whichUnit
endfunction
 
Status
Not open for further replies.
Top