• Listen to a special audio message from Bill Roper to the Hive Workshop community (Bill is a former Vice President of Blizzard Entertainment, Producer, Designer, Musician, Voice Actor) 🔗Click here to hear his message!
  • Read Evilhog's interview with Gregory Alper, the original composer of the music for WarCraft: Orcs & Humans 🔗Click here to read the full interview.

Why does this leak?

Status
Not open for further replies.
Level 15
Joined
Aug 7, 2013
Messages
1,338
Hi,

A follow up post from the previous one.

I have a struct which is recursive, called a Quad. A Quad is basically a wrapper around a JASS rect, but each Quad also an array of Quads which in term can have an array of Quads and so on. In addition to an array of Quads, Quads come up an array of JASS lightning of size 4 (you need 4 lightnings to make any rect visible).

I have an algorithm that divides up each Quad into 2 - 5 Quads (random everytime). The algorithm also takes advantage of the recursive structure of Quads, as depending on how deep it goes (a parameter you pass on the algorithm call), it will apply the algorithm to each child Quad, and to the children of those child Quads, and so on and so forth.

Now I notice that for whatever reason, my game either stops making rects, lightning effects, or both if I call the algorithm too many times. I have been of course debugging it by calling the algorithm with some parameter, and then when I reset the parameter, it cleans up all the previous work, and does a new one. But clearly it's not successful in deallocating all the memory, because the algorithm stops working.

Here is my procedure that frees up a Quad, and then calls the procedure on any of its non-zero Quad array members (pseudo-recursive).

JASS:
    method flush takes nothing returns nothing
        local integer i = 0
        loop
            exitwhen i == BOLTS_PER_RECT
            call DestroyLightning(bolts[i])
            set bolts[i] = null
            set i = i + 1
        endloop
        set i = 0
        loop
            exitwhen i == MAX_QUADS
                if subQuads[i] != 0 then
                    call subQuads[i].flush()
                endif
            set i = i + 1
        endloop
        call RemoveRect(r)
        set r = null
        call this.destroy()
    endmethod

Here is my interface with using the algorithm in game (a player chat string event).

JASS:
scope RectPlay initializer init

globals
    Quad q
    rect r
endglobals

private function main takes nothing returns boolean
    local integer iter = S2I(SubString(GetEventPlayerChatString(), 1, 2))
    call q.flush()
    set q = Quad.create(Rect(-13440, 9184, -11744, 10688))
    call q.highlight()
    call q.partitionAlg(iter)
    return false
endfunction


private function init takes nothing returns nothing
    local trigger t = CreateTrigger()
    local Quad p
    call TriggerRegisterPlayerChatEvent(t, Player(0), "-", false)
    call TriggerAddCondition(t, Condition(function main))
    set q = Quad.create(Rect(-13440, 9184, -11744, 10688))
    call q.highlight()
    call q.partitionAlg(1)
endfunction

endscope

The highlight() method does what its name says--it illuminates the boundaries of a given rect with four JASS lightnings. Here it is.

JASS:
    method highlight takes nothing returns nothing
        local integer i = GetRandomInt(0, TOTAL_LIGHTNINGS)
        set bolts[0] = AddLightningEx(LIGHTNING[i], false, minX, minY, 0.0, minX, maxY, 0.0)
        set bolts[1] = AddLightningEx(LIGHTNING[i], false, maxX, minY, 0.0, maxX, maxY, 0.0)
        set bolts[2] = AddLightningEx(LIGHTNING[i], false, minX, minY, 0.0, maxX, minY, 0.0)
        set bolts[3] = AddLightningEx(LIGHTNING[i], false, minX, maxY, 0.0, maxX, maxY, 0.0)
    endmethod

Now, I don't provide the patitionAlg() because it suffices to know how it works. For each Quad.partitionAlg(...), the algorithm creates anywhere from 2 - 5 Quads. Thus, at the end of each call for a given Quad, we can get up to 6 Quads total (since we count the Quad itself). Each Quad is a rect, and each Quad has an array of 4 JASS lightning.

So in the 'worst' case scenario, we will have six rects and 24 lightning effects for each Quad.

Given a level of iterations (when the algorithm will stop applying to the Quad array members), the maximum rects created is so:

iter = 0 : 6 rects

iter = 1: 6 + (6 x 6) rects

iter = 2: 6 + (6 x 6) + (6 x 6 x 6) rects

...

iter = n: 6 + (62) + (63) + ... + (6n + 1) rects

which we can condense into (changing our notation by +1 as well):

r049w8.png


The amount of JASS lightning is simple 4 x the summation above, since every Quad gets 4 lightnings.

The algorithm breaks in this total. What happens is that it only partially cleans up the the last series of Quads, and then subsequent calls don't do anything

Sum(2) + Sum(3) + Sum(3) = 522 rects and 2088 lightning effects.

If I then do Sum(3), here is what happens.


Note that these are all called linearly temporally--I do Sum(2) then Sum(3) then Sum(3) then Sum(3). When I do a new call, I clean up the screen using the flush() method I showed.

2lxjxgy.png


2rnj4mg.png


28wcbig.png


Here is where it breaks. If you notice, this actually just the remnants of the above call. But it doesn't clean up all of the rects. And if I try any other calls, these lightning effects/rects are here for good. Everything stops at this point.
2rr9r34.png


So from what I showed, at some point it fails to clean up all the rects/lightning effects. And then it won't make any more rects or lightning effects. This is peculiar to me. Any ideas what's going on?
 
Last edited:

Dr Super Good

Spell Reviewer
Level 64
Joined
Jan 18, 2005
Messages
27,258
Check the value of your struct instances (yes they are integers). If you notice them not being recycled then you have a struct leak. This would easily cause the problem you are describing as once it hits over 8192-8190 instances it will stop working at all.

Both rects and lighting should not have such limits so should not be the cause (even if they are leaking).

Also make sure your recursion logic works. Another problem could be that you get nonsense recursion of sub rects as a result of a logic flaw (such as using the initial value of a struct without setting it to a safe state) which results in struct leaks.
 
Level 15
Joined
Aug 7, 2013
Messages
1,338
The problem was in my flush() method. Its loop condition for cleaning up old struct members was wrong, since I learned while the shared array of structs across structs that I used initially has its values set to 0, afterward the values stick to whatever is left over in them. So it was reading off "null" structs so to speak and attempting to clean them up, causing the flush() to crash and grinding everything to a halt.
 
Status
Not open for further replies.
Top