LuaSnap is now working based on eris (at least superficially).
This commit is contained in:
@@ -9,6 +9,15 @@
|
|||||||
LuaSnap::LuaSnap() {
|
LuaSnap::LuaSnap() {
|
||||||
state_ = luaL_newstate();
|
state_ = luaL_newstate();
|
||||||
LuaStack LS(state_);
|
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, "persist", LuaNewTable);
|
||||||
LS.rawset(LuaRegistry, "unpersist", LuaNewTable);
|
LS.rawset(LuaRegistry, "unpersist", LuaNewTable);
|
||||||
}
|
}
|
||||||
@@ -21,11 +30,32 @@ bool LuaSnap::have_snapshot() const {
|
|||||||
return !snapshot_.empty();
|
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;
|
std::ostringstream *oss = (std::ostringstream *)ud;
|
||||||
oss->write((const char *)p, sz);
|
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() {
|
void LuaSnap::snapshot() {
|
||||||
// Snapshot and the lua stack should both be empty.
|
// Snapshot and the lua stack should both be empty.
|
||||||
assert(snapshot_.empty());
|
assert(snapshot_.empty());
|
||||||
@@ -43,16 +73,20 @@ void LuaSnap::snapshot() {
|
|||||||
LS.rawset(regcopy, key, value);
|
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, LUA_RIDX_MAINTHREAD, LuaNil);
|
||||||
LS.rawset(regcopy, "persist", LuaNil);
|
LS.rawset(regcopy, "persist", LuaNil);
|
||||||
LS.rawset(regcopy, "unpersist", LuaNil);
|
LS.rawset(regcopy, "unpersist", LuaNil);
|
||||||
LS.rawset(regcopy, "world", LuaNil);
|
LS.rawset(regcopy, "world", LuaNil);
|
||||||
LS.rawset(regcopy, "gui", 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");
|
LS.rawget(permstable, LuaRegistry, "persist");
|
||||||
|
assert(LS.istable(permstable));
|
||||||
|
|
||||||
// When we call 'LS.result', this should leave the
|
// When we call 'LS.result', this should leave the
|
||||||
// permstable and the regcopy on the stack.
|
// permstable and the regcopy on the stack.
|
||||||
@@ -64,9 +98,35 @@ void LuaSnap::snapshot() {
|
|||||||
std::ostringstream oss;
|
std::ostringstream oss;
|
||||||
eris_dump(state_, oss_writer, (void *)&oss);
|
eris_dump(state_, oss_writer, (void *)&oss);
|
||||||
snapshot_ = oss.str();
|
snapshot_ = oss.str();
|
||||||
|
std::cerr << "Eris dump is " << snapshot_.size() << " bytes." << std::endl;
|
||||||
|
lua_settop(state_, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void LuaSnap::rollback() {
|
void LuaSnap::rollback() {
|
||||||
|
// Snapshot should have data, lua stack should be empty.
|
||||||
assert(!snapshot_.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();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,17 +6,8 @@
|
|||||||
// possible to roll the entire interpreter back to a previously-snapshotted
|
// possible to roll the entire interpreter back to a previously-snapshotted
|
||||||
// state.
|
// state.
|
||||||
//
|
//
|
||||||
// To accomplish this, we take advantage of the 'allocf' parameter to
|
// To accomplish this, we use eris serialization. This is messy and not
|
||||||
// lua_newstate. This lets us hook the lua allocator, which in turn lets us
|
// very modular, but it does work.
|
||||||
// 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.
|
|
||||||
//
|
//
|
||||||
/////////////////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
|||||||
@@ -158,6 +158,7 @@ extern "C" {
|
|||||||
#include "lua.h"
|
#include "lua.h"
|
||||||
#include "lauxlib.h"
|
#include "lauxlib.h"
|
||||||
#include "lualib.h"
|
#include "lualib.h"
|
||||||
|
#include "eris.h"
|
||||||
}
|
}
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|||||||
Reference in New Issue
Block a user