Possible Memory Leak in io::readLine()?
worky68 opened this issue · comments
I have been experimenting with C3 by porting the interpreter from Crafting Interpreters to it. I noticed when I run valgrind against my interpreter code that I see that two allocs were not freed in the temp allocator block of io::readline.
Here is the relevant portion of my code (I believe).
fn void repl() {
for(;;) {
io::printf("μ ");
String line = io::readline()!!;
defer line.free();
if(line == "exit") {
return;
}
vm::interpret(line);
}
}
fn int main(String[] args) {
vm::initVm();
defer vm::freeVm();
if(args.len == 1) {
repl();
}
else if(args.len == 2) {
runFile(args[1]);
}
else {
io::fprintf(io::stderr(), "Usage: mush [path]\n")!!;
return 64;
}
return 0;
}
And here is valgrind output.
$ valgrind --leak-check=full --show-leak-kinds=all ./build/mushd
==279410== Memcheck, a memory error detector
==279410== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==279410== Using Valgrind-3.18.1 and LibVEX; rerun with -h for copyright info
==279410== Command: ./build/mushd
==279410==
μ 1 + 2
== code ==
0000 1 OP_CONSTANT 0 '1'
0002 | OP_CONSTANT 1 '2'
0004 | OP_ADD
0005 | OP_RETURN
0000 1 OP_CONSTANT 0 '1'
[ 1 ]
0002 | OP_CONSTANT 1 '2'
[ 1 ][ 2 ]
0004 | OP_ADD
[ 3 ]
0005 | OP_RETURN
3
μ exit
==279410==
==279410== HEAP SUMMARY:
==279410== in use at exit: 524,368 bytes in 2 blocks
==279410== total heap usage: 10 allocs, 8 frees, 526,547 bytes allocated
==279410==
==279410== 262,184 bytes in 1 blocks are still reachable in loss record 1 of 2
==279410== at 0x4848899: malloc (in /usr/libexec/valgrind/vgpreload_memcheck-amd64-linux.so)
==279410== by 0x14385F: std.core.mem.allocator.LibcAllocator.acquire (libc_allocator.c3:37)
==279410== by 0x14812B: malloc_try (mem_allocator.c3:68)
==279410== by 0x14812B: alloc_with_padding (mem_allocator.c3:236)
==279410== by 0x14812B: std.core.mem.allocator.new_temp_allocator (temp_allocator.c3:40)
==279410== by 0x148376: create_default_sized_temp_allocator (mem_allocator.c3:370)
==279410== by 0x148376: std.core.mem.allocator.init_default_temp_allocators (mem_allocator.c3:393)
==279410== by 0x111923: temp (mem_allocator.c3:386)
==279410== by 0x111923: @pool (mem.c3:514)
==279410== by 0x111923: readline (io.c3:69)
==279410== by 0x111923: mush.repl (main.c3:16)
==279410== by 0x111E87: mush.main (main.c3:43)
==279410== by 0x11243B: @main_to_int_main_args (main_stub.c3:47)
==279410== by 0x11243B: main (main.c3:38)
==279410==
==279410== 262,184 bytes in 1 blocks are still reachable in loss record 2 of 2
==279410== at 0x4848899: malloc (in /usr/libexec/valgrind/vgpreload_memcheck-amd64-linux.so)
==279410== by 0x14385F: std.core.mem.allocator.LibcAllocator.acquire (libc_allocator.c3:37)
==279410== by 0x14812B: malloc_try (mem_allocator.c3:68)
==279410== by 0x14812B: alloc_with_padding (mem_allocator.c3:236)
==279410== by 0x14812B: std.core.mem.allocator.new_temp_allocator (temp_allocator.c3:40)
==279410== by 0x148451: create_default_sized_temp_allocator (mem_allocator.c3:370)
==279410== by 0x148451: std.core.mem.allocator.init_default_temp_allocators (mem_allocator.c3:394)
==279410== by 0x111923: temp (mem_allocator.c3:386)
==279410== by 0x111923: @pool (mem.c3:514)
==279410== by 0x111923: readline (io.c3:69)
==279410== by 0x111923: mush.repl (main.c3:16)
==279410== by 0x111E87: mush.main (main.c3:43)
==279410== by 0x11243B: @main_to_int_main_args (main_stub.c3:47)
==279410== by 0x11243B: main (main.c3:38)
==279410==
==279410== LEAK SUMMARY:
==279410== definitely lost: 0 bytes in 0 blocks
==279410== indirectly lost: 0 bytes in 0 blocks
==279410== possibly lost: 0 bytes in 0 blocks
==279410== still reachable: 524,368 bytes in 2 blocks
==279410== suppressed: 0 bytes in 0 blocks
==279410==
==279410== For lists of detected and suppressed errors, rerun with: -s
==279410== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
No, that's just the temp allocators I believe. Readline just happens to use the temp allocators and they're lazily allocated. Recently I added a finalizer to drop the temp allocators at exit, so you should probably not see those anymore. That said, I do encourage using the temp allocators, and the repl code is ideal for it:
for(;;) {
@pool() {
io::printf("μ ");
String line = io::treadline()!!; // Temp allocated
if(line == "exit") {
return;
}
vm::interpret(line);
}; // Everything temp allocated inside is released!
}
So as you see, this lets you avoid the need to explicitly free.
Thank you! I am enjoying C3 a lot.
I happy to hear that!