gc seems to work!

This commit is contained in:
2023-12-26 22:40:39 +01:00
parent a0febfba0e
commit a86012108e
15 changed files with 672 additions and 86 deletions

147
.clang-tidy Normal file
View File

@@ -0,0 +1,147 @@
# Generated from CLion Inspection settings
---
Checks: '-*,
bugprone-argument-comment,
bugprone-assert-side-effect,
bugprone-bad-signal-to-kill-thread,
bugprone-branch-clone,
bugprone-copy-constructor-init,
bugprone-dangling-handle,
bugprone-dynamic-static-initializers,
bugprone-fold-init-type,
bugprone-forward-declaration-namespace,
bugprone-forwarding-reference-overload,
bugprone-inaccurate-erase,
bugprone-incorrect-roundings,
bugprone-integer-division,
bugprone-lambda-function-name,
bugprone-macro-parentheses,
bugprone-macro-repeated-side-effects,
bugprone-misplaced-operator-in-strlen-in-alloc,
bugprone-misplaced-pointer-arithmetic-in-alloc,
bugprone-misplaced-widening-cast,
bugprone-move-forwarding-reference,
bugprone-multiple-statement-macro,
bugprone-no-escape,
bugprone-parent-virtual-call,
bugprone-posix-return,
bugprone-reserved-identifier,
bugprone-sizeof-container,
bugprone-sizeof-expression,
bugprone-spuriously-wake-up-functions,
bugprone-string-constructor,
bugprone-string-integer-assignment,
bugprone-string-literal-with-embedded-nul,
bugprone-suspicious-enum-usage,
bugprone-suspicious-include,
bugprone-suspicious-memset-usage,
bugprone-suspicious-missing-comma,
bugprone-suspicious-semicolon,
bugprone-suspicious-string-compare,
bugprone-suspicious-memory-comparison,
bugprone-suspicious-realloc-usage,
bugprone-swapped-arguments,
bugprone-terminating-continue,
bugprone-throw-keyword-missing,
bugprone-too-small-loop-variable,
bugprone-undefined-memory-manipulation,
bugprone-undelegated-constructor,
bugprone-unhandled-self-assignment,
bugprone-unused-raii,
bugprone-unused-return-value,
bugprone-use-after-move,
bugprone-virtual-near-miss,
cert-dcl21-cpp,
cert-dcl58-cpp,
cert-err34-c,
cert-err52-cpp,
cert-err60-cpp,
cert-flp30-c,
cert-msc50-cpp,
cert-msc51-cpp,
cert-str34-c,
cppcoreguidelines-interfaces-global-init,
cppcoreguidelines-narrowing-conversions,
cppcoreguidelines-pro-type-member-init,
cppcoreguidelines-pro-type-static-cast-downcast,
cppcoreguidelines-slicing,
google-default-arguments,
google-explicit-constructor,
google-runtime-operator,
hicpp-exception-baseclass,
hicpp-multiway-paths-covered,
misc-misplaced-const,
misc-new-delete-overloads,
misc-no-recursion,
misc-non-copyable-objects,
misc-throw-by-value-catch-by-reference,
misc-unconventional-assign-operator,
misc-uniqueptr-reset-release,
modernize-avoid-bind,
modernize-concat-nested-namespaces,
modernize-deprecated-headers,
modernize-deprecated-ios-base-aliases,
modernize-loop-convert,
modernize-make-shared,
modernize-make-unique,
modernize-pass-by-value,
modernize-raw-string-literal,
modernize-redundant-void-arg,
modernize-replace-auto-ptr,
modernize-replace-disallow-copy-and-assign-macro,
modernize-replace-random-shuffle,
modernize-return-braced-init-list,
modernize-shrink-to-fit,
modernize-unary-static-assert,
modernize-use-auto,
modernize-use-bool-literals,
modernize-use-emplace,
modernize-use-equals-default,
modernize-use-equals-delete,
modernize-use-nodiscard,
modernize-use-noexcept,
modernize-use-nullptr,
modernize-use-override,
modernize-use-transparent-functors,
modernize-use-uncaught-exceptions,
mpi-buffer-deref,
mpi-type-mismatch,
openmp-use-default-none,
performance-faster-string-find,
performance-for-range-copy,
performance-implicit-conversion-in-loop,
performance-inefficient-algorithm,
performance-inefficient-string-concatenation,
performance-inefficient-vector-operation,
performance-move-const-arg,
performance-move-constructor-init,
performance-no-automatic-move,
performance-noexcept-move-constructor,
performance-trivially-destructible,
performance-type-promotion-in-math-fn,
performance-unnecessary-copy-initialization,
performance-unnecessary-value-param,
portability-simd-intrinsics,
readability-avoid-const-params-in-decls,
readability-const-return-type,
readability-container-size-empty,
readability-convert-member-functions-to-static,
readability-delete-null-pointer,
readability-deleted-default,
readability-inconsistent-declaration-parameter-name,
readability-make-member-function-const,
readability-misleading-indentation,
readability-misplaced-array-index,
readability-non-const-parameter,
readability-redundant-control-flow,
readability-redundant-declaration,
readability-redundant-function-ptr-dereference,
readability-redundant-smartptr-get,
readability-redundant-string-cstr,
readability-redundant-string-init,
readability-simplify-subscript-expr,
readability-static-accessed-through-instance,
readability-static-definition-in-anonymous-namespace,
readability-string-compare,
readability-uniqueptr-delete-release,
readability-use-anyofallof'

