1

On a nodemcu, I'm using a closure to send a file over a socket, like this:

function sendfile(sock, name)
    local fd = file.open(name, "r")

    function sendchunk()
        local data = fd:read()
        if data then
            sock:send(data)
        else
            fd:close()
            sock:close()
        end
    end

    sock:on("sent", sendchunk)
    sendchunk()
end

After transferring a few files, the interpreter panics due to "not enough memory". I can imagine this may be due to closure still hanging around. It would be difficult for the garbage collector to determine that sendchunk() will not be called again once the file and socket are closed.

Unfortunately my googling has not revealed a method to end the closure and free the memory it is using.

Am I using a wrong method to do this? Should I perhaps use an anonymous function or something?

Egor Skriptunoff
  • 23,359
  • 2
  • 34
  • 64
Schelte Bron
  • 4,113
  • 1
  • 7
  • 13
  • Have you set `sendchunk = nil` before running the garbage collector? – lhf Apr 23 '18 at 20:38
  • I expected the garbage collector to run automatically. But yes, I have tried to set `sendcheck = nil` in the else block. Using `collectgarbage("count")` I can still see the memory use increasing after each file transfer. Running `collectgarbage()` doesn't appear to release any of that. – Schelte Bron Apr 23 '18 at 21:21
  • in your code sendchunk is a global function so the garbage collector will not remove it until you set it nil as lhf suggested. note that Egor made it local in his answer. but that's not the cause of the memory leak as he also explains. – Piglet Apr 24 '18 at 06:34
  • OK, it seems that collectgarbage("count") doesn't work as advertised. After several 100's of file transfers using Egor's version of the code, it reports that the total amount of memory in use by Lua is 7485 Kbytes, which is very impressive on a device that only has 128 Kbytes of memory to start with. So, collectgarbage("count") doesn't appear to be a usable method of determining if memory is being leaked. – Schelte Bron Apr 24 '18 at 18:47

1 Answers1

4

It was already mentioned here that :on() invocation saves reference to callback closure inside Lua registry.
I guess this closure will be cleared from Lua registry inside __gc metamethod of sock object,
but sock object will not be collected if the closure references sock object.
To solve this problem you should avoid hard-coding a reference to sock upvalue in the body of sendchunk() function.
For example, exploit the fact that the first argument passed to callback function is always the socket object.

function sendfile(sock, name)
   local fd = file.open(name, "r")

   local function sendchunk(sck)
      local data = fd:read()
      if data then
         sck:send(data)
      else
         fd:close()
         sck:close()
      end
   end

   sock:on("sent", sendchunk)
   sendchunk(sock)
end
Egor Skriptunoff
  • 23,359
  • 2
  • 34
  • 64
  • I did several 100's of file transfers using this version of the function and it is still happily ploughing along. It does seem like this plugged my memory leak. Thanks. – Schelte Bron Apr 24 '18 at 18:51