diff --git a/src/cpp/channel.cpp b/src/cpp/channel.cpp index bb25cbe..7dd458f 100644 --- a/src/cpp/channel.cpp +++ b/src/cpp/channel.cpp @@ -14,15 +14,15 @@ void Channel::exportAPI(sol::state_view& lua) { sol::stack::pop(lua); } -Channel::Channel(const sol::stack_object& capacity) : data_(std::make_shared()){ +Channel::Channel(const sol::stack_object& capacity) { if (capacity.valid()) { REQUIRE(capacity.get_type() == sol::type::number) << "bad argument #1 to 'effil.channel' (number expected, got " << luaTypename(capacity) << ")"; REQUIRE(capacity.as() >= 0) << "effil.channel: invalid capacity value = " << capacity.as(); - data_->capacity_ = capacity.as(); + ctx_->capacity_ = capacity.as(); } else { - data_->capacity_ = 0; + ctx_->capacity_ = 0; } } @@ -30,51 +30,51 @@ bool Channel::push(const sol::variadic_args& args) { if (!args.leftover_count()) return false; - std::unique_lock lock(data_->lock_); - if (data_->capacity_ && data_->channel_.size() >= data_->capacity_) + std::unique_lock lock(ctx_->lock_); + if (ctx_->capacity_ && ctx_->channel_.size() >= ctx_->capacity_) return false; - effil::StoredArray array; + StoredArray array; for (const auto& arg : args) { try { auto obj = createStoredObject(arg.get()); - addReference(obj->gcHandle()); + ctx_->addReference(obj->gcHandle()); obj->releaseStrongReference(); array.emplace_back(obj); } RETHROW_WITH_PREFIX("effil.channel:push"); } - if (data_->channel_.empty()) - data_->cv_.notify_one(); - data_->channel_.emplace(array); + if (ctx_->channel_.empty()) + ctx_->cv_.notify_one(); + ctx_->channel_.emplace(array); return true; } StoredArray Channel::pop(const sol::optional& duration, const sol::optional& period) { - std::unique_lock lock(data_->lock_); - while (data_->channel_.empty()) { + std::unique_lock lock(ctx_->lock_); + while (ctx_->channel_.empty()) { if (duration) { - if (data_->cv_.wait_for(lock, fromLuaTime(duration.value(), period)) == std::cv_status::timeout) + if (ctx_->cv_.wait_for(lock, fromLuaTime(duration.value(), period)) == std::cv_status::timeout) return StoredArray(); } else { // No time limit - data_->cv_.wait(lock); + ctx_->cv_.wait(lock); } } - auto ret = data_->channel_.front(); + auto ret = ctx_->channel_.front(); for (const auto& obj: ret) { obj->holdStrongReference(); - removeReference(obj->gcHandle()); + ctx_->removeReference(obj->gcHandle()); } - data_->channel_.pop(); + ctx_->channel_.pop(); return ret; } size_t Channel::size() { - std::lock_guard lock(data_->lock_); - return data_->channel_.size(); + std::lock_guard lock(ctx_->lock_); + return ctx_->channel_.size(); } } // namespace effil diff --git a/src/cpp/channel.h b/src/cpp/channel.h index 1cbbefc..ddc1515 100644 --- a/src/cpp/channel.h +++ b/src/cpp/channel.h @@ -1,16 +1,24 @@ #pragma once -#include "gc-object.h" #include "notifier.h" -#include "stored-object.h" #include "lua-helpers.h" -#include "queue" +#include "gc-data.h" +#include "gc-object.h" + +#include namespace effil { -class Channel : public GCObject { +class ChannelData : public GCData { +public: + std::mutex lock_; + std::condition_variable cv_; + size_t capacity_; + std::queue channel_; +}; + +class Channel : public GCObject { public: - Channel(const sol::stack_object& capacity); static void exportAPI(sol::state_view& lua); bool push(const sol::variadic_args& args); @@ -18,15 +26,13 @@ public: const sol::optional& period); size_t size(); -protected: - struct SharedData { - std::mutex lock_; - std::condition_variable cv_; - size_t capacity_; - std::queue channel_; - }; - std::shared_ptr data_; +public: + Channel() = delete; + +private: + explicit Channel(const sol::stack_object& capacity); + friend class GC; }; } // namespace effil diff --git a/src/cpp/function.cpp b/src/cpp/function.cpp index a583f3c..ee045d5 100644 --- a/src/cpp/function.cpp +++ b/src/cpp/function.cpp @@ -24,7 +24,7 @@ sol::object luaAllowTableUpvalues(sol::this_state state, const sol::stack_object } } -void FunctionObject::initialize(const sol::function& luaObject) { +Function::Function(const sol::function& luaObject) { assert(luaObject.valid()); assert(luaObject.get_type() == sol::type::function); @@ -35,10 +35,10 @@ void FunctionObject::initialize(const sol::function& luaObject) { lua_getinfo(state, ">u", &dbgInfo); // function is popped from stack here sol::stack::push(state, luaObject); - data_->function = dumpFunction(luaObject); - data_->upvalues.resize(dbgInfo.nups); + ctx_->function = dumpFunction(luaObject); + ctx_->upvalues.resize(dbgInfo.nups); #if LUA_VERSION_NUM > 501 - data_->envUpvaluePos = 0; // means no _ENV upvalue + ctx_->envUpvaluePos = 0; // means no _ENV upvalue #endif // LUA_VERSION_NUM > 501 for (unsigned char i = 1; i <= dbgInfo.nups; ++i) { @@ -49,7 +49,7 @@ void FunctionObject::initialize(const sol::function& luaObject) { #if LUA_VERSION_NUM > 501 if (strcmp(valueName, "_ENV") == 0) { // do not serialize _ENV sol::stack::pop(state); - data_->envUpvaluePos = i; + ctx_->envUpvaluePos = i; continue; } #endif // LUA_VERSION_NUM > 501 @@ -71,30 +71,30 @@ void FunctionObject::initialize(const sol::function& luaObject) { } if (storedObject->gcHandle() != nullptr) { - addReference(storedObject->gcHandle()); + ctx_->addReference(storedObject->gcHandle()); storedObject->releaseStrongReference(); } - data_->upvalues[i - 1] = std::move(storedObject); + ctx_->upvalues[i - 1] = std::move(storedObject); } sol::stack::pop(state); } -sol::object FunctionObject::loadFunction(lua_State* state) { - sol::function result = loadString(state, data_->function); +sol::object Function::loadFunction(lua_State* state) { + sol::function result = loadString(state, ctx_->function); assert(result.valid()); sol::stack::push(state, result); - for(size_t i = 0; i < data_->upvalues.size(); ++i) { + for(size_t i = 0; i < ctx_->upvalues.size(); ++i) { #if LUA_VERSION_NUM > 501 - if (data_->envUpvaluePos == i + 1) { + if (ctx_->envUpvaluePos == i + 1) { lua_rawgeti(state, LUA_REGISTRYINDEX, LUA_RIDX_GLOBALS); // push _ENV to stack lua_setupvalue(state, -2, i + 1); // pop _ENV and set as upvalue continue; } #endif // LUA_VERSION_NUM > 501 - assert(data_->upvalues[i].get() != nullptr); + assert(ctx_->upvalues[i].get() != nullptr); - const auto& obj = data_->upvalues[i]->unpack(sol::this_state{state}); + const auto& obj = ctx_->upvalues[i]->unpack(sol::this_state{state}); sol::stack::push(state, obj); lua_setupvalue(state, -2, i + 1); } diff --git a/src/cpp/function.h b/src/cpp/function.h index 4f20083..c3774fd 100644 --- a/src/cpp/function.h +++ b/src/cpp/function.h @@ -1,35 +1,30 @@ #pragma once -#include "gc-object.h" +#include "gc-data.h" #include "utils.h" #include "lua-helpers.h" +#include "gc-object.h" namespace effil { sol::object luaAllowTableUpvalues(sol::this_state state, const sol::stack_object&); -class FunctionObject: public GCObject { +class FunctionData : public GCData { public: - template - FunctionObject(const SolType& luaObject) - : data_(std::make_shared()) { - initialize(luaObject); - } + std::string function; +#if LUA_VERSION_NUM > 501 + unsigned char envUpvaluePos; +#endif // LUA_VERSION_NUM > 501 + std::vector upvalues; +}; +class Function : public GCObject { +public: sol::object loadFunction(lua_State* state); private: - void initialize(const sol::function& luaObject); - - struct SharedData { - std::string function; -#if LUA_VERSION_NUM > 501 - unsigned char envUpvaluePos; -#endif // LUA_VERSION_NUM > 501 - std::vector upvalues; - }; - - std::shared_ptr data_; + explicit Function(const sol::function& luaObject); + friend class GC; }; } // namespace effil diff --git a/src/cpp/garbage-collector.cpp b/src/cpp/garbage-collector.cpp index 6783544..51195a1 100644 --- a/src/cpp/garbage-collector.cpp +++ b/src/cpp/garbage-collector.cpp @@ -17,8 +17,8 @@ GC::GC() void GC::collect() { std::lock_guard g(lock_); - std::unordered_set grey; - std::unordered_map> black; + std::unordered_set grey; + std::unordered_map> black; for (const auto& handleAndObject : objects_) if (handleAndObject.second->instances() > 1) @@ -26,11 +26,11 @@ void GC::collect() { while (!grey.empty()) { auto it = grey.begin(); - GCObjectHandle handle = *it; + GCHandle handle = *it; grey.erase(it); black[handle] = std::move(objects_[handle]); - for (GCObjectHandle refHandle : black[handle]->refers()) { + for (GCHandle refHandle : black[handle]->refers()) { assert(objects_.count(refHandle)); if (black.count(refHandle) == 0 && grey.count(refHandle) == 0) grey.insert(refHandle); @@ -44,12 +44,7 @@ void GC::collect() { lastCleanup_.store(0); } -size_t GC::size() const { - std::lock_guard g(lock_); - return objects_.size(); -} - -size_t GC::count() { +size_t GC::count() const { std::lock_guard g(lock_); return objects_.size(); } diff --git a/src/cpp/garbage-collector.h b/src/cpp/garbage-collector.h index 3c6dc55..38f533f 100644 --- a/src/cpp/garbage-collector.h +++ b/src/cpp/garbage-collector.h @@ -15,12 +15,12 @@ public: static sol::table exportAPI(sol::state_view& lua); // This method is used to create all managed objects. - template - ObjectType create(Args&&... args) { + template + ViewType create(Args&&... args) { if (enabled_ && lastCleanup_.fetch_add(1) == step_) collect(); - auto object = std::make_unique(std::forward(args)...); + std::unique_ptr object(new ViewType(std::forward(args)...)); auto copy = *object; std::lock_guard g(lock_); @@ -29,7 +29,7 @@ public: } template - ObjectType get(GCObjectHandle handle) { + ObjectType get(GCHandle handle) { std::lock_guard g(lock_); auto it = objects_.find(handle); @@ -45,7 +45,7 @@ private: bool enabled_; std::atomic lastCleanup_; size_t step_; - std::unordered_map> objects_; + std::unordered_map> objects_; private: GC(); @@ -53,13 +53,12 @@ private: GC(const GC&) = delete; void collect(); - size_t size() const; void pause() { enabled_ = false; } void resume() { enabled_ = true; } size_t step() const { return step_; } void step(size_t newStep) { step_ = newStep; } bool enabled() { return enabled_; } - size_t count(); + size_t count() const; }; } // effil diff --git a/src/cpp/gc-data.cpp b/src/cpp/gc-data.cpp new file mode 100644 index 0000000..073ca53 --- /dev/null +++ b/src/cpp/gc-data.cpp @@ -0,0 +1,31 @@ +#include "gc-data.h" + +#include +#include + +namespace effil { + +std::unordered_set GCData::refers() const { + std::lock_guard lock(mutex_); + return std::unordered_set( + weakRefs_.begin(), + weakRefs_.end()); +} + +void GCData::addReference(GCHandle handle) { + if (handle == GCNull) return; + + std::lock_guard lock(mutex_); + weakRefs_.insert(handle); +} + +void GCData::removeReference(GCHandle handle) { + if (handle == GCNull) return; + + std::lock_guard lock(mutex_); + auto hit = weakRefs_.find(handle); + assert(hit != std::end(weakRefs_)); + weakRefs_.erase(hit); +} + +} // namespace effil \ No newline at end of file diff --git a/src/cpp/gc-data.h b/src/cpp/gc-data.h new file mode 100644 index 0000000..4137a79 --- /dev/null +++ b/src/cpp/gc-data.h @@ -0,0 +1,31 @@ +#pragma once + +#include "spin-mutex.h" +#include "gc-object.h" + +#include + +namespace effil { + +// Base class for data represented in Lua. +// Derived classes always managed by corresponding views. +class GCData { +public: + GCData() = default; + virtual ~GCData() = default; + + // List of weak references to nested objects + std::unordered_set refers() const; + void addReference(GCHandle handle); + void removeReference(GCHandle handle); + +public: + GCData(const GCData&) = delete; + GCData& operator=(const GCData&) = delete; + +private: + mutable SpinMutex mutex_; + std::unordered_multiset weakRefs_; +}; + +} // namespace effil \ No newline at end of file diff --git a/src/cpp/gc-object.cpp b/src/cpp/gc-object.cpp deleted file mode 100644 index 910524e..0000000 --- a/src/cpp/gc-object.cpp +++ /dev/null @@ -1,42 +0,0 @@ -#include "gc-object.h" - -#include -#include - -namespace effil { - -GCObject::GCObject() - : data_(std::make_shared()) {} - -GCObjectHandle GCObject::handle() const { - return reinterpret_cast(data_.get()); -} - -size_t GCObject::instances() const { - return data_.use_count(); -} - -const std::unordered_set GCObject::refers() const { - std::lock_guard lock(data_->mutex_); - return std::unordered_set( - data_->weakRefs_.begin(), - data_->weakRefs_.end()); -} - -void GCObject::addReference(GCObjectHandle handle) { - if (handle == nullptr) return; - - std::lock_guard lock(data_->mutex_); - data_->weakRefs_.insert(handle); -} - -void GCObject::removeReference(GCObjectHandle handle) { - if (handle == GCNull) return; - - std::lock_guard lock(data_->mutex_); - auto hit = data_->weakRefs_.find(handle); - assert(hit != std::end(data_->weakRefs_)); - data_->weakRefs_.erase(hit); -} - -} // namespace effil \ No newline at end of file diff --git a/src/cpp/gc-object.h b/src/cpp/gc-object.h index 3943404..530f40d 100644 --- a/src/cpp/gc-object.h +++ b/src/cpp/gc-object.h @@ -1,47 +1,54 @@ #pragma once -#include "spin-mutex.h" - #include +#include namespace effil { // Unique handle for all objects spawned from one object. -using GCObjectHandle = void*; +using GCHandle = void*; -static const GCObjectHandle GCNull = nullptr; +// Mock handle for non gc objects +static const GCHandle GCNull = nullptr; -// All effil objects that owned in lua code have to inherit this class. -// This type o object can persist in multiple threads and in multiple lua states. -// Childes have to care about storing data, concurrent access and -// weak references (GCHandle) to other GCObjects. -class GCObject { +// GCObject interface represents beheiviour of object. +// Multiple views may hold shred instance of Impl. +class BaseGCObject { public: - GCObject(); - virtual ~GCObject() = default; + virtual ~BaseGCObject() = default; + virtual GCHandle handle() = 0; + virtual size_t instances() const = 0; + virtual std::unordered_set refers() const = 0; +}; - // Unique handle for any copy of GCObject in any lua state - GCObjectHandle handle() const; +template +class GCObject : public BaseGCObject { +public: + GCObject() : ctx_(std::make_shared()) + {} - // Number of instance copies + // All views are copy constructable + GCObject(const GCObject&) = default; + GCObject& operator=(const GCObject&) = default; + + // Unique handle for any copy of GCData in any lua state + GCHandle handle() final { + return reinterpret_cast(ctx_.get()); + } + + // Number of instances // always greater than 1 // GC holds one copy - size_t instances() const; + size_t instances() const final { + return ctx_.use_count(); + } - // List of weak references to nested objects - const std::unordered_set refers() const; + std::unordered_set refers() const { + return ctx_->refers(); + } protected: - void addReference(GCObjectHandle handle); - void removeReference(GCObjectHandle handle); - -private: - struct SharedData { - mutable SpinMutex mutex_; - std::unordered_multiset weakRefs_; - }; - - std::shared_ptr data_; + std::shared_ptr ctx_; }; } // namespace effil \ No newline at end of file diff --git a/src/cpp/shared-table.cpp b/src/cpp/shared-table.cpp index 32d7950..7edcad3 100644 --- a/src/cpp/shared-table.cpp +++ b/src/cpp/shared-table.cpp @@ -21,8 +21,6 @@ bool isAnyTable(const SolObject& obj) { } // namespace -SharedTable::SharedTable() : data_(std::make_shared()) {} - void SharedTable::exportAPI(sol::state_view& lua) { sol::usertype type("new", sol::no_constructor, "__pairs", &SharedTable::luaPairs, @@ -49,21 +47,21 @@ void SharedTable::exportAPI(sol::state_view& lua) { } void SharedTable::set(StoredObject&& key, StoredObject&& value) { - std::lock_guard g(data_->lock); + std::lock_guard g(ctx_->lock); - addReference(key->gcHandle()); - addReference(value->gcHandle()); + ctx_->addReference(key->gcHandle()); + ctx_->addReference(value->gcHandle()); key->releaseStrongReference(); value->releaseStrongReference(); - data_->entries[std::move(key)] = std::move(value); + ctx_->entries[std::move(key)] = std::move(value); } sol::object SharedTable::get(const StoredObject& key, sol::this_state state) const { - std::lock_guard g(data_->lock); - auto val = data_->entries.find(key); - if (val == data_->entries.end()) { + std::lock_guard g(ctx_->lock); + auto val = ctx_->entries.find(key); + if (val == ctx_->entries.end()) { return sol::nil; } else { return val->second->unpack(state); @@ -75,14 +73,14 @@ void SharedTable::rawSet(const sol::stack_object& luaKey, const sol::stack_objec StoredObject key = createStoredObject(luaKey); if (luaValue.get_type() == sol::type::nil) { - std::lock_guard g(data_->lock); + std::lock_guard g(ctx_->lock); // in this case object is not obligatory to own data - auto it = data_->entries.find(key); - if (it != data_->entries.end()) { - removeReference(it->first->gcHandle()); - removeReference(it->second->gcHandle()); - data_->entries.erase(it); + auto it = ctx_->entries.find(key); + if (it != ctx_->entries.end()) { + ctx_->removeReference(it->first->gcHandle()); + ctx_->removeReference(it->second->gcHandle()); + ctx_->entries.erase(it); } } else { @@ -102,9 +100,9 @@ sol::object SharedTable::rawGet(const sol::stack_object& luaKey, sol::this_state #define DEFFINE_METAMETHOD_CALL_0(methodName) DEFFINE_METAMETHOD_CALL(methodName, *this) #define DEFFINE_METAMETHOD_CALL(methodName, ...) \ { \ - std::unique_lock lock(data_->lock); \ - if (data_->metatable != GCNull) { \ - auto tableHolder = GC::instance().get(data_->metatable); \ + std::unique_lock lock(ctx_->lock); \ + if (ctx_->metatable != GCNull) { \ + auto tableHolder = GC::instance().get(ctx_->metatable); \ lock.unlock(); \ sol::function handler = tableHolder.get(createStoredObject(methodName), state); \ if (handler.valid()) { \ @@ -129,12 +127,12 @@ sol::object SharedTable::basicMetaMethod(const std::string& metamethodName, cons sol::this_state state, const sol::stack_object& leftObject, const sol::stack_object& rightObject) { if (isSharedTable(leftObject)) { SharedTable table = leftObject.as(); - auto data_ = table.data_; + auto ctx_ = table.ctx_; DEFFINE_METAMETHOD_CALL(metamethodName, table, rightObject) } if (isSharedTable(rightObject)) { SharedTable table = rightObject.as(); - auto data_ = table.data_; + auto ctx_ = table.ctx_; DEFFINE_METAMETHOD_CALL(metamethodName, leftObject, table) } throw Exception() << errMsg; @@ -158,9 +156,9 @@ sol::object SharedTable::luaUnm(sol::this_state state) { void SharedTable::luaNewIndex(const sol::stack_object& luaKey, const sol::stack_object& luaValue, sol::this_state state) { { - std::unique_lock lock(data_->lock); - if (data_->metatable != GCNull) { - auto tableHolder = GC::instance().get(data_->metatable); + std::unique_lock lock(ctx_->lock); + if (ctx_->metatable != GCNull) { + auto tableHolder = GC::instance().get(ctx_->metatable); lock.unlock(); sol::function handler = tableHolder.get(createStoredObject("__newindex"), state); if (handler.valid()) { @@ -182,9 +180,9 @@ sol::object SharedTable::luaIndex(const sol::stack_object& luaKey, sol::this_sta } StoredArray SharedTable::luaCall(sol::this_state state, const sol::variadic_args& args) { - std::unique_lock lock(data_->lock); - if (data_->metatable != GCNull) { - auto metatable = GC::instance().get(data_->metatable); + std::unique_lock lock(ctx_->lock); + if (ctx_->metatable != GCNull) { + auto metatable = GC::instance().get(ctx_->metatable); sol::function handler = metatable.get(createStoredObject(std::string("__call")), state); lock.unlock(); if (handler.valid()) { @@ -204,36 +202,36 @@ StoredArray SharedTable::luaCall(sol::this_state state, const sol::variadic_args sol::object SharedTable::luaToString(sol::this_state state) { DEFFINE_METAMETHOD_CALL_0("__tostring"); std::stringstream ss; - ss << "effil.table: " << data_.get(); + ss << "effil.table: " << ctx_.get(); return sol::make_object(state, ss.str()); } sol::object SharedTable::luaLength(sol::this_state state) { DEFFINE_METAMETHOD_CALL_0("__len"); - std::lock_guard g(data_->lock); + std::lock_guard g(ctx_->lock); size_t len = 0u; sol::optional value; - auto iter = data_->entries.find(createStoredObject(static_cast(1))); - if (iter != data_->entries.end()) { + auto iter = ctx_->entries.find(createStoredObject(static_cast(1))); + if (iter != ctx_->entries.end()) { do { ++len; ++iter; - } while ((iter != data_->entries.end()) && (value = storedObjectToIndexType(iter->first)) && + } while ((iter != ctx_->entries.end()) && (value = storedObjectToIndexType(iter->first)) && (static_cast(value.value()) == len + 1)); } return sol::make_object(state, len); } SharedTable::PairsIterator SharedTable::getNext(const sol::object& key, sol::this_state lua) { - std::lock_guard g(data_->lock); + std::lock_guard g(ctx_->lock); if (key) { auto obj = createStoredObject(key); - auto upper = data_->entries.upper_bound(obj); - if (upper != data_->entries.end()) + auto upper = ctx_->entries.upper_bound(obj); + if (upper != ctx_->entries.end()) return PairsIterator(upper->first->unpack(lua), upper->second->unpack(lua)); } else { - if (!data_->entries.empty()) { - const auto& begin = data_->entries.begin(); + if (!ctx_->entries.empty()) { + const auto& begin = ctx_->entries.begin(); return PairsIterator(begin->first->unpack(lua), begin->second->unpack(lua)); } } @@ -273,14 +271,14 @@ SharedTable SharedTable::luaSetMetatable(const sol::stack_object& tbl, const sol SharedTable stable = GC::instance().get(createStoredObject(tbl)->gcHandle()); - std::lock_guard lock(stable.data_->lock); - if (stable.data_->metatable != GCNull) { - stable.removeReference(stable.data_->metatable); - stable.data_->metatable = GCNull; + std::lock_guard lock(stable.ctx_->lock); + if (stable.ctx_->metatable != GCNull) { + stable.ctx_->removeReference(stable.ctx_->metatable); + stable.ctx_->metatable = GCNull; } - stable.data_->metatable = createStoredObject(mt)->gcHandle(); - stable.addReference(stable.data_->metatable); + stable.ctx_->metatable = createStoredObject(mt)->gcHandle(); + stable.ctx_->addReference(stable.ctx_->metatable); return stable; } @@ -289,9 +287,9 @@ sol::object SharedTable::luaGetMetatable(const sol::stack_object& tbl, sol::this REQUIRE(isSharedTable(tbl)) << "bad argument #1 to 'effil.getmetatable' (effil.table expected, got " << luaTypename(tbl) << ")"; auto& stable = tbl.as(); - std::lock_guard lock(stable.data_->lock); - return stable.data_->metatable == GCNull ? sol::nil : - sol::make_object(state, GC::instance().get(stable.data_->metatable)); + std::lock_guard lock(stable.ctx_->lock); + return stable.ctx_->metatable == GCNull ? sol::nil : + sol::make_object(state, GC::instance().get(stable.ctx_->metatable)); } sol::object SharedTable::luaRawGet(const sol::stack_object& tbl, const sol::stack_object& key, sol::this_state state) { @@ -314,8 +312,8 @@ size_t SharedTable::luaSize(const sol::stack_object& tbl) { REQUIRE(isSharedTable(tbl)) << "bad argument #1 to 'effil.size' (effil.table expected, got " << luaTypename(tbl) << ")"; try { auto& stable = tbl.as(); - std::lock_guard g(stable.data_->lock); - return stable.data_->entries.size(); + std::lock_guard g(stable.ctx_->lock); + return stable.ctx_->entries.size(); } RETHROW_WITH_PREFIX("effil.size"); } diff --git a/src/cpp/shared-table.h b/src/cpp/shared-table.h index 889136d..416a282 100644 --- a/src/cpp/shared-table.h +++ b/src/cpp/shared-table.h @@ -1,10 +1,11 @@ #pragma once -#include "gc-object.h" +#include "gc-data.h" #include "stored-object.h" #include "spin-mutex.h" #include "utils.h" #include "lua-helpers.h" +#include "gc-object.h" #include @@ -13,15 +14,21 @@ namespace effil { -class SharedTable : public GCObject { + +class SharedTableData : public GCData { +public: + using DataEntries = std::map; +public: + SpinMutex lock; + DataEntries entries; + GCHandle metatable = GCNull; +}; + +class SharedTable : public GCObject { private: typedef std::pair PairsIterator; - typedef std::map DataEntries; public: - SharedTable(); - SharedTable& operator=(const SharedTable&) = default; - static void exportAPI(sol::state_view& lua); void set(StoredObject&&, StoredObject&&); @@ -64,15 +71,8 @@ private: PairsIterator getNext(const sol::object& key, sol::this_state lua); private: - struct SharedData { - SpinMutex lock; - DataEntries entries; - GCObjectHandle metatable; - - SharedData() : metatable(GCNull) {} - }; - - std::shared_ptr data_; + SharedTable() = default; + friend class GC; }; } // effil diff --git a/src/cpp/stored-object.cpp b/src/cpp/stored-object.cpp index 3a6810f..8bb415a 100644 --- a/src/cpp/stored-object.cpp +++ b/src/cpp/stored-object.cpp @@ -55,7 +55,7 @@ public: handle_ = strongRef_->handle(); } - GCObjectHolder(GCObjectHandle handle) + GCObjectHolder(GCHandle handle) : handle_(handle) { strongRef_ = GC::instance().get(handle_); } @@ -68,7 +68,7 @@ public: return sol::make_object(state, GC::instance().get(handle_)); } - GCObjectHandle gcHandle() const override { return handle_; } + GCHandle gcHandle() const override { return handle_; } void releaseStrongReference() override { strongRef_ = sol::nullopt; @@ -81,32 +81,32 @@ public: } protected: - GCObjectHandle handle_; + GCHandle handle_; sol::optional strongRef_; }; -class FunctionHolder : public GCObjectHolder { +class FunctionHolder : public GCObjectHolder { public: template - FunctionHolder(const SolType& luaObject) : GCObjectHolder(luaObject) {} - FunctionHolder(GCObjectHandle handle) : GCObjectHolder(handle) {} + FunctionHolder(const SolType& luaObject) : GCObjectHolder(luaObject) {} + FunctionHolder(GCHandle handle) : GCObjectHolder(handle) {} sol::object unpack(sol::this_state state) const final { - return GC::instance().get(handle_).loadFunction(state); + return GC::instance().get(handle_).loadFunction(state); } }; // 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> SolTableToShared; +typedef std::vector> SolTableToShared; void dumpTable(SharedTable* target, sol::table luaTable, SolTableToShared& visited); StoredObject makeStoredObject(sol::object luaObject, SolTableToShared& visited) { if (luaObject.get_type() == sol::type::table) { sol::table luaTable = luaObject; - auto comparator = [&luaTable](const std::pair& element) { + auto comparator = [&luaTable](const std::pair& element) { return element.first == luaTable; }; auto st = std::find_if(visited.begin(), visited.end(), comparator); @@ -159,14 +159,14 @@ StoredObject fromSolObject(const SolObject& luaObject) { return std::make_unique>(luaObject); else if (luaObject.template is()) return std::make_unique>(luaObject); - else if (luaObject.template is()) + else if (luaObject.template is()) return std::make_unique(luaObject); else if (luaObject.template is()) return std::make_unique>(luaObject); else throw Exception() << "Unable to store userdata object"; case sol::type::function: { - FunctionObject func = GC::instance().create(luaObject); + Function func = GC::instance().create(luaObject); return std::make_unique(func.handle()); } case sol::type::table: { diff --git a/src/cpp/stored-object.h b/src/cpp/stored-object.h index ef21fbd..da0f138 100644 --- a/src/cpp/stored-object.h +++ b/src/cpp/stored-object.h @@ -22,7 +22,7 @@ public: virtual bool rawCompare(const BaseHolder* other) const = 0; virtual const std::type_info& type() { return typeid(*this); } virtual sol::object unpack(sol::this_state state) const = 0; - virtual GCObjectHandle gcHandle() const { return GCNull; } + virtual GCHandle gcHandle() const { return GCNull; } virtual void releaseStrongReference() { } virtual void holdStrongReference() { } diff --git a/src/cpp/threading.cpp b/src/cpp/threading.cpp index 367a023..1d292a3 100644 --- a/src/cpp/threading.cpp +++ b/src/cpp/threading.cpp @@ -10,20 +10,20 @@ namespace effil { +using Status = ThreaHandle::Status; +using Command = ThreaHandle::Command; + namespace { +const sol::optional NO_TIMEOUT; + +// Thread specific pointer to current thread +static thread_local ThreaHandle* thisThreadHandle = nullptr; + // Doesn't inherit std::exception // to prevent from catching this exception third party lua C++ libs class LuaHookStopException {}; -enum class Status { - Running, - Paused, - Canceled, - Completed, - Failed -}; - bool isFinishStatus(Status stat) { return stat == Status::Canceled || stat == Status::Completed || stat == Status::Failed; } @@ -45,114 +45,6 @@ std::string statusToString(Status status) { return "unknown"; } -enum class Command { - Run, - Cancel, - Pause -}; - -#if LUA_VERSION_NUM > 501 - -int luaErrorHandler(lua_State* state); -const lua_CFunction luaErrorHandlerPtr = luaErrorHandler; - -#else - -const lua_CFunction luaErrorHandlerPtr = nullptr; - -#endif // LUA_VERSION_NUM > 501 - -} // namespace - - -class ThreadHandle { -public: - ThreadHandle() - : status_(Status::Running) - , command_(Command::Run) - , lua_(std::make_unique(luaErrorHandlerPtr)) { - luaL_openlibs(*lua_); - } - - Command command() const { return command_; } - - void putCommand(Command cmd) { - std::unique_lock lock(stateLock_); - if (isFinishStatus(status_)) - return; - - command_ = cmd; - statusNotifier_.reset(); - commandNotifier_.notify(); - } - - void changeStatus(Status stat) { - std::unique_lock lock(stateLock_); - status_ = stat; - commandNotifier_.reset(); - statusNotifier_.notify(); - if (isFinishStatus(stat)) - completionNotifier_.notify(); - } - - template - Status waitForStatusChange(const sol::optional& time) { - if (time) - statusNotifier_.waitFor(*time); - else - statusNotifier_.wait(); - return status_; - } - - template - Command waitForCommandChange(const sol::optional& time) { - if (time) - commandNotifier_.waitFor(*time); - else - commandNotifier_.wait(); - return command_; - } - - template - bool waitForCompletion(const sol::optional& time) { - if (time) { - return completionNotifier_.waitFor(*time); - } - else { - completionNotifier_.wait(); - return true; - } - } - - sol::state& lua() { - assert(lua_); - return *lua_; - } - - void destroyLua() { lua_.reset(); } - - Status status() { return status_; } - - StoredArray& result() { return result_; } - -private: - Status status_; - Command command_; - Notifier statusNotifier_; - Notifier commandNotifier_; - Notifier completionNotifier_; - std::mutex stateLock_; - StoredArray result_; - - std::unique_ptr lua_; -}; - -namespace { - -const sol::optional NO_TIMEOUT; - -static thread_local ThreadHandle* thisThreadHandle = nullptr; - #if LUA_VERSION_NUM > 501 int luaErrorHandler(lua_State* state) { @@ -162,6 +54,12 @@ int luaErrorHandler(lua_State* state) { throw Exception() << sol::stack::pop(state); } +const lua_CFunction luaErrorHandlerPtr = luaErrorHandler; + +#else + +const lua_CFunction luaErrorHandlerPtr = nullptr; + #endif // LUA_VERSION_NUM > 501 void luaHook(lua_State*, lua_Debug*) { @@ -188,10 +86,36 @@ void luaHook(lua_State*, lua_Debug*) { } // namespace +ThreaHandle::ThreaHandle() + : status_(Status::Running) + , command_(Command::Run) + , lua_(std::make_unique(luaErrorHandlerPtr)) { + luaL_openlibs(*lua_); +} + +void ThreaHandle::putCommand(Command cmd) { + std::unique_lock lock(stateLock_); + if (isFinishStatus(status_)) + return; + + command_ = cmd; + statusNotifier_.reset(); + commandNotifier_.notify(); +} + +void ThreaHandle::changeStatus(Status stat) { + std::unique_lock lock(stateLock_); + status_ = stat; + commandNotifier_.reset(); + statusNotifier_.notify(); + if (isFinishStatus(stat)) + completionNotifier_.notify(); +} + void Thread::runThread(Thread thread, - FunctionObject function, + Function function, effil::StoredArray arguments) { - thisThreadHandle = thread.handle_.get(); + thisThreadHandle = thread.ctx_.get(); assert(thisThreadHandle != nullptr); try { @@ -200,32 +124,32 @@ void Thread::runThread(Thread thread, // Let's destroy accociated state // to release all resources as soon as possible arguments.clear(); - thread.handle_->destroyLua(); + thread.ctx_->destroyLua(); }); - sol::function userFuncObj = function.loadFunction(thread.handle_->lua()); + 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::variadic_args args(thread.handle_->lua(), -lua_gettop(thread.handle_->lua())); + sol::variadic_args args(thread.ctx_->lua(), -lua_gettop(thread.ctx_->lua())); for (const auto& iter : args) { StoredObject store = createStoredObject(iter.get()); if (store->gcHandle() != nullptr) { - thread.addReference(store->gcHandle()); + thread.ctx_->addReference(store->gcHandle()); store->releaseStrongReference(); } - thread.handle_->result().emplace_back(std::move(store)); + thread.ctx_->result().emplace_back(std::move(store)); } } - thread.handle_->changeStatus(Status::Completed); + thread.ctx_->changeStatus(Status::Completed); } catch (const LuaHookStopException&) { - thread.handle_->changeStatus(Status::Canceled); + thread.ctx_->changeStatus(Status::Canceled); } catch (const sol::error& err) { DEBUG << "Failed with msg: " << err.what() << std::endl; - auto& returns = thread.handle_->result(); + auto& returns = thread.ctx_->result(); returns.insert(returns.begin(), { createStoredObject("failed"), createStoredObject(err.what()) }); - thread.handle_->changeStatus(Status::Failed); + thread.ctx_->changeStatus(Status::Failed); } } @@ -259,26 +183,25 @@ Thread::Thread(const std::string& path, const std::string& cpath, int step, const sol::function& function, - const sol::variadic_args& variadicArgs) - : handle_(std::make_shared()) { + const sol::variadic_args& variadicArgs) { - sol::optional functionObj; + sol::optional functionObj; try { - functionObj = FunctionObject(function); + functionObj = GC::instance().create(function); } RETHROW_WITH_PREFIX("effil.thread"); - handle_->lua()["package"]["path"] = path; - handle_->lua()["package"]["cpath"] = cpath; - handle_->lua().script("require 'effil'"); + ctx_->lua()["package"]["path"] = path; + ctx_->lua()["package"]["cpath"] = cpath; + ctx_->lua().script("require 'effil'"); if (step != 0) - lua_sethook(handle_->lua(), luaHook, LUA_MASKCOUNT, step); + lua_sethook(ctx_->lua(), luaHook, LUA_MASKCOUNT, step); effil::StoredArray arguments; try { for (const auto& arg : variadicArgs) { const auto& storedObj = createStoredObject(arg.get()); - addReference(storedObj->gcHandle()); + ctx_->addReference(storedObj->gcHandle()); storedObj->releaseStrongReference(); arguments.emplace_back(storedObj); } @@ -306,10 +229,10 @@ void Thread::exportAPI(sol::state_view& lua) { } StoredArray Thread::status(const sol::this_state& lua) { - const auto stat = handle_->status(); + const auto stat = ctx_->status(); if (stat == Status::Failed) { - assert(!handle_->result().empty()); - return handle_->result(); + assert(!ctx_->result().empty()); + return ctx_->result(); } else { const sol::object luaStatus = sol::make_object(lua, statusToString(stat)); return StoredArray({createStoredObject(luaStatus)}); @@ -327,14 +250,14 @@ sol::optional toOptionalTime(const sol::optional StoredArray Thread::wait(const sol::this_state& lua, const sol::optional& duration, const sol::optional& period) { - handle_->waitForCompletion(toOptionalTime(duration, period)); + ctx_->waitForCompletion(toOptionalTime(duration, period)); return status(lua); } StoredArray Thread::get(const sol::optional& duration, const sol::optional& period) { - if (handle_->waitForCompletion(toOptionalTime(duration, period)) && handle_->status() == Status::Completed) - return handle_->result(); + if (ctx_->waitForCompletion(toOptionalTime(duration, period)) && ctx_->status() == Status::Completed) + return ctx_->result(); else return StoredArray(); } @@ -342,21 +265,21 @@ StoredArray Thread::get(const sol::optional& duration, bool Thread::cancel(const sol::this_state&, const sol::optional& duration, const sol::optional& period) { - handle_->putCommand(Command::Cancel); - Status status = handle_->waitForStatusChange(toOptionalTime(duration, period)); + ctx_->putCommand(Command::Cancel); + Status status = ctx_->waitForStatusChange(toOptionalTime(duration, period)); return isFinishStatus(status); } bool Thread::pause(const sol::this_state&, const sol::optional& duration, const sol::optional& period) { - handle_->putCommand(Command::Pause); - Status status = handle_->waitForStatusChange(toOptionalTime(duration, period)); + ctx_->putCommand(Command::Pause); + Status status = ctx_->waitForStatusChange(toOptionalTime(duration, period)); return status == Status::Paused; } void Thread::resume() { - handle_->putCommand(Command::Run); + ctx_->putCommand(Command::Run); } } // effil diff --git a/src/cpp/threading.h b/src/cpp/threading.h index f221c48..b1fee59 100644 --- a/src/cpp/threading.h +++ b/src/cpp/threading.h @@ -1,8 +1,10 @@ #pragma once -#include #include "lua-helpers.h" #include "function.h" +#include "notifier.h" + +#include namespace effil { @@ -11,16 +13,82 @@ std::string threadId(); void yield(); void sleep(const sol::stack_object& duration, const sol::stack_object& metric); -class ThreadHandle; - -class Thread : public GCObject { +class ThreaHandle : public GCData { public: - Thread(const std::string& path, - const std::string& cpath, - int step, - const sol::function& function, - const sol::variadic_args& args); + enum class Status { + Running, + Paused, + Canceled, + Completed, + Failed + }; + enum class Command { + Run, + Cancel, + Pause + }; + +public: + ThreaHandle(); + Command command() const { return command_; } + void putCommand(Command cmd); + void changeStatus(Status stat); + + template + Status waitForStatusChange(const sol::optional& time) { + if (time) + statusNotifier_.waitFor(*time); + else + statusNotifier_.wait(); + return status_; + } + + template + Command waitForCommandChange(const sol::optional& time) { + if (time) + commandNotifier_.waitFor(*time); + else + commandNotifier_.wait(); + return command_; + } + + template + bool waitForCompletion(const sol::optional& time) { + if (time) { + return completionNotifier_.waitFor(*time); + } + else { + completionNotifier_.wait(); + return true; + } + } + + sol::state& lua() { + assert(lua_); + return *lua_; + } + + void destroyLua() { lua_.reset(); } + + Status status() { return status_; } + + StoredArray& result() { return result_; } + +private: + Status status_; + Command command_; + Notifier statusNotifier_; + Notifier commandNotifier_; + Notifier completionNotifier_; + std::mutex stateLock_; + StoredArray result_; + + std::unique_ptr lua_; +}; + +class Thread : public GCObject { +public: static void exportAPI(sol::state_view& lua); StoredArray status(const sol::this_state& state); @@ -38,9 +106,15 @@ public: void resume(); private: - static void runThread(Thread, FunctionObject, effil::StoredArray); + Thread(const std::string& path, + const std::string& cpath, + int step, + const sol::function& function, + const sol::variadic_args& args); + friend class GC; - std::shared_ptr handle_; +private: + static void runThread(Thread, Function, effil::StoredArray); }; } // effil