concurrent_lru_cache.h

00001 /*
00002     Copyright 2005-2011 Intel Corporation.  All Rights Reserved.
00003 
00004     The source code contained or described herein and all documents related
00005     to the source code ("Material") are owned by Intel Corporation or its
00006     suppliers or licensors.  Title to the Material remains with Intel
00007     Corporation or its suppliers and licensors.  The Material is protected
00008     by worldwide copyright laws and treaty provisions.  No part of the
00009     Material may be used, copied, reproduced, modified, published, uploaded,
00010     posted, transmitted, distributed, or disclosed in any way without
00011     Intel's prior express written permission.
00012 
00013     No license under any patent, copyright, trade secret or other
00014     intellectual property right is granted to or conferred upon you by
00015     disclosure or delivery of the Materials, either expressly, by
00016     implication, inducement, estoppel or otherwise.  Any license under such
00017     intellectual property rights must be express and approved by Intel in
00018     writing.
00019 */
00020 #ifndef __TBB_concurrent_lru_cache_H
00021 #define __TBB_concurrent_lru_cache_H
00022 
00023 #if ! TBB_PREVIEW_CONCURRENT_LRU_CACHE
00024     #error Set TBB_PREVIEW_CONCURRENT_LRU_CACHE to include concurrent_lru_cache.h
00025 #endif
00026 
00027 #include <map>
00028 #include <list>
00029 
00030 #include "tbb_stddef.h"
00031 #include "atomic.h"
00032 #include "internal/_aggregator_impl.h"
00033 
00034 namespace tbb{
00035 namespace interface6 {
00036 
00037 
00038 template <typename key_type, typename value_type, typename value_functor_type = value_type (*)(key_type) >
00039 class concurrent_lru_cache : internal::no_assign{
00040 private:
00041     typedef concurrent_lru_cache self_type;
00042     typedef value_functor_type value_function_type;
00043     typedef std::size_t ref_counter_type;
00044     struct map_value_type;
00045     typedef std::map<key_type, map_value_type> map_storage_type;
00046     typedef std::list<typename map_storage_type::iterator> lru_list_type;
00047     struct map_value_type {
00048         value_type my_value;
00049         ref_counter_type my_ref_counter;
00050         typename lru_list_type::iterator my_lru_list_iterator;
00051         bool my_is_ready;
00052 
00053         map_value_type (value_type const& a_value,  ref_counter_type a_ref_counter,    typename lru_list_type::iterator a_lru_list_iterator, bool a_is_ready)
00054             : my_value(a_value), my_ref_counter(a_ref_counter), my_lru_list_iterator (a_lru_list_iterator), my_is_ready(a_is_ready)
00055         {}
00056     };
00057 
00058     class handle_object;
00059 
00060     struct aggregator_operation;
00061     typedef aggregator_operation aggregated_operation_type;
00062     typedef tbb::internal::aggregating_functor<self_type,aggregated_operation_type> aggregator_function_type;
00063     friend class tbb::internal::aggregating_functor<self_type,aggregated_operation_type>;
00064     typedef tbb::internal::aggregator<aggregator_function_type, aggregated_operation_type> aggregator_type;
00065 
00066 private:
00067     value_function_type my_value_function;
00068     std::size_t const my_number_of_lru_history_items;
00069     map_storage_type my_map_storage;
00070     lru_list_type my_lru_list;
00071     aggregator_type my_aggregator;
00072 
00073 public:
00074     typedef handle_object handle;
00075 
00076 public:
00077     concurrent_lru_cache(value_function_type f, std::size_t number_of_lru_history_items)
00078         : my_value_function(f),my_number_of_lru_history_items(number_of_lru_history_items)
00079     {
00080         my_aggregator.initialize_handler(aggregator_function_type(this));
00081     }
00082 
00083     handle_object operator[](key_type k){
00084         retrieve_aggregator_operation op(k);
00085         my_aggregator.execute(&op);
00086         if (op.is_new_value_needed()){
00087              op.result().second.my_value = my_value_function(k);
00088              __TBB_store_with_release(op.result().second.my_is_ready, true);
00089         }else{
00090             tbb::internal::spin_wait_while_eq(op.result().second.my_is_ready,false);
00091         }
00092         return handle_object(*this,op.result());
00093     }
00094 private:
00095     void signal_end_of_usage(typename map_storage_type::reference value_ref){
00096         signal_end_of_usage_aggregator_operation op(value_ref);
00097         my_aggregator.execute(&op);
00098     }
00099 
00100 private:
00101     struct handle_move_t:no_assign{
00102         concurrent_lru_cache & my_cache_ref;
00103         typename map_storage_type::reference my_map_record_ref;
00104         handle_move_t(concurrent_lru_cache & cache_ref, typename map_storage_type::reference value_ref):my_cache_ref(cache_ref),my_map_record_ref(value_ref) {};
00105     };
00106     class handle_object {
00107         concurrent_lru_cache * my_cache_pointer;
00108         typename map_storage_type::reference my_map_record_ref;
00109     public:
00110         handle_object(concurrent_lru_cache & cache_ref, typename map_storage_type::reference value_ref):my_cache_pointer(&cache_ref), my_map_record_ref(value_ref) {}
00111         handle_object(handle_move_t m):my_cache_pointer(&m.my_cache_ref), my_map_record_ref(m.my_map_record_ref){}
00112         operator handle_move_t(){ return move(*this);}
00113         value_type& value(){
00114             __TBB_ASSERT(my_cache_pointer,"get value from moved from object?");
00115             return my_map_record_ref.second.my_value;
00116         }
00117         ~handle_object(){
00118             if (my_cache_pointer){
00119                 my_cache_pointer->signal_end_of_usage(my_map_record_ref);
00120             }
00121         }
00122     private:
00123         friend handle_move_t move(handle_object& h){
00124             return handle_object::move(h);
00125         }
00126         static handle_move_t move(handle_object& h){
00127             __TBB_ASSERT(h.my_cache_pointer,"move from the same object twice ?");
00128             concurrent_lru_cache * cache_pointer = NULL;
00129             std::swap(cache_pointer,h.my_cache_pointer);
00130             return handle_move_t(*cache_pointer,h.my_map_record_ref);
00131         }
00132     private:
00133         void operator=(handle_object&);
00134         handle_object(handle_object &);
00135     };
00136 private:
00137     //TODO: looks like aggregator_operation is a perfect match for statically typed variant type
00138     struct aggregator_operation : tbb::internal::aggregated_operation<aggregator_operation>{
00139         enum e_op_type {op_retive, op_signal_end_of_usage};
00140         //TODO: try to use pointer to function apply_visitor here
00141         //TODO: try virtual functions and measure the difference
00142         e_op_type my_operation_type;
00143         aggregator_operation(e_op_type operation_type): my_operation_type(operation_type) {}
00144         void cast_and_handle(self_type& container ){
00145             if (my_operation_type==op_retive){
00146                 static_cast<retrieve_aggregator_operation*>(this)->handle(container);
00147             }else{
00148                 static_cast<signal_end_of_usage_aggregator_operation*>(this)->handle(container);
00149             }
00150         }
00151     };
00152     struct retrieve_aggregator_operation : aggregator_operation, private internal::no_assign {
00153         key_type my_key;
00154         typename map_storage_type::pointer my_result_map_record_pointer;
00155         bool my_is_new_value_needed;
00156         retrieve_aggregator_operation(key_type key):aggregator_operation(aggregator_operation::op_retive),my_key(key),my_is_new_value_needed(false){}
00157         void handle(self_type& container ){
00158             my_result_map_record_pointer = & container.retrieve_serial(my_key,my_is_new_value_needed);
00159         }
00160         typename map_storage_type::reference result(){ return * my_result_map_record_pointer; }
00161         bool is_new_value_needed(){return my_is_new_value_needed;}
00162     };
00163     struct signal_end_of_usage_aggregator_operation : aggregator_operation, private internal::no_assign {
00164         typename map_storage_type::reference my_map_record_ref;
00165         signal_end_of_usage_aggregator_operation(typename map_storage_type::reference map_record_ref):aggregator_operation(aggregator_operation::op_signal_end_of_usage),my_map_record_ref(map_record_ref){}
00166         void handle(self_type& container ){
00167             container.signal_end_of_usage_serial(my_map_record_ref);
00168         }
00169     };
00170 
00171 private:
00172    void handle_operations(aggregator_operation* op_list){
00173        while(op_list){
00174            op_list->cast_and_handle(*this);
00175            aggregator_operation* tmp = op_list;
00176            op_list=op_list->next;
00177            tbb::internal::itt_store_word_with_release(tmp->status, uintptr_t(1));
00178        }
00179    }
00180 
00181 private:
00182    typename map_storage_type::reference retrieve_serial(key_type k, bool& is_new_value_needed){
00183         typename map_storage_type::iterator it = my_map_storage.find(k);
00184         if (it == my_map_storage.end()){
00185             it = my_map_storage.insert(it,std::make_pair(k,map_value_type(value_type(),0,my_lru_list.end(),false)));
00186             is_new_value_needed = true;
00187         }else {
00188             typename lru_list_type::iterator list_it = it->second.my_lru_list_iterator;
00189             if (list_it!=my_lru_list.end()) {
00190                 __TBB_ASSERT(!it->second.my_ref_counter,"item to be evicted should not have a live references");
00191                 //item is going to be used. Therefore it is not a subject for eviction
00192                 //so - remove it from LRU history.
00193                 my_lru_list.erase(list_it);
00194                 it->second.my_lru_list_iterator= my_lru_list.end();
00195             }
00196         }
00197         ++(it->second.my_ref_counter);
00198         return *it;
00199     }
00200 
00201     void signal_end_of_usage_serial(typename map_storage_type::reference map_record_ref){
00202         typename map_storage_type::iterator it = my_map_storage.find(map_record_ref.first);
00203         __TBB_ASSERT(it!=my_map_storage.end(),"cache should not return past-end iterators to outer world");
00204         __TBB_ASSERT(&(*it) == &map_record_ref,"dangling reference has been returned to outside world? data race ?");
00205         __TBB_ASSERT( my_lru_list.end()== std::find(my_lru_list.begin(),my_lru_list.end(),it),
00206                 "object in use should not be in list of unused objects ");
00207         if (! --(it->second.my_ref_counter)){
00208             //it was the last reference so put it to the LRU history
00209             if (my_lru_list.size()>=my_number_of_lru_history_items){
00210                 //evict items in order to get a space
00211                 size_t number_of_elements_to_evict = 1 + my_lru_list.size() - my_number_of_lru_history_items;
00212                 for (size_t i=0; i<number_of_elements_to_evict; ++i){
00213                     typename map_storage_type::iterator it_to_evict = my_lru_list.back();
00214                     __TBB_ASSERT(!it_to_evict->second.my_ref_counter,"item to be evicted should not have a live references");
00215                     my_lru_list.pop_back();
00216                     my_map_storage.erase(it_to_evict);
00217                 }
00218             }
00219             my_lru_list.push_front(it);
00220             it->second.my_lru_list_iterator = my_lru_list.begin();
00221         }
00222     }
00223 };
00224 } // namespace interface6
00225 
00226 using interface6::concurrent_lru_cache;
00227 
00228 } // namespace tbb
00229 #endif //__TBB_concurrent_lru_cache_H

Copyright © 2005-2011 Intel Corporation. All Rights Reserved.

Intel, Pentium, Intel Xeon, Itanium, Intel XScale and VTune are registered trademarks or trademarks of Intel Corporation or its subsidiaries in the United States and other countries.

* Other names and brands may be claimed as the property of others.