1
.idea/cmake.xml generated
View File

@@ -3,6 +3,7 @@
<component name="CMakeSharedSettings"> <component name="CMakeSharedSettings">
<configurations> <configurations>
<configuration PROFILE_NAME="Debug" ENABLED="true" CONFIG_NAME="Debug" GENERATION_OPTIONS="-DSANITIZE=YES" /> <configuration PROFILE_NAME="Debug" ENABLED="true" CONFIG_NAME="Debug" GENERATION_OPTIONS="-DSANITIZE=YES" />
<configuration PROFILE_NAME="RelWithDebInfo" ENABLED="true" CONFIG_NAME="RelWithDebInfo" />
</configurations> </configurations>
</component> </component>
</project> </project>

View File

@@ -1,5 +1,45 @@
<component name="ProjectCodeStyleConfiguration"> <component name="ProjectCodeStyleConfiguration">
<code_scheme name="Project" version="173"> <code_scheme name="Project" version="173">
<Objective-C-extensions>
<rules>
<rule entity="NAMESPACE" visibility="ANY" specifier="ANY" prefix="" style="PASCAL_CASE" suffix="" />
<rule entity="MACRO" visibility="ANY" specifier="ANY" prefix="" style="SCREAMING_SNAKE_CASE" suffix="" />
<rule entity="CLASS" visibility="ANY" specifier="ANY" prefix="" style="PASCAL_CASE" suffix="" />
<rule entity="STRUCT" visibility="ANY" specifier="ANY" prefix="" style="PASCAL_CASE" suffix="" />
<rule entity="ENUM" visibility="ANY" specifier="ANY" prefix="" style="PASCAL_CASE" suffix="" />
<rule entity="ENUMERATOR" visibility="ANY" specifier="ANY" prefix="" style="PASCAL_CASE" suffix="" />
<rule entity="TYPEDEF" visibility="ANY" specifier="ANY" prefix="" style="PASCAL_CASE" suffix="" />
<rule entity="UNION" visibility="ANY" specifier="ANY" prefix="" style="PASCAL_CASE" suffix="" />
<rule entity="CLASS_MEMBER_FUNCTION" visibility="ANY" specifier="ANY" prefix="" style="SNAKE_CASE" suffix="" />
<rule entity="STRUCT_MEMBER_FUNCTION" visibility="ANY" specifier="ANY" prefix="" style="SNAKE_CASE" suffix="" />
<rule entity="CLASS_MEMBER_FIELD" visibility="ANY" specifier="ANY" prefix="_" style="SNAKE_CASE" suffix="" />
<rule entity="STRUCT_MEMBER_FIELD" visibility="ANY" specifier="ANY" prefix="_" style="SNAKE_CASE" suffix="" />
<rule entity="GLOBAL_FUNCTION" visibility="ANY" specifier="ANY" prefix="" style="SNAKE_CASE" suffix="" />
<rule entity="GLOBAL_VARIABLE" visibility="ANY" specifier="ANY" prefix="" style="SCREAMING_SNAKE_CASE" suffix="" />
<rule entity="PARAMETER" visibility="ANY" specifier="ANY" prefix="" style="SNAKE_CASE" suffix="" />
<rule entity="LOCAL_VARIABLE" visibility="ANY" specifier="ANY" prefix="" style="SNAKE_CASE" suffix="" />
</rules>
</Objective-C-extensions>
<Objective-C-extensions>
<rules>
<rule entity="NAMESPACE" visibility="ANY" specifier="ANY" prefix="" style="PASCAL_CASE" suffix="" />
<rule entity="MACRO" visibility="ANY" specifier="ANY" prefix="" style="SCREAMING_SNAKE_CASE" suffix="" />
<rule entity="CLASS" visibility="ANY" specifier="ANY" prefix="" style="PASCAL_CASE" suffix="" />
<rule entity="STRUCT" visibility="ANY" specifier="ANY" prefix="" style="PASCAL_CASE" suffix="" />
<rule entity="ENUM" visibility="ANY" specifier="ANY" prefix="" style="PASCAL_CASE" suffix="" />
<rule entity="ENUMERATOR" visibility="ANY" specifier="ANY" prefix="" style="PASCAL_CASE" suffix="" />
<rule entity="TYPEDEF" visibility="ANY" specifier="ANY" prefix="" style="PASCAL_CASE" suffix="" />
<rule entity="UNION" visibility="ANY" specifier="ANY" prefix="" style="PASCAL_CASE" suffix="" />
<rule entity="CLASS_MEMBER_FUNCTION" visibility="ANY" specifier="ANY" prefix="" style="SNAKE_CASE" suffix="" />
<rule entity="STRUCT_MEMBER_FUNCTION" visibility="ANY" specifier="ANY" prefix="" style="SNAKE_CASE" suffix="" />
<rule entity="CLASS_MEMBER_FIELD" visibility="ANY" specifier="ANY" prefix="_" style="SNAKE_CASE" suffix="" />
<rule entity="STRUCT_MEMBER_FIELD" visibility="ANY" specifier="ANY" prefix="_" style="SNAKE_CASE" suffix="" />
<rule entity="GLOBAL_FUNCTION" visibility="ANY" specifier="ANY" prefix="" style="SNAKE_CASE" suffix="" />
<rule entity="GLOBAL_VARIABLE" visibility="ANY" specifier="ANY" prefix="" style="SCREAMING_SNAKE_CASE" suffix="" />
<rule entity="PARAMETER" visibility="ANY" specifier="ANY" prefix="" style="SNAKE_CASE" suffix="" />
<rule entity="LOCAL_VARIABLE" visibility="ANY" specifier="ANY" prefix="" style="SNAKE_CASE" suffix="" />
</rules>
</Objective-C-extensions>
<clangFormatSettings> <clangFormatSettings>
<option name="ENABLED" value="true" /> <option name="ENABLED" value="true" />
</clangFormatSettings> </clangFormatSettings>

