SharedTable able to store lua tables && related tests.

Fix codestyle.
This commit is contained in:
Ilia Udalov 2017-01-21 22:39:58 +03:00
parent 7e35ab3a76
commit 5407a33947
11 changed files with 328 additions and 198 deletions

View File

@ -14,7 +14,7 @@ FILE(GLOB SOURCES src/*.cpp src/*.h)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/build) set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/build)
if(APPLE) if(APPLE)
# Supress CMP0042 # Supress warning CMP0042
set(CMAKE_MACOSX_RPATH 1) set(CMAKE_MACOSX_RPATH 1)
endif() endif()

View File

@ -3,24 +3,25 @@
#include <lua.hpp> #include <lua.hpp>
static sol::object create_thread(sol::this_state lua, sol::function func, const sol::variadic_args& args) namespace {
{
static sol::object createThread(sol::this_state lua, sol::function func, const sol::variadic_args &args) noexcept {
return sol::make_object(lua, std::make_unique<threading::LuaThread>(func, args)); return sol::make_object(lua, std::make_unique<threading::LuaThread>(func, args));
} }
static sol::object create_share(sol::this_state lua) static sol::object createShare(sol::this_state lua) noexcept {
{
return sol::make_object(lua, std::make_unique<share_data::SharedTable>()); return sol::make_object(lua, std::make_unique<share_data::SharedTable>());
} }
extern "C" int luaopen_libwoofer(lua_State *L) } // namespace
{
extern "C" int luaopen_libwoofer(lua_State *L) {
sol::state_view lua(L); sol::state_view lua(L);
threading::LuaThread::get_user_type(lua); threading::LuaThread::getUserType(lua);
share_data::SharedTable::get_user_type(lua); share_data::SharedTable::getUserType(lua);
sol::table public_api = lua.create_table_with( sol::table public_api = lua.create_table_with(
"thread", create_thread, "thread", createThread,
"share", create_share "share", createShare
); );
sol::stack::push(lua, public_api); sol::stack::push(lua, public_api);
return 1; return 1;

View File

@ -5,7 +5,7 @@
namespace share_data { namespace share_data {
sol::object SharedTable::get_user_type(sol::state_view& lua) noexcept { sol::object SharedTable::getUserType(sol::state_view &lua) noexcept {
static sol::usertype<share_data::SharedTable> type( static sol::usertype<share_data::SharedTable> type(
sol::call_construction(), sol::default_constructor, sol::call_construction(), sol::default_constructor,
sol::meta_function::new_index, &share_data::SharedTable::luaSet, sol::meta_function::new_index, &share_data::SharedTable::luaSet,
@ -16,6 +16,11 @@ sol::object SharedTable::get_user_type(sol::state_view& lua) noexcept {
return sol::stack::pop<sol::object>(lua); return sol::stack::pop<sol::object>(lua);
} }
void SharedTable::set(StoredObject key, StoredObject value) noexcept {
std::lock_guard<SpinMutex> g(lock_);
data_[std::move(key)] = std::move(value);
}
void SharedTable::luaSet(sol::stack_object luaKey, sol::stack_object luaValue) noexcept { void SharedTable::luaSet(sol::stack_object luaKey, sol::stack_object luaValue) noexcept {
assert(luaKey.valid()); assert(luaKey.valid());
@ -31,7 +36,7 @@ void SharedTable::luaSet(sol::stack_object luaKey, sol::stack_object luaValue) n
} }
} }
sol::object SharedTable::luaGet(sol::stack_object key, sol::this_state state) noexcept { sol::object SharedTable::luaGet(sol::stack_object key, sol::this_state state) const noexcept {
assert(key.valid()); assert(key.valid());
StoredObject cppKey(key); StoredObject cppKey(key);
@ -44,15 +49,16 @@ sol::object SharedTable::luaGet(sol::stack_object key, sol::this_state state) no
} }
} }
size_t SharedTable::size() noexcept { size_t SharedTable::size() const noexcept {
std::lock_guard<SpinMutex> g(lock_); std::lock_guard<SpinMutex> g(lock_);
return data_.size(); return data_.size();
} }
SharedTable* TablePool::getNew() noexcept { SharedTable* TablePool::getNew() noexcept {
SharedTable* ptr = new SharedTable();
std::lock_guard<SpinMutex> g(lock_); std::lock_guard<SpinMutex> g(lock_);
data_.push_back(std::make_unique<SharedTable>()); data_.emplace_back(ptr);
return (*data_.rend()).get(); return ptr;
} }
std::size_t TablePool::size() const noexcept { std::size_t TablePool::size() const noexcept {
std::lock_guard<SpinMutex> g(lock_); std::lock_guard<SpinMutex> g(lock_);

View File

@ -15,16 +15,17 @@ class SharedTable {
public: public:
SharedTable() = default; SharedTable() = default;
virtual ~SharedTable() = default; virtual ~SharedTable() = default;
static sol::object getUserType(sol::state_view &lua) noexcept;
void set(StoredObject, StoredObject) noexcept;
size_t size() const noexcept;
public: // lua bindings
void luaSet(sol::stack_object luaKey, sol::stack_object luaValue) noexcept; void luaSet(sol::stack_object luaKey, sol::stack_object luaValue) noexcept;
sol::object luaGet(sol::stack_object key, sol::this_state state) noexcept; sol::object luaGet(sol::stack_object key, sol::this_state state) const noexcept;
static sol::object get_user_type(sol::state_view& lua) noexcept;
private: // lau bindings
size_t size() noexcept;
protected: protected:
SpinMutex lock_; mutable SpinMutex lock_;
std::unordered_map<StoredObject, StoredObject> data_; std::unordered_map<StoredObject, StoredObject> data_;
private: private:
@ -49,4 +50,4 @@ private:
TablePool& defaultPool() noexcept; TablePool& defaultPool() noexcept;
} // core } // share_data

View File

@ -21,4 +21,4 @@ private:
std::atomic_flag lock_ = ATOMIC_FLAG_INIT; std::atomic_flag lock_ = ATOMIC_FLAG_INIT;
}; };
} // core } // share_data

View File

@ -1,51 +1,183 @@
#include "stored-object.h" #include "stored-object.h"
#include "shared-table.h" #include "shared-table.h"
#include "utils.h"
#include <map> #include <map>
#include <vector> #include <vector>
#include <algorithm>
#include <cassert> #include <cassert>
namespace share_data { namespace share_data {
bool FunctionHolder::rawCompare(const BaseHolder* other) const noexcept { namespace {
return function_ == static_cast<const FunctionHolder*>(other)->function_;
}
bool FunctionHolder::rawLess(const BaseHolder* other) const noexcept { template<typename StoredType>
return function_ < static_cast<const FunctionHolder*>(other)->function_; class PrimitiveHolder : public BaseHolder {
} public:
PrimitiveHolder(sol::stack_object luaObject) noexcept
: data_(luaObject.as<StoredType>()) {}
std::size_t FunctionHolder::hash() const noexcept { PrimitiveHolder(sol::object luaObject) noexcept
return std::hash<std::string>()(function_); : data_(luaObject.as<StoredType>()) {}
}
sol::object FunctionHolder::unpack(sol::this_state state) const noexcept { PrimitiveHolder(const StoredType& init) noexcept
sol::state_view lua((lua_State*)state); : data_(init) {}
sol::function loader = lua["loadstring"];
assert(loader.valid());
sol::function result = loader(function_); bool rawCompare(const BaseHolder* other) const noexcept final {
if (!result.valid()) { assert(type_ == other->type());
ERROR << "Unable to restore function!" << std::endl; return static_cast<const PrimitiveHolder<StoredType>*>(other)->data_ == data_;
ERROR << "Content:" << std::endl; }
ERROR << function_ << std::endl;
bool rawLess(const BaseHolder* other) const noexcept final {
assert(type_ == other->type());
return data_ < static_cast<const PrimitiveHolder<StoredType>*>(other)->data_;
}
std::size_t hash() const noexcept final {
return std::hash<StoredType>()(data_);
}
sol::object unpack(sol::this_state state) const noexcept final {
return sol::make_object(state, data_);
}
private:
StoredType data_;
};
class FunctionHolder : public BaseHolder {
public:
template<typename SolObject>
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 {
return function_ == static_cast<const FunctionHolder*>(other)->function_;
}
bool rawLess(const BaseHolder* other) const noexcept final {
return function_ < static_cast<const FunctionHolder*>(other)->function_;
}
std::size_t hash() const noexcept final {
return std::hash<std::string>()(function_);
}
sol::object unpack(sol::this_state state) const noexcept final {
sol::state_view lua((lua_State*)state);
sol::function loader = lua["loadstring"];
assert(loader.valid());
sol::function result = loader(function_);
if (!result.valid()) {
ERROR << "Unable to restore function!" << std::endl;
ERROR << "Content:" << std::endl;
ERROR << function_ << std::endl;
}
return sol::make_object(state, result);
}
private:
std::string function_;
};
// This class is used as a storage for visited sol::tables
// TODO: try to use map or unordered map instead of linear search in vector
// TODO: Trick is - sol::object has only operator==:/
typedef std::vector<std::pair<sol::object, SharedTable*>> SolTableToShared;
void dumpTable(SharedTable* target, sol::table luaTable, SolTableToShared& visited) noexcept;
StoredObject makeStoredObject(sol::object luaObject, SolTableToShared& visited) noexcept {
if (luaObject.get_type() == sol::type::table) {
sol::table luaTable = luaObject;
auto comparator = [&luaTable](const std::pair<sol::table, SharedTable*>& element){
return element.first == luaTable;
};
auto st = std::find_if(visited.begin(), visited.end(), comparator);
if (st == std::end(visited)) {
SharedTable* table = defaultPool().getNew();
visited.emplace_back(std::make_pair(luaTable, table));
dumpTable(table, luaTable, visited);
return StoredObject(table);
} else {
return StoredObject(st->second);
}
} else {
return StoredObject(luaObject);
} }
return sol::make_object(state, result);
} }
void dumpTable(SharedTable* target, sol::table luaTable, SolTableToShared& visited) noexcept {
for(auto& row : luaTable) {
target->set(makeStoredObject(row.first, visited), makeStoredObject(row.second, visited));
}
}
template<typename SolObject>
std::unique_ptr<BaseHolder> fromSolObject(SolObject luaObject) {
switch(luaObject.get_type()) {
case sol::type::nil:
break;
case sol::type::boolean:
return std::make_unique<PrimitiveHolder<bool>>(luaObject);
case sol::type::number:
return std::make_unique<PrimitiveHolder<double>>(luaObject);
case sol::type::string:
return std::make_unique<PrimitiveHolder<std::string>>(luaObject);
case sol::type::userdata:
return std::make_unique<PrimitiveHolder<SharedTable*>>(luaObject);
case sol::type::function:
return std::make_unique<FunctionHolder>(luaObject);
case sol::type::table:
{
sol::table luaTable = luaObject;
// Tables pool is used to store tables.
// Right now not defiantly clear how ownership between states works.
SharedTable* table = defaultPool().getNew();
SolTableToShared visited{{luaTable, table}};
// Let's dump table and all subtables
// SolTableToShared is used to prevent from infinity recursion
// in recursive tables
dumpTable(table, luaTable, visited);
return std::make_unique<PrimitiveHolder<SharedTable*>>(table);
}
default:
ERROR << "Unable to store object of that type: " << (int)luaObject.get_type() << std::endl;
}
return nullptr;
}
} // namespace
StoredObject::StoredObject(StoredObject&& init) noexcept StoredObject::StoredObject(StoredObject&& init) noexcept
: data_(std::move(init.data_)) {} : data_(std::move(init.data_)) {}
StoredObject::operator bool() const noexcept {
return (bool)data_;
}
StoredObject::StoredObject(SharedTable* table) noexcept StoredObject::StoredObject(SharedTable* table) noexcept
: data_(new PrimitiveHolder<SharedTable*>(table)) { : data_(new PrimitiveHolder<SharedTable*>(table)) {
} }
StoredObject::StoredObject(sol::object object) noexcept
: data_(fromSolObject(object)) {
}
StoredObject::StoredObject(sol::stack_object object) noexcept
: data_(fromSolObject(object)) {
}
StoredObject::operator bool() const noexcept {
return (bool)data_;
}
std::size_t StoredObject::hash() const noexcept { std::size_t StoredObject::hash() const noexcept {
if (data_) if (data_)
return data_->hash(); return data_->hash();
@ -79,4 +211,4 @@ bool StoredObject::operator<(const StoredObject& o) const noexcept {
return data_.get() < o.data_.get(); return data_.get() < o.data_.get();
} }
} } // share_data

View File

@ -7,8 +7,6 @@
namespace share_data { namespace share_data {
#define ERROR std::cerr
class BaseHolder { class BaseHolder {
public: public:
BaseHolder() noexcept : type_(sol::type::nil) {} BaseHolder() noexcept : type_(sol::type::nil) {}
@ -40,60 +38,6 @@ private:
BaseHolder(BaseHolder&) = delete; BaseHolder(BaseHolder&) = delete;
}; };
template<typename StoredType>
class PrimitiveHolder : public BaseHolder {
public:
PrimitiveHolder(sol::stack_object luaObject) noexcept
: data_(luaObject.as<StoredType>()) {}
PrimitiveHolder(sol::object luaObject) noexcept
: data_(luaObject.as<StoredType>()) {}
PrimitiveHolder(const StoredType& init) noexcept
: data_(init) {}
bool rawCompare(const BaseHolder* other) const noexcept final {
assert(type_ == other->type());
return static_cast<const PrimitiveHolder<StoredType>*>(other)->data_ == data_;
}
bool rawLess(const BaseHolder* other) const noexcept final {
assert(type_ == other->type());
return data_ < static_cast<const PrimitiveHolder<StoredType>*>(other)->data_;
}
std::size_t hash() const noexcept final {
return std::hash<StoredType>()(data_);
}
sol::object unpack(sol::this_state state) const noexcept final {
return sol::make_object(state, data_);
}
private:
StoredType data_;
};
class FunctionHolder : public BaseHolder {
public:
template<typename SolObject>
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;
sol::object unpack(sol::this_state state) const noexcept final;
private:
std::string function_;
};
class SharedTable; class SharedTable;
class StoredObject { class StoredObject {
@ -101,32 +45,8 @@ public:
StoredObject() = default; StoredObject() = default;
StoredObject(StoredObject&& init) noexcept; StoredObject(StoredObject&& init) noexcept;
StoredObject(SharedTable*) noexcept; StoredObject(SharedTable*) noexcept;
StoredObject(sol::object) noexcept;
template<typename SolObject> StoredObject(sol::stack_object) noexcept;
StoredObject(SolObject luaObject)
{
switch(luaObject.get_type()) {
case sol::type::nil:
break;
case sol::type::boolean:
data_.reset(new PrimitiveHolder<bool>(luaObject));
break;
case sol::type::number:
data_.reset(new PrimitiveHolder<double>(luaObject));
break;
case sol::type::string:
data_.reset(new PrimitiveHolder<std::string>(luaObject));
break;
case sol::type::userdata:
data_.reset(new PrimitiveHolder<SharedTable*>(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; operator bool() const noexcept;
std::size_t hash() const noexcept; std::size_t hash() const noexcept;
@ -147,6 +67,7 @@ private:
namespace std { namespace std {
// For storing as key in std::unordered_map
template<> template<>
struct hash<share_data::StoredObject> { struct hash<share_data::StoredObject> {
std::size_t operator()(const share_data::StoredObject &object) const noexcept { std::size_t operator()(const share_data::StoredObject &object) const noexcept {

View File

@ -2,7 +2,7 @@
namespace threading { namespace threading {
LuaThread::LuaThread(const sol::function& function, const sol::variadic_args& args) noexcept{ LuaThread::LuaThread(const sol::function& function, const sol::variadic_args& args) noexcept {
// 1. Dump function to string // 1. Dump function to string
sol::state_view lua(function.lua_state()); sol::state_view lua(function.lua_state());
str_function_ = lua["string"]["dump"](function); str_function_ = lua["string"]["dump"](function);
@ -14,31 +14,27 @@ LuaThread::LuaThread(const sol::function& function, const sol::variadic_args& ar
sol::lib::base, sol::lib::string, sol::lib::base, sol::lib::string,
sol::lib::package, sol::lib::io, sol::lib::os sol::lib::package, sol::lib::io, sol::lib::os
); );
get_user_type(*p_state_); getUserType(*p_state_);
share_data::SharedTable::get_user_type(*p_state_); share_data::SharedTable::getUserType(*p_state_);
// 3. Save parameters // 3. Save parameters
store_args(args); storeArgs(args);
// 4. Run thread // 4. Run thread
p_thread_.reset(new std::thread(&LuaThread::work, this)); p_thread_.reset(new std::thread(&LuaThread::work, this));
assert(p_thread_.get() != NULL); assert(p_thread_.get() != NULL);
} }
void LuaThread::store_args(const sol::variadic_args& args) noexcept void LuaThread::storeArgs(const sol::variadic_args &args) noexcept {
{
p_arguments_ = std::make_shared<std::vector<sol::object>>(); p_arguments_ = std::make_shared<std::vector<sol::object>>();
for(auto iter = args.begin(); iter != args.end(); iter++) for(auto iter = args.begin(); iter != args.end(); iter++) {
{
share_data::StoredObject store(iter->get<sol::object>()); share_data::StoredObject store(iter->get<sol::object>());
p_arguments_->push_back(store.unpack(sol::this_state{p_state_->lua_state()})); p_arguments_->push_back(store.unpack(sol::this_state{p_state_->lua_state()}));
} }
} }
void LuaThread::join() noexcept void LuaThread::join() noexcept {
{ if (p_thread_.get()) {
if (p_thread_.get())
{
p_thread_->join(); p_thread_->join();
p_thread_.reset(); p_thread_.reset();
} }
@ -48,44 +44,38 @@ void LuaThread::join() noexcept
p_state_.reset(); p_state_.reset();
} }
void LuaThread::detach() noexcept void LuaThread::detach() noexcept {
{
p_thread_->detach(); p_thread_->detach();
} }
void LuaThread::work() noexcept void LuaThread::work() noexcept {
{ if (p_state_.get() && p_arguments_.get()) {
if (p_state_.get() && p_arguments_.get())
{
std::string func_owner = std::move(str_function_); std::string func_owner = std::move(str_function_);
std::shared_ptr<sol::state> state_owner = p_state_; std::shared_ptr<sol::state> state_owner = p_state_;
std::shared_ptr<std::vector<sol::object>> arguments_owner = p_arguments_; std::shared_ptr<std::vector<sol::object>> arguments_owner = p_arguments_;
sol::function_result func = (*state_owner)["loadstring"](func_owner); sol::function_result func = (*state_owner)["loadstring"](func_owner);
func.get<sol::function>()(sol::as_args(*arguments_owner)); func.get<sol::function>()(sol::as_args(*arguments_owner));
} } else {
else
{
throw sol::error("Internal error: invalid thread Lua state"); throw sol::error("Internal error: invalid thread Lua state");
} }
} }
std::string LuaThread::thread_id() noexcept std::string LuaThread::threadId() noexcept {
{
std::stringstream ss; std::stringstream ss;
ss << std::this_thread::get_id(); ss << std::this_thread::get_id();
return ss.str(); return ss.str();
} }
sol::object LuaThread::get_user_type(sol::state_view& lua) noexcept sol::object LuaThread::getUserType(sol::state_view &lua) noexcept
{ {
static sol::usertype<LuaThread> type( static sol::usertype<LuaThread> type(
sol::call_construction(), sol::constructors<sol::types<sol::function, sol::variadic_args>>(), sol::call_construction(), sol::constructors<sol::types<sol::function, sol::variadic_args>>(),
"join", &LuaThread::join, "join", &LuaThread::join,
"detach", &LuaThread::detach, "detach", &LuaThread::detach,
"thread_id", &LuaThread::thread_id "thread_id", &LuaThread::threadId
); );
sol::stack::push(lua, type); sol::stack::push(lua, type);
return sol::stack::pop<sol::object>(lua); return sol::stack::pop<sol::object>(lua);
} }
} } // threading

View File

@ -10,20 +10,19 @@
namespace threading { namespace threading {
class LuaThread class LuaThread {
{
public: public:
LuaThread(const sol::function& function, const sol::variadic_args& args) noexcept; LuaThread(const sol::function& function, const sol::variadic_args& args) noexcept;
virtual ~LuaThread() noexcept = default; virtual ~LuaThread() noexcept = default;
void join() noexcept; void join() noexcept;
void detach() noexcept; void detach() noexcept;
static std::string thread_id() noexcept; static std::string threadId() noexcept;
static sol::object get_user_type(sol::state_view& lua) noexcept; static sol::object getUserType(sol::state_view &lua) noexcept;
private: private:
void work() noexcept; void work() noexcept;
void store_args(const sol::variadic_args& args) noexcept; void storeArgs(const sol::variadic_args &args) noexcept;
std::string str_function_; std::string str_function_;
std::shared_ptr<sol::state> p_state_; std::shared_ptr<sol::state> p_state_;
@ -31,4 +30,4 @@ private:
std::shared_ptr<std::vector<sol::object>> p_arguments_; std::shared_ptr<std::vector<sol::object>> p_arguments_;
}; };
} } // threading

9
src/utils.h Normal file
View File

@ -0,0 +1,9 @@
#pragma once
#include <iostream>
#ifdef NDEBUG
# define ERROR if(false) std::cerr
#else
# define ERROR std::cerr
#endif

View File

@ -2,21 +2,24 @@
#include "shared-table.h" #include "shared-table.h"
using namespace core; #include <thread>
using namespace share_data;
namespace { namespace {
void bootstrapState(sol::state& lua) { void bootstrapState(sol::state& lua) {
lua.open_libraries( lua.open_libraries(
sol::lib::base, sol::lib::base,
sol::lib::string sol::lib::string,
sol::lib::table
); );
SharedTable::bind(lua); SharedTable::getUserType(lua);
} }
} } // namespace
TEST(sharedTable, singleThreadSet) { TEST(sharedTable, primitiveTypes) {
SharedTable st; SharedTable st;
sol::state lua; sol::state lua;
bootstrapState(lua); bootstrapState(lua);
@ -30,10 +33,8 @@ st.thr = true
st.del = "secret" st.del = "secret"
st.del = nil st.del = nil
)"); )");
if (!res1.valid()) {
FAIL() << "Set res1 failed";
}
ASSERT_TRUE(res1.valid()) << "Set res1 failed";
ASSERT_EQ(lua["st"]["fst"], std::string("first")); ASSERT_EQ(lua["st"]["fst"], std::string("first"));
ASSERT_EQ(lua["st"]["snd"], (double)2); ASSERT_EQ(lua["st"]["snd"], (double)2);
ASSERT_EQ(lua["st"]["thr"], true); ASSERT_EQ(lua["st"]["thr"], true);
@ -48,10 +49,8 @@ st[42] = "answer"
st[42] = nil st[42] = nil
st.deleted = st[42] == nil st.deleted = st[42] == nil
)"); )");
if (!res2.valid()) {
FAIL() << "Set res2 failed";
}
ASSERT_TRUE(res2.valid()) << "Set res2 failed";
ASSERT_EQ(lua["st"][1], 3); ASSERT_EQ(lua["st"][1], 3);
ASSERT_EQ(lua["st"][2], std::string("number")); ASSERT_EQ(lua["st"][2], std::string("number"));
ASSERT_EQ(lua["st"][-1], false); ASSERT_EQ(lua["st"][-1], false);
@ -61,34 +60,13 @@ st.deleted = st[42] == nil
st[true] = false st[true] = false
st[false] = 9 st[false] = 9
)"); )");
if (!res3.valid()) {
FAIL() << "Set res3 failed";
}
ASSERT_TRUE(res3.valid()) << "Set res3 failed";
ASSERT_EQ(lua["st"][true], false); ASSERT_EQ(lua["st"][true], false);
ASSERT_EQ(lua["st"][false], 9); ASSERT_EQ(lua["st"][false], 9);
} }
TEST(sharedTable, asGlobalTable) { TEST(sharedTable, multipleStates) {
sol::state lua;
SharedTable st;
SharedTable::bind(lua);
lua["st"] = &st;
lua.script(R"(
_G = st
n = 1
b = false
s = "Oo"
)");
ASSERT_EQ(lua["n"], 1);
ASSERT_EQ(lua["b"], false);
ASSERT_EQ(lua["s"], std::string("Oo"));
}
TEST(sharedTable, severalStates) {
sol::state lua1, lua2; sol::state lua1, lua2;
bootstrapState(lua1); bootstrapState(lua1);
bootstrapState(lua2); bootstrapState(lua2);
@ -153,7 +131,7 @@ st.thr = true)");
ASSERT_EQ(lua["st"]["thr"], true); ASSERT_EQ(lua["st"]["thr"], true);
} }
TEST(sharedTable, nestedTables) { TEST(sharedTable, playingWithSharedTables) {
SharedTable recursive, st1, st2; SharedTable recursive, st1, st2;
sol::state lua; sol::state lua;
bootstrapState(lua); bootstrapState(lua);
@ -204,3 +182,96 @@ end
ASSERT_EQ(sf2(std::string("SUCCESS")).get<std::string>(), std::string("*SUCCESS*")); ASSERT_EQ(sf2(std::string("SUCCESS")).get<std::string>(), std::string("*SUCCESS*"));
} }
TEST(sharedTable, playingWithTables) {
SharedTable st;
sol::state lua;
bootstrapState(lua);
lua["st"] = &st;
auto res = lua.script(R"(
st.works = "fine"
st.person = {name = 'John Doe', age = 25}
pet = {
type = "cat",
name = "Tomas",
real = "Яша",
owner = "Mama",
spec = { colour = "grey", legs = 4, eyes = 2 }
}
st.pet = pet
recursive = {}
recursive.next = recursive
recursive.prev = recursive
recursive.val = "recursive"
st.recursive = recursive
)");
ASSERT_TRUE(res.valid());
ASSERT_EQ(lua["st"]["person"]["name"], std::string("John Doe"));
ASSERT_EQ(lua["st"]["person"]["age"], 25);
ASSERT_EQ(lua["st"]["pet"]["type"], std::string("cat"));
ASSERT_EQ(lua["st"]["pet"]["name"], std::string("Tomas"));
ASSERT_EQ(lua["st"]["pet"]["real"], std::string("Яша"));
ASSERT_EQ(lua["st"]["pet"]["spec"]["colour"], std::string("grey"));
ASSERT_EQ(lua["st"]["pet"]["spec"]["legs"], 4);
ASSERT_EQ(lua["st"]["recursive"]["prev"]["next"]["next"]["val"], std::string("recursive"));
defaultPool().clear();
}
TEST(sharedTable, stress) {
sol::state lua;
bootstrapState(lua);
SharedTable st;
lua["st"] = &st;
auto res1 = lua.script(R"(
for i = 1, 1000000 do
st[i] = tostring(i)
end
)");
ASSERT_TRUE(res1.valid());
ASSERT_TRUE(st.size() == 1'000'000);
auto res2 = lua.script(R"(
for i = 1000000, 1, -1 do
st[i] = nil
end
)");
ASSERT_TRUE(res2.valid());
ASSERT_TRUE(st.size() == 0);
}
TEST(sharedTable, stressWithThreads) {
SharedTable st;
const size_t threadCount = 10;
std::vector<std::thread> threads;
for(size_t i = 0; i < threadCount; i++) {
threads.emplace_back([&st, thrId(i)] {
sol::state lua;
bootstrapState(lua);
lua["st"] = &st;
std::stringstream ss;
ss << "st[" << thrId << "] = 1" << std::endl;
ss << "for i = 1, 100000 do" << std::endl;
ss << " st[" << thrId << "] = " << "st[" << thrId << "] + 1" << std::endl;
ss << "end" << std::endl;
lua.script(ss.str());
});
}
for(auto& thread : threads) {
thread.join();
}
sol::state lua;
bootstrapState(lua);
lua["st"] = &st;
for(size_t i = 0; i < threadCount; i++) {
ASSERT_TRUE(lua["st"][i] == 100'001) << (double)lua["st"][i] << std::endl;
}
}