Lua Notes

Hot-code loading
Login

hot-code loading

To get hot-code loading you need to separate your program into an immutable loader (compiled to an executable and run directly) and a mutable hot-loaded library (compiled to a shared object and linked at runtime). When and how to do this are matters completely under your control.

To demonstrate this, a library:

local function greet() <cexport 'greet'>
  print('hello, world')
end

and a loader:

local ffi = require 'ffi'  -- from nelua-batteries
local fs = require 'fs'    -- from nelua-batteries

local function usleep(usec: integer): cint <cimport, nodecl, cinclude '<unistd.h>'> end

local LIB <comptime> = './greet.so'

local function load(): (ffi, function())
  local lib = ffi.load(LIB)
  assert(lib.handle ~= nilptr, "can't load lib")
  local f = lib:get('greet')
  assert(f, "can't find sym")
  return lib, (@function())(f)
end

local function unload(lib: ffi)
  assert(lib:unload(), 'failed to unload lib')
end

local function fresh(last: int64): (int64, boolean)
  local buf, err, code = fs.stat(LIB)
  assert(code == 0, err)
  return buf.mtime, last ~= buf.mtime
end

local function main()
  local last: int64
  last = fresh()
  local lib, f = load()
  defer unload(lib) end
  local reload: boolean

  while true do
    last, reload = fresh(last)
    if reload then
      unload(lib)
      lib, f = load()
    end
    f()
    usleep(200 * 1000)
  end
end

main()

And to run it:

$ nelua -P noentrypoint -o greet.so greet.nelua
$ nelua live.nelua
hello, world
hello, world
hello, world
hello, world
...

While this is running you can edit greet.nelua, rebuild the .so, and see the changes reflected in the running program.

It's not necessary to set pragmas.noentrypoint since the linking is at runtime, but for static or dynamic linking this avoids a name collision on main. NB. Windows has some extra security around DLLs that will probably require you to swap back and forth between two libraries rather than update a single library while it's being used.