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 | |
53 | static 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 | |
73 | static 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 | |
99 | my_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 | |
128 | void 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 | |
157 | uchar *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 | |
194 | my_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 | |
251 | end: |
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 | |
272 | void 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 | |