diff --git a/src/cpp/shared-table.cpp b/src/cpp/shared-table.cpp index 7edcad3..a4bc48c 100644 --- a/src/cpp/shared-table.cpp +++ b/src/cpp/shared-table.cpp @@ -2,13 +2,16 @@ #include "utils.h" -#include #include +#include namespace effil { namespace { +typedef std::unique_lock UniqueLock; +typedef std::shared_lock SharedLock; + template bool isSharedTable(const SolObject& obj) { return obj.valid() && obj.get_type() == sol::type::userdata && obj.template is(); @@ -47,7 +50,7 @@ void SharedTable::exportAPI(sol::state_view& lua) { } void SharedTable::set(StoredObject&& key, StoredObject&& value) { - std::lock_guard g(ctx_->lock); + UniqueLock g(ctx_->lock); ctx_->addReference(key->gcHandle()); ctx_->addReference(value->gcHandle()); @@ -59,7 +62,7 @@ void SharedTable::set(StoredObject&& key, StoredObject&& value) { } sol::object SharedTable::get(const StoredObject& key, sol::this_state state) const { - std::lock_guard g(ctx_->lock); + SharedLock g(ctx_->lock); auto val = ctx_->entries.find(key); if (val == ctx_->entries.end()) { return sol::nil; @@ -73,7 +76,7 @@ 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(ctx_->lock); + UniqueLock g(ctx_->lock); // in this case object is not obligatory to own data auto it = ctx_->entries.find(key); @@ -100,7 +103,7 @@ 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(ctx_->lock); \ + SharedLock lock(ctx_->lock); \ if (ctx_->metatable != GCNull) { \ auto tableHolder = GC::instance().get(ctx_->metatable); \ lock.unlock(); \ @@ -156,7 +159,7 @@ 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(ctx_->lock); + SharedLock lock(ctx_->lock); if (ctx_->metatable != GCNull) { auto tableHolder = GC::instance().get(ctx_->metatable); lock.unlock(); @@ -180,7 +183,7 @@ 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(ctx_->lock); + SharedLock lock(ctx_->lock); if (ctx_->metatable != GCNull) { auto metatable = GC::instance().get(ctx_->metatable); sol::function handler = metatable.get(createStoredObject(std::string("__call")), state); @@ -208,7 +211,7 @@ sol::object SharedTable::luaToString(sol::this_state state) { sol::object SharedTable::luaLength(sol::this_state state) { DEFFINE_METAMETHOD_CALL_0("__len"); - std::lock_guard g(ctx_->lock); + SharedLock g(ctx_->lock); size_t len = 0u; sol::optional value; auto iter = ctx_->entries.find(createStoredObject(static_cast(1))); @@ -223,7 +226,7 @@ sol::object SharedTable::luaLength(sol::this_state state) { } SharedTable::PairsIterator SharedTable::getNext(const sol::object& key, sol::this_state lua) { - std::lock_guard g(ctx_->lock); + SharedLock g(ctx_->lock); if (key) { auto obj = createStoredObject(key); auto upper = ctx_->entries.upper_bound(obj); @@ -271,7 +274,7 @@ SharedTable SharedTable::luaSetMetatable(const sol::stack_object& tbl, const sol SharedTable stable = GC::instance().get(createStoredObject(tbl)->gcHandle()); - std::lock_guard lock(stable.ctx_->lock); + UniqueLock lock(stable.ctx_->lock); if (stable.ctx_->metatable != GCNull) { stable.ctx_->removeReference(stable.ctx_->metatable); stable.ctx_->metatable = GCNull; @@ -287,7 +290,7 @@ 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.ctx_->lock); + SharedLock lock(stable.ctx_->lock); return stable.ctx_->metatable == GCNull ? sol::nil : sol::make_object(state, GC::instance().get(stable.ctx_->metatable)); } @@ -312,7 +315,7 @@ 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.ctx_->lock); + SharedLock g(stable.ctx_->lock); return stable.ctx_->entries.size(); } RETHROW_WITH_PREFIX("effil.size"); } diff --git a/src/cpp/spin-mutex.h b/src/cpp/spin-mutex.h index 36cf0fb..ef4cb3f 100644 --- a/src/cpp/spin-mutex.h +++ b/src/cpp/spin-mutex.h @@ -8,15 +8,40 @@ namespace effil { class SpinMutex { public: void lock() noexcept { - while (lock_.test_and_set(std::memory_order_acquire)) { + while (lock_.exchange(true, std::memory_order_acquire)) { + std::this_thread::yield(); + } + while (counter_ != 0) { std::this_thread::yield(); } } - void unlock() noexcept { lock_.clear(std::memory_order_release); } + void unlock() noexcept { + lock_.exchange(false, std::memory_order_release); + } + + void lock_shared() noexcept { + while (true) { + while (lock_) { + std::this_thread::yield(); + } + + counter_.fetch_add(1, std::memory_order_acquire); + + if (lock_) + counter_.fetch_sub(1, std::memory_order_release); + else + return; + } + } + + void unlock_shared() noexcept { + counter_.fetch_sub(1, std::memory_order_release); + } private: - std::atomic_flag lock_ = ATOMIC_FLAG_INIT; + std::atomic_int counter_ {0}; + std::atomic_bool lock_ {false}; }; } // effil