• 🏆 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!

Variables and leaks

Status
Not open for further replies.
Level 2
Joined
Mar 21, 2020
Messages
13
Hello, sorry if this is a stupid question.
Should I delete a temp variable used to prevent leaks if it will be used over and over again?
I've read some tutorials and posts about leaks but this is still not very clear to me.
My guess it that it's ok not to delete them, but idk.

An example:
I set tempLoc as the position of triggering unit when a unit dies.
Should I delete tempLoc if lots of units are going to die?
Will it create only one leak or multiple leaks if I don't delete it?
 
Level 20
Joined
Aug 13, 2013
Messages
1,696
It depends on how you're referencing a handle: you create, use, destroy, clean.
In your case, it looks like you're creating a location handle for each unit, therefore you destroy each location after using.

In case you should overwrite, you must destroy the referenced handle first.
If you don't, that would leak as the reference for that memory is completely lost.

In case you're reusing it and not actually creating it then you're fine.

Illustration:

#1 Basic
Set Location = Position Of (Unit) <- Creating a location on the position of a unit.
Move (Enemy Unit) to the Location <- Using it.
call RemoveLocation(udg_Location) <- Destroy after using. No leak.

#2 Overwriting
Set Location = Position Of (Unit) <- Creating a location on the position of a unit.
Move (Enemy Unit) to the Location <- Using it.
Set Location = Position Of (Unit) <- Creating another location on the position of a unit. (Overwriting)
call RemoveLocation(udg_Location) <- Destroy. Leaks.

Why? We overwrite the reference here to another location. The first location has lost its reference, so we can't destroy the first location anymore, which leads to a leak.

What about we put another call for RemoveLocation(udg_Location) at the end?
That wouldn't make sense either as the reference wouldn't be the first location.

What about we put the other call before overwriting the Location? You got it. No leaks.
->
Set Location = Position Of (Unit) <- Creating a location on the position of a unit.
Move (Enemy Unit) to the Location <- Using it.
call RemoveLocation(udg_Location) <- Destroy.
Set Location = Position Of (Unit) <- Creating another location on the position of a unit.
call RemoveLocation(udg_Location) <- Destroy. No leak.

#3 Keeping
Trigger 1 Event - Map Initialization
Set Location = Center Of Map <- Creating a location on the center of the map.

Trigger 2 Event - every 1 second of game time.
Move (Unit) to the Location <- Using it.

Trigger 3 Event - any unit dies
call MoveLocation(Location, 100, 100) <- Moving the location to coordinates. (No handle creation)
Move (Died Unit) to the Location <- Using it again.

^ Leaks? No. Why? No references were lost. We're reusing it. We can call for destroy anytime.

#4 Not referencing
Trigger 1 Event - time elapsed to 0.
Create 1 Unit At (Position Of Unit) facing 0 angle. <- Creating a location without a reference.
Create 1 Unit At (Center Of Region) facing 0 angle. <- ^
Create 1 Unit At (Random Point of Region) facing 0 angle. <- ^
Move (Last Created Unit) to (Position Of Unit) <- ^

^ Leaks? 100%. Is it that bad? Not really, unless you're constantly triggering this which leads to a multiple creations of locations that have no reference and therefore multiple leaks. It's a bad practice.
Let's say:
Trigger 2 Event - every 0.05 seconds of game time.
Move (Last Created Unit) to (Position Of Unit) <- Creating and using a location without a reference.
^This is the bad thing.

Why?
In the Trigger 1 Event - time elapsed to 0. We only have 4 leaking locations that aren't referenced so we couldn't destroy them throughout the game. So that's a minimal leaking locations.
In the Trigger 2 Event - every 0.05 seconds of game time. Obviously, we're constantly creating a location that is not referenced to destroy.
0.05 seconds interval at this rate...
1/0.05 = 20 leaking locations per second.
60*20 = 1200 leaking locations per minute.
60*1200 = 72000 leaking locations per hour.

Trigger 1 - 4 locations leak throughout the whole game.
Trigger 2 - 72000 locations leak per hour of the game.

I hope this clarifies to anyone that also comes up with the question.
 
