diff options
Diffstat (limited to 'src/inkgc')
-rw-r--r-- | src/inkgc/CMakeLists.txt | 15 | ||||
-rw-r--r-- | src/inkgc/README | 8 | ||||
-rw-r--r-- | src/inkgc/gc-alloc.h | 75 | ||||
-rw-r--r-- | src/inkgc/gc-core.h | 202 | ||||
-rw-r--r-- | src/inkgc/gc-managed.h | 60 | ||||
-rw-r--r-- | src/inkgc/gc-soft-ptr.h | 70 | ||||
-rw-r--r-- | src/inkgc/gc.cpp | 313 |
7 files changed, 743 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..e84399f --- /dev/null +++ b/src/inkgc/gc-alloc.h @@ -0,0 +1,75 @@ +// 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, + ScanPolicy scan=SCANNED, + CollectionPolicy collect=AUTO> +class Alloc { +public: + typedef T value_type; + typedef T *pointer; + typedef std::size_t size_type; + + template <typename U> + struct rebind { typedef Alloc<U, scan, collect> other; }; + + Alloc() = default; + template <typename U> Alloc(Alloc<U, scan, collect> const &) {} + + pointer allocate(size_type count, void const * =nullptr) { + return static_cast<pointer>(::operator new(count * sizeof(T), scan, collect)); + } + + void deallocate(pointer p, size_type) { ::operator delete(p, GC); } +}; + +// allocators with the same scan and collection policy are interchangeable + +template <typename T1, typename T2, + ScanPolicy scan1, ScanPolicy scan2, + CollectionPolicy collect1, CollectionPolicy collect2> +bool operator==(Alloc<T1, scan1, collect1> const &, Alloc<T2, scan2, collect2> const &) { + return collect1 == collect2 && scan1 == scan2; +} + +template <typename T1, typename T2, + ScanPolicy scan1, ScanPolicy scan2, + CollectionPolicy collect1, CollectionPolicy collect2> +bool operator!=(Alloc<T1, scan1, collect1> const &, Alloc<T2, scan2, collect2> const &) { + return collect1 != collect2 || scan1 != scan2; +} + +} + +} + +#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 : |