- Main Page
- Related Pages
- Modules
- Namespaces
- Classes
- Files

- File List
- File Members

# concurrent_priority_queue.h

    00001 /*
    00002     Copyright 2005-2012 Intel Corporation.  All Rights Reserved.
    00003
    00004     This file is part of Threading Building Blocks.
    00005
    00006     Threading Building Blocks is free software; you can redistribute it
    00007     and/or modify it under the terms of the GNU General Public License
    00008     version 2 as published by the Free Software Foundation.
    00009
    00010     Threading Building Blocks is distributed in the hope that it will be
    00011     useful, but WITHOUT ANY WARRANTY; without even the implied warranty
    00012     of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    00013     GNU General Public License for more details.
    00014
    00015     You should have received a copy of the GNU General Public License
    00016     along with Threading Building Blocks; if not, write to the Free Software
    00017     Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
    00018
    00019     As a special exception, you may use this file as part of a free software
    00020     library without restriction.  Specifically, if other files instantiate
    00021     templates or use macros or inline functions from this file, or you compile
    00022     this file and link it with other files to produce an executable, this
    00023     file does not by itself cause the resulting executable to be covered by
    00024     the GNU General Public License.  This exception does not however
    00025     invalidate any other reasons why the executable file might be covered by
    00026     the GNU General Public License.
    00027 */
    00028
    00029 #ifndef __TBB_concurrent_priority_queue_H
    00030 #define __TBB_concurrent_priority_queue_H
    00031
    00032 #include "atomic.h"
    00033 #include "cache_aligned_allocator.h"
    00034 #include "tbb_exception.h"
    00035 #include "tbb_stddef.h"
    00036 #include "tbb_profiling.h"
    00037 #include "internal/_aggregator_impl.h"
    00038 #include <vector>
    00039 #include <iterator>
    00040 #include <functional>
    00041
    00042 namespace tbb {
    00043 namespace interface5 {
    00044
    00045 using namespace tbb::internal;
    00046
    00048 template <typename T, typename Compare=std::less<T>, typename A=cache_aligned_allocator<T> >
    00049 class concurrent_priority_queue {
    00050  public:
    00052     typedef T value_type;
    00053
    00055     typedef T& reference;
    00056
    00058     typedef const T& const_reference;
    00059
    00061     typedef size_t size_type;
    00062
    00064     typedef ptrdiff_t difference_type;
    00065
    00067     typedef A allocator_type;
    00068
    00070     explicit concurrent_priority_queue(const allocator_type& a = allocator_type()) : mark(0), my_size(0), data(a)
    00071     {
    00072         my_aggregator.initialize_handler(my_functor_t(this));
    00073     }
    00074
    00076     explicit concurrent_priority_queue(size_type init_capacity, const allocator_type& a = allocator_type()) :
    00077         mark(0), my_size(0), data(a)
    00078     {
    00079         data.reserve(init_capacity);
    00080         my_aggregator.initialize_handler(my_functor_t(this));
    00081     }
    00082
    00084     template<typename InputIterator>
    00085     concurrent_priority_queue(InputIterator begin, InputIterator end, const allocator_type& a = allocator_type()) :
    00086         data(begin, end, a)
    00087     {
    00088         mark = 0;
    00089         my_aggregator.initialize_handler(my_functor_t(this));
    00090         heapify();
    00091         my_size = data.size();
    00092     }
    00093
    00095
    00096     explicit concurrent_priority_queue(const concurrent_priority_queue& src) : mark(src.mark),
    00097         my_size(src.my_size), data(src.data.begin(), src.data.end(), src.data.get_allocator())
    00098     {
    00099         my_aggregator.initialize_handler(my_functor_t(this));
    00100         heapify();
    00101     }
    00102
    00104
    00105     concurrent_priority_queue(const concurrent_priority_queue& src, const allocator_type& a) : mark(src.mark),
    00106         my_size(src.my_size), data(src.data.begin(), src.data.end(), a)
    00107     {
    00108         my_aggregator.initialize_handler(my_functor_t(this));
    00109         heapify();
    00110     }
    00111
    00113
    00114     concurrent_priority_queue& operator=(const concurrent_priority_queue& src) {
    00115         if (this != &src) {
    00116             std::vector<value_type, allocator_type>(src.data.begin(), src.data.end(), src.data.get_allocator()).swap(data);
    00117             mark = src.mark;
    00118             my_size = src.my_size;
    00119         }
    00120         return *this;
    00121     }
    00122
    00124
    00126     bool empty() const { return size()==0; }
    00127
    00129
    00131     size_type size() const { return __TBB_load_with_acquire(my_size); }
    00132
    00134
    00135     void push(const_reference elem) {
    00136         cpq_operation op_data(elem, PUSH_OP);
    00137         my_aggregator.execute(&op_data);
    00138         if (op_data.status == FAILED) // exception thrown
    00139             throw_exception(eid_bad_alloc);
    00140     }
    00141
    00143
    00146     bool try_pop(reference elem) {
    00147         cpq_operation op_data(POP_OP);
    00148         op_data.elem = &elem;
    00149         my_aggregator.execute(&op_data);
    00150         return op_data.status==SUCCEEDED;
    00151     }
    00152
    00154
    00157     void clear() {
    00158         data.clear();
    00159         mark = 0;
    00160         my_size = 0;
    00161     }
    00162
    00164
    00165     void swap(concurrent_priority_queue& q) {
    00166         data.swap(q.data);
    00167         std::swap(mark, q.mark);
    00168         std::swap(my_size, q.my_size);
    00169     }
    00170
    00172     allocator_type get_allocator() const { return data.get_allocator(); }
    00173
    00174  private:
    00175     enum operation_type {INVALID_OP, PUSH_OP, POP_OP};
    00176     enum operation_status { WAIT=0, SUCCEEDED, FAILED };
    00177
    00178     class cpq_operation : public aggregated_operation<cpq_operation> {
    00179      public:
    00180         operation_type type;
    00181         union {
    00182             value_type *elem;
    00183             size_type sz;
    00184         };
    00185         cpq_operation(const_reference e, operation_type t) :
    00186             type(t), elem(const_cast<value_type*>(&e)) {}
    00187         cpq_operation(operation_type t) : type(t) {}
    00188     };
    00189
    00190     class my_functor_t {
    00191         concurrent_priority_queue<T, Compare, A> *cpq;
    00192      public:
    00193         my_functor_t() {}
    00194         my_functor_t(concurrent_priority_queue<T, Compare, A> *cpq_) : cpq(cpq_) {}
    00195         void operator()(cpq_operation* op_list) {
    00196             cpq->handle_operations(op_list);
    00197         }
    00198     };
    00199
    00200     aggregator< my_functor_t, cpq_operation> my_aggregator;
    00202     char padding1[NFS_MaxLineSize - sizeof(aggregator< my_functor_t, cpq_operation >)];
    00204     size_type mark;
    00205     __TBB_atomic size_type my_size;
    00206     Compare compare;
    00208     char padding2[NFS_MaxLineSize - (2*sizeof(size_type)) - sizeof(Compare)];
    00210
    00227     std::vector<value_type, allocator_type> data;
    00228
    00229     void handle_operations(cpq_operation *op_list) {
    00230         cpq_operation *tmp, *pop_list=NULL;
    00231
    00232         __TBB_ASSERT(mark == data.size(), NULL);
    00233
    00234         // First pass processes all constant (amortized; reallocation may happen) time pushes and pops.
    00235         while (op_list) {
    00236             // ITT note: &(op_list->status) tag is used to cover accesses to op_list
    00237             // node. This thread is going to handle the operation, and so will acquire it
    00238             // and perform the associated operation w/o triggering a race condition; the
    00239             // thread that created the operation is waiting on the status field, so when
    00240             // this thread is done with the operation, it will perform a
    00241             // store_with_release to give control back to the waiting thread in
    00242             // aggregator::insert_operation.
    00243             call_itt_notify(acquired, &(op_list->status));
    00244             __TBB_ASSERT(op_list->type != INVALID_OP, NULL);
    00245             tmp = op_list;
    00246             op_list = itt_hide_load_word(op_list->next);
    00247             if (tmp->type == PUSH_OP) {
    00248                 __TBB_TRY {
    00249                     data.push_back(*(tmp->elem));
    00250                     __TBB_store_with_release(my_size, my_size+1);
    00251                     itt_store_word_with_release(tmp->status, uintptr_t(SUCCEEDED));
    00252                 } __TBB_CATCH(...) {
    00253                     itt_store_word_with_release(tmp->status, uintptr_t(FAILED));
    00254                 }
    00255             }
    00256             else { // tmp->type == POP_OP
    00257                 __TBB_ASSERT(tmp->type == POP_OP, NULL);
    00258                 if (mark < data.size() &&
    00259                     compare(data[0], data[data.size()-1])) {
    00260                     // there are newly pushed elems and the last one
    00261                     // is higher than top
    00262                     *(tmp->elem) = data[data.size()-1]; // copy the data
    00263                     __TBB_store_with_release(my_size, my_size-1);
    00264                     itt_store_word_with_release(tmp->status, uintptr_t(SUCCEEDED));
    00265                     data.pop_back();
    00266                     __TBB_ASSERT(mark<=data.size(), NULL);
    00267                 }
    00268                 else { // no convenient item to pop; postpone
    00269                     itt_hide_store_word(tmp->next, pop_list);
    00270                     pop_list = tmp;
    00271                 }
    00272             }
    00273         }
    00274
    00275         // second pass processes pop operations
    00276         while (pop_list) {
    00277             tmp = pop_list;
    00278             pop_list = itt_hide_load_word(pop_list->next);
    00279             __TBB_ASSERT(tmp->type == POP_OP, NULL);
    00280             if (data.empty()) {
    00281                 itt_store_word_with_release(tmp->status, uintptr_t(FAILED));
    00282             }
    00283             else {
    00284                 __TBB_ASSERT(mark<=data.size(), NULL);
    00285                 if (mark < data.size() &&
    00286                     compare(data[0], data[data.size()-1])) {
    00287                     // there are newly pushed elems and the last one is
    00288                     // higher than top
    00289                     *(tmp->elem) = data[data.size()-1]; // copy the data
    00290                     __TBB_store_with_release(my_size, my_size-1);
    00291                     itt_store_word_with_release(tmp->status, uintptr_t(SUCCEEDED));
    00292                     data.pop_back();
    00293                 }
    00294                 else { // extract top and push last element down heap
    00295                     *(tmp->elem) = data[0]; // copy the data
    00296                     __TBB_store_with_release(my_size, my_size-1);
    00297                     itt_store_word_with_release(tmp->status, uintptr_t(SUCCEEDED));
    00298                     reheap();
    00299                 }
    00300             }
    00301         }
    00302
    00303         // heapify any leftover pushed elements before doing the next
    00304         // batch of operations
    00305         if (mark<data.size()) heapify();
    00306         __TBB_ASSERT(mark == data.size(), NULL);
    00307     }
    00308
    00310     void heapify() {
    00311         if (!mark && data.size()>0) mark = 1;
    00312         for (; mark<data.size(); ++mark) {
    00313             // for each unheapified element under size
    00314             size_type cur_pos = mark;
    00315             value_type to_place = data[mark];
    00316             do { // push to_place up the heap
    00317                 size_type parent = (cur_pos-1)>>1;
    00318                 if (!compare(data[parent], to_place)) break;
    00319                 data[cur_pos] = data[parent];
    00320                 cur_pos = parent;
    00321             } while( cur_pos );
    00322             data[cur_pos] = to_place;
    00323         }
    00324     }
    00325
    00327
    00328     void reheap() {
    00329         size_type cur_pos=0, child=1;
    00330
    00331         while (child < mark) {
    00332             size_type target = child;
    00333             if (child+1 < mark && compare(data[child], data[child+1]))
    00334                 ++target;
    00335             // target now has the higher priority child
    00336             if (compare(data[target], data[data.size()-1])) break;
    00337             data[cur_pos] = data[target];
    00338             cur_pos = target;
    00339             child = (cur_pos<<1)+1;
    00340         }
    00341         data[cur_pos] = data[data.size()-1];
    00342         data.pop_back();
    00343         if (mark > data.size()) mark = data.size();
    00344     }
    00345 };
    00346
    00347 } // namespace interface5
    00348
    00349 using interface5::concurrent_priority_queue;
    00350
    00351 } // namespace tbb
    00352
    00353 #endif /* __TBB_concurrent_priority_queue_H */

Copyright © 2005-2012 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.