Thread overview
Memory leak issue between extern (c) and D function
Apr 14, 2023
backtrack
Apr 14, 2023
Paul Backus
Apr 14, 2023
Guillaume Piolat
Apr 14, 2023
Guillaume Piolat
Apr 14, 2023
backtrack
Apr 16, 2023
Guillaume Piolat
Apr 15, 2023
Ali Çehreli
April 14, 2023

Dear All, I am new to D lang. I have been given a task to consume the .dll generated from a D lang project.

I added extern (c) function for call the .dll from CPP file. i have code like below

// myfile.d
extern(c)
{

    mystruct* getmystruct()
    {
        mystruct* mystruct =  cast(mystruct*)malloc(mystruct.sizeof);
        return mystruct;

    }

   char* do_some_work(const mystruct* mystruct , immutable(char)*  input, int inputlenght)
   {

       InputStruct input;
       input.name = input[0..inputlenght];
       auto output = mystruct.getValue(input);
       // do some process and return char*
       return output_char.ptr;
    }

}


struct mystruct
{
   OutputStruct[] getValue(InputStruct input)
  {
    // some string operation and other operation done here
    return output
  }
}

struct InputStruct
{
   string name;
}

struct OutputStruct
{
   string name;
   string value;
}

Now i have my cpp file which calls like below

mystruct* mystruct = lib.getmystruct();
char* output = lib.do_some_work(mystruct, "input", 5);

The problem i am facing is, the memory keep on increasing and i am not able to fix the memory issue. I am suspecting that since the D lang function is called from extern(c) function GC is not clearing the memeory.

Can you please help me on this?

Thank you

April 14, 2023

On Friday, 14 April 2023 at 03:50:37 UTC, backtrack wrote:

>

Dear All, I am new to D lang. I have been given a task to consume the .dll generated from a D lang project.

I added extern (c) function for call the .dll from CPP file. i have code like below

// myfile.d
extern(c)
{

    mystruct* getmystruct()
    {
        mystruct* mystruct =  cast(mystruct*)malloc(mystruct.sizeof);
        return mystruct;

    }

[...]

>

Now i have my cpp file which calls like below

mystruct* mystruct = lib.getmystruct();
char* output = lib.do_some_work(mystruct, "input", 5);

The problem i am facing is, the memory keep on increasing and i am not able to fix the memory issue. I am suspecting that since the D lang function is called from extern(c) function GC is not clearing the memeory.

The GC does not clean up memory allocated by malloc. Since you're using malloc to allocate your memory, the only way you can free it is by using free.

If you want the GC to clean up your memory, use new to allocate it instead of malloc. Like this:

mystruct* getmystruct()
{
    return new mystruct;
}
April 14, 2023

On Friday, 14 April 2023 at 04:43:39 UTC, Paul Backus wrote:

>

If you want the GC to clean up your memory, use new to allocate it instead of malloc. Like this:

mystruct* getmystruct()
{
    return new mystruct;
}

That won't work because the C++ programm calling the D dynlib will not have its stack scanned, leading to that object being reclaimed early.

OP could add another extern(C) D function to free the allocated object.
Or another extern(C) D function to call GC.addRoot

April 14, 2023

On Friday, 14 April 2023 at 11:15:59 UTC, Guillaume Piolat wrote:

>

OP could add another extern(C) D function to free the allocated object.
Or another extern(C) D function to call GC.addRoot

Or simpler, add that object to a list of object in D DLL __gshared list, then clear the list
(or set individual reference to null) if you want to reclaim space.

April 14, 2023

On Friday, 14 April 2023 at 11:15:59 UTC, Guillaume Piolat wrote:

>

On Friday, 14 April 2023 at 04:43:39 UTC, Paul Backus wrote:

>

If you want the GC to clean up your memory, use new to allocate it instead of malloc. Like this:

mystruct* getmystruct()
{
    return new mystruct;
}

That won't work because the C++ programm calling the D dynlib will not have its stack scanned, leading to that object being reclaimed early.

OP could add another extern(C) D function to free the allocated object.
Or another extern(C) D function to call GC.addRoot

Thank you for your response.

I added something like this in extern (c) function.

       __delete(output);

When i debugged, it shows null to output after executing above line. however the memory is not releasing.

Thank you.

April 15, 2023
On 4/13/23 20:50, backtrack wrote:

>      mystruct* getmystruct()
>      {
>          mystruct* mystruct = cast(mystruct*)malloc(mystruct.sizeof);
>          return mystruct;
>
>      }

There must be a corresponding function to do the cleaning:

void freemystruct(mystruct* ptr) {
    free ptr;
}

You have to make sure on the D side that freemystruct() is called once for each getmystruct().

I have the following presentation that covers similar concepts:

  https://www.youtube.com/watch?v=FNL-CPX4EuM

I think this point is most relevant:

  https://www.youtube.com/watch?v=FNL-CPX4EuM&t=1833s

Ali

April 16, 2023

On Friday, 14 April 2023 at 17:31:02 UTC, backtrack wrote:

>

however the memory is not releasing.

With the D GC, your object can have three state:

  • reachable by GC. If D code can see the reference, then it's "alive", kept alive by GC scanning. The GC finds the reference and doesn't touch it. This is the invariant that you need to maintain when interacting with C.

  • unreachable by GC and thus at a risk of being reclaimable at next GC collect.
    This happens when all your references are null.
    If you want immediate destructor, call .destroy() before nulling your references so that the object can't be scanned. By calling destroy() on all your objects manually, you can reach destruction determinism.

  • non-existing, memory has been reclaimed. You don't necessarily need to do that with __delete, this should be very rare even. If the references to the object are null, then their destructor will eventually be called if it wasn't already with .destroy, the memory eventually reclaimed. Usually you don't need to care about that state.