Invent Impl&View concept (#96)

Invent Impl&View concept
This commit is contained in:
Ilia 2017-10-31 11:45:34 +03:00 committed by mihacooper
parent 1df5e73f49
commit 0f1bef4300
16 changed files with 398 additions and 381 deletions

View File

@ -14,15 +14,15 @@ void Channel::exportAPI(sol::state_view& lua) {
sol::stack::pop<sol::object>(lua);
}
Channel::Channel(const sol::stack_object& capacity) : data_(std::make_shared<SharedData>()){
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<int>() >= 0) << "effil.channel: invalid capacity value = " << capacity.as<int>();
data_->capacity_ = capacity.as<size_t>();
ctx_->capacity_ = capacity.as<size_t>();
}
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<std::mutex> lock(data_->lock_);
if (data_->capacity_ && data_->channel_.size() >= data_->capacity_)
std::unique_lock<std::mutex> 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<sol::object>());
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<int>& duration,
const sol::optional<std::string>& period) {
std::unique_lock<std::mutex> lock(data_->lock_);
while (data_->channel_.empty()) {
std::unique_lock<std::mutex> 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<std::mutex> lock(data_->lock_);
return data_->channel_.size();
std::lock_guard<std::mutex> lock(ctx_->lock_);
return ctx_->channel_.size();
}
} // namespace effil

View File

@ -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 <queue>
namespace effil {
class Channel : public GCObject {
class ChannelData : public GCData {
public:
std::mutex lock_;
std::condition_variable cv_;
size_t capacity_;
std::queue<StoredArray> channel_;
};
class Channel : public GCObject<ChannelData> {
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<std::string>& period);
size_t size();
protected:
struct SharedData {
std::mutex lock_;
std::condition_variable cv_;
size_t capacity_;
std::queue<StoredArray> channel_;
};
std::shared_ptr<SharedData> data_;
public:
Channel() = delete;
private:
explicit Channel(const sol::stack_object& capacity);
friend class GC;
};
} // namespace effil

View File

@ -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<sol::object>(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<sol::object>(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);
}

View File

@ -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 <typename SolType>
FunctionObject(const SolType& luaObject)
: data_(std::make_shared<SharedData>()) {
initialize(luaObject);
}
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<StoredObject> upvalues;
};
};
std::shared_ptr<SharedData> data_;
class Function : public GCObject<FunctionData> {
public:
sol::object loadFunction(lua_State* state);
private:
explicit Function(const sol::function& luaObject);
friend class GC;
};
} // namespace effil

View File

