summaryrefslogtreecommitdiffstats
path: root/src/inkgc
diff options
context:
space:
mode:
Diffstat (limited to 'src/inkgc')
-rw-r--r--src/inkgc/CMakeLists.txt15
-rw-r--r--src/inkgc/README8
-rw-r--r--src/inkgc/gc-alloc.h71
-rw-r--r--src/inkgc/gc-core.h202
-rw-r--r--src/inkgc/gc-managed.h60
-rw-r--r--src/inkgc/gc-soft-ptr.h70
-rw-r--r--src/inkgc/gc.cpp313
7 files changed, 739 insertions, 0 deletions
diff --git a/src/inkgc/CMakeLists.txt b/src/inkgc/CMakeLists.txt
new file mode 100644
index 0000000..f0ec2ff
--- /dev/null
+++ b/src/inkgc/CMakeLists.txt
@@ -0,0 +1,15 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+set(libgc_SRC
+ gc.cpp
+
+ # -------
+ # Headers
+ gc-alloc.h
+ ../gc-anchored.h
+ gc-core.h
+ gc-managed.h
+ gc-soft-ptr.h
+)
+
+add_inkscape_lib(gc_LIB "${libgc_SRC}")
+# add_inkscape_source("${libgc_SRC}")
diff --git a/src/inkgc/README b/src/inkgc/README
new file mode 100644
index 0000000..c1104bc
--- /dev/null
+++ b/src/inkgc/README
@@ -0,0 +1,8 @@
+
+
+This directory contains code related to the Boehm Garbage Collector.
+
+To do:
+
+* Replace this code with C++11 smart pointers.
+
diff --git a/src/inkgc/gc-alloc.h b/src/inkgc/gc-alloc.h
new file mode 100644
index 0000000..8eaec94
--- /dev/null
+++ b/src/inkgc/gc-alloc.h
@@ -0,0 +1,71 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/** @file
+ * Inkscape::GC::Alloc - GC-aware STL allocator
+ *//*
+ * Authors:
+ * see git history
+ * MenTaLguY <mental@rydia.net>
+ *
+ * Copyright (C) 2018 Authors
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+
+#ifndef SEEN_INKSCAPE_GC_ALLOC_H
+#define SEEN_INKSCAPE_GC_ALLOC_H
+
+#include <cstddef>
+#include "inkgc/gc-core.h"
+
+namespace Inkscape {
+
+namespace GC {
+
+template <typename T, CollectionPolicy collect>
+class Alloc {
+public:
+ typedef T value_type;
+ typedef T *pointer;
+ typedef std::size_t size_type;
+
+ template <typename U>
+ struct rebind { typedef Alloc<U, collect> other; };
+
+ Alloc() = default;
+ template <typename U> Alloc(Alloc<U, collect> const &) {}
+
+ pointer allocate(size_type count, void const * =nullptr) {
+ return static_cast<pointer>(::operator new(count * sizeof(T), SCANNED, collect));
+ }
+
+ void deallocate(pointer p, size_type) { ::operator delete(p, GC); }
+};
+
+// allocators with the same collection policy are interchangeable
+
+template <typename T1, typename T2,
+ CollectionPolicy collect1, CollectionPolicy collect2>
+bool operator==(Alloc<T1, collect1> const &, Alloc<T2, collect2> const &) {
+ return collect1 == collect2;
+}
+
+template <typename T1, typename T2,
+ CollectionPolicy collect1, CollectionPolicy collect2>
+bool operator!=(Alloc<T1, collect1> const &, Alloc<T2, collect2> const &) {
+ return collect1 != collect2;
+}
+
+}
+
+}
+
+#endif
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/src/inkgc/gc-core.h b/src/inkgc/gc-core.h
new file mode 100644
index 0000000..cbd4b7e
--- /dev/null
+++ b/src/inkgc/gc-core.h
@@ -0,0 +1,202 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/** @file
+ * @brief Wrapper for Boehm GC
+ */
+/* Authors:
+ * MenTaLguY <mental@rydia.net>
+ *
+ * Copyright (C) 2004 MenTaLguY
+ *
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+
+#ifndef SEEN_INKSCAPE_GC_CORE_H
+#define SEEN_INKSCAPE_GC_CORE_H
+
+#include <new>
+#include <cstdlib>
+
+# include <gc.h>
+
+namespace Inkscape {
+namespace GC {
+
+enum ScanPolicy {
+ SCANNED,
+ ATOMIC
+};
+
+enum CollectionPolicy {
+ AUTO,
+ MANUAL
+};
+
+enum Delete {
+ GC
+};
+
+typedef void (*CleanupFunc)(void *mem, void *data);
+
+struct Ops {
+ void (*do_init)();
+ void *(*malloc)(std::size_t size);
+ void *(*malloc_atomic)(std::size_t size);
+ void *(*malloc_uncollectable)(std::size_t size);
+ void *(*malloc_atomic_uncollectable)(std::size_t size);
+ void *(*base)(void *ptr);
+ void (*register_finalizer_ignore_self)(void *base,
+ CleanupFunc func, void *data,
+ CleanupFunc *old_func,
+ void **old_data);
+ int (*general_register_disappearing_link)(void **p_ptr,
+ void const *base);
+ int (*unregister_disappearing_link)(void **p_ptr);
+ std::size_t (*get_heap_size)();
+ std::size_t (*get_free_bytes)();
+ void (*gcollect)();
+ void (*enable)();
+ void (*disable)();
+ void (*free)(void *ptr);
+};
+
+struct Core {
+public:
+ static void init();
+ static inline void *malloc(std::size_t size) {
+ return _ops.malloc(size);
+ }
+ static inline void *malloc_atomic(std::size_t size) {
+ return _ops.malloc_atomic(size);
+ }
+ static inline void *malloc_uncollectable(std::size_t size) {
+ return _ops.malloc_uncollectable(size);
+ }
+ static inline void *malloc_atomic_uncollectable(std::size_t size) {
+ return _ops.malloc_atomic_uncollectable(size);
+ }
+ static inline void *base(void *ptr) {
+ return _ops.base(ptr);
+ }
+ static inline void register_finalizer_ignore_self(void *base,
+ CleanupFunc func,
+ void *data,
+ CleanupFunc *old_func,
+ void **old_data)
+ {
+ return _ops.register_finalizer_ignore_self(base, func, data,
+ old_func, old_data);
+ }
+ static inline int general_register_disappearing_link(void **p_ptr,
+ void *base)
+ {
+ return _ops.general_register_disappearing_link(p_ptr, base);
+ }
+ static inline int unregister_disappearing_link(void **p_ptr) {
+ return _ops.unregister_disappearing_link(p_ptr);
+ }
+ static inline std::size_t get_heap_size() {
+ return _ops.get_heap_size();
+ }
+ static inline std::size_t get_free_bytes() {
+ return _ops.get_free_bytes();
+ }
+ static inline void gcollect() {
+ _ops.gcollect();
+ }
+ static inline void enable() {
+ _ops.enable();
+ }
+ static inline void disable() {
+ _ops.disable();
+ }
+ static inline void free(void *ptr) {
+ return _ops.free(ptr);
+ }
+private:
+ static Ops _ops;
+};
+
+inline void init() {
+ Core::init();
+}
+
+void request_early_collection();
+
+}
+}
+
+inline void *operator new(std::size_t size,
+ Inkscape::GC::ScanPolicy scan,
+ Inkscape::GC::CollectionPolicy collect,
+ Inkscape::GC::CleanupFunc cleanup=nullptr,
+ void *data=nullptr)
+{
+ using namespace Inkscape::GC;
+
+ void *mem;
+ if ( collect == AUTO ) {
+ if ( scan == SCANNED ) {
+ mem = Core::malloc(size);
+ } else {
+ mem = Core::malloc_atomic(size);
+ }
+ } else {
+ if ( scan == SCANNED ) {
+ mem = Core::malloc_uncollectable(size);
+ } else {
+ mem = Core::malloc_atomic_uncollectable(size);
+ }
+ }
+ if (!mem) {
+ throw std::bad_alloc();
+ }
+ if (cleanup) {
+ Core::register_finalizer_ignore_self(mem, cleanup, data, nullptr, nullptr);
+ }
+ return mem;
+}
+
+inline void *operator new(std::size_t size,
+ Inkscape::GC::ScanPolicy scan,
+ Inkscape::GC::CleanupFunc cleanup=nullptr,
+ void *data=nullptr)
+{
+ return operator new(size, scan, Inkscape::GC::AUTO, cleanup, data);
+}
+
+inline void *operator new[](std::size_t size,
+ Inkscape::GC::ScanPolicy scan,
+ Inkscape::GC::CollectionPolicy collect,
+ Inkscape::GC::CleanupFunc cleanup=nullptr,
+ void *data=nullptr)
+{
+ return operator new(size, scan, collect, cleanup, data);
+}
+
+inline void *operator new[](std::size_t size,
+ Inkscape::GC::ScanPolicy scan,
+ Inkscape::GC::CleanupFunc cleanup=nullptr,
+ void *data=nullptr)
+{
+ return operator new[](size, scan, Inkscape::GC::AUTO, cleanup, data);
+}
+
+inline void operator delete(void *mem, Inkscape::GC::Delete) {
+ Inkscape::GC::Core::free(mem);
+}
+
+inline void operator delete[](void *mem, Inkscape::GC::Delete) {
+ operator delete(mem, Inkscape::GC::GC);
+}
+
+#endif
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/src/inkgc/gc-managed.h b/src/inkgc/gc-managed.h
new file mode 100644
index 0000000..2c2ef7c
--- /dev/null
+++ b/src/inkgc/gc-managed.h
@@ -0,0 +1,60 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/** @file
+ * Base class for GC-managed objects
+ *//*
+ * Authors:
+ * see git history
+ * MenTaLguY <mental@rydia.net>
+ *
+ * Copyright (C) 2018 Authors
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+
+#ifndef SEEN_INKSCAPE_GC_MANAGED_H
+#define SEEN_INKSCAPE_GC_MANAGED_H
+
+#include "inkgc/gc-core.h"
+
+namespace Inkscape {
+
+namespace GC {
+
+/** @brief A base class for objects for whom the normal new and delete
+ * operators should use the garbage-collected allocator
+ */
+template <ScanPolicy default_scan=SCANNED,
+ CollectionPolicy default_collect=AUTO>
+class Managed {
+public:
+ void *operator new(std::size_t size,
+ ScanPolicy scan=default_scan,
+ CollectionPolicy collect=default_collect)
+ {
+ return ::operator new(size, scan, collect);
+ }
+
+ void *operator new[](std::size_t size,
+ ScanPolicy scan=default_scan,
+ CollectionPolicy collect=default_collect)
+ {
+ return ::operator new[](size, scan, collect);
+ }
+
+ void operator delete(void *p) { return ::operator delete(p, GC); }
+};
+
+}
+
+}
+
+#endif
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/src/inkgc/gc-soft-ptr.h b/src/inkgc/gc-soft-ptr.h
new file mode 100644
index 0000000..b245d81
--- /dev/null
+++ b/src/inkgc/gc-soft-ptr.h
@@ -0,0 +1,70 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/** @file
+ * TODO: insert short description here
+ *//*
+ * Authors:
+ * see git history
+ * MenTaLguY <mental@rydia.net>
+ *
+ * Copyright (C) 2018 Authors
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+
+#ifndef SEEN_INKSCAPE_GC_SOFT_PTR_H
+#define SEEN_INKSCAPE_GC_SOFT_PTR_H
+
+#include "inkgc/gc-core.h"
+
+namespace Inkscape {
+
+namespace GC {
+
+/**
+ * A class for pointers which can be automatically cleared to break
+ * finalization cycles.
+ */
+template <typename T>
+class soft_ptr {
+public:
+ soft_ptr(T *pointer=nullptr) : _pointer(pointer) {
+ _register();
+ }
+
+ operator T *() const { return static_cast<T *>(_pointer); }
+ T &operator*() const { return *static_cast<T *>(_pointer); }
+ T *operator->() const { return static_cast<T *>(_pointer); }
+ T &operator[](int i) const { return static_cast<T *>(_pointer)[i]; }
+
+ soft_ptr &operator=(T *pointer) {
+ _pointer = pointer;
+ return *this;
+ }
+
+ // default copy
+
+private:
+ void _register() {
+ void *base=Core::base(this);
+ if (base) {
+ Core::general_register_disappearing_link(&_pointer, base);
+ }
+ }
+
+ void *_pointer;
+};
+
+}
+
+}
+
+#endif
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/src/inkgc/gc.cpp b/src/inkgc/gc.cpp
new file mode 100644
index 0000000..a290423
--- /dev/null
+++ b/src/inkgc/gc.cpp
@@ -0,0 +1,313 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/** @file
+ * Wrapper for Boehm GC.
+ */
+/* Authors:
+ * MenTaLguY <mental@rydia.net>
+ *
+ * Copyright (C) 2004 MenTaLguY
+ *
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+
+#include "inkgc/gc-core.h"
+#include <stdexcept>
+#include <cstring>
+#include <string>
+#include <glib.h>
+#include <sigc++/functors/ptr_fun.h>
+#include <glibmm/main.h>
+#include <cstddef>
+
+namespace Inkscape {
+namespace GC {
+
+namespace {
+
+void display_warning(char *msg, GC_word arg) {
+ g_warning(msg, arg);
+}
+
+void do_init() {
+ GC_set_no_dls(1);
+ GC_set_all_interior_pointers(1);
+ GC_set_finalize_on_demand(0);
+
+ GC_INIT();
+
+ GC_set_warn_proc(&display_warning);
+}
+
+void *debug_malloc(std::size_t size) {
+ return GC_debug_malloc(size, GC_EXTRAS);
+}
+
+void *debug_malloc_atomic(std::size_t size) {
+ return GC_debug_malloc_atomic(size, GC_EXTRAS);
+}
+
+void *debug_malloc_uncollectable(std::size_t size) {
+ return GC_debug_malloc_uncollectable(size, GC_EXTRAS);
+}
+
+void *debug_malloc_atomic_uncollectable(std::size_t size) {
+ return GC_debug_malloc_uncollectable(size, GC_EXTRAS);
+}
+
+std::ptrdiff_t compute_debug_base_fixup() {
+ char *base=reinterpret_cast<char *>(GC_debug_malloc(1, GC_EXTRAS));
+ char *real_base=reinterpret_cast<char *>(GC_base(base));
+ GC_debug_free(base);
+ return base - real_base;
+}
+
+inline std::ptrdiff_t const &debug_base_fixup() {
+ static std::ptrdiff_t fixup=compute_debug_base_fixup();
+ return fixup;
+}
+
+void *debug_base(void *ptr) {
+ char *base=reinterpret_cast<char *>(GC_base(ptr));
+ return base + debug_base_fixup();
+}
+
+int debug_general_register_disappearing_link(void **p_ptr, void const *base) {
+ char const *real_base = reinterpret_cast<char const *>(base) - debug_base_fixup();
+#if (GC_MAJOR_VERSION >= 7 && GC_MINOR_VERSION >= 4)
+ return GC_general_register_disappearing_link(p_ptr, real_base);
+#else // compatibility with older Boehm GC versions
+ return GC_general_register_disappearing_link(p_ptr, const_cast<char *>(real_base));
+#endif
+}
+
+void dummy_do_init() {}
+
+void *dummy_base(void *) { return nullptr; }
+
+void dummy_register_finalizer(void *, CleanupFunc, void *,
+ CleanupFunc *old_func, void **old_data)
+{
+ if (old_func) {
+ *old_func = nullptr;
+ }
+ if (old_data) {
+ *old_data = nullptr;
+ }
+}
+
+int dummy_general_register_disappearing_link(void **, void const *) { return false; }
+
+int dummy_unregister_disappearing_link(void **/*link*/) { return false; }
+
+std::size_t dummy_get_heap_size() { return 0; }
+
+std::size_t dummy_get_free_bytes() { return 0; }
+
+void dummy_gcollect() {}
+
+void dummy_enable() {}
+
+void dummy_disable() {}
+
+Ops enabled_ops = {
+ &do_init,
+ &GC_malloc,
+ &GC_malloc_atomic,
+ &GC_malloc_uncollectable,
+ &GC_malloc_atomic_uncollectable,
+ &GC_base,
+ &GC_register_finalizer_ignore_self,
+#if (GC_MAJOR_VERSION >= 7 && GC_MINOR_VERSION >= 4)
+ &GC_general_register_disappearing_link,
+#else // compatibility with older Boehm GC versions
+ (int (*)(void**, const void*))(&GC_general_register_disappearing_link),
+#endif
+ &GC_unregister_disappearing_link,
+ &GC_get_heap_size,
+ &GC_get_free_bytes,
+ &GC_gcollect,
+ &GC_enable,
+ &GC_disable,
+ &GC_free
+};
+
+Ops debug_ops = {
+ &do_init,
+ &debug_malloc,
+ &debug_malloc_atomic,
+ &debug_malloc_uncollectable,
+ &debug_malloc_atomic_uncollectable,
+ &debug_base,
+ &GC_debug_register_finalizer_ignore_self,
+ &debug_general_register_disappearing_link,
+ &GC_unregister_disappearing_link,
+ &GC_get_heap_size,
+ &GC_get_free_bytes,
+ &GC_gcollect,
+ &GC_enable,
+ &GC_disable,
+ &GC_debug_free
+};
+
+Ops disabled_ops = {
+ &dummy_do_init,
+ &std::malloc,
+ &std::malloc,
+ &std::malloc,
+ &std::malloc,
+ &dummy_base,
+ &dummy_register_finalizer,
+ &dummy_general_register_disappearing_link,
+ &dummy_unregister_disappearing_link,
+ &dummy_get_heap_size,
+ &dummy_get_free_bytes,
+ &dummy_gcollect,
+ &dummy_enable,
+ &dummy_disable,
+ &std::free
+};
+
+class InvalidGCModeError : public std::runtime_error {
+public:
+ InvalidGCModeError(const char *mode)
+ : runtime_error(std::string("Unknown GC mode \"") + mode + "\"")
+ {}
+};
+
+Ops const &get_ops() {
+ char *mode_string=std::getenv("_INKSCAPE_GC");
+ if (mode_string) {
+ if (!std::strcmp(mode_string, "enable")) {
+ return enabled_ops;
+ } else if (!std::strcmp(mode_string, "debug")) {
+ return debug_ops;
+ } else if (!std::strcmp(mode_string, "disable")) {
+ return disabled_ops;
+ } else {
+ throw InvalidGCModeError(mode_string);
+ }
+ } else {
+ return enabled_ops;
+ }
+}
+
+void die_because_not_initialized() {
+ g_error("Attempt to use GC allocator before call to Inkscape::GC::init()");
+}
+
+void *stub_malloc(std::size_t) {
+ die_because_not_initialized();
+ return nullptr;
+}
+
+void *stub_base(void *) {
+ die_because_not_initialized();
+ return nullptr;
+}
+
+void stub_register_finalizer_ignore_self(void *, CleanupFunc, void *,
+ CleanupFunc *, void **)
+{
+ die_because_not_initialized();
+}
+
+int stub_general_register_disappearing_link(void **, void const *) {
+ die_because_not_initialized();
+ return 0;
+}
+
+int stub_unregister_disappearing_link(void **) {
+ die_because_not_initialized();
+ return 0;
+}
+
+std::size_t stub_get_heap_size() {
+ die_because_not_initialized();
+ return 0;
+}
+
+std::size_t stub_get_free_bytes() {
+ die_because_not_initialized();
+ return 0;
+}
+
+void stub_gcollect() {
+ die_because_not_initialized();
+}
+
+void stub_enable() {
+ die_because_not_initialized();
+}
+
+void stub_disable() {
+ die_because_not_initialized();
+}
+
+void stub_free(void *) {
+ die_because_not_initialized();
+}
+
+}
+
+Ops Core::_ops = {
+ nullptr,
+ &stub_malloc,
+ &stub_malloc,
+ &stub_malloc,
+ &stub_malloc,
+ &stub_base,
+ &stub_register_finalizer_ignore_self,
+ &stub_general_register_disappearing_link,
+ &stub_unregister_disappearing_link,
+ &stub_get_heap_size,
+ &stub_get_free_bytes,
+ &stub_gcollect,
+ &stub_enable,
+ &stub_disable,
+ &stub_free
+};
+
+void Core::init() {
+ try {
+ _ops = get_ops();
+ } catch (InvalidGCModeError &e) {
+ g_warning("%s; enabling normal collection", e.what());
+ _ops = enabled_ops;
+ }
+
+ _ops.do_init();
+}
+
+
+namespace {
+
+bool collection_requested=false;
+bool collection_task() {
+ Core::gcollect();
+ Core::gcollect();
+ collection_requested=false;
+ return false;
+}
+
+}
+
+void request_early_collection() {
+ if (!collection_requested) {
+ collection_requested=true;
+ Glib::signal_idle().connect(sigc::ptr_fun(&collection_task));
+ }
+}
+
+}
+}
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :