00001 /* 00002 Copyright (c) 2012, The Cinder Project 00003 All rights reserved. 00004 00005 This code is designed for use with the Cinder C++ library, http://libcinder.org 00006 00007 Redistribution and use in source and binary forms, with or without modification, are permitted provided that 00008 the following conditions are met: 00009 00010 * Redistributions of source code must retain the above copyright notice, this list of conditions and 00011 the following disclaimer. 00012 * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and 00013 the following disclaimer in the documentation and/or other materials provided with the distribution. 00014 00015 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED 00016 WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 00017 PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 00018 ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 00019 TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 00020 HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 00021 NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 00022 POSSIBILITY OF SUCH DAMAGE. 00023 */ 00024 00025 00026 #pragma once 00027 00028 #include <boost/circular_buffer.hpp> 00029 #include <boost/noncopyable.hpp> 00030 #include "cinder/Thread.h" 00031 00032 namespace cinder { 00033 00034 template<typename T> 00035 class ConcurrentCircularBuffer : public boost::noncopyable { 00036 public: 00037 typedef boost::circular_buffer<T> container_type; 00038 typedef typename container_type::size_type size_type; 00039 typedef typename container_type::value_type value_type; 00040 typedef typename boost::call_traits<value_type>::param_type param_type; 00041 00042 explicit ConcurrentCircularBuffer( size_type capacity ) 00043 : mNumUnread( 0 ), mContainer( capacity ), mCanceled( false ) 00044 {} 00045 00046 void pushFront( param_type item ) { 00047 // param_type represents the "best" way to pass a parameter of type value_type to a method 00048 std::unique_lock<std::mutex> lock( mMutex ); 00049 while( ! is_not_full_impl() && ! mCanceled ) { 00050 mNotFullCond.wait( lock ); 00051 } 00052 if( mCanceled ) 00053 return; 00054 mContainer.push_front( item ); 00055 ++mNumUnread; 00056 mNotEmptyCond.notify_one(); 00057 } 00058 00059 void popBack(value_type* pItem) { 00060 std::unique_lock<std::mutex> lock( mMutex ); 00061 while( ! is_not_empty_impl() && ! mCanceled ) { 00062 mNotEmptyCond.wait( lock ); 00063 } 00064 if( mCanceled ) 00065 return; 00066 *pItem = mContainer[--mNumUnread]; 00067 mNotFullCond.notify_one(); 00068 } 00069 00071 bool tryPushFront( param_type item ) { 00072 // param_type represents the "best" way to pass a parameter of type value_type to a method 00073 std::unique_lock<std::mutex> lock( mMutex ); 00074 if( ! is_not_full_impl() ) 00075 return false; 00076 mContainer.push_front( item ); 00077 ++mNumUnread; 00078 mNotEmptyCond.notify_one(); 00079 return true; 00080 } 00081 00083 bool tryPopBack( value_type* pItem ) { 00084 std::unique_lock<std::mutex> lock( mMutex ); 00085 if( ! is_not_empty_impl() ) 00086 return false; 00087 *pItem = mContainer[--mNumUnread]; 00088 mNotFullCond.notify_one(); 00089 return true; 00090 } 00091 00092 bool isNotEmpty() { 00093 std::unique_lock<std::mutex> lock( mMutex ); 00094 return is_not_empty_impl(); 00095 } 00096 00097 bool isNotFull() { 00098 std::unique_lock<std::mutex> lock( mMutex ); 00099 return is_not_full_impl(); 00100 } 00101 00102 void cancel() { 00103 std::lock_guard<std::mutex> lock( mMutex ); 00104 mCanceled = true; 00105 mNotFullCond.notify_all(); 00106 mNotEmptyCond.notify_all(); 00107 } 00108 00110 size_t size() const { return (size_t)mContainer.capacity(); } 00111 00112 private: 00113 bool is_not_empty_impl() const { return mNumUnread > 0; } 00114 bool is_not_full_impl() const { return mNumUnread < mContainer.capacity(); } 00115 00116 size_type mNumUnread; 00117 container_type mContainer; 00118 std::mutex mMutex; 00119 std::condition_variable mNotEmptyCond; 00120 std::condition_variable mNotFullCond; 00121 bool mCanceled; 00122 }; 00123 00124 } // namespace cinder