@ -17,8 +17,8 @@ GC::GC()
void GC::collect() {
std::lock_guard<std::mutex> g(lock_);
std::unordered_set<GCObjectHandle> grey;
std::unordered_map<GCObjectHandle, std::unique_ptr<GCObject>> black;
std::unordered_set<GCHandle> grey;
std::unordered_map<GCHandle, std::unique_ptr<BaseGCObject>> 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<std::mutex> g(lock_);
return objects_.size();
}
size_t GC::count() {
size_t GC::count() const {
std::lock_guard<std::mutex> g(lock_);
return objects_.size();
}

View File

@ -15,12 +15,12 @@ public:
static sol::table exportAPI(sol::state_view& lua);
// This method is used to create all managed objects.
template <typename ObjectType, typename... Args>
ObjectType create(Args&&... args) {
template <typename ViewType, typename... Args>
ViewType create(Args&&... args) {
if (enabled_ && lastCleanup_.fetch_add(1) == step_)
collect();
auto object = std::make_unique<ObjectType>(std::forward<Args>(args)...);
std::unique_ptr<ViewType> object(new ViewType(std::forward<Args>(args)...));
auto copy = *object;
std::lock_guard<std::mutex> g(lock_);
@ -29,7 +29,7 @@ public:
}
template <typename ObjectType>
ObjectType get(GCObjectHandle handle) {
ObjectType get(GCHandle handle) {
std::lock_guard<std::mutex> g(lock_);
auto it = objects_.find(handle);
@ -45,7 +45,7 @@ private:
bool enabled_;
std::atomic<size_t> lastCleanup_;
size_t step_;
std::unordered_map<GCObjectHandle, std::unique_ptr<GCObject>> objects_;
std::unordered_map<GCHandle, std::unique_ptr<BaseGCObject>> 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

31
src/cpp/gc-data.cpp Normal file
View File

@ -0,0 +1,31 @@
#include "gc-data.h"
#include <mutex>
#include <cassert>
namespace effil {
std::unordered_set<GCHandle> GCData::refers() const {
std::lock_guard<SpinMutex> lock(mutex_);
return std::unordered_set<GCHandle>(
weakRefs_.begin(),
weakRefs_.end());
}
void GCData::addReference(GCHandle handle) {
if (handle == GCNull) return;
std::lock_guard<SpinMutex> lock(mutex_);
weakRefs_.insert(handle);
}
void GCData::removeReference(GCHandle handle) {
if (handle == GCNull) return;
std::lock_guard<SpinMutex> lock(mutex_);
auto hit = weakRefs_.find(handle);
assert(hit != std::end(weakRefs_));
weakRefs_.erase(hit);
}
} // namespace effil

31
src/cpp/gc-data.h Normal file
View File

@ -0,0 +1,31 @@
#pragma once
#include "spin-mutex.h"
#include "gc-object.h"
#include <unordered_set>
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<GCHandle> 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<GCHandle> weakRefs_;
};
} // namespace effil

View File

@ -1,42 +0,0 @@
#include "gc-object.h"
#include <mutex>
#include <cassert>
namespace effil {
GCObject::GCObject()
: data_(std::make_shared<SharedData>()) {}
GCObjectHandle GCObject::handle() const {
return reinterpret_cast<GCObjectHandle>(data_.get());
}
size_t GCObject::instances() const {
return data_.use_count();
}
const std::unordered_set<GCObjectHandle> GCObject::refers() const {
std::lock_guard<SpinMutex> lock(data_->mutex_);
return std::unordered_set<GCObjectHandle>(
data_->weakRefs_.begin(),
data_->weakRefs_.end());
}
void GCObject::addReference(GCObjectHandle handle) {
if (handle == nullptr) return;
std::lock_guard<SpinMutex> lock(data_->mutex_);
data_->weakRefs_.insert(handle);
}
void GCObject::removeReference(GCObjectHandle handle) {
if (handle == GCNull) return;
std::lock_guard<SpinMutex> lock(data_->mutex_);
auto hit = data_->weakRefs_.find(handle);
assert(hit != std::end(data_->weakRefs_));
data_->weakRefs_.erase(hit);
}
} // namespace effil

View File

@ -1,47 +1,54 @@
#pragma once
#include "spin-mutex.h"
#include <unordered_set>
#include <memory>
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<GCHandle> refers() const = 0;
};
// Unique handle for any copy of GCObject in any lua state
GCObjectHandle handle() const;
template<typename Impl>
class GCObject : public BaseGCObject {
public:
GCObject() : ctx_(std::make_shared<Impl>())
{}
// 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<GCHandle>(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<GCObjectHandle> refers() const;
std::unordered_set<GCHandle> refers() const {
return ctx_->refers();
}
protected:
void addReference(GCObjectHandle handle);
void removeReference(GCObjectHandle handle);
private:
struct SharedData {
mutable SpinMutex mutex_;
std::unordered_multiset<GCObjectHandle> weakRefs_;
};
std::shared_ptr<SharedData> data_;
std::shared_ptr<Impl> ctx_;
};
} // namespace effil

View File

