1/* Copyright (C) 2003-2007 MySQL AB
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; either version 2 of the License, or
6 (at your option) any later version.
7
8 This program is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 GNU General Public License for more details.
12
13 You should have received a copy of the GNU General Public License
14 along with this program; if not, write to the Free Software
15 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA */
16
17/*
18 Handling of multiple key caches
19
20 The idea is to have a thread safe hash on the table name,
21 with a default key cache value that is returned if the table name is not in
22 the cache.
23*/
24
25#include "mysys_priv.h"
26#include <m_string.h>
27#include "my_safehash.h"
28
29/*****************************************************************************
30 General functions to handle SAFE_HASH objects.
31
32 A SAFE_HASH object is used to store the hash, the mutex and default value
33 needed by the rest of the key cache code.
34 This is a separate struct to make it easy to later reuse the code for other
35 purposes
36
37 All entries are linked in a list to allow us to traverse all elements
38 and delete selected ones. (HASH doesn't allow any easy ways to do this).
39*****************************************************************************/
40
41
42/*
43 Free a SAFE_HASH_ENTRY
44
45 SYNOPSIS
46 safe_hash_entry_free()
47 entry The entry which should be freed
48
49 NOTE
50 This function is called by the hash object on delete
51*/
52
53static void safe_hash_entry_free(SAFE_HASH_ENTRY *entry)
54{
55 DBUG_ENTER("safe_hash_entry_free");
56 my_free(entry);
57 DBUG_VOID_RETURN;
58}
59
60
61/*
62 Get key and length for a SAFE_HASH_ENTRY
63
64 SYNOPSIS
65 safe_hash_entry_get()
66 entry The entry for which the key should be returned
67 length Length of the key
68
69 RETURN
70 # reference on the key
71*/
72
73static uchar *safe_hash_entry_get(SAFE_HASH_ENTRY *entry, size_t *length,
74 my_bool not_used __attribute__((unused)))
75{
76 *length= entry->length;
77 return (uchar*) entry->key;
78}
79
80
81/*
82 Init a SAFE_HASH object
83
84 SYNOPSIS
85 safe_hash_init()
86 hash safe_hash handler
87 elements Expected max number of elements
88 default_value default value
89
90 NOTES
91 In case of error we set hash->default_value to 0 to allow one to call
92 safe_hash_free on an object that couldn't be initialized.
93
94 RETURN
95 0 OK
96 1 error
97*/
98
99my_bool safe_hash_init(SAFE_HASH *hash, uint elements,
100 uchar *default_value)
101{
102 DBUG_ENTER("safe_hash_init");
103 if (my_hash_init(&hash->hash, &my_charset_bin, elements,
104 0, 0, (my_hash_get_key) safe_hash_entry_get,
105 (void (*)(void*)) safe_hash_entry_free, 0))
106 {
107 hash->default_value= 0;
108 DBUG_RETURN(1);
109 }
110 mysql_rwlock_init(key_SAFEHASH_mutex, &hash->mutex);
111 hash->default_value= default_value;
112 hash->root= 0;
113 DBUG_RETURN(0);
114}
115
116
117/*
118 Free a SAFE_HASH object
119
120 SYNOPSIS
121 safe_hash_free()
122 hash Hash handle
123
124 NOTES
125 This is safe to call on any object that has been sent to safe_hash_init()
126*/
127
128void safe_hash_free(SAFE_HASH *hash)
129{
130 /*
131 Test if safe_hash_init succeeded. This will also guard us against multiple
132 free calls.
133 */
134 if (hash->default_value)
135 {
136 my_hash_free(&hash->hash);
137 mysql_rwlock_destroy(&hash->mutex);
138 hash->default_value=0;
139 }
140}
141
142
143/*
144 Return the value stored for a key or default value if no key
145
146 SYNOPSIS
147 safe_hash_search()
148 hash Hash handle
149 key key (path to table etc..)
150 length Length of key
151 def Default value of data
152
153 RETURN
154 # data associated with the key of default value if data was not found
155*/
156
157uchar *safe_hash_search(SAFE_HASH *hash, const uchar *key, uint length,
158 uchar *def)
159{
160 uchar *result;
161 DBUG_ENTER("safe_hash_search");
162 mysql_rwlock_rdlock(&hash->mutex);
163 result= my_hash_search(&hash->hash, key, length);
164 mysql_rwlock_unlock(&hash->mutex);
165 if (!result)
166 result= def;
167 else
168 result= ((SAFE_HASH_ENTRY*) result)->data;
169 DBUG_PRINT("exit",("data: %p", result));
170 DBUG_RETURN(result);
171}
172
173
174/*
175 Associate a key with some data
176
177 SYNOPSIS
178 safe_hash_set()
179 hash Hash handle
180 key key (path to table etc..)
181 length Length of key
182 data data to to associate with the data
183
184 NOTES
185 This can be used both to insert a new entry and change an existing
186 entry.
187 If one associates a key with the default key cache, the key is deleted
188
189 RETURN
190 0 OK
191 1 error (Can only be EOM). In this case my_message() is called.
192*/
193
194my_bool safe_hash_set(SAFE_HASH *hash, const uchar *key, uint length,
195 uchar *data)
196{
197 SAFE_HASH_ENTRY *entry;
198 my_bool error= 0;
199 DBUG_ENTER("safe_hash_set");
200 DBUG_PRINT("enter",("key: %.*s data: %p", length, key, data));
201
202 mysql_rwlock_wrlock(&hash->mutex);
203 entry= (SAFE_HASH_ENTRY*) my_hash_search(&hash->hash, key, length);
204
205 if (data == hash->default_value)
206 {
207 /*
208 The key is to be associated with the default entry. In this case
209 we can just delete the entry (if it existed) from the hash as a
210 search will return the default entry
211 */
212 if (!entry) /* nothing to do */
213 goto end;
214 /* unlink entry from list */
215 if ((*entry->prev= entry->next))
216 entry->next->prev= entry->prev;
217 my_hash_delete(&hash->hash, (uchar*) entry);
218 goto end;
219 }
220 if (entry)
221 {
222 /* Entry existed; Just change the pointer to point at the new data */
223 entry->data= data;
224 }
225 else
226 {
227 if (!(entry= (SAFE_HASH_ENTRY *) my_malloc(sizeof(*entry) + length,
228 MYF(MY_WME))))
229 {
230 error= 1;
231 goto end;
232 }
233 entry->key= (uchar*) (entry +1);
234 memcpy((char*) entry->key, (char*) key, length);
235 entry->length= length;
236 entry->data= data;
237 /* Link entry to list */
238 if ((entry->next= hash->root))
239 entry->next->prev= &entry->next;
240 entry->prev= &hash->root;
241 hash->root= entry;
242 if (my_hash_insert(&hash->hash, (uchar*) entry))
243 {
244 /* This can only happen if hash got out of memory */
245 my_free(entry);
246 error= 1;
247 goto end;
248 }
249 }
250
251end:
252 mysql_rwlock_unlock(&hash->mutex);
253 DBUG_RETURN(error);
254}
255
256
257/*
258 Change all entries with one data value to another data value
259
260 SYNOPSIS
261 safe_hash_change()
262 hash Hash handle
263 old_data Old data
264 new_data Change all 'old_data' to this
265
266 NOTES
267 We use the linked list to traverse all elements in the hash as
268 this allows us to delete elements in the case where 'new_data' is the
269 default value.
270*/
271
272void safe_hash_change(SAFE_HASH *hash, uchar *old_data, uchar *new_data)
273{
274 SAFE_HASH_ENTRY *entry, *next;
275 DBUG_ENTER("safe_hash_change");
276
277 mysql_rwlock_wrlock(&hash->mutex);
278
279 for (entry= hash->root ; entry ; entry= next)
280 {
281 next= entry->next;
282 if (entry->data == old_data)
283 {
284 if (new_data == hash->default_value)
285 {
286 if ((*entry->prev= entry->next))
287 entry->next->prev= entry->prev;
288 my_hash_delete(&hash->hash, (uchar*) entry);
289 }
290 else
291 entry->data= new_data;
292 }
293 }
294
295 mysql_rwlock_unlock(&hash->mutex);
296 DBUG_VOID_RETURN;
297}
298