CUBRID Engine  latest
lockfree_bitmap.cpp
Go to the documentation of this file.
1 /*
2  * Copyright 2008 Search Solution Corporation
3  * Copyright 2016 CUBRID Corporation
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, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  *
17  */
18 
19 #include "lockfree_bitmap.hpp"
20 
21 #include "memory_alloc.h"
22 
23 #include <cassert>
24 
25 namespace lockfree
26 {
27  const float bitmap::FULL_USAGE_RATIO = 1.0f;
29 
30  static void lf_bitmap_init (LF_BITMAP *bitmap, LF_BITMAP_STYLE style, int entries_cnt, float usage_threshold);
31  static void lf_bitmap_destroy (LF_BITMAP *bitmap);
32  static int lf_bitmap_get_entry (LF_BITMAP *bitmap);
33  static void lf_bitmap_free_entry (LF_BITMAP *bitmap, int entry_idx);
34 
36  : bitfield (NULL)
37  , entry_count (0)
38  , entry_count_in_use { 0 }
39  , style (chunking_style::ONE_CHUNK)
41  , start_idx { 0 }
42  {
43  }
44 
46  {
47  destroy ();
48  }
49 
50  void
51  bitmap::init (chunking_style style_arg, int entries_count_arg, float usage_ratio_arg)
52  {
53  lf_bitmap_init (this, style_arg, entries_count_arg, usage_ratio_arg);
54  }
55 
56  void
58  {
59  lf_bitmap_destroy (this);
60  }
61 
62  int
64  {
65  return lf_bitmap_get_entry (this);
66  }
67 
68  void
69  bitmap::free_entry (int entry_idx)
70  {
71  lf_bitmap_free_entry (this, entry_idx);
72  }
73 
74  bool
75  bitmap::is_full () const
76  {
77  return ((float) entry_count_in_use.load ()) >= usage_threshold * entry_count;
78  }
79 
80  static void
82  {
83  size_t chunk_count;
84  unsigned int mask, chunk;
85  int i;
86 
87  assert (bitmap != NULL);
88  /* We only allow full usage for LF_BITMAP_ONE_CHUNK. */
89  assert (style == LF_BITMAP_LIST_OF_CHUNKS || usage_threshold == 1.0f);
90 
91  bitmap->style = style;
92  bitmap->entry_count = entries_cnt;
93  bitmap->entry_count_in_use = 0;
95  if (usage_threshold < 0.0f || usage_threshold > 1.0f)
96  {
97  bitmap->usage_threshold = 1.0f;
98  }
99  bitmap->start_idx = 0;
100 
101  /* initialize bitfield */
102  chunk_count = (size_t) CEIL_PTVDIV (entries_cnt, LF_BITFIELD_WORD_SIZE);
103  bitmap->bitfield = new std::atomic<unsigned int>[chunk_count] ();
104  for (size_t it = 0; it < chunk_count; it++)
105  {
106  bitmap->bitfield[it] = 0;
107  }
108 
109  /* pad out the rest bits with 1, It will simplify the code in lf_bitmap_get_entry() */
110  if (entries_cnt % LF_BITFIELD_WORD_SIZE != 0)
111  {
112  chunk = 0;
113  mask = 1;
114  for (i = entries_cnt % LF_BITFIELD_WORD_SIZE, mask <<= i; i < LF_BITFIELD_WORD_SIZE; i++, mask <<= 1)
115  {
116  chunk |= mask;
117  }
118  bitmap->bitfield[chunk_count - 1] = chunk;
119  }
120  }
121 
122  static void
124  {
125  assert (bitmap != NULL);
126  delete [] bitmap->bitfield;
127  bitmap->bitfield = NULL;
128  bitmap->entry_count = 0;
129  bitmap->entry_count_in_use = 0;
130  bitmap->style = LF_BITMAP_ONE_CHUNK;
131  bitmap->usage_threshold = 1.0f;
132  bitmap->start_idx = 0;
133  }
134 
135  static int
137  {
138  int chunk_count;
139  unsigned int mask, chunk, start_idx;
140  int i, chunk_idx, slot_idx;
141 
142  assert (bitmap != NULL);
143  assert (bitmap->entry_count > 0);
144  assert (bitmap->bitfield != NULL);
145 
146  chunk_count = CEIL_PTVDIV (bitmap->entry_count, LF_BITFIELD_WORD_SIZE);
147 
148 restart: /* wait-free process */
149  chunk_idx = -1;
150  slot_idx = -1;
151 
152  /* when reaches the predefined threshold */
153  if (LF_BITMAP_IS_FULL (bitmap))
154  {
155  return -1;
156  }
157 
158 #if defined (SERVER_MODE)
159  /* round-robin to get start chunk index */
160  start_idx = bitmap->start_idx++;
161  start_idx = start_idx % ((unsigned int) chunk_count);
162 #else
163  /* iterate from the last allocated chunk */
164  start_idx = bitmap->start_idx;
165 #endif
166 
167  /* find a chunk with an empty slot */
168  i = start_idx;
169  do
170  {
171  chunk = bitmap->bitfield[i].load ();
172  if (~chunk)
173  {
174  chunk_idx = i;
175  break;
176  }
177 
178  i++;
179  if (i >= chunk_count)
180  {
181  i = 0;
182  }
183  }
184  while (i != (int) start_idx);
185 
186  if (chunk_idx == -1)
187  {
188  /* full? */
189  if (bitmap->style == LF_BITMAP_ONE_CHUNK)
190  {
191  assert (false);
192  }
193  return -1;
194  }
195 
196  /* find first empty slot in chunk */
197  for (i = 0, mask = 1; i < LF_BITFIELD_WORD_SIZE; i++, mask <<= 1)
198  {
199  if ((~chunk) & mask)
200  {
201  slot_idx = i;
202  break;
203  }
204  }
205 
206  if (slot_idx == -1)
207  {
208  /* chunk was filled in the meantime */
209  goto restart;
210  }
211 
212  assert ((chunk_idx * LF_BITFIELD_WORD_SIZE + slot_idx) < bitmap->entry_count);
213  do
214  {
215  chunk = bitmap->bitfield[chunk_idx].load ();
216  if (chunk & mask)
217  {
218  /* slot was marked by someone else */
219  goto restart;
220  }
221  }
222  while (!bitmap->bitfield[chunk_idx].compare_exchange_weak (chunk, chunk | mask));
223 
224  if (bitmap->style == LF_BITMAP_LIST_OF_CHUNKS)
225  {
226  bitmap->entry_count_in_use++;
227  }
228 
229 #if !defined (SERVER_MODE)
230  bitmap->start_idx = chunk_idx;
231 #endif
232 
233  return chunk_idx * LF_BITFIELD_WORD_SIZE + slot_idx;
234  }
235 
236  static void
238  {
239  unsigned int mask, inverse_mask, curr;
240  int pos, bit;
241 
242  assert (bitmap != NULL);
243  assert (entry_idx >= 0);
244  assert (entry_idx < bitmap->entry_count);
245 
246  /* clear bitfield so slot may be reused */
247  pos = entry_idx / LF_BITFIELD_WORD_SIZE;
248  bit = entry_idx % LF_BITFIELD_WORD_SIZE;
249  inverse_mask = (unsigned int) (1 << bit);
250  mask = ~inverse_mask;
251 
252  do
253  {
254  /* clear slot */
255  curr = bitmap->bitfield[pos].load ();
256 
257  if (! (curr & inverse_mask))
258  {
259  assert (false);
260  return;
261  }
262  }
263  while (!bitmap->bitfield[pos].compare_exchange_weak (curr, curr & mask));
264 
265  if (bitmap->style == LF_BITMAP_LIST_OF_CHUNKS)
266  {
267  bitmap->entry_count_in_use--;
268  }
269 
270 #if !defined (SERVER_MODE)
271  bitmap->start_idx = pos; /* hint for a free slot */
272 #endif
273  }
274 } // namespace lockfree
static const float FULL_USAGE_RATIO
static const LF_BITMAP_STYLE LF_BITMAP_ONE_CHUNK
bool is_full() const
static void lf_bitmap_destroy(LF_BITMAP *bitmap)
void free_entry(int entry_idx)
#define LF_BITMAP_IS_FULL(bitmap)
static const float NINTETYFIVE_PERCENTILE_USAGE_RATIO
#define assert(x)
static void lf_bitmap_init(LF_BITMAP *bitmap, LF_BITMAP_STYLE style, int entries_cnt, float usage_threshold)
chunking_style style
std::atomic< int > entry_count_in_use
#define NULL
Definition: freelistheap.h:34
static const LF_BITMAP_STYLE LF_BITMAP_LIST_OF_CHUNKS
#define LF_BITFIELD_WORD_SIZE
#define CEIL_PTVDIV(dividend, divisor)
Definition: memory_alloc.h:50
std::atomic< unsigned int > * bitfield
static void lf_bitmap_free_entry(LF_BITMAP *bitmap, int entry_idx)
int i
Definition: dynamic_load.c:954
std::atomic< unsigned int > start_idx
static int lf_bitmap_get_entry(LF_BITMAP *bitmap)
void init(chunking_style style, int entries_count, float usage_ratio)