@ -21,8 +21,6 @@ bool isAnyTable(const SolObject& obj) {
} // namespace
SharedTable::SharedTable() : data_(std::make_shared<SharedData>()) {}
void SharedTable::exportAPI(sol::state_view& lua) {
sol::usertype<SharedTable> 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<SpinMutex> g(data_->lock);
std::lock_guard<SpinMutex> 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<SpinMutex> g(data_->lock);
auto val = data_->entries.find(key);
if (val == data_->entries.end()) {
std::lock_guard<SpinMutex> 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<SpinMutex> g(data_->lock);
std::lock_guard<SpinMutex> 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<SpinMutex> lock(data_->lock); \
if (data_->metatable != GCNull) { \
auto tableHolder = GC::instance().get<SharedTable>(data_->metatable); \
std::unique_lock<SpinMutex> lock(ctx_->lock); \
if (ctx_->metatable != GCNull) { \
auto tableHolder = GC::instance().get<SharedTable>(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<SharedTable>();
auto data_ = table.data_;
auto ctx_ = table.ctx_;
DEFFINE_METAMETHOD_CALL(metamethodName, table, rightObject)
}
if (isSharedTable(rightObject)) {
SharedTable table = rightObject.as<SharedTable>();
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<SpinMutex> lock(data_->lock);
if (data_->metatable != GCNull) {
auto tableHolder = GC::instance().get<SharedTable>(data_->metatable);
std::unique_lock<SpinMutex> lock(ctx_->lock);
if (ctx_->metatable != GCNull) {
auto tableHolder = GC::instance().get<SharedTable>(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<SpinMutex> lock(data_->lock);
if (data_->metatable != GCNull) {
auto metatable = GC::instance().get<SharedTable>(data_->metatable);
std::unique_lock<SpinMutex> lock(ctx_->lock);
if (ctx_->metatable != GCNull) {
auto metatable = GC::instance().get<SharedTable>(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<SpinMutex> g(data_->lock);
std::lock_guard<SpinMutex> g(ctx_->lock);
size_t len = 0u;
sol::optional<LUA_INDEX_TYPE> value;
auto iter = data_->entries.find(createStoredObject(static_cast<LUA_INDEX_TYPE>(1)));
if (iter != data_->entries.end()) {
auto iter = ctx_->entries.find(createStoredObject(static_cast<LUA_INDEX_TYPE>(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<size_t>(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<SpinMutex> g(data_->lock);
std::lock_guard<SpinMutex> 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<SharedTable>(createStoredObject(tbl)->gcHandle());
std::lock_guard<SpinMutex> lock(stable.data_->lock);
if (stable.data_->metatable != GCNull) {
stable.removeReference(stable.data_->metatable);
stable.data_->metatable = GCNull;
std::lock_guard<SpinMutex> 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<SharedTable>();
std::lock_guard<SpinMutex> lock(stable.data_->lock);
return stable.data_->metatable == GCNull ? sol::nil :
sol::make_object(state, GC::instance().get<SharedTable>(stable.data_->metatable));
std::lock_guard<SpinMutex> lock(stable.ctx_->lock);
return stable.ctx_->metatable == GCNull ? sol::nil :
sol::make_object(state, GC::instance().get<SharedTable>(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<SharedTable>();
std::lock_guard<SpinMutex> g(stable.data_->lock);
return stable.data_->entries.size();
std::lock_guard<SpinMutex> g(stable.ctx_->lock);
return stable.ctx_->entries.size();
} RETHROW_WITH_PREFIX("effil.size");
}

View File

@ -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 <sol.hpp>
@ -13,15 +14,21 @@
namespace effil {
class SharedTable : public GCObject {
class SharedTableData : public GCData {
public:
using DataEntries = std::map<StoredObject, StoredObject, StoredObjectLess>;
public:
SpinMutex lock;
DataEntries entries;
GCHandle metatable = GCNull;
};
class SharedTable : public GCObject<SharedTableData> {
private:
typedef std::pair<sol::object, sol::object> PairsIterator;
typedef std::map<StoredObject, StoredObject, StoredObjectLess> 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<SharedData> data_;
SharedTable() = default;
friend class GC;
};
} // effil

View File

@ -55,7 +55,7 @@ public:
handle_ = strongRef_->handle();
}
GCObjectHolder(GCObjectHandle handle)
GCObjectHolder(GCHandle handle)
: handle_(handle) {
strongRef_ = GC::instance().get<T>(handle_);
}
@ -68,7 +68,7 @@ public:
return sol::make_object(state, GC::instance().get<T>(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<T> strongRef_;
};
class FunctionHolder : public GCObjectHolder<FunctionObject> {
class FunctionHolder : public GCObjectHolder<Function> {
public:
template <typename SolType>
FunctionHolder(const SolType& luaObject) : GCObjectHolder<FunctionObject>(luaObject) {}
FunctionHolder(GCObjectHandle handle) : GCObjectHolder(handle) {}
FunctionHolder(const SolType& luaObject) : GCObjectHolder<Function>(luaObject) {}
FunctionHolder(GCHandle handle) : GCObjectHolder(handle) {}
sol::object unpack(sol::this_state state) const final {
return GC::instance().get<FunctionObject>(handle_).loadFunction(state);
return GC::instance().get<Function>(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<std::pair<sol::object, GCObjectHandle>> SolTableToShared;
typedef std::vector<std::pair<sol::object, GCHandle>> 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<sol::table, GCObjectHandle>& element) {
auto comparator = [&luaTable](const std::pair<sol::table, GCHandle>& 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<GCObjectHolder<SharedTable>>(luaObject);
else if (luaObject.template is<Channel>())
return std::make_unique<GCObjectHolder<Channel>>(luaObject);
else if (luaObject.template is<FunctionObject>())
else if (luaObject.template is<Function>())
return std::make_unique<FunctionHolder>(luaObject);
else if (luaObject.template is<Thread>())
return std::make_unique<GCObjectHolder<Thread>>(luaObject);
else
throw Exception() << "Unable to store userdata object";
case sol::type::function: {
FunctionObject func = GC::instance().create<FunctionObject>(luaObject);
Function func = GC::instance().create<Function>(luaObject);
return std::make_unique<FunctionHolder>(func.handle());
}
case sol::type::table: {

View File

@ -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() { }

View File

@ -10,20 +10,20 @@
namespace effil {
using Status = ThreaHandle::Status;
using Command = ThreaHandle::Command;
namespace {
const sol::optional<std::chrono::milliseconds> 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<sol::state>(luaErrorHandlerPtr)) {
luaL_openlibs(*lua_);
}
Command command() const { return command_; }
void putCommand(Command cmd) {
std::unique_lock<std::mutex> lock(stateLock_);
if (isFinishStatus(status_))
return;
command_ = cmd;
statusNotifier_.reset();
commandNotifier_.notify();
}
void changeStatus(Status stat) {
std::unique_lock<std::mutex> lock(stateLock_);
status_ = stat;
commandNotifier_.reset();
statusNotifier_.notify();
if (isFinishStatus(stat))
completionNotifier_.notify();
}
template <typename T>
Status waitForStatusChange(const sol::optional<T>& time) {
if (time)
statusNotifier_.waitFor(*time);
else
statusNotifier_.wait();
return status_;
}
template <typename T>
Command waitForCommandChange(const sol::optional<T>& time) {
if (time)
commandNotifier_.waitFor(*time);
else
commandNotifier_.wait();
return command_;
}
template <typename T>
bool waitForCompletion(const sol::optional<T>& 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<sol::state> lua_;
};
namespace {
const sol::optional<std::chrono::milliseconds> 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<std::string>(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<sol::state>(luaErrorHandlerPtr)) {
luaL_openlibs(*lua_);
}
void ThreaHandle::putCommand(Command cmd) {
std::unique_lock<std::mutex> lock(stateLock_);
if (isFinishStatus(status_))
return;
command_ = cmd;
statusNotifier_.reset();
commandNotifier_.notify();
}
void ThreaHandle::changeStatus(Status stat) {
std::unique_lock<std::mutex> 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<sol::object>());
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<ThreadHandle>()) {
const sol::variadic_args& variadicArgs) {
sol::optional<FunctionObject> functionObj;
sol::optional<Function> functionObj;
try {
functionObj = FunctionObject(function);
functionObj = GC::instance().create<Function>(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<sol::object>());
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<std::chrono::milliseconds> toOptionalTime(const sol::optional<int>
StoredArray Thread::wait(const sol::this_state& lua,
const sol::optional<int>& duration,
const sol::optional<std::string>& period) {
handle_->waitForCompletion(toOptionalTime(duration, period));
ctx_->waitForCompletion(toOptionalTime(duration, period));
return status(lua);
}
StoredArray Thread::get(const sol::optional<int>& duration,
const sol::optional<std::string>& 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<int>& duration,
bool Thread::cancel(const sol::this_state&,
const sol::optional<int>& duration,
const sol::optional<std::string>& 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<int>& duration,
const sol::optional<std::string>& 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

View File

@ -1,8 +1,10 @@
#pragma once
#include <sol.hpp>
#include "lua-helpers.h"
#include "function.h"
#include "notifier.h"
#include <sol.hpp>
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 <typename T>
Status waitForStatusChange(const sol::optional<T>& time) {
if (time)
statusNotifier_.waitFor(*time);
else
statusNotifier_.wait();
return status_;
}
template <typename T>
Command waitForCommandChange(const sol::optional<T>& time) {
if (time)
commandNotifier_.waitFor(*time);
else
commandNotifier_.wait();
return command_;
}
template <typename T>
bool waitForCompletion(const sol::optional<T>& 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<sol::state> lua_;
};
class Thread : public GCObject<ThreaHandle> {
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<ThreadHandle> handle_;
private:
static void runThread(Thread, Function, effil::StoredArray);
};
} // effil