parent
1df5e73f49
commit
0f1bef4300
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
std::string function;
|
||||
#if LUA_VERSION_NUM > 501
|
||||
unsigned char envUpvaluePos;
|
||||
#endif // LUA_VERSION_NUM > 501
|
||||
std::vector<StoredObject> upvalues;
|
||||
};
|
||||
|
||||
class Function : public GCObject<FunctionData> {
|
||||
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<StoredObject> upvalues;
|
||||
};
|
||||
|
||||
std::shared_ptr<SharedData> data_;
|
||||
explicit Function(const sol::function& luaObject);
|
||||
friend class GC;
|
||||
};
|
||||
|
||||
} // namespace effil
|
||||
|
||||
@ -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();
|
||||
}
|
||||
|
||||
@ -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
31
src/cpp/gc-data.cpp
Normal 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
31
src/cpp/gc-data.h
Normal 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
|
||||
@ -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
|
||||
@ -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
|
||||
@ -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");
|
||||
}
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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: {
|
||||
|
||||
@ -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() { }
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user