IT戦記

プログラミング、起業などについて書いているプログラマーのブログです😚

ICU の解放順序でハマった

ちょっとメモ

以下のようなコードを書いて失敗した。

#include <boost/shared_ptr.hpp>
#include <unicode/uclean.h>
#include <unicode/ucnv.h>

int main()
{
    UErrorCode status = U_ZERO_ERROR;
    u_init(&status);

    // (snip)

    boost::shared_ptr<UConverter> ucnv(ucnv_open("Shift_JIS", &status), ucnv_close);

    // (snip)

    u_cleanup();
}

u_cleanup より後に ucnv_close が呼ばれてしまうのが問題。

valgrind で検出

このコード問題があるのに、普通にエラーも何もでないので気が付かない。 valgrind を使ったら、 still reachable なメモリがあると教えてくれた。

$ valgrind ./a.out
==17685== Memcheck, a memory error detector.
==17685== Copyright (C) 2002-2006, and GNU GPL'd, by Julian Seward et al.
==17685== Using LibVEX rev 1658, a library for dynamic binary translation.
==17685== Copyright (C) 2004-2006, and GNU GPL'd, by OpenWorks LLP.
==17685== Using valgrind-3.2.1, a dynamic binary instrumentation framework.
==17685== Copyright (C) 2000-2006, and GNU GPL'd, by Julian Seward et al.
==17685== For more details, rerun with: -v
==17685== 
==17685== 
==17685== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 7 from 1)
==17685== malloc/free: in use at exit: 24,944 bytes in 4 blocks.
==17685== malloc/free: 8 allocs, 4 frees, 25,376 bytes allocated.
==17685== For counts of detected errors, rerun with: -v
==17685== searching for pointers to 4 not-freed blocks.
==17685== checked 285,112 bytes.
==17685== 
==17685== LEAK SUMMARY:
==17685==    definitely lost: 0 bytes in 0 blocks.
==17685==      possibly lost: 0 bytes in 0 blocks.
==17685==    still reachable: 24,944 bytes in 4 blocks. // ←ここ!
==17685==         suppressed: 0 bytes in 0 blocks.
==17685== Reachable blocks (those to which a pointer was found) are not shown.
==17685== To see them, rerun with: --show-reachable=yes

修正

で、さっきのコードを以下のようにすればいい。

#include <boost/shared_ptr.hpp>
#include <unicode/uclean.h>
#include <unicode/ucnv.h>

struct ICUInitializer
{
    ICUInitializer() : status_(U_ZERO_ERROR) { u_init(&status_); }
    ~ICUInitializer() { u_cleanup(); }
    UErrorCode status_;
};

int main()
{
    ICUInitializer init;

    // (snip)

    UErrorCode status = U_ZERO_ERROR;
    boost::shared_ptr<UConverter> ucnv(ucnv_open("Shift_JIS", &status), ucnv_close);

    // (snip)
}

valgrind で確認

$ valgrind ./a.out
==17834== Memcheck, a memory error detector.
==17834== Copyright (C) 2002-2006, and GNU GPL'd, by Julian Seward et al.
==17834== Using LibVEX rev 1658, a library for dynamic binary translation.
==17834== Copyright (C) 2004-2006, and GNU GPL'd, by OpenWorks LLP.
==17834== Using valgrind-3.2.1, a dynamic binary instrumentation framework.
==17834== Copyright (C) 2000-2006, and GNU GPL'd, by Julian Seward et al.
==17834== For more details, rerun with: -v
==17834== 
==17834== 
==17834== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 7 from 1)
==17834== malloc/free: in use at exit: 0 bytes in 0 blocks.
==17834== malloc/free: 8 allocs, 8 frees, 25,376 bytes allocated.
==17834== For counts of detected errors, rerun with: -v
==17834== All heap blocks were freed -- no leaks are possible.

まとめ

valgrind で多い日も安心!