Jump to page: 1 2
Thread overview
Is it possible to avoid call to destructor for structs?
Sep 24, 2017
Haridas
Sep 24, 2017
Adam D. Ruppe
Sep 24, 2017
Haridas
Sep 24, 2017
Haridas
Sep 24, 2017
Biotronic
Sep 25, 2017
Haridas
Sep 25, 2017
bitwise
Sep 25, 2017
Haridas
Sep 24, 2017
bitwise
Sep 25, 2017
Adrian Matoga
Sep 25, 2017
bitwise
Sep 28, 2017
Elronnd
Sep 28, 2017
Elronnd
September 24, 2017
In the following code, Bar is an element of struct Foo. Is there a way to avoid a call to ~Bar when ~Foo is getting executed?

// >>>>>>>>>>

import std.stdio;

struct Foo {
  Bar bar;
  ~this() {
    writeln("~Foo");
    // some code that disables call to ~Bar
  }
}

struct Bar {
  ~this() {
    writeln("~Bar");
  }
}

void main() {
  Foo foo;
}

September 24, 2017
On Sunday, 24 September 2017 at 17:11:26 UTC, Haridas wrote:
> In the following code, Bar is an element of struct Foo. Is there a way to avoid a call to ~Bar when ~Foo is getting executed?

No, but you could just set a flag in Bar that the destructor checks and skips running if it is set.

In fact, the destructor should do nothing if bar is in its initial state, so that's how you could check it. For example

~this() {
   if(this.member is null) return;
   // finalize this.member
}


September 24, 2017
Thanks Adam

Actually Bar's API and implementation is not in my control. Consider the scenario where it is implemented in a library. Or a scenario where I have millions of instances of Bar (not necessarily as a component of Foo) and I do not want to add to runtime memory footprint.

Ok, consider the following code. Now I am (seemingly) avoiding a call to Bar's destructor by way of using pointers. But I have doubts if delete on void pointer would reclaim the memory that has been allocated while still not calling the destructor?

// >>>>
import std.stdio;

struct Foo {
  Bar* bar;
  this(size_t l) {
    bar = cast(Bar*) new Bar[l];
  }
  ~this() {
    writeln("~Foo");
    void *tmp = cast(void*) bar;
    // would this reclaim memory
    // allocated to Bar*
    delete(tmp);
  }
}

struct Bar {
  ~this() {
    writeln("~Bar");
  }
}

void main() {
  Foo foo = 4;
}

September 24, 2017
Also consider the following code. Please let me know if I am doing the right thing for dynamic arrays. My hack seems to have the desired effect on shutting down the destructor. Is this hack legal use of D? Can you please guide me if/how it can be achieved for std.container.Array?

// >>>>
import std.stdio;

struct Bar {
  ~this() {
    writeln("~Bar");
  }
}

void main() {
  {				// dynamic array
    Bar[] bars;
    bars.length = 4;
    void* tmp = bars.ptr;
    delete(tmp);
    bars.length = 0;
  }
  {				// std.container.Array
    import std.container: Array;
    Array!Bar bars;
    bars.length = 6;
    // does not seem to work
    void* tmp = &(bars[0]);
    delete(tmp);
    bars.length = 0;
  }
}

September 24, 2017
On Sunday, 24 September 2017 at 18:46:15 UTC, Haridas wrote:
> Also consider the following code. Please let me know if I am doing the right thing for dynamic arrays. My hack seems to have the desired effect on shutting down the destructor. Is this hack legal use of D? Can you please guide me if/how it can be achieved for std.container.Array?
>
> // >>>>
> import std.stdio;
>
> struct Bar {
>   ~this() {
>     writeln("~Bar");
>   }
> }
>
> void main() {
>   {				// dynamic array
>     Bar[] bars;
>     bars.length = 4;
>     void* tmp = bars.ptr;
>     delete(tmp);
>     bars.length = 0;
>   }
>   {				// std.container.Array
>     import std.container: Array;
>     Array!Bar bars;
>     bars.length = 6;
>     // does not seem to work
>     void* tmp = &(bars[0]);
>     delete(tmp);
>     bars.length = 0;
>   }
> }

Since you're deleting the memory the dynamic array is pointing to, what you're doing is potentially unsafe - if anyone touches that memory after it's been deleted, nasal demons may follow.

What you want is something like this:

import std.stdio;

struct Bar
{
    this(int n) {}

    ~this()
    {
        writeln("~Bar");
    }
}

struct SuppressGC(T)
{
    // Disguise T as a humble array.
    private ubyte[T.sizeof] _payload;

    // Create from instance of T.
    this(T arg) {
        _payload = *cast(ubyte[T.sizeof]*)&arg;
    }

    // Or forward constructor arguments to T's constructor.
    static if (__traits(hasMember, T, "__ctor"))
    {
        this(Args...)(Args args)
        if (__traits(compiles, (Args e){__traits(getMember, T.init, "__ctor")(e);}))
        {
            __traits(getMember, get, "__ctor")(args);
        }
    }

    // Pretend to be a T.
    @property
    ref T get()
    {
        return *cast(T*)_payload.ptr;
    }

    alias get this;
}

void useBar(ref Bar b) {}

unittest
{
    // Construct from instance.
    //This creates a temporary on the stack, and its destructor will be called.
    SuppressGC!Bar a = Bar(3);

    // Or by forwarding constructor arguments.
    // This constructs in-place inside SuppressGC, and no destructor will be called.
    auto b = SuppressGC!Bar(3);

    SuppressGC!Bar[] arr;
    arr.length = 3;

    // Another stack temporary. Destructor will be called.
    arr[0] = Bar(5);

    // No temp
    arr[1] = SuppressGC!Bar(5);

    // It even pretends to be the wrapped struct:
    useBar(b);
}

