Overhaul of thread handling to support blocking functions other than wait
This commit is contained in:
@@ -45,14 +45,15 @@ LuaDefine(global_table, "globalname", "get a table where global data can be stor
|
|||||||
LuaRet globaltab;
|
LuaRet globaltab;
|
||||||
LuaVar globaldb;
|
LuaVar globaldb;
|
||||||
LuaStack LS(L, globalname, globaltab, globaldb);
|
LuaStack LS(L, globalname, globaltab, globaldb);
|
||||||
|
LS.checkstring(globalname);
|
||||||
|
|
||||||
// Get a pointer to the globaldb.
|
// Get a pointer to the globaldb.
|
||||||
LS.rawget(globaldb, LuaRegistry, "globaldb");
|
LS.rawget(globaldb, LuaRegistry, "globaldb");
|
||||||
if (!LS.istable(globaldb)) {
|
|
||||||
return lua_yield(L, 0); // donotpredict
|
|
||||||
}
|
|
||||||
|
|
||||||
LS.checkstring(globalname);
|
// nopredict
|
||||||
|
if (lua_isyieldable(L) && (!LS.istable(globaldb))) {
|
||||||
|
return lua_yield(L, 0);
|
||||||
|
}
|
||||||
|
|
||||||
// Get the globaltab from the globaldb, sanity check it.
|
// Get the globaltab from the globaldb, sanity check it.
|
||||||
LS.rawget(globaltab, globaldb, globalname);
|
LS.rawget(globaltab, globaldb, globalname);
|
||||||
|
|||||||
@@ -20,11 +20,13 @@ using ClientVector = eng::vector<UniqueClient>;
|
|||||||
|
|
||||||
class LpxServer : public DrivenEngine {
|
class LpxServer : public DrivenEngine {
|
||||||
public:
|
public:
|
||||||
|
using StringVec = LuaConsole::StringVec;
|
||||||
UniqueWorld master_;
|
UniqueWorld master_;
|
||||||
LuaConsole console_;
|
LuaConsole console_;
|
||||||
ClientVector clients_;
|
ClientVector clients_;
|
||||||
PrintChanneler print_channeler_;
|
PrintChanneler print_channeler_;
|
||||||
int64_t admin_id_;
|
int64_t admin_id_;
|
||||||
|
Gui gui_;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
virtual void event_init(int argc, char *argv[]) {
|
virtual void event_init(int argc, char *argv[]) {
|
||||||
@@ -61,6 +63,22 @@ public:
|
|||||||
stdostream() << "Syntax Error: " << words[1] << std::endl;
|
stdostream() << "Syntax Error: " << words[1] << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void do_menu_command(const StringVec &cmd) {
|
||||||
|
int64_t place = sv::to_int64(cmd[1], admin_id_);
|
||||||
|
master_->update_gui(admin_id_, place, &gui_);
|
||||||
|
stdostream() << gui_.menu_debug_string();
|
||||||
|
}
|
||||||
|
|
||||||
|
void do_choose_command(const StringVec &cmd) {
|
||||||
|
eng::string action = gui_.get_action(sv::to_int64(cmd[1]));
|
||||||
|
if (action == "") {
|
||||||
|
stdostream() << "Invalid menu item #" << std::endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
stdostream() << "Invoking plan: " << action << std::endl;
|
||||||
|
master_->invoke(Invocation(Invocation::KIND_PLAN, admin_id_, gui_.place(), action));
|
||||||
|
}
|
||||||
|
|
||||||
void do_tick_command(const util::StringVec &words) {
|
void do_tick_command(const util::StringVec &words) {
|
||||||
master_->invoke(Invocation(Invocation::KIND_TICK, admin_id_, admin_id_, ""));
|
master_->invoke(Invocation(Invocation::KIND_TICK, admin_id_, admin_id_, ""));
|
||||||
}
|
}
|
||||||
@@ -78,6 +96,8 @@ public:
|
|||||||
else if (words[0] == "luainvoke") do_luainvoke_command(words);
|
else if (words[0] == "luainvoke") do_luainvoke_command(words);
|
||||||
else if (words[0] == "luaprobe") do_luaprobe_command(words);
|
else if (words[0] == "luaprobe") do_luaprobe_command(words);
|
||||||
else if (words[0] == "syntax") do_syntax_command(words);
|
else if (words[0] == "syntax") do_syntax_command(words);
|
||||||
|
else if (words[0] == "menu") do_menu_command(words);
|
||||||
|
else if (words[0] == "choose") do_choose_command(words);
|
||||||
else if (words[0] == "tick") do_tick_command(words);
|
else if (words[0] == "tick") do_tick_command(words);
|
||||||
else if (words[0] == "cpl") do_cpl_command(words);
|
else if (words[0] == "cpl") do_cpl_command(words);
|
||||||
else if (words[0] == "quit") do_quit_command(words);
|
else if (words[0] == "quit") do_quit_command(words);
|
||||||
|
|||||||
@@ -120,8 +120,8 @@ static void tabify(Inspector &insp, int level) {
|
|||||||
|
|
||||||
static void pprint_r(Inspector &insp, int level, LuaSlot root) {
|
static void pprint_r(Inspector &insp, int level, LuaSlot root) {
|
||||||
lua_checkstack(insp.L, 20);
|
lua_checkstack(insp.L, 20);
|
||||||
LuaVar idv, pairs, key, val;
|
LuaVar idv, pairs, key, val, nextseq;
|
||||||
LuaStack LS(insp.L, idv, pairs, key, val);
|
LuaStack LS(insp.L, idv, pairs, key, val, nextseq);
|
||||||
|
|
||||||
// If it's anything but a table, use 'atomic_print'.
|
// If it's anything but a table, use 'atomic_print'.
|
||||||
if (!LS.istable(root)) {
|
if (!LS.istable(root)) {
|
||||||
@@ -175,9 +175,9 @@ static void pprint_r(Inspector &insp, int level, LuaSlot root) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// State variables.
|
// State variables.
|
||||||
int nextseq = 1;
|
|
||||||
bool needcomma = false;
|
bool needcomma = false;
|
||||||
bool multiline = false;
|
bool multiline = false;
|
||||||
|
LS.set(nextseq, 1);
|
||||||
|
|
||||||
// Open the brackets.
|
// Open the brackets.
|
||||||
(*insp.stream) << "{";
|
(*insp.stream) << "{";
|
||||||
@@ -190,10 +190,10 @@ static void pprint_r(Inspector &insp, int level, LuaSlot root) {
|
|||||||
LS.rawget(val, pairs, i+1);
|
LS.rawget(val, pairs, i+1);
|
||||||
if (needcomma) (*insp.stream) << ",";
|
if (needcomma) (*insp.stream) << ",";
|
||||||
needcomma = true;
|
needcomma = true;
|
||||||
if (LS.isnumber(key) && (LS.ckint(key) == nextseq)) {
|
if (LS.rawequal(key, nextseq)) {
|
||||||
(*insp.stream) << " ";
|
(*insp.stream) << " ";
|
||||||
pprint_r(insp, level + 1, val);
|
pprint_r(insp, level + 1, val);
|
||||||
nextseq = nextseq + 1;
|
LS.set(nextseq, LS.ckinteger(nextseq) + 1);
|
||||||
} else {
|
} else {
|
||||||
multiline = true;
|
multiline = true;
|
||||||
tabify(insp, level + 1);
|
tabify(insp, level + 1);
|
||||||
@@ -227,7 +227,7 @@ static void pprint_r(Inspector &insp, int level, LuaSlot root) {
|
|||||||
// Close the brackets.
|
// Close the brackets.
|
||||||
if (multiline) {
|
if (multiline) {
|
||||||
tabify(insp, level);
|
tabify(insp, level);
|
||||||
} else if (nextseq > 1) {
|
} else if (LS.ckinteger(nextseq) > 1) {
|
||||||
(*insp.stream) << " ";
|
(*insp.stream) << " ";
|
||||||
}
|
}
|
||||||
(*insp.stream) << "}";
|
(*insp.stream) << "}";
|
||||||
|
|||||||
@@ -24,6 +24,8 @@ eng::string SchedEntry::debug_string() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Schedule::add(int64_t clk, int64_t thid, int64_t plid) {
|
void Schedule::add(int64_t clk, int64_t thid, int64_t plid) {
|
||||||
|
assert(plid != 0);
|
||||||
|
assert(thid != 0);
|
||||||
schedule_.insert(SchedEntry(clk, thid, plid));
|
schedule_.insert(SchedEntry(clk, thid, plid));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -328,9 +328,10 @@ static eng::string source_load_lfunctions(lua_State *L) {
|
|||||||
|
|
||||||
// Call the closure. If there's an error, collect it.
|
// Call the closure. If there's an error, collect it.
|
||||||
lua_pushvalue(L, closure.index());
|
lua_pushvalue(L, closure.index());
|
||||||
if (traceback_pcall(L, 0, 0) != 0) {
|
eng::string msg = traceback_pcall(L, 0, 0);
|
||||||
lua_replace(L, err.index());
|
if (!msg.empty()) {
|
||||||
errss << LS.ckstring(err);
|
LS.set(err, msg);
|
||||||
|
errss << msg << std::endl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
LS.result();
|
LS.result();
|
||||||
@@ -380,9 +381,10 @@ void SourceDB::run_unittests() {
|
|||||||
LS.rawget(func, unittests, name);
|
LS.rawget(func, unittests, name);
|
||||||
|
|
||||||
lua_pushvalue(L, func.index());
|
lua_pushvalue(L, func.index());
|
||||||
if (traceback_pcall(L, 0, 0) != 0) {
|
eng::string msg = traceback_pcall(L, 0, 0);
|
||||||
lua_replace(L, err.index());
|
if (!msg.empty()) {
|
||||||
std::cerr << LS.ckstring(err);
|
LS.set(err, msg);
|
||||||
|
std::cerr << msg << std::endl;
|
||||||
any = true;
|
any = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -76,12 +76,19 @@ int traceback_coroutine(lua_State *L) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int traceback_pcall(lua_State *L, int narg, int nret) {
|
eng::string traceback_pcall(lua_State *L, int narg, int nret) {
|
||||||
int status;
|
int status;
|
||||||
int base = lua_gettop(L) - narg; /* function index */
|
int base = lua_gettop(L) - narg; /* function index */
|
||||||
lua_pushcfunction(L, traceback_coroutine); /* push traceback function */
|
lua_pushcfunction(L, traceback_coroutine); /* push traceback function */
|
||||||
lua_insert(L, base); /* put it under chunk and args */
|
lua_insert(L, base); /* put it under chunk and args */
|
||||||
status = lua_pcall(L, narg, nret, base);
|
status = lua_pcall(L, narg, nret, base);
|
||||||
lua_remove(L, base); /* remove traceback function */
|
lua_remove(L, base); /* remove traceback function */
|
||||||
return status;
|
if (status != LUA_OK) {
|
||||||
|
const char *msg = lua_tostring(L, -1);
|
||||||
|
if ((msg == NULL) || (msg[0] == 0)) {
|
||||||
|
msg = "unknown error";
|
||||||
|
}
|
||||||
|
return msg;
|
||||||
|
}
|
||||||
|
return "";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,9 +22,11 @@ int traceback_coroutine(lua_State *L);
|
|||||||
|
|
||||||
// traceback_pcall
|
// traceback_pcall
|
||||||
//
|
//
|
||||||
// same as lua_pcall, except that it automatically supplies
|
// Similar to lua_pcall, except that it automatically supplies
|
||||||
// traceback_coroutine as a message handler.
|
// traceback_coroutine as a message handler. It also automatically
|
||||||
|
// returns any error message. Returns empty string if there's
|
||||||
|
// no error.
|
||||||
//
|
//
|
||||||
int traceback_pcall(lua_State *L, int narg, int nret);
|
eng::string traceback_pcall(lua_State *L, int narg, int nret);
|
||||||
|
|
||||||
#endif // TRACEBACK_HPP
|
#endif // TRACEBACK_HPP
|
||||||
|
|||||||
@@ -510,10 +510,6 @@ double distance_squared(double x1, double y1, double x2, double y2) {
|
|||||||
return dx*dx + dy*dy;
|
return dx*dx + dy*dy;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool world_type_authoritative(util::WorldType wt) {
|
|
||||||
return (wt == WORLD_TYPE_MASTER) || (wt == WORLD_TYPE_STANDALONE);
|
|
||||||
}
|
|
||||||
|
|
||||||
LuaSourcePtr make_lua_source(const eng::string &code) {
|
LuaSourcePtr make_lua_source(const eng::string &code) {
|
||||||
LuaSourcePtr result(new LuaSourceVec);
|
LuaSourcePtr result(new LuaSourceVec);
|
||||||
eng::string fn = "file.lua";
|
eng::string fn = "file.lua";
|
||||||
|
|||||||
@@ -212,9 +212,6 @@ eng::string toupper(eng::string input);
|
|||||||
// Calculate distance between two points
|
// Calculate distance between two points
|
||||||
double distance_squared(double x1, double y1, double x2, double y2);
|
double distance_squared(double x1, double y1, double x2, double y2);
|
||||||
|
|
||||||
// Return true if a world type is authoritative.
|
|
||||||
bool world_type_authoritative(util::WorldType wt);
|
|
||||||
|
|
||||||
// Make a LuaSourceVec with one element, for unit testing.
|
// Make a LuaSourceVec with one element, for unit testing.
|
||||||
LuaSourcePtr make_lua_source(const eng::string &code);
|
LuaSourcePtr make_lua_source(const eng::string &code);
|
||||||
|
|
||||||
|
|||||||
@@ -294,23 +294,40 @@ LuaDefine(tangible_scan, "plane,x,y,radius,omit_nowhere",
|
|||||||
|
|
||||||
LuaDefine(wait, "nticks",
|
LuaDefine(wait, "nticks",
|
||||||
"|Wait the specified number of ticks.") {
|
"|Wait the specified number of ticks.") {
|
||||||
if ((lua_gettop(L) != 1) || (lua_type(L, -1) != LUA_TNUMBER)) {
|
World *w = World::fetch_global_pointer(L);
|
||||||
luaL_error(L, "Argument to wait must be a number.");
|
LuaArg seconds;
|
||||||
|
LuaStack LS(L, seconds);
|
||||||
|
int64_t n = LS.ckinteger(seconds);
|
||||||
|
if ((n < 0) || (n > 1000000)) {
|
||||||
|
luaL_error(L, "Argument to wait must be between 0 and 1000000");
|
||||||
|
return LS.result();
|
||||||
|
}
|
||||||
|
if (!lua_isyieldable(L)) {
|
||||||
|
// in a probe, wait throws an error.
|
||||||
|
luaL_error(L, "cannot wait in a probe.");
|
||||||
|
return LS.result();
|
||||||
|
} else if (!w->is_authoritative()) {
|
||||||
|
// in a nonauth model, yield is converted to nopredict.
|
||||||
|
lua_yield(L, 0);
|
||||||
|
return LS.result();
|
||||||
|
} else {
|
||||||
|
// in an authoritative model, wait schedules a continuation.
|
||||||
|
w->thread_sched_.add(w->clock_ + n, w->lthread_thread_id_, w->lthread_place_id_);
|
||||||
|
lua_yield(L, 0);
|
||||||
|
return LS.result();
|
||||||
}
|
}
|
||||||
return lua_yield(L, 1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
LuaDefine(tangible_nopredict, "",
|
LuaDefine(nopredict, "",
|
||||||
"|Stop predictive execution of this thread.") {
|
"|Stop predictive execution of this thread.") {
|
||||||
if (lua_gettop(L) != 0) {
|
if (lua_gettop(L) != 0) {
|
||||||
luaL_error(L, "tangible.nopredict takes no arguments");
|
luaL_error(L, "tangible.nopredict takes no arguments");
|
||||||
}
|
}
|
||||||
World *w = World::fetch_global_pointer(L);
|
World *w = World::fetch_global_pointer(L);
|
||||||
if (util::world_type_authoritative(w->world_type_)) {
|
if (lua_isyieldable(L) && !w->is_authoritative()) {
|
||||||
return 0;
|
|
||||||
} else {
|
|
||||||
return lua_yield(L, 0);
|
return lua_yield(L, 0);
|
||||||
}
|
}
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
LuaDefine(math_random, "(args...)",
|
LuaDefine(math_random, "(args...)",
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ World::World(util::WorldType wt) {
|
|||||||
LS.rawset(LuaRegistry, "tangibles", LuaNewTable);
|
LS.rawset(LuaRegistry, "tangibles", LuaNewTable);
|
||||||
|
|
||||||
// Create the globaldb and oncedb in the registry.
|
// Create the globaldb and oncedb in the registry.
|
||||||
if (util::world_type_authoritative(wt)) {
|
if ((wt == util::WORLD_TYPE_MASTER) || (wt == util::WORLD_TYPE_STANDALONE)) {
|
||||||
LS.rawset(LuaRegistry, "globaldb", LuaNewTable);
|
LS.rawset(LuaRegistry, "globaldb", LuaNewTable);
|
||||||
LS.rawset(LuaRegistry, "oncedb", LuaNewTable);
|
LS.rawset(LuaRegistry, "oncedb", LuaNewTable);
|
||||||
}
|
}
|
||||||
@@ -291,23 +291,20 @@ eng::string World::probe_lua(int64_t actor_id, const eng::string &lua) {
|
|||||||
// Call the closure.
|
// Call the closure.
|
||||||
int top = lua_gettop(L);
|
int top = lua_gettop(L);
|
||||||
lua_pushvalue(L, closure.index());
|
lua_pushvalue(L, closure.index());
|
||||||
open_lthread_state(actor_id, actor_id, false, true);
|
open_lthread_state(actor_id, actor_id, 0, false, true);
|
||||||
status = traceback_pcall(L, 0, LUA_MULTRET);
|
eng::string msg = traceback_pcall(L, 0, LUA_MULTRET);
|
||||||
|
|
||||||
// If there's an error message, print it.
|
// If there's an error message, print it.
|
||||||
// Otherwise, pretty-print the results.
|
// Otherwise, pretty-print the results.
|
||||||
std::ostream *ostream = lthread_print_stream();
|
std::ostream *ostream = lthread_print_stream();
|
||||||
if (status == LUA_OK) {
|
if (msg.empty()) {
|
||||||
for (int i = top + 1; i <= lua_gettop(L); i++) {
|
for (int i = top + 1; i <= lua_gettop(L); i++) {
|
||||||
LuaSpecial root(i);
|
LuaSpecial root(i);
|
||||||
pprint(LS, root, true, ostream);
|
pprint(LS, root, true, ostream);
|
||||||
(*ostream) << std::endl;
|
(*ostream) << std::endl;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
const char *msg = lua_tostring(L, -1);
|
assert(msg != "attempt to yield from outside a coroutine");
|
||||||
if (msg == NULL) {
|
|
||||||
msg = "(error object is not a string)";
|
|
||||||
}
|
|
||||||
(*ostream) << msg << std::endl;
|
(*ostream) << msg << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -363,13 +360,14 @@ void World::update_gui(int64_t actor_id, int64_t place_id, Gui *gui) {
|
|||||||
lua_pushvalue(L, actor.index());
|
lua_pushvalue(L, actor.index());
|
||||||
lua_pushvalue(L, place.index());
|
lua_pushvalue(L, place.index());
|
||||||
Gui::store_global_pointer(L, gui);
|
Gui::store_global_pointer(L, gui);
|
||||||
open_lthread_state(actor_id, place_id, false, false);
|
open_lthread_state(actor_id, place_id, 0, false, false);
|
||||||
int status = traceback_pcall(L, 2, 0);
|
eng::string msg = traceback_pcall(L, 2, 0);
|
||||||
close_lthread_state();
|
close_lthread_state();
|
||||||
Gui::store_global_pointer(L, nullptr);
|
Gui::store_global_pointer(L, nullptr);
|
||||||
if (status != 0) {
|
if (!msg.empty()) {
|
||||||
gui->clear(0);
|
gui->clear(0);
|
||||||
std::cerr << lua_tostring(L, -1);
|
assert(msg != "attempt to yield from outside a coroutine");
|
||||||
|
std::cerr << msg << std::endl;
|
||||||
LS.result();
|
LS.result();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -503,7 +501,7 @@ void World::invoke_lua(int64_t actor_id, int64_t place_id, const eng::string &ac
|
|||||||
LS.newtable(thinfo);
|
LS.newtable(thinfo);
|
||||||
LS.rawset(thinfo, "thread", thread);
|
LS.rawset(thinfo, "thread", thread);
|
||||||
LS.rawset(thinfo, "actorid", actor_id);
|
LS.rawset(thinfo, "actorid", actor_id);
|
||||||
LS.rawset(thinfo, "nargs", 0);
|
LS.rawset(thinfo, "isnew", true);
|
||||||
LS.rawset(thinfo, "useppool", true);
|
LS.rawset(thinfo, "useppool", true);
|
||||||
LS.rawset(thinfo, "print", true);
|
LS.rawset(thinfo, "print", true);
|
||||||
|
|
||||||
@@ -596,7 +594,7 @@ void World::invoke_plan(int64_t actor_id, int64_t place_id, const eng::string &a
|
|||||||
LS.newtable(thinfo);
|
LS.newtable(thinfo);
|
||||||
LS.rawset(thinfo, "thread", thread);
|
LS.rawset(thinfo, "thread", thread);
|
||||||
LS.rawset(thinfo, "actorid", actor_id);
|
LS.rawset(thinfo, "actorid", actor_id);
|
||||||
LS.rawset(thinfo, "nargs", 3); // actor, place, invdata
|
LS.rawset(thinfo, "isnew", true);
|
||||||
LS.rawset(thinfo, "useppool", true);
|
LS.rawset(thinfo, "useppool", true);
|
||||||
LS.rawset(thinfo, "print", false);
|
LS.rawset(thinfo, "print", false);
|
||||||
|
|
||||||
@@ -617,7 +615,7 @@ void World::invoke_plan(int64_t actor_id, int64_t place_id, const eng::string &a
|
|||||||
}
|
}
|
||||||
|
|
||||||
void World::invoke_tick(int64_t actor_id, int64_t place_id, const eng::string &action, const InvocationData &data) {
|
void World::invoke_tick(int64_t actor_id, int64_t place_id, const eng::string &action, const InvocationData &data) {
|
||||||
if (!util::world_type_authoritative(world_type_)) {
|
if (!is_authoritative()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
clock_ += 1;
|
clock_ += 1;
|
||||||
@@ -625,7 +623,7 @@ void World::invoke_tick(int64_t actor_id, int64_t place_id, const eng::string &a
|
|||||||
}
|
}
|
||||||
|
|
||||||
void World::invoke_lua_source(int64_t actor_id, int64_t place_id, const eng::string &action, const InvocationData &data) {
|
void World::invoke_lua_source(int64_t actor_id, int64_t place_id, const eng::string &action, const InvocationData &data) {
|
||||||
if (!util::world_type_authoritative(world_type_)) {
|
if (!is_authoritative()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// We need some kind of authentication here.
|
// We need some kind of authentication here.
|
||||||
@@ -642,8 +640,8 @@ void World::invoke_lua_source(int64_t actor_id, int64_t place_id, const eng::str
|
|||||||
void World::run_scheduled_threads() {
|
void World::run_scheduled_threads() {
|
||||||
assert(stack_is_clear());
|
assert(stack_is_clear());
|
||||||
lua_State *L = state();
|
lua_State *L = state();
|
||||||
LuaVar tangibles, place, mt, threads, thinfo, actorid, nargs, useppool, thread, print;
|
LuaVar tangibles, place, mt, threads, thinfo, actorid, isnew, useppool, thread, print;
|
||||||
LuaStack LS(L, tangibles, place, mt, threads, thinfo, actorid, nargs, useppool, thread, print);
|
LuaStack LS(L, tangibles, place, mt, threads, thinfo, actorid, isnew, useppool, thread, print);
|
||||||
|
|
||||||
LS.rawget(tangibles, LuaRegistry, "tangibles");
|
LS.rawget(tangibles, LuaRegistry, "tangibles");
|
||||||
while (thread_sched_.ready(clock_)) {
|
while (thread_sched_.ready(clock_)) {
|
||||||
@@ -668,8 +666,8 @@ void World::run_scheduled_threads() {
|
|||||||
if (!LS.isnumber(actorid)) {
|
if (!LS.isnumber(actorid)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
LS.rawget(nargs, thinfo, "nargs");
|
LS.rawget(isnew, thinfo, "isnew");
|
||||||
if (!LS.isnumber(nargs)) {
|
if (!LS.isboolean(isnew)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
LS.rawget(useppool, thinfo, "useppool");
|
LS.rawget(useppool, thinfo, "useppool");
|
||||||
@@ -683,35 +681,16 @@ void World::run_scheduled_threads() {
|
|||||||
|
|
||||||
// Resume the coroutine.
|
// Resume the coroutine.
|
||||||
lua_State *CO = LS.ckthread(thread);
|
lua_State *CO = LS.ckthread(thread);
|
||||||
open_lthread_state(LS.ckinteger(actorid), sched.place_id(), LS.ckboolean(useppool), true);
|
open_lthread_state(LS.ckinteger(actorid), sched.place_id(), sched.thread_id(), LS.ckboolean(useppool), true);
|
||||||
int status = lua_resume(CO, nullptr, LS.ckint(nargs));
|
int nargs = LS.ckboolean(isnew) ? (lua_gettop(CO) - 1) : lua_gettop(CO);
|
||||||
|
int status = lua_resume(CO, nullptr, nargs);
|
||||||
std::ostream *ostream = lthread_print_stream();
|
std::ostream *ostream = lthread_print_stream();
|
||||||
|
|
||||||
// Three possible outcomes: finished, yielded, or errored.
|
if (status == LUA_OK) {
|
||||||
if (!util::world_type_authoritative(world_type_)) {
|
|
||||||
LS.rawset(threads, sched.thread_id(), LuaNil);
|
|
||||||
} else if (status == LUA_YIELD) {
|
|
||||||
// If there's nothing on the stack, infer that tangible.nopredict yielded.
|
|
||||||
if (lua_gettop(CO) == 0) {
|
|
||||||
LS.rawset(threads, sched.thread_id(), LuaNil);
|
|
||||||
}
|
|
||||||
// If there's a single number on the stack, infer that 'wait' yielded.
|
|
||||||
else if ((lua_gettop(CO) == 1) && (lua_isnumber(CO, 1))) {
|
|
||||||
lua_Number delay = lua_tonumber(CO, 1);
|
|
||||||
lua_settop(CO, 0);
|
|
||||||
LS.rawset(thinfo, "nargs", 0);
|
|
||||||
LS.rawset(thinfo, "useppool", false);
|
|
||||||
thread_sched_.add(clock_ + int64_t(delay), sched.thread_id(), sched.place_id());
|
|
||||||
}
|
|
||||||
// In any other case, generate an error and kill the coroutine.
|
|
||||||
else {
|
|
||||||
std::cerr << "Thread yielded incorrectly. Killing it." << std::endl;
|
|
||||||
LS.rawset(threads, sched.thread_id(), LuaNil);
|
|
||||||
}
|
|
||||||
} else if (status == LUA_OK) {
|
|
||||||
// Successfully ran to completion. Print any return values.
|
// Successfully ran to completion. Print any return values.
|
||||||
// Remove from thread table.
|
// Remove from thread table.
|
||||||
LS.rawget(print, thinfo, "print");
|
LS.rawget(print, thinfo, "print");
|
||||||
|
LS.rawset(threads, sched.thread_id(), LuaNil);
|
||||||
LuaStack LSCO(CO);
|
LuaStack LSCO(CO);
|
||||||
if (LS.ckboolean(print)) {
|
if (LS.ckboolean(print)) {
|
||||||
for (int i = 1; i <= lua_gettop(CO); i++) {
|
for (int i = 1; i <= lua_gettop(CO); i++) {
|
||||||
@@ -719,12 +698,21 @@ void World::run_scheduled_threads() {
|
|||||||
(*ostream) << std::endl;
|
(*ostream) << std::endl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
LS.rawset(threads, sched.thread_id(), LuaNil);
|
} else if (status == LUA_YIELD) {
|
||||||
|
if (is_authoritative()) {
|
||||||
|
LS.rawset(thinfo, "isnew", false);
|
||||||
|
LS.rawset(thinfo, "useppool", false);
|
||||||
|
} else {
|
||||||
|
// In a nonauth model, a yield is converted to a 'nopredict'.
|
||||||
|
LS.rawset(threads, sched.thread_id(), LuaNil);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// Generated an error. Add a traceback, print, and kill the coroutine.
|
// Generated an error. Add a traceback, print, and kill the coroutine.
|
||||||
// Currently, the error is sent to the actor. That seems... not right in the long run.
|
// Currently, the error is sent to the actor. That seems... not right in the long run.
|
||||||
traceback_coroutine(CO);
|
if (is_authoritative()) {
|
||||||
(*ostream) << lua_tostring(CO, -1);
|
traceback_coroutine(CO);
|
||||||
|
(*ostream) << lua_tostring(CO, -1);
|
||||||
|
}
|
||||||
LS.rawset(threads, sched.thread_id(), LuaNil);
|
LS.rawset(threads, sched.thread_id(), LuaNil);
|
||||||
}
|
}
|
||||||
close_lthread_state();
|
close_lthread_state();
|
||||||
@@ -757,12 +745,14 @@ void World::clear_lthread_state() {
|
|||||||
lthread_prints_.reset();
|
lthread_prints_.reset();
|
||||||
lthread_actor_id_ = 0;
|
lthread_actor_id_ = 0;
|
||||||
lthread_place_id_ = 0;
|
lthread_place_id_ = 0;
|
||||||
|
lthread_thread_id_ = 0;
|
||||||
lthread_use_ppool_ = false;
|
lthread_use_ppool_ = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void World::open_lthread_state(int64_t actor, int64_t place, bool ppool, bool prints) {
|
void World::open_lthread_state(int64_t actor, int64_t place, int64_t thread, bool ppool, bool prints) {
|
||||||
lthread_actor_id_ = actor;
|
lthread_actor_id_ = actor;
|
||||||
lthread_place_id_ = place;
|
lthread_place_id_ = place;
|
||||||
|
lthread_thread_id_ = thread;
|
||||||
lthread_use_ppool_ = ppool;
|
lthread_use_ppool_ = ppool;
|
||||||
if (prints) {
|
if (prints) {
|
||||||
lthread_prints_.reset(new eng::ostringstream);
|
lthread_prints_.reset(new eng::ostringstream);
|
||||||
@@ -780,8 +770,7 @@ void World::close_lthread_state() {
|
|||||||
const eng::string &output = lthread_prints_->str();
|
const eng::string &output = lthread_prints_->str();
|
||||||
Tangible *actor = tangible_get(lthread_actor_id_);
|
Tangible *actor = tangible_get(lthread_actor_id_);
|
||||||
if (actor != nullptr) {
|
if (actor != nullptr) {
|
||||||
bool auth = util::world_type_authoritative(world_type_);
|
actor->print_buffer_.add_string(output, is_authoritative());
|
||||||
actor->print_buffer_.add_string(output, auth);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Now clean up everything.
|
// Now clean up everything.
|
||||||
|
|||||||
@@ -212,6 +212,10 @@ public:
|
|||||||
//
|
//
|
||||||
static World *fetch_global_pointer(lua_State *L);
|
static World *fetch_global_pointer(lua_State *L);
|
||||||
|
|
||||||
|
// Check if the world is authoritative.
|
||||||
|
//
|
||||||
|
bool is_authoritative() const { return (world_type_ == util::WORLD_TYPE_MASTER) || (world_type_ == util::WORLD_TYPE_STANDALONE); }
|
||||||
|
|
||||||
// Serialize and deserialize.
|
// Serialize and deserialize.
|
||||||
//
|
//
|
||||||
void serialize(StreamBuffer *sb);
|
void serialize(StreamBuffer *sb);
|
||||||
@@ -245,7 +249,7 @@ public:
|
|||||||
// cleared.
|
// cleared.
|
||||||
//
|
//
|
||||||
void clear_lthread_state();
|
void clear_lthread_state();
|
||||||
void open_lthread_state(int64_t actor_id, int64_t place_id, bool ppool, bool prints);
|
void open_lthread_state(int64_t actor_id, int64_t place_id, int64_t thread_id, bool ppool, bool prints);
|
||||||
void close_lthread_state();
|
void close_lthread_state();
|
||||||
|
|
||||||
std::ostream *lthread_print_stream() const;
|
std::ostream *lthread_print_stream() const;
|
||||||
@@ -260,7 +264,6 @@ public:
|
|||||||
int64_t alloc_id_predictable();
|
int64_t alloc_id_predictable();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
// Store a pointer to a world model into a lua registry.
|
// Store a pointer to a world model into a lua registry.
|
||||||
//
|
//
|
||||||
static void store_global_pointer(lua_State *L, World *w);
|
static void store_global_pointer(lua_State *L, World *w);
|
||||||
@@ -489,6 +492,7 @@ private:
|
|||||||
//
|
//
|
||||||
int64_t lthread_actor_id_;
|
int64_t lthread_actor_id_;
|
||||||
int64_t lthread_place_id_;
|
int64_t lthread_place_id_;
|
||||||
|
int64_t lthread_thread_id_;
|
||||||
int64_t lthread_use_ppool_;
|
int64_t lthread_use_ppool_;
|
||||||
std::unique_ptr<eng::ostringstream> lthread_prints_;
|
std::unique_ptr<eng::ostringstream> lthread_prints_;
|
||||||
|
|
||||||
@@ -503,6 +507,8 @@ private:
|
|||||||
friend int lfn_tangible_scan(lua_State *L);
|
friend int lfn_tangible_scan(lua_State *L);
|
||||||
friend int lfn_math_random(lua_State *L);
|
friend int lfn_math_random(lua_State *L);
|
||||||
friend int lfn_math_randomstate(lua_State *L);
|
friend int lfn_math_randomstate(lua_State *L);
|
||||||
|
friend int lfn_wait(lua_State *L);
|
||||||
|
friend int lfn_nopredict(lua_State *L);
|
||||||
};
|
};
|
||||||
|
|
||||||
using UniqueWorld = std::unique_ptr<World>;
|
using UniqueWorld = std::unique_ptr<World>;
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ makeclass('login')
|
|||||||
function login.interface(actor, place)
|
function login.interface(actor, place)
|
||||||
gui.menu_item("cb_becomeplayer", "Become a Player")
|
gui.menu_item("cb_becomeplayer", "Become a Player")
|
||||||
gui.menu_item("cb_p123", "Print 1, 2, 3")
|
gui.menu_item("cb_p123", "Print 1, 2, 3")
|
||||||
|
gui.menu_item("cb_p123_nopredict", "Print 1, 2, 3 nopredict")
|
||||||
end
|
end
|
||||||
|
|
||||||
function login.cb_becomeplayer(actor, place, dialog)
|
function login.cb_becomeplayer(actor, place, dialog)
|
||||||
@@ -20,9 +21,21 @@ function login.cb_p123(actor, place, dialog)
|
|||||||
print(3)
|
print(3)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function login.cb_p123_nopredict(actor, place, dialog)
|
||||||
|
nopredict()
|
||||||
|
print(1)
|
||||||
|
wait(1)
|
||||||
|
print(2)
|
||||||
|
wait(1)
|
||||||
|
print(3)
|
||||||
|
end
|
||||||
|
|
||||||
-- this is function documentation for setfoo.
|
-- this is function documentation for setfoo.
|
||||||
function setfoo(n)
|
function setfoo(n)
|
||||||
tangible.nopredict()
|
if (n == nil) then
|
||||||
|
error("setfoo(n) - n must be an integer")
|
||||||
|
end
|
||||||
|
nopredict()
|
||||||
tangible.actor().inventory.foo = n
|
tangible.actor().inventory.foo = n
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -591,6 +591,9 @@ LUA_API int lua_yieldk (lua_State *L, int nresults, int ctx, lua_CFunction k) {
|
|||||||
return 0; /* return to 'luaD_hook' */
|
return 0; /* return to 'luaD_hook' */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LUA_API int lua_isyieldable (lua_State *L) {
|
||||||
|
return (L->nny == 0);
|
||||||
|
}
|
||||||
|
|
||||||
int luaD_pcall (lua_State *L, Pfunc func, void *u,
|
int luaD_pcall (lua_State *L, Pfunc func, void *u,
|
||||||
ptrdiff_t old_top, ptrdiff_t ef) {
|
ptrdiff_t old_top, ptrdiff_t ef) {
|
||||||
|
|||||||
@@ -277,6 +277,7 @@ LUA_API int (lua_yieldk) (lua_State *L, int nresults, int ctx,
|
|||||||
#define lua_yield(L,n) lua_yieldk(L, (n), 0, NULL)
|
#define lua_yield(L,n) lua_yieldk(L, (n), 0, NULL)
|
||||||
LUA_API int (lua_resume) (lua_State *L, lua_State *from, int narg);
|
LUA_API int (lua_resume) (lua_State *L, lua_State *from, int narg);
|
||||||
LUA_API int (lua_status) (lua_State *L);
|
LUA_API int (lua_status) (lua_State *L);
|
||||||
|
LUA_API int (lua_isyieldable) (lua_State *L);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** garbage-collection function and options
|
** garbage-collection function and options
|
||||||
|
|||||||
Reference in New Issue
Block a user