Permalink
Newer
Older
100644 294 lines (245 sloc) 8.58 KB
1
/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2
/*
3
* Hash table
4
*
5
* The hash function used here is by Bob Jenkins, 1996:
6
* <http://burtleburtle.net/bob/hash/doobs.html>
7
* "By Bob Jenkins, 1996. bob_jenkins@burtleburtle.net.
8
* You may use this code any way you wish, private, educational,
9
* or commercial. It's free."
10
*
11
* The rest of the file is licensed under the BSD license. See LICENSE.
12
*/
Steven Grimm
Merge multithreaded into trunk, commit #2 (first commit only did the
Apr 16, 2007
13
14
#include "memcached.h"
15
#include <sys/stat.h>
16
#include <sys/socket.h>
17
#include <sys/signal.h>
18
#include <sys/resource.h>
19
#include <fcntl.h>
20
#include <netinet/in.h>
21
#include <errno.h>
22
#include <stdlib.h>
23
#include <stdio.h>
24
#include <string.h>
Jun 27, 2003
25
#include <assert.h>
26
#include <pthread.h>
27
28
static pthread_cond_t maintenance_cond = PTHREAD_COND_INITIALIZER;
29
31
typedef unsigned long int ub4; /* unsigned 4-byte quantities */
32
typedef unsigned char ub1; /* unsigned 1-byte quantities */
33
34
/* how many powers of 2's worth of buckets we use */
35
unsigned int hashpower = HASHPOWER_DEFAULT;
36
37
#define hashsize(n) ((ub4)1<<(n))
38
#define hashmask(n) (hashsize(n)-1)
39
40
/* Main hash table. This is where we look except during expansion. */
41
static item** primary_hashtable = 0;
42
43
/*
44
* Previous hash table. During expansion, we look here for keys that haven't
45
* been moved over to the primary yet.
46
*/
47
static item** old_hashtable = 0;
48
49
/* Number of items in the hash table. */
50
static unsigned int hash_items = 0;
51
52
/* Flag: Are we in the middle of expanding now? */
54
static bool started_expanding = false;
55
56
/*
57
* During expansion we migrate values with bucket granularity; this is how
58
* far we've gotten so far. Ranges from 0 .. hashsize(hashpower - 1) - 1.
59
*/
60
static unsigned int expand_bucket = 0;
62
void assoc_init(const int hashtable_init) {
63
if (hashtable_init) {
64
hashpower = hashtable_init;
65
}
66
primary_hashtable = calloc(hashsize(hashpower), sizeof(void *));
68
fprintf(stderr, "Failed to init hashtable.\n");
Paul Lindner
Apr 12, 2007
69
exit(EXIT_FAILURE);
71
STATS_LOCK();
72
stats.hash_power_level = hashpower;
73
stats.hash_bytes = hashsize(hashpower) * sizeof(void *);
74
STATS_UNLOCK();
77
item *assoc_find(const char *key, const size_t nkey, const uint32_t hv) {
80
81
if (expanding &&
82
(oldbucket = (hv & hashmask(hashpower - 1))) >= expand_bucket)
83
{
84
it = old_hashtable[oldbucket];
85
} else {
86
it = primary_hashtable[hv & hashmask(hashpower)];
87
}
89
item *ret = NULL;
90
int depth = 0;
Jun 20, 2003
91
while (it) {
92
if ((nkey == it->nkey) && (memcmp(key, ITEM_key(it), nkey) == 0)) {
93
ret = it;
94
break;
Jun 20, 2003
96
it = it->h_next;
97
++depth;
99
MEMCACHED_ASSOC_FIND(key, nkey, depth);
100
return ret;
103
/* returns the address of the item pointer before the key. if *item == 0,
104
the item wasn't found */
106
static item** _hashitem_before (const char *key, const size_t nkey, const uint32_t hv) {
110
if (expanding &&
111
(oldbucket = (hv & hashmask(hashpower - 1))) >= expand_bucket)
112
{
113
pos = &old_hashtable[oldbucket];
114
} else {
115
pos = &primary_hashtable[hv & hashmask(hashpower)];
116
}
117
118
while (*pos && ((nkey != (*pos)->nkey) || memcmp(key, ITEM_key(*pos), nkey))) {
Jun 20, 2003
119
pos = &(*pos)->h_next;
124
/* grows the hashtable to the next power of 2. */
125
static void assoc_expand(void) {
126
old_hashtable = primary_hashtable;
127
128
primary_hashtable = calloc(hashsize(hashpower + 1), sizeof(void *));
129
if (primary_hashtable) {
Steven Grimm
Merge multithreaded into trunk, commit #2 (first commit only did the
Apr 16, 2007
130
if (settings.verbose > 1)
131
fprintf(stderr, "Hash table expansion starting\n");
135
STATS_LOCK();
136
stats.hash_power_level = hashpower;
137
stats.hash_bytes += hashsize(hashpower) * sizeof(void *);
138
stats.hash_is_expanding = 1;
139
STATS_UNLOCK();
140
} else {
141
primary_hashtable = old_hashtable;
Steven Grimm
Merge multithreaded into trunk, commit #2 (first commit only did the
Apr 16, 2007
142
/* Bad news, but we can keep running. */
146
static void assoc_start_expand(void) {
147
if (started_expanding)
148
return;
149
started_expanding = true;
150
pthread_cond_signal(&maintenance_cond);
151
}
152
153
/* Note: this isn't an assoc_update. The key must not already exist to call this */
154
int assoc_insert(item *it, const uint32_t hv) {
157
// assert(assoc_find(ITEM_key(it), it->nkey) == 0); /* shouldn't have duplicately named things defined */
158
159
if (expanding &&
160
(oldbucket = (hv & hashmask(hashpower - 1))) >= expand_bucket)
161
{
162
it->h_next = old_hashtable[oldbucket];
163
old_hashtable[oldbucket] = it;
164
} else {
165
it->h_next = primary_hashtable[hv & hashmask(hashpower)];
166
primary_hashtable[hv & hashmask(hashpower)] = it;
167
}
168
169
hash_items++;
170
if (! expanding && hash_items > (hashsize(hashpower) * 3) / 2) {
171
assoc_start_expand();
174
MEMCACHED_ASSOC_INSERT(ITEM_key(it), it->nkey, hash_items);
Jun 20, 2003
175
return 1;
178
void assoc_delete(const char *key, const size_t nkey, const uint32_t hv) {
179
item **before = _hashitem_before(key, nkey, hv);
182
item *nxt;
183
hash_items--;
184
/* The DTrace probe cannot be triggered as the last instruction
185
* due to possible tail-optimization by the compiler
186
*/
187
MEMCACHED_ASSOC_DELETE(key, nkey, hash_items);
188
nxt = (*before)->h_next;
Jun 20, 2003
189
(*before)->h_next = 0; /* probably pointless, but whatever. */
190
*before = nxt;
191
return;
193
/* Note: we never actually get here. the callers don't delete things
194
they can't find. */
195
assert(*before != 0);
197
198
199
static volatile int do_run_maintenance_thread = 1;
200
201
#define DEFAULT_HASH_BULK_MOVE 1
202
int hash_bulk_move = DEFAULT_HASH_BULK_MOVE;
203
204
static void *assoc_maintenance_thread(void *arg) {
205
206
while (do_run_maintenance_thread) {
207
int ii = 0;
208
209
/* Lock the cache, and bulk move multiple buckets to the new
210
* hash table. */
211
item_lock_global();
212
mutex_lock(&cache_lock);
214
for (ii = 0; ii < hash_bulk_move && expanding; ++ii) {
215
item *it, *next;
216
int bucket;
217
218
for (it = old_hashtable[expand_bucket]; NULL != it; it = next) {
219
next = it->h_next;
220
221
bucket = hash(ITEM_key(it), it->nkey, 0) & hashmask(hashpower);
222
it->h_next = primary_hashtable[bucket];
223
primary_hashtable[bucket] = it;
224
}
225
226
old_hashtable[expand_bucket] = NULL;
227
228
expand_bucket++;
229
if (expand_bucket == hashsize(hashpower - 1)) {
230
expanding = false;
231
free(old_hashtable);
232
STATS_LOCK();
233
stats.hash_bytes -= hashsize(hashpower - 1) * sizeof(void *);
234
stats.hash_is_expanding = 0;
235
STATS_UNLOCK();
236
if (settings.verbose > 1)
237
fprintf(stderr, "Hash table expansion done\n");
238
}
239
}
240
241
mutex_unlock(&cache_lock);
242
item_unlock_global();
243
244
if (!expanding) {
245
/* finished expanding. tell all threads to use fine-grained locks */
246
switch_item_lock_type(ITEM_LOCK_GRANULAR);
247
slabs_rebalancer_resume();
248
/* We are done expanding.. just wait for next invocation */
249
mutex_lock(&cache_lock);
250
started_expanding = false;
251
pthread_cond_wait(&maintenance_cond, &cache_lock);
252
/* Before doing anything, tell threads to use a global lock */
253
mutex_unlock(&cache_lock);
254
slabs_rebalancer_pause();
255
switch_item_lock_type(ITEM_LOCK_GLOBAL);
256
mutex_lock(&cache_lock);
257
assoc_expand();
258
mutex_unlock(&cache_lock);
259
}
260
}
261
return NULL;
262
}
263
264
static pthread_t maintenance_tid;
265
266
int start_assoc_maintenance_thread() {
267
int ret;
268
char *env = getenv("MEMCACHED_HASH_BULK_MOVE");
269
if (env != NULL) {
270
hash_bulk_move = atoi(env);
271
if (hash_bulk_move == 0) {
272
hash_bulk_move = DEFAULT_HASH_BULK_MOVE;
273
}
274
}
275
if ((ret = pthread_create(&maintenance_tid, NULL,
276
assoc_maintenance_thread, NULL)) != 0) {
277
fprintf(stderr, "Can't create thread: %s\n", strerror(ret));
278
return -1;
279
}
280
return 0;
281
}
282
283
void stop_assoc_maintenance_thread() {
284
mutex_lock(&cache_lock);
285
do_run_maintenance_thread = 0;
286
pthread_cond_signal(&maintenance_cond);
287
mutex_unlock(&cache_lock);
288
289
/* Wait for the maintenance thread to stop */
290
pthread_join(maintenance_tid, NULL);
291
}
292
293