Robust exception handling

This commit is contained in:
Ilia Udalov 2017-02-11 15:44:17 +03:00
parent 9f5267e23b
commit 3310901ce3
9 changed files with 79 additions and 80 deletions

View File

@ -7,12 +7,12 @@
namespace effil { namespace effil {
GarbageCollector::GarbageCollector() noexcept GarbageCollector::GarbageCollector()
: state_(GCState::Idle), : state_(GCState::Idle),
lastCleanup_(0), lastCleanup_(0),
step_(200) {} step_(200) {}
GCObject* GarbageCollector::get(GCObjectHandle handle) noexcept { GCObject* GarbageCollector::get(GCObjectHandle handle) {
std::lock_guard<std::mutex> g(lock_); std::lock_guard<std::mutex> g(lock_);
auto it = objects_.find(handle); auto it = objects_.find(handle);
if (it == objects_.end()) { if (it == objects_.end()) {
@ -22,7 +22,7 @@ GCObject* GarbageCollector::get(GCObjectHandle handle) noexcept {
return it->second.get(); return it->second.get();
} }
bool GarbageCollector::has(GCObjectHandle handle) const noexcept { bool GarbageCollector::has(GCObjectHandle handle) const {
std::lock_guard<std::mutex> g(lock_); std::lock_guard<std::mutex> g(lock_);
return objects_.find(handle) != objects_.end(); return objects_.find(handle) != objects_.end();
} }
@ -63,25 +63,25 @@ void GarbageCollector::cleanup() {
lastCleanup_.store(0); lastCleanup_.store(0);
} }
size_t GarbageCollector::size() const noexcept { size_t GarbageCollector::size() const {
std::lock_guard<std::mutex> g(lock_); std::lock_guard<std::mutex> g(lock_);
return objects_.size(); return objects_.size();
} }
void GarbageCollector::stop() noexcept { void GarbageCollector::stop() {
std::lock_guard<std::mutex> g(lock_); std::lock_guard<std::mutex> g(lock_);
assert(state_ == GCState::Idle || state_ == GCState::Stopped); assert(state_ == GCState::Idle || state_ == GCState::Stopped);
state_ = GCState::Stopped; state_ = GCState::Stopped;
} }
void GarbageCollector::resume() noexcept { void GarbageCollector::resume() {
std::lock_guard<std::mutex> g(lock_); std::lock_guard<std::mutex> g(lock_);
assert(state_ == GCState::Idle || state_ == GCState::Stopped); assert(state_ == GCState::Idle || state_ == GCState::Stopped);
state_ = GCState::Idle; state_ = GCState::Idle;
} }
GarbageCollector& getGC() noexcept { GarbageCollector& getGC() {
static GarbageCollector pool; static GarbageCollector pool;
return pool; return pool;
} }

View File

@ -39,7 +39,7 @@ enum class GCState {
class GarbageCollector { class GarbageCollector {
public: public:
GarbageCollector() noexcept; GarbageCollector();
~GarbageCollector() = default; ~GarbageCollector() = default;
// This method is used to create all managed objects. // This method is used to create all managed objects.
@ -53,15 +53,15 @@ public:
return *object; return *object;
} }
GCObject* get(GCObjectHandle handle) noexcept; GCObject* get(GCObjectHandle handle);
bool has(GCObjectHandle handle) const noexcept; bool has(GCObjectHandle handle) const;
void cleanup(); void cleanup();
size_t size() const noexcept; size_t size() const;
void stop() noexcept; void stop();
void resume() noexcept; void resume();
size_t step() const noexcept { return step_; } size_t step() const { return step_; }
void step(size_t newStep) noexcept { step_ = newStep; } void step(size_t newStep) { step_ = newStep; }
GCState state() const noexcept { return state_; } GCState state() const { return state_; }
private: private:
mutable std::mutex lock_; mutable std::mutex lock_;
@ -76,6 +76,6 @@ private:
}; };
GarbageCollector& getGC() noexcept; GarbageCollector& getGC();
} // effil } // effil

View File

@ -7,17 +7,17 @@
namespace effil { namespace effil {
SharedTable::SharedTable() noexcept SharedTable::SharedTable()
: GCObject(), : GCObject(),
data_(std::make_shared<SharedData>()) { data_(std::make_shared<SharedData>()) {
} }
SharedTable::SharedTable(const SharedTable& init) noexcept SharedTable::SharedTable(const SharedTable& init)
: GCObject(init), : GCObject(init),
data_(init.data_) { data_(init.data_) {
} }
sol::object SharedTable::getUserType(sol::state_view &lua) noexcept { sol::object SharedTable::getUserType(sol::state_view &lua) {
static sol::usertype<SharedTable> type( static sol::usertype<SharedTable> type(
"new", sol::no_constructor, "new", sol::no_constructor,
sol::meta_function::new_index, &SharedTable::luaSet, sol::meta_function::new_index, &SharedTable::luaSet,
@ -28,7 +28,7 @@ sol::object SharedTable::getUserType(sol::state_view &lua) noexcept {
return sol::stack::pop<sol::object>(lua); return sol::stack::pop<sol::object>(lua);
} }
void SharedTable::set(StoredObject&& key, StoredObject&& value) noexcept { void SharedTable::set(StoredObject&& key, StoredObject&& value) {
std::lock_guard<SpinMutex> g(data_->lock); std::lock_guard<SpinMutex> g(data_->lock);
if (key->gcHandle()) refs_->insert(key->gcHandle()); if (key->gcHandle()) refs_->insert(key->gcHandle());
@ -38,7 +38,7 @@ void SharedTable::set(StoredObject&& key, StoredObject&& value) noexcept {
} }
void SharedTable::luaSet(const sol::stack_object& luaKey, const sol::stack_object& luaValue) { void SharedTable::luaSet(const sol::stack_object& luaKey, const sol::stack_object& luaValue) {
ASSERT(luaKey.valid()) << "Invalid table index"; REQUIRE(luaKey.valid()) << "Indexing by nil";
StoredObject key = createStoredObject(luaKey); StoredObject key = createStoredObject(luaKey);
if (luaValue.get_type() == sol::type::nil) { if (luaValue.get_type() == sol::type::nil) {
@ -57,12 +57,12 @@ void SharedTable::luaSet(const sol::stack_object& luaKey, const sol::stack_objec
} }
} }
sol::object SharedTable::luaGet(const sol::stack_object& key, const sol::this_state& state) const { sol::object SharedTable::luaGet(const sol::stack_object& luaKey, const sol::this_state& state) const {
ASSERT(key.valid()); REQUIRE(luaKey.valid()) << "Indexing by nil";
StoredObject cppKey = createStoredObject(key); StoredObject key = createStoredObject(luaKey);
std::lock_guard<SpinMutex> g(data_->lock); std::lock_guard<SpinMutex> g(data_->lock);
auto val = data_->entries.find(cppKey); auto val = data_->entries.find(key);
if (val == data_->entries.end()) { if (val == data_->entries.end()) {
return sol::nil; return sol::nil;
} else { } else {
@ -70,7 +70,7 @@ sol::object SharedTable::luaGet(const sol::stack_object& key, const sol::this_st
} }
} }
size_t SharedTable::size() const noexcept { size_t SharedTable::size() const {
std::lock_guard<SpinMutex> g(data_->lock); std::lock_guard<SpinMutex> g(data_->lock);
return data_->entries.size(); return data_->entries.size();
} }

View File

@ -13,18 +13,18 @@ namespace effil {
class SharedTable : public GCObject { class SharedTable : public GCObject {
public: public:
SharedTable() noexcept; SharedTable();
SharedTable(SharedTable&&) = default; SharedTable(SharedTable&&) = default;
SharedTable(const SharedTable& init) noexcept; SharedTable(const SharedTable& init);
virtual ~SharedTable() = default; virtual ~SharedTable() = default;
static sol::object getUserType(sol::state_view &lua) noexcept; static sol::object getUserType(sol::state_view &lua);
void set(StoredObject&&, StoredObject&&) noexcept; void set(StoredObject&&, StoredObject&&);
// These functions could be invoked from lua scripts // These functions could be invoked from lua scripts
void luaSet(const sol::stack_object& luaKey, const sol::stack_object& luaValue); void luaSet(const sol::stack_object& luaKey, const sol::stack_object& luaValue);
sol::object luaGet(const sol::stack_object& key, const sol::this_state& state) const; sol::object luaGet(const sol::stack_object& luaKey, const sol::this_state& state) const;
size_t size() const noexcept; size_t size() const;
private: private:
typedef std::unique_ptr<BaseHolder> StoredObject; typedef std::unique_ptr<BaseHolder> StoredObject;

View File

@ -47,7 +47,7 @@ public:
FunctionHolder(SolObject luaObject) noexcept { FunctionHolder(SolObject luaObject) noexcept {
sol::state_view lua(luaObject.lua_state()); sol::state_view lua(luaObject.lua_state());
sol::function dumper = lua["string"]["dump"]; sol::function dumper = lua["string"]["dump"];
ASSERT(dumper.valid()); if (!dumper.valid()) throw Exception() << "Invalid string.dump()";
function_ = dumper(luaObject); function_ = dumper(luaObject);
} }
@ -62,9 +62,10 @@ public:
sol::object unpack(sol::this_state state) const final { sol::object unpack(sol::this_state state) const final {
sol::state_view lua((lua_State*)state); sol::state_view lua((lua_State*)state);
sol::function loader = lua["loadstring"]; sol::function loader = lua["loadstring"];
ASSERT(loader.valid()); REQUIRE(loader.valid()) << "Invalid loadstring()";
sol::function result = loader(function_); sol::function result = loader(function_);
ASSERT(result.valid()) << "Unable to restore function!\n" << "Content:\n" << function_; // The result of restaring always is valid function.
assert(result.valid());
return sol::make_object(state, result); return sol::make_object(state, result);
} }
@ -75,28 +76,28 @@ private:
class TableHolder : public BaseHolder { class TableHolder : public BaseHolder {
public: public:
template<typename SolType> template<typename SolType>
TableHolder(const SolType& luaObject) noexcept { TableHolder(const SolType& luaObject) {
assert(luaObject.template is<SharedTable>()); assert(luaObject.template is<SharedTable>());
handle_ = luaObject.template as<SharedTable>().handle(); handle_ = luaObject.template as<SharedTable>().handle();
assert(getGC().has(handle_)); assert(getGC().has(handle_));
} }
TableHolder(GCObjectHandle handle) noexcept TableHolder(GCObjectHandle handle)
: handle_(handle) {} : handle_(handle) {}
bool rawCompare(const BaseHolder *other) const noexcept final { bool rawCompare(const BaseHolder *other) const final {
return static_cast<const TableHolder*>(other)->handle_ == handle_; return static_cast<const TableHolder*>(other)->handle_ == handle_;
} }
std::size_t hash() const noexcept final { std::size_t hash() const final {
return std::hash<GCObjectHandle>()(handle_); return std::hash<GCObjectHandle>()(handle_);
} }
sol::object unpack(sol::this_state state) const noexcept final { sol::object unpack(sol::this_state state) const final {
return sol::make_object(state, *static_cast<SharedTable*>(getGC().get(handle_))); return sol::make_object(state, *static_cast<SharedTable*>(getGC().get(handle_)));
} }
GCObjectHandle gcHandle() const noexcept override { return handle_; } GCObjectHandle gcHandle() const override { return handle_; }
private: private:
GCObjectHandle handle_; GCObjectHandle handle_;
}; };
@ -165,7 +166,7 @@ StoredObject fromSolObject(const SolObject& luaObject) {
return std::make_unique<TableHolder>(table.handle()); return std::make_unique<TableHolder>(table.handle());
} }
default: default:
ERROR << "Unable to store object of that type: " << (int)luaObject.get_type() << "\n"; throw Exception() << "Unable to store object of that type: " << (int)luaObject.get_type() << "\n";
} }
return nullptr; return nullptr;
} }

View File

@ -11,16 +11,16 @@ public:
BaseHolder() = default; BaseHolder() = default;
virtual ~BaseHolder() = default; virtual ~BaseHolder() = default;
bool compare(const BaseHolder* other) const noexcept { bool compare(const BaseHolder* other) const {
return typeid(*this) == typeid(*other) && rawCompare(other); return typeid(*this) == typeid(*other) && rawCompare(other);
} }
virtual bool rawCompare(const BaseHolder* other) const = 0; virtual bool rawCompare(const BaseHolder* other) const = 0;
virtual const std::type_info& type() { return typeid(*this); } virtual const std::type_info& type() { return typeid(*this); }
virtual std::size_t hash() const noexcept = 0; virtual std::size_t hash() const = 0;
virtual sol::object unpack(sol::this_state state) const = 0; virtual sol::object unpack(sol::this_state state) const = 0;
virtual GCObjectHandle gcHandle() const noexcept { return GCNull; } virtual GCObjectHandle gcHandle() const { return GCNull; }
private: private:
BaseHolder(const BaseHolder&) = delete; BaseHolder(const BaseHolder&) = delete;

View File

@ -7,19 +7,19 @@ namespace effil {
class LuaHookStopException : public std::exception {}; class LuaHookStopException : public std::exception {};
std::string threadId() noexcept std::string threadId()
{ {
std::stringstream ss; std::stringstream ss;
ss << std::this_thread::get_id(); ss << std::this_thread::get_id();
return ss.str(); return ss.str();
} }
void yield() noexcept void yield()
{ {
std::this_thread::yield(); std::this_thread::yield();
} }
void sleep(int64_t time, sol::optional<std::string> period) noexcept void sleep(int64_t time, sol::optional<std::string> period)
{ {
std::string metric = period ? period.value() : "s"; std::string metric = period ? period.value() : "s";
if (metric == "ms") if (metric == "ms")
@ -38,7 +38,7 @@ thread_local LuaThread::ThreadData* LuaThread::pThreadLocalData = NULL;
LuaThread::LuaThread(std::shared_ptr<ThreadData> threadData, const std::string& function, const sol::variadic_args& args) { LuaThread::LuaThread(std::shared_ptr<ThreadData> threadData, const std::string& function, const sol::variadic_args& args) {
pThreadData_ = threadData; pThreadData_ = threadData;
ASSERT(pThreadData_.get()); assert(pThreadData_);
pThreadData_->command = ThreadCommand::Nothing; pThreadData_->command = ThreadCommand::Nothing;
pThreadData_->status = ThreadStatus::Running; pThreadData_->status = ThreadStatus::Running;
@ -49,7 +49,7 @@ LuaThread::LuaThread(std::shared_ptr<ThreadData> threadData, const std::string&
} }
pThread_.reset(new std::thread(&LuaThread::work, pThreadData_, function, std::move(arguments))); pThread_.reset(new std::thread(&LuaThread::work, pThreadData_, function, std::move(arguments)));
ASSERT(pThread_.get() != nullptr); assert(pThread_);
pThread_->detach(); pThread_->detach();
} }
@ -84,12 +84,12 @@ void LuaThread::luaHook(lua_State*, lua_Debug*)
} }
} }
void LuaThread::work(std::shared_ptr<ThreadData> threadData, const std::string strFunction, std::vector<sol::object>&& arguments) noexcept { void LuaThread::work(std::shared_ptr<ThreadData> threadData, const std::string strFunction, std::vector<sol::object>&& arguments) {
try { try {
pThreadLocalData = threadData.get(); pThreadLocalData = threadData.get();
ASSERT(threadData.get()) << "invalid internal thread state\n"; assert(threadData);
const sol::object& stringLoader = threadData->luaState["loadstring"]; const sol::object& stringLoader = threadData->luaState["loadstring"];
ASSERT(stringLoader.valid() && stringLoader.get_type() == sol::type::function); REQUIRE(stringLoader.valid() && stringLoader.get_type() == sol::type::function) << "Invalid loadstring function";
sol::function userFuncObj = static_cast<const sol::function&>(stringLoader)(strFunction); sol::function userFuncObj = static_cast<const sol::function&>(stringLoader)(strFunction);
sol::function_result results = userFuncObj(sol::as_args(arguments)); sol::function_result results = userFuncObj(sol::as_args(arguments));
(void)results; // TODO: try to avoid use of useless sol::function_result here (void)results; // TODO: try to avoid use of useless sol::function_result here
@ -111,22 +111,22 @@ void LuaThread::work(std::shared_ptr<ThreadData> threadData, const std::string s
} }
} }
void LuaThread::cancel() noexcept void LuaThread::cancel()
{ {
pThreadData_->command = ThreadCommand::Cancel; pThreadData_->command = ThreadCommand::Cancel;
} }
void LuaThread::pause() noexcept void LuaThread::pause()
{ {
pThreadData_->command = ThreadCommand::Pause; pThreadData_->command = ThreadCommand::Pause;
} }
void LuaThread::resume() noexcept void LuaThread::resume()
{ {
pThreadData_->command = ThreadCommand::Resume; pThreadData_->command = ThreadCommand::Resume;
} }
std::tuple<sol::object, sol::table> LuaThread::wait(sol::this_state state) const noexcept std::tuple<sol::object, sol::table> LuaThread::wait(sol::this_state state) const
{ {
ThreadStatus stat = pThreadData_->status; ThreadStatus stat = pThreadData_->status;
@ -145,7 +145,7 @@ std::tuple<sol::object, sol::table> LuaThread::wait(sol::this_state state) const
return std::make_tuple(sol::make_object(state, threadStatusToString(stat)), std::move(returns)); return std::make_tuple(sol::make_object(state, threadStatusToString(stat)), std::move(returns));
} }
std::string LuaThread::threadStatusToString(ThreadStatus stat) const noexcept std::string LuaThread::threadStatusToString(ThreadStatus stat) const
{ {
switch(stat) switch(stat)
{ {
@ -159,12 +159,12 @@ std::string LuaThread::threadStatusToString(ThreadStatus stat) const noexcept
return "unknown"; return "unknown";
} }
std::string LuaThread::status() const noexcept std::string LuaThread::status() const
{ {
return threadStatusToString(pThreadData_->status); return threadStatusToString(pThreadData_->status);
} }
sol::object LuaThread::getUserType(sol::state_view &lua) noexcept sol::object LuaThread::getUserType(sol::state_view &lua)
{ {
static sol::usertype<LuaThread> type( static sol::usertype<LuaThread> type(
"new", sol::no_constructor, "new", sol::no_constructor,
@ -183,8 +183,7 @@ sol::object LuaThread::getUserType(sol::state_view &lua) noexcept
ThreadFactory::ThreadFactory(const sol::function& func) : stepwise_(false), step_(100U) { ThreadFactory::ThreadFactory(const sol::function& func) : stepwise_(false), step_(100U) {
sol::state_view lua(func.lua_state()); sol::state_view lua(func.lua_state());
const sol::object& dumper = lua["string"]["dump"]; const sol::object& dumper = lua["string"]["dump"];
ASSERT(dumper.valid() && dumper.get_type() == sol::type::function) REQUIRE(dumper.valid() && dumper.get_type() == sol::type::function) << "Unable to get string.dump()";
<< "Unable to get string.dump()";
strFunction_ = static_cast<const sol::function&>(dumper)(func); strFunction_ = static_cast<const sol::function&>(dumper)(func);
// Inherit all pathes from parent state by default // Inherit all pathes from parent state by default
@ -193,8 +192,8 @@ ThreadFactory::ThreadFactory(const sol::function& func) : stepwise_(false), step
} }
std::unique_ptr<LuaThread> ThreadFactory::runThread(const sol::variadic_args& args) { std::unique_ptr<LuaThread> ThreadFactory::runThread(const sol::variadic_args& args) {
std::shared_ptr<LuaThread::ThreadData> threadData(new LuaThread::ThreadData); std::shared_ptr<LuaThread::ThreadData> threadData = std::make_shared<LuaThread::ThreadData>();
ASSERT(threadData.get()); assert(threadData.get());
threadData->luaState.open_libraries( threadData->luaState.open_libraries(
sol::lib::base, sol::lib::string, sol::lib::base, sol::lib::string,
sol::lib::package, sol::lib::io, sol::lib::os sol::lib::package, sol::lib::io, sol::lib::os
@ -246,7 +245,7 @@ std::string ThreadFactory::packageCPath(const sol::optional<std::string>& value)
return ret; return ret;
} }
sol::object ThreadFactory::getUserType(sol::state_view &lua) noexcept sol::object ThreadFactory::getUserType(sol::state_view &lua)
{ {
static sol::usertype<ThreadFactory> type( static sol::usertype<ThreadFactory> type(
"new", sol::no_constructor, "new", sol::no_constructor,

View File

@ -9,9 +9,9 @@
namespace effil { namespace effil {
// Lua this thread API // Lua this thread API
std::string threadId() noexcept; std::string threadId();
void yield() noexcept; void yield();
void sleep(int64_t, sol::optional<std::string>) noexcept; void sleep(int64_t, sol::optional<std::string>);
class LuaThread { class LuaThread {
public: public:
@ -39,22 +39,22 @@ public:
}; };
LuaThread(std::shared_ptr<ThreadData> threadData, const std::string& function, const sol::variadic_args& args); LuaThread(std::shared_ptr<ThreadData> threadData, const std::string& function, const sol::variadic_args& args);
static sol::object getUserType(sol::state_view &lua) noexcept; static sol::object getUserType(sol::state_view &lua);
static void luaHook(lua_State*, lua_Debug*); static void luaHook(lua_State*, lua_Debug*);
/* Public lua methods*/ /* Public lua methods*/
void cancel() noexcept; void cancel();
void pause() noexcept; void pause();
void resume() noexcept; void resume();
std::string status() const noexcept; std::string status() const;
std::tuple<sol::object, sol::table> wait(sol::this_state state) const noexcept; std::tuple<sol::object, sol::table> wait(sol::this_state state) const;
private: private:
LuaThread(const LuaThread&) = delete; LuaThread(const LuaThread&) = delete;
LuaThread& operator=(const LuaThread&) = delete; LuaThread& operator=(const LuaThread&) = delete;
std::string threadStatusToString(ThreadStatus stat) const noexcept; std::string threadStatusToString(ThreadStatus stat) const;
static void work(std::shared_ptr<ThreadData> threadData, const std::string strFunction, std::vector<sol::object>&& arguments) noexcept; static void work(std::shared_ptr<ThreadData> threadData, const std::string strFunction, std::vector<sol::object>&& arguments);
std::shared_ptr<ThreadData> pThreadData_; std::shared_ptr<ThreadData> pThreadData_;
std::shared_ptr<std::thread> pThread_; std::shared_ptr<std::thread> pThread_;
@ -65,7 +65,7 @@ private:
class ThreadFactory { class ThreadFactory {
public: public:
ThreadFactory(const sol::function& func); ThreadFactory(const sol::function& func);
static sol::object getUserType(sol::state_view &lua) noexcept; static sol::object getUserType(sol::state_view &lua);
/* Public lua methods*/ /* Public lua methods*/
std::unique_ptr<LuaThread> runThread(const sol::variadic_args& args); std::unique_ptr<LuaThread> runThread(const sol::variadic_args& args);

View File

@ -29,8 +29,7 @@ private:
} // effil } // effil
#define ERROR throw effil::Exception() << __FILE__ << ":" << __LINE__ #define REQUIRE(cond) if (!cond) throw effil::Exception()
#define ASSERT(cond) if (!(cond)) ERROR << "In condition '" << #cond << "': "
#ifdef NDEBUG #ifdef NDEBUG
# define DEBUG if (false) std::cout # define DEBUG if (false) std::cout