feat(library/tactic): execute Lua tactics using coroutines
This is very important when several Lua tactics are implemented in the same Lua State object. In this case, even if we use the par combinator, a Lua tactic will block the other Lua tactics running in the same Lua State object. With this commit, a Lua tactic can use yield to allow other tactics in the same State object to execute. Signed-off-by: Leonardo de Moura <leonardo@microsoft.com>
This commit is contained in:
parent
1eeec07713
commit
ce674d2d43
6 changed files with 94 additions and 13 deletions
|
@ -386,20 +386,41 @@ static int mk_lua_tactic01(lua_State * L) {
|
|||
luaref ref(L, 1);
|
||||
return push_tactic(L,
|
||||
mk_tactic01([=](environment const & env, io_state const & ios, proof_state const & s) -> optional<proof_state> {
|
||||
optional<proof_state> r;
|
||||
script_state _S(S);
|
||||
_S.exec_protected([&]() {
|
||||
ref.push(); // push user-fun on the stack
|
||||
push_environment(L, env);
|
||||
push_io_state(L, ios);
|
||||
push_proof_state(L, s);
|
||||
pcall(L, 3, 1, 0);
|
||||
if (is_proof_state(L, -1)) {
|
||||
r = to_proof_state(L, -1);
|
||||
}
|
||||
lua_pop(L, 1);
|
||||
});
|
||||
return r;
|
||||
optional<proof_state> r;
|
||||
luaref coref; // Remark: we have to release the reference in a protected block.
|
||||
try {
|
||||
bool done = false;
|
||||
lua_State * co;
|
||||
_S.exec_protected([&]() {
|
||||
co = lua_newthread(L); // create a coroutine for executing user-fun
|
||||
coref = luaref(L, -1); // make sure co-routine in not deleted
|
||||
lua_pop(L, 1);
|
||||
ref.push(); // push user-fun on the stack
|
||||
push_environment(L, env); // push args...
|
||||
push_io_state(L, ios);
|
||||
push_proof_state(L, s);
|
||||
lua_xmove(L, co, 4); // move function and arguments to co
|
||||
done = resume(co, 3);
|
||||
});
|
||||
while (!done) {
|
||||
check_interrupted();
|
||||
std::this_thread::yield(); // give another thread a chance to execute
|
||||
_S.exec_protected([&]() {
|
||||
done = resume(co, 0);
|
||||
});
|
||||
}
|
||||
_S.exec_protected([&]() {
|
||||
if (is_proof_state(co, -1)) {
|
||||
r = to_proof_state(co, -1);
|
||||
}
|
||||
coref.release();
|
||||
});
|
||||
return r;
|
||||
} catch (...) {
|
||||
_S.exec_protected([&]() { coref.release(); });
|
||||
throw;
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
|
|
|
@ -115,6 +115,21 @@ void pcall(lua_State * L, int nargs, int nresults, int errorfun) {
|
|||
check_result(L, result);
|
||||
}
|
||||
|
||||
bool resume(lua_State * L, int nargs) {
|
||||
#if LUA_VERSION_NUM < 502
|
||||
int result = lua_resume(L, nargs);
|
||||
#else
|
||||
int result = lua_resume(L, nullptr, nargs);
|
||||
#endif
|
||||
if (result == LUA_YIELD)
|
||||
return false;
|
||||
if (result == 0)
|
||||
return true;
|
||||
check_result(L, result);
|
||||
lean_unreachable();
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Wrapper for "customers" that are only using a subset
|
||||
of Lean libraries.
|
||||
|
|
|
@ -20,6 +20,11 @@ size_t objlen(lua_State * L, int idx);
|
|||
void dofile(lua_State * L, char const * fname);
|
||||
void dostring(lua_State * L, char const * str);
|
||||
void pcall(lua_State * L, int nargs, int nresults, int errorfun);
|
||||
/**
|
||||
\brief Return true iff coroutine is done, false if it has yielded,
|
||||
and throws an exception if error.
|
||||
*/
|
||||
bool resume(lua_State * L, int nargs);
|
||||
int lessthan(lua_State * L, int idx1, int idx2);
|
||||
int equal(lua_State * L, int idx1, int idx2);
|
||||
int get_nonnil_top(lua_State * L);
|
||||
|
|
|
@ -34,6 +34,13 @@ luaref::~luaref() {
|
|||
luaL_unref(m_state, LUA_REGISTRYINDEX, m_ref);
|
||||
}
|
||||
|
||||
void luaref::release() {
|
||||
if (m_state) {
|
||||
luaL_unref(m_state, LUA_REGISTRYINDEX, m_ref);
|
||||
m_state = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
luaref & luaref::operator=(luaref const & r) {
|
||||
if (m_ref == r.m_ref)
|
||||
return *this;
|
||||
|
|
|
@ -23,6 +23,7 @@ public:
|
|||
luaref(luaref const & r);
|
||||
luaref(luaref && r);
|
||||
~luaref();
|
||||
void release();
|
||||
luaref & operator=(luaref const & r);
|
||||
void push() const;
|
||||
lua_State * get_state() const { return m_state; }
|
||||
|
|
32
tests/lua/threads/tactic2.lua
Normal file
32
tests/lua/threads/tactic2.lua
Normal file
|
@ -0,0 +1,32 @@
|
|||
local env = environment()
|
||||
local ios = io_state()
|
||||
local Bool = Const("Bool")
|
||||
env:add_var("p", Bool)
|
||||
env:add_var("q", Bool)
|
||||
local p, q = Consts("p, q")
|
||||
local ctx = context()
|
||||
|
||||
S = State()
|
||||
-- tactics t1 and t2 uses yield to implement cooperative
|
||||
-- multitasking
|
||||
local counter1 = 0
|
||||
local t1 = tactic(function(env, ios, s)
|
||||
while true do
|
||||
counter1 = counter1 + 1
|
||||
coroutine.yield()
|
||||
end
|
||||
end)
|
||||
|
||||
local counter2 = 0
|
||||
local t2 = tactic(function(env, ios, s)
|
||||
while true do
|
||||
counter2 = counter2 + 1
|
||||
coroutine.yield()
|
||||
end
|
||||
end)
|
||||
|
||||
local T = (t1:par(t2)):try_for(150)
|
||||
T:solve(env, ios, ctx, p)
|
||||
print(counter1, counter2)
|
||||
assert(counter1 > 2)
|
||||
assert(counter2 > 2)
|
Loading…
Reference in a new issue