View File

@@ -0,0 +1,6 @@
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Project Default" />
<inspection_tool class="OCInconsistentNaming" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
</profile>
</component>

1
.idea/misc.xml generated
View File

@@ -4,6 +4,7 @@
<component name="CidrRootsConfiguration"> <component name="CidrRootsConfiguration">
<excludeRoots> <excludeRoots>
<file path="$PROJECT_DIR$/cmake-build-debug" /> <file path="$PROJECT_DIR$/cmake-build-debug" />
<file path="$PROJECT_DIR$/cmake-build-relwithdebinfo" />
</excludeRoots> </excludeRoots>
</component> </component>
</project> </project>

View File

@@ -23,7 +23,7 @@ struct Cell {
virtual ~Cell() = 0; virtual ~Cell() = 0;
CellType _type; CellType _type;
bool live = false; std::atomic<bool> live = false;
}; };
struct NumAtomCell : public Cell { struct NumAtomCell : public Cell {
@@ -45,8 +45,8 @@ struct ConsCell : public Cell {
explicit ConsCell(Cell *car) : Cell(CellType::CONS), _car(car) {} explicit ConsCell(Cell *car) : Cell(CellType::CONS), _car(car) {}
ConsCell(Cell *car, Cell *cdr) : Cell(CellType::CONS), _car(car), _cdr(cdr) {} ConsCell(Cell *car, Cell *cdr) : Cell(CellType::CONS), _car(car), _cdr(cdr) {}
Cell *_car = nullptr; std::atomic<Cell *> _car = nullptr;
Cell *_cdr = nullptr; std::atomic<Cell *> _cdr = nullptr;
}; };
#endif//PSIL_CELL_H #endif//PSIL_CELL_H

View File

@@ -9,13 +9,13 @@
#include "MemoryContext.h" #include "MemoryContext.h"
namespace ConsUtils { namespace ConsUtils {
static inline MCHandle car(const MCHandle &cell) { return dynamic_cast<ConsCell &>(*cell)._car; } static inline MCHandle car(const MCHandle &cell) { return dynamic_cast<ConsCell &>(*cell)._car.load(); }
static inline MCHandle cdr(const MCHandle &cell) { return dynamic_cast<ConsCell &>(*cell)._cdr; } static inline MCHandle cdr(const MCHandle &cell) { return dynamic_cast<ConsCell &>(*cell)._cdr.load(); }
static inline CellValType val(const MCHandle &cell) { return dynamic_cast<NumAtomCell &>(*cell)._val; } static inline CellValType val(const MCHandle &cell) { return dynamic_cast<NumAtomCell &>(*cell)._val; }
static inline std::string_view strval(const MCHandle &cell) { return dynamic_cast<StrAtomCell &>(*cell)._val; } static inline std::string_view strval(const MCHandle &cell) { return dynamic_cast<StrAtomCell &>(*cell)._val; }
MCHandle cons(const MCHandle &car, const MCHandle &cdr); MCHandle cons(const MCHandle &car, const MCHandle &cdr);
MCHandle pop(MCHandle &from); MCHandle pop(MCHandle &from);
MCHandle push(MCHandle &to, const MCHandle &what); void push(MCHandle &to, const MCHandle &what);
void append(MCHandle to, const MCHandle &what); void append(MCHandle to, const MCHandle &what);
MCHandle makeNumCell(int64_t val); MCHandle makeNumCell(int64_t val);
MCHandle makeStrCell(std::string val); MCHandle makeStrCell(std::string val);

View File

@@ -6,7 +6,14 @@
#define PSIL_MEMORYCONTEXT_H #define PSIL_MEMORYCONTEXT_H
#include <atomic> #include <atomic>
#include <condition_variable>
#include <iostream>
#include <list> #include <list>
#include <map>
#include <mutex>
#include <queue>
#include <set>
#include <thread>
#include "Cell.h" #include "Cell.h"
@@ -19,53 +26,164 @@ public:
template<typename CT, typename... Args> template<typename CT, typename... Args>
Handle create_cell(Args... args) { Handle create_cell(Args... args) {
CT *cell = new CT(std::forward<Args>(args)...); return alloc_cell<CT>(std::forward<Args>(args)...);
_cells.emplace_back(cell);
return cell;
} }
template<> // template<>
Handle create_cell<NumAtomCell>(CellValType val) { // Handle create_cell<NumAtomCell>(CellValType val) {
if (numatom_index.contains(val)) // {
return numatom_index.at(val); // std::lock_guard l(_cells_lock);
// if (_numatom_index.contains(val)) {
NumAtomCell *cell = new NumAtomCell(val); // _numatom_index.at(val)->live = true;
_cells.emplace_back(cell); // return _numatom_index.at(val);
numatom_index.emplace(val, cell); // }
return cell; // }
} // Handle newc = alloc_cell<NumAtomCell>(val);
// {
template<> // std::lock_guard l(_cells_lock);
Handle create_cell<StrAtomCell>(std::string val) { // _numatom_index.emplace(val, newc.get());
if (stratom_index.contains(val)) // }
return stratom_index.at(val); // return newc;
// }
StrAtomCell *cell = new StrAtomCell(std::move(val)); //
_cells.emplace_back(cell); // template<>
stratom_index.emplace(cell->_val, cell); // Handle create_cell<StrAtomCell>(std::string val) {
return cell; // {
} // std::lock_guard l(_cells_lock);
// if (_stratom_index.contains(val)) {
// _stratom_index.at(val)->live = true;
// return _stratom_index.at(val);
// }
// }
// Handle newc = alloc_cell<StrAtomCell>(val);
// {
// std::lock_guard l(_cells_lock);
// _stratom_index.emplace(std::move(val), newc.get());
// }
// return newc;
// }
class Handle { class Handle {
public: public:
Handle(Cell *target) : _target(target) {} Handle(Cell *target);
~Handle();
Handle(Handle const &other);
Handle &operator=(Handle other);
Cell *operator->() const { return _target; } Cell *operator->() const { return _target; }
Cell &operator*() const { return *_target; } Cell &operator*() const { return *_target; }
Cell *get() const noexcept { return _target; } Cell *get() const noexcept { return _target; }
bool operator==(const Handle &rhs) const { bool operator==(const Handle &rhs) const {
return _target == rhs._target; if (_target == rhs._target) {
return true;
} else if ((_target != nullptr && rhs._target != nullptr) && (_target->_type == rhs._target->_type)) {
if (_target->_type == CellType::NUMATOM) {
return dynamic_cast<NumAtomCell &>(*_target)._val == dynamic_cast<NumAtomCell &>(*rhs._target)._val;
} else if (_target->_type == CellType::STRATOM) {
return dynamic_cast<StrAtomCell &>(*_target)._val == dynamic_cast<StrAtomCell &>(*rhs._target)._val;
}
}
return false;
} }
private: private:
Cell *_target = nullptr; Cell *_target = nullptr;
}; };
void request_gc_and_wait() {
std::unique_lock l(_gc_done_m);
_gc_done = false;
{
request_gc();
}
if (!_gc_done)
_gc_done_cv.wait(l, [&] { return _gc_done.load(); });
}
void request_gc() {
std::lock_guard l(_gc_request_m);
_gc_request = true;
_gc_request_cv.notify_all();
}
size_t cell_count() {
return _cells_num;
}
void run_dirty(const Handle &h, std::function<void()> f);
private: private:
std::list<Cell *> _cells; template<typename CT, typename... Args>
std::unordered_map<CellValType, Cell *> numatom_index; Handle alloc_cell(Args... args) {
std::unordered_map<std::string, Cell *> stratom_index;
size_t tcellnum;
{
std::lock_guard tmplg(_new_roots_lock);
tcellnum = _temp_cells.size();
}
if ((_cells_num + tcellnum) >= (_cell_limit)) {
// We might need to run GC twice as it has to process the messages;
std::cerr << "Running forced GC" << std::endl;
for (int i = 0; i < 3 && (_cells_num + tcellnum) >= (_cell_limit); i++) {
request_gc_and_wait();
{
std::lock_guard tmplg(_new_roots_lock);
tcellnum = _temp_cells.size();
}
}
if ((_cells_num + tcellnum) >= (_cell_limit)) {
throw std::runtime_error("Out of cells");
}
}
CT *cell = new CT(std::forward<Args>(args)...);
{
std::lock_guard tmplg(_new_roots_lock);
Handle ret(cell);
_temp_cells.emplace(cell);
if ((_cells_num + _temp_cells.size() + 1) >= (size_t) (_cell_limit / 2)) {
request_gc();
}
return ret;
}
}
void add_root(Cell *c);
void remove_root(Cell *c);
std::mutex _cells_lock;
std::set<Cell *> _cells;
std::atomic<size_t> _cells_num = 0;
std::set<Cell *> _temp_cells;
std::unordered_map<CellValType, Cell *> _numatom_index;
std::unordered_map<std::string, Cell *> _stratom_index;
static constexpr size_t _cell_limit = {50000};
void gc_thread_entry();
std::map<Cell *, int64_t> _roots;
std::map<Cell *, int64_t> _new_roots;
std::recursive_mutex _new_roots_lock;
std::set<Cell *> _gc_dirty_notif_queue;
std::mutex _gc_dirty_notif_queue_lock;
std::atomic<bool> _gc_request = false;
std::mutex _gc_request_m;
std::condition_variable _gc_request_cv;
std::atomic<bool> _gc_done = false;
std::mutex _gc_done_m;
std::condition_variable _gc_done_cv;
std::thread _gc_thread;
std::atomic<bool> _gc_thread_stop = false;
}; };
using MCHandle = MemoryContext::Handle; using MCHandle = MemoryContext::Handle;

View File

@@ -54,8 +54,6 @@ private:
MCHandle PUTNUM = ConsUtils::makeStrCell("PUTNUM"); MCHandle PUTNUM = ConsUtils::makeStrCell("PUTNUM");
MCHandle CONS = ConsUtils::makeStrCell("CONS"); MCHandle CONS = ConsUtils::makeStrCell("CONS");
// void gc();
}; };
#endif//PSIL_VM_H #endif//PSIL_VM_H

