もしもBoost.Threadにinterruptが無かったら。


VC限定なコードですけが、こんな感じで追加できるんじゃないかと。

元ネタはtwitterid:melponさんがstd::threadにinterruptが無いので欲しいってとこからです。

id:melponさんがつぶやいてたコードの大部分借りました。

#include "stdafx.h"

#include <iostream>
#pragma warning(push)
#pragma warning(disable: 4819)
#include <boost/thread.hpp>
#include <boost/bind.hpp>
#include <boost/scoped_ptr.hpp>
#pragma warning(pop)

namespace thread { namespace cancellation {

class interrupted_exception {};

class interruptible_thread;

template <class F>
void thread_proxy(interruptible_thread* p, F f);

class interruptible_thread
{
  __declspec(thread) struct tls {
    interruptible_thread* this_thread;
  };
public: // publicなのは手抜きです。すみません。
  static __declspec(thread) tls thread_info;
private:
  boost::scoped_ptr<boost::thread> th;
  mutable boost::mutex mutex;
  bool interrupted_;
public:
  template <class F>
  explicit interruptible_thread(F f)
    : th()
    , mutex()
    , interrupted_(false)
  {
    // threadの開始thread化したい関数fは
    // detail::thread_proxy<F>でcallする
    th.reset(
      new boost::thread(
        boost::bind(&detail::thread_proxy<F>, this, f)));
  }

  void join() {
    th->join();
  }
  // boost::threadへの転送関数を定義

  void interrupt()
  {
    boost::unique_lock<boost::mutex> lock(mutex);
    interrupted_ = true;
  }

  bool interrupted() const
  {
    boost::unique_lock<boost::mutex> lock(mutex);
    return interrupted_;
  }
};
interruptible_thread::tls interruptible_thread::thread_info;

namespace detail {
template <class F>
void thread_proxy(interruptible_thread* p, F f) {
  interruptible_thread::thread_info.this_thread = p; // thread-local
  try {
    f();
  } catch (interrupted_exception&) {}
}
} // namecpace detail

} // namecpace cancellation
} // namecpace thread

// ここからテスト用のコード
void worker_thread() {
  namespace tc = thread::cancellation;
  while(1) {
    if (tc::interruptible_thread::thread_info.this_thread->interrupted()) {
      throw tc::interrupted_exception();
    }
  }
}

int _tmain(int argc, _TCHAR* argv[])
{
  namespace tc = thread::cancellation;
  tc::interruptible_thread t0(&worker_thread);
  tc::interruptible_thread t1(&worker_thread);
  t0.interrupt();
  t0.join();
  t1.interrupt();
  t1.join();
  std::cout << "cancel" << std::endl;
  return 0;
}

C++0xではthread_localキーワードが追加されるので

TlsAlloc(Windows) pthread_key_create(POSIX)とか環境依存なコードは書かなくても済みそうです

でも作るのは面倒なので最初から付けてください・・・。

ちなみにg++では

  __declspec(thread) struct tls {
    interruptible_thread* this_thread;
  };
public: // publicなのは手抜きです。すみません。
  static __declspec(thread) tls thread_info;

な部分はどう書けば良いんですかね。

  struct tls {
    interruptible_thread* this_thread;
  };
public: // publicなのは手抜きです。すみません。
  static __thread tls thread_info;

ではダメだったです。windowsgccだったから?
#linux上では試してないです。