1 /*****************************************************************************
2 
3 Copyright (c) 1997, 2016, Oracle and/or its affiliates. All Rights Reserved.
4 
5 This program is free software; you can redistribute it and/or modify it under
6 the terms of the GNU General Public License as published by the Free Software
7 Foundation; version 2 of the License.
8 
9 This program is distributed in the hope that it will be useful, but WITHOUT
10 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11 FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
12 
13 You should have received a copy of the GNU General Public License along with
14 this program; if not, write to the Free Software Foundation, Inc.,
15 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA
16 
17 *****************************************************************************/
18 
19 /**************************************************//**
20 @file ha/hash0hash.cc
21 The simple hash table utility
22 
23 Created 5/20/1997 Heikki Tuuri
24 *******************************************************/
25 
26 #include "hash0hash.h"
27 #include "mem0mem.h"
28 #include "sync0sync.h"
29 
30 /************************************************************//**
31 Reserves all the locks of a hash table, in an ascending order. */
32 void
hash_lock_x_all(hash_table_t * table)33 hash_lock_x_all(
34 /*============*/
35 	hash_table_t*	table)	/*!< in: hash table */
36 {
37 	ut_ad(table->type == HASH_TABLE_SYNC_RW_LOCK);
38 
39 	for (ulint i = 0; i < table->n_sync_obj; i++) {
40 
41 		rw_lock_t* lock = table->sync_obj.rw_locks + i;
42 
43 		ut_ad(!rw_lock_own(lock, RW_LOCK_S));
44 		ut_ad(!rw_lock_own(lock, RW_LOCK_X));
45 
46 		rw_lock_x_lock(lock);
47 	}
48 }
49 
50 /************************************************************//**
51 Releases all the locks of a hash table, in an ascending order. */
52 void
hash_unlock_x_all(hash_table_t * table)53 hash_unlock_x_all(
54 /*==============*/
55 	hash_table_t*	table)	/*!< in: hash table */
56 {
57 	ut_ad(table->type == HASH_TABLE_SYNC_RW_LOCK);
58 
59 	for (ulint i = 0; i < table->n_sync_obj; i++) {
60 
61 		rw_lock_t* lock = table->sync_obj.rw_locks + i;
62 
63 		ut_ad(rw_lock_own(lock, RW_LOCK_X));
64 
65 		rw_lock_x_unlock(lock);
66 	}
67 }
68 
69 /************************************************************//**
70 Releases all but passed in lock of a hash table, */
71 void
hash_unlock_x_all_but(hash_table_t * table,rw_lock_t * keep_lock)72 hash_unlock_x_all_but(
73 /*==================*/
74 	hash_table_t*	table,		/*!< in: hash table */
75 	rw_lock_t*	keep_lock)	/*!< in: lock to keep */
76 {
77 	ut_ad(table->type == HASH_TABLE_SYNC_RW_LOCK);
78 
79 	for (ulint i = 0; i < table->n_sync_obj; i++) {
80 
81 		rw_lock_t* lock = table->sync_obj.rw_locks + i;
82 
83 		ut_ad(rw_lock_own(lock, RW_LOCK_X));
84 
85 		if (keep_lock != lock) {
86 			rw_lock_x_unlock(lock);
87 		}
88 	}
89 }
90 
91 /*************************************************************//**
92 Creates a hash table with >= n array cells. The actual number of cells is
93 chosen to be a prime number slightly bigger than n.
94 @return own: created table */
95 hash_table_t*
hash_create(ulint n)96 hash_create(
97 /*========*/
98 	ulint	n)	/*!< in: number of array cells */
99 {
100 	hash_cell_t*	array;
101 	ulint		prime;
102 	hash_table_t*	table;
103 
104 	prime = ut_find_prime(n);
105 
106 	table = static_cast<hash_table_t*>(
107 		ut_malloc_nokey(sizeof(hash_table_t)));
108 
109 	array = static_cast<hash_cell_t*>(
110 		ut_malloc_nokey(sizeof(hash_cell_t) * prime));
111 
112 	/* The default type of hash_table is HASH_TABLE_SYNC_NONE i.e.:
113 	the caller is responsible for access control to the table. */
114 	table->type = HASH_TABLE_SYNC_NONE;
115 	table->array = array;
116 	table->n_cells = prime;
117 #ifdef BTR_CUR_HASH_ADAPT
118 # if defined UNIV_AHI_DEBUG || defined UNIV_DEBUG
119 	table->adaptive = FALSE;
120 # endif /* UNIV_AHI_DEBUG || UNIV_DEBUG */
121 #endif /* BTR_CUR_HASH_ADAPT */
122 	table->n_sync_obj = 0;
123 	table->sync_obj.mutexes = NULL;
124 	table->heaps = NULL;
125 	table->heap = NULL;
126 	ut_d(table->magic_n = HASH_TABLE_MAGIC_N);
127 
128 	/* Initialize the cell array */
129 	hash_table_clear(table);
130 
131 	return(table);
132 }
133 
134 /*************************************************************//**
135 Frees a hash table. */
136 void
hash_table_free(hash_table_t * table)137 hash_table_free(
138 /*============*/
139 	hash_table_t*	table)	/*!< in, own: hash table */
140 {
141 	ut_ad(table->magic_n == HASH_TABLE_MAGIC_N);
142 
143 	ut_free(table->array);
144 	ut_free(table);
145 }
146 
147 /*************************************************************//**
148 Creates a sync object array to protect a hash table.
149 ::sync_obj can be mutexes or rw_locks depening on the type of
150 hash table. */
151 void
hash_create_sync_obj(hash_table_t * table,enum hash_table_sync_t type,latch_id_t id,ulint n_sync_obj)152 hash_create_sync_obj(
153 /*=================*/
154 	hash_table_t*		table,	/*!< in: hash table */
155 	enum hash_table_sync_t	type,	/*!< in: HASH_TABLE_SYNC_MUTEX
156 					or HASH_TABLE_SYNC_RW_LOCK */
157 	latch_id_t		id,	/*!< in: latch ID */
158 	ulint			n_sync_obj)/*!< in: number of sync objects,
159 					must be a power of 2 */
160 {
161 	ut_a(n_sync_obj > 0);
162 	ut_a(ut_is_2pow(n_sync_obj));
163 	ut_ad(table->magic_n == HASH_TABLE_MAGIC_N);
164 
165 	table->type = type;
166 
167 	switch (table->type) {
168 	case HASH_TABLE_SYNC_MUTEX:
169 		table->sync_obj.mutexes = static_cast<ib_mutex_t*>(
170 			ut_malloc_nokey(n_sync_obj * sizeof(ib_mutex_t)));
171 
172 		for (ulint i = 0; i < n_sync_obj; i++) {
173 			mutex_create(id, table->sync_obj.mutexes + i);
174 		}
175 
176 		break;
177 
178 	case HASH_TABLE_SYNC_RW_LOCK: {
179 
180 		latch_level_t	level = sync_latch_get_level(id);
181 
182 		ut_a(level != SYNC_UNKNOWN);
183 
184 		table->sync_obj.rw_locks = static_cast<rw_lock_t*>(
185 			ut_malloc_nokey(n_sync_obj * sizeof(rw_lock_t)));
186 
187 		for (ulint i = 0; i < n_sync_obj; i++) {
188 			rw_lock_create(hash_table_locks_key,
189 			     table->sync_obj.rw_locks + i, level);
190 		}
191 
192 		break;
193 	}
194 
195 	case HASH_TABLE_SYNC_NONE:
196 		ut_error;
197 	}
198 
199 	table->n_sync_obj = n_sync_obj;
200 }
201