Last edited:

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,578
Hello, sorry if this is a stupid question.
Should I delete a temp variable used to prevent leaks if it will be used over and over again?
I've read some tutorials and posts about leaks but this is still not very clear to me.
My guess it that it's ok not to delete them, but idk.

An example:
I set tempLoc as the position of triggering unit when a unit dies.
Should I delete tempLoc if lots of units are going to die?
Will it create only one leak or multiple leaks if I don't delete it?
You will need to delete tempLoc every single time you're done using it. So whenever you set tempLoc you must be sure that you have removed the previous tempLoc, otherwise you'll create a leak.

A leak is created when you lose reference to a Point (or other leak-able variable types). So let's say you set tempLoc AGAIN while another tempLoc already exists. This will cause a leak because you have lost reference to the old tempLoc. You can no longer use "call RemoveLocation(udg_tempLoc)" to delete the old tempLoc because all that will do is delete the new one. Thus, that old Point exists in the game's memory forever and has become the monster we know as a MEMORY LEAK (scary stuff I know).

Here's some information regarding how triggers work and how everything is executed.

So take this trigger setup for example. After 1.00 second I create 10 Footman. This leads you to believe that all 10 footman are created at the same time, however, this isn't really the case. In actuality the Footman will be created 1 at a time until all 10 created.
  • Create
    • Events
      • Time - Elapsed game time is 1.00 seconds
    • Conditions
    • Actions
      • Set Variable Point = (Center of (Playable map area))
      • Unit - Create 10 Footman for Player 1 (Red) at Point facing Default building facing degrees
      • Custom script: call RemoveLocation (udg_Point)
This 1 at a time order is important because say for instance you had another trigger like this:
  • Enter Map
    • Events
      • Unit - A unit enters (Playable map area)
    • Conditions
    • Actions
      • Game - Display to (All players) for 30.00 seconds the text: A Unit Entered The Map
If all 10 footman were created at once then how would this trigger know which unit entered the map? Which of the 10 footman would be considered the entering unit? But that's not a problem since each Footman spawns 1 by 1, meaning that this Event will happen 10 times, once for each Footman.

So regarding your comment about tempLoc and what happens if a lot of units die, understand that each unit will die 1 by 1 meaning that a trigger like this will happen once for each dying unit:
  • Unit Dies
    • Events
      • Unit - A unit Dies
    • Conditions
    • Actions
      • Game - Display to (All players) for 30.00 seconds the text: A Unit Died
So if you set tempLoc in this Unit Dies trigger, it would get created once for each dying unit (so 10 times if our Footman died). So if we killed our 10 Footman then tempLoc would be set 10 times, and if you didn't remove tempLoc each time then you've leaked the first 9 of those tempLoc variables. The reason you haven't technically leaked the 10th (most recent) tempLoc is because you can still use "call RemoveLocation (udg_tempLoc)" to get rid of it.

Another important thing to understand:

Triggers, similar to actions, are executed in a certain order and certain number of times. If an Action in a trigger were to cause the Event of another trigger to go off, like how creating a unit sets off the "A unit enters map" Event, then the "A unit enters map" trigger's actions would actually be added onto the current trigger.

Here is that concept in action:

This is our Create trigger.
  • Create
    • Events
      • Time - Elapsed game time is 1.00 seconds
    • Conditions
    • Actions
      • Set Variable Point = (Center of (Playable map area))
      • Unit - Create 1 Footman for Player 1 (Red) at Point facing Default building facing degrees
      • Custom script: call RemoveLocation (udg_Point)
This is our Enter Map trigger.
  • Enter Map
    • Events
      • Unit - A unit enters (Playable map area)
    • Conditions
    • Actions
      • Set Variable Point = (Random point in (Playable map area))
      • Special Effect - Create a special effect at Point using Abilities\Spells\Human\ThunderClap\ThunderClapCaster.mdl
      • Special Effect - Destroy (Last created special effect)
      • Custom script: call RemoveLocation (udg_Point)
