ぐぐるさんの Protocol Buffers ってメモリリークしてる気がするのかしらー

諸事情で、google さんが提供してる Protocol buffers を利用しています。

さて、VC2008EEで "#include <crtdbg.h>" しつつ作業してたら、終了後にリーク報告受けちゃいました。

こういう場合は当然自分のコードを疑うわけですけれども(ぉ)、"int main(){ return 0; }" な何もしないコードでもリーク報告受けちゃうんですねー。

何が原因かしら…? と、allocate id を元に break設定 してみたら "once.cc" とかいう protocol buffers の中のコードで引っかかる。 ……マジかぐーぐる先生!

# ちなみに利用バージョンは 2.1.0 かしら

コード

// once.h の一部

struct GoogleOnceInternal;

struct LIBPROTOBUF_EXPORT GoogleOnceType {
  GoogleOnceType();
  void Init(void (*init_func)());

  volatile bool initialized_;
  GoogleOnceInternal* internal_;
};

#define GOOGLE_PROTOBUF_DECLARE_ONCE(NAME)                    \
  ::google::protobuf::GoogleOnceType NAME

inline void GoogleOnceInit(GoogleOnceType* once, void (*init_func)()) {
  // Note:  Double-checked locking is safe on x86.
  if (!once->initialized_) {
    once->Init(init_func);
  }
}
// once.cc のコードの一部
struct GoogleOnceInternal {
  GoogleOnceInternal() {
    InitializeCriticalSection(&critical_section);
  }
  ~GoogleOnceInternal() {
    DeleteCriticalSection(&critical_section);
  }
  CRITICAL_SECTION critical_section;
};

GoogleOnceType::GoogleOnceType() {
  // internal_ may be non-NULL if Init() was already called.
  if (internal_ == NULL) internal_ = new GoogleOnceInternal;
  // ※↑この new した子が解放されない
}

void GoogleOnceType::Init(void (*init_func)()) {
  // internal_ may be NULL if we're still in dynamic initialization and the
  // constructor has not been called yet.  As mentioned in once.h, we assume
  // that the program is still single-threaded at this time, and therefore it
  // should be safe to initialize internal_ like so.
  if (internal_ == NULL) internal_ = new GoogleOnceInternal;

  EnterCriticalSection(&internal_->critical_section);
  if (!initialized_) {
    init_func();
    initialized_ = true;
  }
  LeaveCriticalSection(&internal_->critical_section);
}


GoogleOnceType::GoogleOnceType();internal_ = new GoogleOnceInternal; してるは良い物の、この子を delete する構文が見あたらず……。うーむ? (…ってか、動的初期化がうんうんとかなんだろう。)



解決策としては、以下の2つのどちらかかなぁと。

  1. 構造体定義中の GoogleOnceInternal* internal_; を、std::auto_ptr で囲んであげる *1
  2. デストラクタを定義して、その中で delete する。

前者より後者の方がカンタンなので今のおいらはそっちで対応してますが、気分は auto_ptr で囲ってあげる方がいいよねぇーとか思ってみたり。 まぁお好みでって話ですね。 :-]




……しかしまぁ、何でこんな事に……。ってか、こんな単純なミスはどこかの誰かが間違いなく気づいてるんだろうなぁコレ。まぁいいや。にばんじ(←なぜか変換できない)でもいいよもう。(ぉ

ってかそもそも、おいらの使い方が何か間違ってるのかなぁ。 いやでも、終了処理用の google::protobuf::ShutdownProtobufLibrary(); を最後に入れてリークしてたしなぁ…うーむ。なんだろう。

*1:ってか、生ポ 使ってるのも道なんだろうなぁコレ