Issue #160: make thread runner shared table
This commit is contained in:
parent
92a7bb50d2
commit
2333863a68
@ -1,8 +1,8 @@
|
||||
image: Visual Studio 2015
|
||||
|
||||
configuration:
|
||||
- debug
|
||||
- release
|
||||
# - debug
|
||||
|
||||
platform:
|
||||
- x86
|
||||
|
||||
@ -2,6 +2,7 @@
|
||||
#include "shared-table.h"
|
||||
#include "garbage-collector.h"
|
||||
#include "channel.h"
|
||||
#include "thread_runner.h"
|
||||
|
||||
#include <lua.hpp>
|
||||
|
||||
@ -52,30 +53,18 @@ sol::object luaDump(sol::this_state lua, const sol::stack_object& obj) {
|
||||
<< luaTypename(obj) << ")";
|
||||
}
|
||||
|
||||
sol::table luaThreadConfig(sol::this_state state, const sol::stack_object& obj) {
|
||||
sol::table createThreadRunner(sol::this_state state, const sol::stack_object& obj) {
|
||||
REQUIRE(obj.valid() && obj.get_type() == sol::type::function)
|
||||
<< "bad argument #1 to 'effil.thread' (function expected, got "
|
||||
<< luaTypename(obj) << ")";
|
||||
|
||||
auto lua = sol::state_view(state);
|
||||
const sol::function func = obj.as<sol::function>();
|
||||
|
||||
auto config = lua.create_table_with(
|
||||
"path", lua["package"]["path"],
|
||||
"cpath", lua["package"]["cpath"],
|
||||
"step", 200
|
||||
);
|
||||
|
||||
auto meta = lua.create_table_with();
|
||||
meta[sol::meta_function::call] = [func](sol::this_state lua,
|
||||
const sol::stack_table& self, const sol::variadic_args& args)
|
||||
{
|
||||
return sol::make_object(lua, GC::instance().create<Thread>(
|
||||
self["path"], self["cpath"], self["step"], func, args));
|
||||
};
|
||||
|
||||
config[sol::metatable_key] = meta;
|
||||
return config;
|
||||
return sol::make_object(lua, GC::instance().create<ThreadRunner>(
|
||||
lua["package"]["path"],
|
||||
lua["package"]["cpath"],
|
||||
200,
|
||||
obj.as<sol::function>()
|
||||
));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
@ -89,6 +78,7 @@ int luaopen_effil(lua_State* L) {
|
||||
Thread::exportAPI(lua);
|
||||
SharedTable::exportAPI(lua);
|
||||
Channel::exportAPI(lua);
|
||||
ThreadRunner::exportAPI(lua);
|
||||
|
||||
const sol::table gcApi = GC::exportAPI(lua);
|
||||
const sol::object gLuaTable = sol::make_object(lua, globalTable);
|
||||
@ -106,7 +96,7 @@ int luaopen_effil(lua_State* L) {
|
||||
};
|
||||
|
||||
sol::usertype<EffilApiMarker> type("new", sol::no_constructor,
|
||||
"thread", luaThreadConfig,
|
||||
"thread", createThreadRunner,
|
||||
"thread_id", threadId,
|
||||
"sleep", sleep,
|
||||
"yield", yield,
|
||||
|
||||
@ -4,6 +4,7 @@
|
||||
#include "shared-table.h"
|
||||
#include "function.h"
|
||||
#include "utils.h"
|
||||
#include "thread_runner.h"
|
||||
|
||||
#include <map>
|
||||
#include <vector>
|
||||
@ -180,6 +181,8 @@ StoredObject fromSolObject(const SolObject& luaObject, SolTableToShared& visited
|
||||
return std::make_unique<GCObjectHolder<Thread>>(luaObject);
|
||||
else if (luaObject.template is<EffilApiMarker>())
|
||||
return std::make_unique<ApiReferenceHolder>();
|
||||
else if (luaObject.template is<ThreadRunner>())
|
||||
return std::make_unique<GCObjectHolder<ThreadRunner>>(luaObject);
|
||||
else
|
||||
throw Exception() << "Unable to store userdata object";
|
||||
case sol::type::function: {
|
||||
|
||||
39
src/cpp/thread_runner.cpp
Normal file
39
src/cpp/thread_runner.cpp
Normal file
@ -0,0 +1,39 @@
|
||||
#include "thread_runner.h"
|
||||
|
||||
namespace effil {
|
||||
|
||||
void ThreadRunner::initialize(
|
||||
const std::string& path,
|
||||
const std::string& cpath,
|
||||
lua_Number step,
|
||||
const sol::function& func)
|
||||
{
|
||||
ctx_->path_ = path;
|
||||
ctx_->cpath_ = cpath;
|
||||
ctx_->step_ = step;
|
||||
try {
|
||||
ctx_->function_ = createStoredObject(func);
|
||||
} RETHROW_WITH_PREFIX("effil.thread");
|
||||
|
||||
ctx_->addReference(ctx_->function_->gcHandle());
|
||||
ctx_->function_->releaseStrongReference();
|
||||
}
|
||||
|
||||
sol::object ThreadRunner::call(sol::this_state lua, const sol::variadic_args& args) {
|
||||
return sol::make_object(lua, GC::instance().create<Thread>(
|
||||
ctx_->path_, ctx_->cpath_, ctx_->step_, ctx_->function_->unpack(lua), args));
|
||||
}
|
||||
|
||||
void ThreadRunner::exportAPI(sol::state_view& lua) {
|
||||
|
||||
sol::usertype<ThreadRunner> type("new", sol::no_constructor,
|
||||
sol::meta_function::call, &ThreadRunner::call,
|
||||
"path", sol::property(&ThreadRunner::getPath, &ThreadRunner::setPath),
|
||||
"cpath", sol::property(&ThreadRunner::getCPath, &ThreadRunner::setCPath),
|
||||
"step", sol::property(&ThreadRunner::getStep, &ThreadRunner::setStep)
|
||||
);
|
||||
sol::stack::push(lua, type);
|
||||
sol::stack::pop<sol::object>(lua);
|
||||
}
|
||||
|
||||
} // namespace effil
|
||||
39
src/cpp/thread_runner.h
Normal file
39
src/cpp/thread_runner.h
Normal file
@ -0,0 +1,39 @@
|
||||
#include "threading.h"
|
||||
#include "gc-data.h"
|
||||
#include "gc-object.h"
|
||||
|
||||
namespace effil {
|
||||
|
||||
class ThreadRunnerData : public GCData {
|
||||
public:
|
||||
std::string path_;
|
||||
std::string cpath_;
|
||||
lua_Number step_;
|
||||
StoredObject function_;
|
||||
};
|
||||
|
||||
struct ThreadRunner: public GCObject<ThreadRunnerData> {
|
||||
static void exportAPI(sol::state_view& lua);
|
||||
|
||||
std::string getPath() const { return ctx_->path_; }
|
||||
void setPath(const std::string& p) { ctx_->path_ = p; }
|
||||
|
||||
std::string getCPath() const { return ctx_->cpath_; }
|
||||
void setCPath(const std::string& p) { ctx_->cpath_ = p; }
|
||||
|
||||
lua_Number getStep() const { return ctx_->step_; }
|
||||
void setStep(lua_Number s) { ctx_->step_ = s; }
|
||||
|
||||
private:
|
||||
ThreadRunner() = default;
|
||||
void initialize(
|
||||
const std::string& path,
|
||||
const std::string& cpath,
|
||||
lua_Number step,
|
||||
const sol::function& func);
|
||||
|
||||
sol::object call(sol::this_state lua, const sol::variadic_args& args);
|
||||
friend class GC;
|
||||
};
|
||||
|
||||
} // namespace effil
|
||||
@ -45,29 +45,22 @@ std::string statusToString(Status status) {
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
#if LUA_VERSION_NUM > 501
|
||||
|
||||
int luaErrorHandler(lua_State* state) {
|
||||
luaL_traceback(state, state, nullptr, 1);
|
||||
const auto stacktrace = sol::stack::pop<std::string>(state);
|
||||
thisThreadHandle->result().emplace_back(createStoredObject(stacktrace));
|
||||
throw Exception() << sol::stack::pop<std::string>(state);
|
||||
return 1;
|
||||
}
|
||||
|
||||
const lua_CFunction luaErrorHandlerPtr = luaErrorHandler;
|
||||
|
||||
#else
|
||||
|
||||
const lua_CFunction luaErrorHandlerPtr = nullptr;
|
||||
|
||||
#endif // LUA_VERSION_NUM > 501
|
||||
|
||||
void luaHook(lua_State*, lua_Debug*) {
|
||||
assert(thisThreadHandle);
|
||||
switch (thisThreadHandle->command()) {
|
||||
case Command::Run:
|
||||
break;
|
||||
case Command::Cancel:
|
||||
thisThreadHandle->changeStatus(Status::Canceled);
|
||||
throw LuaHookStopException();
|
||||
case Command::Pause: {
|
||||
thisThreadHandle->changeStatus(Status::Paused);
|
||||
@ -75,10 +68,12 @@ void luaHook(lua_State*, lua_Debug*) {
|
||||
do {
|
||||
cmd = thisThreadHandle->waitForCommandChange(NO_TIMEOUT);
|
||||
} while(cmd != Command::Run && cmd != Command::Cancel);
|
||||
if (cmd == Command::Run)
|
||||
if (cmd == Command::Run) {
|
||||
thisThreadHandle->changeStatus(Status::Running);
|
||||
else
|
||||
} else {
|
||||
thisThreadHandle->changeStatus(Status::Canceled);
|
||||
throw LuaHookStopException();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -89,7 +84,7 @@ void luaHook(lua_State*, lua_Debug*) {
|
||||
ThreadHandle::ThreadHandle()
|
||||
: status_(Status::Running)
|
||||
, command_(Command::Run)
|
||||
, lua_(std::make_unique<sol::state>(luaErrorHandlerPtr)) {
|
||||
, lua_(std::make_unique<sol::state>()) {
|
||||
luaL_openlibs(*lua_);
|
||||
}
|
||||
|
||||
@ -126,9 +121,26 @@ void Thread::runThread(Thread thread,
|
||||
arguments.clear();
|
||||
thread.ctx_->destroyLua();
|
||||
});
|
||||
sol::function userFuncObj = function.loadFunction(thread.ctx_->lua());
|
||||
sol::function_result results = userFuncObj(std::move(arguments));
|
||||
(void)results; // just leave all returns on the stack
|
||||
sol::protected_function userFuncObj = function.loadFunction(thread.ctx_->lua());
|
||||
|
||||
#if LUA_VERSION_NUM > 501
|
||||
|
||||
sol::stack::push(thread.ctx_->lua(), luaErrorHandlerPtr);
|
||||
userFuncObj.error_handler = sol::reference(thread.ctx_->lua());
|
||||
sol::stack::pop_n(thread.ctx_->lua(), 1);
|
||||
|
||||
#endif // LUA_VERSION NUM > 501
|
||||
|
||||
sol::protected_function_result result = userFuncObj(std::move(arguments));
|
||||
if (!result.valid()) {
|
||||
if (thread.ctx_->status() == Status::Canceled)
|
||||
return;
|
||||
|
||||
sol::error err = result;
|
||||
std::string what = err.what();
|
||||
throw std::runtime_error(what);
|
||||
}
|
||||
|
||||
sol::variadic_args args(thread.ctx_->lua(), -lua_gettop(thread.ctx_->lua()));
|
||||
for (const auto& iter : args) {
|
||||
StoredObject store = createStoredObject(iter.get<sol::object>());
|
||||
@ -143,7 +155,7 @@ void Thread::runThread(Thread thread,
|
||||
thread.ctx_->changeStatus(Status::Completed);
|
||||
} catch (const LuaHookStopException&) {
|
||||
thread.ctx_->changeStatus(Status::Canceled);
|
||||
} catch (const sol::error& err) {
|
||||
} catch (const std::exception& err) {
|
||||
DEBUG("thread") << "Failed with msg: " << err.what() << std::endl;
|
||||
auto& returns = thread.ctx_->result();
|
||||
returns.insert(returns.begin(),
|
||||
@ -193,7 +205,10 @@ void Thread::initialize(
|
||||
|
||||
ctx_->lua()["package"]["path"] = path;
|
||||
ctx_->lua()["package"]["cpath"] = cpath;
|
||||
ctx_->lua().script("require 'effil'");
|
||||
try {
|
||||
luaopen_effil(ctx_->lua());
|
||||
sol::stack::pop<sol::object>(ctx_->lua());
|
||||
} RETHROW_WITH_PREFIX("effil.thread");
|
||||
|
||||
if (step != 0)
|
||||
lua_sethook(ctx_->lua(), luaHook, LUA_MASKCOUNT, step);
|
||||
|
||||
@ -45,6 +45,7 @@ test.gc_stress.regress_for_concurent_thread_creation = function ()
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
test.gc_stress.regress_for_concurent_function_creation = function ()
|
||||
local a = function() end
|
||||
local b = function() end
|
||||
|
||||
@ -8,6 +8,28 @@ test.thread.hardware_threads = function()
|
||||
test.is_true(effil.hardware_threads() >= 0)
|
||||
end
|
||||
|
||||
test.thread.runner_is_serializible = function ()
|
||||
local table = effil.table()
|
||||
local runner = effil.thread(function(n) return n * 2 end)
|
||||
|
||||
table["runner"] = runner
|
||||
test.equal(table["runner"](123):get(), 246)
|
||||
end
|
||||
|
||||
test.thread.runner_path_check_p = function (config_key, pkg)
|
||||
local table = effil.table()
|
||||
local runner = effil.thread(function()
|
||||
require(pkg)
|
||||
end)
|
||||
test.equal(runner():wait(), "completed")
|
||||
|
||||
runner[config_key] = ""
|
||||
test.equal(runner():wait(), "failed")
|
||||
end
|
||||
|
||||
test.thread.runner_path_check_p("path", "size") -- some testing Lua file to import
|
||||
test.thread.runner_path_check_p("cpath", "effil")
|
||||
|
||||
test.thread.wait = function ()
|
||||
local thread = effil.thread(function()
|
||||
print 'Effil is not that tower'
|
||||
@ -395,13 +417,15 @@ test.thread.traceback = function()
|
||||
local status, err, trace = effil.thread(foo)():wait()
|
||||
print("status: ", status)
|
||||
print("error: ", err)
|
||||
print("stacktrace: ", trace)
|
||||
print("stacktrace:")
|
||||
print(trace)
|
||||
|
||||
test.equal(status, "failed")
|
||||
-- <souce file>.lua:<string number>: <error message>
|
||||
test.is_not_nil(string.find(err, curr_file .. ":%d+: err msg"))
|
||||
test.is_not_nil(string.find(trace, (
|
||||
[[stack traceback:
|
||||
%%s%%[C%%]: in function 'error'
|
||||
%%s%s:%%d+: in function 'boom'
|
||||
%%s%s:%%d+: in function 'bar'
|
||||
%%s%s:%%d+: in function <%s:%%d+>]]
|
||||
|
||||
@ -40,7 +40,7 @@ test.upvalues.check_single_upvalue_p(function() return effil.thread(foo)() end,
|
||||
test.upvalues.check_invalid_coroutine = function()
|
||||
local obj = coroutine.create(foo)
|
||||
local thread_worker = function() return tostring(obj) end
|
||||
local ret, err = pcall(effil.thread(thread_worker))
|
||||
local ret, err = pcall(effil.thread, thread_worker)
|
||||
if ret then
|
||||
ret:wait()
|
||||
end
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user