View File

@@ -17,19 +17,18 @@ MCHandle ConsUtils::pop(MCHandle &from) {
return ret; return ret;
} }
MCHandle ConsUtils::push(MCHandle &to, const MCHandle &what) { void ConsUtils::push(MCHandle &to, const MCHandle &what) {
to = cons(what, to); to = cons(what, to);
return to;
} }
void ConsUtils::append(MCHandle to, const MCHandle &what) { void ConsUtils::append(MCHandle to, const MCHandle &what) {
assert(to.get() != nullptr); assert(to.get() != nullptr);
if (car(to).get() == nullptr) { if (car(to).get() == nullptr) {
dynamic_cast<ConsCell &>(*to)._car = what.get(); setcar(to, what);
return; return;
} }
while (dynamic_cast<ConsCell &>(*to)._cdr != nullptr) to = cdr(to); while (cdr(to).get() != nullptr) to = cdr(to);
dynamic_cast<ConsCell &>(*to)._cdr = cons(what, nullptr).get(); setcdr(to, cons(what, nullptr));
} }
MCHandle ConsUtils::makeNumCell(int64_t val) { MCHandle ConsUtils::makeNumCell(int64_t val) {
@@ -41,9 +40,13 @@ MCHandle ConsUtils::makeStrCell(std::string val) {
} }
void ConsUtils::setcar(const MCHandle &to, const MCHandle &car) { void ConsUtils::setcar(const MCHandle &to, const MCHandle &car) {
CURRENT_MC.load()->run_dirty(to, [&] {
dynamic_cast<ConsCell &>(*to)._car = car.get(); dynamic_cast<ConsCell &>(*to)._car = car.get();
});
} }
void ConsUtils::setcdr(const MCHandle &to, const MCHandle &cdr) { void ConsUtils::setcdr(const MCHandle &to, const MCHandle &cdr) {
CURRENT_MC.load()->run_dirty(to, [&] {
dynamic_cast<ConsCell &>(*to)._cdr = cdr.get(); dynamic_cast<ConsCell &>(*to)._cdr = cdr.get();
});
} }

View File

@@ -4,14 +4,18 @@
#include "MemoryContext.h" #include "MemoryContext.h"
#include <chrono>
#include <exception> #include <exception>
#include <iostream> #include <iostream>
#include <unordered_set>
std::atomic<MemoryContext *> CURRENT_MC = nullptr; std::atomic<MemoryContext *> CURRENT_MC = nullptr;
MemoryContext::MemoryContext() { MemoryContext::MemoryContext() {
MemoryContext *expected = nullptr; MemoryContext *expected = nullptr;
if (!CURRENT_MC.compare_exchange_strong(expected, this)) throw std::runtime_error("MC already exists!"); if (!CURRENT_MC.compare_exchange_strong(expected, this)) throw std::runtime_error("MC already exists!");
_gc_thread = std::thread(std::bind(&MemoryContext::gc_thread_entry, this));
} }
MemoryContext::~MemoryContext() { MemoryContext::~MemoryContext() {
@@ -20,4 +24,204 @@ MemoryContext::~MemoryContext() {
std::cerr << "Global MC pointer was overwritten!" << std::endl; std::cerr << "Global MC pointer was overwritten!" << std::endl;
std::abort(); std::abort();
} }
_gc_thread_stop = true;
_gc_request_cv.notify_all();
_gc_thread.join();
}
MemoryContext::Handle::Handle(Cell *target) : _target(target) {
if (target != nullptr)
CURRENT_MC.load()->add_root(target);
}
MemoryContext::Handle::~Handle() {
if (_target != nullptr)
CURRENT_MC.load()->remove_root(_target);
}
MemoryContext::Handle::Handle(MemoryContext::Handle const &other) : _target(other._target) {
if (_target != nullptr)
CURRENT_MC.load()->add_root(_target);
}
MemoryContext::Handle &MemoryContext::Handle::operator=(MemoryContext::Handle other) {
std::swap(_target, other._target);
return *this;
}
void MemoryContext::add_root(Cell *c) {
{
std::lock_guard l(_new_roots_lock);
_new_roots[c]++;
// std::cerr << "new root: " << c << " T: " << static_cast<int>(c->_type) << " NUM: " << _new_roots[c] << "\n";
}
}
void MemoryContext::remove_root(Cell *c) {
{
std::lock_guard l(_new_roots_lock);
_new_roots[c]--;
// std::cerr << "del root: " << c << " T: " << static_cast<int>(c->_type) << " NUM: " << _new_roots[c] << "\n";
if (_new_roots[c] == 0) _new_roots.erase(c);
}
}
void MemoryContext::run_dirty(const MemoryContext::Handle &h, std::function<void()> f) {
{
std::lock_guard l(_gc_dirty_notif_queue_lock);
// std::cerr << "dirtied: " << h.get() << "\n";
_gc_dirty_notif_queue.emplace(h.get());
f();
}
}
void MemoryContext::gc_thread_entry() {
while (true) {
{
std::unique_lock l(_gc_request_m);
_gc_request_cv.wait(l, [&] { return _gc_request || _gc_thread_stop; });
}
if (_gc_thread_stop) return;
std::cerr << "gc start " << '\n';
std::unordered_set<Cell *> seenroots;
std::queue<Cell *> toVisit;
auto visitAll = [&]() {
while (!toVisit.empty()) {
Cell *c = toVisit.front();
toVisit.pop();
if (c == nullptr) continue;
if (!_cells.contains(c)) _cells.emplace(c);
if (c->live) continue;
c->live = true;
// std::cerr << "processing c " << c << " " << static_cast<int>(c->_type) << "\n";
if (c->_type == CellType::CONS) {
ConsCell &cc = dynamic_cast<ConsCell &>(*c);
// std::cerr << "processing car " << cc._car << "\n";
toVisit.emplace(cc._car);
// std::cerr << "real car " << toVisit.back() << "\n";
// std::cerr << "processing cdr " << cc._cdr << "\n";
toVisit.emplace(cc._cdr);
// std::cerr << "real cdr " << toVisit.back() << "\n";
}
}
};
{
std::lock_guard cl(_cells_lock);
auto start = std::chrono::high_resolution_clock::now();
{
decltype(_new_roots) new_roots;
{
decltype(_temp_cells) temp_cells;
{
std::lock_guard l(_new_roots_lock);
std::swap(new_roots, _new_roots);
std::swap(temp_cells, _temp_cells);
}
_cells.insert(temp_cells.begin(), temp_cells.end());
}
_cells_num = _cells.size();
for (auto const &r: new_roots) {
// std::cerr << "processing new " << r.first << " diff " << r.second << "\n";
if (r.second == 0) continue;
_roots[r.first] += r.second;
if (_roots[r.first] <= 0)
_roots.erase(r.first);
}
}
auto stop = std::chrono::high_resolution_clock::now();
std::cerr << "New roots time: " << std::chrono::duration_cast<std::chrono::microseconds>(stop - start).count() << "\n";
assert(std::none_of(_cells.begin(), _cells.end(), [](const auto &p) { return p->live.load(); }));
start = std::chrono::high_resolution_clock::now();
for (const auto &r: _roots) {
// std::cerr << "processing r " << r.first << " diff " << r.second << "\n";
seenroots.emplace(r.first);
toVisit.emplace(r.first);
}
visitAll();
stop = std::chrono::high_resolution_clock::now();
// std::cerr << "Scanned " << _roots.size() << " roots" << std::endl;
std::cerr << "Roots scan time: " << std::chrono::duration_cast<std::chrono::microseconds>(stop - start).count() << "\n";
{
decltype(_gc_dirty_notif_queue) dirtied;
std::unique_lock dql(_gc_dirty_notif_queue_lock);
std::swap(dirtied, _gc_dirty_notif_queue);
start = std::chrono::high_resolution_clock::now();
while (!dirtied.empty()) {
dql.unlock();
for (const auto &r: dirtied) {
// std::cerr << "processing dirty " << r << "\n";
if (seenroots.contains(r)) continue;
seenroots.emplace(r);
toVisit.emplace(r);
}
visitAll();
dirtied = {};
dql.lock();
std::swap(dirtied, _gc_dirty_notif_queue);
}
stop = std::chrono::high_resolution_clock::now();
std::cerr << "Dirty mark time: " << std::chrono::duration_cast<std::chrono::microseconds>(stop - start).count() << "\n";
assert(dql.owns_lock());
uint64_t freed = 0;
start = std::chrono::high_resolution_clock::now();
std::vector<Cell *> toremove;
for (const auto &l: _cells) {
if (!l->live) {
freed += 1;
// if (l->_type == CellType::NUMATOM) {
// std::cerr << "deleting num: " << l << "\n";
// _numatom_index.erase(dynamic_cast<NumAtomCell &>(*l)._val);
// } else if (l->_type == CellType::STRATOM) {
// std::cerr << "deleting str: " << l << "\n";
// _stratom_index.erase(dynamic_cast<StrAtomCell &>(*l)._val);
// }
assert(!_roots.contains(l));
// std::cerr << "deleting: " << l << "\n";
toremove.emplace_back(l);
delete l;
} else {
// std::cerr << "resetting num: " << l << "\n";
l->live = false;
}
}
for (const auto &l: toremove) {
_cells.erase(l);
}
stop = std::chrono::high_resolution_clock::now();
std::cerr << "Sweep time: " << std::chrono::duration_cast<std::chrono::microseconds>(stop - start).count() << "\n";
std::cerr << "GC Freed " << freed << " cells left: " << _cells_num << " \n";
}
{
std::unique_lock l(_gc_done_m);
std::unique_lock l2(_gc_request_m);
_gc_done = true;
_gc_request = false;
_gc_done_cv.notify_all();
}
}
}
} }