This is how our Create trigger actually works. The Actions from Enter Map are added onto the Create trigger:
  • Create
    • Events
      • Time - Elapsed game time is 1.00 seconds
    • Conditions
    • Actions
      • Set Variable Point = (Center of (Playable map area))
      • Unit - Create 1 Footman for Player 1 (Red) at Point facing Default building facing degrees
      • Set Variable Point = (Random point in (Playable map area))
      • Special Effect - Create a special effect at Point using Abilities\Spells\Human\ThunderClap\ThunderClapCaster.mdl
      • Special Effect - Destroy (Last created special effect)
      • Custom script: call RemoveLocation (udg_Point)
      • Custom script: call RemoveLocation (udg_Point)
So that's clearly a problem. Notice how the Actions from Enter Map shove their way into our Create trigger, happening immediately after the Footman is created. Because of this our first Point variable will never get removed because the Point from Enter Map is replacing it. This replacement effect creates a memory leak because we've lost reference to our first Point. A solution to a situation like this is to use unique variables, that way we can set them apart.

If we used different Point variables in both our Create trigger and our Enter Map trigger, we could end up with a leakless result like this:
  • Create
    • Events
      • Time - Elapsed game time is 1.00 seconds
    • Conditions
    • Actions
      • Set Variable Point1 = (Center of (Playable map area))
      • Unit - Create 1 Footman for Player 1 (Red) at Point1 facing Default building facing degrees
      • Set Variable Point2 = (Random point in (Playable map area))
      • Special Effect - Create a special effect at Point2 using Abilities\Spells\Human\ThunderClap\ThunderClapCaster.mdl
      • Special Effect - Destroy (Last created special effect)
      • Custom script: call RemoveLocation (udg_Point2)
      • Custom script: call RemoveLocation (udg_Point1)
With that being said, having a single Point variable like tempLoc still comes in handy. There are plenty of situations where a triggers Actions DON'T set off another Event, so in those cases you can freely use tempLoc knowing that the situation above won't happen.
Here is an example of properly cleaning up Point leaks and Unit Group leaks:
  • Create And Kill
    • Events
      • Time - Every 1.00 seconds of game time
    • Conditions
    • Actions
      • Set VariableSet Point = (Center of (Playable map area))
      • Unit - Create 10 Footman for Player 1 (Red) at Point facing Default building facing degrees
      • Custom script: call RemoveLocation (udg_Point)
      • -------- --------
      • Set VariableSet UnitGroup = (Units of type Footman)
      • Unit Group - Pick every unit in UnitGroup and do (Actions)
        • Loop - Actions
          • Unit - Kill (Picked unit)
      • Custom script: call DestroyGroup (udg_UnitGroup)
  • Unit Dies
    • Events
      • Unit - A unit Dies
    • Conditions
    • Actions
      • Set Variable Point = (Position of (Triggering unit))
      • Special Effect - Create a special effect at Point using Abilities\Spells\Human\ThunderClap\ThunderClapCaster.mdl
      • Special Effect - Destroy (Last created special effect)
      • Custom script: call RemoveLocation (udg_Point)
 
Last edited:
Level 8
Joined
Jun 16, 2008
Messages
333
It depends on how you're referencing a handle: you create, use, destroy, clean.
In your case, it looks like you're creating a location handle for each unit, therefore you destroy each location after using.

In case you should overwrite, you must destroy the referenced handle first.
If you don't, that would leak as the reference for that memory is completely lost.

In case you're reusing it and not actually creating it then you're fine.

Illustration:

#1 Basic
Set Location = Position Of (Unit) <- Creating a location on the position of a unit.
Move (Enemy Unit) to the Location <- Using it.
call RemoveLocation(udg_Location) <- Destroy after using. No leak.

#2 Overwriting
Set Location = Position Of (Unit) <- Creating a location on the position of a unit.
Move (Enemy Unit) to the Location <- Using it.
Set Location = Position Of (Unit) <- Creating another location on the position of a unit. (Overwriting)
call RemoveLocation(udg_Location) <- Destroy. Leaks.

Why? We overwrite the reference here to another location. The first location has lost its reference, so we can't destroy the first location anymore, which leads to a leak.

What about we put another call for RemoveLocation(udg_Location) at the end?
That wouldn't make sense either as the reference wouldn't be the first location.