In general, of course, this is a bad idea - there's probably a reason that destructor does the thing it's doing. If you're sure skipping it is what you want, go ahead.

--
  Biotronic
September 24, 2017
On Sunday, 24 September 2017 at 17:11:26 UTC, Haridas wrote:
> In the following code, Bar is an element of struct Foo. Is there a way to avoid a call to ~Bar when ~Foo is getting executed?
>

Don't construct it to begin with.

struct Bar {
    import std.stdio : writeln;
    int a = 123;
    void boink() { writeln(a); }
    ~this(){ writeln("bar dtor"); }
}

struct Foo
{
    ubyte[Bar.sizeof] barBuffer;
    Bar* _bar = null;

    ref Bar bar()
    {
        import std.conv : emplace;

        if(!_bar) {
            _bar = cast(Bar*)barBuffer.ptr;
            emplace(_bar);
        }

        return *_bar;
    }
}

int main(string[] argv)
{
    Foo foo;
    foo.bar.boink();
    return 0;
}

September 25, 2017
> In general, of course, this is a bad idea - there's probably a reason that destructor does the thing it's doing. If you're sure skipping it is what you want, go ahead.

@Biotronic, the code you have provided may be exactly what I am looking for. Let me explain my situation.

I have a library that has been ported from a C/C++ code. A struct Bar that is part of the library implements its own refcounted GC. The refcounted GC of course works via constructor and destructor. But this refcounted GC also uses some thread local variables since there is a need to implement parallelism.

It all works well so far. But as soon as I create an instance of Bar inside a Dlang class (say Foo) or as part of a Dlang dynamic array, hell follows. At some point, Dlang's GC kicks in and Bar's destructor gets called from within Dlang's GC. Now since Dlang executes GC on a different thread, the destructor gets confused and segfaults. Fortunately for me, it would be fine if the call to destructor is completely avoided within the realm of Dlang's GC since I do not need the Bar instances thereafter. All I need to do is to dismantle Bar completely from the memory to avoid leaks. So long as I can avoid calls to Bar's destructor from within Dlang's GC, I am fine.

I also have some limitations. Since we need to run all this on an embedded system with limited RAM (and given that I have millions of instances of Bar which is only 32-bit wide), I do not want to add a flag or other stuff to Bar. And that is the reason we are using thread local storage in the refcounted GC.

So there. I will try your way. Let me see if I can make it to work in my situation. Thanks for sharing.
September 25, 2017
On Monday, 25 September 2017 at 01:46:15 UTC, Haridas wrote:
> [...]
>
> It all works well so far. But as soon as I create an instance of Bar inside a Dlang class (say Foo) or as part of a Dlang dynamic array, hell follows. At some point, Dlang's GC kicks in and Bar's destructor gets called from within Dlang's GC. Now since Dlang executes GC on a different thread, the destructor gets confused and segfaults.

I actually wrote a C# style Dispatcher for exactly this reason. I have D classes that own non thread-safe resources. So in the destructor of the D class, I add a call that queues the destruction to the main thread's dispatcher.

In your case, the postblit of Bar is still going to run and add a ref to it's count when you place it in Foo, right? That means that if you don't destroy it, it will leak memory or resources.

Unfortunately, my dispatcher is not production-ready yet, but you can get around this with a simpler approach. Just keep a shared container of your ref counted object type somewhere. When a destructor of a GC class runs, move the ref counted object into the trash container. Then, next time you want to create an instance of the ref counted object, you can empty the trash container at the same time. You should protect the container with a Mutex of some kind. Also, be sure that the container doesn't allocate using the GC since it will be called from class destructors. IIRC std.container.Array uses malloc, not GC, so you may be able to use that.






September 25, 2017
> In your case, the postblit of Bar is still going to run and add a ref to it's count when you place it in Foo, right? That means that if you don't destroy it, it will leak memory or resources.
>

Actually no. Since when Foo (class that instantiates Bar) gets GCed, that is the point that I need to destroy the whole infrastructure around Bar, including its recounted GC. So I am Ok if destructor of Bar does not get called this one time since I am just going to delete the whole block of memory that I have allocated for placing the refcounted instances of Bar.
September 25, 2017
On Sunday, 24 September 2017 at 19:52:52 UTC, bitwise wrote:
> On Sunday, 24 September 2017 at 17:11:26 UTC, Haridas wrote:
>> In the following code, Bar is an element of struct Foo. Is there a way to avoid a call to ~Bar when ~Foo is getting executed?
>>
>
> Don't construct it to begin with.
>
> struct Bar {
>     import std.stdio : writeln;
>     int a = 123;
>     void boink() { writeln(a); }
>     ~this(){ writeln("bar dtor"); }
> }
>
> struct Foo
> {
>     ubyte[Bar.sizeof] barBuffer;
>     Bar* _bar = null;
>
>     ref Bar bar()
>     {
>         import std.conv : emplace;
>
>         if(!_bar) {
>             _bar = cast(Bar*)barBuffer.ptr;
>             emplace(_bar);
>         }
>
>         return *_bar;
>     }
> }

You shouldn't store the pointer to barBuffer inside Foo. The language allows moving the structure around with a simple memcpy, so _bar is likely to point into garbage soon after it's assigned. Why don't you just return *cast(Bar*)barBuffer.ptr in bar()? You could still emplace a Bar inside barBuffer in Foo's constructor, if needed.



« First   ‹ Prev
1 2