vector.hh

00001 // This file is a part of Aurelia.
00002 // Copyright (C) 2010  Valentin David
00003 // Copyright (C) 2010  University of Bergen
00004 //
00005 // This program is free software: you can redistribute it and/or modify
00006 // it under the terms of the GNU General Public License as published by
00007 // the Free Software Foundation, either version 3 of the License, or
00008 // (at your option) any later version.
00009 //
00010 // This program is distributed in the hope that it will be useful,
00011 // but WITHOUT ANY WARRANTY; without even the implied warranty of
00012 // 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 this program.  If not, see <http://www.gnu.org/licenses/>.
00017 
00018 #ifndef __VECTOR_HH
00019 # define __VECTOR_HH
00020 
00021 # include <atomic>
00022 # include <cassert>
00023 # include "simple_stack.hh"
00024 
00025 namespace lockfree {
00026 
00027   template <typename T, size_t SEG_SIZE = 1024u,
00028             typename Alloc = std::allocator<T> >
00029   struct vector {
00030   private:
00031     struct seg_table {
00032       size_t n_segments;
00033       T** segments;
00034     };
00035 
00036     typedef typename Alloc::template rebind<seg_table>::other st_alloc_t;
00037     typedef typename Alloc::template rebind<T*>::other pt_alloc_t;
00038 
00039     Alloc alloc;
00040     st_alloc_t st_alloc;
00041     pt_alloc_t pt_alloc;
00042     std::atomic<seg_table*> table;
00043     simple_stack<seg_table*> free;
00044     T default_value;
00045 
00046     /* This is not completely thread safe. However, for our use, the vector
00047      * can be fixed if it was modified.
00048      */
00049     void set_size(size_t size) {
00050       size_t isize = (size+SEG_SIZE-1)/SEG_SIZE;
00051       seg_table* t = table.load();
00052       if (t->n_segments >= isize) {
00053         return ;
00054       }
00055       seg_table* newt = st_alloc.allocate(1);
00056       newt->n_segments = isize;
00057       newt->segments = pt_alloc.allocate(isize);
00058       size_t delete_from = isize;
00059       do {
00060         t = table.load();
00061         if (t->n_segments >= isize) {
00062           for (size_t i = delete_from; i < isize; ++i) {
00063             for (size_t j = 0; j < SEG_SIZE; ++j)
00064               alloc.destroy(newt->segments[i] + j);
00065             alloc.deallocate(newt->segments[i], SEG_SIZE);
00066           }
00067           pt_alloc.deallocate(newt->segments, isize);
00068           st_alloc.deallocate(newt, 1);
00069           return ;
00070         }
00071         size_t i = 0;
00072         for (; (i < t->n_segments); ++i) {
00073           if (i >= delete_from) {
00074             for (size_t j = 0; j < SEG_SIZE; ++j)
00075               alloc.destroy(newt->segments[i] + j);
00076             alloc.deallocate(newt->segments[i], SEG_SIZE);
00077           }
00078           newt->segments[i] = t->segments[i];
00079         }
00080         size_t delete_from_next = i;
00081         for (; (i < delete_from); ++i) {
00082           newt->segments[i] = alloc.allocate(SEG_SIZE);
00083           for (size_t j = 0; j < SEG_SIZE; ++j)
00084             alloc.construct(newt->segments[i] + j, default_value);
00085         }
00086         delete_from = delete_from_next;
00087       } while (!table.compare_exchange_strong(t, newt));
00088       free.push((seg_table*)(t));
00089     }
00090 
00091   public:
00092     vector(T default_value = T(), Alloc alloc = Alloc())
00093       : alloc(alloc), st_alloc(alloc), pt_alloc(alloc),
00094         table(st_alloc.allocate(1)), default_value(default_value) {
00095       table.load()->n_segments = 0;
00096     }
00097 
00098     T& operator[](size_t n) {
00099       seg_table* t = table.load();
00100       assert(n < (SEG_SIZE*t->n_segments));
00101       return t->segments[n/SEG_SIZE][n%SEG_SIZE];
00102     }
00103 
00104     const T& operator[](size_t n) const {
00105       seg_table* t = table.load();
00106       assert(n < (SEG_SIZE*t->n_segments));
00107       return t->segments[n/SEG_SIZE][n%SEG_SIZE];
00108     }
00109 
00110     void resize(size_t size) {
00111       set_size(size);
00112     }
00113 
00114     void collect() {
00115       while (true) {
00116         typename simple_stack<seg_table*>::wrapped w = free.pop();
00117         if (w.end())
00118           break ;
00119         seg_table *t = *w;
00120         if (t->n_segments > 0)
00121           pt_alloc.deallocate(t->segments, t->n_segments);
00122         st_alloc.deallocate(t, 1);
00123       }
00124     }
00125 
00126     size_t size() const {
00127       return table.load()->n_segments * SEG_SIZE;
00128     }
00129 
00130     ~vector() {
00131       collect();
00132       seg_table* t = table.load();
00133       for (size_t i = 0; i < t->n_segments; ++i) {
00134         for (size_t j = 0; j < SEG_SIZE; ++j) {
00135           alloc.destroy(t->segments[i] + j);
00136         }
00137         alloc.deallocate(t->segments[i], SEG_SIZE);
00138       }
00139       if (t->n_segments > 0)
00140         pt_alloc.deallocate(t->segments, t->n_segments);
00141       st_alloc.deallocate(t, 1);
00142     }
00143   };
00144 }
00145 
00146 #endif