View File

@@ -86,7 +86,6 @@ void VM::step() {
_s = s; _s = s;
push(_s, ret); push(_s, ret);
// gc();
} else if (poppedH == DUM) { } else if (poppedH == DUM) {
push(_e, nullptr); push(_e, nullptr);
} else if (poppedH == RAP) { } else if (poppedH == RAP) {
@@ -135,41 +134,3 @@ void VM::step() {
assert(false); assert(false);
} }
} }
//void VM::gc() {
// std::function<void(ConsCell *)> visit = [&](ConsCell *c) {
// if (c == nullptr) return;
// if (c->live) return;
//
// c->live = true;
//
// if (c->_car) {
// if (c->_car->_type == CellType::CONS) visit(dynamic_cast<ConsCell *>(c->_car));
// c->_car->live = true;
// }
// if (c->_cdr) {
// if (c->_cdr->_type == CellType::CONS) visit(dynamic_cast<ConsCell *>(c->_cdr));
// c->_cdr->live = true;
// }
// };
// visit(_s);
// visit(_e);
// visit(_c);
// visit(_d);
//
// uint64_t freed = 0;
//
// _cells.remove_if([&](Cell *l) {
// bool ret = !l->live;
// if (ret) {
// freed += 1;
// delete l;
// } else {
// l->live = false;
// }
// return ret;
// });
//
// std::cout << "GC Freed " << freed << std::endl;
//}
//

