mf_keycaches.cc 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313
  1. /* Copyright (c) 2003, 2018, Oracle and/or its affiliates. All rights reserved.
  2. This program is free software; you can redistribute it and/or modify
  3. it under the terms of the GNU General Public License, version 2.0,
  4. as published by the Free Software Foundation.
  5. This program is also distributed with certain software (including
  6. but not limited to OpenSSL) that is licensed under separate terms,
  7. as designated in a particular file or component or in included license
  8. documentation. The authors of MySQL hereby grant you an additional
  9. permission to link the program and your derivative works with the
  10. separately licensed software that they have included with MySQL.
  11. Without limiting anything contained in the foregoing, this file,
  12. which is part of C Driver for MySQL (Connector/C), is also subject to the
  13. Universal FOSS Exception, version 1.0, a copy of which can be found at
  14. http://oss.oracle.com/licenses/universal-foss-exception.
  15. This program is distributed in the hope that it will be useful,
  16. but WITHOUT ANY WARRANTY; without even the implied warranty of
  17. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  18. GNU General Public License, version 2.0, for more details.
  19. You should have received a copy of the GNU General Public License
  20. along with this program; if not, write to the Free Software
  21. Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
  22. /**
  23. @file mysys/mf_keycaches.cc
  24. Handling of multiple key caches.
  25. The idea is to have a thread safe hash on the table name,
  26. with a default key cache value that is returned if the table name is not in
  27. the cache.
  28. */
  29. #include <string.h>
  30. #include <sys/types.h>
  31. #include <string>
  32. #include "keycache.h"
  33. #include "m_ctype.h"
  34. #include "map_helpers.h"
  35. #include "my_dbug.h"
  36. #include "my_inttypes.h"
  37. #include "my_sys.h"
  38. #include "mysql/psi/mysql_rwlock.h"
  39. #include "mysql/service_mysql_alloc.h"
  40. #include "mysys/mysys_priv.h"
  41. #include "template_utils.h"
  42. using std::string;
  43. /*****************************************************************************
  44. General functions to handle SAFE_HASH objects.
  45. A SAFE_HASH object is used to store the hash, the lock and default value
  46. needed by the rest of the key cache code.
  47. This is a separate struct to make it easy to later reuse the code for other
  48. purposes
  49. All entries are linked in a list to allow us to traverse all elements
  50. and delete selected ones. (HASH doesn't allow any easy ways to do this).
  51. *****************************************************************************/
  52. /*
  53. Struct to store a key and pointer to object
  54. */
  55. struct SAFE_HASH_ENTRY {
  56. char *key;
  57. uint length;
  58. uchar *data;
  59. SAFE_HASH_ENTRY *next, **prev;
  60. };
  61. struct SAFE_HASH {
  62. mysql_rwlock_t lock;
  63. malloc_unordered_map<string, unique_ptr_my_free<SAFE_HASH_ENTRY>> hash{
  64. key_memory_SAFE_HASH_ENTRY};
  65. uchar *default_value;
  66. SAFE_HASH_ENTRY *root;
  67. };
  68. /*
  69. Init a SAFE_HASH object
  70. SYNOPSIS
  71. safe_hash_init()
  72. hash safe_hash handler
  73. elements Expected max number of elements
  74. default_value default value
  75. NOTES
  76. In case of error we set hash->default_value to 0 to allow one to call
  77. safe_hash_free on an object that couldn't be initialized.
  78. RETURN
  79. 0 ok
  80. 1 error
  81. */
  82. static bool safe_hash_init(SAFE_HASH *hash, uchar *default_value) {
  83. DBUG_ENTER("safe_hash");
  84. mysql_rwlock_init(key_SAFE_HASH_lock, &hash->lock);
  85. hash->default_value = default_value;
  86. hash->root = 0;
  87. DBUG_RETURN(0);
  88. }
  89. /*
  90. Free a SAFE_HASH object
  91. NOTES
  92. This is safe to call on any object that has been sent to safe_hash_init()
  93. */
  94. static void safe_hash_free(SAFE_HASH *hash) {
  95. /*
  96. Test if safe_hash_init succeeded. This will also guard us against multiple
  97. free calls.
  98. */
  99. if (hash->default_value) {
  100. hash->hash.clear();
  101. mysql_rwlock_destroy(&hash->lock);
  102. hash->default_value = 0;
  103. }
  104. }
  105. /*
  106. Return the value stored for a key or default value if no key
  107. */
  108. static uchar *safe_hash_search(SAFE_HASH *hash, const uchar *key, uint length) {
  109. uchar *result;
  110. DBUG_ENTER("safe_hash_search");
  111. mysql_rwlock_rdlock(&hash->lock);
  112. auto it = hash->hash.find(string(pointer_cast<const char *>(key), length));
  113. if (it == hash->hash.end())
  114. result = hash->default_value;
  115. else
  116. result = it->second->data;
  117. mysql_rwlock_unlock(&hash->lock);
  118. DBUG_PRINT("exit", ("data: %p", result));
  119. DBUG_RETURN(result);
  120. }
  121. /*
  122. Associate a key with some data
  123. SYONOPSIS
  124. safe_hash_set()
  125. hash Hash handle
  126. key key (path to table etc..)
  127. length Length of key
  128. data data to to associate with the data
  129. NOTES
  130. This can be used both to insert a new entry and change an existing
  131. entry.
  132. If one associates a key with the default key cache, the key is deleted
  133. RETURN
  134. 0 ok
  135. 1 error (Can only be EOM). In this case my_message() is called.
  136. */
  137. static bool safe_hash_set(SAFE_HASH *hash, const uchar *key, uint length,
  138. uchar *data) {
  139. SAFE_HASH_ENTRY *entry;
  140. bool error = 0;
  141. DBUG_ENTER("safe_hash_set");
  142. DBUG_PRINT("enter", ("key: %.*s data: %p", length, key, data));
  143. string key_str(pointer_cast<const char *>(key), length);
  144. mysql_rwlock_wrlock(&hash->lock);
  145. entry = find_or_nullptr(hash->hash, key_str);
  146. if (data == hash->default_value) {
  147. /*
  148. The key is to be associated with the default entry. In this case
  149. we can just delete the entry (if it existed) from the hash as a
  150. search will return the default entry
  151. */
  152. if (!entry) /* nothing to do */
  153. goto end;
  154. /* unlink entry from list */
  155. if ((*entry->prev = entry->next)) entry->next->prev = entry->prev;
  156. hash->hash.erase(key_str);
  157. goto end;
  158. }
  159. if (entry) {
  160. /* Entry existed; Just change the pointer to point at the new data */
  161. entry->data = data;
  162. } else {
  163. if (!(entry = (SAFE_HASH_ENTRY *)my_malloc(key_memory_SAFE_HASH_ENTRY,
  164. sizeof(*entry) + length,
  165. MYF(MY_WME)))) {
  166. error = 1;
  167. goto end;
  168. }
  169. entry->key = (char *)(entry + 1);
  170. memcpy(entry->key, key, length);
  171. entry->length = length;
  172. entry->data = data;
  173. /* Link entry to list */
  174. if ((entry->next = hash->root)) entry->next->prev = &entry->next;
  175. entry->prev = &hash->root;
  176. hash->root = entry;
  177. hash->hash.emplace(
  178. string(pointer_cast<const char *>(entry->key), entry->length),
  179. unique_ptr_my_free<SAFE_HASH_ENTRY>(entry));
  180. }
  181. end:
  182. mysql_rwlock_unlock(&hash->lock);
  183. DBUG_RETURN(error);
  184. }
  185. /*
  186. Change all entres with one data value to another data value
  187. SYONOPSIS
  188. safe_hash_change()
  189. hash Hash handle
  190. old_data Old data
  191. new_data Change all 'old_data' to this
  192. NOTES
  193. We use the linked list to traverse all elements in the hash as
  194. this allows us to delete elements in the case where 'new_data' is the
  195. default value.
  196. */
  197. static void safe_hash_change(SAFE_HASH *hash, uchar *old_data,
  198. uchar *new_data) {
  199. SAFE_HASH_ENTRY *entry, *next;
  200. DBUG_ENTER("safe_hash_set");
  201. mysql_rwlock_wrlock(&hash->lock);
  202. for (entry = hash->root; entry; entry = next) {
  203. next = entry->next;
  204. if (entry->data == old_data) {
  205. if (new_data == hash->default_value) {
  206. if ((*entry->prev = entry->next)) entry->next->prev = entry->prev;
  207. hash->hash.erase(string(entry->key, entry->length));
  208. } else
  209. entry->data = new_data;
  210. }
  211. }
  212. mysql_rwlock_unlock(&hash->lock);
  213. DBUG_VOID_RETURN;
  214. }
  215. /*****************************************************************************
  216. Functions to handle the key cache objects
  217. *****************************************************************************/
  218. /* Variable to store all key cache objects */
  219. static SAFE_HASH key_cache_hash;
  220. bool multi_keycache_init(void) {
  221. return safe_hash_init(&key_cache_hash, (uchar *)dflt_key_cache);
  222. }
  223. void multi_keycache_free(void) { safe_hash_free(&key_cache_hash); }
  224. /*
  225. Get a key cache to be used for a specific table.
  226. SYNOPSIS
  227. multi_key_cache_search()
  228. key key to find (usually table path)
  229. uint length Length of key.
  230. NOTES
  231. This function is coded in such a way that we will return the
  232. default key cache even if one never called multi_keycache_init.
  233. This will ensure that it works with old MyISAM clients.
  234. RETURN
  235. key cache to use
  236. */
  237. KEY_CACHE *multi_key_cache_search(uchar *key, uint length) {
  238. if (key_cache_hash.hash.empty()) return dflt_key_cache;
  239. return (KEY_CACHE *)safe_hash_search(&key_cache_hash, key, length);
  240. }
  241. /*
  242. Assosiate a key cache with a key
  243. SYONOPSIS
  244. multi_key_cache_set()
  245. key key (path to table etc..)
  246. length Length of key
  247. key_cache cache to assococite with the table
  248. NOTES
  249. This can be used both to insert a new entry and change an existing
  250. entry
  251. */
  252. bool multi_key_cache_set(const uchar *key, uint length, KEY_CACHE *key_cache) {
  253. return safe_hash_set(&key_cache_hash, key, length, (uchar *)key_cache);
  254. }
  255. void multi_key_cache_change(KEY_CACHE *old_data, KEY_CACHE *new_data) {
  256. safe_hash_change(&key_cache_hash, (uchar *)old_data, (uchar *)new_data);
  257. }