MySQL 5.6.14 Source Code Document
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
lf_hash.c
1 /* Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
2 
3  This program is free software; you can redistribute it and/or modify
4  it under the terms of the GNU General Public License as published by
5  the Free Software Foundation; version 2 of the License.
6 
7  This program is distributed in the hope that it will be useful,
8  but WITHOUT ANY WARRANTY; without even the implied warranty of
9  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10  GNU General Public License for more details.
11 
12  You should have received a copy of the GNU General Public License
13  along with this program; if not, write to the Free Software
14  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
15 
16 /*
17  extensible hash
18 
19  TODO
20  try to get rid of dummy nodes ?
21  for non-unique hash, count only _distinct_ values
22  (but how to do it in lf_hash_delete ?)
23 */
24 #include <my_global.h>
25 #include <m_string.h>
26 #include <my_sys.h>
27 #include <my_bit.h>
28 #include <lf.h>
29 
30 LF_REQUIRE_PINS(3)
31 
32 /* An element of the list */
33 typedef struct {
34  intptr volatile link; /* a pointer to the next element in a listand a flag */
35  uint32 hashnr; /* reversed hash number, for sorting */
36  const uchar *key;
37  size_t keylen;
38  /*
39  data is stored here, directly after the keylen.
40  thus the pointer to data is (void*)(slist_element_ptr+1)
41  */
42 } LF_SLIST;
43 
44 const int LF_HASH_OVERHEAD= sizeof(LF_SLIST);
45 
46 /*
47  a structure to pass the context (pointers two the three successive elements
48  in a list) from lfind to linsert/ldelete
49 */
50 typedef struct {
51  intptr volatile *prev;
52  LF_SLIST *curr, *next;
53 } CURSOR;
54 
55 /*
56  the last bit in LF_SLIST::link is a "deleted" flag.
57  the helper macros below convert it to a pure pointer or a pure flag
58 */
59 #define PTR(V) (LF_SLIST *)((V) & (~(intptr)1))
60 #define DELETED(V) ((V) & 1)
61 
62 /*
63  DESCRIPTION
64  Search for hashnr/key/keylen in the list starting from 'head' and
65  position the cursor. The list is ORDER BY hashnr, key
66 
67  RETURN
68  0 - not found
69  1 - found
70 
71  NOTE
72  cursor is positioned in either case
73  pins[0..2] are used, they are NOT removed on return
74 */
75 static int lfind(LF_SLIST * volatile *head, CHARSET_INFO *cs, uint32 hashnr,
76  const uchar *key, uint keylen, CURSOR *cursor, LF_PINS *pins)
77 {
78  uint32 cur_hashnr;
79  const uchar *cur_key;
80  uint cur_keylen;
81  intptr link;
82 
83 retry:
84  cursor->prev= (intptr *)head;
85  do { /* PTR() isn't necessary below, head is a dummy node */
86  cursor->curr= (LF_SLIST *)(*cursor->prev);
87  _lf_pin(pins, 1, cursor->curr);
88  } while (*cursor->prev != (intptr)cursor->curr && LF_BACKOFF);
89  for (;;)
90  {
91  if (unlikely(!cursor->curr))
92  return 0; /* end of the list */
93  do {
94  /* QQ: XXX or goto retry ? */
95  link= cursor->curr->link;
96  cursor->next= PTR(link);
97  _lf_pin(pins, 0, cursor->next);
98  } while (link != cursor->curr->link && LF_BACKOFF);
99  cur_hashnr= cursor->curr->hashnr;
100  cur_key= cursor->curr->key;
101  cur_keylen= cursor->curr->keylen;
102  if (*cursor->prev != (intptr)cursor->curr)
103  {
104  (void)LF_BACKOFF;
105  goto retry;
106  }
107  if (!DELETED(link))
108  {
109  if (cur_hashnr >= hashnr)
110  {
111  int r= 1;
112  if (cur_hashnr > hashnr ||
113  (r= my_strnncoll(cs, (uchar*) cur_key, cur_keylen, (uchar*) key,
114  keylen)) >= 0)
115  return !r;
116  }
117  cursor->prev= &(cursor->curr->link);
118  _lf_pin(pins, 2, cursor->curr);
119  }
120  else
121  {
122  /*
123  we found a deleted node - be nice, help the other thread
124  and remove this deleted node
125  */
126  if (my_atomic_casptr((void **)cursor->prev,
127  (void **)&cursor->curr, cursor->next))
128  _lf_alloc_free(pins, cursor->curr);
129  else
130  {
131  (void)LF_BACKOFF;
132  goto retry;
133  }
134  }
135  cursor->curr= cursor->next;
136  _lf_pin(pins, 1, cursor->curr);
137  }
138 }
139 
140 /*
141  DESCRIPTION
142  insert a 'node' in the list that starts from 'head' in the correct
143  position (as found by lfind)
144 
145  RETURN
146  0 - inserted
147  not 0 - a pointer to a duplicate (not pinned and thus unusable)
148 
149  NOTE
150  it uses pins[0..2], on return all pins are removed.
151  if there're nodes with the same key value, a new node is added before them.
152 */
153 static LF_SLIST *linsert(LF_SLIST * volatile *head, CHARSET_INFO *cs,
154  LF_SLIST *node, LF_PINS *pins, uint flags)
155 {
156  CURSOR cursor;
157  int res;
158 
159  for (;;)
160  {
161  if (lfind(head, cs, node->hashnr, node->key, node->keylen,
162  &cursor, pins) &&
163  (flags & LF_HASH_UNIQUE))
164  {
165  res= 0; /* duplicate found */
166  break;
167  }
168  else
169  {
170  node->link= (intptr)cursor.curr;
171  DBUG_ASSERT(node->link != (intptr)node); /* no circular references */
172  DBUG_ASSERT(cursor.prev != &node->link); /* no circular references */
173  if (my_atomic_casptr((void **)cursor.prev, (void **)&cursor.curr, node))
174  {
175  res= 1; /* inserted ok */
176  break;
177  }
178  }
179  }
180  _lf_unpin(pins, 0);
181  _lf_unpin(pins, 1);
182  _lf_unpin(pins, 2);
183  /*
184  Note that cursor.curr is not pinned here and the pointer is unreliable,
185  the object may dissapear anytime. But if it points to a dummy node, the
186  pointer is safe, because dummy nodes are never freed - initialize_bucket()
187  uses this fact.
188  */
189  return res ? 0 : cursor.curr;
190 }
191 
192 /*
193  DESCRIPTION
194  deletes a node as identified by hashnr/keey/keylen from the list
195  that starts from 'head'
196 
197  RETURN
198  0 - ok
199  1 - not found
200 
201  NOTE
202  it uses pins[0..2], on return all pins are removed.
203 */
204 static int ldelete(LF_SLIST * volatile *head, CHARSET_INFO *cs, uint32 hashnr,
205  const uchar *key, uint keylen, LF_PINS *pins)
206 {
207  CURSOR cursor;
208  int res;
209 
210  for (;;)
211  {
212  if (!lfind(head, cs, hashnr, key, keylen, &cursor, pins))
213  {
214  res= 1; /* not found */
215  break;
216  }
217  else
218  {
219  /* mark the node deleted */
220  if (my_atomic_casptr((void **)&(cursor.curr->link),
221  (void **)&cursor.next,
222  (void *)(((intptr)cursor.next) | 1)))
223  {
224  /* and remove it from the list */
225  if (my_atomic_casptr((void **)cursor.prev,
226  (void **)&cursor.curr, cursor.next))
227  _lf_alloc_free(pins, cursor.curr);
228  else
229  {
230  /*
231  somebody already "helped" us and removed the node ?
232  Let's check if we need to help that someone too!
233  (to ensure the number of "set DELETED flag" actions
234  is equal to the number of "remove from the list" actions)
235  */
236  lfind(head, cs, hashnr, key, keylen, &cursor, pins);
237  }
238  res= 0;
239  break;
240  }
241  }
242  }
243  _lf_unpin(pins, 0);
244  _lf_unpin(pins, 1);
245  _lf_unpin(pins, 2);
246  return res;
247 }
248 
249 /*
250  DESCRIPTION
251  searches for a node as identified by hashnr/keey/keylen in the list
252  that starts from 'head'
253 
254  RETURN
255  0 - not found
256  node - found
257 
258  NOTE
259  it uses pins[0..2], on return the pin[2] keeps the node found
260  all other pins are removed.
261 */
262 static LF_SLIST *lsearch(LF_SLIST * volatile *head, CHARSET_INFO *cs,
263  uint32 hashnr, const uchar *key, uint keylen,
264  LF_PINS *pins)
265 {
266  CURSOR cursor;
267  int res= lfind(head, cs, hashnr, key, keylen, &cursor, pins);
268  if (res)
269  _lf_pin(pins, 2, cursor.curr);
270  _lf_unpin(pins, 0);
271  _lf_unpin(pins, 1);
272  return res ? cursor.curr : 0;
273 }
274 
275 static inline const uchar* hash_key(const LF_HASH *hash,
276  const uchar *record, size_t *length)
277 {
278  if (hash->get_key)
279  return (*hash->get_key)(record, length, 0);
280  *length= hash->key_length;
281  return record + hash->key_offset;
282 }
283 
284 /*
285  Compute the hash key value from the raw key.
286 
287  @note, that the hash value is limited to 2^31, because we need one
288  bit to distinguish between normal and dummy nodes.
289 */
290 static inline uint calc_hash(LF_HASH *hash, const uchar *key, uint keylen)
291 {
292  ulong nr1= 1, nr2= 4;
293  hash->charset->coll->hash_sort(hash->charset, (uchar*) key, keylen,
294  &nr1, &nr2);
295  return nr1 & INT_MAX32;
296 }
297 
298 #define MAX_LOAD 1.0 /* average number of elements in a bucket */
299 
300 static int initialize_bucket(LF_HASH *, LF_SLIST * volatile*, uint, LF_PINS *);
301 
302 /*
303  Initializes lf_hash, the arguments are compatible with hash_init
304 
305  @note element_size sets both the size of allocated memory block for
306  lf_alloc and a size of memcpy'ed block size in lf_hash_insert. Typically
307  they are the same, indeed. But LF_HASH::element_size can be decreased
308  after lf_hash_init, and then lf_alloc will allocate larger block that
309  lf_hash_insert will copy over. It is desireable if part of the element
310  is expensive to initialize - for example if there is a mutex or
311  DYNAMIC_ARRAY. In this case they should be initialize in the
312  LF_ALLOCATOR::constructor, and lf_hash_insert should not overwrite them.
313  See wt_init() for example.
314 */
315 void lf_hash_init(LF_HASH *hash, uint element_size, uint flags,
316  uint key_offset, uint key_length, my_hash_get_key get_key,
317  CHARSET_INFO *charset)
318 {
319  lf_alloc_init(&hash->alloc, sizeof(LF_SLIST)+element_size,
320  offsetof(LF_SLIST, key));
321  lf_dynarray_init(&hash->array, sizeof(LF_SLIST *));
322  hash->size= 1;
323  hash->count= 0;
324  hash->element_size= element_size;
325  hash->flags= flags;
326  hash->charset= charset ? charset : &my_charset_bin;
327  hash->key_offset= key_offset;
328  hash->key_length= key_length;
329  hash->get_key= get_key;
330  DBUG_ASSERT(get_key ? !key_offset && !key_length : key_length);
331 }
332 
333 void lf_hash_destroy(LF_HASH *hash)
334 {
335  LF_SLIST *el, **head= (LF_SLIST **)_lf_dynarray_value(&hash->array, 0);
336 
337  if (unlikely(!head))
338  return;
339  el= *head;
340 
341  while (el)
342  {
343  intptr next= el->link;
344  if (el->hashnr & 1)
345  lf_alloc_direct_free(&hash->alloc, el); /* normal node */
346  else
347  my_free(el); /* dummy node */
348  el= (LF_SLIST *)next;
349  }
350  lf_alloc_destroy(&hash->alloc);
351  lf_dynarray_destroy(&hash->array);
352 }
353 
354 /*
355  DESCRIPTION
356  inserts a new element to a hash. it will have a _copy_ of
357  data, not a pointer to it.
358 
359  RETURN
360  0 - inserted
361  1 - didn't (unique key conflict)
362  -1 - out of memory
363 
364  NOTE
365  see linsert() for pin usage notes
366 */
367 int lf_hash_insert(LF_HASH *hash, LF_PINS *pins, const void *data)
368 {
369  int csize, bucket, hashnr;
370  LF_SLIST *node, * volatile *el;
371 
372  lf_rwlock_by_pins(pins);
373  node= (LF_SLIST *)_lf_alloc_new(pins);
374  if (unlikely(!node))
375  return -1;
376  memcpy(node+1, data, hash->element_size);
377  node->key= hash_key(hash, (uchar *)(node+1), &node->keylen);
378  hashnr= calc_hash(hash, node->key, node->keylen);
379  bucket= hashnr % hash->size;
380  el= _lf_dynarray_lvalue(&hash->array, bucket);
381  if (unlikely(!el))
382  return -1;
383  if (*el == NULL && unlikely(initialize_bucket(hash, el, bucket, pins)))
384  return -1;
385  node->hashnr= my_reverse_bits(hashnr) | 1; /* normal node */
386  if (linsert(el, hash->charset, node, pins, hash->flags))
387  {
388  _lf_alloc_free(pins, node);
389  lf_rwunlock_by_pins(pins);
390  return 1;
391  }
392  csize= hash->size;
393  if ((my_atomic_add32(&hash->count, 1)+1.0) / csize > MAX_LOAD)
394  my_atomic_cas32(&hash->size, &csize, csize*2);
395  lf_rwunlock_by_pins(pins);
396  return 0;
397 }
398 
399 /*
400  DESCRIPTION
401  deletes an element with the given key from the hash (if a hash is
402  not unique and there're many elements with this key - the "first"
403  matching element is deleted)
404  RETURN
405  0 - deleted
406  1 - didn't (not found)
407  -1 - out of memory
408  NOTE
409  see ldelete() for pin usage notes
410 */
411 int lf_hash_delete(LF_HASH *hash, LF_PINS *pins, const void *key, uint keylen)
412 {
413  LF_SLIST * volatile *el;
414  uint bucket, hashnr= calc_hash(hash, (uchar *)key, keylen);
415 
416  bucket= hashnr % hash->size;
417  lf_rwlock_by_pins(pins);
418  el= _lf_dynarray_lvalue(&hash->array, bucket);
419  if (unlikely(!el))
420  return -1;
421  /*
422  note that we still need to initialize_bucket here,
423  we cannot return "node not found", because an old bucket of that
424  node may've been split and the node was assigned to a new bucket
425  that was never accessed before and thus is not initialized.
426  */
427  if (*el == NULL && unlikely(initialize_bucket(hash, el, bucket, pins)))
428  return -1;
429  if (ldelete(el, hash->charset, my_reverse_bits(hashnr) | 1,
430  (uchar *)key, keylen, pins))
431  {
432  lf_rwunlock_by_pins(pins);
433  return 1;
434  }
435  my_atomic_add32(&hash->count, -1);
436  lf_rwunlock_by_pins(pins);
437  return 0;
438 }
439 
440 /*
441  RETURN
442  a pointer to an element with the given key (if a hash is not unique and
443  there're many elements with this key - the "first" matching element)
444  NULL if nothing is found
445  MY_ERRPTR if OOM
446 
447  NOTE
448  see lsearch() for pin usage notes
449 */
450 void *lf_hash_search(LF_HASH *hash, LF_PINS *pins, const void *key, uint keylen)
451 {
452  LF_SLIST * volatile *el, *found;
453  uint bucket, hashnr= calc_hash(hash, (uchar *)key, keylen);
454 
455  bucket= hashnr % hash->size;
456  lf_rwlock_by_pins(pins);
457  el= _lf_dynarray_lvalue(&hash->array, bucket);
458  if (unlikely(!el))
459  return MY_ERRPTR;
460  if (*el == NULL && unlikely(initialize_bucket(hash, el, bucket, pins)))
461  return MY_ERRPTR;
462  found= lsearch(el, hash->charset, my_reverse_bits(hashnr) | 1,
463  (uchar *)key, keylen, pins);
464  lf_rwunlock_by_pins(pins);
465  return found ? found+1 : 0;
466 }
467 
468 static const uchar *dummy_key= (uchar*)"";
469 
470 /*
471  RETURN
472  0 - ok
473  -1 - out of memory
474 */
475 static int initialize_bucket(LF_HASH *hash, LF_SLIST * volatile *node,
476  uint bucket, LF_PINS *pins)
477 {
478  uint parent= my_clear_highest_bit(bucket);
479  LF_SLIST *dummy= (LF_SLIST *)my_malloc(sizeof(LF_SLIST), MYF(MY_WME));
480  LF_SLIST **tmp= 0, *cur;
481  LF_SLIST * volatile *el= _lf_dynarray_lvalue(&hash->array, parent);
482  if (unlikely(!el || !dummy))
483  return -1;
484  if (*el == NULL && bucket &&
485  unlikely(initialize_bucket(hash, el, parent, pins)))
486  return -1;
487  dummy->hashnr= my_reverse_bits(bucket) | 0; /* dummy node */
488  dummy->key= dummy_key;
489  dummy->keylen= 0;
490  if ((cur= linsert(el, hash->charset, dummy, pins, LF_HASH_UNIQUE)))
491  {
492  my_free(dummy);
493  dummy= cur;
494  }
495  my_atomic_casptr((void **)node, (void **)&tmp, dummy);
496  /*
497  note that if the CAS above failed (after linsert() succeeded),
498  it would mean that some other thread has executed linsert() for
499  the same dummy node, its linsert() failed, it picked up our
500  dummy node (in "dummy= cur") and executed the same CAS as above.
501  Which means that even if CAS above failed we don't need to retry,
502  and we should not free(dummy) - there's no memory leak here
503  */
504  return 0;
505 }