-
Notifications
You must be signed in to change notification settings - Fork 30
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
free memory allocated by Golang using C.CString() #390
free memory allocated by Golang using C.CString() #390
Conversation
@olomix can you take a look a this PR? |
Can we simply call the |
I'm actually not sure why we're not making something like:
Maybe this way we could also get rid of Or I don't know some background of this |
@yushihang we have already merged the PR 386 with malloc.free() , is it enough? |
Thank you very much for your response and suggestions. For me, having the opportunity to participate in open-source code work is a great learning opportunity. A concise description of the approach for this PR is: "Following the RAII (Resource Acquisition Is Initialization) pattern, who allocates, who releases". I will try to express my understanding of this memory management case using the diagram A below: The steps in the diagram are as follows:
After following this procedure, all memory on the heap that was dynamically allocated (the green and red parts in Diagram A) will be properly released, and there will be no memory leak. PR #386 handles the release logic for the green block of memory in Diagram A, while the current PR handles the release logic for the red block of memory. Another point that needs to be discussed for current PR is "why we need to wrap a function in Go to release I have attempted to illustrate this implementation method using Diagram B: As can be seen from the diagram, steps 5 and 6 in Diagram A have been combined into a new step 5, which means to call C-style-free() directly from the Flutter/dart side to release the memory of In fact, I have considered this method when dealing with the release logic of the red block memory in the Diagram B.
The main difference between Diagram A & B is : Should "the memory allocated by Golang from OS" be released by Golang self, or can it be released by Other module(Flutter)? The reason for adopting the method in Diagram A in current pull request, i.e. releasing
PLGNFreeCString() does not only provide a C.free() function for Flutter, but it should be understood as follows:
Another situation is that memory allocation is not only done through C.CString()/malloc(), but can also be done through a method such as In such cases, Golang may replace the current C.CString()/C.free() calls by linking to a third-party library. If this situation arises, it is not appropriate for Flutter to continue calling C-style-free(). If we adopt Diagram A's approach, which is the approach taken in this PR, where the responsibility for allocating and releasing memory is borne by Golang, and the actual allocate/release method used is also determined by Golang. Then the Flutter code does not need to be modified, even if the native library changes this part of the memory management method. This approach is more in line with the RAII principle of "who allocates, who releases". A summary in one sentence is: "Memory allocated by Flutter is released by Flutter (green blocks in Diagram A), and memory allocated by Golang is released by Golang (red blocks in Diagram A). " Actually, the above content is not limited to Flutter and Golang. Any cross-programming language memory management will encounter such situations. Another issue is: for the module that allocates a block of memory, if this block of memory is released by other modules/languages, but the allocator module has not received a notification, this can also bring potential problems to memory management. |
There are two kinds of memory Go can allocate. First, from the internal Go-specific memory allocator that is under Go Garbage Collector control. And second, from the OS that is not under control of the Go garbage collector. In our c-polygonid library we are trying not to return Go-controlled memory. So whatever you do, you can't cause a memory leak inside Go runtime, only in system memory (I hope). Then other issue is where should we put the code that releases the memory. In C world, if you return a pointer to the complex data structure with embedded pointers, then you should provide a function to release such complex structure. But if you return a pointer to a simple data structure as a string (char *) or an array of ints (int *) and this memory could be released with just a call to a We design our c-polygonid library to work with strings only: for input and output data. The only "complex" structure we provide is PLGNStatus, that contains embedded pointers to strings with error messages. And we provide a function There are a lot of functions in standard library that return strings: like |
Thank you for the discussion and insights~
Yes, currently, the memory leak only occurs in the memory allocated by C.CString() (red memory in diagram), and not in the memory managed by Go's Garbage Collector.
I agree with your point in c-world. However, the example is within the same programming language or same module, sometimes this is the chosen implementation because there is no better alternative.
The Standard C library provides Correspondingly, C-polygonid may provide Considering that c-polygonid provides a C-style API, it may not only be called by Flutter, which means that when calling c-polygonid functions from any other programming language, they will also face this problem (directly calling the language's wrapper for C.free, but without a corresponding malloc call for this free() call ).
|
You stress on a "Go language" as something important. The As to confusion, from my point of view, it can be confusing why I can't release pointer to char just by calling I can show you another confusing conversation from the other side if function
You are right about the usage of the PLGNFreeStatus function. Better to use it always, not to free PLGNStatus by yourself. You can (if you know the structure of the PLGNStatus), but for consistency and convenience, better to use PLGNFreeStatus. If we will add some fields to the PLGNStatus structure in the future, your custom free functionality may be broken at some time. And in this case we provide a new type About babyjubjub cstring_free(). I'm not a Rust expert. But if I understand Rust documentation correctly, Rust returns strings that located inside a Rust's memory. So you can't just release it with a |
There might be a miscommunication here. *jsonResponse = C.CString(string(respB))
I actually don't think the language used in c-polygonid, be it Go, Rust, or C, would affect this conclusion because my point is who allocates should take the responsibility to release~
This conversation is a good point to discuss~ Suppose we add documents/comments to PLGNCreateClaim() like asprintf()
As a programmer calling PLGNCreateClaim(), I would certainly be curious and look into the implementation of PLGNFreeCString(), but I would not choose to directly call free by my side. Because the caller don't need to maintain the implementation of PLGNFreeCString() and PLGNCreateClaim(), the c-polygonid library will ensure consistent memory management. As long as PLGNFreeCString() and PLGNCreateClaim() are called in pairs according to the document. Everything will be ok.
I got your point~ We cannot prevent someone from manually calling free() even if there is a provided function such as PLGNFreeStatus() for memory management. It is not an issue of "right or wrong", but of "right or better". It's true we can call In our case, so the conversation may be:
The name
. During our discussion, I've gained valuable insights into your design for c-polygon. Once again, thank you for your insights and suggestions! @olomix |
close #391