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

View File

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

View File

@ -7,17 +7,17 @@
namespace effil {
SharedTable::SharedTable() noexcept
SharedTable::SharedTable()
: GCObject(),
data_(std::make_shared<SharedData>()) {
}
SharedTable::SharedTable(const SharedTable& init) noexcept
SharedTable::SharedTable(const SharedTable& init)
: GCObject(init),
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(
"new", sol::no_constructor,
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);
}
void SharedTable::set(StoredObject&& key, StoredObject&& value) noexcept {
void SharedTable::set(StoredObject&& key, StoredObject&& value) {
std::lock_guard<SpinMutex> g(data_->lock);
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) {
ASSERT(luaKey.valid()) << "Invalid table index";
REQUIRE(luaKey.valid()) << "Indexing by nil";
StoredObject key = createStoredObject(luaKey);
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 {
ASSERT(key.valid());
sol::object SharedTable::luaGet(const sol::stack_object& luaKey, const sol::this_state& state) const {
REQUIRE(luaKey.valid()) << "Indexing by nil";
StoredObject cppKey = createStoredObject(key);
StoredObject key = createStoredObject(luaKey);
std::lock_guard<SpinMutex> g(data_->lock);
auto val = data_->entries.find(cppKey);
auto val = data_->entries.find(key);
if (val == data_->entries.end()) {
return sol::nil;
} 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);
return data_->entries.size();
}

View File

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

View File

@ -47,7 +47,7 @@ public:
FunctionHolder(SolObject luaObject) noexcept {
sol::state_view lua(luaObject.lua_state());
sol::function dumper = lua["string"]["dump"];
ASSERT(dumper.valid());
if (!dumper.valid()) throw Exception() << "Invalid string.dump()";
function_ = dumper(luaObject);
}
@ -62,9 +62,10 @@ public:
sol::object unpack(sol::this_state state) const final {
sol::state_view lua((lua_State*)state);
sol::function loader = lua["loadstring"];
ASSERT(loader.valid());
REQUIRE(loader.valid()) << "Invalid loadstring()";
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);
}
@ -75,28 +76,28 @@ private:
class TableHolder : public BaseHolder {
public:
template<typename SolType>
TableHolder(const SolType& luaObject) noexcept {
TableHolder(const SolType& luaObject) {
assert(luaObject.template is<SharedTable>());
handle_ = luaObject.template as<SharedTable>().handle();
assert(getGC().has(handle_));
}
TableHolder(GCObjectHandle handle) noexcept
TableHolder(GCObjectHandle 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_;
}
std::size_t hash() const noexcept final {
std::size_t hash() const final {
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_)));
}
GCObjectHandle gcHandle() const noexcept override { return handle_; }
GCObjectHandle gcHandle() const override { return handle_; }
private:
GCObjectHandle handle_;
};
@ -165,7 +166,7 @@ StoredObject fromSolObject(const SolObject& luaObject) {
return std::make_unique<TableHolder>(table.handle());
}
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;
}

View File

@ -11,16 +11,16 @@ public:
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);
}
virtual bool rawCompare(const BaseHolder* other) const = 0;
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 GCObjectHandle gcHandle() const noexcept { return GCNull; }
virtual GCObjectHandle gcHandle() const { return GCNull; }
private:
BaseHolder(const BaseHolder&) = delete;

View File

@ -7,19 +7,19 @@ namespace effil {
class LuaHookStopException : public std::exception {};
std::string threadId() noexcept
std::string threadId()
{
std::stringstream ss;
ss << std::this_thread::get_id();
return ss.str();
}
void yield() noexcept
void 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";
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) {
pThreadData_ = threadData;
ASSERT(pThreadData_.get());
assert(pThreadData_);
pThreadData_->command = ThreadCommand::Nothing;
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)));
ASSERT(pThread_.get() != nullptr);
assert(pThread_);
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 {
pThreadLocalData = threadData.get();
ASSERT(threadData.get()) << "invalid internal thread state\n";
assert(threadData);
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_result results = userFuncObj(sol::as_args(arguments));
(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;
}
void LuaThread::pause() noexcept
void LuaThread::pause()
{
pThreadData_->command = ThreadCommand::Pause;
}
void LuaThread::resume() noexcept
void LuaThread::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;
@ -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));
}
std::string LuaThread::threadStatusToString(ThreadStatus stat) const noexcept
std::string LuaThread::threadStatusToString(ThreadStatus stat) const
{
switch(stat)
{
@ -159,12 +159,12 @@ std::string LuaThread::threadStatusToString(ThreadStatus stat) const noexcept
return "unknown";
}
std::string LuaThread::status() const noexcept
std::string LuaThread::status() const
{
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(
"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) {
sol::state_view lua(func.lua_state());
const sol::object& dumper = lua["string"]["dump"];
ASSERT(dumper.valid() && dumper.get_type() == sol::type::function)
<< "Unable to get string.dump()";
REQUIRE(dumper.valid() && dumper.get_type() == sol::type::function) << "Unable to get string.dump()";
strFunction_ = static_cast<const sol::function&>(dumper)(func);
// 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::shared_ptr<LuaThread::ThreadData> threadData(new LuaThread::ThreadData);
ASSERT(threadData.get());
std::shared_ptr<LuaThread::ThreadData> threadData = std::make_shared<LuaThread::ThreadData>();
assert(threadData.get());
threadData->luaState.open_libraries(
sol::lib::base, sol::lib::string,
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;
}
sol::object ThreadFactory::getUserType(sol::state_view &lua) noexcept
sol::object ThreadFactory::getUserType(sol::state_view &lua)
{
static sol::usertype<ThreadFactory> type(
"new", sol::no_constructor,

View File

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

View File

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