What about we put the other call before overwriting the Location? You got it. No leaks.
->
Set Location = Position Of (Unit) <- Creating a location on the position of a unit.
Move (Enemy Unit) to the Location <- Using it.
call RemoveLocation(udg_Location) <- Destroy.
Set Location = Position Of (Unit) <- Creating another location on the position of a unit.
call RemoveLocation(udg_Location) <- Destroy. No leak.

#3 Keeping
Trigger 1 Event - Map Initialization
Set Location = Center Of Map <- Creating a location on the center of the map.

Trigger 2 Event - every 1 second of game time.
Move (Unit) to the Location <- Using it.

Trigger 3 Event - any unit dies
call MoveLocation(Location, 100, 100) <- Moving the location to coordinates. (No handle creation)
Move (Died Unit) to the Location <- Using it again.

^ Leaks? No. Why? No references were lost. We're reusing it. We can call for destroy anytime.

#4 Not referencing
Trigger 1 Event - time elapsed to 0.
Create 1 Unit At (Position Of Unit) facing 0 angle. <- Creating a location without a reference.
Create 1 Unit At (Center Of Region) facing 0 angle. <- ^
Create 1 Unit At (Random Point of Region) facing 0 angle. <- ^
Move (Last Created Unit) to (Position Of Unit) <- ^

^ Leaks? 100%. Is it that bad? Not really, unless you're constantly triggering this which leads to a multiple creations of locations that have no reference and therefore multiple leaks. It's a bad practice.
Let's say:
Trigger 2 Event - every 0.05 seconds of game time.
Move (Last Created Unit) to (Position Of Unit) <- Creating and using a location without a reference.
^This is the bad thing.

Why?
In the Trigger 1 Event - time elapsed to 0. We only have 4 leaking locations that aren't referenced so we couldn't destroy them throughout the game. So that's a minimal leaking locations.
In the Trigger 2 Event - every 0.05 seconds of game time. Obviously, we're constantly creating a location that is not referenced to destroy.
0.05 seconds interval at this rate...
1/0.05 = 20 leaking locations per second.
60*20 = 1200 leaking locations per minute.
60*1200 = 72000 leaking locations per hour.

Trigger 1 - 4 locations leak throughout the whole game.
Trigger 2 - 72000 locations leak per hour of the game.

I hope this clarifies to anyone that also comes up with the question.

So if I call the variable once in a non repeatable trigger, it wont leak? But if a trigger repeats with a variable in it without destroying/removing it, that will leak?

and LUA doesn't really leak because it is local and gets destroyed after the function is called?

I think I got it but let me know if I am off
 

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,578
You're using these "temp" variables for a reason, right? Because you want to use 1 Point variable throughout your triggers instead of creating a new variable every time. But it's "temporary" for a reason, because it's going to be removed. You will ALWAYS want to remove it after you're doing using it, that way you can set it again without creating a leak.

Quoting my post above:

You will need to delete tempLoc every single time you're done using it. So whenever you set tempLoc you must be sure that you have removed the previous tempLoc (if there is one), otherwise you'll create a leak.

A leak is created when you lose reference to a Point (or other leak-able variable types). So let's say you set tempLoc AGAIN while another tempLoc already exists. This will cause a leak because you have lost reference to the old tempLoc. You can no longer use "call RemoveLocation(udg_tempLoc)" to delete the old tempLoc because all that will do is delete the new one. Thus, that old Point exists in the game's memory forever and has become the monster we know as a MEMORY LEAK.


An example of a Point that you wouldn't want to remove is something that will stay constant for the entire game. Like "set CenterPoint = center of playable map area". CenterPoint will never change, it'll always be the center of the map, therefore you don't need to Remove it or re-set it. So anytime you want to create something at the center of the map you can reference CenterPoint, and it's perfectly fine to do so.

And I believe Lua still leaks points even if they're local, but I may be wrong. You don't have to null (nil in lua) the local variable though, which is nice.
 
Last edited:
Level 2
Joined
Mar 21, 2020
Messages
13
Wow, thank you very much people!
I wasn't expecting such in depth explanations.
This helps a lot! +rep
 
Status
Not open for further replies.
Top