From 49df7b4895fe4cf4a491d3c81b88a2e105c549b0 Mon Sep 17 00:00:00 2001 From: Josh Yelon Date: Mon, 1 Mar 2021 18:10:40 -0500 Subject: [PATCH] LuaSnap is now working based on eris (at least superficially). --- luprex/core/cpp/luasnap.cpp | 68 +++++++++++++++++++++++++++++++++--- luprex/core/cpp/luasnap.hpp | 13 ++----- luprex/core/cpp/luastack.hpp | 1 + 3 files changed, 67 insertions(+), 15 deletions(-) diff --git a/luprex/core/cpp/luasnap.cpp b/luprex/core/cpp/luasnap.cpp index 1ceba283..6c9b81a3 100644 --- a/luprex/core/cpp/luasnap.cpp +++ b/luprex/core/cpp/luasnap.cpp @@ -9,6 +9,15 @@ LuaSnap::LuaSnap() { state_ = luaL_newstate(); LuaStack LS(state_); + + // Create the persist table and the unpersist table. + // + // These tables need to contain all C functions. Whenever + // the source module inserts a C function into the lua environment, + // it also needs to register the C function in these tables. + // + // I don't think anything else needs to go in these tables. + // LS.rawset(LuaRegistry, "persist", LuaNewTable); LS.rawset(LuaRegistry, "unpersist", LuaNewTable); } @@ -21,11 +30,32 @@ bool LuaSnap::have_snapshot() const { return !snapshot_.empty(); } -static void oss_writer(lua_State *L, const void *p, size_t sz, void *ud) { +static int oss_writer(lua_State *L, const void *p, size_t sz, void *ud) { std::ostringstream *oss = (std::ostringstream *)ud; oss->write((const char *)p, sz); + return 0; } +// Convert a C++ string into a lua_Reader. +class StringReader { +public: + const std::string &str_; + bool done_; + StringReader(const std::string &s) : str_(s), done_(false) {} + + static const char *fn(lua_State *L, void *data, size_t *size) { + StringReader *sr = (StringReader*)data; + if (sr->done_) { + *size = 0; + return nullptr; + } else { + sr->done_ = true; + *size = sr->str_.size(); + return sr->str_.c_str(); + } + } +}; + void LuaSnap::snapshot() { // Snapshot and the lua stack should both be empty. assert(snapshot_.empty()); @@ -43,16 +73,20 @@ void LuaSnap::snapshot() { LS.rawset(regcopy, key, value); } - // Remove certain things from the copy. + // Remove certain things from the copy. These items + // don't get included in the snapshot. + // + // From a modularity perspective, this is bad. + // LS.rawset(regcopy, LUA_RIDX_MAINTHREAD, LuaNil); LS.rawset(regcopy, "persist", LuaNil); LS.rawset(regcopy, "unpersist", LuaNil); LS.rawset(regcopy, "world", LuaNil); LS.rawset(regcopy, "gui", LuaNil); - LS.result(); - // Get the permanents table from the registry. + // Get the eris permanents table from the registry. LS.rawget(permstable, LuaRegistry, "persist"); + assert(LS.istable(permstable)); // When we call 'LS.result', this should leave the // permstable and the regcopy on the stack. @@ -64,9 +98,35 @@ void LuaSnap::snapshot() { std::ostringstream oss; eris_dump(state_, oss_writer, (void *)&oss); snapshot_ = oss.str(); + std::cerr << "Eris dump is " << snapshot_.size() << " bytes." << std::endl; + lua_settop(state_, 0); } void LuaSnap::rollback() { + // Snapshot should have data, lua stack should be empty. assert(!snapshot_.empty()); + assert(lua_gettop(state_) == 0); + + // Call eris with the permanents table and passing the snapshot as a lua_Reader. + lua_getfield(state_, LUA_REGISTRYINDEX, "unpersist"); + StringReader sr(snapshot_); + eris_undump(state_, StringReader::fn, &sr); + assert(lua_gettop(state_) == 2); + + // Set up a stack frame. + LuaArg permstable, regcopy; + LuaVar key, value; + LuaStack LS(state_, permstable, regcopy, key, value); + + assert(LS.istable(regcopy)); + + // Copy the contents of the snapshot over to the registry. + LS.set(key, LuaNil); + while (LS.next(regcopy, key, value) != 0) { + LS.rawset(LuaRegistry, key, value); + } + LS.result(); + assert(lua_gettop(state_) == 0); + snapshot_.clear(); } diff --git a/luprex/core/cpp/luasnap.hpp b/luprex/core/cpp/luasnap.hpp index c8ce019e..d8794c34 100644 --- a/luprex/core/cpp/luasnap.hpp +++ b/luprex/core/cpp/luasnap.hpp @@ -6,17 +6,8 @@ // possible to roll the entire interpreter back to a previously-snapshotted // state. // -// To accomplish this, we take advantage of the 'allocf' parameter to -// lua_newstate. This lets us hook the lua allocator, which in turn lets us -// find every block of memory currently in use by lua. To snapshot, we 'memcpy' -// every block of memory that lua is using into a buffer. To rollback, we just -// 'memcpy' the data back. -// -// The current implementation is a proof-of-concept. It's quite wasteful of -// memory, roughly doubling the amount of RAM that LUA uses. It's also not -// super-fast, since it allocates tons of tiny blocks. But it totally -// works, so it demonstrate that this method of snapshot and rollback is -// feasible. +// To accomplish this, we use eris serialization. This is messy and not +// very modular, but it does work. // ///////////////////////////////////////////////////////////////////////////// diff --git a/luprex/core/cpp/luastack.hpp b/luprex/core/cpp/luastack.hpp index e5ab639f..b3edcdab 100644 --- a/luprex/core/cpp/luastack.hpp +++ b/luprex/core/cpp/luastack.hpp @@ -158,6 +158,7 @@ extern "C" { #include "lua.h" #include "lauxlib.h" #include "lualib.h" +#include "eris.h" } #include