GraphLab: Distributed Graph-Parallel API  2.1
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
lock_free_pool.hpp
1 /*
2  * Copyright (c) 2009 Carnegie Mellon University.
3  * All rights reserved.
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing,
12  * software distributed under the License is distributed on an "AS
13  * IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
14  * express or implied. See the License for the specific language
15  * governing permissions and limitations under the License.
16  *
17  * For more about this software visit:
18  *
19  * http://www.graphlab.ml.cmu.edu
20  *
21  */
22 
23 
24 #ifndef LOCK_FREE_POOL_HPP
25 #define LOCK_FREE_POOL_HPP
26 #include <stdint.h>
27 #include <vector>
28 #include <graphlab/logger/assertions.hpp>
29 #include <graphlab/parallel/atomic.hpp>
30 #include <graphlab/util/lock_free_internal.hpp>
31 #include <graphlab/util/branch_hints.hpp>
32 
33 namespace graphlab {
34  template <typename T, typename index_type = uint32_t>
35  class lock_free_pool{
36  private:
37  std::vector<T> data;
38  T* lower_ptrlimit;
39  T* upper_ptrlimit;
40  // freelist[i] points to the next free list element
41  // if freelist[i] == index_type(-1), then it is the last element
42  // allocated entries are set to index_type(0), though
43  // note that there is no way to disambiguate between allocated
44  // and non-allocated entries by simply looking at the freelist
45  std::vector<index_type> freelist;
46  typedef lock_free_internal::reference_with_counter<index_type> queue_ref_type;
47  volatile queue_ref_type freelisthead;
48 
49  public:
50  lock_free_pool(size_t poolsize = 0) { reset_pool(poolsize); }
51 
52 
53  void reset_pool(size_t poolsize) {
54  if (poolsize == 0) {
55  data.clear();
56  freelist.clear();
57  lower_ptrlimit = NULL;
58  upper_ptrlimit = NULL;
59  } else {
60  data.resize(poolsize);
61  freelist.resize(poolsize);
62  for (index_type i = 0;i < freelist.size(); ++i) {
63  freelist[i] = i + 1;
64  }
65  freelist[freelist.size() - 1] = index_type(-1);
66  lower_ptrlimit = &(data[0]);
67  upper_ptrlimit = &(data[data.size() - 1]);
68  }
69  freelisthead.q.val = 0;
70  freelisthead.q.counter = 0;
71  }
72 
73  std::vector<T>& unsafe_get_pool_ref() { return data; }
74 
75  T* alloc() {
76  // I need to atomically advance freelisthead to the freelist[head]
77  queue_ref_type oldhead;
78  queue_ref_type newhead;
79  do {
80  oldhead.combined = freelisthead.combined;
81  if (oldhead.q.val == index_type(-1)) return new T; // ran out of pool elements
82  newhead.q.val = freelist[oldhead.q.val];
83  newhead.q.counter = oldhead.q.counter + 1;
84  } while(!atomic_compare_and_swap(freelisthead.combined,
85  oldhead.combined,
86  newhead.combined));
87  freelist[oldhead.q.val] = index_type(-1);
88  return &(data[oldhead.q.val]);
89  }
90 
91  void free(T* p) {
92  // is this from the pool?
93  // if it is below the pointer limits
94  if (__unlikely__(p < lower_ptrlimit || p > upper_ptrlimit)) {
95  delete p;
96  return;
97  }
98 
99  index_type cur = index_type(p - &(data[0]));
100 
101  // prepare for free list insertion
102  // I need to atomically set freelisthead == cur
103  // and freelist[cur] = freelisthead
104  queue_ref_type oldhead;
105  queue_ref_type newhead;
106  do{
107  oldhead.combined = freelisthead.combined;
108  freelist[cur] = oldhead.q.val;
109  newhead.q.val = cur;
110  newhead.q.counter = oldhead.q.counter + 1;
111  // now try to atomically move freelisthead
112  } while(!atomic_compare_and_swap(freelisthead.combined,
113  oldhead.combined,
114  newhead.combined));
115  }
116  }; // end of lock free pool
117 
118 }; // end of graphlab namespace
119 #endif