MySQL 5.6.14 Source Code Document
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
assoc.c
1 /* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /*
3  * Hash table
4  *
5  */
6 #include "config.h"
7 #include <fcntl.h>
8 #include <errno.h>
9 #include <stdlib.h>
10 #include <stdio.h>
11 #include <string.h>
12 #include <assert.h>
13 #include <pthread.h>
14 
15 #include "default_engine.h"
16 
17 #define hashsize(n) ((uint32_t)1<<(n))
18 #define hashmask(n) (hashsize(n)-1)
19 
20 ENGINE_ERROR_CODE assoc_init(struct default_engine *engine) {
21  engine->assoc.primary_hashtable = calloc(hashsize(engine->assoc.hashpower), sizeof(void *));
22  return (engine->assoc.primary_hashtable != NULL) ? ENGINE_SUCCESS : ENGINE_ENOMEM;
23 }
24 
25 hash_item *assoc_find(struct default_engine *engine, uint32_t hash, const char *key, const size_t nkey) {
26  hash_item *it;
27  unsigned int oldbucket;
28 
29  if (engine->assoc.expanding &&
30  (oldbucket = (hash & hashmask(engine->assoc.hashpower - 1))) >= engine->assoc.expand_bucket)
31  {
32  it = engine->assoc.old_hashtable[oldbucket];
33  } else {
34  it = engine->assoc.primary_hashtable[hash & hashmask(engine->assoc.hashpower)];
35  }
36 
37  hash_item *ret = NULL;
38  int depth = 0;
39  while (it) {
40  if ((nkey == it->nkey) && (memcmp(key, item_get_key(it), nkey) == 0)) {
41  ret = it;
42  break;
43  }
44  it = it->h_next;
45  ++depth;
46  }
47  MEMCACHED_ASSOC_FIND(key, nkey, depth);
48  return ret;
49 }
50 
51 /* returns the address of the item pointer before the key. if *item == 0,
52  the item wasn't found */
53 
54 static hash_item** _hashitem_before(struct default_engine *engine,
55  uint32_t hash,
56  const char *key,
57  const size_t nkey) {
58  hash_item **pos;
59  unsigned int oldbucket;
60 
61  if (engine->assoc.expanding &&
62  (oldbucket = (hash & hashmask(engine->assoc.hashpower - 1))) >= engine->assoc.expand_bucket)
63  {
64  pos = &engine->assoc.old_hashtable[oldbucket];
65  } else {
66  pos = &engine->assoc.primary_hashtable[hash & hashmask(engine->assoc.hashpower)];
67  }
68 
69  while (*pos && ((nkey != (*pos)->nkey) || memcmp(key, item_get_key(*pos), nkey))) {
70  pos = &(*pos)->h_next;
71  }
72  return pos;
73 }
74 
75 static void *assoc_maintenance_thread(void *arg);
76 
77 /* grows the hashtable to the next power of 2. */
78 static void assoc_expand(struct default_engine *engine) {
79  engine->assoc.old_hashtable = engine->assoc.primary_hashtable;
80 
81  engine->assoc.primary_hashtable = calloc(hashsize(engine->assoc.hashpower + 1), sizeof(void *));
82  if (engine->assoc.primary_hashtable) {
83  engine->assoc.hashpower++;
84  engine->assoc.expanding = true;
85  engine->assoc.expand_bucket = 0;
86 
87  /* start a thread to do the expansion */
88  int ret = 0;
89  pthread_t tid;
90  pthread_attr_t attr;
91 
92  if (pthread_attr_init(&attr) != 0 ||
93  pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED) != 0 ||
94  (ret = pthread_create(&tid, &attr,
95  assoc_maintenance_thread, engine)) != 0)
96  {
98  logger = (void*)engine->server.extension->get_extension(EXTENSION_LOGGER);
99  logger->log(EXTENSION_LOG_WARNING, NULL,
100  "Can't create thread: %s\n", strerror(ret));
101  engine->assoc.hashpower--;
102  engine->assoc.expanding = false;
103  free(engine->assoc.primary_hashtable);
104  engine->assoc.primary_hashtable =engine->assoc.old_hashtable;
105  }
106  } else {
107  engine->assoc.primary_hashtable = engine->assoc.old_hashtable;
108  /* Bad news, but we can keep running. */
109  }
110 }
111 
112 /* Note: this isn't an assoc_update. The key must not already exist to call this */
113 int assoc_insert(struct default_engine *engine, uint32_t hash, hash_item *it) {
114  unsigned int oldbucket;
115 
116  assert(assoc_find(engine, hash, item_get_key(it), it->nkey) == 0); /* shouldn't have duplicately named things defined */
117 
118  if (engine->assoc.expanding &&
119  (oldbucket = (hash & hashmask(engine->assoc.hashpower - 1))) >= engine->assoc.expand_bucket)
120  {
121  it->h_next = engine->assoc.old_hashtable[oldbucket];
122  engine->assoc.old_hashtable[oldbucket] = it;
123  } else {
124  it->h_next = engine->assoc.primary_hashtable[hash & hashmask(engine->assoc.hashpower)];
125  engine->assoc.primary_hashtable[hash & hashmask(engine->assoc.hashpower)] = it;
126  }
127 
128  engine->assoc.hash_items++;
129  if (! engine->assoc.expanding && engine->assoc.hash_items > (hashsize(engine->assoc.hashpower) * 3) / 2) {
130  assoc_expand(engine);
131  }
132 
133  MEMCACHED_ASSOC_INSERT(item_get_key(it), it->nkey, engine->assoc.hash_items);
134  return 1;
135 }
136 
137 void assoc_delete(struct default_engine *engine, uint32_t hash, const char *key, const size_t nkey) {
138  hash_item **before = _hashitem_before(engine, hash, key, nkey);
139 
140  if (*before) {
141  hash_item *nxt;
142  engine->assoc.hash_items--;
143  /* The DTrace probe cannot be triggered as the last instruction
144  * due to possible tail-optimization by the compiler
145  */
146  MEMCACHED_ASSOC_DELETE(key, nkey, engine->assoc.hash_items);
147  nxt = (*before)->h_next;
148  (*before)->h_next = 0; /* probably pointless, but whatever. */
149  *before = nxt;
150  return;
151  }
152  /* Note: we never actually get here. the callers don't delete things
153  they can't find. */
154  assert(*before != 0);
155 }
156 
157 
158 
159 #define DEFAULT_HASH_BULK_MOVE 1
160 int hash_bulk_move = DEFAULT_HASH_BULK_MOVE;
161 
162 static void *assoc_maintenance_thread(void *arg) {
163  struct default_engine *engine = arg;
164  bool done = false;
165  do {
166  int ii;
167  pthread_mutex_lock(&engine->cache_lock);
168 
169  for (ii = 0; ii < hash_bulk_move && engine->assoc.expanding; ++ii) {
170  hash_item *it, *next;
171  int bucket;
172 
173  for (it = engine->assoc.old_hashtable[engine->assoc.expand_bucket];
174  NULL != it; it = next) {
175  next = it->h_next;
176 
177  bucket = engine->server.core->hash(item_get_key(it), it->nkey, 0)
178  & hashmask(engine->assoc.hashpower);
179  it->h_next = engine->assoc.primary_hashtable[bucket];
180  engine->assoc.primary_hashtable[bucket] = it;
181  }
182 
183  engine->assoc.old_hashtable[engine->assoc.expand_bucket] = NULL;
184  engine->assoc.expand_bucket++;
185  if (engine->assoc.expand_bucket == hashsize(engine->assoc.hashpower - 1)) {
186  engine->assoc.expanding = false;
187  free(engine->assoc.old_hashtable);
188  if (engine->config.verbose > 1) {
190  logger = (void*)engine->server.extension->get_extension(EXTENSION_LOGGER);
191  logger->log(EXTENSION_LOG_INFO, NULL,
192  "Hash table expansion done\n");
193  }
194  }
195  }
196  if (!engine->assoc.expanding) {
197  done = true;
198  }
199  pthread_mutex_unlock(&engine->cache_lock);
200  } while (!done);
201 
202  return NULL;
203 }