this code is obviously leaking the memory allocated by curl_easy_init()
import libcurl
proc initializer_generator() : proc =
var started = false
proc fun() =
if started:
discard global_init(GLOBAL_DEFAULT)
return fun
let init_curl = initializer_generator()
init_curl()
discard easy_init()
and if I run it with valgrind i get
==20004== Memcheck, a memory error detector
==20004== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==20004== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==20004== Command: ./gc_test
==20004==
==20004==
==20004== HEAP SUMMARY:
==20004== in use at exit: 101,649 bytes in 78 blocks
==20004== total heap usage: 3,394 allocs, 3,316 frees, 210,683 bytes allocated
==20004==
==20004== 37,967 (21,256 direct, 16,711 indirect) bytes in 1 blocks are definitely lost in loss record 17 of 17
==20004== at 0x4C2EF35: calloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==20004== by 0x5B5F35E: ??? (in /usr/lib/libcurl.so.4.5.0)
==20004== by 0x5B6C3D5: curl_easy_init (in /usr/lib/libcurl.so.4.5.0)
==20004== by 0x109E6C: NimMainModule (in /home/walter/code/nim_test/gc_test)
==20004== by 0x109CC4: NimMainInner (in /home/walter/code/nim_test/gc_test)
==20004== by 0x109D00: NimMain (in /home/walter/code/nim_test/gc_test)
==20004== by 0x109D4E: main (in /home/walter/code/nim_test/gc_test)
==20004==
==20004== LEAK SUMMARY:
==20004== definitely lost: 21,256 bytes in 1 blocks
==20004== indirectly lost: 16,711 bytes in 4 blocks
==20004== possibly lost: 0 bytes in 0 blocks
==20004== still reachable: 63,682 bytes in 73 blocks
==20004== suppressed: 0 bytes in 0 blocks
==20004== Reachable blocks (those to which a pointer was found) are not shown.
==20004== To see them, rerun with: --leak-check=full --show-leak-kinds=all
==20004==
==20004== For counts of detected and suppressed errors, rerun with: -v
==20004== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
so far, so good.. but If I replace the last line with this
let curl = easy_init()
this is what I get when I run the program with valgrind
==23000== Memcheck, a memory error detector
==23000== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==23000== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==23000== Command: ./gc_test
==23000==
==23000==
==23000== HEAP SUMMARY:
==23000== in use at exit: 63,682 bytes in 73 blocks
==23000== total heap usage: 3,389 allocs, 3,316 frees, 172,716 bytes allocated
==23000==
==23000== LEAK SUMMARY:
==23000== definitely lost: 0 bytes in 0 blocks
==23000== indirectly lost: 0 bytes in 0 blocks
==23000== possibly lost: 0 bytes in 0 blocks
==23000== still reachable: 63,682 bytes in 73 blocks
==23000== suppressed: 0 bytes in 0 blocks
==23000== Reachable blocks (those to which a pointer was found) are not shown.
==23000== To see them, rerun with: --leak-check=full --show-leak-kinds=all
==23000==
==23000== For counts of detected and suppressed errors, rerun with: -v
==23000== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
I really cannot understand how can it not leak memory anymore: the only right way to free the memory allocated by easy_init is to call easy_cleanup on the same pointer, which cannot happen since the compiler doesn't know anything about it, in the same way I don't think it is possible for the compiler not to call easy_init only because the returned value is not used, since it doesn't know whether it has side effects or not. What's happening under the hood, then? yes, that' basically what happens.. I'll just add that the above example can be simply rewritten in this way
import libcurl
discard global_init(GLOBAL_DEFAULT)
let curl = easy_init()
with the same results depending on the presence of the discard keyword in spite of the let (or var, there is no difference with that) assignment