C++ Logo

std-proposals

Advanced search

Re: [std-proposals] Save Cascading Free of Smart Pointers

From: Chris Ryan <chrisr98008_at_[hidden]>
Date: Tue, 16 Jul 2024 15:15:46 -0700
Recursive deleting has been an issue forever with many list type classes.
Much of the time to handle this type of issue they use a manager class to
construct/insert/manage/delete nodes. To delete multiple nodes or the
whole list the manager class does a manual tail unwinding to convert a
recursive process into an iterative process.

It would be inappropriate for the language to change the
deterministic destruction order. The language could/should not do that
because it would require actually understanding the nature of the
hierarchy and relationship of the structures and data.

Chris++;

On Tue, Jul 16, 2024 at 2:14 PM M.C.A. (Marco) Devillers via Std-Proposals <
std-proposals_at_[hidden]> wrote:

> I came up against this and wanted to float an idea. It doesn't seem
> popular but I think I am right. This should be resolved.
>
> Document number: xxx
> Date: 2024-7-16
> Audience: GCC email list
> Reply-to: marco.devillers_at_[hidden], std-proposals_at_[hidden]
>
> I.
> II. Introduction
>
> Because C++ smart pointers are based on RAII it is easy to trigger an
> overflow of the C stack since destructors call each other. Smart
> pointers are supposed to be safe, smart pointers are likely to be used
> extensively in the future, and this behaviour could make a large
> number of C++ programs core dump unexpectedly.
> This proposal is to remove this behaviour from GCCs standard library
> and also showcases a small trick by which that can be done.
>
> III. Motivation and Scope
>
> We all want smart pointers since they allow for easy and safe memory
> management, this desire is only expected to increase over the
> following decades.
>
> However due to RAII semantics it's easy to trigger an overflow of the
> C stack once garbage goes out of scope. Observe the following trivial
> program:
>
> #include <iostream>
> #include <memory>
>
> struct list_node {
> using ptr = std::unique_ptr<list_node>;
> ~list_node() {
> }
>
> int x;
> ptr next;
> };
>
> int main() {
> list_node::ptr next = nullptr;
>
> for(int i = 0; i < 100000; ++i) { // decrease value to see it not
> segfault
> next = list_node::ptr(new list_node{i, std::move(next)});
> }
> }
>
> Cascading frees will make this program core dump depending the size of
> the list. Please note, that that program will segfault on for current
> data-driven days relatively tiny sizes.
>
> I give it to you that this is unsafe and unwanted behaviour since now
> every program employing nested structures can core dump easily and
> unexpectedly. For example: GUIs, editors, parsers, transformers, etc.
> The problem is only expected to worsen with more developers using safe
> pointers.
>
> The proposal is to remove this behaviour from the GCC standard library
> by hardening smart pointers in the following manner: instead of
> calling garbage recursively garbage is first offloaded to a stack and
> that stack destroys objects until it is empty. Or by any other means
> that removes segfaulting cascading frees.
>
> A reference implementation (not concurrent) for unique pointers is
> given as an Addendum. (The code is due to Alipha on libera.net).
>
> IV. Impact On the Standard
>
> This shouldn't impact other parts of the standard.
>
> V. Design Decisions
>
> Offload destructor calls to an explicit stack to make these calls
> sequential instead of recursive. A non-concurrent implementation of
> unique pointers is given below.
>
> VI. Technical Specifications
>
> This shouldn't change anything.
>
> VII. Acknowledgements
>
> This cascading free behaviour was noticed during the development of
> the Egel language interpreter, and the author has great interest in
> having this resolved. The problem was discussed on various channels
> and together with Alipha on libera.net a solution was developed. The
> idea was subsequently floated on the GCC mailing list where it was
> forwarded.
>
> VIII. Addendum
>
> #include <iostream>
> #include <functional>
> #include <vector>
> #include <utility>
> #include <memory>
>
> namespace detail {
> inline bool doing_cleanup = false;
> inline std::vector<std::function<void()>> ptr_cleanup;
> }
>
> template<typename T>
> class safe_unique_ptr {
> public:
> safe_unique_ptr() : ptr() {}
> safe_unique_ptr(T *p) : ptr(p) {}
>
> safe_unique_ptr(safe_unique_ptr &&other) :
> ptr(std::exchange(other.ptr, nullptr)) {}
>
> safe_unique_ptr &operator=(safe_unique_ptr &&other) {
> cleanup(ptr);
> ptr = std::exchange(other.ptr, nullptr);
> return *this;
> }
>
> T &operator*() const { return *ptr; }
> T *operator->() const { return ptr; }
>
> ~safe_unique_ptr() { cleanup(ptr); }
>
> private:
> void cleanup(T *p) {
> using namespace detail;
>
> if(!p) return;
>
> if(!doing_cleanup) {
> doing_cleanup = true;
> delete p;
>
> while(!ptr_cleanup.empty()) {
> std::function<void()> deleter = ptr_cleanup.back();
> ptr_cleanup.pop_back();
> deleted();
> }
>
> doing_cleanup = false;
> } else {
> ptr_cleanup.push_back([p]() { delete p; });
> }
> }
>
> T *ptr;
> };
>
> struct list_node {
> using ptr = safe_unique_ptr<list_node>;
> ~list_node() {}
>
> int x;
> ptr next;
> };
> --
> Std-Proposals mailing list
> Std-Proposals_at_[hidden]
> https://lists.isocpp.org/mailman/listinfo.cgi/std-proposals
>

Received on 2024-07-16 22:16:00