-
Notifications
You must be signed in to change notification settings - Fork 3.4k
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
[C#][Integration] C# C Data interface does not release ArrowArray
imports where a non-validity buffer is NULL
#40898
Comments
It would not surprise me to find that there's logic like "if length > 0 then deallocate". I'm reasonably sure the C# implementation doesn't allocate any memory for a zero-length buffer and the interop code may have inherited this assumption. What's the easiest way to trigger the problem? Run the tests on the branch in your draft PR? |
Got it! There are some more things I can check...I was just excited to get this far in my debugging 🙂 The reproducing right now is a little complicated since there are a few fixes in various PRs. From a checkout of https://github.com/paleolimbot/arrow/tree/archery-nanoarrow-integration : git clone https://github.com/paleolimbot/arrow-nanoarrow.git nanoarrow
cd nanoarrow && git switch integration-fixes && cd ..
archery docker run \
-e ARCHERY_INTEGRATION_WITH_NANOARROW=1 \
-e ARROW_INTEGRATION_CPP=OFF \
-e ARROW_INTEGRATION_JAVA=OFF \
-e ARROW_INTEGRATION_JS=OFF \
-e ARROW_INTEGRATION_GO=OFF \
conda-integration |
Hmm...upon inspecting the other files where there are leaks, I'm seeing that the pattern is more like: C# is failing to release anything once a very specific thing happens (but I am not sure what that very specific thing is and why it only happens when nanoarrow is exporting). I will try to set up a more minimal reproducer trying smaller parts of those files to see if I can narrow it down. |
ArrowArray
importsArrowArray
imports
I managed to get a more minimal reproducer with a local build; however, I'm still working on how to step through that in a debugger to see where exactly things are going awry: From the root arrow checkout of https://github.com/paleolimbot/arrow Make sure archery is installed:
From the csharp/ subdirectory run
From the arrow checkout, clone + build nanoarrow where the default Archery
Run the integration test for exactly one file that is failing
|
I am fairly sure that the problem is The bit of Python I ran to test that locally in details below. from archery.integration import cdata
from archery.integration.tester_cpp import CppTester
from archery.integration.tester_csharp import CSharpTester
from archery.integration.tester_nanoarrow import NanoarrowTester
with open("primitive-empty.json", "w") as f:
f.write("""{
"schema": {
"fields": [
{"name": "binary_nullable", "type": {"name": "binary"}, "nullable": true, "children": []}
]
},
"batches": [
{
"count": 0,
"columns": [
{"name": "binary_nullable", "count": 0, "VALIDITY": [], "OFFSET": [0], "DATA": []}
]
}
]
}""")
tester_na = NanoarrowTester()
tester_cs = CSharpTester()
exporter = tester_na.make_c_data_exporter()
importer = tester_cs.make_c_data_importer()
json_path = "primitive-empty.json"
ffi = cdata.ffi()
c_array_ptr = ffi.new("struct ArrowArray*")
with cdata.check_memory_released(exporter, importer):
exporter.export_batch_from_json(json_path, 0, c_array_ptr)
importer.import_batch_and_compare_to_json(json_path, 0, c_array_ptr) |
ArrowArray
importsArrowArray
imports where a non-validity buffer is NULL
…e Arrays (#41054) ### Rationale for this change When implementing integration tests for nanoarrow, it was observed that C# never released arrays where `array->buffers[i]` was `NULL` (including any buffers of any recursive child arrays). This is allowed ( https://arrow.apache.org/docs/format/CDataInterface.html#c.ArrowArray.buffers ); however, every other implementation appears to allocate even for length zero buffers (including nanoarrow after apache/arrow-nanoarrow#399 ). ### What changes are included in this PR? `AddMemory()` is replaced with `ArrowBuffer.Empty` if the length of the imported buffer would have been 0 bytes. For other buffers (or anywhere I saw dereferencing a buffer pointer), I added a `Debug.Assert` just to be sure. ### Are these changes tested? I'm not sure what the best way to test them is! They won't be tested in the nanoarrow integration tests since at the point that they run, nanoarrow will no longer export arrays that would trigger this. ### Are there any user-facing changes? No * GitHub Issue: #40898 Authored-by: Dewey Dunnington <dewey@fishandwhistle.net> Signed-off-by: Curt Hagenlocher <curt@hagenlocher.org>
Issue resolved by pull request 41054 |
…terface Arrays (apache#41054) ### Rationale for this change When implementing integration tests for nanoarrow, it was observed that C# never released arrays where `array->buffers[i]` was `NULL` (including any buffers of any recursive child arrays). This is allowed ( https://arrow.apache.org/docs/format/CDataInterface.html#c.ArrowArray.buffers ); however, every other implementation appears to allocate even for length zero buffers (including nanoarrow after apache/arrow-nanoarrow#399 ). ### What changes are included in this PR? `AddMemory()` is replaced with `ArrowBuffer.Empty` if the length of the imported buffer would have been 0 bytes. For other buffers (or anywhere I saw dereferencing a buffer pointer), I added a `Debug.Assert` just to be sure. ### Are these changes tested? I'm not sure what the best way to test them is! They won't be tested in the nanoarrow integration tests since at the point that they run, nanoarrow will no longer export arrays that would trigger this. ### Are there any user-facing changes? No * GitHub Issue: apache#40898 Authored-by: Dewey Dunnington <dewey@fishandwhistle.net> Signed-off-by: Curt Hagenlocher <curt@hagenlocher.org>
…terface Arrays (apache#41054) ### Rationale for this change When implementing integration tests for nanoarrow, it was observed that C# never released arrays where `array->buffers[i]` was `NULL` (including any buffers of any recursive child arrays). This is allowed ( https://arrow.apache.org/docs/format/CDataInterface.html#c.ArrowArray.buffers ); however, every other implementation appears to allocate even for length zero buffers (including nanoarrow after apache/arrow-nanoarrow#399 ). ### What changes are included in this PR? `AddMemory()` is replaced with `ArrowBuffer.Empty` if the length of the imported buffer would have been 0 bytes. For other buffers (or anywhere I saw dereferencing a buffer pointer), I added a `Debug.Assert` just to be sure. ### Are these changes tested? I'm not sure what the best way to test them is! They won't be tested in the nanoarrow integration tests since at the point that they run, nanoarrow will no longer export arrays that would trigger this. ### Are there any user-facing changes? No * GitHub Issue: apache#40898 Authored-by: Dewey Dunnington <dewey@fishandwhistle.net> Signed-off-by: Curt Hagenlocher <curt@hagenlocher.org>
Describe the bug, including details regarding any error messages, version, and platform.
In #39302, the integration tests where nanoarrow is producing an C# is consuming are reporting memory leaks. These leaks are based on allocations/frees reported by nanoarrow's special buffer allocator, which I instrumented to do some printing of the results. Those adventures are in a few gists ( https://gist.github.com/paleolimbot/257be29c926c3467b174fd83c3baf2b1 , https://gist.github.com/paleolimbot/8183f50a1c1425db4a744903ba3f905b ) as well as later in this thread, and the output that led me to this conclusion is below: basically, I instrumented the special allocator to print to stdout and did some scraping of the output. I noticed that no buffers are freed when csharp is responsible for releasing, but all the buffers are freed when nanoarrow is (and presumably other implementations too, since no memory leaks are reported for those).
This only happens for custom_metadata, extension, primitive_zerolength, recursive_nested, and union cases, and the patterns seems to be that something happens and then the release callback is just never called. This only happens when nanoarrow is exporting.
The commit that eliminated the leaks from nanoarrow's side was a bit of code that ensured that there were no NULL buffers that were exported (except validity buffers, which can be null).
Component(s)
C#, Integration
The text was updated successfully, but these errors were encountered: