終了処理を確実に行う方法


ハードウェアデバイスや、共有メモリなどのリソースを確保した場合 は確実に終了処理をしなければならないが、 SIGINTなどで強制終了するときなど、終了処理が必要なリソース全てに対して 終了処理を行うことは意外に難しい。 そこでCommandパターンとSingletonを使って終了処理を確実に行うことができる方法を 考えた。Singletonは必須ではないが、グローバル変数を使いたくなかったのでSingletonを使っている。ここではLokiのSingletonを用いた。

サンプルを以下に示す。


#include <iostream>
#include <list>
#include <Singleton.h>


class Terminator{
public:
  typedef std::list<Terminator*> termlist;

  Terminator(){
    std::cout<< "Terminator::Terminator()"<< std::endl;
    Loki::SingletonHolder<termlist>::Instance().push_back(this);
  }

  virtual void terminate() = 0;
};


void destroyAllResource(void){
  typedef Terminator::termlist T;
    for(T::iterator i = Loki::SingletonHolder<T>::Instance().begin();
	i != Loki::SingletonHolder<T>::Instance().end(); i++){
      (*i)->terminate();
    }
}


class Resource : public Terminator{
public:
  void terminate(){
    close();
    std::cout<< "Resource close OK"<< std::endl;
  }


  void close(){
    if(isOpen){
      closeResource(hResource);
      isOpen = true;
    }
  }

private:
  handle hResource;
  bool isOpen;
};


void handler(int signum){
  terminate();
  exit(0);
}


int main(void){
  Resource r1;
  Resource r2;

  signal(SIGINT, handler);

  while(true)
    sleep(1);

  return 0;
}

終了処理が必要なリソースはTerminatorを親クラスとするクラスにする。純粋仮想関数terminateをオーバーライドし終了処理を書いておく。 SIGINTのハンドラなどの終了処理を行う場所でdestroyAllResource関数を呼ぶ。 上記の例だと、Resourceがインスタンス化されると、Resourceのポインタがlistに格納される。destroyAllResourceが呼ばれると、listに格納されている全ての要素のterminate関数が呼ばれ、終了処理が行われる。