www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Freeing memory from C

reply "Chris" <wendlec tcd.ie> writes:
I have a C module that dynamically allocates memory for a string 
like so:

char *result = (char*)malloc(length + 1); // 'length' has been 
calculated

When I call it from D (via extern (C)), is it ok to free it from 
there like so:

void callFunction() {
   auto result = callToCFunction(); // Returns above *result
   // ...
   std.c.stdlib.free(result);
}

The problem is that, as it is now, *result is allocated in a 
different place in the C module (not in "callToCFunction()) and 
cannot be freed before D has called it.

If D cannot free it in this way, I'll have a serious memory leak 
and I'll have to rewrite the existing C module (which is probably 
better given the awkward situation above).
Dec 03 2013
parent reply "John Colvin" <john.loughran.colvin gmail.com> writes:
On Tuesday, 3 December 2013 at 10:57:51 UTC, Chris wrote:
 I have a C module that dynamically allocates memory for a 
 string like so:

 char *result = (char*)malloc(length + 1); // 'length' has been 
 calculated

 When I call it from D (via extern (C)), is it ok to free it 
 from there like so:

 void callFunction() {
   auto result = callToCFunction(); // Returns above *result
   // ...
   std.c.stdlib.free(result);
 }

 The problem is that, as it is now, *result is allocated in a 
 different place in the C module (not in "callToCFunction()) and 
 cannot be freed before D has called it.

 If D cannot free it in this way, I'll have a serious memory 
 leak and I'll have to rewrite the existing C module (which is 
 probably better given the awkward situation above).
You should be fine to free in that way as long as you haven't done anything crazy like separately static linking libc. core.stdc.stdlib; is the correct module to use, std.c.* only exist for backwards compatibility.
Dec 03 2013
next sibling parent reply "Chris" <wendlec tcd.ie> writes:
On Tuesday, 3 December 2013 at 12:31:16 UTC, John Colvin wrote:
 On Tuesday, 3 December 2013 at 10:57:51 UTC, Chris wrote:
 I have a C module that dynamically allocates memory for a 
 string like so:

 char *result = (char*)malloc(length + 1); // 'length' has been 
 calculated

 When I call it from D (via extern (C)), is it ok to free it 
 from there like so:

 void callFunction() {
  auto result = callToCFunction(); // Returns above *result
  // ...
  std.c.stdlib.free(result);
 }

 The problem is that, as it is now, *result is allocated in a 
 different place in the C module (not in "callToCFunction()) 
 and cannot be freed before D has called it.

 If D cannot free it in this way, I'll have a serious memory 
 leak and I'll have to rewrite the existing C module (which is 
 probably better given the awkward situation above).
