GC API for lua (#43)
Almost old gc api, but: rebased on new master GC::State -> enabled_ flag release lua state as soon as possible
This commit is contained in:
parent
6d20b0ff03
commit
64628c1757
@ -7,13 +7,12 @@
|
||||
|
||||
namespace effil {
|
||||
|
||||
GarbageCollector::GarbageCollector()
|
||||
: state_(GCState::Idle)
|
||||
GC::GC()
|
||||
: enabled_(true)
|
||||
, lastCleanup_(0)
|
||||
, step_(200) {}
|
||||
|
||||
GCObject* GarbageCollector::get(GCObjectHandle handle) {
|
||||
std::lock_guard<std::mutex> g(lock_);
|
||||
GCObject* GC::findObject(GCObjectHandle handle) {
|
||||
auto it = objects_.find(handle);
|
||||
if (it == objects_.end()) {
|
||||
DEBUG << "Null handle " << handle << std::endl;
|
||||
@ -22,21 +21,16 @@ GCObject* GarbageCollector::get(GCObjectHandle handle) {
|
||||
return it->second.get();
|
||||
}
|
||||
|
||||
bool GarbageCollector::has(GCObjectHandle handle) const {
|
||||
bool GC::has(GCObjectHandle handle) const {
|
||||
std::lock_guard<std::mutex> g(lock_);
|
||||
return objects_.find(handle) != objects_.end();
|
||||
}
|
||||
|
||||
// Here is the naive tri-color marking
|
||||
// garbage collecting algorithm implementation.
|
||||
void GarbageCollector::cleanup() {
|
||||
void GC::collect() {
|
||||
std::lock_guard<std::mutex> g(lock_);
|
||||
|
||||
if (state_ == GCState::Stopped)
|
||||
return;
|
||||
assert(state_ != GCState::Running);
|
||||
state_ = GCState::Running;
|
||||
|
||||
std::vector<GCObjectHandle> grey;
|
||||
std::map<GCObjectHandle, std::shared_ptr<GCObject>> black;
|
||||
|
||||
@ -59,30 +53,44 @@ void GarbageCollector::cleanup() {
|
||||
// Sweep phase
|
||||
objects_ = std::move(black);
|
||||
|
||||
state_ = GCState::Idle;
|
||||
lastCleanup_.store(0);
|
||||
}
|
||||
|
||||
size_t GarbageCollector::size() const {
|
||||
size_t GC::size() const {
|
||||
std::lock_guard<std::mutex> g(lock_);
|
||||
return objects_.size();
|
||||
}
|
||||
|
||||
void GarbageCollector::stop() {
|
||||
size_t GC::count() {
|
||||
std::lock_guard<std::mutex> g(lock_);
|
||||
assert(state_ == GCState::Idle || state_ == GCState::Stopped);
|
||||
state_ = GCState::Stopped;
|
||||
return objects_.size();
|
||||
}
|
||||
|
||||
void GarbageCollector::resume() {
|
||||
std::lock_guard<std::mutex> g(lock_);
|
||||
assert(state_ == GCState::Idle || state_ == GCState::Stopped);
|
||||
state_ = GCState::Idle;
|
||||
}
|
||||
|
||||
GarbageCollector& getGC() {
|
||||
static GarbageCollector pool;
|
||||
GC& GC::instance() {
|
||||
static GC pool;
|
||||
return pool;
|
||||
}
|
||||
|
||||
sol::table GC::getLuaApi(sol::state_view& lua) {
|
||||
sol::table api = lua.create_table_with();
|
||||
api["collect"] = [=] {
|
||||
instance().collect();
|
||||
};
|
||||
api["pause"] = [] { instance().pause(); };
|
||||
api["resume"] = [] { instance().resume(); };
|
||||
api["enabled"] = [] { return instance().enabled(); };
|
||||
api["step"] = [](sol::optional<int> newStep){
|
||||
auto previous = instance().step();
|
||||
if (newStep) {
|
||||
REQUIRE(*newStep <= 0) << "gc.step have to be > 0";
|
||||
instance().step(static_cast<size_t>(*newStep));
|
||||
}
|
||||
return previous;
|
||||
};
|
||||
api["count"] = [] {
|
||||
return instance().count();
|
||||
};
|
||||
return api;
|
||||
}
|
||||
|
||||
} // effil
|
||||
@ -2,6 +2,8 @@
|
||||
|
||||
#include "spin-mutex.h"
|
||||
|
||||
#include <sol.hpp>
|
||||
|
||||
#include <mutex>
|
||||
#include <map>
|
||||
#include <set>
|
||||
@ -32,18 +34,17 @@ protected:
|
||||
std::shared_ptr<std::set<GCObjectHandle>> refs_;
|
||||
};
|
||||
|
||||
enum class GCState { Idle, Running, Stopped };
|
||||
|
||||
class GarbageCollector {
|
||||
class GC {
|
||||
public:
|
||||
GarbageCollector();
|
||||
~GarbageCollector() = default;
|
||||
GC();
|
||||
~GC() = default;
|
||||
|
||||
// This method is used to create all managed objects.
|
||||
template <typename ObjectType, typename... Args>
|
||||
ObjectType create(Args&&... args) {
|
||||
if (lastCleanup_.fetch_add(1) == step_)
|
||||
cleanup();
|
||||
if (enabled_ && lastCleanup_.fetch_add(1) == step_)
|
||||
collect();
|
||||
|
||||
auto object = std::make_shared<ObjectType>(std::forward<Args>(args)...);
|
||||
|
||||
std::lock_guard<std::mutex> g(lock_);
|
||||
@ -51,28 +52,39 @@ public:
|
||||
return *object;
|
||||
}
|
||||
|
||||
GCObject* get(GCObjectHandle handle);
|
||||
template <typename ObjectType>
|
||||
ObjectType get(GCObjectHandle handle) {
|
||||
std::lock_guard<std::mutex> g(lock_);
|
||||
// TODO: add dynamic cast to check?
|
||||
return *static_cast<ObjectType*>(findObject(handle));
|
||||
}
|
||||
bool has(GCObjectHandle handle) const;
|
||||
void cleanup();
|
||||
|
||||
void collect();
|
||||
size_t size() const;
|
||||
void stop();
|
||||
void resume();
|
||||
void pause() { enabled_ = false; };
|
||||
void resume() { enabled_ = true; };
|
||||
size_t step() const { return step_; }
|
||||
void step(size_t newStep) { step_ = newStep; }
|
||||
GCState state() const { return state_; }
|
||||
bool enabled() { return enabled_; };
|
||||
size_t count();
|
||||
|
||||
static GC& instance();
|
||||
static sol::table getLuaApi(sol::state_view& lua);
|
||||
|
||||
private:
|
||||
mutable std::mutex lock_;
|
||||
GCState state_;
|
||||
bool enabled_;
|
||||
std::atomic<size_t> lastCleanup_;
|
||||
size_t step_;
|
||||
std::map<GCObjectHandle, std::shared_ptr<GCObject>> objects_;
|
||||
|
||||
private:
|
||||
GarbageCollector(GarbageCollector&&) = delete;
|
||||
GarbageCollector(const GarbageCollector&) = delete;
|
||||
GCObject* findObject(GCObjectHandle handle);
|
||||
|
||||
private:
|
||||
GC(GC&&) = delete;
|
||||
GC(const GC&) = delete;
|
||||
};
|
||||
|
||||
GarbageCollector& getGC();
|
||||
|
||||
} // effil
|
||||
@ -18,9 +18,11 @@ sol::object createThread(const sol::this_state& lua,
|
||||
return sol::make_object(lua, std::make_shared<Thread>(path, cpath, stepwise, step, function, args));
|
||||
}
|
||||
|
||||
sol::object createTable(sol::this_state lua) { return sol::make_object(lua, getGC().create<SharedTable>()); }
|
||||
sol::object createTable(sol::this_state lua) {
|
||||
return sol::make_object(lua, GC::instance().create<SharedTable>());
|
||||
}
|
||||
|
||||
SharedTable globalTable = getGC().create<SharedTable>();
|
||||
SharedTable globalTable = GC::instance().create<SharedTable>();
|
||||
|
||||
} // namespace
|
||||
|
||||
@ -39,7 +41,9 @@ extern "C" int luaopen_libeffil(lua_State* L) {
|
||||
"size", SharedTable::luaSize,
|
||||
"setmetatable", SharedTable::luaSetMetatable,
|
||||
"getmetatable", SharedTable::luaGetMetatable,
|
||||
"G", sol::make_object(lua, globalTable)
|
||||
"G", sol::make_object(lua, globalTable),
|
||||
"getmetatable", SharedTable::luaGetMetatable,
|
||||
"gc", GC::getLuaApi(lua)
|
||||
);
|
||||
sol::stack::push(lua, publicApi);
|
||||
return 1;
|
||||
|
||||
@ -104,7 +104,7 @@ sol::object SharedTable::rawGet(const sol::stack_object& luaKey, sol::this_state
|
||||
{ \
|
||||
std::unique_lock<SpinMutex> lock(data_->lock); \
|
||||
if (data_->metatable != GCNull) { \
|
||||
SharedTable tableHolder = *static_cast<SharedTable*>(getGC().get(data_->metatable)); \
|
||||
auto tableHolder = GC::instance().get<SharedTable>(data_->metatable); \
|
||||
lock.unlock(); \
|
||||
sol::function handler = tableHolder.get(createStoredObject(methodName), state); \
|
||||
if (handler.valid()) { \
|
||||
@ -160,7 +160,7 @@ void SharedTable::luaNewIndex(const sol::stack_object& luaKey, const sol::stack_
|
||||
{
|
||||
std::unique_lock<SpinMutex> lock(data_->lock);
|
||||
if (data_->metatable != GCNull) {
|
||||
SharedTable tableHolder = *static_cast<SharedTable*>(getGC().get(data_->metatable));
|
||||
auto tableHolder = GC::instance().get<SharedTable>(data_->metatable);
|
||||
lock.unlock();
|
||||
sol::function handler = tableHolder.get(createStoredObject("__newindex"), state);
|
||||
if (handler.valid()) {
|
||||
@ -180,7 +180,8 @@ 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) {
|
||||
sol::function handler = static_cast<SharedTable*>(getGC().get(data_->metatable))->get(createStoredObject(std::string("__call")), state);
|
||||
auto metatable = GC::instance().get<SharedTable>(data_->metatable);
|
||||
sol::function handler = metatable.get(createStoredObject(std::string("__call")), state);
|
||||
lock.unlock();
|
||||
if (handler.valid()) {
|
||||
StoredArray storedResults;
|
||||
@ -280,7 +281,7 @@ SharedTable SharedTable::luaSetMetatable(SharedTable& stable, const sol::stack_o
|
||||
sol::object SharedTable::luaGetMetatable(const SharedTable& stable, sol::this_state state) {
|
||||
std::lock_guard<SpinMutex> lock(stable.data_->lock);
|
||||
return stable.data_->metatable == GCNull ? sol::nil :
|
||||
sol::make_object(state, *static_cast<SharedTable*>(getGC().get(stable.data_->metatable)));
|
||||
sol::make_object(state, GC::instance().get<SharedTable>(stable.data_->metatable));
|
||||
}
|
||||
|
||||
sol::object SharedTable::luaRawGet(const SharedTable& stable, const sol::stack_object& key, sol::this_state state) {
|
||||
|
||||
@ -79,7 +79,7 @@ public:
|
||||
TableHolder(const SolType& luaObject) {
|
||||
assert(luaObject.template is<SharedTable>());
|
||||
handle_ = luaObject.template as<SharedTable>().handle();
|
||||
assert(getGC().has(handle_));
|
||||
assert(GC::instance().has(handle_));
|
||||
}
|
||||
|
||||
TableHolder(GCObjectHandle handle)
|
||||
@ -90,7 +90,7 @@ public:
|
||||
}
|
||||
|
||||
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, GC::instance().get<SharedTable>(handle_));
|
||||
}
|
||||
|
||||
GCObjectHandle gcHandle() const override { return handle_; }
|
||||
@ -115,7 +115,7 @@ StoredObject makeStoredObject(sol::object luaObject, SolTableToShared& visited)
|
||||
auto st = std::find_if(visited.begin(), visited.end(), comparator);
|
||||
|
||||
if (st == std::end(visited)) {
|
||||
SharedTable table = getGC().create<SharedTable>();
|
||||
SharedTable table = GC::instance().create<SharedTable>();
|
||||
visited.emplace_back(std::make_pair(luaTable, table.handle()));
|
||||
dumpTable(&table, luaTable, visited);
|
||||
return createStoredObject(table.handle());
|
||||
@ -160,7 +160,7 @@ StoredObject fromSolObject(const SolObject& luaObject) {
|
||||
sol::table luaTable = luaObject;
|
||||
// Tables pool is used to store tables.
|
||||
// Right now not defiantly clear how ownership between states works.
|
||||
SharedTable table = getGC().create<SharedTable>();
|
||||
SharedTable table = GC::instance().create<SharedTable>();
|
||||
SolTableToShared visited{{luaTable, table.handle()}};
|
||||
|
||||
// Let's dump table and all subtables
|
||||
|
||||
@ -54,7 +54,6 @@ class ThreadHandle {
|
||||
public:
|
||||
const bool managed;
|
||||
|
||||
sol::state lua;
|
||||
Status status;
|
||||
StoredArray result;
|
||||
|
||||
@ -68,8 +67,9 @@ public:
|
||||
ThreadHandle(bool isManaged)
|
||||
: managed(isManaged)
|
||||
, status(Status::Running)
|
||||
, command_(Command::Run) {
|
||||
luaL_openlibs(lua);
|
||||
, command_(Command::Run)
|
||||
, lua_(std::make_unique<sol::state>()) {
|
||||
luaL_openlibs(*lua_);
|
||||
}
|
||||
|
||||
Command command() const { return command_; }
|
||||
@ -81,9 +81,18 @@ public:
|
||||
command_ = cmd;
|
||||
}
|
||||
|
||||
sol::state& lua() {
|
||||
assert(lua_);
|
||||
return *lua_;
|
||||
}
|
||||
|
||||
void destroyLua() { lua_.reset(); }
|
||||
|
||||
private:
|
||||
SpinMutex commandMutex_;
|
||||
Command command_;
|
||||
|
||||
std::unique_ptr<sol::state> lua_;
|
||||
};
|
||||
|
||||
namespace {
|
||||
@ -112,13 +121,8 @@ void luaHook(lua_State*, lua_Debug*) {
|
||||
|
||||
class ScopeGuard {
|
||||
public:
|
||||
ScopeGuard(const std::function<void()>& f)
|
||||
: f_(f) {
|
||||
}
|
||||
|
||||
~ScopeGuard() {
|
||||
f_();
|
||||
}
|
||||
ScopeGuard(const std::function<void()>& f) : f_(f) {}
|
||||
~ScopeGuard() { f_(); }
|
||||
|
||||
private:
|
||||
std::function<void()> f_;
|
||||
@ -130,6 +134,9 @@ void runThread(std::shared_ptr<ThreadHandle> handle,
|
||||
|
||||
ScopeGuard reportComplete([=](){
|
||||
DEBUG << "Finished " << std::endl;
|
||||
// Let's destroy accociated state
|
||||
// to release all resources as soon as possible
|
||||
handle->destroyLua();
|
||||
handle->completion.notify();
|
||||
});
|
||||
|
||||
@ -137,10 +144,10 @@ void runThread(std::shared_ptr<ThreadHandle> handle,
|
||||
thisThreadHandle = handle.get();
|
||||
|
||||
try {
|
||||
sol::function userFuncObj = loadString(handle->lua, strFunction);
|
||||
sol::function userFuncObj = loadString(handle->lua(), strFunction);
|
||||
sol::function_result results = userFuncObj(std::move(arguments));
|
||||
(void)results; // just leave all returns on the stack
|
||||
sol::variadic_args args(handle->lua, -lua_gettop(handle->lua));
|
||||
sol::variadic_args args(handle->lua(), -lua_gettop(handle->lua()));
|
||||
for (const auto& iter : args) {
|
||||
StoredObject store = createStoredObject(iter.get<sol::object>());
|
||||
handle->result.emplace_back(std::move(store));
|
||||
@ -193,12 +200,12 @@ Thread::Thread(const std::string& path,
|
||||
const sol::variadic_args& variadicArgs)
|
||||
: handle_(std::make_shared<ThreadHandle>(managed)) {
|
||||
|
||||
handle_->lua["package"]["path"] = path;
|
||||
handle_->lua["package"]["cpath"] = cpath;
|
||||
handle_->lua.script("require 'effil'");
|
||||
handle_->lua()["package"]["path"] = path;
|
||||
handle_->lua()["package"]["cpath"] = cpath;
|
||||
handle_->lua().script("require 'effil'");
|
||||
|
||||
if (managed)
|
||||
lua_sethook(handle_->lua, luaHook, LUA_MASKCOUNT, step);
|
||||
lua_sethook(handle_->lua(), luaHook, LUA_MASKCOUNT, step);
|
||||
|
||||
std::string strFunction = dumpFunction(function);
|
||||
|
||||
|
||||
@ -23,6 +23,7 @@ local api = {
|
||||
setmetatable = capi.setmetatable,
|
||||
getmetatable = capi.getmetatable,
|
||||
G = capi.G,
|
||||
gc = capi.gc
|
||||
}
|
||||
|
||||
local function run_thread(config, f, ...)
|
||||
|
||||
@ -12,10 +12,10 @@ TEST(gc, GCObject) {
|
||||
GCObject o1;
|
||||
EXPECT_EQ(o1.instances(), (size_t)1);
|
||||
|
||||
GCObject o2 = getGC().create<GCObject>();
|
||||
GCObject o2 = GC::instance().create<GCObject>();
|
||||
EXPECT_EQ(o2.instances(), (size_t)2);
|
||||
|
||||
GCObject o3 = getGC().create<GCObject>();
|
||||
GCObject o3 = GC::instance().create<GCObject>();
|
||||
GCObject o4(o3);
|
||||
GCObject o5(o4);
|
||||
EXPECT_EQ(o5.instances(), o3.instances());
|
||||
@ -25,18 +25,15 @@ TEST(gc, GCObject) {
|
||||
}
|
||||
|
||||
TEST(gc, collect) {
|
||||
getGC().cleanup();
|
||||
ASSERT_EQ(getGC().size(), (size_t)1);
|
||||
|
||||
GC::instance().collect();
|
||||
ASSERT_EQ(GC::instance().size(), (size_t)1);
|
||||
{
|
||||
GCObject o1 = getGC().create<GCObject>();
|
||||
;
|
||||
GCObject o2 = getGC().create<GCObject>();
|
||||
;
|
||||
GCObject o1 = GC::instance().create<GCObject>();
|
||||
GCObject o2 = GC::instance().create<GCObject>();
|
||||
}
|
||||
EXPECT_EQ(getGC().size(), (size_t)3);
|
||||
getGC().cleanup();
|
||||
EXPECT_EQ(getGC().size(), (size_t)1);
|
||||
EXPECT_EQ(GC::instance().size(), (size_t)3);
|
||||
GC::instance().collect();
|
||||
EXPECT_EQ(GC::instance().size(), (size_t)1);
|
||||
}
|
||||
|
||||
namespace {
|
||||
@ -44,26 +41,27 @@ namespace {
|
||||
struct Dummy : public GCObject {
|
||||
void add(GCObjectHandle ref) { refs_->insert(ref); }
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
TEST(gc, withRefs) {
|
||||
getGC().cleanup();
|
||||
GC::instance().collect();
|
||||
{
|
||||
Dummy root = getGC().create<Dummy>();
|
||||
Dummy root = GC::instance().create<Dummy>();
|
||||
|
||||
{
|
||||
Dummy orphan = getGC().create<Dummy>();
|
||||
Dummy orphan = GC::instance().create<Dummy>();
|
||||
for (size_t i = 0; i < 3; i++) {
|
||||
Dummy child = getGC().create<Dummy>();
|
||||
Dummy child = GC::instance().create<Dummy>();
|
||||
root.add(child.handle());
|
||||
}
|
||||
}
|
||||
EXPECT_EQ(getGC().size(), (size_t)6);
|
||||
getGC().cleanup();
|
||||
EXPECT_EQ(getGC().size(), (size_t)5);
|
||||
EXPECT_EQ(GC::instance().size(), (size_t)6);
|
||||
GC::instance().collect();
|
||||
EXPECT_EQ(GC::instance().size(), (size_t)5);
|
||||
}
|
||||
getGC().cleanup();
|
||||
EXPECT_EQ(getGC().size(), (size_t)1);
|
||||
GC::instance().collect();
|
||||
EXPECT_EQ(GC::instance().size(), (size_t)1);
|
||||
}
|
||||
|
||||
TEST(gc, autoCleanup) {
|
||||
@ -73,59 +71,59 @@ TEST(gc, autoCleanup) {
|
||||
for (size_t i = 0; i < 5; i++)
|
||||
threads.emplace_back([=] {
|
||||
for (size_t i = 0; i < objectsPerThread; i++)
|
||||
getGC().create<GCObject>();
|
||||
GC::instance().create<GCObject>();
|
||||
});
|
||||
|
||||
for (auto& thread : threads)
|
||||
thread.join();
|
||||
|
||||
EXPECT_LT(getGC().size(), getGC().step());
|
||||
EXPECT_LT(GC::instance().size(), GC::instance().step());
|
||||
}
|
||||
|
||||
TEST(gc, gcInLuaState) {
|
||||
sol::state lua;
|
||||
bootstrapState(lua);
|
||||
|
||||
lua["st"] = getGC().create<SharedTable>();
|
||||
lua["st"] = GC::instance().create<SharedTable>();
|
||||
lua.script(R"(
|
||||
for i=1,1000 do
|
||||
st[i] = {"Wow"}
|
||||
end
|
||||
)");
|
||||
EXPECT_EQ(getGC().size(), (size_t)1002);
|
||||
EXPECT_EQ(GC::instance().size(), (size_t)1002);
|
||||
|
||||
lua.script(R"(
|
||||
for i=1,1000 do
|
||||
st[i] = nil
|
||||
end
|
||||
)");
|
||||
getGC().cleanup();
|
||||
EXPECT_EQ(getGC().size(), (size_t)2);
|
||||
GC::instance().collect();
|
||||
EXPECT_EQ(GC::instance().size(), (size_t)2);
|
||||
}
|
||||
|
||||
TEST(gc, cycles) {
|
||||
{
|
||||
sol::state lua;
|
||||
bootstrapState(lua);
|
||||
getGC().cleanup();
|
||||
GC::instance().collect();
|
||||
|
||||
lua["st"] = getGC().create<SharedTable>();
|
||||
lua["st"] = GC::instance().create<SharedTable>();
|
||||
lua.script(R"(
|
||||
st.parent = {}
|
||||
st.parent.child = { ref = st.parent }
|
||||
st[4] = { one = 1 }
|
||||
st[5] = { flag = true }
|
||||
)");
|
||||
EXPECT_EQ(getGC().size(), (size_t)6);
|
||||
EXPECT_EQ(GC::instance().size(), (size_t)6);
|
||||
|
||||
lua.script("st.parent = nil");
|
||||
|
||||
lua.collect_garbage();
|
||||
getGC().cleanup();
|
||||
EXPECT_EQ(getGC().size(), (size_t)4);
|
||||
GC::instance().collect();
|
||||
EXPECT_EQ(GC::instance().size(), (size_t)4);
|
||||
}
|
||||
getGC().cleanup();
|
||||
EXPECT_EQ(getGC().size(), (size_t)1);
|
||||
GC::instance().collect();
|
||||
EXPECT_EQ(GC::instance().size(), (size_t)1);
|
||||
}
|
||||
|
||||
TEST(gc, multipleStates) {
|
||||
@ -135,12 +133,12 @@ TEST(gc, multipleStates) {
|
||||
bootstrapState(lua2);
|
||||
|
||||
{
|
||||
SharedTable st = getGC().create<SharedTable>();
|
||||
SharedTable st = GC::instance().create<SharedTable>();
|
||||
lua1["st"] = st;
|
||||
lua2["st"] = st;
|
||||
}
|
||||
getGC().cleanup();
|
||||
EXPECT_EQ(getGC().size(), (size_t)2);
|
||||
GC::instance().collect();
|
||||
EXPECT_EQ(GC::instance().size(), (size_t)2);
|
||||
|
||||
lua1.script(R"(
|
||||
st.men = { name = "John", age = 22 }
|
||||
@ -152,13 +150,13 @@ st.men.car = st.car
|
||||
st.men.cat = st.cat
|
||||
st.men.fish = st.fish
|
||||
)");
|
||||
getGC().cleanup();
|
||||
EXPECT_EQ(getGC().size(), (size_t)6);
|
||||
GC::instance().collect();
|
||||
EXPECT_EQ(GC::instance().size(), (size_t)6);
|
||||
|
||||
lua2.script("copy = { st.men } st = nil");
|
||||
lua1.script("st = nil");
|
||||
lua1.collect_garbage();
|
||||
lua2.collect_garbage();
|
||||
getGC().cleanup();
|
||||
EXPECT_EQ(getGC().size(), (size_t)5);
|
||||
GC::instance().collect();
|
||||
EXPECT_EQ(GC::instance().size(), (size_t)5);
|
||||
}
|
||||
|
||||
@ -129,9 +129,9 @@ TEST(sharedTable, playingWithSharedTables) {
|
||||
sol::state lua;
|
||||
bootstrapState(lua);
|
||||
|
||||
lua["recursive"] = getGC().create<SharedTable>();
|
||||
lua["st1"] = getGC().create<SharedTable>();
|
||||
lua["st2"] = getGC().create<SharedTable>();
|
||||
lua["recursive"] = GC::instance().create<SharedTable>();
|
||||
lua["st1"] = GC::instance().create<SharedTable>();
|
||||
lua["st2"] = GC::instance().create<SharedTable>();
|
||||
|
||||
lua.script(R"(
|
||||
st1.proxy = st2
|
||||
|
||||
40
tests/lua/gc.lua
Normal file
40
tests/lua/gc.lua
Normal file
@ -0,0 +1,40 @@
|
||||
local effil = require "effil"
|
||||
local gc = effil.gc
|
||||
|
||||
TestGC = { tearDown = tearDown }
|
||||
|
||||
function TestGC:testCleanup()
|
||||
collectgarbage()
|
||||
gc.collect()
|
||||
test.assertEquals(gc.count(), 1)
|
||||
|
||||
for i = 0, 10000 do
|
||||
local tmp = effil.table()
|
||||
end
|
||||
|
||||
collectgarbage()
|
||||
gc.collect()
|
||||
test.assertEquals(gc.count(), 1)
|
||||
end
|
||||
|
||||
function TestGC:testDisableGC()
|
||||
local nobjects = 10000
|
||||
|
||||
collectgarbage()
|
||||
gc.collect()
|
||||
test.assertEquals(gc.count(), 1)
|
||||
|
||||
gc.pause()
|
||||
test.assertFalse(gc.enabled())
|
||||
|
||||
for i = 1, nobjects do
|
||||
local tmp = effil.table()
|
||||
end
|
||||
|
||||
test.assertEquals(gc.count(), nobjects + 1)
|
||||
collectgarbage()
|
||||
gc.collect()
|
||||
test.assertEquals(gc.count(), 1)
|
||||
|
||||
gc.resume()
|
||||
end
|
||||
@ -31,6 +31,7 @@ effil = require 'effil'
|
||||
require 'test_utils'
|
||||
require 'thread'
|
||||
require 'shared_table'
|
||||
require 'gc'
|
||||
|
||||
-- Hack tests functions to print when test starts
|
||||
for suite_name, suite in pairs(_G) do
|
||||
|
||||
@ -46,6 +46,10 @@ end
|
||||
|
||||
function tearDown()
|
||||
collectgarbage()
|
||||
effil.gc.collect()
|
||||
-- effil.G is always present
|
||||
-- thus, gc has one object
|
||||
test.assertEquals(effil.gc.count(), 1)
|
||||
end
|
||||
|
||||
function make_test_with_param(test_suite, test_case_pattern, ...)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user