Pregunta

He escuchado a Mumplings alrededor de los Internet sobre poder intercambiar un código en HOT en LUA, similar a cómo se hace en Java, Erlang, Lisp, etc. Sin embargo, 30 minutos de buscar en Google para él no han provocado nada. ¿Alguien ha leído algo sustancial sobre esto? ¿Alguien tiene alguna experiencia haciéndolo? ¿Funciona en Luajit o solo en la VM de referencia?

Estoy más interesado en la técnica como un atajo en el desarrollo/depuración que una ruta de actualización en un entorno en vivo.

¿Fue útil?

Solución

Lua, y la mayoría de los idiomas de secuencias de comandos, no admiten la forma más generalizada de "intercambio en caliente" como lo define. Es decir, no puedes garantizadamente Cambie un archivo en el disco y tenga algún cambio en él propagarse en un programa de ejecución.

Sin embargo, Lua, y la mayoría de los idiomas de secuencias de comandos, son perfectamente capaces de revisado formas de intercambio en caliente. Las funciones globales son funciones globales. Los módulos simplemente cargan funciones globales (si las usa de esa manera). Entonces, si un módulo carga funciones globales, puede volver a cargar el módulo nuevamente si se cambia, y esas referencias de funciones globales cambiarán a las funciones recién cargadas.

Sin embargo, Lua, y la mayoría de los idiomas de secuencias de comandos para el caso, no garantizan esto. Todo lo que está sucediendo es el cambio de datos del estado global. Si alguien copió una función antigua en una variable local, aún puede acceder a ella. Si su módulo usa datos de estado local, la nueva versión del módulo no puede acceder al estado del módulo anterior. Si un módulo crea algún tipo de objeto que tenga funciones miembros, a menos que esos miembros sean obtenidos de los globales, estos objetos siempre se referirán a las funciones antiguas, no a las nuevas. Etcétera.

Además, Lua no es seguro de hilo; No puedes simplemente interrumpir un lua_State En algún momento e intente cargar un módulo nuevamente. Por lo tanto, tendría que configurar un punto específico en el tiempo para que revise las cosas y la recarga de archivos cambió.

Entonces puedes hacerlo, pero no es "compatible" en el sentido de que puede suceder. Tienes que trabajar para ello, y debe tener cuidado con cómo escribe las cosas y qué pone en las funciones locales frente a globales.

Otros consejos

Como Nicol dijo, el idioma en sí no lo hace por ti.

Sin embargo, si desea implementar algo como esto usted mismo, no es tan difícil, lo único que "prevenirle" es cualquier referencia "sobrante" (que aún apuntará al código anterior) y el hecho require almacena en caché su valor de devolución en package.loaded.

La forma en que lo haría es dividiendo su código en 3 módulos:

  • la lógica de recarga en el punto de entrada (main.lua)
  • Cualquier datos que desee preservar a través de las recargas (data.lua)
  • el código real para recargar (payload.lua), asegurándose de no tener ninguna referencia a eso (lo que a veces no es posible cuando tiene que dar devoluciones de llamada a alguna biblioteca; ver a continuación).
-- main.lua:
local PL = require("payload")
local D = require("data")

function reload(module)
  package.loaded[module]=nil -- this makes `require` forget about its cache
  return require(module)
end

PL.setX(5)
PL.setY(10)

PL.printX()
PL.printY()

-- .... somehow detect you want to reload:
print "reloading"
PL = reload("payload") -- make sure you don't keep references to PL elsewhere, e.g. as a function upvalue!

PL.printX()
PL.printY()
-- data.lua:
return {} -- this is a pretty dumb module, it's literally just a table stored in `package.loaded.data` to make sure everyone gets the same instance when requiring it.
-- payload.lua:
local D = require("data")
local y = 0
return {
  setX = function(nx) D.x = nx end, -- using the data module is preserved
  setY = function(ny) y = ny end, -- using a local is reset upon reload
  printX = function() print("x:",D.x) end,
  printY = function() print("y:", y) end
}

producción:

x: 5
y: 10
reloading
x: 5
y: 0

Podría desarrollar esa lógica un poco mejor al tener un "módulo de registro" que realice un seguimiento de todos los requisitos/recargadores para usted y abstrae cualquier acceso a los módulos (lo que le permite reemplazar las referencias) y, utilizando el __index Metatible en ese registro, podría hacerlo prácticamente transparente sin tener que llamar a los getteros feos por todo el lugar. Esto también significa que puede suministrar devoluciones de llamada "un revestimiento" que en realidad simplemente llaman a través del registro, si alguna biblioteca de terceros necesita eso.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top