diff --git a/lua-api/woofer.lua b/lua-api/woofer.lua index 625fc5e..cbf9e3c 100644 --- a/lua-api/woofer.lua +++ b/lua-api/woofer.lua @@ -1,11 +1,6 @@ --local thr = require('libbevy') -package.cpath = package.cpath .. ";./?.dylib" -require('libwoofer') -local thr = thread -return { - new = function(func) - local str_func = ("%q"):format(string.dump(func)) - return thr.new(str_func) - end, - thread_id = thr.thread_id -} +package.cpath = package.cpath .. ";./?.dylib" -- MAC OS support +local api = require('libwoofer') +api.thread.thread_id = nil +api.thread.join = nil +return api \ No newline at end of file diff --git a/src/lua-module.cpp b/src/lua-module.cpp new file mode 100644 index 0000000..5d2219c --- /dev/null +++ b/src/lua-module.cpp @@ -0,0 +1,18 @@ +#include "lua.hpp" + +#include "threading.h" +#include "shared-table.h" + +extern "C" int luaopen_libwoofer(lua_State *L) +{ + sol::state_view lua(L); + auto thread_obj = threading::LuaThread::get_user_type(lua); + auto share_obj = share_data::SharedTable::get_user_type(lua); + sol::table public_api = lua.create_table_with( + "thread", thread_obj, + "share", share_obj + ); + sol::stack::push(lua, public_api); + return 1; +} + diff --git a/src/shared-table.cpp b/src/shared-table.cpp index a11353b..d84c812 100644 --- a/src/shared-table.cpp +++ b/src/shared-table.cpp @@ -3,13 +3,17 @@ #include #include -namespace core { +namespace share_data { -void SharedTable::bind(sol::state_view& lua) noexcept { - lua.new_usertype("shared_table", - sol::meta_function::new_index, &SharedTable::luaSet, - sol::meta_function::index, &SharedTable::luaGet, - sol::meta_function::length, &SharedTable::size); +sol::object SharedTable::get_user_type(sol::state_view& lua) noexcept { + static sol::usertype type( + sol::call_construction(), sol::default_constructor, + sol::meta_function::new_index, &share_data::SharedTable::luaSet, + sol::meta_function::index, &share_data::SharedTable::luaGet, + sol::meta_function::length, &SharedTable::size + ); + sol::stack::push(lua, type); + return sol::stack::pop(lua); } void SharedTable::luaSet(sol::stack_object luaKey, sol::stack_object luaValue) noexcept { @@ -65,4 +69,4 @@ TablePool& defaultPool() noexcept { return pool; } -} // core \ No newline at end of file +} // share_data diff --git a/src/shared-table.h b/src/shared-table.h index e8bc3d5..f1c9a37 100644 --- a/src/shared-table.h +++ b/src/shared-table.h @@ -9,7 +9,7 @@ #include #include -namespace core { +namespace share_data { class SharedTable { public: @@ -18,8 +18,7 @@ public: void luaSet(sol::stack_object luaKey, sol::stack_object luaValue) noexcept; sol::object luaGet(sol::stack_object key, sol::this_state state) noexcept; - // Add usertype to state - static void bind(sol::state_view& lua) noexcept; + static sol::object get_user_type(sol::state_view& lua) noexcept; private: // lau bindings size_t size() noexcept; @@ -50,4 +49,4 @@ private: TablePool& defaultPool() noexcept; -} // core \ No newline at end of file +} // core diff --git a/src/spin-mutex.h b/src/spin-mutex.h index d497a32..f5b6537 100644 --- a/src/spin-mutex.h +++ b/src/spin-mutex.h @@ -3,7 +3,7 @@ #include #include -namespace core { +namespace share_data { class SpinMutex { public: diff --git a/src/stored-object.cpp b/src/stored-object.cpp index 2e695b7..a3a05f6 100644 --- a/src/stored-object.cpp +++ b/src/stored-object.cpp @@ -4,21 +4,10 @@ #include #include -#include #include -#define ERROR std::cerr - -namespace core { - -FunctionHolder::FunctionHolder(sol::stack_object luaObject) noexcept { - sol::state_view lua(luaObject.lua_state()); - sol::function dumper = lua["string"]["dump"]; - - assert(dumper.valid()); - function_ = dumper(luaObject); -} +namespace share_data { bool FunctionHolder::rawCompare(const BaseHolder* other) const noexcept { return function_ == static_cast(other)->function_; @@ -49,31 +38,6 @@ sol::object FunctionHolder::unpack(sol::this_state state) const noexcept { StoredObject::StoredObject(StoredObject&& init) noexcept : data_(std::move(init.data_)) {} -StoredObject::StoredObject(sol::stack_object luaObject) noexcept - : data_(nullptr) { - switch(luaObject.get_type()) { - case sol::type::nil: - break; - case sol::type::boolean: - data_.reset(new PrimitiveHolder(luaObject)); - break; - case sol::type::number: - data_.reset(new PrimitiveHolder(luaObject)); - break; - case sol::type::string: - data_.reset(new PrimitiveHolder(luaObject)); - break; - case sol::type::userdata: - data_.reset(new PrimitiveHolder(luaObject)); - break; - case sol::type::function: - data_.reset(new FunctionHolder(luaObject)); - break; - default: - ERROR << "Unable to store object of that type: " << (int)luaObject.get_type() << std::endl; - } -} - StoredObject::operator bool() const noexcept { return (bool)data_; } @@ -115,4 +79,4 @@ bool StoredObject::operator<(const StoredObject& o) const noexcept { return data_.get() < o.data_.get(); } -} // core \ No newline at end of file +} diff --git a/src/stored-object.h b/src/stored-object.h index 9897171..92dd1e4 100644 --- a/src/stored-object.h +++ b/src/stored-object.h @@ -3,8 +3,11 @@ #include #include +#include -namespace core { +namespace share_data { + +#define ERROR std::cerr class BaseHolder { public: @@ -41,8 +44,10 @@ template class PrimitiveHolder : public BaseHolder { public: PrimitiveHolder(sol::stack_object luaObject) noexcept - : data_(luaObject.as()) { - } + : data_(luaObject.as()) {} + + PrimitiveHolder(sol::object luaObject) noexcept + : data_(luaObject.as()) {} PrimitiveHolder(const StoredType& init) noexcept : data_(init) {} @@ -71,7 +76,15 @@ private: class FunctionHolder : public BaseHolder { public: - FunctionHolder(sol::stack_object luaObject) noexcept; + template + FunctionHolder(SolObject luaObject) noexcept { + sol::state_view lua(luaObject.lua_state()); + sol::function dumper = lua["string"]["dump"]; + + assert(dumper.valid()); + function_ = dumper(luaObject); + } + bool rawCompare(const BaseHolder* other) const noexcept final; bool rawLess(const BaseHolder* other) const noexcept final; std::size_t hash() const noexcept final; @@ -87,10 +100,34 @@ class StoredObject { public: StoredObject() = default; StoredObject(StoredObject&& init) noexcept; - StoredObject(sol::stack_object luaObject) noexcept; - //StoredObject(sol::object luaObject) noexcept; StoredObject(SharedTable*) noexcept; + template + StoredObject(SolObject luaObject) + { + switch(luaObject.get_type()) { + case sol::type::nil: + break; + case sol::type::boolean: + data_.reset(new PrimitiveHolder(luaObject)); + break; + case sol::type::number: + data_.reset(new PrimitiveHolder(luaObject)); + break; + case sol::type::string: + data_.reset(new PrimitiveHolder(luaObject)); + break; + case sol::type::userdata: + data_.reset(new PrimitiveHolder(luaObject)); + break; + case sol::type::function: + data_.reset(new FunctionHolder(luaObject)); + break; + default: + ERROR << "Unable to store object of that type: " << (int)luaObject.get_type() << std::endl; + } + } + operator bool() const noexcept; std::size_t hash() const noexcept; sol::object unpack(sol::this_state state) const noexcept; @@ -106,15 +143,15 @@ private: StoredObject& operator=(const StoredObject&) = delete; }; -} // core +} // share_data namespace std { template<> -struct hash { - std::size_t operator()(const core::StoredObject &object) const noexcept { +struct hash { + std::size_t operator()(const share_data::StoredObject &object) const noexcept { return object.hash(); } }; -} // std \ No newline at end of file +} // std diff --git a/src/threading.cpp b/src/threading.cpp index 5b3e799..f7f0962 100644 --- a/src/threading.cpp +++ b/src/threading.cpp @@ -1,92 +1,75 @@ -extern "C" -{ -#include "lua.h" -#include "lauxlib.h" -#include "lualib.h" +#include "threading.h" + +namespace threading { + +LuaThread::LuaThread(const sol::function& function, const sol::variadic_args& args) noexcept{ + // 1. Dump function to string + sol::state_view lua(function.lua_state()); + str_function_ = lua["string"]["dump"](function); + + // 2. Create new state + p_state_.reset(new sol::state); + assert(p_state_.get() != NULL); + p_state_->open_libraries( + sol::lib::base, sol::lib::string, + sol::lib::package, sol::lib::io, sol::lib::os + ); + get_user_type(*p_state_); + share_data::SharedTable::get_user_type(*p_state_); + + // 3. Save parameters + store_args(args); + + // 4. Run thread + p_thread_.reset(new std::thread(&LuaThread::work, this)); + assert(p_thread_.get() != NULL); } -#include -#include -#include -#include - -#include "shared-table.h" - -core::SharedTable shareTable; - -class LuaThread +void LuaThread::store_args(const sol::variadic_args& args) noexcept { -public: - - LuaThread(const std::string& rawFunction) - : m_strFunction(rawFunction) + const auto end = --args.end(); + for(auto iter = args.begin(); iter != end; iter++) { - std::cout << "LuaThread" << std::endl; - m_pState.reset(new sol::state); - core::SharedTable::bind(*m_pState); - (*m_pState)["share"] = &shareTable; - assert(m_pState.get() != NULL); - m_pState->open_libraries( - sol::lib::base, sol::lib::string, - sol::lib::package, sol::lib::io, sol::lib::os - ); - m_pThread.reset(new std::thread(&LuaThread::Impl, this)); - assert(m_pThread.get() != NULL); - std::cout << "LuaThread##" << std::endl; + share_data::StoredObject store(iter->get()); + arguments_.push_back(store.unpack(sol::this_state{p_state_->lua_state()})); } +} - virtual ~LuaThread() +void LuaThread::join() noexcept +{ + if (p_thread_.get()) { - std::cout << "~LuaThread" << std::endl; - Join(); + p_thread_->join(); + p_thread_.reset(); } + arguments_.clear(); + if (p_state_.get()) + p_state_.reset(); +} - void Join() - { - std::cout << "Join started" << std::endl; - if (m_pThread.get()) - { - m_pThread->join(); - m_pThread.reset(); - } - if (m_pState.get()) - { - m_pState.reset(); - } - std::cout << "Join finished" << std::endl; - } +void LuaThread::work() noexcept +{ + sol::state& lua = *p_state_; + sol::function_result func = lua["loadstring"](str_function_); + func.get()(sol::as_args(arguments_)); +} -private: - void Impl() - { - std::cout << "Impl" << std::endl; - std::stringstream script; - script << "loadstring(" << m_strFunction << ")()"; - m_pState->script(script.str()); - } - - std::string m_strFunction; - std::shared_ptr m_pState; - std::shared_ptr m_pThread; -}; - -static std::string ThreadId() +std::string LuaThread::thread_id() noexcept { std::stringstream ss; - ss << std::hash()(std::this_thread::get_id()); + ss << std::this_thread::get_id(); return ss.str(); } -extern "C" int luaopen_libwoofer(lua_State *L) +sol::object LuaThread::get_user_type(sol::state_view& lua) noexcept { - sol::state_view lua(L); - lua.new_usertype("thread", - sol::constructors>(), - "join", &LuaThread::Join + static sol::usertype type( + sol::call_construction(), sol::constructors>(), + "join", &LuaThread::join, + "thread_id", &LuaThread::thread_id ); - lua["thread"]["thread_id"] = ThreadId; - - core::SharedTable::bind(lua); - lua["share"] = &shareTable; - return 0; + sol::stack::push(lua, type); + return sol::stack::pop(lua); +} + } diff --git a/src/threading.h b/src/threading.h new file mode 100644 index 0000000..d5a0be1 --- /dev/null +++ b/src/threading.h @@ -0,0 +1,33 @@ +#pragma once + +#include "shared-table.h" + +#include + +#include +#include +#include + +namespace threading { + +class LuaThread +{ +public: + LuaThread(const sol::function& function, const sol::variadic_args& args) noexcept; + virtual ~LuaThread() noexcept = default; + void join() noexcept; + + static std::string thread_id() noexcept; + static sol::object get_user_type(sol::state_view& lua) noexcept; + +private: + void work() noexcept; + void store_args(const sol::variadic_args& args) noexcept; + + std::string str_function_; + std::shared_ptr p_state_; + std::shared_ptr p_thread_; + std::vector arguments_; +}; + +} diff --git a/tests/smoke_test.lua b/tests/smoke_test.lua index 75fa632..bdbb923 100644 --- a/tests/smoke_test.lua +++ b/tests/smoke_test.lua @@ -1,95 +1,55 @@ -local thr = require('woofer') -t = thr.new( - function() - local thr = require('woofer') - print(share['key1']) - print(share['key2']) - print(share['key3']) - print(thr.thread_id()) - for i = 1, 100 do - io.write('2') - io.flush() - os.execute("sleep 0.1") +function compare(o1, o2) + if o1 == o2 then return true end + local o1Type = type(o1) + local o2Type = type(o2) + if o1Type ~= o2Type then return false end + if o1Type ~= 'table' then return false end + + local keySet = {} + for key1, value1 in pairs(o1) do + local value2 = o2[key1] + if value2 == nil or equals(value1, value2) == false then + return false end - end -) - -share['key1'] = 'val' -share['key2'] = 100500 -share['key3'] = true -print(thr.thread_id()) -for i = 1, 100 do - io.write('1') - io.flush() - os.execute("sleep 0.1") -end -t:join() - ---[[ -ppp("qwe") -t = thr.new( - function() - local thr = require('bevy') - print(("0x%x"):format(thr.thread_id())) - for i = 1, 10 do - io.write('.') - io.flush() - os.execute("sleep " .. 1) - end - end -) -print(("0x%x"):format(thr.thread_id())) - -for i = 1, 10 do - io.write(',') - io.flush() - os.execute("sleep " .. 1) -end - -t:join() - -print() -]] - ---[[ -str_foo = "" - -asd =10 -qwe = 20 -function bar() - local lala = { 40 } - function foo(a, b) - local d = asd - local l = lala[1] - lala = 10 - return function() return a + asd + b + qwe + l end + keySet[key1] = true end - print(debug.getupvalue(foo, 1)) - print(debug.getupvalue(foo, 2)) - table_print(debug.getinfo(foo)) - str_foo = string.dump(foo) + for key2, _ in pairs(o2) do + if not keySet[key2] then return false end + end + return true end -bar() +function check(left, right) + if not compare(left, right) then + print("ERROR") + end +end -foo2 = loadstring(str_foo) +----------- +-- TESTS -- +----------- +do -- Simple smoke + package.cpath = package.cpath .. ";./?.dylib" -- MAC OS support + local woofer = require('libwoofer') + local share = woofer.share() -local t = {} -setmetatable(t, - { - __index = function(self, key) - print("Call ", key) - return _ENV[key] + share["number"] = 100500 + share["string"] = "string value" + share["bool"] = true + + local thread = woofer.thread( + function(share) + share["child.number"] = share["number"] + share["child.string"] = share["string"] + share["child.bool"] = share["bool"] end, - __newindex = function(self, key, value) - print("Call ", key, value) - _ENV[key] = value - end - } -) -debug.setupvalue(foo2, 1, t) -debug.setupvalue(foo2, 2, { 99 }) -print(foo2(10, 20)()) -]] + share + ) + thread:join() + + check(share["child.number"], share["number"]) + check(share["child.string"], share["string"]) + check(share["child.bool"], share["bool"]) +end