View File

@@ -18,6 +18,17 @@ target_link_libraries(
GTest::gtest_main GTest::gtest_main
) )
add_executable(
GCTest
GCTest.cpp
)
target_link_libraries(
GCTest
vm
GTest::gtest_main
)
include(GoogleTest) include(GoogleTest)
gtest_discover_tests(VMTest) gtest_discover_tests(VMTest)
gtest_discover_tests(VMWithParserTest) gtest_discover_tests(VMWithParserTest)
gtest_discover_tests(GCTest)

92
test/vm/GCTest.cpp Normal file
View File

@@ -0,0 +1,92 @@
//
// Created by Stepan Usatiuk on 26.12.2023.
//
#include <gtest/gtest.h>
#include "ConsUtils.h"
#include "MemoryContext.h"
using namespace ConsUtils;
TEST(GCTest, GCTest) {
MemoryContext mc;
{
MCHandle c = cons(nullptr, nullptr);
mc.request_gc_and_wait();
append(c, makeNumCell(1));
append(c, makeNumCell(2));
mc.request_gc_and_wait();
EXPECT_EQ(val(car(c)), 1);
EXPECT_EQ(val(car(cdr(c))), 2);
}
mc.request_gc_and_wait();
mc.request_gc_and_wait();
mc.request_gc_and_wait();
EXPECT_EQ(mc.cell_count(), 0);
{
MCHandle c = cons(nullptr, nullptr);
mc.request_gc_and_wait();
push(c, makeNumCell(1));
push(c, makeNumCell(2));
mc.request_gc_and_wait();
EXPECT_EQ(val(car(c)), 2);
EXPECT_EQ(val(car(cdr(c))), 1);
}
mc.request_gc_and_wait();
mc.request_gc_and_wait();
mc.request_gc_and_wait();
EXPECT_EQ(mc.cell_count(), 0);
}
TEST(GCTest, GCTestAppend) {
MemoryContext mc;
for (int i = 0; i < 25000; i++) {
MCHandle c = cons(nullptr, nullptr);
mc.request_gc();
append(c, makeNumCell(1));
mc.request_gc();
EXPECT_EQ(val(car(c)), 1);
}
mc.request_gc_and_wait();
mc.request_gc_and_wait();
mc.request_gc_and_wait();
EXPECT_EQ(mc.cell_count(), 0);
}
TEST(GCTest, GCTestPop) {
MemoryContext mc;
{
MCHandle c = cons(nullptr, nullptr);
static constexpr int test_size = 20000;
for (int i = 0; i < test_size; i++) {
mc.request_gc();
push(c, makeNumCell(i));
}
for (int i = test_size - 1; i >= 0; i--) {
mc.request_gc();
EXPECT_EQ(i, val(pop(c)));
}
}
mc.request_gc_and_wait();
mc.request_gc_and_wait();
mc.request_gc_and_wait();
EXPECT_EQ(mc.cell_count(), 0);
}
TEST(GCTest, GCTestAppend2) {
MemoryContext mc;
MCHandle c = cons(nullptr, nullptr);
static constexpr int test_size = 2000;
for (int i = 0; i < test_size; i++) {
mc.request_gc();
append(c, makeNumCell(i));
}
for (int i = 0; i < test_size; i++) {
mc.request_gc();
EXPECT_EQ(i, val(pop(c)));
}
mc.request_gc_and_wait();
mc.request_gc_and_wait();
mc.request_gc_and_wait();
EXPECT_EQ(mc.cell_count(), 0);
}

