diff --git a/src/cpp/garbage-collector.cpp b/src/cpp/garbage-collector.cpp index 51195a1..25d943b 100644 --- a/src/cpp/garbage-collector.cpp +++ b/src/cpp/garbage-collector.cpp @@ -7,10 +7,12 @@ namespace effil { +constexpr size_t MINIMUN_SIZE_LEFT = 100; + GC::GC() : enabled_(true) - , lastCleanup_(0) - , step_(200) {} + , lastCleanup_(MINIMUN_SIZE_LEFT) + , step_(2.) {} // Here is the naive tri-color marking // garbage collecting algorithm implementation. @@ -41,7 +43,7 @@ void GC::collect() { DEBUG << "Removing " << (objects_.size() - black.size()) << " out of " << objects_.size() << std::endl; objects_ = std::move(black); - lastCleanup_.store(0); + lastCleanup_.store(std::max(objects_.size(), MINIMUN_SIZE_LEFT)); } size_t GC::count() const { @@ -63,9 +65,13 @@ sol::table GC::exportAPI(sol::state_view& lua) { api["step"] = [](const sol::stack_object& newStep){ auto previous = instance().step(); if (newStep.valid()) { - REQUIRE(newStep.get_type() == sol::type::number) << "bad argument #1 to 'effil.gc.step' (number expected, got " << luaTypename(newStep) << ")"; - REQUIRE(newStep.as() >= 0) << "effil.gc.step: invalid capacity value = " << newStep.as(); - instance().step(newStep.as()); + REQUIRE(newStep.get_type() == sol::type::number) + << "bad argument #1 to 'effil.gc.step' (number expected, got " + << luaTypename(newStep) << ")"; + REQUIRE(newStep.as() > 1) + << "effil.gc.step: invalid capacity value = " + << newStep.as(); + instance().step(newStep.as()); } return previous; }; diff --git a/src/cpp/garbage-collector.h b/src/cpp/garbage-collector.h index 38f533f..5d3f77a 100644 --- a/src/cpp/garbage-collector.h +++ b/src/cpp/garbage-collector.h @@ -17,8 +17,8 @@ public: // This method is used to create all managed objects. template ViewType create(Args&&... args) { - if (enabled_ && lastCleanup_.fetch_add(1) == step_) - collect(); + if (enabled_ && count() >= step_ * lastCleanup_) + collect(); std::unique_ptr object(new ViewType(std::forward(args)...)); auto copy = *object; @@ -43,8 +43,8 @@ public: private: mutable std::mutex lock_; bool enabled_; - std::atomic lastCleanup_; - size_t step_; + std::atomic lastCleanup_; + double step_; std::unordered_map> objects_; private: @@ -55,8 +55,8 @@ private: void collect(); void pause() { enabled_ = false; } void resume() { enabled_ = true; } - size_t step() const { return step_; } - void step(size_t newStep) { step_ = newStep; } + double step() const { return step_; } + void step(double newStep) { step_ = newStep; } bool enabled() { return enabled_; } size_t count() const; }; diff --git a/tests/lua/gc.lua b/tests/lua/gc.lua index c979019..c9d9499 100644 --- a/tests/lua/gc.lua +++ b/tests/lua/gc.lua @@ -55,3 +55,67 @@ test.gc.store_same_value = function() effil.gc.collect() c:pop()[1] = 0 end + +local function create_fabric() + local f = { data = {} } + + function f:create(num) + for i = 1, num do + table.insert(self.data, effil.table()) + end + end + + function f:remove(num) + for i = 1, num do + table.remove(self.data, 1) + end + end + return f +end + +test.gc.check_iterative = function() + test.equal(gc.count(), 1) + local fabric = create_fabric() + test.equal(2, effil.gc.step()) + + fabric:create(199) + test.equal(gc.count(), 200) + + fabric:remove(50) + collectgarbage() + test.equal(gc.count(), 200) + + fabric:create(1) -- trigger GC + test.equal(gc.count(), 151) + fabric:remove(150) + + fabric:create(149) + test.equal(gc.count(), 300) + collectgarbage() + + fabric:create(1) -- trigger GC + test.equal(gc.count(), 151) +end + +test.gc.check_step = function() + local fabric = create_fabric() + effil.gc.step(3) + + fabric:create(299) + fabric:remove(100) + test.equal(gc.count(), 300) + collectgarbage() + + fabric:create(1) -- trigger GC + test.equal(gc.count(), 201) + + test.equal(3, effil.gc.step(2.5)) + + fabric:create(299) + fabric:remove(250) + test.equal(gc.count(), 500) + collectgarbage() + + fabric:create(1) -- trigger GC + test.equal(gc.count(), 251) +end