汎用的な Blocking コンテナ
を作ってみた。
#ifndef ANMELT_THREAD_BLOCKING_CONTAINER_H_INCLUDED #define ANMELT_THREAD_BLOCKING_CONTAINER_H_INCLUDED // Boost #include <boost/thread.hpp> namespace anmelt{ namespace thread { template<class Container, class Adapter> class blocking_container { private: Container container_; boost::mutex mutex_; boost::condition condition_; public: template<class T> void push(T v) { boost::mutex::scoped_lock lock(mutex_); while (!Adapter::push(container_, v)) { condition_.wait(lock); } condition_.notify_all(); } template<class T> void pop(T& v) { boost::mutex::scoped_lock lock(mutex_); while (!Adapter::pop(container_, v)) { condition_.wait(lock); } condition_.notify_all(); } }; }} #endif // ANMELT_THREAD_BLOCKING_CONTAINER_H_INCLUDED
単に Guarded Suspension パターンで push, pop の際に条件が満たされるまで待っているだけなんだけど、その処理を Adapter に渡しているのが特徴。
例えばコンテナとして std::queue<> を使って、push はいくらでも出来るけど pop は要素が無い場合は無理、という場合はこんな感じに作ることが出来る。
template<class T> class infinite_push_queue_adapter { public: template<class U> static bool push(std::queue<T>& queue, U v) { queue.push(v); return true; } template<class U> static bool pop(std::queue<T>& queue, U& v) { if (queue.empty()) return false; v = queue.front(); queue.pop(); return true; } }; template<class T> class blocking_queue : public blocking_container<std::queue<T>, infinite_push_queue_adapter<T> > { };
push は MaxSize 個までという制限で、コンテナとして std::stack<> を使用する場合はこんな感じ。
template<class T, boost::uint32_t MaxSize> class limited_push_stack_adapter { public: template<class U> static bool push(std::stack<T>& stack, U v) { if (stack.size() >= MaxSize) return false; stack.push(v); return true; } template<class U> static bool pop(std::stack<T>& stack, U& v) { if (stack.empty()) return false; v = stack.top(); stack.pop(); return true; } }; template<class T, boost::uint32_t MaxSize> class limited_blocking_stack : public blocking_container<std::stack<T>, limited_push_stack_adapter<T, MaxSize> > { };
blocking_container は、Container に対してデフォルトコンストラクタ呼び出しぐらいしか操作を行っていないので、どんなコンテナでも Adapter さえ作ってやれば動かすことが出来る。