Overhaul of thread handling to support blocking functions other than wait

This commit is contained in:
2022-04-25 17:17:41 -04:00
parent bd389c7815
commit 9aec7c5299
15 changed files with 144 additions and 88 deletions

View File

@@ -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);

View File

@@ -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);

View File

@@ -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) << "}";

View File

@@ -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));
} }

View File

@@ -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;
} }
} }

View File

@@ -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 "";
} }

View File

@@ -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

View File

@@ -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";

View File

@@ -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);

View File

@@ -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...)",

View File

@@ -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.

View File

@@ -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>;

View File

@@ -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

View File

@@ -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) {

View File

@@ -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