Redesigned ID allocator and tangible.actor

This commit is contained in:
2021-10-14 11:43:16 -04:00
parent 50d62d9bfd
commit 796a3c4139
6 changed files with 131 additions and 126 deletions

View File

@@ -61,24 +61,6 @@ void IdGlobalPool::salvage(int64_t batch) {
salvaged_.push_back(batch);
}
void IdGlobalPool::salvage_thread(lua_State *L) {
salvage(lua_getnextid(L));
lua_setnextid(L, 0);
}
int64_t IdGlobalPool::alloc_id_for_thread(lua_State *L) {
int64_t batch = lua_getnextid(L);
if (batch != 0) {
int64_t id = batch;
batch += 1;
if ((batch & 0xFF) == 0) batch = 0;
lua_setnextid(L, batch);
return id;
} else {
return get_one();
}
}
void IdGlobalPool::serialize(StreamBuffer *sb) const {
sb->write_int64(next_batch_);
sb->write_int64(next_id_);
@@ -146,30 +128,22 @@ void IdPlayerPool::test_clear_ranges() {
ranges_.clear();
}
int64_t IdPlayerPool::get_batch() {
while (int(ranges_.size()) < fifo_capacity_ + 1) {
int64_t batch = global_->get_batch();
if (batch == 0) break;
ranges_.push_back(batch);
}
int64_t IdPlayerPool::get_one() {
refill();
if (ranges_.empty()) {
return 0;
return global_->get_one();
} else {
int64_t batch = ranges_.front();
ranges_.pop_front();
return batch;
int64_t id = ranges_.front();
if ((id & 0xFF) == 0xFF) {
ranges_.pop_front();
refill();
} else {
ranges_.front() = id + 1;
}
return id;
}
}
void IdPlayerPool::salvage_thread(lua_State *L) {
global_->salvage_thread(L);
}
void IdPlayerPool::prepare_thread(lua_State *L) {
global_->salvage_thread(L);
lua_setnextid(L, get_batch());
}
void IdPlayerPool::serialize(StreamBuffer *sb) const {
sb->write_uint8(fifo_capacity_);
sb->write_uint8(ranges_.size());
@@ -324,25 +298,24 @@ LuaDefine(unittests_idalloc, "c") {
LuaAssert(L, gp.get_batch() == nthbatch(2));
// In the synchronous model, refill should do nothing.
// The player pool should shunt through to the global.
pp.test_clear_ranges();
gp.init_synch();
pp.set_fifo_capacity(3);
pp.refill();
LuaAssert(L, pp.size() == 0);
LuaAssert(L, pp.get_batch() == 0);
LuaAssert(L, pp.get_one() == 0x001E000000000000);
LuaAssert(L, pp.size() == 0);
LuaAssert(L, pp.get_batch() == 0);
// In the master model, with fifo disabled. Fifo should remain
// empty, but batches should be returned.
// empty, and the player pool should shunt through to the global.
gp.init_master();
pp.test_clear_ranges();
pp.set_fifo_capacity(0);
pp.refill();
LuaAssert(L, pp.size() == 0);
LuaAssert(L, pp.get_batch() == nthbatch(0));
LuaAssert(L, pp.get_one() == 0x0010000000000000);
LuaAssert(L, pp.size() == 0);
LuaAssert(L, pp.get_batch() == nthbatch(1));
// Test refill from master (with enabled fifo).
gp.init_master();
@@ -351,44 +324,24 @@ LuaDefine(unittests_idalloc, "c") {
pp.refill();
LuaAssert(L, ranges_equal(pp.ranges_, nthbatch(0), nthbatch(1), nthbatch(2)));
// Now test that get_batch keeps the pool filled from master.
LuaAssert(L, pp.get_batch() == nthbatch(0));
LuaAssert(L, ranges_equal(pp.ranges_, nthbatch(1), nthbatch(2), nthbatch(3)));
// Now test that get_one() keeps the pool filled from master.
for (int i = 0; i < 256; i++) {
LuaAssert(L, pp.get_one() == nthbatch(0) + i);
}
for (int i = 0; i < 256; i++) {
LuaAssert(L, pp.get_one() == nthbatch(1) + i);
}
LuaAssert(L, ranges_equal(pp.ranges_, nthbatch(2), nthbatch(3), nthbatch(4)));
// Test unqueueing the batches.
LuaAssert(L, gp.get_batch() == nthbatch(4));
LuaAssert(L, gp.get_batch() == nthbatch(5));
LuaAssert(L, gp.get_batch() == nthbatch(6));
pp.set_fifo_capacity(0);
LuaAssert(L, gp.get_batch() == nthbatch(1));
LuaAssert(L, gp.get_batch() == nthbatch(2));
LuaAssert(L, gp.get_batch() == nthbatch(3));
LuaAssert(L, gp.get_batch() == nthbatch(6));
LuaAssert(L, gp.get_batch() == nthbatch(4));
LuaAssert(L, gp.get_batch() == nthbatch(7));
// Try preparing a thread and salvaging a thread.
gp.init_master();
pp.test_clear_ranges();
pp.set_fifo_capacity(3);
lua_setnextid(L, 0);
pp.prepare_thread(L);
LuaAssert(L, lua_getnextid(L) == nthbatch(0));
lua_setnextid(L, 0);
pp.prepare_thread(L);
LuaAssert(L, lua_getnextid(L) == nthbatch(1));
// Try salvaging the pool from the thread.
pp.salvage_thread(L);
LuaAssert(L, lua_getnextid(L) == 0);
LuaAssert(L, gp.get_batch() == nthbatch(1));
// Allocate IDs from inside a thread.
gp.init_master();
lua_setnextid(L, 0xFD);
LuaAssert(L, gp.alloc_id_for_thread(L) == 0xFD);
LuaAssert(L, gp.alloc_id_for_thread(L) == 0xFE);
LuaAssert(L, gp.alloc_id_for_thread(L) == 0xFF);
LuaAssert(L, gp.alloc_id_for_thread(L) == 0x0010000000000000);
LuaAssert(L, lua_getnextid(L) == 0);
// Serialize and deserialize a global pool.
gp.init_master();
gpds.init_master();
@@ -415,9 +368,7 @@ LuaDefine(unittests_idalloc, "c") {
ppds.deserialize(&sb);
LuaAssert(L, ppds.get_fifo_capacity()==3);
LuaAssert(L, ppds.size() == 3);
LuaAssert(L, ppds.get_batch() == nthbatch(1));
LuaAssert(L, ppds.get_batch() == nthbatch(2));
LuaAssert(L, ppds.get_batch() == nthbatch(3));
LuaAssert(L, ranges_equal(ppds.ranges_, nthbatch(1), nthbatch(2), nthbatch(3)));
// Difference transmit compare two empty pools.
gp.init_master();

View File

@@ -101,14 +101,6 @@ public:
// zero, or the batch contains fewer than 128 IDs.
void salvage(int64_t batch);
// Return the thread's batch to the global pool. If no batch
// is present, that's okay. Set the thread's batch to zero (invalid).
void salvage_thread(lua_State *L);
// Allocate an ID for the specified thread. Uses the thread's
// batch if possible. If not, fetches one ID from the global pool.
int64_t alloc_id_for_thread(lua_State *L);
// Serialize to or deserialize from a streambuffer.
void serialize(StreamBuffer *sb) const;
void deserialize(StreamBuffer *sb);
@@ -144,17 +136,8 @@ public:
// Refill the fifo of batches from the global pool.
void refill();
// Get a batch from the fifo. Also refills the fifo.
int64_t get_batch();
// Return the thread's batch to the global pool. If no batch
// is present, that's okay. Set the thread's batch to zero (invalid).
void salvage_thread(lua_State *L);
// Fetch a batch from the fifo and install it in the thread.
// If the thread already had a batch, the thread's previous batch
// is returned to the global pool.
void prepare_thread(lua_State *L);
// Get a single ID from the fifo. Also refills the fifo.
int64_t get_one();
// Return the size of the queue.
int size() { return ranges_.size(); }

View File

@@ -22,7 +22,7 @@ LuaDefine(tangible_animate, "c") {
LuaStack LS(L, tanobj, config);
World *w = World::fetch_global_pointer(L);
Tangible *tan = w->tangible_get(LS, tanobj);
int64_t id = w->id_global_pool_.alloc_id_for_thread(L);
int64_t id = w->alloc_id_predictable();
const AnimStep &prev = tan->anim_queue_.back();
AnimStep step;
step.from_lua(L, config.index(), prev);
@@ -94,7 +94,8 @@ LuaDefine(tangible_build, "c") {
// TODO: generate error if there's extra crap in the config table.
World *w = World::fetch_global_pointer(L);
Tangible *tan = w->tangible_make(L, 0, "nowhere", true);
int64_t new_id = w->alloc_id_predictable();
Tangible *tan = w->tangible_make(L, new_id, "nowhere", true);
lua_replace(L, database.index());
// Update the class of the new tangible.
@@ -102,7 +103,7 @@ LuaDefine(tangible_build, "c") {
LS.rawset(mt, "__index", classtab);
// Update the animation queue and planemap of the new tangible.
int64_t stepid = w->id_global_pool_.alloc_id_for_thread(L);
int64_t stepid = w->alloc_id_predictable();
tan->anim_queue_.add(stepid, initstep);
tan->update_plane_item();
@@ -153,21 +154,26 @@ LuaDefine(tangible_id, "c") {
return LS.result();
}
LuaDefine(tangible_getactor, "c") {
LuaDefine(tangible_actor, "c") {
LuaRet actor;
LuaStack LS(L, actor);
LS.rawget(actor, LuaRegistry, "actor");
LuaVar tangibles;
LuaStack LS(L, tangibles, actor);
World *w = World::fetch_global_pointer(L);
LS.rawget(tangibles, LuaRegistry, "tangibles");
LS.rawget(actor, tangibles, w->lthread_actor_id_);
return LS.result();
}
LuaDefine(tangible_getplace, "c") {
LuaDefine(tangible_place, "c") {
LuaRet place;
LuaStack LS(L, place);
LS.rawget(place, LuaRegistry, "place");
LuaVar tangibles;
LuaStack LS(L, tangibles, place);
World *w = World::fetch_global_pointer(L);
LS.rawget(tangibles, LuaRegistry, "tangibles");
LS.rawget(place, tangibles, w->lthread_place_id_);
return LS.result();
}
LuaDefine(world_wait, "f") {
if ((lua_gettop(L) != 1) || (lua_type(L, -1) != LUA_TNUMBER)) {
luaL_error(L, "Argument to wait must be a number.");

View File

@@ -47,6 +47,9 @@ World::World(util::WorldType wt) {
// Clear the global GUI pointer.
Gui::store_global_pointer(state(), nullptr);
// Clear the lthread state.
set_lthread_state(0, 0, false);
// Set the tabletype of the registry.
LS.settabletype(LuaRegistry, LUA_TT_REGISTRY);
@@ -151,14 +154,12 @@ Tangible *World::tangible_make(lua_State *L, int64_t id, const std::string &plan
L = state();
assert(!pushdb);
}
assert(id != 0);
LuaVar tangibles, metatab;
LuaRet database;
LuaStack LS(L, tangibles, database, metatab);
// Allocate an ID if we don't already have one.
if (id == 0) id = id_global_pool_.alloc_id_for_thread(L);
// Create the C++ part of the structure.
std::unique_ptr<Tangible> &t = tangibles_[id];
assert (t == nullptr);
@@ -250,7 +251,8 @@ World::Redirects World::fetch_redirects() {
}
int64_t World::create_login_actor() {
Tangible *tan = tangible_make(state(), 0, "nowhere", true);
int64_t id = id_global_pool_.get_one();
Tangible *tan = tangible_make(state(), id, "nowhere", true);
LuaArg database;
LuaVar classtab, mt;
LuaStack LS(state(), database, classtab, mt);
@@ -302,11 +304,9 @@ void World::update_gui(int64_t actor_id, int64_t place_id, Gui *gui) {
lua_pushvalue(L, actor.index());
lua_pushvalue(L, place.index());
Gui::store_global_pointer(L, gui);
LS.rawset(LuaRegistry, "actor", actor);
LS.rawset(LuaRegistry, "place", place);
set_lthread_state(actor_id, place_id, false);
int status = traceback_pcall(L, 2, 0);
LS.rawset(LuaRegistry, "actor", LuaNil);
LS.rawset(LuaRegistry, "place", LuaNil);
set_lthread_state(0, 0, false);
Gui::store_global_pointer(L, nullptr);
if (status != 0) {
gui->clear();
@@ -359,9 +359,9 @@ void World::invoke_plan(int64_t actor_id, int64_t place_id, const std::string &a
return;
}
// Get an ID batch for the thread, and take one for the thread itself.
int64_t id_batch = tactor->id_player_pool_.get_batch();
int64_t tid = id_batch++;
// Get an ID for the thread. We always use the player
// pool in this case.
int64_t tid = tactor->id_player_pool_.get_one();
// Set up for Lua manipulation.
lua_State *L = state();
@@ -418,8 +418,9 @@ void World::invoke_plan(int64_t actor_id, int64_t place_id, const std::string &a
// Create the thread info table.
LS.newtable(thinfo);
LS.rawset(thinfo, "thread", thread);
LS.rawset(thinfo, "actor", actor);
LS.rawset(thinfo, "actorid", actor_id);
LS.rawset(thinfo, "isnew", true);
LS.rawset(thinfo, "useppool", true);
// Store the thread into place's thread table.
LS.rawget(threads, mt, "threads");
@@ -440,8 +441,8 @@ void World::invoke_plan(int64_t actor_id, int64_t place_id, const std::string &a
void World::run_scheduled_threads(int64_t clk) {
assert(stack_is_clear());
lua_State *L = state();
LuaVar tangibles, actor, place, mt, threads, thinfo, isnew, thread;
LuaStack LS(L, tangibles, actor, place, mt, threads, thinfo, isnew, thread);
LuaVar tangibles, place, mt, threads, thinfo, actorid, isnew, useppool, thread;
LuaStack LS(L, tangibles, place, mt, threads, thinfo, actorid, isnew, useppool, thread);
LS.rawget(tangibles, LuaRegistry, "tangibles");
while (thread_sched_.ready(clk)) {
@@ -462,27 +463,28 @@ void World::run_scheduled_threads(int64_t clk) {
if (!LS.istable(thinfo)) {
continue;
}
LS.rawget(actor, thinfo, "actor");
if (!LS.istable(actor)) {
LS.rawget(actorid, thinfo, "actorid");
if (!LS.isnumber(actorid)) {
continue;
}
LS.rawget(isnew, thinfo, "isnew");
if (!LS.isboolean(isnew)) {
continue;
}
LS.rawget(useppool, thinfo, "useppool");
if (!LS.isboolean(useppool)) {
continue;
}
LS.rawget(thread, thinfo, "thread");
if (!LS.isthread(thread)) {
continue;
}
// Resume the coroutine.
LS.rawset(thinfo, "isnew", false);
lua_State *CO = LS.ckthread(thread);
LS.rawset(LuaRegistry, "actor", actor);
LS.rawset(LuaRegistry, "place", place);
set_lthread_state(LS.ckinteger(actorid), sched.place_id(), LS.ckboolean(useppool));
int status = lua_resume(CO, nullptr, LS.ckboolean(isnew) ? 3 : 0);
LS.rawset(LuaRegistry, "actor", LuaNil);
LS.rawset(LuaRegistry, "place", LuaNil);
set_lthread_state(0, 0, false);
// Three possible outcomes: finished, yielded, or errored.
if (status == LUA_YIELD) {
@@ -493,6 +495,8 @@ void World::run_scheduled_threads(int64_t clk) {
} else {
lua_Number delay = lua_tonumber(CO, 1);
lua_settop(CO, 0);
LS.rawset(thinfo, "isnew", false);
LS.rawset(thinfo, "useppool", false);
std::cerr << "Thread wait = " << delay << std::endl;
thread_sched_.add(sched.clock() + int64_t(delay), sched.thread_id(), sched.place_id());
std::cerr << "Added to schedule." << std::endl;
@@ -512,6 +516,23 @@ void World::run_scheduled_threads(int64_t clk) {
assert(stack_is_clear());
}
int64_t World::alloc_id_predictable() {
if (!lthread_use_ppool_) {
return id_global_pool_.get_one();
}
Tangible *t = tangible_get(lthread_actor_id_);
if (t == nullptr) {
return id_global_pool_.get_one();
}
return t->id_player_pool_.get_one();
}
void World::set_lthread_state(int64_t actor, int64_t place, bool ppool) {
lthread_actor_id_ = actor;
lthread_place_id_ = place;
lthread_use_ppool_ = ppool;
}
void World::serialize(StreamBuffer *sb) {
assert(stack_is_clear());
assert(redirects_.empty());

View File

@@ -207,6 +207,29 @@ public:
//
bool stack_is_clear() const { return lua_gettop(state()) == 0; }
// Set the lthread state.
//
// Whenever lua code is running, and ONLY when lua code is running,
// we store the following information in the world model:
//
// * lthread_actor_id: current actor
// * lthread_place_id: current place
// * lthread_use_ppool: true if we should use the player ID pool.
//
// As soon as the lua code stops executing, these variables are
// cleared.
//
void set_lthread_state(int64_t actor_id, int64_t place_id, bool ppool);
// Allocate a single ID.
//
// The rules are as follows:
// * if lthread_use_ppool is false, uses the global pool.
// * if lthread_actor_id is not a valid actor id, uses the global pool.
// * otherwise, uses the player pool of lthread_actor_id.
//
int64_t alloc_id_predictable();
private:
// Store a pointer to a world model into a lua registry.
@@ -406,10 +429,18 @@ private:
//
Redirects redirects_;
// lthread variables: see set_lthread_state for explanation.
//
int64_t lthread_actor_id_;
int64_t lthread_place_id_;
int64_t lthread_use_ppool_;
friend class Tangible;
friend int lfn_tangible_animate(lua_State *L);
friend int lfn_tangible_build(lua_State *L);
friend int lfn_tangible_redirect(lua_State *L);
friend int lfn_tangible_actor(lua_State *L);
friend int lfn_tangible_place(lua_State *L);
};
#endif // WORLD_HPP

View File

@@ -1,6 +1,10 @@
maketangible('player')
function player.interface(actor, place)
-- print("actor=", actor)
-- print("place=", place)
-- print("t.actor=", tangible.actor())
-- print("t.place=", tangible.place())
gui.menu_item("north", "Go North")
gui.menu_item("south", "Go South")
gui.menu_item("east", "Go East")
@@ -34,5 +38,14 @@ function player.action.west(actor, place, dialog)
print("Moving west")
tangible.animate(place, {action="walk", dx=-1})
actor:printanimstate()
-- print("actor=", actor)
-- print("place=", place)
-- print("t.actor=", tangible.actor())
-- print("t.place=", tangible.place())
-- wait(1)
-- print("actor=", actor)
-- print("place=", place)
-- print("t.actor=", tangible.actor())
-- print("t.place=", tangible.place())
end