From 796a3c41395e2dcfd2cdf4301d602084bbc43171 Mon Sep 17 00:00:00 2001 From: Josh Yelon Date: Thu, 14 Oct 2021 11:43:16 -0400 Subject: [PATCH] Redesigned ID allocator and tangible.actor --- luprex/core/cpp/idalloc.cpp | 103 ++++++++--------------------- luprex/core/cpp/idalloc.hpp | 21 +----- luprex/core/cpp/world-accessor.cpp | 26 +++++--- luprex/core/cpp/world-core.cpp | 63 ++++++++++++------ luprex/core/cpp/world.hpp | 31 +++++++++ luprex/core/lua/player.lua | 13 ++++ 6 files changed, 131 insertions(+), 126 deletions(-) diff --git a/luprex/core/cpp/idalloc.cpp b/luprex/core/cpp/idalloc.cpp index 3feed430..da99a72e 100644 --- a/luprex/core/cpp/idalloc.cpp +++ b/luprex/core/cpp/idalloc.cpp @@ -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(); diff --git a/luprex/core/cpp/idalloc.hpp b/luprex/core/cpp/idalloc.hpp index 456fe63f..c5bd174e 100644 --- a/luprex/core/cpp/idalloc.hpp +++ b/luprex/core/cpp/idalloc.hpp @@ -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(); } diff --git a/luprex/core/cpp/world-accessor.cpp b/luprex/core/cpp/world-accessor.cpp index 79e2e8dd..985dd30c 100644 --- a/luprex/core/cpp/world-accessor.cpp +++ b/luprex/core/cpp/world-accessor.cpp @@ -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."); diff --git a/luprex/core/cpp/world-core.cpp b/luprex/core/cpp/world-core.cpp index 82c60eb0..a8661849 100644 --- a/luprex/core/cpp/world-core.cpp +++ b/luprex/core/cpp/world-core.cpp @@ -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 &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()); diff --git a/luprex/core/cpp/world.hpp b/luprex/core/cpp/world.hpp index e180b969..2dd4deb1 100644 --- a/luprex/core/cpp/world.hpp +++ b/luprex/core/cpp/world.hpp @@ -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 diff --git a/luprex/core/lua/player.lua b/luprex/core/lua/player.lua index dca9cab3..e68a8ae1 100644 --- a/luprex/core/lua/player.lua +++ b/luprex/core/lua/player.lua @@ -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