Refactor code to make it easier to do 'nopredict' inside of any function without having a dependency on world model.

This commit is contained in:
2023-03-01 16:07:13 -05:00
parent aa77480fb5
commit 9ce34d950b
15 changed files with 86 additions and 49 deletions

View File

@@ -323,8 +323,8 @@ bool AnimStep::from_string(const eng::string &config) {
return true;
}
AnimQueue::AnimQueue(util::WorldType wt) {
version_autoinc_ = (wt == util::WORLD_TYPE_MASTER);
AnimQueue::AnimQueue(WorldType wt) {
version_autoinc_ = (wt == WORLD_TYPE_MASTER);
size_limit_ = 10; // Default size limit.
steps_.emplace_back();
steps_.front().keep_state_only();
@@ -576,8 +576,8 @@ static bool diff_works(const AnimQueue &master, AnimQueue &sync) {
LuaDefine(unittests_animqueue, "", "some unit tests") {
// Useful objects.
AnimStep stp;
AnimQueue aq(util::WORLD_TYPE_MASTER);
AnimQueue aqds(util::WORLD_TYPE_S_SYNC);
AnimQueue aq(WORLD_TYPE_MASTER);
AnimQueue aqds(WORLD_TYPE_PREDICTIVE);
StreamBuffer sb;
// Debug string of a newly initialized queue

View File

@@ -169,7 +169,7 @@ private:
class AnimQueue : public eng::nevernew {
public:
// World type determines whether versions increment or autozero
AnimQueue(util::WorldType wt);
AnimQueue(WorldType wt);
// Simple getters.
const AnimStep &nth(int n) const { return steps_[n]; }

View File

@@ -114,7 +114,7 @@ private:
void event_init(int argc, char *argv[])
{
world_.reset(new World(util::WORLD_TYPE_STANDALONE));
world_.reset(new World(WORLD_TYPE_MASTER));
world_->update_source(get_lua_source());
world_->run_unittests();
stop_driver();

View File

@@ -7,6 +7,8 @@ LuaDefine(global_once, "name", "for a given string, returns true exactly once")
LuaVar oncedb, val;
LuaStack LS(L, name, flag, oncedb, val);
LS.guard_nopredict("global.once");
// Get a pointer to the oncedb.
LS.rawget(oncedb, LuaRegistry, "oncedb");
if (!LS.istable(oncedb)) {
@@ -25,11 +27,14 @@ LuaDefine(global_once, "name", "for a given string, returns true exactly once")
return LS.result();
}
LuaDefine(global_clearonce, "name", "reset the specified once-flag") {
LuaArg name;
LuaVar oncedb;
LuaStack LS(L, name, oncedb);
LS.guard_nopredict("global.clearonce");
// Get a pointer to the oncedb.
LS.rawget(oncedb, LuaRegistry, "oncedb");
if (!LS.istable(oncedb)) {
@@ -50,11 +55,6 @@ LuaDefine(global_table, "globalname", "get a table where global data can be stor
// Get a pointer to the globaldb.
LS.rawget(globaldb, LuaRegistry, "globaldb");
// nopredict
if (lua_isyieldable(L) && (!LS.istable(globaldb))) {
return lua_yield(L, 0);
}
// Get the globaltab from the globaldb, sanity check it.
LS.rawget(globaltab, globaldb, globalname);
if (LS.istable(globaltab)) {

View File

@@ -24,7 +24,7 @@ public:
public:
void set_initial_state() {
// Create the world model.
world_.reset(new World(util::WORLD_TYPE_C_SYNC));
world_.reset(new World(WORLD_TYPE_PREDICTIVE));
// This is a temporary actor that will be used only until the server sends
// us the first difference transmission. We do this only to establish

View File

@@ -33,7 +33,7 @@ public:
public:
virtual void event_init(int argc, char *argv[]) {
// Create the master world model.
master_.reset(new World(util::WORLD_TYPE_MASTER));
master_.reset(new World(WORLD_TYPE_MASTER));
// Update the source code of the master model.
master_->update_source(get_lua_source());
@@ -189,7 +189,7 @@ public:
Client *client = new Client;
client->actor_id_ = master_->create_login_actor();
client->channel_ = std::move(chan);
client->sync_.reset(new World(util::WORLD_TYPE_S_SYNC));
client->sync_.reset(new World(WORLD_TYPE_PREDICTIVE));
client->sync_->create_login_actor();
clients_.emplace_back(client);
stdostream() << "New client: actor id=" << client->actor_id_ << std::endl;

View File

@@ -85,7 +85,8 @@ void LuaSnap::deserialize(StreamBuffer *sb) {
void *ud = sb->lua_reader_ud(len);
// Call eris with the permanents table and passing the snapshot as a lua_Reader.
lua_getfield(state_, LUA_REGISTRYINDEX, "unpersist");
lua_pushstring(state_, "unpersist");
lua_rawget(state_, LUA_REGISTRYINDEX);
eris_undump(state_, sb->lua_reader, ud);
assert(lua_gettop(state_) == 2);

View File

@@ -466,6 +466,24 @@ void LuaStack::setvisited(LuaSlot tab, bool visited) const {
lua_modflagbits(L_, tab.index(), 0x0010, visited ? 0x0010 : 0);
}
WorldType LuaStack::world_type() const {
lua_pushstring(L_, "worldtype");
lua_rawget(L_, LUA_REGISTRYINDEX);
lua_Integer n = lua_tointeger(L_, -1);
lua_pop(L_, 1);
assert(n != 0);
return (WorldType)n;
}
void LuaStack::guard_nopredict(const char *fn) {
if (lua_isyieldable(L_)) {
if (!is_authoritative()) {
lua_yield(L_, 0);
luaL_error(L_, "unexplained nopredict failure in %s", fn);
}
}
}
LuaKeywordParser::LuaKeywordParser(lua_State *L, int slot) {
L_ = L;
slot_ = slot;

View File

@@ -226,6 +226,13 @@ int LuaTypeTagValue(lua_State *L) { return 0; }
#define LUA_TT_GLOBALDB 22
#define LUA_TT_CLASS 23
// World types enum.
enum WorldType {
WORLD_TYPE_MASTER = 1,
WORLD_TYPE_PREDICTIVE = 2,
};
// We use lightuserdata to store 'tokens': short
// strings of 8 characters or less. These tokens
// are useful as unique markers. The 8 characters
@@ -517,6 +524,17 @@ public:
bool getvisited(LuaSlot tab) const;
void setvisited(LuaSlot tab, bool visited) const;
// Return the world type (from the registry).
WorldType world_type() const;
// World types that are authoritative.
static bool is_authoritative(WorldType t) { return (t == WORLD_TYPE_MASTER); }
bool is_authoritative() { return is_authoritative(world_type()); }
// Stop execution of this thread if in a nonauth model,
// and if the thread is not a probe.
void guard_nopredict(const char *fn);
// Return true if the int64 value can be stored as a lua number.
static bool int64_storable(int64_t v) { return (v <= MAXINT) && (v >= -MAXINT); }
};

View File

@@ -98,7 +98,7 @@ private:
void event_init(int argc, char *argv[])
{
world_.reset(new World(util::WORLD_TYPE_STANDALONE));
world_.reset(new World(WORLD_TYPE_MASTER));
world_->update_source(get_lua_source());
world_->run_unittests();
actor_id_ = world_->create_login_actor();

View File

@@ -198,13 +198,6 @@ bool valid_number(string_view v, bool plus, bool minus, bool dec, bool exp);
namespace util {
enum WorldType {
WORLD_TYPE_STANDALONE,
WORLD_TYPE_C_SYNC,
WORLD_TYPE_S_SYNC,
WORLD_TYPE_MASTER,
};
enum MessageType {
MSG_NULL,
MSG_DIFF,

View File

@@ -29,12 +29,12 @@ World *World::fetch_global_pointer(lua_State *L) {
World::~World() {
}
World::World(util::WorldType wt) {
World::World(WorldType wt) {
// Master world model by default.
world_type_ = wt;
// Initialize the ID allocator in master mode.
if (wt == util::WORLD_TYPE_MASTER || wt == util::WORLD_TYPE_STANDALONE) {
if (is_authoritative()) {
id_global_pool_.init_master();
} else {
id_global_pool_.init_synch();
@@ -63,11 +63,12 @@ World::World(util::WorldType wt) {
// Create the tangibles table in the registry.
LS.rawset(LuaRegistry, "tangibles", LuaNewTable);
// Store the world type in the registry.
LS.rawset(LuaRegistry, "worldtype", wt);
// Create the globaldb and oncedb in the registry.
if ((wt == util::WORLD_TYPE_MASTER) || (wt == util::WORLD_TYPE_STANDALONE)) {
LS.rawset(LuaRegistry, "globaldb", LuaNewTable);
LS.rawset(LuaRegistry, "oncedb", LuaNewTable);
}
LS.rawset(LuaRegistry, "globaldb", LuaNewTable);
LS.rawset(LuaRegistry, "oncedb", LuaNewTable);
// Initialize the SourceDB. At this stage, the sourcedb is
// empty, so it's just populating the lua builtins.
@@ -788,12 +789,12 @@ void World::invoke_lua_source(int64_t actor_id, int64_t place_id, const eng::str
void World::guard_blockable(lua_State *L, const char *fn) {
if (lthread_thread_id_ == 0) {
// in a probe, http.get throws an error.
// in a probe, blocking functions like http.get throw an error.
luaL_error(L, "cannot %s in a probe", fn);
assert(false);
}
if (!is_authoritative()) {
// in a nonauth model, http.get is converted to nopredict.
// in a nonauth model, blocking functions like http.get are converted to nopredict.
lua_yield(L, 0);
luaL_error(L, "unexplained nopredict failure in %s", fn);
assert(false);
@@ -801,6 +802,8 @@ void World::guard_blockable(lua_State *L, const char *fn) {
}
void World::guard_nopredict(lua_State *L, const char *fn) {
// Caution: this code must be equivalent to the
// code in LuaStack::guard_nopredict.
if (lthread_thread_id_ == 0) {
return;
}
@@ -883,7 +886,10 @@ void World::run_scheduled_threads() {
LS.rawset(thinfo, "isnew", false);
LS.rawset(thinfo, "useppool", false);
} else {
// In a nonauth model, a yield is converted to a 'nopredict'.
// When a nonauthoritative model yields, for any reason,
// the thread is discarded. This is also used as a way to implement
// nopredict: the thread that wants to 'nopredict' just yields,
// knowing that this will cause it to be killed.
LS.rawset(threads, sched.thread_id(), LuaNil);
}
} else {

View File

@@ -136,8 +136,6 @@ void World::pair_lua_tables(const IdVector &basis, lua_State *master) {
// If the master table is already paired, skip.
MLS.rawget(midx, mtnmap, mtab);
if (MLS.isnumber(midx)) continue;
// If the synch table is not a table, skip.
if (!SLS.istable(stab)) continue;
// If the synch table doesn't have a number, skip.
SLS.rawget(sidx, stnmap, stab);
if (!SLS.isnumber(sidx)) continue;

View File

@@ -241,9 +241,9 @@ static bool worlds_identical(const UniqueWorld &w1, const UniqueWorld &w2) {
}
LuaDefine(unittests_world1animdiff, "", "some unit tests") {
UniqueWorld m(new World(util::WORLD_TYPE_MASTER));
UniqueWorld ss(new World(util::WORLD_TYPE_S_SYNC));
UniqueWorld cs(new World(util::WORLD_TYPE_C_SYNC));
UniqueWorld m(new World(WORLD_TYPE_MASTER));
UniqueWorld ss(new World(WORLD_TYPE_PREDICTIVE));
UniqueWorld cs(new World(WORLD_TYPE_PREDICTIVE));
StreamBuffer sb;
util::IdVector ids = util::id_vector_create(123, 345);
@@ -311,8 +311,8 @@ LuaDefine(unittests_world1animdiff, "", "some unit tests") {
}
LuaDefine(unittests_world2pairtab, "", "some unit tests") {
UniqueWorld m(new World(util::WORLD_TYPE_MASTER));
UniqueWorld ss(new World(util::WORLD_TYPE_S_SYNC));
UniqueWorld m(new World(WORLD_TYPE_MASTER));
UniqueWorld ss(new World(WORLD_TYPE_PREDICTIVE));
StreamBuffer sb;
int ncreate;
@@ -359,9 +359,9 @@ LuaDefine(unittests_world2pairtab, "", "some unit tests") {
}
LuaDefine(unittests_world3diffluatab, "", "some unit tests") {
UniqueWorld m(new World(util::WORLD_TYPE_MASTER));
UniqueWorld ss(new World(util::WORLD_TYPE_S_SYNC));
UniqueWorld cs(new World(util::WORLD_TYPE_C_SYNC));
UniqueWorld m(new World(WORLD_TYPE_MASTER));
UniqueWorld ss(new World(WORLD_TYPE_PREDICTIVE));
UniqueWorld cs(new World(WORLD_TYPE_PREDICTIVE));
StreamBuffer sb;
// Initialize all three models so that a tangible exists.
@@ -413,9 +413,9 @@ LuaDefine(unittests_world3diffluatab, "", "some unit tests") {
}
LuaDefine(unittests_world4difftanclass, "", "some unit tests") {
UniqueWorld m(new World(util::WORLD_TYPE_MASTER));
UniqueWorld ss(new World(util::WORLD_TYPE_S_SYNC));
UniqueWorld cs(new World(util::WORLD_TYPE_C_SYNC));
UniqueWorld m(new World(WORLD_TYPE_MASTER));
UniqueWorld ss(new World(WORLD_TYPE_PREDICTIVE));
UniqueWorld cs(new World(WORLD_TYPE_PREDICTIVE));
StreamBuffer sb;
// Initialize all three models so that a tangible exists.

View File

@@ -104,7 +104,7 @@ public:
// The constructor also calls 'lua_open' to create a new
// lua interpreter for this world model.
//
World(util::WorldType wt);
World(WorldType wt);
// Destructor.
//
@@ -236,8 +236,8 @@ public:
// Check if the world is authoritative.
//
bool is_authoritative() const { return (world_type_ == util::WORLD_TYPE_MASTER) || (world_type_ == util::WORLD_TYPE_STANDALONE); }
bool is_authoritative() const { return LuaStack::is_authoritative(world_type_); }
// Get a table showing all outstanding HTTP requests.
//
const HttpClientRequestMap &http_requests() const { return http_requests_; }
@@ -249,6 +249,9 @@ public:
// Snapshot and rollback.
//
// These are used by the client to convert the synchronous model
// to an asynchronous model and back.
//
void snapshot();
void rollback();
bool snapshot_empty() { return snapshot_.empty(); }
@@ -494,7 +497,7 @@ public:
private:
// Type of model
util::WorldType world_type_;
WorldType world_type_;
// A lua intepreter with snapshot function.
//