1 /* Quorum
2  * Copyright (C) 2012  Genome group at University of Maryland.
3  *
4  * This program is free software: you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License as
6  * published by the Free Software Foundation, either version 3 of the
7  * License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
16  */
17 
18 #ifndef __JELLYFISH_ATOMIC_BITS_ARRAY_HPP__
19 #define __JELLYFISH_ATOMIC_BITS_ARRAY_HPP__
20 
21 #include <stdexcept>
22 #include <iterator>
23 
24 #include <jellyfish/allocators_mmap.hpp>
25 #include <jellyfish/atomic_gcc.hpp>
26 #include <jellyfish/divisor.hpp>
27 
28 namespace jellyfish {
29 template<typename Value, typename T, typename Derived>
30 class atomic_bits_array_base {
31   static const int       w_ = sizeof(T) * 8;
32   const int              bits_;
33   const size_t           size_;
34   const T                mask_;
35   const jflib::divisor64 d_;
36   size_t                 size_bytes_;
37   T*                     data_;
38   static atomic::gcc     atomic_;
39 
40   friend class iterator;
41   class iterator : public std::iterator<std::input_iterator_tag, Value> {
42     friend class atomic_bits_array_base;
43     const atomic_bits_array_base& ary_;
44     T*                            word_;
45     T                             mask_;
46     int                           off_;
47 
iterator(const atomic_bits_array_base & a,T * w,T m,int o)48     iterator(const atomic_bits_array_base& a, T* w, T m, int o) : ary_(a), word_(w), mask_(m), off_(o) { }
49   public:
operator ==(const iterator & rhs) const50     bool operator==(const iterator& rhs) const { return word_ == rhs.word_ && off_ == rhs.off_; }
operator !=(const iterator & rhs) const51     bool operator!=(const iterator& rhs) const { return word_ != rhs.word_ || off_ != rhs.off_; }
operator *() const52     Value operator*() const { return static_cast<Value>((*word_ & mask_) >> off_); }
operator ->() const53     Value* operator->() const { return 0; }
operator ++()54     iterator& operator++() {
55       off_ += ary_.bits_;
56       if(off_ + ary_.bits_ < w_) {
57         mask_ <<= ary_.bits_;
58       } else {
59         ++word_;
60         mask_ = ary_.mask_;
61         off_  = 0;
62       }
63       return *this;
64     }
operator ++(int)65     iterator operator++(int) {
66       iterator res(*this);
67       ++*this;
68       return res;
69     }
70   };
71 
72   class element_proxy {
73     T*        word_;
74     const T   mask_;
75     const int off_;
76     T         prev_word_;
77 
get_val(T v) const78     Value get_val(T v) const {
79       return static_cast<Value>((v & mask_) >> off_);
80     }
81 
82   public:
element_proxy(T * word,T mask,int off)83     element_proxy(T* word, T mask, int off) :
84       word_(word), mask_(mask), off_(off)
85     { }
86 
operator Value() const87     operator Value() const { return get_val(*word_); }
get()88     Value get() {
89       prev_word_ = *word_;
90       return get_val(prev_word_);
91     }
92 
set(Value & nval)93     bool set(Value& nval) {
94       Value pval;
95       Value cval = get_val(prev_word_);
96       do {
97         pval = cval;
98         const T new_word    = (prev_word_ & ~mask_) | ((static_cast<T>(nval) << off_) & mask_);
99         const T actual_word = atomic_.cas(word_, prev_word_, new_word);
100         if(__builtin_expect(actual_word == prev_word_, 1))
101           return true;
102         prev_word_ = actual_word;
103         cval = get_val(prev_word_);
104       } while(pval == cval);
105       nval = cval;
106       return false;
107     }
108   };
109 
110 public:
atomic_bits_array_base(int bits,size_t size)111   atomic_bits_array_base(int bits, // Number of bits per entry
112                          size_t size) : // Number of entries
113     bits_(bits),
114     size_(size),
115     mask_((T)-1 >> (w_ - bits)), // mask of one entry at the LSB of a word
116     d_(w_ / bits),              // divisor of the number of entries per word
117     size_bytes_((size / d_ + (size % d_ != 0)) * sizeof(T)),
118     data_(static_cast<Derived*>(this)->alloc_data(size_bytes_))
119   {
120     static_assert(sizeof(T) >= sizeof(Value), "Container type T must have at least as many bits as value type");
121     if((size_t)bits > sizeof(Value) * 8)
122       throw std::runtime_error("The number of bits per entry must be less than the number of bits in the value type");
123     if(!data_)
124       throw std::runtime_error("Can't allocate memory for atomic_bits_array");
125   }
126 
127   // Return the element at position pos. No check for out of bounds.
operator [](size_t pos)128   element_proxy operator[](size_t pos) {
129     uint64_t q, r;
130     d_.division(pos, q, r);
131     const int off = r * bits_;
132     return element_proxy(data_ + q, mask_ << off, off);
133   }
operator [](size_t pos) const134   const element_proxy operator[](size_t pos) const {
135     uint64_t q, r;
136     d_.division(pos, q, r);
137     const int off = r * bits_;
138     return element_proxy(data_ + q, mask_ << off, off);
139   }
write(std::ostream & os) const140   void write(std::ostream& os) const {
141     os.write((const char*)data_, size_bytes_);
142   }
size_bytes() const143   size_t size_bytes() const { return size_bytes_; }
bits() const144   int bits() const { return bits_; }
145 
begin() const146   iterator begin() const { return iterator(*this, data_, mask_, 0); }
end() const147   iterator end() const {
148     uint64_t q, r;
149     d_.division(size_, q, r);
150     const int off = r * bits_;
151     return iterator(*this, data_ + q, mask_ << off, off);
152   }
153 };
154 
155 template<typename Value, typename T = uint64_t>
156 class atomic_bits_array :
157     protected allocators::mmap,
158     public atomic_bits_array_base<Value, T, atomic_bits_array<Value, T> >
159 {
160   typedef atomic_bits_array_base<Value, T, atomic_bits_array<Value, T> > super;
161   friend class atomic_bits_array_base<Value, T, atomic_bits_array<Value, T> >;
162 public:
atomic_bits_array(int bits,size_t size)163   atomic_bits_array(int bits, size_t size) :
164     allocators::mmap(),
165     super(bits, size)
166   { }
167 
168 protected:
alloc_data(size_t s)169   T* alloc_data(size_t s) {
170     allocators::mmap::realloc(s);
171     return (T*)allocators::mmap::get_ptr();
172   }
173 };
174 
175 struct mem_info {
176   void*  ptr_;
177   size_t bytes_;
mem_infojellyfish::mem_info178   mem_info(void* ptr, size_t bytes) : ptr_(ptr), bytes_(bytes) { }
179 };
180 template<typename Value, typename T = uint64_t>
181 class atomic_bits_array_raw :
182     protected mem_info,
183     public atomic_bits_array_base<Value, T, atomic_bits_array_raw<Value, T> >
184 {
185   typedef atomic_bits_array_base<Value, T, atomic_bits_array_raw<Value, T> > super;
186   friend class atomic_bits_array_base<Value, T, atomic_bits_array_raw<Value, T> >;
187 public:
atomic_bits_array_raw(void * ptr,size_t bytes,int bits,size_t size)188   atomic_bits_array_raw(void* ptr, size_t bytes, int bits, size_t size) :
189     mem_info(ptr, bytes),
190     super(bits, size)
191   { }
192 
193 protected:
alloc_data(size_t s)194   T* alloc_data(size_t s) {
195     assert(bytes_ == s);
196     return (T*)ptr_;
197   }
198 };
199 
200 } // namespace jellyfish
201 
202 #endif /* __JELLYFISH_ATOMIC_BITS_ARRAY_HPP__ */
203