You should be fine to free in that way as long as you haven't done anything crazy like separately static linking libc. core.stdc.stdlib; is the correct module to use, std.c.* only exist for backwards compatibility.
Ok. Thanks for the answer. std.c.stdlib.free() is mentioned on the "How to interface to C" page (http://dlang.org/interfaceToC.html). So maybe that needs an update.
Dec 03 2013
parent reply "bearophile" <bearophileHUGS lycos.com> writes:
Chris:

 std.c.stdlib.free() is mentioned on the "How to interface to C" 
 page (http://dlang.org/interfaceToC.html). So maybe that needs 
 an update.
You can file an enhancement request for the documentation, or fix the docs yourself. I'll file a little bug report for the other library deprecation problem. Bye, bearophile
Dec 03 2013
parent "Adam D. Ruppe" <destructionator gmail.com> writes:
On Tuesday, 3 December 2013 at 12:43:08 UTC, bearophile wrote:
 You can file an enhancement request for the documentation, or 
 fix the docs yourself.
https://github.com/D-Programming-Language/dlang.org/pull/427
Dec 03 2013
prev sibling parent reply Mike Parker <aldacron gmail.com> writes:
On 12/3/2013 9:31 PM, John Colvin wrote:
 You should be fine to free in that way as long as you haven't done
 anything crazy like separately static linking libc.
I wouldn't advise this in the general case. When you have complete end-to-end control, sure. But if, for example, you're using a dynamic binding to load a shared library, all bets are off. Most likely on Linux and Mac you'll be fine. But on Windows, the shared lib could have been compiled with DMC, GCC, MSVC, or who knows what else.
Dec 03 2013
next sibling parent reply "John Colvin" <john.loughran.colvin gmail.com> writes:
On Tuesday, 3 December 2013 at 13:05:20 UTC, Mike Parker wrote:
 On 12/3/2013 9:31 PM, John Colvin wrote:
 You should be fine to free in that way as long as you haven't 
 done
 anything crazy like separately static linking libc.
I wouldn't advise this in the general case. When you have complete end-to-end control, sure. But if, for example, you're using a dynamic binding to load a shared library, all bets are off. Most likely on Linux and Mac you'll be fine. But on Windows, the shared lib could have been compiled with DMC, GCC, MSVC, or who knows what else.
Fair point. What I should have said is: This is fine as long as you know you that both the C code and D code will be using the same so/dll/dylib C runtime. It's worth noting that the situation is not specific to D: it's exactly the same as freeing memory in C that you got from a library. If they are using different runtimes, or even different instances of the same runtime, all bets are off. At best, your memory won't get freed, at worst it will cause corruption.
Dec 03 2013
parent reply "Chris" <wendlec tcd.ie> writes:
On Tuesday, 3 December 2013 at 14:18:35 UTC, John Colvin wrote:
 On Tuesday, 3 December 2013 at 13:05:20 UTC, Mike Parker wrote:
 On 12/3/2013 9:31 PM, John Colvin wrote:
 You should be fine to free in that way as long as you haven't 
 done
 anything crazy like separately static linking libc.
I wouldn't advise this in the general case. When you have complete end-to-end control, sure. But if, for example, you're using a dynamic binding to load a shared library, all bets are off. Most likely on Linux and Mac you'll be fine. But on Windows, the shared lib could have been compiled with DMC, GCC, MSVC, or who knows what else.
Fair point. What I should have said is: This is fine as long as you know you that both the C code and D code will be using the same so/dll/dylib C runtime. It's worth noting that the situation is not specific to D: it's exactly the same as freeing memory in C that you got from a library. If they are using different runtimes, or even different instances of the same runtime, all bets are off. At best, your memory won't get freed, at worst it will cause corruption.
As with all C code I can only say I _hope_ I know what I'm doing. The C code is compiled into a library and linked to the D program at compile time. It's one executable. So I hope that's fine. I noticed a strange behavior though. In my C code I had an uninitialized variable[1], the "int length;" mentioned above. When I compiled it to a library and linked it with my program, everything worked fine, the string was allocated correctly and came out as expected, i.e. the length was calculated correctly with "length += otherlength;". However, when I moved the same library to my vibe.d project, compiled and linked, I got a segmentation fault and the program crashed. It wasn't until then, that I became aware of uninitialized variable. I think that the latter behavior is the correct one (segfault > crash). But why did it work correctly in the other D program, how did the C variable get initialized? [1] a D habit not to write "int i = 0;", but just "int i;", being the spoiled D-brat I am
Dec 03 2013
parent reply =?UTF-8?B?QWxpIMOHZWhyZWxp?= <acehreli yahoo.com> writes:
On 12/03/2013 06:45 AM, Chris wrote:

 I became aware of uninitialized variable. I think that the latter
 behavior is the correct one (segfault > crash). But why did it work
 correctly in the other D program, how did the C variable get initialized?
Undefined behavior sometimes manifests itself as working correctly. :) Regarding your original question, I suggest that the C library provides a function that frees the memory itself. The D code should just call that function with the pointer at hand. Ali
Dec 03 2013
parent "Chris" <wendlec tcd.ie> writes:
On Tuesday, 3 December 2013 at 16:52:29 UTC, Ali Çehreli wrote:
 On 12/03/2013 06:45 AM, Chris wrote:

 I became aware of uninitialized variable. I think that the
latter
 behavior is the correct one (segfault > crash). But why did
it work
 correctly in the other D program, how did the C variable get
initialized? Undefined behavior sometimes manifests itself as working correctly. :)
True. The weird thing was that it seemed to work correctly in one program, i.e. during the test runs it calculated the right value of "int length;" and returned the string that was expected while it wasn't even initialized (!), and failed immediately in the other program defaulting to value -5180 or something (which is correct).
 Regarding your original question, I suggest that the C library 
 provides a function that frees the memory itself. The D code 
 should just call that function with the pointer at hand.

 Ali
You are right of course. Especially because I will need the C library for other languages like Python and I don't think they can handle it the way D can. Today the "hack" of freeing it from D worked well. The memory was freed correctly.
Dec 03 2013
prev sibling parent "Chris" <wendlec tcd.ie> writes:
On Tuesday, 3 December 2013 at 13:05:20 UTC, Mike Parker wrote:
 On 12/3/2013 9:31 PM, John Colvin wrote:
 You should be fine to free in that way as long as you haven't 
 done
 anything crazy like separately static linking libc.
I wouldn't advise this in the general case. When you have complete end-to-end control, sure. But if, for example, you're using a dynamic binding to load a shared library, all bets are off. Most likely on Linux and Mac you'll be fine. But on Windows, the shared lib could have been compiled with DMC, GCC, MSVC, or who knows what else.
Fortunately, I have end-to-end control. It is a bit of a hack and I'm not too happy with it. But as long as it doesn't leak, it'll do for now.
Dec 03 2013