View File

@@ -52,15 +52,19 @@ TEST(VMWithParserTest, BasicFunction) {
TEST(VMWithParserTest, RecFunction) { TEST(VMWithParserTest, RecFunction) {
std::stringstream ssin; std::stringstream ssin;
std::stringstream ssout; std::stringstream ssout;
{
MemoryContext mc; MemoryContext mc;
{
VM vm(ssin, ssout); VM vm(ssin, ssout);
Parser parser; Parser parser;
parser.loadStr( parser.loadStr(
"( DUM NIL LDF ( LD ( 1 . 1 ) SEL ( LD ( 1 . 1 ) LDC -1 ADD SEL ( NIL LD ( 1 . 1 ) LDC -1 ADD CONS LD ( 2 . 1 ) AP NIL LD ( 1 . 1 ) LDC -2 ADD CONS LD ( 2 . 1 ) AP ADD JOIN ) ( LDC 1 JOIN ) JOIN ) ( LDC 0 JOIN ) RET ) CONS LDF ( NIL LDC 10 CONS LD ( 1 . 1 ) AP RET ) RAP PUTNUM STOP )"); "( DUM NIL LDF ( LD ( 1 . 1 ) SEL ( LD ( 1 . 1 ) LDC -1 ADD SEL ( NIL LD ( 1 . 1 ) LDC -1 ADD CONS LD ( 2 . 1 ) AP NIL LD ( 1 . 1 ) LDC -2 ADD CONS LD ( 2 . 1 ) AP ADD JOIN ) ( LDC 1 JOIN ) JOIN ) ( LDC 0 JOIN ) RET ) CONS LDF ( NIL LDC 20 CONS LD ( 1 . 1 ) AP RET ) RAP PUTNUM STOP )");
vm.loadControl(parser.parseExpr()); vm.loadControl(parser.parseExpr());
vm.run(); vm.run();
} }
mc.request_gc_and_wait();
mc.request_gc_and_wait();
mc.request_gc_and_wait();
EXPECT_EQ(mc.cell_count(), 0);
ssout.flush(); ssout.flush();
EXPECT_EQ(ssout.str(), "55"); EXPECT_EQ(ssout.str(), "6765");
} }