My program crashes codea

Hi -

I have been writing longish program. It’s currently well over 30 tabs / classes.

I can get it in a state where Codea will crash in about a minute of leaving it idle. I think I have the problem narrowed down to the code below.

In fact I think it’s the creation of the local table “tiles” that is created every draw that is the problem. But I don’t understand why.

If any fine folk could look at this and give me a clue that would be great.

Thanks

-- here we play a tile if we are snapping
-- then we record what type it was and decide if
-- we are done playing tiles for this action
function Expand:PreDraw( player )
    local estate = player:GetEstate()
    local openTiles = estate:getOpenTiles()

    local tiles = {}
    for i = 1, #openTiles do
        local coords = openTiles[i]:getCoords()
        tiles[#tiles + 1] = Tile( coords, OPEN )
        
        local pos = openTiles[i]:GetDPos()
        pos = pos + player:GetZoomPos()
        tiles[i]:SetDPos( pos )
    end

    local tile = self.spawn:Snapped( tiles )
    if tile ~= nil then
        local qtype = estate:playTile( tile )
        if qtype ~= nil then
            self.quadsPlayed[qtype] = self.quadsPlayed[qtype] + 1
        end
        local t = tile:getType()
        self.tilesPlayed[t] = self.tilesPlayed[t] + 1
        player:RemovePlayerTile( t )
        self.tilesLeft = self.tilesLeft - 1
        
        self:InitSpawn()
        self.game:AdjustExchange()
        
        if self.tilesLeft == 0 then
            self.state = EE_DONE
            self.player:SetDrawOpen( false )
        end
    end
    
end

I suspect that OPEN is just a state that a tile can be in like BEGAN, MOVING & ENDED

difficult to help from so little info…
1/ try to put collectgarbage() at the beginning of this function.
2/ in tiles[#tiles + 1] = Tile( coords, OPEN ) do you open a file? try to open it once only, and put it into a static table. Then populate your tiles table from this one.

Agreed. Sorry. I tried to find an amount of code that would not scare off reviewers.

No open files. I am writing a tile laying type board game.this function checks to see if the player has dragged one of their tiles to an open location.

I have to create a temp table of open tiles so I can normalize their xy position. To compare against the xy tile they are dragging.

Collectgarbage didnt help. Thanks for trying.

  • Dwight

@Dwight - it could be any of the many functions your code is calling, it’s hard to see what it could be from what you’ve provided

program crash with no error usually comes from too much memory used. This happens.
1/ when you make big images, many of them.
2/ when you create many objects without clearing them. You could check with memory tracking.

I found this here and installed it.

-- Put in Main draw func
    if (ElapsedTime - elapsedTime > 1) then
        memory = collectgarbage("count") / 1024
        elapsedTime = ElapsedTime
    end

    pushMatrix()
    resetMatrix()
    pushStyle()
    fontSize(20)
    fill(255, 255, 255, 255)
    FPS = FPS * 0.9 + 0.1 / DeltaTime
    text(string.format("%.0fFPS %.1fMB", FPS, memory), WIDTH - 80, 30)
    popStyle()
    popMatrix()

It shows the memory at about 5MB when it crashes Codea. Is that horrible. It is definitely climbing which is bad.

When I remove the code at the top of this thread the rate the memory climbs is much slower. But it still climbs which can’t be good.

Thanks again for your help

  • Dwight

‘collectgarbage didn’t help’.
if you put collectgarbage() at the right locations, or call it every n frames in the draw, your memory count should never climb up endlessly as you describe. So, either you dont call it often enough, either you never dereference the data you create, so they will never be deleted by the garbage collector…
I am fairly sure this bug is in your code, somewhere, and it is not codea’s fault… but you havent posted the code so you’ll just have to find it yourself. Good luck!

I agree its not a Codea problem.

I think this is the problem:
“you never dereference the data you create, so they will never be deleted by the garbage collecton”

If its local does it need to be dereference?
I will scrub my code.

I would love to share the whole thing. Is there an easy way to share lots of code? I spent a small amount of time trying the ccConfig tab and Codea Comunity. I think I will try this again but I used to get a request failed error: Not found (404)

Is 5MB of data large?

thanks again.

@Dwight One thing I would try is to see what the memory size is in some of your different functions. You could add 2 lines of code like below at the start and end of any function with that function name that you think might be a problem. Because this is saved in global data, after your program crashes, you could “readGlobalData” with those function names and see what functions are high in memory and maybe that will lead you to your problem. Also, if you see a “start” without an “end” then that would tell you what function crashed. It’s also possible that the function that crashes might execute several times, so you’ll have a “start” and “end”. If that’s the case, you could add a function counter to help identify the function that crashes. It might take a lot of time to track it down, but this could be a start.

function xyz()
     saveGlobalData("xyz_start",collectgarbage("count"))  -- memory used
     .....
     .....
     saveGlobalData("xyz_end",collectgarbage("count"))
end

@Dwight Maybe adding a counter isn’t a good idea. That could create a lot of GlobalData that you would have to delete.

EDIT: I was originally thinking of the counter as part of the name. If the counter is the value, then the counter could be an odd number at the start and an even number at the end. That way if you see a function name with a larger odd number than the even number, then you know that function was started, but crashed before it ended.

@dave1707 Thanks for the good suggestions. I get the gist of what you are saying and I can try it.

@Jmv38

I have added

    collect = collect + 1
    if collect % 6 == 0 then
        collectgarbage()
    end

To my main draw. It has reduced the noise of the memory meter nicely.

The code at the top of this thread still increases the total memory used by about 1 tenth of a MB per second. When I comment it out the usage climb is almost nil.

I strongly think its this code:

 local tiles = {}
    for i = 1, #openTiles do
        local coords = openTiles[i]:getCoords()
        tiles[#tiles + 1] = Tile( coords, OPEN )

        local pos = openTiles[i]:GetDPos()
        pos = pos + player:GetZoomPos()
        tiles[i]:SetDPos( pos )
    end

The functions calls do very little. Thery are mostly getters and setters. its the creation of a local table of Tiles that I think is the memory leak. But it doesnt make sense because at the end of the function I set tiles = {}. I thnik that wold make it to be garbage collected. Does that make sense?

Thanks

  • Dwight

this is correct.
So for each object you create (openTiles?) check if the number of element is not endlessly increasing. Maybe?

So is it a smart pointer issue…

I create a temp table of tiles. Then I pass that table to spawn::Snapped()

I think this is reference to the temp table. But maybe it’s a copy or like smart pointers the table is marked as “needed” ?

Below is the snapped function.most of the time state is not SNAPPING. So this function does nothing. But maybe the damage is done.

function Spawn:Snapped( openTiles )
    local tile = nil
    
    if self.state == S_SNAPPING then
        local inside = nil  -- tile we are intersecting
        for i = 1, #openTiles do
            if openTiles[i]:inside(self.dragTile:GetDPos()) then
                inside = openTiles[i]
                break
            end
        end
        
        if inside ~= nil then
            local coords = inside:getCoords()
            tile = Tile( coords, self.dragType )
        end
    
        self.state = S_NOT_DRAGGING
    end
    
    return tile
end

So I “fixed” the problem. I refactored snapped to do its own calculation:

function Spawn:Snapped( openTiles )
    local tile = nil
    
    if self.state == S_SNAPPING then
        local inside = nil  -- tile we are intersecting
        for i = 1, #openTiles do
            local p = self.dragTile:GetDPos() - self.player:GetZoomPos()
            if openTiles[i]:inside( p ) then
                inside = openTiles[i]
                break
            end
        end
        
        if inside ~= nil then
            local coords = inside:getCoords()
            tile = Tile( coords, self.dragType )
        end
    
        self.state = S_NOT_DRAGGING
    end
    
    return tile
end

I like this change. It’s simpler. But I don’t like that I don’t know what I was doing wrong.

  • Dwight

change

            local p = self.dragTile:GetDPos() - self.player:GetZoomPos()

by

            local p = self.dragTile:GetDPos()

if the bug comes back, then that means it is in self.dragTile:GetDPos()

Sorry I was not clear.

All self.dragTile:GetDPos() and self.player:GetZoomPos() do are return a member variable.

The main change was removing

 local tiles = {}
    for i = 1, #openTiles do
        local coords = openTiles[i]:getCoords()
        tiles[#tiles + 1] = Tile( coords, OPEN )

        local pos = openTiles[i]:GetDPos()
        pos = pos + player:GetZoomPos()
        tiles[i]:SetDPos( pos )
    end

What that did was create a temp table of tiles where I could adjust their screen pos. then I passed that temp table to

function Spawn:Snapped( openTiles )
    local tile = nil

    if self.state == S_SNAPPING then
        local inside = nil  -- tile we are intersecting
        for i = 1, #openTiles do
            if openTiles[i]:inside(self.dragTile:GetDPos()) then
                inside = openTiles[i]
                break
            end
        end

        if inside ~= nil then
            local coords = inside:getCoords()
            tile = Tile( coords, self.dragType )
        end

        self.state = S_NOT_DRAGGING
    end

    return tile
end

But now I just pass in openTiles instead of the temp table and then do the calculation within :Snapped()

The memory leak I think involves the temp table not getting garbage collected. But I don’t understand why. Maybe because it’s being copied or something.but not creating the temp table has “fixed” the probmem.

Thank you for your time and patients.

  • Dwight