1 /* Copyright (c) 2007, 2021, Oracle and/or its affiliates.
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, version 2.0,
5 as published by the Free Software Foundation.
6
7 This program is also distributed with certain software (including
8 but not limited to OpenSSL) that is licensed under separate terms,
9 as designated in a particular file or component or in included license
10 documentation. The authors of MySQL hereby grant you an additional
11 permission to link the program and your derivative works with the
12 separately licensed software that they have included with MySQL.
13
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License, version 2.0, for more details.
18
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 Foundation,
21 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA */
22
23
24 #include "mdl.h"
25 #include "debug_sync.h"
26 #include "prealloced_array.h"
27 #include <lf.h>
28 #include <mysqld_error.h>
29 #include <mysql/plugin.h>
30 #include <mysql/service_thd_wait.h>
31 #include <pfs_metadata_provider.h>
32 #include <mysql/psi/mysql_mdl.h>
33 #include <pfs_stage_provider.h>
34 #include <mysql/psi/mysql_stage.h>
35 #include <my_murmur3.h>
36 #include <algorithm>
37 #include <functional>
38
39 static PSI_memory_key key_memory_MDL_context_acquire_locks;
40
41 #ifdef WITH_WSREP
42 #include "debug_sync.h"
43 #include "wsrep_mysqld.h"
44 #include "wsrep_thd.h"
45 //extern "C" my_thread_id wsrep_thd_thread_id(THD *thd);
46 //extern "C" char *wsrep_thd_query(THD *thd);
47 void sql_print_information(const char *format, ...)
48 __attribute__((format(printf, 1, 2)));
49 extern bool
50 wsrep_grant_mdl_exception(const MDL_context *requestor_ctx,
51 MDL_ticket *ticket,
52 const MDL_key *key);
53 #endif /* WITH_WSREP */
54 #ifdef HAVE_PSI_INTERFACE
55 static PSI_mutex_key key_MDL_wait_LOCK_wait_status;
56
57 static PSI_mutex_info all_mdl_mutexes[]=
58 {
59 { &key_MDL_wait_LOCK_wait_status, "MDL_wait::LOCK_wait_status", 0}
60 };
61
62 static PSI_rwlock_key key_MDL_lock_rwlock;
63 static PSI_rwlock_key key_MDL_context_LOCK_waiting_for;
64
65 static PSI_rwlock_info all_mdl_rwlocks[]=
66 {
67 { &key_MDL_lock_rwlock, "MDL_lock::rwlock", 0},
68 { &key_MDL_context_LOCK_waiting_for, "MDL_context::LOCK_waiting_for", 0}
69 };
70
71 static PSI_cond_key key_MDL_wait_COND_wait_status;
72
73 static PSI_cond_info all_mdl_conds[]=
74 {
75 { &key_MDL_wait_COND_wait_status, "MDL_context::COND_wait_status", 0}
76 };
77
78 static PSI_memory_info all_mdl_memory[]=
79 {
80 { &key_memory_MDL_context_acquire_locks, "MDL_context::acquire_locks", 0}
81 };
82
83 /**
84 Initialise all the performance schema instrumentation points
85 used by the MDL subsystem.
86 */
init_mdl_psi_keys(void)87 static void init_mdl_psi_keys(void)
88 {
89 int count;
90
91 count= array_elements(all_mdl_mutexes);
92 mysql_mutex_register("sql", all_mdl_mutexes, count);
93
94 count= array_elements(all_mdl_rwlocks);
95 mysql_rwlock_register("sql", all_mdl_rwlocks, count);
96
97 count= array_elements(all_mdl_conds);
98 mysql_cond_register("sql", all_mdl_conds, count);
99
100 count= array_elements(all_mdl_memory);
101 mysql_memory_register("sql", all_mdl_memory, count);
102
103 MDL_key::init_psi_keys();
104 }
105 #endif /* HAVE_PSI_INTERFACE */
106
107
108 /**
109 Thread state names to be used in case when we have to wait on resource
110 belonging to certain namespace.
111 */
112
113 PSI_stage_info MDL_key::m_namespace_to_wait_state_name[NAMESPACE_END]=
114 {
115 {0, "Waiting for global read lock", 0},
116 {0, "Waiting for tablespace metadata lock", 0},
117 {0, "Waiting for schema metadata lock", 0},
118 {0, "Waiting for table metadata lock", 0},
119 {0, "Waiting for stored function metadata lock", 0},
120 {0, "Waiting for stored procedure metadata lock", 0},
121 {0, "Waiting for trigger metadata lock", 0},
122 {0, "Waiting for event metadata lock", 0},
123 {0, "Waiting for commit lock", 0},
124 {0, "User lock", 0}, /* Be compatible with old status. */
125 {0, "Waiting for locking service lock", 0}
126 };
127
128 #ifdef HAVE_PSI_INTERFACE
init_psi_keys()129 void MDL_key::init_psi_keys()
130 {
131 int i;
132 int count;
133 PSI_stage_info *info MY_ATTRIBUTE((unused));
134
135 count= array_elements(MDL_key::m_namespace_to_wait_state_name);
136 for (i= 0; i<count; i++)
137 {
138 /* mysql_stage_register wants an array of pointers, registering 1 by 1. */
139 info= & MDL_key::m_namespace_to_wait_state_name[i];
140 mysql_stage_register("sql", &info, 1);
141 }
142 }
143 #endif
144
145 static bool mdl_initialized= 0;
146
147
148 /**
149 A collection of all MDL locks. A singleton,
150 there is only one instance of the map in the server.
151 */
152
153 class MDL_map
154 {
155 public:
156 void init();
157 void destroy();
158
159 inline MDL_lock *find(LF_PINS *pins, const MDL_key *key, bool *pinned);
160 inline MDL_lock *find_or_insert(LF_PINS *pins, const MDL_key *key, bool *pinned);
161
162 /**
163 Decrement unused MDL_lock objects counter.
164 */
lock_object_used()165 void lock_object_used()
166 {
167 my_atomic_add32(&m_unused_lock_objects, -1);
168 }
169
170 /**
171 Increment unused MDL_lock objects counter. If number of such objects
172 exceeds threshold and unused/total objects ratio is high enough try
173 to free some of them.
174 */
lock_object_unused(MDL_context * ctx,LF_PINS * pins)175 void lock_object_unused(MDL_context *ctx, LF_PINS *pins)
176 {
177 /*
178 Use thread local copy of unused locks counter for performance/
179 scalability reasons. It is updated on both successfull and failed
180 attempts to delete unused MDL_lock objects in order to avoid infinite
181 loops,
182 */
183 int32 unused_locks= my_atomic_add32(&m_unused_lock_objects, 1) + 1;
184
185 while (unused_locks > mdl_locks_unused_locks_low_water &&
186 (unused_locks > m_locks.count * MDL_LOCKS_UNUSED_LOCKS_MIN_RATIO))
187 {
188 /*
189 If number of unused lock objects exceeds low water threshold and
190 unused/total objects ratio is high enough - try to do random dive
191 into m_locks hash, find an unused object by iterating upwards
192 through its split-ordered list and try to free it.
193 If we fail to do this - update local copy of unused objects
194 counter and retry if needed,
195
196 Note that:
197 *) It is not big deal if "m_unused_lock_objects" due to races becomes
198 negative temporarily as we perform signed comparison.
199 *) There is a good chance that we will find an unused object quickly
200 because unused/total ratio is high enough.
201 *) There is no possibility for infinite loop since our PRNG works
202 in such way that we eventually cycle through all LF_HASH hash
203 buckets (@sa MDL_context::get_random()).
204 *) Thanks to the fact that we choose random object to expel -
205 objects which are used more often will naturally stay
206 in the cache and rarely used objects will be expelled from it.
207 *) Non-atomic read of LF_HASH::count which happens above should be
208 OK as LF_HASH code does them too + preceding atomic operation
209 provides memory barrier.
210 */
211 remove_random_unused(ctx, pins, &unused_locks);
212 }
213 }
214
215 /**
216 Get number of unused MDL_lock objects in MDL_map cache.
217
218 @note Does non-atomic read so can return stale results. This is OK since
219 this method is used only in unit-tests. The latter employ means
220 of thread synchronization which are external to MDL and prevent
221 memory reordering/ensure that thread calling this method have
222 up-to-date view on the memory. @sa m_unused_lock_objects.
223 */
get_unused_locks_count() const224 int32 get_unused_locks_count() const
225 {
226 return m_unused_lock_objects;
227 }
228
229 /**
230 Allocate pins which are necessary for MDL_context/thread to be able
231 to work with MDL_map container.
232 */
get_pins()233 LF_PINS *get_pins() { return lf_hash_get_pins(&m_locks); }
234
235 /**
236 Check if MDL_lock object corresponding to the key is going to be
237 singleton.
238 */
is_lock_object_singleton(const MDL_key * mdl_key) const239 bool is_lock_object_singleton(const MDL_key *mdl_key) const
240 {
241 return (mdl_key->mdl_namespace() == MDL_key::GLOBAL ||
242 mdl_key->mdl_namespace() == MDL_key::COMMIT);
243 }
244
245 private:
246
247 void remove_random_unused(MDL_context *ctx, LF_PINS *pins, int32 *unused_locks);
248
249 /** LF_HASH with all locks in the server. */
250 LF_HASH m_locks;
251 /** Pre-allocated MDL_lock object for GLOBAL namespace. */
252 MDL_lock *m_global_lock;
253 /** Pre-allocated MDL_lock object for COMMIT namespace. */
254 MDL_lock *m_commit_lock;
255 /**
256 Number of unused MDL_lock objects in the server.
257
258 Updated using atomic operations, read using both atomic and ordinary
259 reads. We assume that ordinary reads of 32-bit words can't result in
260 partial results, but may produce stale results thanks to memory
261 reordering, LF_HASH seems to be using similar assumption.
262
263 Note that due to fact that updates to this counter are not atomic with
264 marking MDL_lock objects as used/unused it might easily get negative
265 for some short period of time. Code which uses its value needs to take
266 this into account.
267 */
268 volatile int32 m_unused_lock_objects;
269 };
270
271
272 /**
273 Threshold for number of unused MDL_lock objects.
274
275 We will start considering freeing some unused objects only after exceeding
276 this value and if unused/total objects ratio is high enough.
277
278 Normally this threshold is constant. It is exposed outside of MDL subsystem
279 as a variable only in order to simplify unit testing.
280 */
281 int32 mdl_locks_unused_locks_low_water=
282 MDL_LOCKS_UNUSED_LOCKS_LOW_WATER_DEFAULT;
283
284
285 /**
286 A context of the recursive traversal through all contexts
287 in all sessions in search for deadlock.
288 */
289
290 class Deadlock_detection_visitor: public MDL_wait_for_graph_visitor
291 {
292 public:
Deadlock_detection_visitor(MDL_context * start_node_arg)293 Deadlock_detection_visitor(MDL_context *start_node_arg)
294 : m_start_node(start_node_arg),
295 m_victim(NULL),
296 m_current_search_depth(0),
297 m_found_deadlock(FALSE)
298 {}
299 virtual bool enter_node(MDL_context *node);
300 virtual void leave_node(MDL_context *node);
301
302 virtual bool inspect_edge(MDL_context *dest);
303
get_victim() const304 MDL_context *get_victim() const { return m_victim; }
305 private:
306 /**
307 Change the deadlock victim to a new one if it has lower deadlock
308 weight.
309 */
310 void opt_change_victim_to(MDL_context *new_victim);
311 private:
312 /**
313 The context which has initiated the search. There
314 can be multiple searches happening in parallel at the same time.
315 */
316 MDL_context *m_start_node;
317 /** If a deadlock is found, the context that identifies the victim. */
318 MDL_context *m_victim;
319 /** Set to the 0 at start. Increased whenever
320 we descend into another MDL context (aka traverse to the next
321 wait-for graph node). When MAX_SEARCH_DEPTH is reached, we
322 assume that a deadlock is found, even if we have not found a
323 loop.
324 */
325 uint m_current_search_depth;
326 /** TRUE if we found a deadlock. */
327 bool m_found_deadlock;
328 /**
329 Maximum depth for deadlock searches. After this depth is
330 achieved we will unconditionally declare that there is a
331 deadlock.
332
333 @note This depth should be small enough to avoid stack
334 being exhausted by recursive search algorithm.
335
336 TODO: Find out what is the optimal value for this parameter.
337 Current value is safe, but probably sub-optimal,
338 as there is an anecdotal evidence that real-life
339 deadlocks are even shorter typically.
340 */
341 static const uint MAX_SEARCH_DEPTH= 32;
342 };
343
344
345 /**
346 Enter a node of a wait-for graph. After
347 a node is entered, inspect_edge() will be called
348 for all wait-for destinations of this node. Then
349 leave_node() will be called.
350 We call "enter_node()" for all nodes we inspect,
351 including the starting node.
352
353 @retval TRUE Maximum search depth exceeded.
354 @retval FALSE OK.
355 */
356
enter_node(MDL_context * node)357 bool Deadlock_detection_visitor::enter_node(MDL_context *node)
358 {
359 m_found_deadlock= ++m_current_search_depth >= MAX_SEARCH_DEPTH;
360 if (m_found_deadlock)
361 {
362 assert(! m_victim);
363 opt_change_victim_to(node);
364 }
365 return m_found_deadlock;
366 }
367
368
369 /**
370 Done inspecting this node. Decrease the search
371 depth. If a deadlock is found, and we are
372 backtracking to the start node, optionally
373 change the deadlock victim to one with lower
374 deadlock weight.
375 */
376
leave_node(MDL_context * node)377 void Deadlock_detection_visitor::leave_node(MDL_context *node)
378 {
379 --m_current_search_depth;
380 if (m_found_deadlock)
381 opt_change_victim_to(node);
382 }
383
384
385 /**
386 Inspect a wait-for graph edge from one MDL context to another.
387
388 @retval TRUE A loop is found.
389 @retval FALSE No loop is found.
390 */
391
inspect_edge(MDL_context * node)392 bool Deadlock_detection_visitor::inspect_edge(MDL_context *node)
393 {
394 m_found_deadlock= node == m_start_node;
395 return m_found_deadlock;
396 }
397
398
399 /**
400 Change the deadlock victim to a new one if it has lower deadlock
401 weight.
402
403 @param new_victim New candidate for deadlock victim.
404 */
405
406 void
opt_change_victim_to(MDL_context * new_victim)407 Deadlock_detection_visitor::opt_change_victim_to(MDL_context *new_victim)
408 {
409 if (m_victim == NULL ||
410 m_victim->get_deadlock_weight() >= new_victim->get_deadlock_weight())
411 {
412 /* Swap victims, unlock the old one. */
413 MDL_context *tmp= m_victim;
414 m_victim= new_victim;
415 m_victim->lock_deadlock_victim();
416 if (tmp)
417 tmp->unlock_deadlock_victim();
418 }
419 }
420
421
422 /**
423 Get a bit corresponding to enum_mdl_type value in a granted/waiting bitmaps
424 and compatibility matrices.
425 */
426
427 #define MDL_BIT(A) static_cast<MDL_lock::bitmap_t>(1U << A)
428
429 /**
430 The lock context. Created internally for an acquired lock.
431 For a given name, there exists only one MDL_lock instance,
432 and it exists only when the lock has been granted.
433 Can be seen as an MDL subsystem's version of TABLE_SHARE.
434
435 This is an abstract class which lacks information about
436 compatibility rules for lock types. They should be specified
437 in its descendants.
438 */
439
440 class MDL_lock
441 {
442 public:
443 typedef unsigned short bitmap_t;
444
445 class Ticket_list
446 {
447 public:
448 typedef I_P_List<MDL_ticket,
449 I_P_List_adapter<MDL_ticket,
450 &MDL_ticket::next_in_lock,
451 &MDL_ticket::prev_in_lock>,
452 I_P_List_null_counter,
453 I_P_List_fast_push_back<MDL_ticket> >
454 List;
operator const List&() const455 operator const List &() const { return m_list; }
Ticket_list()456 Ticket_list() :m_bitmap(0) {}
457
458 void add_ticket(MDL_ticket *ticket);
459 void remove_ticket(MDL_ticket *ticket);
is_empty() const460 bool is_empty() const { return m_list.is_empty(); }
bitmap() const461 bitmap_t bitmap() const { return m_bitmap; }
462 private:
463 void clear_bit_if_not_in_list(enum_mdl_type type);
464 private:
465 /** List of tickets. */
466 List m_list;
467 /** Bitmap of types of tickets in this list. */
468 bitmap_t m_bitmap;
469 };
470
471 typedef Ticket_list::List::Iterator Ticket_iterator;
472
473 typedef longlong fast_path_state_t;
474
475 /**
476 Helper struct which defines how different types of locks are handled
477 for a specific MDL_lock. In practice we use only two strategies: "scoped"
478 lock strategy for locks in GLOBAL, COMMIT, TABLESPACE and SCHEMA namespaces
479 and "object" lock strategy for all other namespaces.
480 */
481 struct MDL_lock_strategy
482 {
483 /**
484 Compatibility (or rather "incompatibility") matrices for lock types.
485
486 Array of bitmaps which elements specify which granted locks are
487 incompatible with the type of lock being requested.
488 */
489 bitmap_t m_granted_incompatible[MDL_TYPE_END];
490 /**
491 Arrays of bitmaps which elements specify which waiting locks are
492 incompatible with the type of lock being requested. Basically, each
493 array defines priorities between lock types.
494 We need 4 separate arrays since in order to prevent starvation for
495 some of lock request types, we use different priority matrices:
496 0) in "normal" situation.
497 1) in situation when the number of successively granted "piglet" requests
498 exceeds the max_write_lock_count limit.
499 2) in situation when the number of successively granted "hog" requests
500 exceeds the max_write_lock_count limit.
501 3) in situation when both "piglet" and "hog" counters exceed limit.
502 */
503 bitmap_t m_waiting_incompatible[4][MDL_TYPE_END];
504 /**
505 Array of increments for "unobtrusive" types of lock requests for locks.
506 @sa MDL_lock::get_unobtrusive_lock_increment().
507 */
508 fast_path_state_t m_unobtrusive_lock_increment[MDL_TYPE_END];
509 /**
510 Indicates that locks of this type are affected by
511 the max_write_lock_count limit.
512 */
513 bool m_is_affected_by_max_write_lock_count;
514
515 /**
516 Pointer to a static method which determines if the type of lock
517 requested requires notification of conflicting locks. NULL if there
518 are no lock types requiring notification.
519 */
520 bool (*m_needs_notification)(const MDL_ticket *ticket);
521 /**
522 Pointer to a static method which allows notification of owners of
523 conflicting locks about the fact that a type of lock requiring
524 notification was requested.
525 */
526 void (*m_notify_conflicting_locks)(MDL_context *ctx, MDL_lock *lock);
527 /**
528 Pointer to a static method which converts information about
529 locks granted using "fast" path from fast_path_state_t
530 representation to bitmap of lock types.
531 */
532 bitmap_t (*m_fast_path_granted_bitmap)(const MDL_lock &lock);
533 /**
534 Pointer to a static method which determines if waiting for the lock
535 should be aborted when when connection is lost. NULL if locks of
536 this type don't require such aborts.
537 */
538 bool (*m_needs_connection_check)(const MDL_lock *lock);
539 };
540
541 public:
542 /** The key of the object (data) being protected. */
543 MDL_key key;
544 /**
545 Read-write lock protecting this lock context.
546
547 @note The fact that we use read-write lock prefers readers here is
548 important as deadlock detector won't work correctly otherwise.
549
550 For example, imagine that we have following waiters graph:
551
552 ctxA -> obj1 -> ctxB -> obj1 -|
553 ^ |
554 |----------------------------|
555
556 and both ctxA and ctxB start deadlock detection process:
557
558 ctxA read-locks obj1 ctxB read-locks obj2
559 ctxA goes deeper ctxB goes deeper
560
561 Now ctxC comes in who wants to start waiting on obj1, also
562 ctxD comes in who wants to start waiting on obj2.
563
564 ctxC tries to write-lock obj1 ctxD tries to write-lock obj2
565 ctxC is blocked ctxD is blocked
566
567 Now ctxA and ctxB resume their search:
568
569 ctxA tries to read-lock obj2 ctxB tries to read-lock obj1
570
571 If m_rwlock prefers writes (or fair) both ctxA and ctxB would be
572 blocked because of pending write locks from ctxD and ctxC
573 correspondingly. Thus we will get a deadlock in deadlock detector.
574 If m_wrlock prefers readers (actually ignoring pending writers is
575 enough) ctxA and ctxB will continue and no deadlock will occur.
576 */
577 mysql_prlock_t m_rwlock;
578
incompatible_granted_types_bitmap() const579 const bitmap_t *incompatible_granted_types_bitmap() const
580 {
581 return m_strategy->m_granted_incompatible;
582 }
583
incompatible_waiting_types_bitmap() const584 const bitmap_t *incompatible_waiting_types_bitmap() const
585 {
586 return
587 m_strategy->m_waiting_incompatible[m_current_waiting_incompatible_idx];
588 }
589
590 /**
591 Get index of priority matrice in MDL_lock_strategy::m_waiting_incompatible
592 array which corresponds to current values of the m_piglet_lock_count and
593 m_hog_lock_count counters and the max_write_lock_count threshold.
594 */
get_incompatible_waiting_types_bitmap_idx() const595 uint get_incompatible_waiting_types_bitmap_idx() const
596 {
597 mysql_prlock_assert_write_owner(&m_rwlock);
598 /*
599 To prevent starvation for lock types with lower priority use:
600
601 *) MDL_lock_strategy::m_waiting_incompatible[0] matrice by default.
602 *) MDL_lock_strategy::m_waiting_incompatible[1] when the number of
603 successively granted "piglet" requests exceeds max_write_lock_count.
604 *) MDL_lock_strategy::m_waiting_incompatible[2] when the number of
605 successively granted "hog" requests exceeds max_write_lock_count.
606 *) MDL_lock_strategy::m_waiting_incompatible[3] when both "piglet" and
607 "hog" counters exceed this limit.
608 */
609 uint idx= 0;
610 if (m_piglet_lock_count >= max_write_lock_count)
611 idx+= 1;
612 if (m_hog_lock_count >= max_write_lock_count)
613 idx+= 2;
614 return idx;
615 }
616
617 /**
618 Switch priority matrice for the MDL_lock object if m_piglet_lock_count or/
619 and m_hog_lock_count counters have crossed max_write_lock_count threshold.
620
621 @returns true - if priority matrice has been changed, false - otherwise.
622 */
switch_incompatible_waiting_types_bitmap_if_needed()623 bool switch_incompatible_waiting_types_bitmap_if_needed()
624 {
625 mysql_prlock_assert_write_owner(&m_rwlock);
626
627 uint new_idx= get_incompatible_waiting_types_bitmap_idx();
628 if (m_current_waiting_incompatible_idx == new_idx)
629 return false;
630 m_current_waiting_incompatible_idx= new_idx;
631 return true;
632 }
633
634 bool has_pending_conflicting_lock(enum_mdl_type type);
635
636 bool can_grant_lock(enum_mdl_type type,
637 const MDL_context *requestor_ctx) const;
638
639 void reschedule_waiters();
640
641 void remove_ticket(MDL_context *ctx, LF_PINS *pins,
642 Ticket_list MDL_lock::*queue,
643 MDL_ticket *ticket);
644
645 bool visit_subgraph(MDL_ticket *waiting_ticket,
646 MDL_wait_for_graph_visitor *gvisitor);
647
needs_notification(const MDL_ticket * ticket) const648 bool needs_notification(const MDL_ticket *ticket) const
649 {
650 return m_strategy->m_needs_notification ?
651 m_strategy->m_needs_notification(ticket) : false;
652 }
653
notify_conflicting_locks(MDL_context * ctx)654 void notify_conflicting_locks(MDL_context *ctx)
655 {
656 if (m_strategy->m_notify_conflicting_locks)
657 m_strategy->m_notify_conflicting_locks(ctx, this);
658 }
659
needs_connection_check() const660 bool needs_connection_check() const
661 {
662 return m_strategy->m_needs_connection_check ?
663 m_strategy->m_needs_connection_check(this) : false;
664 }
665
666 inline static bool needs_hton_notification(MDL_key::enum_mdl_namespace
667 mdl_namespace);
668
is_affected_by_max_write_lock_count() const669 bool is_affected_by_max_write_lock_count() const
670 {
671 return m_strategy->m_is_affected_by_max_write_lock_count;
672 }
673
674 /**
675 If we just have granted a lock of "piglet" or "hog" type and there are
676 pending lower priority locks, increase the appropriate counter. If this
677 counter now exceeds the max_write_lock_count threshold, switch priority
678 matrice for the MDL_lock object.
679
680 @returns true - if priority matrice has been changed, false - otherwise.
681 */
count_piglets_and_hogs(enum_mdl_type type)682 bool count_piglets_and_hogs(enum_mdl_type type)
683 {
684 mysql_prlock_assert_write_owner(&m_rwlock);
685
686 if ((MDL_BIT(type) & MDL_OBJECT_HOG_LOCK_TYPES) != 0)
687 {
688 if (m_waiting.bitmap() & ~MDL_OBJECT_HOG_LOCK_TYPES)
689 {
690 m_hog_lock_count++;
691 if (switch_incompatible_waiting_types_bitmap_if_needed())
692 return true;
693 }
694 }
695 else if (type == MDL_SHARED_WRITE)
696 {
697 if (m_waiting.bitmap() & MDL_BIT(MDL_SHARED_READ_ONLY))
698 {
699 m_piglet_lock_count++;
700 if (switch_incompatible_waiting_types_bitmap_if_needed())
701 return true;
702 }
703 }
704 return false;
705 }
706
707 /**
708 @returns "Fast path" increment for request for "unobtrusive" type
709 of lock, 0 - if it is request for "obtrusive" type of
710 lock.
711
712 @note We split all lock types for each of MDL namespaces
713 in two sets:
714
715 A) "unobtrusive" lock types
716 1) Each type from this set should be compatible with all other
717 types from the set (including itself).
718 2) These types should be common for DML operations
719
720 Our goal is to optimize acquisition and release of locks of this
721 type by avoiding complex checks and manipulations on m_waiting/
722 m_granted bitmaps/lists. We replace them with a check of and
723 increment/decrement of integer counters.
724 We call the latter type of acquisition/release "fast path".
725 Use of "fast path" reduces the size of critical section associated
726 with MDL_lock::m_rwlock lock in the common case and thus increases
727 scalability.
728
729 The amount by which acquisition/release of specific type
730 "unobtrusive" lock increases/decreases packed counter in
731 MDL_lock::m_fast_path_state is returned by this function.
732
733 B) "obtrusive" lock types
734 1) Granted or pending lock of those type is incompatible with
735 some other types of locks or with itself.
736 2) Not common for DML operations
737
738 These locks have to be always acquired involving manipulations on
739 m_waiting/m_granted bitmaps/lists, i.e. we have to use "slow path"
740 for them. Moreover in the presence of active/pending locks from
741 "obtrusive" set we have to acquire using "slow path" even locks of
742 "unobtrusive" type.
743
744 @sa MDL_scoped_lock/MDL_object_lock::m_unobtrusive_lock_increment for
745 definitions of these sets for scoped and per-object locks.
746 */
747 inline static fast_path_state_t
748 get_unobtrusive_lock_increment(const MDL_request *request);
749
750 /**
751 @returns "Fast path" increment if type of lock is "unobtrusive" type,
752 0 - if it is "obtrusive" type of lock.
753 */
get_unobtrusive_lock_increment(enum_mdl_type type) const754 fast_path_state_t get_unobtrusive_lock_increment(enum_mdl_type type) const
755 {
756 return m_strategy->m_unobtrusive_lock_increment[type];
757 }
758
759 /**
760 Check if type of lock requested is "obtrusive" type of lock.
761
762 @sa MDL_lock::get_unobtrusive_lock_increment() description.
763 */
is_obtrusive_lock(enum_mdl_type type) const764 bool is_obtrusive_lock(enum_mdl_type type) const
765 {
766 return get_unobtrusive_lock_increment(type) == 0;
767 }
768
769 /**
770 Return set of types of lock requests which were granted using
771 "fast path" algorithm in the bitmap_t form.
772
773 This method is only called from MDL_lock::can_grant_lock() and its
774 return value is only important when we are trying to figure out if
775 we can grant an obtrusive lock. But this means that the HAS_OBTRUSIVE
776 flag is set so all changes to m_fast_path_state happen under protection
777 of MDL_lock::m_rwlock (see invariant [INV1]).
778 Since can_grant_lock() is called only when MDL_lock::m_rwlock is held,
779 it is safe to do an ordinary read of m_fast_path_state here.
780 */
fast_path_granted_bitmap() const781 bitmap_t fast_path_granted_bitmap() const
782 {
783 return m_strategy->m_fast_path_granted_bitmap(*this);
784 }
785
786 /** List of granted tickets for this lock. */
787 Ticket_list m_granted;
788 /** Tickets for contexts waiting to acquire a lock. */
789 Ticket_list m_waiting;
790
791 private:
792 /**
793 Number of times high priority, "hog" lock requests (X, SNRW, SNW) have been
794 granted while lower priority lock requests (all other types) were waiting.
795 Currently used only for object locks. Protected by m_rwlock lock.
796 */
797 ulong m_hog_lock_count;
798 /**
799 Number of times high priority, "piglet" lock requests (SW) have been
800 granted while locks requests with lower priority (SRO) were waiting.
801 Currently used only for object locks. Protected by m_rwlock lock.
802 */
803 ulong m_piglet_lock_count;
804 /**
805 Index of one of the MDL_lock_strategy::m_waiting_incompatible
806 arrays which represents the current priority matrice.
807 */
808 uint m_current_waiting_incompatible_idx;
809
810 public:
811
812 /**
813 Do "expensive" part of MDL_lock object initialization,
814 Called by LF_ALLOCATOR for each newly malloc()'ed MDL_lock object, is not
815 called in cases when LF_ALLOCATOR decides to reuse object which was
816 returned to it earlier. "Full" initialization happens later by calling
817 MDL_lock::reinit(). So @sa MDL_lock::reiniti()
818 */
MDL_lock()819 MDL_lock()
820 : m_obtrusive_locks_granted_waiting_count(0)
821 {
822 mysql_prlock_init(key_MDL_lock_rwlock, &m_rwlock);
823 }
824
825 inline void reinit(const MDL_key *mdl_key);
826
~MDL_lock()827 ~MDL_lock()
828 {
829 mysql_prlock_destroy(&m_rwlock);
830 }
831
832 inline static MDL_lock *create(const MDL_key *key);
833 inline static void destroy(MDL_lock *lock);
834
835 inline MDL_context *get_lock_owner() const;
836
837 public:
838 /**
839 Number of granted or waiting lock requests of "obtrusive" type.
840 Also includes "obtrusive" lock requests for which we about to check
841 if they can be granted.
842
843
844 @sa MDL_lock::get_unobtrusive_lock_increment() description.
845
846 @note This number doesn't include "unobtrusive" locks which were acquired
847 using "slow path".
848 */
849 uint m_obtrusive_locks_granted_waiting_count;
850 /**
851 Flag in MDL_lock::m_fast_path_state that indicates that the MDL_lock
852 object was marked for destruction and will be destroyed once all threads
853 referencing to it through hazard pointers have unpinned it.
854 Set using atomic compare-and-swap AND under protection of
855 MDL_lock::m_rwlock lock.
856 Thanks to this can be read either by using atomic compare-and-swap OR
857 using ordinary read under protection of MDL_lock::m_rwlock lock.
858 */
859 static const fast_path_state_t IS_DESTROYED= 1ULL << 62;
860 /**
861 Flag in MDL_lock::m_fast_path_state that indicates that there are
862 "obtrusive" locks which are granted, waiting or for which we are
863 about to check if they can be granted.
864 Corresponds to "MDL_lock::m_obtrusive_locks_granted_waiting_count == 0"
865 predicate.
866 Set using atomic compare-and-swap AND under protection of
867 MDL_lock::m_rwlock lock.
868 Thanks to this can be read either by using atomic compare-and-swap OR
869 using ordinary read under protection of MDL_lock::m_rwlock lock.
870
871 Invariant [INV1]: When this flag is set all changes to m_fast_path_state
872 member has to be done under protection of m_rwlock lock.
873 */
874 static const fast_path_state_t HAS_OBTRUSIVE= 1ULL << 61;
875 /**
876 Flag in MDL_lock::m_fast_path_state that indicates that there are
877 "slow" path locks which are granted, waiting or for which we are
878 about to check if they can be granted.
879 Corresponds to MDL_lock::m_granted/m_waiting lists being non-empty
880 (except special case in MDL_context::try_acquire_lock()).
881 Set using atomic compare-and-swap AND under protection of m_rwlock
882 lock. The latter is necessary because value of this flag needs to be
883 synchronized with contents of MDL_lock::m_granted/m_waiting lists.
884 */
885 static const fast_path_state_t HAS_SLOW_PATH= 1ULL << 60;
886 /**
887 Combination of IS_DESTROYED/HAS_OBTRUSIVE/HAS_SLOW_PATH flags and packed
888 counters of specific types of "unobtrusive" locks which were granted using
889 "fast path".
890
891 @sa MDL_scoped_lock::m_unobtrusive_lock_increment and
892 MDL_object_lock::m_unobtrusive_lock_increment for details about how
893 counts of different types of locks are packed into this field.
894
895 @note Doesn't include "unobtrusive" locks granted using "slow path".
896
897 @note We use combination of atomic operations and protection by
898 MDL_lock::m_rwlock lock to work with this member:
899
900 * Write and Read-Modify-Write operations are always carried out
901 atomically. This is necessary to avoid lost updates on 32-bit
902 platforms among other things.
903 * In some cases Reads can be done non-atomically because we don't
904 really care about value which they will return (for example,
905 if further down the line there will be an atomic compare-and-swap
906 operation, which will validate this value and provide the correct
907 value if the validation will fail).
908 * In other cases Reads can be done non-atomically since they happen
909 under protection of MDL_lock::m_rwlock and there is some invariant
910 which ensures that concurrent updates of the m_fast_path_state
911 member can't happen while MDL_lock::m_rwlock is held
912 (@sa IS_DESTROYED, HAS_OBTRUSIVE, HAS_SLOW_PATH).
913
914 @note IMPORTANT!!!
915 In order to enforce the above rules and other invariants,
916 MDL_lock::m_fast_path_state should not be updated directly.
917 Use fast_path_state_cas()/add()/reset() wrapper methods instead.
918
919 @note Needs to be volatile in order to be compatible with our
920 my_atomic_*() API.
921 */
922 volatile fast_path_state_t m_fast_path_state;
923
924 /**
925 Wrapper for my_atomic_cas64 operation on m_fast_path_state member
926 which enforces locking and other invariants.
927 */
fast_path_state_cas(fast_path_state_t * old_state,fast_path_state_t new_state)928 bool fast_path_state_cas(fast_path_state_t *old_state,
929 fast_path_state_t new_state)
930 {
931 /*
932 IS_DESTROYED, HAS_OBTRUSIVE and HAS_SLOW_PATH flags can be set or
933 cleared only while holding MDL_lock::m_rwlock lock.
934 If HAS_SLOW_PATH flag is set all changes to m_fast_path_state
935 should happen under protection of MDL_lock::m_rwlock ([INV1]).
936 */
937 #if !defined(NDEBUG)
938 if (((*old_state & (IS_DESTROYED | HAS_OBTRUSIVE | HAS_SLOW_PATH)) !=
939 (new_state & (IS_DESTROYED | HAS_OBTRUSIVE | HAS_SLOW_PATH))) ||
940 *old_state & HAS_OBTRUSIVE)
941 {
942 mysql_prlock_assert_write_owner(&m_rwlock);
943 }
944 #endif
945 /*
946 We should not change state of destroyed object
947 (fast_path_state_reset() being exception).
948 */
949 assert(! (*old_state & IS_DESTROYED));
950
951 return my_atomic_cas64(&m_fast_path_state, old_state, new_state);
952 }
953
954 /**
955 Wrapper for my_atomic_add64 operation on m_fast_path_state member
956 which enforces locking and other invariants.
957 */
fast_path_state_add(fast_path_state_t value)958 fast_path_state_t fast_path_state_add(fast_path_state_t value)
959 {
960 /*
961 Invariant [INV1] requires all changes to m_fast_path_state happen
962 under protection of m_rwlock if HAS_OBTRUSIVE flag is set.
963 Since this operation doesn't check this flag it can be called only
964 under protection of m_rwlock.
965 */
966 mysql_prlock_assert_write_owner(&m_rwlock);
967
968 fast_path_state_t old_state= my_atomic_add64(&m_fast_path_state, value);
969
970 /*
971 We should not change state of destroyed object
972 (fast_path_state_reset() being exception).
973 */
974 assert(! (old_state & IS_DESTROYED));
975 return old_state;
976 }
977
978 /**
979 Wrapper for resetting m_fast_path_state enforcing locking invariants.
980 */
fast_path_state_reset()981 void fast_path_state_reset()
982 {
983 /* HAS_DESTROYED flag can be cleared only under protection of m_rwlock. */
984 mysql_prlock_assert_write_owner(&m_rwlock);
985 my_atomic_store64(&m_fast_path_state, 0);
986 }
987
988 /**
989 Pointer to strategy object which defines how different types of lock
990 requests should be handled for the namespace to which this lock belongs.
991 @sa MDL_lock::m_scoped_lock_strategy and MDL_lock:m_object_lock_strategy.
992 */
993 const MDL_lock_strategy *m_strategy;
994
995 /**
996 Get bitmap of "unobtrusive" locks granted using "fast path" algorithm
997 for scoped locks.
998
999 @sa MDL_lock::fast_path_granted_bitmap() for explanation about why it
1000 is safe to use non-atomic read of MDL_lock::m_fast_path_state here.
1001 */
scoped_lock_fast_path_granted_bitmap(const MDL_lock & lock)1002 static bitmap_t scoped_lock_fast_path_granted_bitmap(const MDL_lock &lock)
1003 {
1004 return (lock.m_fast_path_state & 0xFFFFFFFFFFFFFFFULL) ?
1005 MDL_BIT(MDL_INTENTION_EXCLUSIVE) : 0;
1006 }
1007
1008 /**
1009 Check if we are requesting X lock on the object, so threads holding
1010 conflicting S/SH metadata locks on it need to be notified.
1011
1012 @sa MDL_lock::object_lock_notify_conflicting_locks.
1013 */
object_lock_needs_notification(const MDL_ticket * ticket)1014 static bool object_lock_needs_notification(const MDL_ticket *ticket)
1015 {
1016 return (ticket->get_type() == MDL_EXCLUSIVE);
1017 }
1018 static void object_lock_notify_conflicting_locks(MDL_context *ctx,
1019 MDL_lock *lock);
1020 /**
1021 Get bitmap of "unobtrusive" locks granted using "fast path" algorithm
1022 for per-object locks.
1023
1024 @sa MDL_lock::fast_path_granted_bitmap() for explanation about why it
1025 is safe to use non-atomic read of MDL_lock::m_fast_path_state here.
1026 */
object_lock_fast_path_granted_bitmap(const MDL_lock & lock)1027 static bitmap_t object_lock_fast_path_granted_bitmap(const MDL_lock &lock)
1028 {
1029 bitmap_t result= 0;
1030 fast_path_state_t fps= lock.m_fast_path_state;
1031 if (fps & 0xFFFFFULL)
1032 result|= MDL_BIT(MDL_SHARED);
1033 if (fps & (0xFFFFFULL << 20))
1034 result|= MDL_BIT(MDL_SHARED_READ);
1035 if (fps & (0xFFFFFULL << 40))
1036 result|= MDL_BIT(MDL_SHARED_WRITE);
1037 return result;
1038 }
1039
1040 /**
1041 Check if MDL_lock object represents user-level lock or locking service
1042 lock, so threads waiting for it need to check if connection is lost
1043 and abort waiting when it is.
1044 */
object_lock_needs_connection_check(const MDL_lock * lock)1045 static bool object_lock_needs_connection_check(const MDL_lock *lock)
1046 {
1047 return (lock->key.mdl_namespace() == MDL_key::USER_LEVEL_LOCK ||
1048 lock->key.mdl_namespace() == MDL_key::LOCKING_SERVICE);
1049 }
1050
1051 /**
1052 Bitmap with "hog" lock types for object locks.
1053
1054 Locks of these types can easily starve out lower priority locks.
1055 To prevent this we only grant them max_write_lock_count times in
1056 a row while other lock types are waiting.
1057 */
1058 static const bitmap_t MDL_OBJECT_HOG_LOCK_TYPES=
1059 (MDL_BIT(MDL_SHARED_NO_WRITE) |
1060 MDL_BIT(MDL_SHARED_NO_READ_WRITE) |
1061 MDL_BIT(MDL_EXCLUSIVE));
1062
1063
1064 static const MDL_lock_strategy m_scoped_lock_strategy;
1065 static const MDL_lock_strategy m_object_lock_strategy;
1066 };
1067
1068
1069 static MDL_map mdl_locks;
1070
1071
1072 extern "C"
1073 {
1074 static uchar *
mdl_locks_key(const uchar * record,size_t * length,my_bool not_used MY_ATTRIBUTE ((unused)))1075 mdl_locks_key(const uchar *record, size_t *length,
1076 my_bool not_used MY_ATTRIBUTE((unused)))
1077 {
1078 MDL_lock *lock=(MDL_lock*) record;
1079 *length= lock->key.length();
1080 return (uchar*) lock->key.ptr();
1081 }
1082 } /* extern "C" */
1083
1084
1085 /**
1086 Initialize the metadata locking subsystem.
1087
1088 This function is called at server startup.
1089
1090 In particular, initializes the new global mutex and
1091 the associated condition variable: LOCK_mdl and COND_mdl.
1092 These locking primitives are implementation details of the MDL
1093 subsystem and are private to it.
1094 */
1095
mdl_init()1096 void mdl_init()
1097 {
1098 assert(! mdl_initialized);
1099 mdl_initialized= TRUE;
1100
1101 #ifdef HAVE_PSI_INTERFACE
1102 init_mdl_psi_keys();
1103 #endif
1104
1105 mdl_locks.init();
1106 }
1107
1108
1109 /**
1110 Release resources of metadata locking subsystem.
1111
1112 Destroys the global mutex and the condition variable.
1113 Called at server shutdown.
1114 */
1115
mdl_destroy()1116 void mdl_destroy()
1117 {
1118 if (mdl_initialized)
1119 {
1120 mdl_initialized= FALSE;
1121 mdl_locks.destroy();
1122 }
1123 }
1124
1125
1126 /**
1127 Get number of unused MDL_lock objects in MDL_map cache.
1128 Mostly needed for unit-testing.
1129 */
1130
mdl_get_unused_locks_count()1131 int32 mdl_get_unused_locks_count()
1132 {
1133 return mdl_locks.get_unused_locks_count();
1134 }
1135
1136
1137 extern "C"
1138 {
mdl_lock_cons(uchar * arg)1139 static void mdl_lock_cons(uchar *arg)
1140 {
1141 new (arg+LF_HASH_OVERHEAD) MDL_lock();
1142 }
1143
mdl_lock_dtor(uchar * arg)1144 static void mdl_lock_dtor(uchar *arg)
1145 {
1146 MDL_lock *lock= (MDL_lock *)(arg+LF_HASH_OVERHEAD);
1147 lock->~MDL_lock();
1148 }
1149
mdl_lock_reinit(uchar * dst_arg,const uchar * src_arg)1150 static void mdl_lock_reinit(uchar *dst_arg, const uchar *src_arg)
1151 {
1152 MDL_lock *dst= (MDL_lock *)dst_arg;
1153 const MDL_key *src= (const MDL_key *)src_arg;
1154 dst->reinit(src);
1155 }
1156
1157 /**
1158 Adapter function which allows to use murmur3 with LF_HASH implementation.
1159 */
1160
murmur3_adapter(const LF_HASH *,const uchar * key,size_t length)1161 static uint murmur3_adapter(const LF_HASH*, const uchar *key, size_t length)
1162 {
1163 return murmur3_32(key, length, 0);
1164 }
1165
1166 } /* extern "C" */
1167
1168
1169 /** Initialize the container for all MDL locks. */
1170
init()1171 void MDL_map::init()
1172 {
1173 MDL_key global_lock_key(MDL_key::GLOBAL, "", "");
1174 MDL_key commit_lock_key(MDL_key::COMMIT, "", "");
1175
1176 m_global_lock= MDL_lock::create(&global_lock_key);
1177 m_commit_lock= MDL_lock::create(&commit_lock_key);
1178
1179 m_unused_lock_objects= 0;
1180
1181 lf_hash_init2(&m_locks, sizeof(MDL_lock), LF_HASH_UNIQUE,
1182 0, 0, mdl_locks_key, &my_charset_bin, &murmur3_adapter,
1183 &mdl_lock_cons, &mdl_lock_dtor, &mdl_lock_reinit);
1184 }
1185
1186
1187 /**
1188 Destroy the container for all MDL locks.
1189 @pre It must be empty.
1190 */
1191
destroy()1192 void MDL_map::destroy()
1193 {
1194 MDL_lock::destroy(m_global_lock);
1195 MDL_lock::destroy(m_commit_lock);
1196
1197 lf_hash_destroy(&m_locks);
1198 }
1199
1200
1201 /**
1202 Find MDL_lock object corresponding to the key.
1203
1204 @param[in/out] pins LF_PINS to be used for pinning pointers during
1205 look-up and returned MDL_lock object.
1206 @param[in] mdl_key Key for which MDL_lock object needs to be found.
1207 @param[out] pinned TRUE - if MDL_lock object is pinned,
1208 FALSE - if MDL_lock object doesn't require pinning
1209 (i.e. it is an object for GLOBAL or COMMIT
1210 namespaces).
1211
1212 @retval MY_ERRPTR - Failure (OOM)
1213 @retval other-non-NULL - MDL_lock object found.
1214 @retval NULL - Object not found.
1215 */
1216
find(LF_PINS * pins,const MDL_key * mdl_key,bool * pinned)1217 MDL_lock* MDL_map::find(LF_PINS *pins, const MDL_key *mdl_key, bool *pinned)
1218 {
1219 MDL_lock *lock;
1220
1221 if (is_lock_object_singleton(mdl_key))
1222 {
1223 /*
1224 Avoid look up in m_locks hash when lock for GLOBAL or COMMIT namespace
1225 is requested. Return pointer to pre-allocated MDL_lock instance instead.
1226 Such an optimization allows us to avoid a few atomic operations for any
1227 statement changing data.
1228
1229 It works since these namespaces contain only one element so keys
1230 for them look like '<namespace-id>\0\0'.
1231 */
1232 assert(mdl_key->length() == 3);
1233
1234 lock= (mdl_key->mdl_namespace() == MDL_key::GLOBAL) ? m_global_lock :
1235 m_commit_lock;
1236
1237 *pinned= false;
1238
1239 return lock;
1240 }
1241
1242 lock= static_cast<MDL_lock *>(lf_hash_search(&m_locks, pins, mdl_key->ptr(),
1243 mdl_key->length()));
1244
1245 if (lock == NULL || lock == MY_ERRPTR)
1246 {
1247 lf_hash_search_unpin(pins);
1248 *pinned= false; // Avoid warnings on older compilers.
1249 return lock;
1250 }
1251
1252 *pinned= true;
1253
1254 return lock;
1255 }
1256
1257
1258 /**
1259 Find MDL_lock object corresponding to the key, create it
1260 if it does not exist.
1261
1262 @param[in/out] pins LF_PINS to be used for pinning pointers during
1263 look-up and returned MDL_lock object.
1264 @param[in] mdl_key Key for which MDL_lock object needs to be found.
1265 @param[out] pinned TRUE - if MDL_lock object is pinned,
1266 FALSE - if MDL_lock object doesn't require pinning
1267 (i.e. it is an object for GLOBAL or COMMIT
1268 namespaces).
1269
1270 @retval non-NULL - Success. MDL_lock instance for the key with
1271 locked MDL_lock::m_rwlock.
1272 @retval NULL - Failure (OOM).
1273 */
1274
find_or_insert(LF_PINS * pins,const MDL_key * mdl_key,bool * pinned)1275 MDL_lock* MDL_map::find_or_insert(LF_PINS *pins, const MDL_key *mdl_key,
1276 bool *pinned)
1277 {
1278 MDL_lock *lock;
1279
1280 while ((lock= find(pins, mdl_key, pinned)) == NULL)
1281 {
1282 /*
1283 MDL_lock for key isn't present in hash, try to insert new object.
1284 This can fail due to concurrent inserts.
1285 */
1286 int rc= lf_hash_insert(&m_locks, pins, mdl_key);
1287 if (rc == -1) /* If OOM. */
1288 return NULL;
1289 else if (rc == 0)
1290 {
1291 /*
1292 New MDL_lock object is not used yet. So we need to
1293 increment number of unused lock objects.
1294 */
1295 my_atomic_add32(&m_unused_lock_objects, 1);
1296 }
1297 }
1298 if (lock == MY_ERRPTR)
1299 {
1300 /* If OOM in lf_hash_search. */
1301 return NULL;
1302 }
1303
1304 return lock;
1305 }
1306
1307
1308 extern "C"
1309 {
1310 /**
1311 Helper function which allows to check if MDL_lock object stored in LF_HASH
1312 is unused - i.e. doesn't have any locks on both "fast" and "slow" paths
1313 and is not marked as deleted.
1314 */
mdl_lock_match_unused(const uchar * arg)1315 static int mdl_lock_match_unused(const uchar *arg)
1316 {
1317 const MDL_lock *lock= (const MDL_lock *)arg;
1318 /*
1319 It is OK to check MDL_lock::m_fast_path_state non-atomically here
1320 since the fact that MDL_lock object is unused will be properly
1321 validated later anyway.
1322 */
1323 return (lock->m_fast_path_state == 0);
1324 }
1325 } /* extern "C" */
1326
1327
1328 /**
1329 Try to find random MDL_lock object in MDL_map for which there are no "fast"
1330 path nor "slow" path locks. If found - mark it as destroyed, remove object
1331 from MDL_map and return it back to allocator.
1332
1333 @param[in] ctx Context on which behalf we are trying to remove
1334 unused object. Primarily needed to generate
1335 random value to be used for random dive into
1336 the hash in MDL_map.
1337 @param[in/out] pins Pins for the calling thread to be used for
1338 hash lookup and deletion.
1339 @param[out] unused_locks Number of unused lock objects after operation.
1340
1341 @note
1342 In reality MDL_lock object will be returned to allocator once it is no
1343 longer pinned by any threads.
1344 */
1345
remove_random_unused(MDL_context * ctx,LF_PINS * pins,int32 * unused_locks)1346 void MDL_map::remove_random_unused(MDL_context *ctx, LF_PINS *pins,
1347 int32 *unused_locks)
1348 {
1349 DEBUG_SYNC(ctx->get_thd(), "mdl_remove_random_unused_before_search");
1350
1351 /*
1352 Try to find an unused MDL_lock object by doing random dive into the hash.
1353 Since this method is called only when unused/total lock objects ratio is
1354 high enough, there is a good chance for this technique to succeed.
1355 */
1356 MDL_lock *lock= static_cast<MDL_lock *>(lf_hash_random_match(&m_locks,
1357 pins, &mdl_lock_match_unused,
1358 ctx->get_random()));
1359
1360 if (lock == NULL || lock == MY_ERRPTR)
1361 {
1362 /*
1363 We were unlucky and no unused objects were found. This can happen,
1364 for example, if our random dive into LF_HASH was close to the tail
1365 of split-ordered list used in its implementation or if some other
1366 thread managed to destroy or start re-using MDL_lock object
1367 concurrently.
1368 */
1369 lf_hash_search_unpin(pins);
1370 *unused_locks= m_unused_lock_objects;
1371 return;
1372 }
1373
1374 DEBUG_SYNC(ctx->get_thd(), "mdl_remove_random_unused_after_search");
1375
1376 /*
1377 Acquire MDL_lock::m_rwlock to ensure that IS_DESTROYED flag is set
1378 atomically AND under protection of MDL_lock::m_rwlock, so it can be
1379 safely read using both atomics and ordinary read under protection of
1380 m_rwlock. This also means that it is safe to unpin MDL_lock object
1381 after we have checked its IS_DESTROYED flag if we keep m_rwlock lock.
1382 */
1383 mysql_prlock_wrlock(&lock->m_rwlock);
1384
1385 if (lock->m_fast_path_state & MDL_lock::IS_DESTROYED)
1386 {
1387 /*
1388 Somebody has managed to mark MDL_lock object as destroyed before
1389 we have acquired MDL_lock::m_rwlock.
1390 */
1391 mysql_prlock_unlock(&lock->m_rwlock);
1392 lf_hash_search_unpin(pins);
1393 *unused_locks= m_unused_lock_objects;
1394 return;
1395 }
1396 lf_hash_search_unpin(pins);
1397
1398 /*
1399 Atomically check that number of "fast path" and "slow path" locks is 0 and
1400 set IS_DESTROYED flag.
1401
1402 This is the only place where we rely on the fact that our compare-and-swap
1403 operation can't spuriously fail i.e. is of strong kind.
1404 */
1405 MDL_lock::fast_path_state_t old_state= 0;
1406
1407 if (lock->fast_path_state_cas(&old_state, MDL_lock::IS_DESTROYED))
1408 {
1409 /*
1410 There were no "fast path" or "slow path" references and we
1411 have successfully set IS_DESTROYED flag.
1412 */
1413 mysql_prlock_unlock(&lock->m_rwlock);
1414
1415 DEBUG_SYNC(ctx->get_thd(), "mdl_remove_random_unused_after_is_destroyed_set");
1416
1417 /*
1418 Even though other threads can't rely on the MDL_lock object being around
1419 once IS_DESTROYED flag is set, we know that it was not removed from
1420 the hash yet (as it is responsibility of the current thread, i.e. one
1421 which executes this MDL_map::remove_random_unused() call) and thus was
1422 not deallocated.
1423 And since lf_hash_delete() finds and pins the object for the key as its
1424 first step and keeps pins until its end it is safe to use MDL_lock::key
1425 as parameter to lf_hash_delete().
1426 */
1427 int rc= lf_hash_delete(&m_locks, pins, lock->key.ptr(), lock->key.length());
1428
1429 /* The MDL_lock object must be present in the hash. */
1430 assert(rc != 1);
1431
1432 if (rc == -1)
1433 {
1434 /*
1435 In unlikely case of OOM MDL_lock object stays in the hash. The best
1436 thing we can do is to reset IS_DESTROYED flag. The object will be
1437 destroyed either by further calls to lf_hash_delete() or by final
1438 call to lf_hash_destroy().
1439 Resetting needs to happen atomically AND under protection of
1440 MDL_lock::m_rwlock so it safe to read this flag both using atomics
1441 and ordinary reads under protection of m_rwlock lock.
1442 */
1443 mysql_prlock_wrlock(&lock->m_rwlock);
1444 lock->fast_path_state_reset();
1445 mysql_prlock_unlock(&lock->m_rwlock);
1446 }
1447 else
1448 {
1449 /* Success. */
1450 *unused_locks= my_atomic_add32(&m_unused_lock_objects, -1) - 1;
1451 }
1452 }
1453 else
1454 {
1455 /*
1456 Some other thread has managed to find and use this MDL_lock object after
1457 it has been found by the above call to lf_hash_random_match().
1458 There are/were "fast" or "slow path" references so MDL_lock object can't
1459 be deleted.
1460
1461 Assert that compare-and-swap operation is of strong kind and can't
1462 fail spuriously.
1463 */
1464 assert(old_state != 0);
1465 mysql_prlock_unlock(&lock->m_rwlock);
1466 *unused_locks= m_unused_lock_objects;
1467 }
1468 }
1469
1470
1471 /**
1472 Initialize a metadata locking context.
1473
1474 This is to be called when a new server connection is created.
1475 */
1476
MDL_context()1477 MDL_context::MDL_context()
1478 :
1479 m_owner(NULL),
1480 m_needs_thr_lock_abort(FALSE),
1481 m_force_dml_deadlock_weight(false),
1482 m_waiting_for(NULL),
1483 m_pins(NULL),
1484 m_rand_state(UINT_MAX32)
1485 {
1486 mysql_prlock_init(key_MDL_context_LOCK_waiting_for, &m_LOCK_waiting_for);
1487 }
1488
1489
1490 /**
1491 Destroy metadata locking context.
1492
1493 Assumes and asserts that there are no active or pending locks
1494 associated with this context at the time of the destruction.
1495
1496 Currently does nothing. Asserts that there are no pending
1497 or satisfied lock requests. The pending locks must be released
1498 prior to destruction. This is a new way to express the assertion
1499 that all tables are closed before a connection is destroyed.
1500 */
1501
destroy()1502 void MDL_context::destroy()
1503 {
1504 assert(m_tickets[MDL_STATEMENT].is_empty());
1505 assert(m_tickets[MDL_TRANSACTION].is_empty());
1506 assert(m_tickets[MDL_EXPLICIT].is_empty());
1507
1508 mysql_prlock_destroy(&m_LOCK_waiting_for);
1509 if (m_pins)
1510 lf_hash_put_pins(m_pins);
1511 }
1512
1513
1514 /**
1515 Allocate pins which are necessary to work with MDL_map container
1516 if they are not allocated already.
1517 */
1518
fix_pins()1519 bool MDL_context::fix_pins()
1520 {
1521 if (! m_pins)
1522 m_pins= mdl_locks.get_pins();
1523 return (m_pins == NULL);
1524 }
1525
1526
1527 /**
1528 Initialize a lock request.
1529
1530 This is to be used for every lock request.
1531
1532 Note that initialization and allocation are split into two
1533 calls. This is to allow flexible memory management of lock
1534 requests. Normally a lock request is stored in statement memory
1535 (e.g. is a member of struct TABLE_LIST), but we would also like
1536 to allow allocation of lock requests in other memory roots,
1537 for example in the grant subsystem, to lock privilege tables.
1538
1539 The MDL subsystem does not own or manage memory of lock requests.
1540
1541 @param mdl_namespace Id of namespace of object to be locked
1542 @param db Name of database to which the object belongs
1543 @param name Name of of the object
1544 @param mdl_type The MDL lock type for the request.
1545 */
1546
init_with_source(MDL_key::enum_mdl_namespace mdl_namespace,const char * db_arg,const char * name_arg,enum_mdl_type mdl_type_arg,enum_mdl_duration mdl_duration_arg,const char * src_file,uint src_line)1547 void MDL_request::init_with_source(MDL_key::enum_mdl_namespace mdl_namespace,
1548 const char *db_arg,
1549 const char *name_arg,
1550 enum_mdl_type mdl_type_arg,
1551 enum_mdl_duration mdl_duration_arg,
1552 const char *src_file,
1553 uint src_line)
1554 {
1555 key.mdl_key_init(mdl_namespace, db_arg, name_arg);
1556 type= mdl_type_arg;
1557 duration= mdl_duration_arg;
1558 ticket= NULL;
1559 m_src_file= src_file;
1560 m_src_line= src_line;
1561 }
1562
1563
1564 /**
1565 Initialize a lock request using pre-built MDL_key.
1566
1567 @sa MDL_request::init(namespace, db, name, type).
1568
1569 @param key_arg The pre-built MDL key for the request.
1570 @param mdl_type_arg The MDL lock type for the request.
1571 */
1572
init_by_key_with_source(const MDL_key * key_arg,enum_mdl_type mdl_type_arg,enum_mdl_duration mdl_duration_arg,const char * src_file,uint src_line)1573 void MDL_request::init_by_key_with_source(const MDL_key *key_arg,
1574 enum_mdl_type mdl_type_arg,
1575 enum_mdl_duration mdl_duration_arg,
1576 const char *src_file,
1577 uint src_line)
1578 {
1579 key.mdl_key_init(key_arg);
1580 type= mdl_type_arg;
1581 duration= mdl_duration_arg;
1582 ticket= NULL;
1583 m_src_file= src_file;
1584 m_src_line= src_line;
1585 }
1586
1587
1588 /**
1589 Auxiliary functions needed for creation/destruction of MDL_lock objects.
1590 */
1591
create(const MDL_key * mdl_key)1592 inline MDL_lock *MDL_lock::create(const MDL_key *mdl_key)
1593 {
1594 MDL_lock *result= new (std::nothrow) MDL_lock();
1595 if (result)
1596 result->reinit(mdl_key);
1597 return result;
1598 }
1599
1600
destroy(MDL_lock * lock)1601 void MDL_lock::destroy(MDL_lock *lock)
1602 {
1603 delete lock;
1604 }
1605
1606
1607 /**
1608 Finalize initialization or re-initialize MDL_lock returned from
1609 LF_ALLOCATOR's cache to represent object identified by provided key.
1610
1611 @note All non-static MDL_lock members:
1612 1) either have to be reinitialized here
1613 (like IS_DESTROYED flag in MDL_lock::m_fast_path_state).
1614 2) or need to be initialized in constructor AND returned to their
1615 pristine state once they are removed from MDL_map container
1616 (like MDL_lock::m_granted or MDL_lock::m_rwlock).
1617 Otherwise it is possible that we will end up in situation
1618 when "new" (actually reused) MDL_lock object inserted in
1619 LF_HASH will inherit some values from old object.
1620 */
1621
reinit(const MDL_key * mdl_key)1622 inline void MDL_lock::reinit(const MDL_key *mdl_key)
1623 {
1624 key.mdl_key_init(mdl_key);
1625 switch (mdl_key->mdl_namespace())
1626 {
1627 case MDL_key::GLOBAL:
1628 case MDL_key::TABLESPACE:
1629 case MDL_key::SCHEMA:
1630 case MDL_key::COMMIT:
1631 m_strategy= &m_scoped_lock_strategy;
1632 break;
1633 default:
1634 m_strategy= &m_object_lock_strategy;
1635 break;
1636 }
1637 m_hog_lock_count= 0;
1638 m_piglet_lock_count= 0;
1639 m_current_waiting_incompatible_idx= 0;
1640 m_fast_path_state= 0;
1641 /*
1642 Check that we have clean "m_granted" and "m_waiting" sets/lists in both
1643 cases when we have fresh and re-used object.
1644 */
1645 assert(m_granted.is_empty() && m_waiting.is_empty());
1646 /* The same should be true for "m_obtrusive_locks_granted_waiting_count". */
1647 assert(m_obtrusive_locks_granted_waiting_count == 0);
1648 }
1649
1650
1651 /**
1652 @returns "Fast path" increment for request for "unobtrusive" type
1653 of lock, 0 - if it is request for "obtrusive" type of
1654 lock.
1655
1656 @sa Description at method declaration for more details.
1657 */
1658
1659 MDL_lock::fast_path_state_t
get_unobtrusive_lock_increment(const MDL_request * request)1660 MDL_lock::get_unobtrusive_lock_increment(const MDL_request *request)
1661 {
1662 switch (request->key.mdl_namespace())
1663 {
1664 case MDL_key::GLOBAL:
1665 case MDL_key::TABLESPACE:
1666 case MDL_key::SCHEMA:
1667 case MDL_key::COMMIT:
1668 return m_scoped_lock_strategy.m_unobtrusive_lock_increment[request->type];
1669 default:
1670 return m_object_lock_strategy.m_unobtrusive_lock_increment[request->type];
1671 }
1672 }
1673
1674
1675 /**
1676 Indicates whether object belongs to namespace which requires storage engine
1677 to be notified before acquiring and after releasing exclusive lock.
1678 */
1679
1680 bool
needs_hton_notification(MDL_key::enum_mdl_namespace mdl_namespace)1681 MDL_lock::needs_hton_notification(MDL_key::enum_mdl_namespace mdl_namespace)
1682 {
1683 switch (mdl_namespace)
1684 {
1685 case MDL_key::TABLESPACE:
1686 case MDL_key::SCHEMA:
1687 case MDL_key::TABLE:
1688 case MDL_key::FUNCTION:
1689 case MDL_key::PROCEDURE:
1690 case MDL_key::TRIGGER:
1691 case MDL_key::EVENT:
1692 return true;
1693 default:
1694 return false;
1695 }
1696 }
1697
1698 /**
1699 Auxiliary functions needed for creation/destruction of MDL_ticket
1700 objects.
1701
1702 @todo This naive implementation should be replaced with one that saves
1703 on memory allocation by reusing released objects.
1704 */
1705
create(MDL_context * ctx_arg,enum_mdl_type type_arg,enum_mdl_duration duration_arg)1706 MDL_ticket *MDL_ticket::create(MDL_context *ctx_arg, enum_mdl_type type_arg
1707 #ifndef NDEBUG
1708 , enum_mdl_duration duration_arg
1709 #endif
1710 )
1711 {
1712 return new (std::nothrow)
1713 MDL_ticket(ctx_arg, type_arg
1714 #ifndef NDEBUG
1715 , duration_arg
1716 #endif
1717 );
1718 }
1719
1720
destroy(MDL_ticket * ticket)1721 void MDL_ticket::destroy(MDL_ticket *ticket)
1722 {
1723 mysql_mdl_destroy(ticket->m_psi);
1724 ticket->m_psi= NULL;
1725
1726 delete ticket;
1727 }
1728
1729
1730 /**
1731 Return the 'weight' of this ticket for the victim selection algorithm.
1732 Requests with lower weight are preferred to requests with higher weight
1733 when choosing a victim.
1734
1735 @note When MDL_context::m_force_dml_deadlock_weight is set, the return value
1736 of this method is ignored and DEADLOCK_WEIGHT_DML is used for the
1737 context.
1738 */
1739
get_deadlock_weight() const1740 uint MDL_ticket::get_deadlock_weight() const
1741 {
1742 /*
1743 Waits for user-level locks have lower weight than waits for locks
1744 typically acquired by DDL, so we don't abort DDL in case of deadlock
1745 involving user-level locks and DDL. Deadlock errors are not normally
1746 expected from DDL by users.
1747 Waits for user-level locks have higher weight than waits for locks
1748 acquired by DML, so we prefer to abort DML in case of deadlock involving
1749 user-level locks and DML. User-level locks are explicitly requested by
1750 user, so they are probably important for them. Plus users expect
1751 deadlocks from DML transactions and for DML statements executed in
1752 @@autocommit=1 mode back-off and retry algorithm hides deadlock errors.
1753 */
1754 if (m_lock->key.mdl_namespace() == MDL_key::USER_LEVEL_LOCK)
1755 return DEADLOCK_WEIGHT_ULL;
1756
1757 /*
1758 Locks higher or equal to MDL_SHARED_UPGRADABLE:
1759 *) Are typically acquired for DDL and LOCK TABLES statements.
1760 *) Are often acquired in a way which doesn't allow simple release of
1761 locks and restart of lock acquisition process in case of deadlock
1762 (e.g. through lock_table_names() call).
1763
1764 To avoid such statements getting aborted with ER_LOCK_DEADLOCK error
1765 we use the higher DEADLOCK_WEIGHT_DDL weight for them.
1766
1767 Note that two DDL statements should not typically deadlock with each
1768 other since they normally acquire locks in the same order, thanks to
1769 to the fact that lock_table_names() uses MDL_context::acquire_locks()
1770 method which sorts lock requests before trying to acquire them.
1771
1772 In cases when "strong" locks can be acquired out-of-order (e.g. for
1773 LOCK TABLES) we try to use DEADLOCK_WEIGHT_DML instead.
1774
1775 TODO/FIXME: The below condition needs to be updated. The fact that a
1776 lock from GLOBAL namespace is requested no longer means
1777 that this is a DDL statement. There is a bug report about
1778 this.
1779 */
1780 if (m_lock->key.mdl_namespace() == MDL_key::GLOBAL ||
1781 m_type >= MDL_SHARED_UPGRADABLE)
1782 return DEADLOCK_WEIGHT_DDL;
1783
1784 return DEADLOCK_WEIGHT_DML;
1785 }
1786
1787
1788 /** Construct an empty wait slot. */
1789
MDL_wait()1790 MDL_wait::MDL_wait()
1791 :m_wait_status(EMPTY)
1792 {
1793 mysql_mutex_init(key_MDL_wait_LOCK_wait_status, &m_LOCK_wait_status, NULL);
1794 mysql_cond_init(key_MDL_wait_COND_wait_status, &m_COND_wait_status);
1795 }
1796
1797
1798 /** Destroy system resources. */
1799
~MDL_wait()1800 MDL_wait::~MDL_wait()
1801 {
1802 mysql_mutex_destroy(&m_LOCK_wait_status);
1803 mysql_cond_destroy(&m_COND_wait_status);
1804 }
1805
1806
1807 /**
1808 Set the status unless it's already set. Return FALSE if set,
1809 TRUE otherwise.
1810 */
1811
set_status(enum_wait_status status_arg)1812 bool MDL_wait::set_status(enum_wait_status status_arg)
1813 {
1814 bool was_occupied= TRUE;
1815 mysql_mutex_lock(&m_LOCK_wait_status);
1816 if (m_wait_status == EMPTY)
1817 {
1818 was_occupied= FALSE;
1819 m_wait_status= status_arg;
1820 mysql_cond_signal(&m_COND_wait_status);
1821 }
1822 mysql_mutex_unlock(&m_LOCK_wait_status);
1823 return was_occupied;
1824 }
1825
1826
1827 /** Query the current value of the wait slot. */
1828
get_status()1829 MDL_wait::enum_wait_status MDL_wait::get_status()
1830 {
1831 enum_wait_status result;
1832 mysql_mutex_lock(&m_LOCK_wait_status);
1833 result= m_wait_status;
1834 mysql_mutex_unlock(&m_LOCK_wait_status);
1835 return result;
1836 }
1837
1838
1839 /** Clear the current value of the wait slot. */
1840
reset_status()1841 void MDL_wait::reset_status()
1842 {
1843 mysql_mutex_lock(&m_LOCK_wait_status);
1844 m_wait_status= EMPTY;
1845 mysql_mutex_unlock(&m_LOCK_wait_status);
1846 }
1847
1848
1849 /**
1850 Wait for the status to be assigned to this wait slot.
1851
1852 @param owner MDL context owner.
1853 @param abs_timeout Absolute time after which waiting should stop.
1854 @param set_status_on_timeout TRUE - If in case of timeout waiting
1855 context should close the wait slot by
1856 sending TIMEOUT to itself.
1857 FALSE - Otherwise.
1858 @param wait_state_name Thread state name to be set for duration of wait.
1859
1860 @returns Signal posted.
1861 */
1862
1863 MDL_wait::enum_wait_status
timed_wait(MDL_context_owner * owner,struct timespec * abs_timeout,bool set_status_on_timeout,const PSI_stage_info * wait_state_name)1864 MDL_wait::timed_wait(MDL_context_owner *owner, struct timespec *abs_timeout,
1865 bool set_status_on_timeout,
1866 const PSI_stage_info *wait_state_name)
1867 {
1868 PSI_stage_info old_stage;
1869 enum_wait_status result;
1870 int wait_result= 0;
1871
1872 mysql_mutex_lock(&m_LOCK_wait_status);
1873
1874 owner->ENTER_COND(&m_COND_wait_status, &m_LOCK_wait_status,
1875 wait_state_name, & old_stage);
1876 thd_wait_begin(NULL, THD_WAIT_META_DATA_LOCK);
1877 while (!m_wait_status && !owner->is_killed() &&
1878 wait_result != ETIMEDOUT && wait_result != ETIME)
1879 {
1880 #ifdef WITH_WSREP
1881 // Allow tests to block the applier thread using the DBUG facilities
1882 DBUG_EXECUTE_IF("sync.wsrep_before_mdl_wait",
1883 {
1884 const char act[]=
1885 "now "
1886 "wait_for signal.wsrep_before_mdl_wait";
1887 assert(!debug_sync_set_action((owner->get_thd()),
1888 STRING_WITH_LEN(act)));
1889 };);
1890 if (wsrep_thd_is_BF(owner->get_thd(), false))
1891 {
1892 wait_result= mysql_cond_wait(&m_COND_wait_status, &m_LOCK_wait_status);
1893 }
1894 else
1895 #endif /* WITH_WSREP */
1896 wait_result= mysql_cond_timedwait(&m_COND_wait_status, &m_LOCK_wait_status,
1897 abs_timeout);
1898 }
1899 thd_wait_end(NULL);
1900
1901 if (m_wait_status == EMPTY)
1902 {
1903 /*
1904 Wait has ended not due to a status being set from another
1905 thread but due to this connection/statement being killed or a
1906 time out.
1907 To avoid races, which may occur if another thread sets
1908 GRANTED status before the code which calls this method
1909 processes the abort/timeout, we assign the status under
1910 protection of the m_LOCK_wait_status, within the critical
1911 section. An exception is when set_status_on_timeout is
1912 false, which means that the caller intends to restart the
1913 wait.
1914 */
1915 if (owner->is_killed())
1916 m_wait_status= KILLED;
1917 else if (set_status_on_timeout)
1918 m_wait_status= TIMEOUT;
1919 }
1920 result= m_wait_status;
1921
1922 mysql_mutex_unlock(&m_LOCK_wait_status);
1923 owner->EXIT_COND(& old_stage);
1924
1925 return result;
1926 }
1927
1928 /**
1929 Clear bit corresponding to the type of metadata lock in bitmap representing
1930 set of such types if list of tickets does not contain ticket with such type.
1931
1932 @param[in,out] bitmap Bitmap representing set of types of locks.
1933 @param[in] list List to inspect.
1934 @param[in] type Type of metadata lock to look up in the list.
1935 */
1936
clear_bit_if_not_in_list(enum_mdl_type type)1937 void MDL_lock::Ticket_list::clear_bit_if_not_in_list(enum_mdl_type type)
1938 {
1939 MDL_lock::Ticket_iterator it(m_list);
1940 const MDL_ticket *ticket;
1941
1942 while ((ticket= it++))
1943 if (ticket->get_type() == type)
1944 return;
1945 m_bitmap&= ~ MDL_BIT(type);
1946 }
1947
1948
1949 /**
1950 Add ticket to MDL_lock's list of waiting requests and
1951 update corresponding bitmap of lock types.
1952 */
1953
add_ticket(MDL_ticket * ticket)1954 void MDL_lock::Ticket_list::add_ticket(MDL_ticket *ticket)
1955 {
1956 /*
1957 Ticket being added to the list must have MDL_ticket::m_lock set,
1958 since for such tickets methods accessing this member might be
1959 called by other threads.
1960 */
1961 assert(ticket->get_lock());
1962 #ifdef WITH_WSREP
1963 if ((this == &(ticket->get_lock()->m_waiting)) &&
1964 wsrep_thd_is_BF((void *)(ticket->get_ctx()->wsrep_get_thd()), false))
1965 {
1966 Ticket_iterator itw(ticket->get_lock()->m_waiting);
1967 Ticket_iterator itg(ticket->get_lock()->m_granted);
1968
1969 MDL_ticket *waiting, *granted;
1970 MDL_ticket *prev=NULL;
1971 bool added= false;
1972
1973 while ((waiting= itw++) && !added)
1974 {
1975 if (!wsrep_thd_is_BF((void *)(waiting->get_ctx()->wsrep_get_thd()), true))
1976 {
1977 WSREP_DEBUG("MDL add_ticket inserted before: %u %s",
1978 wsrep_thd_thread_id(waiting->get_ctx()->wsrep_get_thd()),
1979 wsrep_thd_query(waiting->get_ctx()->wsrep_get_thd()));
1980 m_list.insert_after(prev, ticket);
1981 added= true;
1982 }
1983 prev= waiting;
1984 }
1985 if (!added) m_list.push_back(ticket);
1986
1987 while ((granted= itg++))
1988 {
1989 if (granted->get_ctx() != ticket->get_ctx() &&
1990 granted->is_incompatible_when_granted(ticket->get_type()))
1991 {
1992 if (!wsrep_grant_mdl_exception(ticket->get_ctx(), granted,
1993 &ticket->get_lock()->key))
1994 {
1995 WSREP_DEBUG("MDL victim killed at add_ticket");
1996 }
1997 }
1998 }
1999 }
2000 else
2001 {
2002 #endif /* WITH_WSREP */
2003 /*
2004 Add ticket to the *back* of the queue to ensure fairness
2005 among requests with the same priority.
2006 */
2007 m_list.push_back(ticket);
2008 #ifdef WITH_WSREP
2009 }
2010 #endif /* WITH_WSREP */
2011 m_bitmap|= MDL_BIT(ticket->get_type());
2012 }
2013
2014
2015 /**
2016 Remove ticket from MDL_lock's list of requests and
2017 update corresponding bitmap of lock types.
2018 */
2019
remove_ticket(MDL_ticket * ticket)2020 void MDL_lock::Ticket_list::remove_ticket(MDL_ticket *ticket)
2021 {
2022 m_list.remove(ticket);
2023 /*
2024 Check if waiting queue has another ticket with the same type as
2025 one which was removed. If there is no such ticket, i.e. we have
2026 removed last ticket of particular type, then we need to update
2027 bitmap of waiting ticket's types.
2028 Note that in most common case, i.e. when shared lock is removed
2029 from waiting queue, we are likely to find ticket of the same
2030 type early without performing full iteration through the list.
2031 So this method should not be too expensive.
2032 */
2033 clear_bit_if_not_in_list(ticket->get_type());
2034 }
2035
2036
2037 /**
2038 Determine waiting contexts which requests for the lock can be
2039 satisfied, grant lock to them and wake them up.
2040
2041 @note Together with MDL_lock::add_ticket() this method implements
2042 fair scheduling among requests with the same priority.
2043 It tries to grant lock from the head of waiters list, while
2044 add_ticket() adds new requests to the back of this list.
2045
2046 */
2047
reschedule_waiters()2048 void MDL_lock::reschedule_waiters()
2049 {
2050 MDL_lock::Ticket_iterator it(m_waiting);
2051 MDL_ticket *ticket;
2052
2053 /*
2054 Find the first (and hence the oldest) waiting request which
2055 can be satisfied (taking into account priority). Grant lock to it.
2056 Repeat the process for the remainder of waiters.
2057 Note we don't need to re-start iteration from the head of the
2058 list after satisfying the first suitable request as in our case
2059 all compatible types of requests have the same priority.
2060
2061 TODO/FIXME: We should:
2062 - Either switch to scheduling without priorities
2063 which will allow to stop iteration through the
2064 list of waiters once we found the first ticket
2065 which can't be satisfied
2066 - Or implement some check using bitmaps which will
2067 allow to stop iteration in cases when, e.g., we
2068 grant SNRW lock and there are no pending S or
2069 SH locks.
2070 */
2071 while ((ticket= it++))
2072 {
2073 if (can_grant_lock(ticket->get_type(), ticket->get_ctx()))
2074 {
2075 if (! ticket->get_ctx()->m_wait.set_status(MDL_wait::GRANTED))
2076 {
2077 /*
2078 Satisfy the found request by updating lock structures.
2079 It is OK to do so even after waking up the waiter since any
2080 session which tries to get any information about the state of
2081 this lock has to acquire MDL_lock::m_rwlock first and thus,
2082 when manages to do so, already sees an updated state of the
2083 MDL_lock object.
2084
2085 It doesn't matter if we are dealing with "obtrusive" lock here,
2086 we are moving lock request from waiting to granted lists,
2087 so m_obtrusive_locks_granted_waiting_count should stay the same.
2088 */
2089 m_waiting.remove_ticket(ticket);
2090 m_granted.add_ticket(ticket);
2091
2092 if (is_affected_by_max_write_lock_count())
2093 {
2094 /*
2095 Increase the counter of successively granted high priority "hog" or
2096 "piglet" locks, if we have granted one and there are pending
2097 lower priority locks.
2098 */
2099 if (count_piglets_and_hogs(ticket->get_type()))
2100 {
2101 /*
2102 Switch of priority matrice might have unblocked some lower-prio
2103 locks which are still compatible with the lock type we just have
2104 granted (for example, when we grant SNW lock and there are pending
2105 requests of SR type). Restart iteration to wake them up, otherwise
2106 we might get deadlocks.
2107 */
2108 it.rewind();
2109 continue;
2110 }
2111 }
2112 }
2113 /*
2114 If we could not update the wait slot of the waiter,
2115 it can be due to fact that its connection/statement was
2116 killed or it has timed out (i.e. the slot is not empty).
2117 Since in all such cases the waiter assumes that the lock was
2118 not been granted, we should keep the request in the waiting
2119 queue and look for another request to reschedule.
2120 */
2121 }
2122 }
2123
2124 if (is_affected_by_max_write_lock_count())
2125 {
2126 /*
2127 Reset number of successively granted higher-prio "hog"/"piglet" locks
2128 if there are no pending lower-prio conflicting locks.
2129 This ensures:
2130 - That m_hog_lock_count/m_piglet_lock_count is correctly reset after
2131 a strong lock is released and weak locks are granted (or there are
2132 no other lock requests).
2133 - That the situation when SNW lock is granted along with some SR/SRO
2134 locks, but SW locks are still blocked is handled correctly.
2135 - That m_hog_lock_count/m_piglet_lock_count is zero in all cases
2136 when there are no pending weak locks (e.g. when weak locks are
2137 removed due to deadlock, being killed or timeout).
2138
2139 Also switch priority matrice accordingly. Note that switch in
2140 this particular place doesn't require reschedule since:
2141
2142 1) We never switch to a matrice which prefers lower priority locks
2143 more than "hog"/"piglet" locks here (this might have happened if
2144 MDL_lock::switch_incompatible_waiting_types_bitmap_if_needed()
2145 was used instead and max_write_lock_count was decreased
2146 concurrently).
2147 2) When we switch from matrice #1 (which prefers SRO over SW) to
2148 default matrice #0 only the priority of SW vs SRO requests changes.
2149 Since the switch happens only when there are no pending SRO
2150 locks, no reschedule is required.
2151 3) When we switch from matrice #2 (which prefers all non-"nog" over
2152 "hog" requests) to default matrice #0 only the priority of "hog" vs
2153 non-"hog" requests changes. But since this happens when there are
2154 no non-"hog" requests, no reschedule is required.
2155 4) When we switch from matrice #3 (which prefers SRO over SW and
2156 non-"hog"-minus-SW over "hog" locks) to default matrice #0 only
2157 the priority of non-"hog"-minus-SW vs "hog" and SRO vs SW changes
2158 (see invariant [INV3]). Since the switch happens only when there
2159 are no pending non-"hog"-minus-SW/SRO requests, no reschedule is
2160 required.
2161
2162 Note that we might be switching priority matrice in a situation when we
2163 had pending SRO/non-"hog"/non-"hog"-minus-SW requests at the start of
2164 the call but they were granted during the loop. If some "piglet"/"hog"
2165 requests are compatible with those lower priority locks, they were
2166 granted as well. Those which were not compatible were not granted and
2167 should stay waiting until lower priority locks are released (in other
2168 words, the fact that a lock moved from pending to granted doesn't unblock
2169 additional requests, see invariant [INV2]).
2170 */
2171 if (m_current_waiting_incompatible_idx == 3)
2172 {
2173 /*
2174 We can't simply switch from matrice #3 to matrice #2 when there are no
2175 pending SRO locks, as that would allow a stream of concurrent SW and SRO
2176 requests to starve out "hog" locks when max_write_lock_count is set
2177 (there will be always pending SW or/and SRO locks in this case, so no
2178 switch back to matrice #0 will ever happen).
2179
2180 So we switch from matrice #3 to #0 directly and ignore pending SW/SWLP
2181 locks. This is OK as situation when matrice #3 is active can only
2182 occur when there were max_write_lock_count SW locks granted recently
2183 (before switch from #0 -> #1 which preceded switch #3 or before switch
2184 #2 -> #3).
2185 */
2186 if ((m_waiting.bitmap() & ~(MDL_OBJECT_HOG_LOCK_TYPES |
2187 MDL_BIT(MDL_SHARED_WRITE) |
2188 MDL_BIT(MDL_SHARED_WRITE_LOW_PRIO))) == 0)
2189 {
2190 m_piglet_lock_count= 0;
2191 m_hog_lock_count= 0;
2192 m_current_waiting_incompatible_idx= 0;
2193 }
2194 }
2195 else
2196 {
2197 if ((m_waiting.bitmap() & ~MDL_OBJECT_HOG_LOCK_TYPES) == 0)
2198 {
2199 m_hog_lock_count= 0;
2200 m_current_waiting_incompatible_idx&= ~2;
2201 }
2202 if ((m_waiting.bitmap() & MDL_BIT(MDL_SHARED_READ_ONLY)) == 0)
2203 {
2204 m_piglet_lock_count= 0;
2205 m_current_waiting_incompatible_idx&= ~1;
2206 }
2207 }
2208 }
2209 }
2210
2211
2212 /**
2213 Strategy instances to be used with scoped metadata locks (i.e. locks
2214 from GLOBAL, COMMIT, TABLESPACE and SCHEMA namespaces).
2215 The only locking modes which are supported at the moment are SHARED and
2216 INTENTION EXCLUSIVE and EXCLUSIVE.
2217 */
2218
2219 const MDL_lock::MDL_lock_strategy MDL_lock::m_scoped_lock_strategy =
2220 {
2221 /**
2222 Compatibility (or rather "incompatibility") matrices for scoped metadata
2223 lock. Arrays of bitmaps which elements specify which granted/waiting locks
2224 are incompatible with type of lock being requested.
2225
2226 The first array specifies if particular type of request can be satisfied
2227 if there is granted scoped lock of certain type.
2228
2229 | Type of active |
2230 Request | scoped lock |
2231 type | IS(*) IX S X |
2232 ---------+------------------+
2233 IS | + + + + |
2234 IX | + + - - |
2235 S | + - + - |
2236 X | + - - - |
2237
2238 Here: "+" -- means that request can be satisfied
2239 "-" -- means that request can't be satisfied and should wait
2240
2241 (*) Since intention shared scoped locks are compatible with all other
2242 type of locks we don't even have any accounting for them.
2243
2244 Note that relation between scoped locks and objects locks requested
2245 by statement is not straightforward and is therefore fully defined
2246 by SQL-layer.
2247 For example, in order to support global read lock implementation
2248 SQL-layer acquires IX lock in GLOBAL namespace for each statement
2249 that can modify metadata or data (i.e. for each statement that
2250 needs SW, SU, SNW, SNRW or X object locks). OTOH, to ensure that
2251 DROP DATABASE works correctly with concurrent DDL, IX metadata locks
2252 in SCHEMA namespace are acquired for DDL statements which can update
2253 metadata in the schema (i.e. which acquire SU, SNW, SNRW and X locks
2254 on schema objects) and aren't acquired for DML.
2255 */
2256 {
2257 MDL_BIT(MDL_EXCLUSIVE) | MDL_BIT(MDL_SHARED),
2258 MDL_BIT(MDL_EXCLUSIVE) | MDL_BIT(MDL_INTENTION_EXCLUSIVE),
2259 0, 0, 0, 0, 0, 0, 0, 0,
2260 MDL_BIT(MDL_EXCLUSIVE) | MDL_BIT(MDL_SHARED) |
2261 MDL_BIT(MDL_INTENTION_EXCLUSIVE)
2262 },
2263 /**
2264 Each array in the next group specifies if a particular type of request can
2265 be satisfied if there is already a waiting request for the scoped lock of
2266 a certain type. I.e. each array specifies a matrice with priorities for
2267 different lock types.
2268
2269 Scoped locks only use the first array which represents the "default"
2270 priority matrix. The remaing 3 matrices are not relevant for them.
2271
2272 | Pending |
2273 Request | scoped lock |
2274 type | IS(*) IX S X |
2275 ---------+-----------------+
2276 IS | + + + + |
2277 IX | + + - - |
2278 S | + + + - |
2279 X | + + + + |
2280
2281 Here the meaning of "+", "-" and (*) is the same as above.
2282 */
2283 {
2284 {
2285 MDL_BIT(MDL_EXCLUSIVE) | MDL_BIT(MDL_SHARED),
2286 MDL_BIT(MDL_EXCLUSIVE), 0, 0, 0, 0, 0, 0, 0, 0, 0
2287 },
2288 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
2289 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
2290 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }
2291 },
2292 /**
2293 Array of increments for "unobtrusive" types of lock requests for scoped
2294 locks.
2295
2296 @sa MDL_lock::get_unobtrusive_lock_increment().
2297
2298 For scoped locks:
2299 - "unobtrusive" types: IX
2300 - "obtrusive" types: X and S
2301
2302 We encode number of IX locks acquired using "fast path" in bits 0 .. 59
2303 of MDL_lock::m_fast_path_state.
2304 */
2305 { 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
2306 /*
2307 In scoped locks, only IX lock request would starve because of X/S.
2308 But that is practically a very rare case. So we don't apply the
2309 max_write_lock_count limit to them.
2310 */
2311 false,
2312 /*
2313 Scoped locks doesn't require notification of owners of conflicting
2314 locks for any type of requests. Hence 'm_needs_notification' is NULL.
2315 */
2316 NULL,
2317 /*
2318 For the same reason, 'm_notify_conflicting_locks' is NULL for scoped locks.
2319 */
2320 NULL,
2321 &MDL_lock::scoped_lock_fast_path_granted_bitmap,
2322 /* Scoped locks never require connection check. */
2323 NULL
2324 };
2325
2326
2327 /**
2328 Strategy instance for per-object locks.
2329 Supports all locked modes except INTENTION EXCLUSIVE locks.
2330 */
2331
2332 const MDL_lock::MDL_lock_strategy MDL_lock::m_object_lock_strategy =
2333 {
2334 /**
2335 Compatibility (or rather "incompatibility") matrices for per-object
2336 metadata lock. Arrays of bitmaps which elements specify which granted/
2337 waiting locks are incompatible with type of lock being requested.
2338
2339 The first array specifies if particular type of request can be satisfied
2340 if there is granted lock of certain type.
2341
2342 Request | Granted requests for lock |
2343 type | S SH SR SW SWLP SU SRO SNW SNRW X |
2344 ----------+---------------------------------------------+
2345 S | + + + + + + + + + - |
2346 SH | + + + + + + + + + - |
2347 SR | + + + + + + + + - - |
2348 SW | + + + + + + - - - - |
2349 SWLP | + + + + + + - - - - |
2350 SU | + + + + + - + - - - |
2351 SRO | + + + - - + + + - - |
2352 SNW | + + + - - - + - - - |
2353 SNRW | + + - - - - - - - - |
2354 X | - - - - - - - - - - |
2355
2356 Here: "+" -- means that request can be satisfied
2357 "-" -- means that request can't be satisfied and should wait
2358
2359 @note In cases then current context already has "stronger" type
2360 of lock on the object it will be automatically granted
2361 thanks to usage of the MDL_context::find_ticket() method.
2362
2363 @note IX locks are excluded since they are not used for per-object
2364 metadata locks.
2365 */
2366 {
2367 0,
2368 MDL_BIT(MDL_EXCLUSIVE),
2369 MDL_BIT(MDL_EXCLUSIVE),
2370 MDL_BIT(MDL_EXCLUSIVE) | MDL_BIT(MDL_SHARED_NO_READ_WRITE),
2371 MDL_BIT(MDL_EXCLUSIVE) | MDL_BIT(MDL_SHARED_NO_READ_WRITE) |
2372 MDL_BIT(MDL_SHARED_NO_WRITE) | MDL_BIT(MDL_SHARED_READ_ONLY),
2373 MDL_BIT(MDL_EXCLUSIVE) | MDL_BIT(MDL_SHARED_NO_READ_WRITE) |
2374 MDL_BIT(MDL_SHARED_NO_WRITE) | MDL_BIT(MDL_SHARED_READ_ONLY),
2375 MDL_BIT(MDL_EXCLUSIVE) | MDL_BIT(MDL_SHARED_NO_READ_WRITE) |
2376 MDL_BIT(MDL_SHARED_NO_WRITE) | MDL_BIT(MDL_SHARED_UPGRADABLE),
2377 MDL_BIT(MDL_EXCLUSIVE) | MDL_BIT(MDL_SHARED_NO_READ_WRITE) |
2378 MDL_BIT(MDL_SHARED_WRITE_LOW_PRIO) | MDL_BIT(MDL_SHARED_WRITE),
2379 MDL_BIT(MDL_EXCLUSIVE) | MDL_BIT(MDL_SHARED_NO_READ_WRITE) |
2380 MDL_BIT(MDL_SHARED_NO_WRITE) | MDL_BIT(MDL_SHARED_UPGRADABLE) |
2381 MDL_BIT(MDL_SHARED_WRITE_LOW_PRIO) | MDL_BIT(MDL_SHARED_WRITE),
2382 MDL_BIT(MDL_EXCLUSIVE) | MDL_BIT(MDL_SHARED_NO_READ_WRITE) |
2383 MDL_BIT(MDL_SHARED_NO_WRITE) | MDL_BIT(MDL_SHARED_READ_ONLY) |
2384 MDL_BIT(MDL_SHARED_UPGRADABLE) | MDL_BIT(MDL_SHARED_WRITE_LOW_PRIO) |
2385 MDL_BIT(MDL_SHARED_WRITE) | MDL_BIT(MDL_SHARED_READ),
2386 MDL_BIT(MDL_EXCLUSIVE) | MDL_BIT(MDL_SHARED_NO_READ_WRITE) |
2387 MDL_BIT(MDL_SHARED_NO_WRITE) | MDL_BIT(MDL_SHARED_READ_ONLY) |
2388 MDL_BIT(MDL_SHARED_UPGRADABLE) | MDL_BIT(MDL_SHARED_WRITE_LOW_PRIO) |
2389 MDL_BIT(MDL_SHARED_WRITE) | MDL_BIT(MDL_SHARED_READ) |
2390 MDL_BIT(MDL_SHARED_HIGH_PRIO) | MDL_BIT(MDL_SHARED)
2391 },
2392 /**
2393 Each array in the next group specifies if a particular type of request can
2394 be satisfied if there is a waiting request for the same lock of a certain
2395 type. In other words each array specifies a priority matrix for different
2396 lock types.
2397
2398 We use each of the arrays depending on whether the number of successively
2399 granted "piglet" and "hog" lock requests exceed the max_write_lock_count
2400 threshold. This is necessary to avoid high priority lock requests starving
2401 out requests with lower priority.
2402
2403 The first array in the group is used in default situation when both
2404 MDL_lock::m_piglet_lock_count and MDL_lock::m_hog_lock_count don't exceed
2405 the threshold.
2406
2407 A priority matrice specified by it looks like:
2408
2409 Request | Pending requests for lock |
2410 type | S SH SR SW SWLP SU SRO SNW SNRW X |
2411 ----------+--------------------------------------------+
2412 S | + + + + + + + + + - |
2413 SH | + + + + + + + + + + |
2414 SR | + + + + + + + + - - |
2415 SW | + + + + + + + - - - |
2416 SWLP | + + + + + + - - - - |
2417 SU | + + + + + + + + + - |
2418 SRO | + + + - + + + + - - |
2419 SNW | + + + + + + + + + - |
2420 SNRW | + + + + + + + + + - |
2421 X | + + + + + + + + + + |
2422
2423 Invariant [INV2]: for all priority matrices, if A is the set of
2424 incompatible waiting requests for a given request and B is the set
2425 of incompatible granted requests for the same request, then A will
2426 always be a subset of B. This means that moving a lock from waiting
2427 to granted state doesn't unblock additional requests.
2428 MDL_lock::reschedule_waiters() code relies on this.
2429 */
2430 {
2431 {
2432 0,
2433 MDL_BIT(MDL_EXCLUSIVE),
2434 0,
2435 MDL_BIT(MDL_EXCLUSIVE) | MDL_BIT(MDL_SHARED_NO_READ_WRITE),
2436 MDL_BIT(MDL_EXCLUSIVE) | MDL_BIT(MDL_SHARED_NO_READ_WRITE) |
2437 MDL_BIT(MDL_SHARED_NO_WRITE),
2438 MDL_BIT(MDL_EXCLUSIVE) | MDL_BIT(MDL_SHARED_NO_READ_WRITE) |
2439 MDL_BIT(MDL_SHARED_NO_WRITE) | MDL_BIT(MDL_SHARED_READ_ONLY),
2440 MDL_BIT(MDL_EXCLUSIVE),
2441 MDL_BIT(MDL_EXCLUSIVE) | MDL_BIT(MDL_SHARED_NO_READ_WRITE) |
2442 MDL_BIT(MDL_SHARED_WRITE),
2443 MDL_BIT(MDL_EXCLUSIVE),
2444 MDL_BIT(MDL_EXCLUSIVE),
2445 0
2446 },
2447 /**
2448 The second array in the group is used when the number of successively
2449 granted "piglet" (SW) locks exceeds max_write_lock_count.
2450
2451 It is the same matrix as in the first case but with the SW lock type
2452 having lower priority than the SRO lock type.
2453 */
2454 {
2455 0,
2456 MDL_BIT(MDL_EXCLUSIVE),
2457 0,
2458 MDL_BIT(MDL_EXCLUSIVE) | MDL_BIT(MDL_SHARED_NO_READ_WRITE),
2459 MDL_BIT(MDL_EXCLUSIVE) | MDL_BIT(MDL_SHARED_NO_READ_WRITE) |
2460 MDL_BIT(MDL_SHARED_NO_WRITE) | MDL_BIT(MDL_SHARED_READ_ONLY),
2461 MDL_BIT(MDL_EXCLUSIVE) | MDL_BIT(MDL_SHARED_NO_READ_WRITE) |
2462 MDL_BIT(MDL_SHARED_NO_WRITE) | MDL_BIT(MDL_SHARED_READ_ONLY),
2463 MDL_BIT(MDL_EXCLUSIVE),
2464 MDL_BIT(MDL_EXCLUSIVE) | MDL_BIT(MDL_SHARED_NO_READ_WRITE),
2465 MDL_BIT(MDL_EXCLUSIVE),
2466 MDL_BIT(MDL_EXCLUSIVE),
2467 0
2468 },
2469 /**
2470 The third array in the group is used when the number of successively
2471 granted "hog" (SNW, SNRW, X) locks exceeds max_write_lock_count.
2472
2473 In this case S, SH, SR, SW, SNRW, SRO and SU locks types have
2474 priority over all "hog" types.
2475 */
2476 {
2477 0,
2478 0,
2479 0,
2480 0,
2481 0,
2482 MDL_BIT(MDL_SHARED_READ_ONLY),
2483 0,
2484 MDL_BIT(MDL_SHARED_WRITE),
2485 MDL_BIT(MDL_EXCLUSIVE) | MDL_BIT(MDL_SHARED_UPGRADABLE) |
2486 MDL_BIT(MDL_SHARED_WRITE_LOW_PRIO) | MDL_BIT(MDL_SHARED_WRITE),
2487 MDL_BIT(MDL_EXCLUSIVE) | MDL_BIT(MDL_SHARED_READ_ONLY) |
2488 MDL_BIT(MDL_SHARED_UPGRADABLE) | MDL_BIT(MDL_SHARED_WRITE_LOW_PRIO) |
2489 MDL_BIT(MDL_SHARED_WRITE) | MDL_BIT(MDL_SHARED_READ),
2490 static_cast<bitmap_t>(~MDL_OBJECT_HOG_LOCK_TYPES)
2491 },
2492 /**
2493 The fourth array in the group is used when both the number of successively
2494 granted "piglet" (SW) and the number of successively granted "hog"
2495 (SNW, SNRW, X) locks exceed max_write_lock_count.
2496
2497 This matrice prefers SRO locks over SW/SWLP locks. And non-"hog" locks
2498 other than SW/SWLP over "hog" locks.
2499
2500 Note that the fact that "hog" locks have the same priority vs SW/SWLP
2501 locks as in the default matrice (#0) is important and is relied upon in
2502 MDL_lock::reschedule_waiters(). This is invariant [INV3].
2503 */
2504 {
2505 0,
2506 0,
2507 0,
2508 0,
2509 MDL_BIT(MDL_EXCLUSIVE) | MDL_BIT(MDL_SHARED_NO_READ_WRITE) |
2510 MDL_BIT(MDL_SHARED_NO_WRITE) | MDL_BIT(MDL_SHARED_READ_ONLY),
2511 MDL_BIT(MDL_EXCLUSIVE) | MDL_BIT(MDL_SHARED_NO_READ_WRITE) |
2512 MDL_BIT(MDL_SHARED_NO_WRITE) | MDL_BIT(MDL_SHARED_READ_ONLY),
2513 0,
2514 0,
2515 MDL_BIT(MDL_EXCLUSIVE) | MDL_BIT(MDL_SHARED_UPGRADABLE),
2516 MDL_BIT(MDL_EXCLUSIVE) | MDL_BIT(MDL_SHARED_READ_ONLY) |
2517 MDL_BIT(MDL_SHARED_UPGRADABLE) | MDL_BIT(MDL_SHARED_READ),
2518 static_cast<bitmap_t>(~(MDL_OBJECT_HOG_LOCK_TYPES |
2519 MDL_BIT(MDL_SHARED_WRITE) |
2520 MDL_BIT(MDL_SHARED_WRITE_LOW_PRIO)))
2521 }
2522 },
2523 /**
2524 Array of increments for "unobtrusive" types of lock requests for per-object
2525 locks.
2526
2527 @sa MDL_lock::get_unobtrusive_lock_increment().
2528
2529 For per-object locks:
2530 - "unobtrusive" types: S, SH, SR and SW
2531 - "obtrusive" types: SU, SRO, SNW, SNRW, X
2532
2533 Number of locks acquired using "fast path" are encoded in the following
2534 bits of MDL_lock::m_fast_path_state:
2535
2536 - bits 0 .. 19 - S and SH (we don't differentiate them once acquired)
2537 - bits 20 .. 39 - SR
2538 - bits 40 .. 59 - SW and SWLP (we don't differentiate them once acquired)
2539
2540 Overflow is not an issue as we are unlikely to support more than 2^20 - 1
2541 concurrent connections in foreseeable future.
2542
2543 This encoding defines the below contents of increment array.
2544 */
2545 { 0, 1, 1, 1ULL << 20, 1ULL << 40, 1ULL << 40, 0, 0, 0, 0, 0 },
2546 /*
2547 To prevent starvation, "hog" and "piglet" lock types are only granted
2548 max_write_lock_count times in a row while conflicting lock types are
2549 waiting.
2550 */
2551 true,
2552 &MDL_lock::object_lock_needs_notification,
2553 &MDL_lock::object_lock_notify_conflicting_locks,
2554 &MDL_lock::object_lock_fast_path_granted_bitmap,
2555 &MDL_lock::object_lock_needs_connection_check
2556 };
2557
2558 /**
2559 Check if request for the metadata lock can be satisfied given its
2560 current state.
2561
2562 @param type_arg The requested lock type.
2563 @param requestor_ctx The MDL context of the requestor.
2564
2565 @retval TRUE Lock request can be satisfied
2566 @retval FALSE There is some conflicting lock.
2567
2568 @note In cases then current context already has "stronger" type
2569 of lock on the object it will be automatically granted
2570 thanks to usage of the MDL_context::find_ticket() method.
2571 */
2572
2573 bool
can_grant_lock(enum_mdl_type type_arg,const MDL_context * requestor_ctx) const2574 MDL_lock::can_grant_lock(enum_mdl_type type_arg,
2575 const MDL_context *requestor_ctx) const
2576 {
2577 bool can_grant= FALSE;
2578 bitmap_t waiting_incompat_map= incompatible_waiting_types_bitmap()[type_arg];
2579 bitmap_t granted_incompat_map= incompatible_granted_types_bitmap()[type_arg];
2580 #ifdef WITH_WSREP
2581 bool wsrep_can_grant= TRUE;
2582 #endif /* WITH_WSREP */
2583
2584 /*
2585 New lock request can be satisfied iff:
2586 - There are no incompatible types of satisfied requests
2587 in other contexts
2588 - There are no waiting requests which have higher priority
2589 than this request.
2590 */
2591 if (!(m_waiting.bitmap() & waiting_incompat_map))
2592 {
2593 if (! (fast_path_granted_bitmap() & granted_incompat_map))
2594 {
2595 if (! (m_granted.bitmap() & granted_incompat_map))
2596 can_grant= TRUE;
2597 else
2598 {
2599 Ticket_iterator it(m_granted);
2600 MDL_ticket *ticket;
2601
2602 /*
2603 There is an incompatible lock. Check that it belongs to some
2604 other context.
2605
2606 If we are trying to acquire "unobtrusive" type of lock then the
2607 confliciting lock must be from "obtrusive" set, therefore it should
2608 have been acquired using "slow path" and should be present in
2609 m_granted list.
2610
2611 If we are trying to acquire "obtrusive" type of lock then it can be
2612 either another "obtrusive" lock or "unobtrusive" type of lock
2613 acquired on "slow path" (can't be "unobtrusive" lock on fast path
2614 because of surrounding if-statement). In either case it should be
2615 present in m_granted list.
2616 */
2617 while ((ticket= it++))
2618 {
2619 if (ticket->get_ctx() != requestor_ctx &&
2620 ticket->is_incompatible_when_granted(type_arg))
2621 #ifdef WITH_WSREP
2622 {
2623 if (wsrep_thd_is_BF((void *)(requestor_ctx->wsrep_get_thd()),false) &&
2624 key.mdl_namespace() == MDL_key::GLOBAL)
2625 {
2626 WSREP_DEBUG("global lock granted for BF: %u %s",
2627 wsrep_thd_thread_id(requestor_ctx->wsrep_get_thd()),
2628 wsrep_thd_query(requestor_ctx->wsrep_get_thd()));
2629 can_grant = true;
2630 }
2631 else if (!wsrep_grant_mdl_exception(requestor_ctx, ticket, &key))
2632 {
2633 wsrep_can_grant= FALSE;
2634 if (wsrep_log_conflicts)
2635 {
2636 MDL_lock * lock = ticket->get_lock();
2637 WSREP_INFO(
2638 "MDL conflict db=%s table=%s ticket=%d solved by %s",
2639 lock->key.db_name(), lock->key.name(), ticket->get_type(), "abort"
2640 );
2641 }
2642 }
2643 else
2644 {
2645 can_grant= TRUE;
2646 }
2647 }
2648 #else
2649 break;
2650 #endif /* WITH_WSREP */
2651 }
2652 #ifdef WITH_WSREP
2653 if ((ticket == NULL) && wsrep_can_grant)
2654 #else
2655 if (ticket == NULL) /* Incompatible locks are our own. */
2656 #endif /* WITH_WSREP */
2657 can_grant= TRUE;
2658 }
2659 }
2660 else
2661 {
2662 /*
2663 Our lock request conflicts with one of granted "fast path" locks:
2664
2665 This means that we are trying to acquire "obtrusive" lock and:
2666 a) Either we are called from MDL_context::try_acquire_lock_impl()
2667 and then all "fast path" locks belonging to this context were
2668 materialized (as we do for "obtrusive" locks).
2669 b) Or we are called from MDL_lock::reschedule_waiters() then
2670 this context is waiting for this request and all its "fast
2671 path" locks were materialized before the wait.
2672
2673 The above means that conflicting granted "fast path" lock cannot
2674 belong to us and our request cannot be satisfied.
2675 */
2676 #ifdef WITH_WSREP
2677 if (wsrep_thd_is_BF((void *)(requestor_ctx->wsrep_get_thd()),false) &&
2678 key.mdl_namespace() == MDL_key::GLOBAL)
2679 {
2680 WSREP_DEBUG("global lock granted over unobstrusive locks, for BF: %u %s",
2681 wsrep_thd_thread_id(requestor_ctx->wsrep_get_thd()),
2682 wsrep_thd_query(requestor_ctx->wsrep_get_thd()));
2683 can_grant = true;
2684 }
2685 #ifdef WITH_WSREP_TODO
2686 /* victim unobstrusive lock holder is hard to find.
2687 Skipping here to allow high priority thread to continue,
2688 it will have earlier seqno and victims will die at certification stage
2689 */
2690 else if (!wsrep_grant_mdl_exception(requestor_ctx, ticket))
2691 {
2692 wsrep_can_grant= FALSE;
2693 if (wsrep_log_conflicts)
2694 {
2695 MDL_lock * lock = ticket->get_lock();
2696 WSREP_INFO(
2697 "MDL conflict db=%s table=%s ticket=%d solved by %s",
2698 lock->key.db_name(), lock->key.name(), ticket->get_type(), "abort"
2699 );
2700 }
2701 }
2702 #endif /* WITH_WSREP_TODO */
2703 else if (WSREP(requestor_ctx->wsrep_get_thd()))
2704 {
2705 WSREP_DEBUG("granting MDL for BF thread over unobstrusive locks");
2706 can_grant= TRUE;
2707 }
2708 #endif /* WITH_WSREP */
2709 }
2710 }
2711 #ifdef WITH_WSREP
2712 else
2713 {
2714 if (wsrep_thd_is_BF((void *)(requestor_ctx->wsrep_get_thd()), false) &&
2715 key.mdl_namespace() == MDL_key::GLOBAL)
2716 {
2717 WSREP_DEBUG("global lock granted for BF (waiting queue): %u %s",
2718 wsrep_thd_thread_id(requestor_ctx->wsrep_get_thd()),
2719 wsrep_thd_query(requestor_ctx->wsrep_get_thd()));
2720 can_grant = true;
2721 }
2722 }
2723 #endif /* WITH_WSREP */
2724 return can_grant;
2725 }
2726
2727
2728 /**
2729 Return the first MDL_context which owns the lock.
2730
2731 @return Pointer to the first MDL_context which has acquired the lock
2732 NULL if there are no such contexts.
2733
2734 @note This method works properly only for locks acquired using
2735 "slow" path. It won't return context if it has used "fast"
2736 path to acquire the lock.
2737 */
2738
2739 inline MDL_context *
get_lock_owner() const2740 MDL_lock::get_lock_owner() const
2741 {
2742 Ticket_iterator it(m_granted);
2743 MDL_ticket *ticket;
2744
2745 if ((ticket= it++))
2746 return ticket->get_ctx();
2747 return NULL;
2748 }
2749
2750
2751 /** Remove a ticket from waiting or pending queue and wakeup up waiters. */
2752
remove_ticket(MDL_context * ctx,LF_PINS * pins,Ticket_list MDL_lock::* list,MDL_ticket * ticket)2753 void MDL_lock::remove_ticket(MDL_context *ctx, LF_PINS *pins,
2754 Ticket_list MDL_lock::*list,
2755 MDL_ticket *ticket)
2756 {
2757 bool is_obtrusive= is_obtrusive_lock(ticket->get_type());
2758 bool is_singleton= mdl_locks.is_lock_object_singleton(&key);
2759
2760 mysql_prlock_wrlock(&m_rwlock);
2761 (this->*list).remove_ticket(ticket);
2762
2763 /*
2764 If we are removing "obtrusive" type of request either from granted or
2765 waiting lists we need to decrement "obtrusive" requests counter.
2766 Once last ticket for "obtrusive" lock is removed we should clear
2767 HAS_OBTRUSIVE flag in m_fast_path_state as well.
2768 */
2769 bool last_obtrusive= is_obtrusive &&
2770 ((--m_obtrusive_locks_granted_waiting_count) == 0);
2771 /*
2772 If both m_granted and m_waiting lists become empty as result we also
2773 need to clear HAS_SLOW_PATH flag in m_fast_path_state.
2774 */
2775 bool last_slow_path= m_granted.is_empty() && m_waiting.is_empty();
2776 bool last_use= false;
2777
2778 if (last_slow_path || last_obtrusive)
2779 {
2780 fast_path_state_t old_state= m_fast_path_state;
2781 fast_path_state_t new_state;
2782 do
2783 {
2784 new_state= old_state;
2785 if (last_slow_path)
2786 new_state&= ~MDL_lock::HAS_SLOW_PATH;
2787 if (last_obtrusive)
2788 new_state&= ~MDL_lock::HAS_OBTRUSIVE;
2789 }
2790 while (! fast_path_state_cas(&old_state, new_state));
2791
2792 /*
2793 We don't have any "fast" or "slow" path locks. MDL_lock object becomes
2794 unused so unused objects counter needs to be incremented.
2795 */
2796 if (new_state == 0)
2797 last_use= true;
2798 }
2799
2800
2801 if (last_slow_path)
2802 {
2803 /*
2804 We might end-up with the last waiting ticket being removed and non-0
2805 m_hog_lock_count/m_piglet_lock_count in the following situation:
2806
2807 1) There is a granted "hog"/"piglet" lock blocking a lower priority lock
2808 request.
2809 2) The lower priority lock request is timed out or killed. It is not yet
2810 removed from waiters list and bitmap.
2811 3) The "Hog"/"piglet" lock is released. Its reschedule_waiters() call
2812 will still see the pending lower priority lock so it won't reset
2813 the m_hog_lock_count/m_piglet_lock_count counters.
2814 4) MDL_lock::remove_ticket() is called for the timed out/killed
2815 lower priority ticket. Which turns out to be the last ticket
2816 for this lock.
2817
2818 Hence we need to reset these counters here.
2819 */
2820 m_hog_lock_count= 0;
2821 m_piglet_lock_count= 0;
2822 m_current_waiting_incompatible_idx= 0;
2823 }
2824 else
2825 {
2826 /*
2827 There can be some contexts waiting to acquire a lock
2828 which now might be able to do it. Grant the lock to
2829 them and wake them up!
2830
2831 We always try to reschedule locks, since there is no easy way
2832 (i.e. by looking at the bitmaps) to find out whether it is
2833 required or not.
2834 In a general case, even when the queue's bitmap is not changed
2835 after removal of the ticket, there is a chance that some request
2836 can be satisfied (due to the fact that a granted request
2837 reflected in the bitmap might belong to the same context as a
2838 pending request).
2839 */
2840 reschedule_waiters();
2841 }
2842 mysql_prlock_unlock(&m_rwlock);
2843
2844 /* Don't count singleton MDL_lock objects as unused. */
2845 if (last_use && ! is_singleton)
2846 mdl_locks.lock_object_unused(ctx, pins);
2847 }
2848
2849
2850 /**
2851 Check if we have any pending locks which conflict with existing
2852 shared lock.
2853
2854 @pre The ticket must match an acquired lock.
2855
2856 @return TRUE if there is a conflicting lock request, FALSE otherwise.
2857 */
2858
has_pending_conflicting_lock(enum_mdl_type type)2859 bool MDL_lock::has_pending_conflicting_lock(enum_mdl_type type)
2860 {
2861 bool result;
2862
2863 mysql_mutex_assert_not_owner(&LOCK_open);
2864
2865 mysql_prlock_rdlock(&m_rwlock);
2866 result= (m_waiting.bitmap() & incompatible_granted_types_bitmap()[type]);
2867 mysql_prlock_unlock(&m_rwlock);
2868 return result;
2869 }
2870
2871
~MDL_wait_for_graph_visitor()2872 MDL_wait_for_graph_visitor::~MDL_wait_for_graph_visitor()
2873 {
2874 }
2875
2876
~MDL_wait_for_subgraph()2877 MDL_wait_for_subgraph::~MDL_wait_for_subgraph()
2878 {
2879 }
2880
2881 /**
2882 Check if ticket represents metadata lock of "stronger" or equal type
2883 than specified one. I.e. if metadata lock represented by ticket won't
2884 allow any of locks which are not allowed by specified type of lock.
2885
2886 @return TRUE if ticket has stronger or equal type
2887 FALSE otherwise.
2888 */
2889
has_stronger_or_equal_type(enum_mdl_type type) const2890 bool MDL_ticket::has_stronger_or_equal_type(enum_mdl_type type) const
2891 {
2892 const MDL_lock::bitmap_t *
2893 granted_incompat_map= m_lock->incompatible_granted_types_bitmap();
2894
2895 return ! (granted_incompat_map[type] & ~(granted_incompat_map[m_type]));
2896 }
2897
2898
is_incompatible_when_granted(enum_mdl_type type) const2899 bool MDL_ticket::is_incompatible_when_granted(enum_mdl_type type) const
2900 {
2901 return (MDL_BIT(m_type) &
2902 m_lock->incompatible_granted_types_bitmap()[type]);
2903 }
2904
2905
2906 bool
is_incompatible_when_waiting(enum_mdl_type type) const2907 MDL_ticket::is_incompatible_when_waiting(enum_mdl_type type) const
2908 {
2909 return (MDL_BIT(m_type) &
2910 m_lock->incompatible_waiting_types_bitmap()[type]);
2911 }
2912
2913
2914 /**
2915 Check whether the context already holds a compatible lock ticket
2916 on an object.
2917 Start searching from list of locks for the same duration as lock
2918 being requested. If not look at lists for other durations.
2919
2920 @param mdl_request Lock request object for lock to be acquired
2921 @param[out] result_duration Duration of lock which was found.
2922
2923 @note Tickets which correspond to lock types "stronger" than one
2924 being requested are also considered compatible.
2925
2926 @return A pointer to the lock ticket for the object or NULL otherwise.
2927 */
2928
2929 MDL_ticket *
find_ticket(MDL_request * mdl_request,enum_mdl_duration * result_duration)2930 MDL_context::find_ticket(MDL_request *mdl_request,
2931 enum_mdl_duration *result_duration)
2932 {
2933 MDL_ticket *ticket;
2934 int i;
2935
2936 for (i= 0; i < MDL_DURATION_END; i++)
2937 {
2938 enum_mdl_duration duration= (enum_mdl_duration)((mdl_request->duration+i) %
2939 MDL_DURATION_END);
2940 Ticket_iterator it(m_tickets[duration]);
2941
2942 while ((ticket= it++))
2943 {
2944 if (mdl_request->key.is_equal(&ticket->m_lock->key) &&
2945 ticket->has_stronger_or_equal_type(mdl_request->type))
2946 {
2947 *result_duration= duration;
2948 return ticket;
2949 }
2950 }
2951 }
2952 return NULL;
2953 }
2954
2955
2956 /**
2957 Try to acquire one lock.
2958
2959 Unlike exclusive locks, shared locks are acquired one by
2960 one. This is interface is chosen to simplify introduction of
2961 the new locking API to the system. MDL_context::try_acquire_lock()
2962 is currently used from open_table(), and there we have only one
2963 table to work with.
2964
2965 This function may also be used to try to acquire an exclusive
2966 lock on a destination table, by ALTER TABLE ... RENAME.
2967
2968 Returns immediately without any side effect if encounters a lock
2969 conflict. Otherwise takes the lock.
2970
2971 @param mdl_request [in/out] Lock request object for lock to be acquired
2972
2973 @retval FALSE Success. The lock may have not been acquired.
2974 Check the ticket, if it's NULL, a conflicting lock
2975 exists.
2976 @retval TRUE Out of resources, an error has been reported.
2977 */
2978
2979 bool
try_acquire_lock(MDL_request * mdl_request)2980 MDL_context::try_acquire_lock(MDL_request *mdl_request)
2981 {
2982 MDL_ticket *ticket;
2983
2984 if (try_acquire_lock_impl(mdl_request, &ticket))
2985 return TRUE;
2986
2987 if (! mdl_request->ticket)
2988 {
2989 /*
2990 Our attempt to acquire lock without waiting has failed.
2991 Let us release resources which were acquired in the process.
2992
2993 We don't need to count MDL_lock object as unused and possibly
2994 delete it here because:
2995 - Either there was a conflicting ticket in MDL_lock::m_granted/
2996 m_waiting lists during our call to MDL_lock::can_grant_lock().
2997 This ticket can't go away while MDL_lock::m_rwlock is held.
2998 - Or we have tried to acquire an "obtrusive" lock and there was
2999 a conflicting "fast path" lock in MDL_lock::m_fast_path_state
3000 counter during our call to MDL_lock::can_grant_lock().
3001 In this case HAS_OBTRUSIVE lock flag should have been set before
3002 call to MDL_lock::can_grant_lock() so release of this "fast path"
3003 lock will have to take slow path (see release_lock() and invariant
3004 [INV1]). This means that conflicting "fast path" lock can't go
3005 away until MDL_lock::m_rwlock is released or HAS_OBSTRUSIVE flag
3006 is cleared. In the latter case counting MDL_lock object as unused
3007 is responsibility of thread which is decrementing "fast path" lock
3008 counter. MDL_lock object can't be deleted under out feet since
3009 thread doing deletion needs to acquire MDL_lock::m_rwlock first.
3010 */
3011 MDL_lock *lock= ticket->m_lock;
3012
3013 bool last_obtrusive= lock->is_obtrusive_lock(mdl_request->type) &&
3014 ((--lock->m_obtrusive_locks_granted_waiting_count) == 0);
3015 bool last_slow_path= lock->m_granted.is_empty() &&
3016 lock->m_waiting.is_empty();
3017
3018 if (last_slow_path || last_obtrusive)
3019 {
3020 MDL_lock::fast_path_state_t old_state= lock->m_fast_path_state;
3021 MDL_lock::fast_path_state_t new_state;
3022 do
3023 {
3024 new_state= old_state;
3025 if (last_slow_path)
3026 new_state&= ~MDL_lock::HAS_SLOW_PATH;
3027 if (last_obtrusive)
3028 new_state&= ~MDL_lock::HAS_OBTRUSIVE;
3029 }
3030 while (! lock->fast_path_state_cas(&old_state, new_state));
3031 }
3032
3033 mysql_prlock_unlock(&lock->m_rwlock);
3034
3035 /*
3036 If SEs were notified about impending lock acquisition, the failure
3037 to acquire it requires the same notification as lock release.
3038 */
3039 if (ticket->m_hton_notified)
3040 {
3041 mysql_mdl_set_status(ticket->m_psi, MDL_ticket::POST_RELEASE_NOTIFY);
3042 m_owner->notify_hton_post_release_exclusive(&mdl_request->key);
3043 }
3044
3045 MDL_ticket::destroy(ticket);
3046 }
3047
3048 return FALSE;
3049 }
3050
3051
3052 /**
3053 "Materialize" requests for locks which were satisfied using
3054 "fast path" by properly including them into corresponding
3055 MDL_lock::m_granted bitmaps/lists and removing it from
3056 packed counter in MDL_lock::m_fast_path_state.
3057
3058 @note In future we might optimize this method if necessary,
3059 for example, by keeping pointer to first "fast path"
3060 ticket.
3061 */
3062
materialize_fast_path_locks()3063 void MDL_context::materialize_fast_path_locks()
3064 {
3065 int i;
3066
3067 for (i= 0; i < MDL_DURATION_END; i++)
3068 {
3069 Ticket_iterator it(m_tickets[(enum_mdl_duration)i]);
3070 MDL_ticket *ticket;
3071
3072 while ((ticket= it++))
3073 {
3074 if (ticket->m_is_fast_path)
3075 {
3076 MDL_lock *lock= ticket->m_lock;
3077 MDL_lock::fast_path_state_t unobtrusive_lock_increment=
3078 lock->get_unobtrusive_lock_increment(ticket->get_type());
3079 ticket->m_is_fast_path= false;
3080 mysql_prlock_wrlock(&lock->m_rwlock);
3081 lock->m_granted.add_ticket(ticket);
3082 /*
3083 Atomically decrement counter in MDL_lock::m_fast_path_state.
3084 This needs to happen under protection of MDL_lock::m_rwlock to make
3085 it atomic with addition of ticket to MDL_lock::m_granted list and
3086 to enforce invariant [INV1].
3087 */
3088 MDL_lock::fast_path_state_t old_state= lock->m_fast_path_state;
3089 while (! lock->fast_path_state_cas(&old_state,
3090 ((old_state - unobtrusive_lock_increment) |
3091 MDL_lock::HAS_SLOW_PATH)))
3092 { }
3093 mysql_prlock_unlock(&lock->m_rwlock);
3094 }
3095 }
3096 }
3097 }
3098
3099
3100 /**
3101 Auxiliary method for acquiring lock without waiting.
3102
3103 @param mdl_request [in/out] Lock request object for lock to be acquired
3104 @param out_ticket [out] Ticket for the request in case when lock
3105 has not been acquired.
3106
3107 @retval FALSE Success. The lock may have not been acquired.
3108 Check MDL_request::ticket, if it's NULL, a conflicting
3109 lock exists. In this case "out_ticket" out parameter
3110 points to ticket which was constructed for the request.
3111 MDL_ticket::m_lock points to the corresponding MDL_lock
3112 object and MDL_lock::m_rwlock write-locked.
3113 @retval TRUE Out of resources, an error has been reported.
3114 */
3115
3116 bool
try_acquire_lock_impl(MDL_request * mdl_request,MDL_ticket ** out_ticket)3117 MDL_context::try_acquire_lock_impl(MDL_request *mdl_request,
3118 MDL_ticket **out_ticket)
3119 {
3120 MDL_lock *lock;
3121 MDL_key *key= &mdl_request->key;
3122 MDL_ticket *ticket;
3123 enum_mdl_duration found_duration;
3124 MDL_lock::fast_path_state_t unobtrusive_lock_increment;
3125 bool force_slow;
3126 bool pinned;
3127
3128 assert(mdl_request->ticket == NULL);
3129
3130 /* Don't take chances in production. */
3131 mdl_request->ticket= NULL;
3132 mysql_mutex_assert_not_owner(&LOCK_open);
3133
3134 /*
3135 Check whether the context already holds a shared lock on the object,
3136 and if so, grant the request.
3137 */
3138 if ((ticket= find_ticket(mdl_request, &found_duration)))
3139 {
3140 assert(ticket->m_lock);
3141 assert(ticket->has_stronger_or_equal_type(mdl_request->type));
3142 /*
3143 If the request is for a transactional lock, and we found
3144 a transactional lock, just reuse the found ticket.
3145
3146 It's possible that we found a transactional lock,
3147 but the request is for a HANDLER lock. In that case HANDLER
3148 code will clone the ticket (see below why it's needed).
3149
3150 If the request is for a transactional lock, and we found
3151 a HANDLER lock, create a copy, to make sure that when user
3152 does HANDLER CLOSE, the transactional lock is not released.
3153
3154 If the request is for a handler lock, and we found a
3155 HANDLER lock, also do the clone. HANDLER CLOSE for one alias
3156 should not release the lock on the table HANDLER opened through
3157 a different alias.
3158 */
3159 mdl_request->ticket= ticket;
3160 if ((found_duration != mdl_request->duration ||
3161 mdl_request->duration == MDL_EXPLICIT) &&
3162 clone_ticket(mdl_request))
3163 {
3164 /* Clone failed. */
3165 mdl_request->ticket= NULL;
3166 return TRUE;
3167 }
3168 return FALSE;
3169 }
3170
3171 /*
3172 Prepare context for lookup in MDL_map container by allocating pins
3173 if necessary. This also ensures that this MDL_context has pins allocated
3174 and ready for future attempts elements from MDL_map container (which
3175 might happen during lock release).
3176 */
3177 if (fix_pins())
3178 return TRUE;
3179
3180 if (!(ticket= MDL_ticket::create(this, mdl_request->type
3181 #ifndef NDEBUG
3182 , mdl_request->duration
3183 #endif
3184 )))
3185 return TRUE;
3186
3187 /*
3188 Get increment for "fast path" or indication that this is
3189 request for "obtrusive" type of lock outside of critical section.
3190 */
3191 unobtrusive_lock_increment=
3192 MDL_lock::get_unobtrusive_lock_increment(mdl_request);
3193
3194 /*
3195 If this "obtrusive" type we have to take "slow path".
3196 If this context has open HANDLERs we have to take "slow path"
3197 as well for MDL_object_lock::notify_conflicting_locks() to work
3198 properly.
3199 */
3200 #ifdef WITH_WSREP
3201 force_slow= true;
3202 #else
3203 force_slow= ! unobtrusive_lock_increment || m_needs_thr_lock_abort;
3204 #endif /* WITH_WSREP */
3205
3206 /*
3207 If "obtrusive" lock is requested we need to "materialize" all fast
3208 path tickets, so MDL_lock::can_grant_lock() can safely assume
3209 that all granted "fast path" locks belong to different context.
3210 */
3211 if (! unobtrusive_lock_increment)
3212 materialize_fast_path_locks();
3213
3214 assert(ticket->m_psi == NULL);
3215 ticket->m_psi= mysql_mdl_create(ticket, key,
3216 mdl_request->type,
3217 mdl_request->duration,
3218 MDL_ticket::PENDING,
3219 mdl_request->m_src_file,
3220 mdl_request->m_src_line);
3221
3222 /*
3223 We need to notify/get permission from storage engines before acquiring
3224 X lock if it is requested in one of namespaces interesting for SEs.
3225 */
3226 if (mdl_request->type == MDL_EXCLUSIVE &&
3227 MDL_lock::needs_hton_notification(key->mdl_namespace()))
3228 {
3229 mysql_mdl_set_status(ticket->m_psi, MDL_ticket::PRE_ACQUIRE_NOTIFY);
3230
3231 bool victimized;
3232 if (m_owner->notify_hton_pre_acquire_exclusive(key, &victimized))
3233 {
3234 MDL_ticket::destroy(ticket);
3235 my_error(victimized ? ER_LOCK_DEADLOCK : ER_LOCK_REFUSED_BY_ENGINE, MYF(0));
3236 return TRUE;
3237 }
3238 ticket->m_hton_notified= true;
3239
3240 mysql_mdl_set_status(ticket->m_psi, MDL_ticket::PENDING);
3241 }
3242
3243 retry:
3244 /*
3245 The below call pins pointer to returned MDL_lock object (unless
3246 it is the singleton object for GLOBAL or COMMIT namespaces).
3247 */
3248 if (!(lock= mdl_locks.find_or_insert(m_pins, key, &pinned)))
3249 {
3250 /*
3251 If SEs were notified about impending lock acquisition, the failure
3252 to acquire it requires the same notification as lock release.
3253 */
3254 if (ticket->m_hton_notified)
3255 {
3256 mysql_mdl_set_status(ticket->m_psi, MDL_ticket::POST_RELEASE_NOTIFY);
3257 m_owner->notify_hton_post_release_exclusive(key);
3258 }
3259 MDL_ticket::destroy(ticket);
3260 return TRUE;
3261 }
3262
3263 /*
3264 Code counting unused MDL_lock objects below assumes that object is not
3265 pinned iff it is a singleton.
3266 */
3267 assert(mdl_locks.is_lock_object_singleton(key) == !pinned);
3268
3269 if (! force_slow)
3270 {
3271 /*
3272 "Fast path".
3273
3274 Hurray! We are acquring "unobtrusive" type of lock and not forced
3275 to take "slow path" because of open HANDLERs.
3276
3277 Let us do a few checks first to figure out if we really can acquire
3278 lock using "fast path".
3279
3280 Since the MDL_lock object is pinned at this point (or it is the
3281 singleton) we can access its members without risk of it getting deleted
3282 under out feet.
3283
3284 Ordinary read of MDL_lock::m_fast_path_state which we do here is OK as
3285 correctness of value returned by it will be anyway validated by atomic
3286 compare-and-swap which happens later.
3287 In theory, this algorithm will work correctly (but not very efficiently)
3288 if the read will return random values.
3289 In practice, it won't return values which are too out-of-date as the
3290 above call to MDL_map::find_or_insert() contains memory barrier.
3291 */
3292 MDL_lock::fast_path_state_t old_state= lock->m_fast_path_state;
3293 bool first_use;
3294
3295 do
3296 {
3297 /*
3298 Check if hash look-up returned object marked as destroyed or
3299 it was marked as such while it was pinned by us. If yes we
3300 need to unpin it and retry look-up.
3301 */
3302 if (old_state & MDL_lock::IS_DESTROYED)
3303 {
3304 if (pinned)
3305 lf_hash_search_unpin(m_pins);
3306 DEBUG_SYNC(get_thd(), "mdl_acquire_lock_is_destroyed_fast_path");
3307 goto retry;
3308 }
3309
3310 /*
3311 Check that there are no granted/pending "obtrusive" locks and nobody
3312 even is about to try to check if such lock can be acquired.
3313
3314 In these cases we need to take "slow path".
3315 */
3316 if (old_state & MDL_lock::HAS_OBTRUSIVE)
3317 goto slow_path;
3318
3319 /*
3320 If m_fast_path_state doesn't have HAS_SLOW_PATH set and all "fast"
3321 path counters are 0 then we are about to use an unused MDL_lock
3322 object. We need to decrement unused objects counter eventually.
3323 */
3324 first_use= (old_state == 0);
3325
3326 /*
3327 Now we simply need to increment m_fast_path_state with a value which
3328 corresponds to type of our request (i.e. increment part this member
3329 which contains counter which corresponds to this type).
3330
3331 This needs to be done as atomical operation with the above checks,
3332 which is achieved by using atomic compare-and-swap.
3333
3334 @sa MDL_object_lock::m_unobtrusive_lock_increment for explanation
3335 why overflow is not an issue here.
3336 */
3337 }
3338 while (! lock->fast_path_state_cas(&old_state,
3339 old_state + unobtrusive_lock_increment));
3340
3341 /*
3342 Lock has been acquired. Since this can only be an "unobtrusive" lock and
3343 there were no active/pending requests for "obtrusive" locks, we don't need
3344 to care about "hog" and "piglet" locks and the max_write_lock_count
3345 threshold.
3346 */
3347
3348 if (pinned)
3349 lf_hash_search_unpin(m_pins);
3350
3351 /*
3352 Don't count singleton MDL_lock objects as used, use "pinned == false"
3353 as an indication of such objects.
3354 */
3355 if (first_use && pinned)
3356 mdl_locks.lock_object_used();
3357
3358 /*
3359 Since this MDL_ticket is not visible to any threads other than
3360 the current one, we can set MDL_ticket::m_lock member without
3361 protect of MDL_lock::m_rwlock. MDL_lock won't be deleted
3362 underneath our feet as MDL_lock::m_fast_path_state serves as
3363 reference counter in this case.
3364 */
3365 ticket->m_lock= lock;
3366 ticket->m_is_fast_path= true;
3367
3368 m_tickets[mdl_request->duration].push_front(ticket);
3369
3370 mdl_request->ticket= ticket;
3371
3372 mysql_mdl_set_status(ticket->m_psi, MDL_ticket::GRANTED);
3373 return FALSE;
3374 }
3375
3376 slow_path:
3377
3378 /*
3379 "Slow path".
3380
3381 Do full-blown check and list manipulation if necessary.
3382 */
3383 mysql_prlock_wrlock(&lock->m_rwlock);
3384
3385 /*
3386 First of all, let us check if hash look-up returned MDL_lock object which
3387 is marked as destroyed or was marked as such while it was pinned by us.
3388 If we have got such object we need to retry look-up.
3389
3390 We can use ordinary non-atomic read in this case as this flag is set under
3391 protection of MDL_lock::m_rwlock (we can get inconsistent data for other
3392 parts of m_fast_path_state due to concurrent atomic updates, but we don't
3393 care about them yet).
3394 */
3395 MDL_lock::fast_path_state_t state= lock->m_fast_path_state;
3396
3397 if (state & MDL_lock::IS_DESTROYED)
3398 {
3399 mysql_prlock_unlock(&lock->m_rwlock);
3400 /*
3401 We can't unpin object earlier as lf_hash_delete() might have been
3402 called for it already and so LF_ALLOCATOR is free to deallocate it
3403 once unpinned.
3404 */
3405 if (pinned)
3406 lf_hash_search_unpin(m_pins);
3407 DEBUG_SYNC(get_thd(), "mdl_acquire_lock_is_destroyed_slow_path");
3408 goto retry;
3409 }
3410
3411 /*
3412 Object was not marked as destroyed. Since it can't be deleted from hash
3413 and deallocated until this happens we can unpin it and work with it safely
3414 while MDL_lock::m_rwlock is held.
3415 */
3416 if (pinned)
3417 lf_hash_search_unpin(m_pins);
3418
3419 /*
3420 When we try to acquire the first obtrusive lock for MDL_lock object we
3421 need to atomically set the HAS_OBTRUSIVE flag in m_fast_path_state before
3422 we call the MDL_lock::can_grant_lock() method.
3423 This is necessary to prevent concurrent fast path acquisitions from
3424 invalidating the results of this method.
3425 */
3426 bool first_obtrusive_lock= (unobtrusive_lock_increment == 0) &&
3427 ((lock->m_obtrusive_locks_granted_waiting_count++) == 0);
3428 bool first_use= false;
3429
3430 /*
3431 When we try to acquire the first "slow" path lock for MDL_lock object
3432 we also need to atomically set HAS_SLOW_PATH flag. It is OK to read
3433 HAS_SLOW_PATH non-atomically here since it can be only set under
3434 protection of MDL_lock::m_rwlock lock.
3435 */
3436 if (!(state & MDL_lock::HAS_SLOW_PATH) || first_obtrusive_lock)
3437 {
3438 do
3439 {
3440 /*
3441 If HAS_SLOW_PATH flag is not set and all "fast" path counters
3442 are zero we are about to use previously unused MDL_lock object.
3443 MDL_map::m_unused_lock_objects counter needs to be decremented
3444 eventually.
3445 */
3446 first_use= (state == 0);
3447 }
3448 while (! lock->fast_path_state_cas(&state,
3449 state | MDL_lock::HAS_SLOW_PATH |
3450 (first_obtrusive_lock ? MDL_lock::HAS_OBTRUSIVE: 0)));
3451 }
3452
3453 /*
3454 Don't count singleton MDL_lock objects as used, use "pinned == false"
3455 as an indication of such objects.
3456
3457 If the fact that we do this atomic decrement under MDL_lock::m_rwlock
3458 ever becomes bottleneck (which is unlikely) it can be safely moved
3459 outside of critical section.
3460 */
3461 if (first_use && pinned)
3462 mdl_locks.lock_object_used();
3463
3464 ticket->m_lock= lock;
3465
3466 if (lock->can_grant_lock(mdl_request->type, this))
3467 {
3468 lock->m_granted.add_ticket(ticket);
3469
3470 if (lock->is_affected_by_max_write_lock_count())
3471 {
3472 /*
3473 If we have acquired an higher-prio "piglet" or "hog" lock while there
3474 are pending lower priority locks, we need to increment the appropriate
3475 counter.
3476
3477 If one or both of these counters exceed the max_write_lock_count
3478 threshold, change priority matrice for this MDL_lock object.
3479 Reschedule waiters to avoid problems when the change of matrice
3480 happens concurrently to other thread acquiring a lower priority
3481 lock between its call to MDL_lock::can_grant_lock() and
3482 MDL_context::find_deadlock().
3483 */
3484 if (lock->count_piglets_and_hogs(mdl_request->type))
3485 lock->reschedule_waiters();
3486 }
3487
3488 mysql_prlock_unlock(&lock->m_rwlock);
3489
3490 m_tickets[mdl_request->duration].push_front(ticket);
3491
3492 mdl_request->ticket= ticket;
3493
3494 mysql_mdl_set_status(ticket->m_psi, MDL_ticket::GRANTED);
3495 }
3496 else
3497 *out_ticket= ticket;
3498
3499 return FALSE;
3500 }
3501
3502
3503 /**
3504 Create a copy of a granted ticket.
3505 This is used to make sure that HANDLER ticket
3506 is never shared with a ticket that belongs to
3507 a transaction, so that when we HANDLER CLOSE,
3508 we don't release a transactional ticket, and
3509 vice versa -- when we COMMIT, we don't mistakenly
3510 release a ticket for an open HANDLER.
3511
3512 @retval TRUE Out of memory.
3513 @retval FALSE Success.
3514 */
3515
3516 bool
clone_ticket(MDL_request * mdl_request)3517 MDL_context::clone_ticket(MDL_request *mdl_request)
3518 {
3519 MDL_ticket *ticket;
3520
3521 mysql_mutex_assert_not_owner(&LOCK_open);
3522
3523 /*
3524 Since in theory we can clone ticket belonging to a different context
3525 we need to prepare target context for possible attempts to release
3526 lock and thus possible removal of MDL_lock from MDL_map container.
3527 So we allocate pins to be able to work with this container if they
3528 are not allocated already.
3529 */
3530 if (fix_pins())
3531 return TRUE;
3532
3533 /*
3534 By submitting mdl_request->type to MDL_ticket::create()
3535 we effectively downgrade the cloned lock to the level of
3536 the request.
3537 */
3538 if (!(ticket= MDL_ticket::create(this, mdl_request->type
3539 #ifndef NDEBUG
3540 , mdl_request->duration
3541 #endif
3542 )))
3543 return TRUE;
3544
3545 assert(ticket->m_psi == NULL);
3546 ticket->m_psi= mysql_mdl_create(ticket,
3547 &mdl_request->key,
3548 mdl_request->type,
3549 mdl_request->duration,
3550 MDL_ticket::PENDING,
3551 mdl_request->m_src_file,
3552 mdl_request->m_src_line);
3553
3554 /* clone() is not supposed to be used to get a stronger lock. */
3555 assert(mdl_request->ticket->has_stronger_or_equal_type(ticket->m_type));
3556
3557
3558 /*
3559 If we are to clone exclusive lock in namespace requiring notification
3560 of storage engines we need to notify/get permission from SEs similarly
3561 to situation when lock acquired.
3562 */
3563 if (mdl_request->type == MDL_EXCLUSIVE &&
3564 MDL_lock::needs_hton_notification(mdl_request->key.mdl_namespace()))
3565 {
3566 assert(mdl_request->ticket->m_hton_notified);
3567
3568 mysql_mdl_set_status(ticket->m_psi, MDL_ticket::PRE_ACQUIRE_NOTIFY);
3569
3570 bool victimized;
3571 if (m_owner->notify_hton_pre_acquire_exclusive(&mdl_request->key, &victimized))
3572 {
3573 MDL_ticket::destroy(ticket);
3574 my_error(victimized ? ER_LOCK_DEADLOCK : ER_LOCK_REFUSED_BY_ENGINE, MYF(0));
3575 return TRUE;
3576 }
3577 ticket->m_hton_notified= true;
3578
3579 mysql_mdl_set_status(ticket->m_psi, MDL_ticket::PENDING);
3580 }
3581
3582 ticket->m_lock= mdl_request->ticket->m_lock;
3583
3584 if (mdl_request->ticket->m_is_fast_path)
3585 {
3586 /*
3587 We are cloning ticket which was acquired on "fast path".
3588 Let us use "fast path" to create clone as well.
3589 */
3590 MDL_lock::fast_path_state_t unobtrusive_lock_increment=
3591 ticket->m_lock->get_unobtrusive_lock_increment(ticket->get_type());
3592
3593 /*
3594 "Obtrusive" type of lock can't be cloned from weaker, "unobtrusive"
3595 type of lock.
3596 */
3597 assert(unobtrusive_lock_increment != 0);
3598
3599 /*
3600 Increment of counter in MDL_lock::m_fast_path_state needs to happen here
3601 atomically and under protection of MDL_lock::m_rwlock in order to enforce
3602 invariant [INV1].
3603 */
3604 mysql_prlock_wrlock(&ticket->m_lock->m_rwlock);
3605 ticket->m_lock->fast_path_state_add(unobtrusive_lock_increment);
3606 mysql_prlock_unlock(&ticket->m_lock->m_rwlock);
3607 ticket->m_is_fast_path= true;
3608 }
3609 else
3610 {
3611 /*
3612 We are cloning ticket which was acquired on "slow path".
3613 We will use "slow path" for new ticket as well. We also
3614 need to take into account if new ticket corresponds to
3615 "obtrusive" lock.
3616 */
3617 bool is_obtrusive= ticket->m_lock->is_obtrusive_lock(ticket->m_type);
3618 mysql_prlock_wrlock(&ticket->m_lock->m_rwlock);
3619 ticket->m_lock->m_granted.add_ticket(ticket);
3620 if (is_obtrusive)
3621 {
3622 /*
3623 We don't need to set HAS_OBTRUSIVE flag in MDL_lock::m_fast_path_state
3624 here as it is already set since the ticket being cloned already
3625 represents "obtrusive" lock for this MDL_lock object.
3626 */
3627 assert(ticket->m_lock->m_obtrusive_locks_granted_waiting_count != 0);
3628 ++ticket->m_lock->m_obtrusive_locks_granted_waiting_count;
3629 }
3630 mysql_prlock_unlock(&ticket->m_lock->m_rwlock);
3631 }
3632
3633 mdl_request->ticket= ticket;
3634
3635 m_tickets[mdl_request->duration].push_front(ticket);
3636
3637 mysql_mdl_set_status(ticket->m_psi, MDL_ticket::GRANTED);
3638
3639 return FALSE;
3640 }
3641
3642
3643 /**
3644 Notify threads holding S/SH metadata locks on an object, which conflict
3645 with a pending X lock.
3646
3647 @note Currently this method is guaranteed to notify shared lock
3648 owners which have MDL_context::m_needs_thr_lock_abort flag
3649 set (as for others conficting locks might have been acquired
3650 on "fast path" and thus might be absent from list of granted
3651 locks).
3652 This is OK as notification for other contexts is anyway
3653 no-op now.
3654
3655 @note We don't notify threads holding other than S/SH types of
3656 conflicting locks on the object since notification should
3657 not be needed and anyway will be no-op for them (unless
3658 they also hold S/SH locks on the object).
3659
3660 @param ctx MDL_context for current thread.
3661 @param lock MDL_lock object representing lock which is to be
3662 acquired.
3663 */
3664
object_lock_notify_conflicting_locks(MDL_context * ctx,MDL_lock * lock)3665 void MDL_lock::object_lock_notify_conflicting_locks(MDL_context *ctx, MDL_lock *lock)
3666 {
3667 Ticket_iterator it(lock->m_granted);
3668 MDL_ticket *conflicting_ticket;
3669
3670 while ((conflicting_ticket= it++))
3671 {
3672 if (conflicting_ticket->get_ctx() != ctx &&
3673 (conflicting_ticket->get_type() == MDL_SHARED ||
3674 conflicting_ticket->get_type() == MDL_SHARED_HIGH_PRIO))
3675 {
3676 MDL_context *conflicting_ctx= conflicting_ticket->get_ctx();
3677
3678 /*
3679 If thread which holds conflicting lock is waiting on table-level
3680 lock or some other non-MDL resource we might need to wake it up
3681 by calling code outside of MDL.
3682
3683 The only scenario in which it is important now looks like:
3684 Thread 1: HANDLER t1 OPEN, acquires S metadata lock on t1.
3685 Thread 2: LOCK TABLES t1 WRITE, t2 READ LOCAL, acquire
3686 SNRW lock on t1, SR lock on t2 and TL_READ THR_LOCK
3687 lock on t2.
3688 Thread 1: Executes UPDATE t2 SET i = 1 or some other DML which
3689 will directly or indirectly block on THR_LOCK for t2.
3690 Thread 2: Does ALTER TABLE t1 ... which tries to upgrade SNRW
3691 metadata lock to X lock and blocks because of S lock
3692 from open HANDLER.
3693
3694 A similar scenario is possible when MERGE tables are used instead
3695 of the READ LOCAL clause.
3696 Once we will get rid of the support for READ LOCAL and MERGE clauses
3697 this code can be removed.
3698 */
3699 ctx->get_owner()->
3700 notify_shared_lock(conflicting_ctx->get_owner(),
3701 conflicting_ctx->get_needs_thr_lock_abort());
3702 }
3703 }
3704 }
3705
3706
3707 /**
3708 Acquire one lock with waiting for conflicting locks to go away if needed.
3709
3710 @param mdl_request [in/out] Lock request object for lock to be acquired
3711
3712 @param lock_wait_timeout [in] Seconds to wait before timeout.
3713
3714 @retval FALSE Success. MDL_request::ticket points to the ticket
3715 for the lock.
3716 @retval TRUE Failure (Out of resources or waiting is aborted),
3717 */
3718
3719 bool
acquire_lock(MDL_request * mdl_request,ulong lock_wait_timeout)3720 MDL_context::acquire_lock(MDL_request *mdl_request, ulong lock_wait_timeout)
3721 {
3722 if (lock_wait_timeout == 0)
3723 {
3724 /*
3725 Resort to try_acquire_lock() in case of zero timeout.
3726
3727 This allows to avoid unnecessary deadlock detection attempt and "fake"
3728 deadlocks which might result from it.
3729 In case of failure to acquire lock, try_acquire_lock() preserves
3730 invariants by updating MDL_lock::fast_path_state and obtrusive locks
3731 count. It also performs SE notification if needed.
3732 */
3733 if (try_acquire_lock(mdl_request))
3734 return true;
3735
3736 if (!mdl_request->ticket)
3737 {
3738 /* We have failed to acquire lock instantly. */
3739 DEBUG_SYNC(get_thd(), "mdl_acquire_lock_wait");
3740 my_error(ER_LOCK_WAIT_TIMEOUT, MYF(0));
3741 return true;
3742 }
3743 return false;
3744 }
3745
3746 /* Normal, non-zero timeout case. */
3747
3748 MDL_lock *lock;
3749 MDL_ticket *ticket;
3750 struct timespec abs_timeout;
3751 MDL_wait::enum_wait_status wait_status;
3752 /* Do some work outside the critical section. */
3753 set_timespec(&abs_timeout, lock_wait_timeout);
3754
3755 if (try_acquire_lock_impl(mdl_request, &ticket))
3756 return TRUE;
3757
3758 if (mdl_request->ticket)
3759 {
3760 /*
3761 We have managed to acquire lock without waiting.
3762 MDL_lock, MDL_context and MDL_request were updated
3763 accordingly, so we can simply return success.
3764 */
3765 return FALSE;
3766 }
3767
3768 /*
3769 Our attempt to acquire lock without waiting has failed.
3770 As a result of this attempt we got MDL_ticket with m_lock
3771 member pointing to the corresponding MDL_lock object which
3772 has MDL_lock::m_rwlock write-locked.
3773 */
3774 lock= ticket->m_lock;
3775
3776 lock->m_waiting.add_ticket(ticket);
3777
3778 /*
3779 Once we added a pending ticket to the waiting queue,
3780 we must ensure that our wait slot is empty, so
3781 that our lock request can be scheduled. Do that in the
3782 critical section formed by the acquired write lock on MDL_lock.
3783 */
3784 m_wait.reset_status();
3785
3786 if (lock->needs_notification(ticket))
3787 lock->notify_conflicting_locks(this);
3788
3789 mysql_prlock_unlock(&lock->m_rwlock);
3790
3791 #ifdef HAVE_PSI_METADATA_INTERFACE
3792 PSI_metadata_locker_state state;
3793 PSI_metadata_locker *locker= NULL;
3794
3795 if (ticket->m_psi != NULL)
3796 {
3797 locker= PSI_METADATA_CALL(start_metadata_wait)(&state,
3798 ticket->m_psi,
3799 __FILE__, __LINE__);
3800 }
3801 #endif
3802
3803 will_wait_for(ticket);
3804
3805 /* There is a shared or exclusive lock on the object. */
3806 DEBUG_SYNC(get_thd(), "mdl_acquire_lock_wait");
3807
3808 find_deadlock();
3809
3810 if (lock->needs_notification(ticket) || lock->needs_connection_check())
3811 {
3812 struct timespec abs_shortwait;
3813 set_timespec(&abs_shortwait, 1);
3814 wait_status= MDL_wait::EMPTY;
3815
3816 while (cmp_timespec(&abs_shortwait, &abs_timeout) <= 0)
3817 {
3818 /* abs_timeout is far away. Wait a short while and notify locks. */
3819 wait_status= m_wait.timed_wait(m_owner, &abs_shortwait, FALSE,
3820 mdl_request->key.get_wait_state_name());
3821
3822 if (wait_status != MDL_wait::EMPTY)
3823 break;
3824
3825 if (lock->needs_connection_check() && ! m_owner->is_connected())
3826 {
3827 /*
3828 If this is user-level lock and the client is disconnected don't wait
3829 forever: assume it's the same as statement being killed (this differs
3830 from pre-5.7 where we treat it as timeout, but is more logical).
3831 Using MDL_wait::set_status() to set status atomically wastes one
3832 condition variable wake up but should happen rarely.
3833 We don't want to do this check for all types of metadata locks since
3834 in general case we may want to complete wait/operation even when
3835 connection is lost (e.g. in case of logging into slow/general log).
3836 */
3837 if (! m_wait.set_status(MDL_wait::KILLED))
3838 wait_status= MDL_wait::KILLED;
3839 break;
3840 }
3841
3842 if (lock->needs_notification(ticket))
3843 {
3844 mysql_prlock_wrlock(&lock->m_rwlock);
3845 lock->notify_conflicting_locks(this);
3846 mysql_prlock_unlock(&lock->m_rwlock);
3847 }
3848
3849 set_timespec(&abs_shortwait, 1);
3850 }
3851 if (wait_status == MDL_wait::EMPTY)
3852 wait_status= m_wait.timed_wait(m_owner, &abs_timeout, TRUE,
3853 mdl_request->key.get_wait_state_name());
3854 }
3855 else
3856 {
3857 wait_status= m_wait.timed_wait(m_owner, &abs_timeout, TRUE,
3858 mdl_request->key.get_wait_state_name());
3859 }
3860
3861 done_waiting_for();
3862
3863 #ifdef HAVE_PSI_METADATA_INTERFACE
3864 if (locker != NULL)
3865 {
3866 PSI_METADATA_CALL(end_metadata_wait)(locker, 0);
3867 }
3868 #endif
3869
3870 if (wait_status != MDL_wait::GRANTED)
3871 {
3872 lock->remove_ticket(this, m_pins, &MDL_lock::m_waiting, ticket);
3873
3874 /*
3875 If SEs were notified about impending lock acquisition, the failure
3876 to acquire it requires the same notification as lock release.
3877 */
3878 if (ticket->m_hton_notified)
3879 {
3880 mysql_mdl_set_status(ticket->m_psi, MDL_ticket::POST_RELEASE_NOTIFY);
3881 m_owner->notify_hton_post_release_exclusive(&mdl_request->key);
3882 }
3883
3884 MDL_ticket::destroy(ticket);
3885 switch (wait_status)
3886 {
3887 case MDL_wait::VICTIM:
3888 my_error(ER_LOCK_DEADLOCK, MYF(0));
3889 break;
3890 case MDL_wait::TIMEOUT:
3891 my_error(ER_LOCK_WAIT_TIMEOUT, MYF(0));
3892 break;
3893 case MDL_wait::KILLED:
3894 if (get_owner()->is_killed() == ER_QUERY_TIMEOUT)
3895 my_error(ER_QUERY_TIMEOUT, MYF(0));
3896 else
3897 my_error(ER_QUERY_INTERRUPTED, MYF(0));
3898 break;
3899 default:
3900 assert(0);
3901 break;
3902 }
3903 return TRUE;
3904 }
3905
3906 /*
3907 We have been granted our request.
3908 State of MDL_lock object is already being appropriately updated by a
3909 concurrent thread (@sa MDL_lock:reschedule_waiters()).
3910 So all we need to do is to update MDL_context and MDL_request objects.
3911 */
3912 assert(wait_status == MDL_wait::GRANTED);
3913
3914 m_tickets[mdl_request->duration].push_front(ticket);
3915
3916 mdl_request->ticket= ticket;
3917
3918 mysql_mdl_set_status(ticket->m_psi, MDL_ticket::GRANTED);
3919
3920 return FALSE;
3921 }
3922
3923
3924 class MDL_request_cmp :
3925 public std::binary_function<const MDL_request*, const MDL_request*, bool>
3926 {
3927 public:
operator ()(const MDL_request * req1,const MDL_request * req2)3928 bool operator()(const MDL_request *req1, const MDL_request *req2)
3929 {
3930 int rc= req1->key.cmp(&req2->key);
3931 /*
3932 In cases when both requests correspond to the same key, we need to put
3933 the request for the stronger lock type first, to avoid extra deadlocks.
3934 We do this by simply comparing types of lock requests.
3935 This works OK since it is mostly needed to avoid the scenario when
3936 LOCK TABLES t1 WRITE, t1 READ acquires SRO lock before SNRW lock.
3937 It won't work in the general case (e.g. SRO is not stronger than SW).
3938 */
3939 if (rc == 0)
3940 rc= static_cast<int>(req2->type) - static_cast<int>(req1->type);
3941 return rc < 0;
3942 }
3943 };
3944
3945
3946
3947 /**
3948 Acquire exclusive locks. There must be no granted locks in the
3949 context.
3950
3951 This is a replacement of lock_table_names(). It is used in
3952 RENAME, DROP and other DDL SQL statements.
3953
3954 @param mdl_requests List of requests for locks to be acquired.
3955
3956 @param lock_wait_timeout Seconds to wait before timeout.
3957
3958 @note The list of requests should not contain non-exclusive lock requests.
3959 There should not be any acquired locks in the context.
3960
3961 @note Assumes that one already owns scoped intention exclusive lock.
3962
3963 @note If acquisition fails any locks with MDL_EXPLICIT duration that had
3964 already been taken, are released. Not just locks with MDL_STATEMENT
3965 and MDL_TRANSACTION duration. This makes acquition of MDL_EXPLICIT
3966 locks atomic (all or nothing). This is needed for the locking
3967 service plugin API.
3968
3969 @retval FALSE Success
3970 @retval TRUE Failure
3971 */
3972
acquire_locks(MDL_request_list * mdl_requests,ulong lock_wait_timeout)3973 bool MDL_context::acquire_locks(MDL_request_list *mdl_requests,
3974 ulong lock_wait_timeout)
3975 {
3976 MDL_request_list::Iterator it(*mdl_requests);
3977 MDL_request **p_req;
3978 MDL_savepoint mdl_svp= mdl_savepoint();
3979 /*
3980 Remember the first MDL_EXPLICIT ticket so that we can release
3981 any new such locks taken if acquisition fails.
3982 */
3983 MDL_ticket *explicit_front= m_tickets[MDL_EXPLICIT].front();
3984 const size_t req_count= mdl_requests->elements();
3985
3986 if (req_count == 0)
3987 return FALSE;
3988
3989 /* Sort requests according to MDL_key. */
3990 Prealloced_array<MDL_request*, 16>
3991 sort_buf(key_memory_MDL_context_acquire_locks);
3992 if (sort_buf.reserve(req_count))
3993 return TRUE;
3994
3995 for (size_t ii=0; ii < req_count; ++ii)
3996 {
3997 sort_buf.push_back(it++);
3998 }
3999
4000 std::sort(sort_buf.begin(), sort_buf.end(), MDL_request_cmp());
4001
4002 size_t num_acquired= 0;
4003 for (p_req= sort_buf.begin(); p_req != sort_buf.end(); p_req++)
4004 {
4005 if (acquire_lock(*p_req, lock_wait_timeout))
4006 goto err;
4007 ++num_acquired;
4008 }
4009 return FALSE;
4010
4011 err:
4012 /*
4013 Release locks we have managed to acquire so far.
4014 Use rollback_to_savepoint() since there may be duplicate
4015 requests that got assigned the same ticket.
4016 */
4017 rollback_to_savepoint(mdl_svp);
4018 /*
4019 Also release the MDL_EXPLICIT locks taken in this call.
4020 The locking service plugin API needs acquisition of such
4021 locks to be atomic as well.
4022 */
4023 release_locks_stored_before(MDL_EXPLICIT, explicit_front);
4024
4025 /* Reset lock requests back to its initial state. */
4026 for (p_req= sort_buf.begin();
4027 p_req != sort_buf.begin() + num_acquired; p_req++)
4028 {
4029 (*p_req)->ticket= NULL;
4030 }
4031 return TRUE;
4032 }
4033
4034
4035 /**
4036 Upgrade a shared metadata lock.
4037
4038 Used in ALTER TABLE and CREATE TABLE.
4039
4040 @param mdl_ticket Lock to upgrade.
4041 @param new_type Lock type to upgrade to.
4042 @param lock_wait_timeout Seconds to wait before timeout.
4043
4044 @note In case of failure to upgrade lock (e.g. because upgrader
4045 was killed) leaves lock in its original state (locked in
4046 shared mode).
4047
4048 @note There can be only one upgrader for a lock or we will have deadlock.
4049 This invariant is ensured by the fact that upgradeable locks SU, SNW
4050 and SNRW are not compatible with each other and themselves in case
4051 of ALTER TABLE operation.
4052 In case of CREATE TABLE operation there is chance of deadlock as 'S'
4053 is compatible with 'S'. But the deadlock is recovered by backoff and
4054 retry mechanism.
4055
4056 @retval FALSE Success
4057 @retval TRUE Failure (thread was killed)
4058 */
4059
4060 bool
upgrade_shared_lock(MDL_ticket * mdl_ticket,enum_mdl_type new_type,ulong lock_wait_timeout)4061 MDL_context::upgrade_shared_lock(MDL_ticket *mdl_ticket,
4062 enum_mdl_type new_type,
4063 ulong lock_wait_timeout)
4064 {
4065 MDL_request mdl_new_lock_request;
4066 MDL_savepoint mdl_svp= mdl_savepoint();
4067 bool is_new_ticket;
4068 MDL_lock *lock;
4069
4070 DBUG_ENTER("MDL_context::upgrade_shared_lock");
4071 DEBUG_SYNC(get_thd(), "mdl_upgrade_lock");
4072
4073 /*
4074 Do nothing if already upgraded. Used when we FLUSH TABLE under
4075 LOCK TABLES and a table is listed twice in LOCK TABLES list.
4076 */
4077 if (mdl_ticket->has_stronger_or_equal_type(new_type))
4078 DBUG_RETURN(FALSE);
4079
4080 MDL_REQUEST_INIT_BY_KEY(&mdl_new_lock_request,
4081 &mdl_ticket->m_lock->key, new_type,
4082 MDL_TRANSACTION);
4083
4084 if (acquire_lock(&mdl_new_lock_request, lock_wait_timeout))
4085 DBUG_RETURN(TRUE);
4086
4087 is_new_ticket= ! has_lock(mdl_svp, mdl_new_lock_request.ticket);
4088
4089 lock= mdl_ticket->m_lock;
4090
4091 /* Code below assumes that we were upgrading to "obtrusive" type of lock. */
4092 assert(lock->is_obtrusive_lock(new_type));
4093
4094 /* Merge the acquired and the original lock. @todo: move to a method. */
4095 mysql_prlock_wrlock(&lock->m_rwlock);
4096 if (is_new_ticket)
4097 {
4098 lock->m_granted.remove_ticket(mdl_new_lock_request.ticket);
4099 /*
4100 We should not clear HAS_OBTRUSIVE flag in this case as we will
4101 get "obtrusive' lock as result in any case.
4102 */
4103 --lock->m_obtrusive_locks_granted_waiting_count;
4104 }
4105 /*
4106 Set the new type of lock in the ticket. To update state of
4107 MDL_lock object correctly we need to temporarily exclude
4108 ticket from the granted queue or "fast path" counter and
4109 then include lock back into granted queue.
4110 Note that with current code at this point we can't have
4111 "fast path" tickets as upgrade to "obtrusive" locks
4112 materializes tickets normally. Still we cover this case
4113 for completeness.
4114 */
4115 if (mdl_ticket->m_is_fast_path)
4116 {
4117 /*
4118 Decrement of counter in MDL_lock::m_fast_path_state needs to be done
4119 under protection of MDL_lock::m_rwlock to ensure that it is atomic with
4120 changes to MDL_lock::m_granted list and to enforce invariant [INV1].
4121 Note that since we have HAS_OBTRUSIVE flag set at this point all
4122 concurrent lock acquisitions and releases will have to acquire
4123 MDL_lock::m_rwlock, so nobody will see results of this decrement until
4124 m_rwlock is released.
4125 */
4126 lock->fast_path_state_add(
4127 -lock->get_unobtrusive_lock_increment(mdl_ticket->m_type));
4128 mdl_ticket->m_is_fast_path= false;
4129 }
4130 else
4131 {
4132 lock->m_granted.remove_ticket(mdl_ticket);
4133 /*
4134 Also if we are upgrading from "obtrusive" lock we need to temporarily
4135 decrement m_obtrusive_locks_granted_waiting_count counter.
4136 We should not clear HAS_OBTRUSIVE flag in this case as we will get
4137 "obtrusive' lock as result in any case.
4138 */
4139 if (lock->is_obtrusive_lock(mdl_ticket->m_type))
4140 --lock->m_obtrusive_locks_granted_waiting_count;
4141 }
4142
4143 mdl_ticket->m_type= new_type;
4144
4145 lock->m_granted.add_ticket(mdl_ticket);
4146 /*
4147 Since we always upgrade to "obtrusive" type of lock we need to
4148 increment m_obtrusive_locks_granted_waiting_count counter.
4149
4150 HAS_OBTRUSIVE flag has been already set by acquire_lock()
4151 and should not have been cleared since then.
4152 */
4153 assert(lock->m_fast_path_state & MDL_lock::HAS_OBTRUSIVE);
4154 ++lock->m_obtrusive_locks_granted_waiting_count;
4155
4156 mysql_prlock_unlock(&lock->m_rwlock);
4157
4158 /*
4159 The below code can't handle situation when we upgrade to lock requiring
4160 SE notification and it turns out that we already have lock of this type
4161 associated with different ticket.
4162 */
4163 assert(is_new_ticket || ! mdl_new_lock_request.ticket->m_hton_notified);
4164
4165 mdl_ticket->m_hton_notified= mdl_new_lock_request.ticket->m_hton_notified;
4166
4167 if (is_new_ticket)
4168 {
4169 m_tickets[MDL_TRANSACTION].remove(mdl_new_lock_request.ticket);
4170 MDL_ticket::destroy(mdl_new_lock_request.ticket);
4171 }
4172
4173 DBUG_RETURN(FALSE);
4174 }
4175
4176
4177 /**
4178 A fragment of recursive traversal of the wait-for graph
4179 in search for deadlocks. Direct the deadlock visitor to all
4180 contexts that own the lock the current node in the wait-for
4181 graph is waiting for.
4182 As long as the initial node is remembered in the visitor,
4183 a deadlock is found when the same node is seen twice.
4184 */
4185
visit_subgraph(MDL_ticket * waiting_ticket,MDL_wait_for_graph_visitor * gvisitor)4186 bool MDL_lock::visit_subgraph(MDL_ticket *waiting_ticket,
4187 MDL_wait_for_graph_visitor *gvisitor)
4188 {
4189 MDL_ticket *ticket;
4190 MDL_context *src_ctx= waiting_ticket->get_ctx();
4191 bool result= TRUE;
4192
4193 mysql_prlock_rdlock(&m_rwlock);
4194
4195 /*
4196 Iterators must be initialized after taking a read lock.
4197
4198 Note that MDL_ticket's which correspond to lock requests satisfied
4199 on "fast path" are not present in m_granted list and thus
4200 corresponding edges are missing from wait-for graph.
4201 It is OK since contexts with "fast path" tickets are not allowed to
4202 wait for any resource (they have to convert "fast path" tickets to
4203 normal tickets first) and thus cannot participate in deadlock.
4204 @sa MDL_contex::will_wait_for().
4205 */
4206 Ticket_iterator granted_it(m_granted);
4207 Ticket_iterator waiting_it(m_waiting);
4208
4209 /*
4210 MDL_lock's waiting and granted queues and MDL_context::m_waiting_for
4211 member are updated by different threads when the lock is granted
4212 (see MDL_context::acquire_lock() and MDL_lock::reschedule_waiters()).
4213 As a result, here we may encounter a situation when MDL_lock data
4214 already reflects the fact that the lock was granted but
4215 m_waiting_for member has not been updated yet.
4216
4217 For example, imagine that:
4218
4219 thread1: Owns SNW lock on table t1.
4220 thread2: Attempts to acquire SW lock on t1,
4221 but sees an active SNW lock.
4222 Thus adds the ticket to the waiting queue and
4223 sets m_waiting_for to point to the ticket.
4224 thread1: Releases SNW lock, updates MDL_lock object to
4225 grant SW lock to thread2 (moves the ticket for
4226 SW from waiting to the active queue).
4227 Attempts to acquire a new SNW lock on t1,
4228 sees an active SW lock (since it is present in the
4229 active queue), adds ticket for SNW lock to the waiting
4230 queue, sets m_waiting_for to point to this ticket.
4231
4232 At this point deadlock detection algorithm run by thread1 will see that:
4233 - Thread1 waits for SNW lock on t1 (since m_waiting_for is set).
4234 - SNW lock is not granted, because it conflicts with active SW lock
4235 owned by thread 2 (since ticket for SW is present in granted queue).
4236 - Thread2 waits for SW lock (since its m_waiting_for has not been
4237 updated yet!).
4238 - SW lock is not granted because there is pending SNW lock from thread1.
4239 Therefore deadlock should exist [sic!].
4240
4241 To avoid detection of such false deadlocks we need to check the "actual"
4242 status of the ticket being waited for, before analyzing its blockers.
4243 We do this by checking the wait status of the context which is waiting
4244 for it. To avoid races this has to be done under protection of
4245 MDL_lock::m_rwlock lock.
4246 */
4247 if (src_ctx->m_wait.get_status() != MDL_wait::EMPTY)
4248 {
4249 result= FALSE;
4250 goto end;
4251 }
4252
4253 /*
4254 To avoid visiting nodes which were already marked as victims of
4255 deadlock detection (or whose requests were already satisfied) we
4256 enter the node only after peeking at its wait status.
4257 This is necessary to avoid active waiting in a situation
4258 when previous searches for a deadlock already selected the
4259 node we're about to enter as a victim (see the comment
4260 in MDL_context::find_deadlock() for explanation why several searches
4261 can be performed for the same wait).
4262 There is no guarantee that the node isn't chosen a victim while we
4263 are visiting it but this is OK: in the worst case we might do some
4264 extra work and one more context might be chosen as a victim.
4265 */
4266 if (gvisitor->enter_node(src_ctx))
4267 goto end;
4268
4269 /*
4270 We do a breadth-first search first -- that is, inspect all
4271 edges of the current node, and only then follow up to the next
4272 node. In workloads that involve wait-for graph loops this
4273 has proven to be a more efficient strategy [citation missing].
4274 */
4275 while ((ticket= granted_it++))
4276 {
4277 /* Filter out edges that point to the same node. */
4278 if (ticket->get_ctx() != src_ctx &&
4279 ticket->is_incompatible_when_granted(waiting_ticket->get_type()) &&
4280 gvisitor->inspect_edge(ticket->get_ctx()))
4281 {
4282 goto end_leave_node;
4283 }
4284 }
4285
4286 while ((ticket= waiting_it++))
4287 {
4288 /* Filter out edges that point to the same node. */
4289 if (ticket->get_ctx() != src_ctx &&
4290 ticket->is_incompatible_when_waiting(waiting_ticket->get_type()) &&
4291 gvisitor->inspect_edge(ticket->get_ctx()))
4292 {
4293 goto end_leave_node;
4294 }
4295 }
4296
4297 /* Recurse and inspect all adjacent nodes. */
4298 granted_it.rewind();
4299 while ((ticket= granted_it++))
4300 {
4301 if (ticket->get_ctx() != src_ctx &&
4302 ticket->is_incompatible_when_granted(waiting_ticket->get_type()) &&
4303 ticket->get_ctx()->visit_subgraph(gvisitor))
4304 {
4305 goto end_leave_node;
4306 }
4307 }
4308
4309 waiting_it.rewind();
4310 while ((ticket= waiting_it++))
4311 {
4312 if (ticket->get_ctx() != src_ctx &&
4313 ticket->is_incompatible_when_waiting(waiting_ticket->get_type()) &&
4314 ticket->get_ctx()->visit_subgraph(gvisitor))
4315 {
4316 goto end_leave_node;
4317 }
4318 }
4319
4320 result= FALSE;
4321
4322 end_leave_node:
4323 gvisitor->leave_node(src_ctx);
4324
4325 end:
4326 mysql_prlock_unlock(&m_rwlock);
4327 return result;
4328 }
4329
4330
4331 /**
4332 Traverse a portion of wait-for graph which is reachable
4333 through the edge represented by this ticket and search
4334 for deadlocks.
4335
4336 @retval TRUE A deadlock is found. A pointer to deadlock
4337 victim is saved in the visitor.
4338 @retval FALSE
4339 */
4340
accept_visitor(MDL_wait_for_graph_visitor * gvisitor)4341 bool MDL_ticket::accept_visitor(MDL_wait_for_graph_visitor *gvisitor)
4342 {
4343 return m_lock->visit_subgraph(this, gvisitor);
4344 }
4345
4346
4347 /**
4348 A fragment of recursive traversal of the wait-for graph of
4349 MDL contexts in the server in search for deadlocks.
4350 Assume this MDL context is a node in the wait-for graph,
4351 and direct the visitor to all adjacent nodes. As long
4352 as the starting node is remembered in the visitor, a
4353 deadlock is found when the same node is visited twice.
4354 One MDL context is connected to another in the wait-for
4355 graph if it waits on a resource that is held by the other
4356 context.
4357
4358 @retval TRUE A deadlock is found. A pointer to deadlock
4359 victim is saved in the visitor.
4360 @retval FALSE
4361 */
4362
visit_subgraph(MDL_wait_for_graph_visitor * gvisitor)4363 bool MDL_context::visit_subgraph(MDL_wait_for_graph_visitor *gvisitor)
4364 {
4365 bool result= FALSE;
4366
4367 mysql_prlock_rdlock(&m_LOCK_waiting_for);
4368
4369 if (m_waiting_for)
4370 result= m_waiting_for->accept_visitor(gvisitor);
4371
4372 mysql_prlock_unlock(&m_LOCK_waiting_for);
4373
4374 return result;
4375 }
4376
4377
4378 /**
4379 Try to find a deadlock. This function produces no errors.
4380
4381 @note If during deadlock resolution context which performs deadlock
4382 detection is chosen as a victim it will be informed about the
4383 fact by setting VICTIM status to its wait slot.
4384 */
4385
find_deadlock()4386 void MDL_context::find_deadlock()
4387 {
4388 while (1)
4389 {
4390 /*
4391 The fact that we use fresh instance of gvisitor for each
4392 search performed by find_deadlock() below is important,
4393 the code responsible for victim selection relies on this.
4394 */
4395 Deadlock_detection_visitor dvisitor(this);
4396 MDL_context *victim;
4397
4398 if (! visit_subgraph(&dvisitor))
4399 {
4400 /* No deadlocks are found! */
4401 break;
4402 }
4403
4404 victim= dvisitor.get_victim();
4405
4406 /*
4407 Failure to change status of the victim is OK as it means
4408 that the victim has received some other message and is
4409 about to stop its waiting/to break deadlock loop.
4410 Even when the initiator of the deadlock search is
4411 chosen the victim, we need to set the respective wait
4412 result in order to "close" it for any attempt to
4413 schedule the request.
4414 This is needed to avoid a possible race during
4415 cleanup in case when the lock request on which the
4416 context was waiting is concurrently satisfied.
4417 */
4418 (void) victim->m_wait.set_status(MDL_wait::VICTIM);
4419 victim->unlock_deadlock_victim();
4420
4421 if (victim == this)
4422 break;
4423 /*
4424 After adding a new edge to the waiting graph we found that it
4425 creates a loop (i.e. there is a deadlock). We decided to destroy
4426 this loop by removing an edge, but not the one that we added.
4427 Since this doesn't guarantee that all loops created by addition
4428 of the new edge are destroyed, we have to repeat the search.
4429 */
4430 }
4431 }
4432
4433
4434 /**
4435 Release lock.
4436
4437 @param duration Lock duration.
4438 @param ticket Ticket for lock to be released.
4439
4440 */
4441
release_lock(enum_mdl_duration duration,MDL_ticket * ticket)4442 void MDL_context::release_lock(enum_mdl_duration duration, MDL_ticket *ticket)
4443 {
4444 MDL_lock *lock= ticket->m_lock;
4445 MDL_key key_for_hton;
4446 DBUG_ENTER("MDL_context::release_lock");
4447 DBUG_PRINT("enter", ("db=%s name=%s", lock->key.db_name(),
4448 lock->key.name()));
4449
4450 assert(this == ticket->get_ctx());
4451 mysql_mutex_assert_not_owner(&LOCK_open);
4452
4453
4454 /*
4455 If lock we are about to release requires post-release notification
4456 of SEs, we need to save its MDL_key on stack. This is necessary to
4457 be able to pass this key to SEs after corresponding MDL_lock object
4458 might be freed as result of lock release.
4459 */
4460 if (ticket->m_hton_notified)
4461 key_for_hton.mdl_key_init(&lock->key);
4462
4463 if (ticket->m_is_fast_path)
4464 {
4465 /*
4466 We are releasing ticket which represents lock request which was
4467 satisfied using "fast path". We can use "fast path" release
4468 algorithm of release for it as well.
4469 */
4470 MDL_lock::fast_path_state_t unobtrusive_lock_increment=
4471 lock->get_unobtrusive_lock_increment(ticket->get_type());
4472 bool is_singleton= mdl_locks.is_lock_object_singleton(&lock->key);
4473
4474 /* We should not have "fast path" tickets for "obtrusive" lock types. */
4475 assert(unobtrusive_lock_increment != 0);
4476
4477 /*
4478 We need decrement part of m_fast_path_state which holds number of
4479 acquired "fast path" locks of this type. This needs to be done
4480 by atomic compare-and-swap.
4481
4482 The same atomic compare-and-swap needs to check:
4483
4484 *) If HAS_OBSTRUSIVE flag is set. In this case we need to acquire
4485 MDL_lock::m_rwlock before changing m_fast_path_state. This is
4486 needed to enforce invariant [INV1] and also because we might
4487 have to atomically wake-up some waiters for our "unobtrusive"
4488 lock to go away.
4489 *) If we are about to release last "fast path" lock and there
4490 are no "slow path" locks. In this case we need to count
4491 MDL_lock object as unused and maybe even delete some
4492 unused MDL_lock objects eventually.
4493
4494 Similarly to the case with "fast path" acquisition it is OK to
4495 perform ordinary read of MDL_lock::m_fast_path_state as correctness
4496 of value returned by it will be validated by atomic compare-and-swap.
4497 Again, in theory, this algorithm will work correctly if the read will
4498 return random values.
4499 */
4500 MDL_lock::fast_path_state_t old_state= lock->m_fast_path_state;
4501 bool last_use;
4502
4503 do
4504 {
4505 if (old_state & MDL_lock::HAS_OBTRUSIVE)
4506 {
4507 mysql_prlock_wrlock(&lock->m_rwlock);
4508 /*
4509 It is possible that obtrusive lock has gone away since we have
4510 read m_fast_path_state value. This means that there is possibility
4511 that there are no "slow path" locks (HAS_SLOW_PATH is not set) and
4512 we are about to release last "fast path" lock. In this case MDL_lock
4513 will become unused and needs to be counted as such eventually.
4514 */
4515 last_use= (lock->fast_path_state_add(-unobtrusive_lock_increment) ==
4516 unobtrusive_lock_increment);
4517 /*
4518 There might be some lock requests waiting for ticket being released
4519 to go away. Since this is "fast path" ticket it represents
4520 "unobtrusive" type of lock. In this case if there are any waiters
4521 for it there should be "obtrusive" type of request among them.
4522 */
4523 if (lock->m_obtrusive_locks_granted_waiting_count)
4524 lock->reschedule_waiters();
4525 mysql_prlock_unlock(&lock->m_rwlock);
4526 goto end_fast_path;
4527 }
4528 /*
4529 If there are no "slow path" locks (HAS_SLOW_PATH is not set) and
4530 we are about to release last "fast path" lock - MDL_lock object
4531 will become unused and needs to be counted as such.
4532 */
4533 last_use= (old_state == unobtrusive_lock_increment);
4534 }
4535 while (! lock->fast_path_state_cas(&old_state,
4536 old_state - unobtrusive_lock_increment));
4537
4538 end_fast_path:
4539 /* Don't count singleton MDL_lock objects as unused. */
4540 if (last_use && ! is_singleton)
4541 mdl_locks.lock_object_unused(this, m_pins);
4542 }
4543 else
4544 {
4545 /*
4546 Lock request represented by ticket was acquired using "slow path"
4547 or ticket was materialized later. We need to use "slow path" release.
4548 */
4549 lock->remove_ticket(this, m_pins, &MDL_lock::m_granted, ticket);
4550 }
4551
4552 m_tickets[duration].remove(ticket);
4553
4554 if (ticket->m_hton_notified)
4555 {
4556 mysql_mdl_set_status(ticket->m_psi, MDL_ticket::POST_RELEASE_NOTIFY);
4557 m_owner->notify_hton_post_release_exclusive(&key_for_hton);
4558 }
4559
4560 MDL_ticket::destroy(ticket);
4561
4562 DBUG_VOID_RETURN;
4563 }
4564
4565
4566 /**
4567 Release lock with explicit duration.
4568
4569 @param ticket Ticket for lock to be released.
4570
4571 */
4572
release_lock(MDL_ticket * ticket)4573 void MDL_context::release_lock(MDL_ticket *ticket)
4574 {
4575 assert(ticket->m_duration == MDL_EXPLICIT);
4576
4577 release_lock(MDL_EXPLICIT, ticket);
4578 }
4579
4580
4581 /**
4582 Release all locks associated with the context. If the sentinel
4583 is not NULL, do not release locks stored in the list after and
4584 including the sentinel.
4585
4586 Statement and transactional locks are added to the beginning of
4587 the corresponding lists, i.e. stored in reverse temporal order.
4588 This allows to employ this function to:
4589 - back off in case of a lock conflict.
4590 - release all locks in the end of a statement or transaction
4591 - rollback to a savepoint.
4592 */
4593
release_locks_stored_before(enum_mdl_duration duration,MDL_ticket * sentinel)4594 void MDL_context::release_locks_stored_before(enum_mdl_duration duration,
4595 MDL_ticket *sentinel)
4596 {
4597 MDL_ticket *ticket;
4598 Ticket_iterator it(m_tickets[duration]);
4599 DBUG_ENTER("MDL_context::release_locks_stored_before");
4600
4601 if (m_tickets[duration].is_empty())
4602 DBUG_VOID_RETURN;
4603
4604 while ((ticket= it++) && ticket != sentinel)
4605 {
4606 DBUG_PRINT("info", ("found lock to release ticket=%p", ticket));
4607 release_lock(duration, ticket);
4608 }
4609
4610 DBUG_VOID_RETURN;
4611 }
4612
4613
4614 #ifdef WITH_WSREP
release_explicit_locks()4615 void MDL_context::release_explicit_locks()
4616 {
4617 release_locks_stored_before(MDL_EXPLICIT, NULL);
4618 }
4619 #endif
4620 /**
4621 Release all explicit locks in the context which correspond to the
4622 same name/object as this lock request.
4623
4624 @param name One of the locks for the name/object for which all
4625 locks should be released.
4626 */
4627
release_all_locks_for_name(MDL_ticket * name)4628 void MDL_context::release_all_locks_for_name(MDL_ticket *name)
4629 {
4630 /* Use MDL_ticket::m_lock to identify other locks for the same object. */
4631 MDL_lock *lock= name->m_lock;
4632
4633 /* Remove matching lock tickets from the context. */
4634 MDL_ticket *ticket;
4635 Ticket_iterator it_ticket(m_tickets[MDL_EXPLICIT]);
4636
4637 while ((ticket= it_ticket++))
4638 {
4639 assert(ticket->m_lock);
4640 if (ticket->m_lock == lock)
4641 release_lock(MDL_EXPLICIT, ticket);
4642 }
4643 }
4644
4645
4646 /**
4647 Release all explicit locks in the context for which the release()
4648 method of the provided visitor evalates to true.
4649
4650 @param visitor Object to ask if the lock should be released.
4651 */
4652
release_locks(MDL_release_locks_visitor * visitor)4653 void MDL_context::release_locks(MDL_release_locks_visitor *visitor)
4654 {
4655 /* Remove matching lock tickets from the context. */
4656 MDL_ticket *ticket;
4657 Ticket_iterator it_ticket(m_tickets[MDL_EXPLICIT]);
4658
4659 while ((ticket= it_ticket++))
4660 {
4661 assert(ticket->m_lock);
4662 if (visitor->release(ticket))
4663 release_lock(MDL_EXPLICIT, ticket);
4664 }
4665 }
4666
4667
4668 /**
4669 Downgrade an EXCLUSIVE or SHARED_NO_WRITE lock to shared metadata lock.
4670
4671 @param type Type of lock to which exclusive lock should be downgraded.
4672 */
4673
downgrade_lock(enum_mdl_type new_type)4674 void MDL_ticket::downgrade_lock(enum_mdl_type new_type)
4675 {
4676 bool new_type_is_unobtrusive;
4677 mysql_mutex_assert_not_owner(&LOCK_open);
4678
4679 /*
4680 Do nothing if already downgraded. Used when we FLUSH TABLE under
4681 LOCK TABLES and a table is listed twice in LOCK TABLES list.
4682 Note that this code might even try to "downgrade" a weak lock
4683 (e.g. SW) to a stronger one (e.g SNRW). So we can't even assert
4684 here that target lock is weaker than existing lock.
4685 */
4686 if (m_type == new_type || !has_stronger_or_equal_type(new_type))
4687 return;
4688
4689 /* Only allow downgrade from EXCLUSIVE and SHARED_NO_WRITE. */
4690 assert(m_type == MDL_EXCLUSIVE ||
4691 m_type == MDL_SHARED_NO_WRITE);
4692
4693 /* Below we assume that we always downgrade "obtrusive" locks. */
4694 assert(m_lock->is_obtrusive_lock(m_type));
4695
4696 new_type_is_unobtrusive= ! m_lock->is_obtrusive_lock(new_type);
4697
4698 mysql_prlock_wrlock(&m_lock->m_rwlock);
4699 /*
4700 To update state of MDL_lock object correctly we need to temporarily
4701 exclude ticket from the granted queue and then include it back.
4702
4703 Since we downgrade only "obtrusive" locks we can always assume that the
4704 ticket for the lock being downgraded is a "slow path" ticket.
4705 If we are downgrading to non-"obtrusive" lock we also should decrement
4706 counter of waiting and granted "obtrusive" locks.
4707 */
4708 m_lock->m_granted.remove_ticket(this);
4709 if (new_type_is_unobtrusive)
4710 {
4711 if ((--m_lock->m_obtrusive_locks_granted_waiting_count) == 0)
4712 {
4713 /*
4714 We are downgrading the last "obtrusive" lock. So we need to clear
4715 HAS_OBTRUSIVE flag.
4716 Note that it doesn't matter that we do this before including ticket
4717 to MDL_lock::m_granted list. Threads requesting "obtrusive" locks
4718 won't see this until MDL_lock::m_rwlock is released. And threads
4719 requesting "unobtrusive" locks don't care about this ticket.
4720 */
4721 MDL_lock::fast_path_state_t old_state= m_lock->m_fast_path_state;
4722 while (! m_lock->fast_path_state_cas(&old_state,
4723 old_state & ~MDL_lock::HAS_OBTRUSIVE))
4724 { }
4725 }
4726 }
4727 m_type= new_type;
4728 m_lock->m_granted.add_ticket(this);
4729 m_lock->reschedule_waiters();
4730 mysql_prlock_unlock(&m_lock->m_rwlock);
4731
4732 /*
4733 When we downgrade X lock for which SEs were notified about lock
4734 acquisition we treat situation in the same way as lock release
4735 (from SEs notification point of view).
4736 */
4737 if (m_hton_notified)
4738 {
4739 mysql_mdl_set_status(m_psi, MDL_ticket::POST_RELEASE_NOTIFY);
4740 m_ctx->get_owner()->notify_hton_post_release_exclusive(&m_lock->key);
4741 m_hton_notified= false;
4742 mysql_mdl_set_status(m_psi, MDL_ticket::GRANTED);
4743 }
4744 }
4745
4746
4747 /**
4748 Auxiliary function which allows to check if we have some kind of lock on
4749 a object. Returns TRUE if we have a lock of an equal to given or stronger
4750 type.
4751
4752 @param mdl_namespace Id of object namespace
4753 @param db Name of the database
4754 @param name Name of the object
4755 @param mdl_type Lock type. Pass in the weakest type to find
4756 out if there is at least some lock.
4757
4758 @return TRUE if current context contains satisfied lock for the object,
4759 FALSE otherwise.
4760 */
4761
4762 bool
owns_equal_or_stronger_lock(MDL_key::enum_mdl_namespace mdl_namespace,const char * db,const char * name,enum_mdl_type mdl_type)4763 MDL_context::owns_equal_or_stronger_lock(
4764 MDL_key::enum_mdl_namespace mdl_namespace,
4765 const char *db, const char *name,
4766 enum_mdl_type mdl_type)
4767 {
4768 MDL_request mdl_request;
4769 enum_mdl_duration not_unused;
4770 /* We don't care about exact duration of lock here. */
4771 MDL_REQUEST_INIT(&mdl_request,
4772 mdl_namespace, db, name, mdl_type, MDL_TRANSACTION);
4773 MDL_ticket *ticket= find_ticket(&mdl_request, ¬_unused);
4774
4775 assert(ticket == NULL || ticket->m_lock);
4776
4777 return ticket;
4778 }
4779
4780
4781 /**
4782 Find the first context which owns the lock and inspect it by
4783 calling MDL_context_visitor::visit_context() method.
4784
4785 @return True in case error (e.g. OOM). False otherwise. There
4786 is no guarantee that owner was found in either case.
4787 @note This method only works properly for locks which were
4788 acquired using "slow" path.
4789 */
4790
find_lock_owner(const MDL_key * mdl_key,MDL_context_visitor * visitor)4791 bool MDL_context::find_lock_owner(const MDL_key *mdl_key,
4792 MDL_context_visitor *visitor)
4793 {
4794 MDL_lock *lock;
4795 MDL_context *owner;
4796 bool pinned;
4797
4798 if (fix_pins())
4799 return true;
4800
4801 retry:
4802 if ((lock= mdl_locks.find(m_pins, mdl_key, &pinned)) == MY_ERRPTR)
4803 return true;
4804
4805 /* No MDL_lock object, no owner, nothing to visit. */
4806 if (lock == NULL)
4807 return false;
4808
4809 mysql_prlock_rdlock(&lock->m_rwlock);
4810
4811 if (lock->m_fast_path_state & MDL_lock::IS_DESTROYED)
4812 {
4813 mysql_prlock_unlock(&lock->m_rwlock);
4814 if (pinned)
4815 lf_hash_search_unpin(m_pins);
4816 DEBUG_SYNC(get_thd(), "mdl_find_lock_owner_is_destroyed");
4817 goto retry;
4818 }
4819
4820 if (pinned)
4821 lf_hash_search_unpin(m_pins);
4822
4823 if ((owner= lock->get_lock_owner()))
4824 visitor->visit_context(owner);
4825
4826 mysql_prlock_unlock(&lock->m_rwlock);
4827
4828 return false;
4829 }
4830
4831
4832 /**
4833 Check if we have any pending locks which conflict with existing shared lock.
4834
4835 @pre The ticket must match an acquired lock.
4836
4837 @return TRUE if there is a conflicting lock request, FALSE otherwise.
4838 */
4839
has_pending_conflicting_lock() const4840 bool MDL_ticket::has_pending_conflicting_lock() const
4841 {
4842 return m_lock->has_pending_conflicting_lock(m_type);
4843 }
4844
4845 /** Return a key identifying this lock. */
4846
get_key() const4847 const MDL_key *MDL_ticket::get_key() const
4848 {
4849 return &m_lock->key;
4850 }
4851
4852 /**
4853 Releases metadata locks that were acquired after a specific savepoint.
4854
4855 @note Used to release tickets acquired during a savepoint unit.
4856 @note It's safe to iterate and unlock any locks after taken after this
4857 savepoint because other statements that take other special locks
4858 cause a implicit commit (ie LOCK TABLES).
4859 */
4860
rollback_to_savepoint(const MDL_savepoint & mdl_savepoint)4861 void MDL_context::rollback_to_savepoint(const MDL_savepoint &mdl_savepoint)
4862 {
4863 DBUG_ENTER("MDL_context::rollback_to_savepoint");
4864
4865 /* If savepoint is NULL, it is from the start of the transaction. */
4866 release_locks_stored_before(MDL_STATEMENT, mdl_savepoint.m_stmt_ticket);
4867 release_locks_stored_before(MDL_TRANSACTION, mdl_savepoint.m_trans_ticket);
4868
4869 DBUG_VOID_RETURN;
4870 }
4871
4872
4873 /**
4874 Release locks acquired by normal statements (SELECT, UPDATE,
4875 DELETE, etc) in the course of a transaction. Do not release
4876 HANDLER locks, if there are any.
4877
4878 This method is used at the end of a transaction, in
4879 implementation of COMMIT (implicit or explicit) and ROLLBACK.
4880 */
4881
release_transactional_locks()4882 void MDL_context::release_transactional_locks()
4883 {
4884 DBUG_ENTER("MDL_context::release_transactional_locks");
4885 release_locks_stored_before(MDL_STATEMENT, NULL);
4886 release_locks_stored_before(MDL_TRANSACTION, NULL);
4887 DBUG_VOID_RETURN;
4888 }
4889
4890
release_statement_locks()4891 void MDL_context::release_statement_locks()
4892 {
4893 DBUG_ENTER("MDL_context::release_transactional_locks");
4894 release_locks_stored_before(MDL_STATEMENT, NULL);
4895 DBUG_VOID_RETURN;
4896 }
4897
4898
4899 /**
4900 Does this savepoint have this lock?
4901
4902 @retval TRUE The ticket is older than the savepoint or
4903 is an LT, HA or GLR ticket. Thus it belongs
4904 to the savepoint or has explicit duration.
4905 @retval FALSE The ticket is newer than the savepoint.
4906 and is not an LT, HA or GLR ticket.
4907 */
4908
has_lock(const MDL_savepoint & mdl_savepoint,MDL_ticket * mdl_ticket)4909 bool MDL_context::has_lock(const MDL_savepoint &mdl_savepoint,
4910 MDL_ticket *mdl_ticket)
4911 {
4912 MDL_ticket *ticket;
4913 /* Start from the beginning, most likely mdl_ticket's been just acquired. */
4914 MDL_context::Ticket_iterator s_it(m_tickets[MDL_STATEMENT]);
4915 MDL_context::Ticket_iterator t_it(m_tickets[MDL_TRANSACTION]);
4916
4917 while ((ticket= s_it++) && ticket != mdl_savepoint.m_stmt_ticket)
4918 {
4919 if (ticket == mdl_ticket)
4920 return FALSE;
4921 }
4922
4923 while ((ticket= t_it++) && ticket != mdl_savepoint.m_trans_ticket)
4924 {
4925 if (ticket == mdl_ticket)
4926 return FALSE;
4927 }
4928 return TRUE;
4929 }
4930
4931
4932 /**
4933 Does this context have an lock of the given namespace?
4934
4935 @retval true At least one lock of given namespace held
4936 @retval false No locks of the given namespace held
4937 */
4938
has_locks(MDL_key::enum_mdl_namespace mdl_namespace) const4939 bool MDL_context::has_locks(MDL_key::enum_mdl_namespace mdl_namespace) const
4940 {
4941 MDL_ticket *ticket;
4942
4943 for (int i=0; i < MDL_DURATION_END; i++)
4944 {
4945 enum_mdl_duration duration= static_cast<enum_mdl_duration>(i);
4946
4947 Ticket_iterator it(m_tickets[duration]);
4948 while ((ticket= it++))
4949 {
4950 if (ticket->get_key()->mdl_namespace() == mdl_namespace)
4951 return true;
4952 }
4953 }
4954 return false;
4955 }
4956
4957
4958 /**
4959 Do we hold any locks which are possibly being waited
4960 for by another MDL_context?
4961
4962 @retval TRUE A lock being 'waited_for' was found.
4963 @retval FALSE No one waits for the lock(s) we hold.
4964
4965 @note Should only be called from the thread which
4966 owns the MDL_context
4967 */
4968
has_locks_waited_for() const4969 bool MDL_context::has_locks_waited_for() const
4970 {
4971 MDL_ticket *ticket;
4972
4973 for (int i=0; i < MDL_DURATION_END; i++)
4974 {
4975 const enum_mdl_duration duration= static_cast<enum_mdl_duration>(i);
4976 Ticket_iterator it(m_tickets[duration]);
4977 while ((ticket= it++))
4978 {
4979 MDL_lock *const lock= ticket->m_lock;
4980
4981 mysql_prlock_rdlock(&lock->m_rwlock);
4982 const bool has_waiters= !lock->m_waiting.is_empty();
4983 mysql_prlock_unlock(&lock->m_rwlock);
4984
4985 if (!has_waiters)
4986 return true;
4987 }
4988 }
4989 return false;
4990 }
4991
4992
4993
4994 /**
4995 Change lock duration for transactional lock.
4996
4997 @param ticket Ticket representing lock.
4998 @param duration Lock duration to be set.
4999
5000 @note This method only supports changing duration of
5001 transactional lock to some other duration.
5002 */
5003
set_lock_duration(MDL_ticket * mdl_ticket,enum_mdl_duration duration)5004 void MDL_context::set_lock_duration(MDL_ticket *mdl_ticket,
5005 enum_mdl_duration duration)
5006 {
5007 assert(mdl_ticket->m_duration == MDL_TRANSACTION &&
5008 duration != MDL_TRANSACTION);
5009
5010 m_tickets[MDL_TRANSACTION].remove(mdl_ticket);
5011 m_tickets[duration].push_front(mdl_ticket);
5012 #ifndef NDEBUG
5013 mdl_ticket->m_duration= duration;
5014 #endif
5015 }
5016
5017
5018 /**
5019 Set explicit duration for all locks in the context.
5020 */
5021
set_explicit_duration_for_all_locks()5022 void MDL_context::set_explicit_duration_for_all_locks()
5023 {
5024 int i;
5025 MDL_ticket *ticket;
5026
5027 /*
5028 In the most common case when this function is called list
5029 of transactional locks is bigger than list of locks with
5030 explicit duration. So we start by swapping these two lists
5031 and then move elements from new list of transactional
5032 locks and list of statement locks to list of locks with
5033 explicit duration.
5034 */
5035
5036 m_tickets[MDL_EXPLICIT].swap(m_tickets[MDL_TRANSACTION]);
5037
5038 for (i= 0; i < MDL_EXPLICIT; i++)
5039 {
5040 Ticket_iterator it_ticket(m_tickets[i]);
5041
5042 while ((ticket= it_ticket++))
5043 {
5044 m_tickets[i].remove(ticket);
5045 m_tickets[MDL_EXPLICIT].push_front(ticket);
5046 }
5047 }
5048
5049 #ifndef NDEBUG
5050 Ticket_iterator exp_it(m_tickets[MDL_EXPLICIT]);
5051
5052 while ((ticket= exp_it++))
5053 ticket->m_duration= MDL_EXPLICIT;
5054 #endif
5055 }
5056
5057
5058 /**
5059 Set transactional duration for all locks in the context.
5060 */
5061
set_transaction_duration_for_all_locks()5062 void MDL_context::set_transaction_duration_for_all_locks()
5063 {
5064 MDL_ticket *ticket;
5065
5066 /*
5067 In the most common case when this function is called list
5068 of explicit locks is bigger than two other lists (in fact,
5069 list of statement locks is always empty). So we start by
5070 swapping list of explicit and transactional locks and then
5071 move contents of new list of explicit locks to list of
5072 locks with transactional duration.
5073 */
5074
5075 assert(m_tickets[MDL_STATEMENT].is_empty());
5076
5077 m_tickets[MDL_TRANSACTION].swap(m_tickets[MDL_EXPLICIT]);
5078
5079 Ticket_iterator it_ticket(m_tickets[MDL_EXPLICIT]);
5080
5081 while ((ticket= it_ticket++))
5082 {
5083 m_tickets[MDL_EXPLICIT].remove(ticket);
5084 m_tickets[MDL_TRANSACTION].push_front(ticket);
5085 }
5086
5087 #ifndef NDEBUG
5088 Ticket_iterator trans_it(m_tickets[MDL_TRANSACTION]);
5089
5090 while ((ticket= trans_it++))
5091 ticket->m_duration= MDL_TRANSACTION;
5092 #endif
5093 }
5094 #ifdef WITH_WSREP
wsrep_report(bool debug)5095 void MDL_ticket::wsrep_report(bool debug)
5096 {
5097 if (debug)
5098 {
5099 WSREP_DEBUG("MDL ticket: type: %s space: %s db: %s name: %s",
5100 (get_type() == MDL_INTENTION_EXCLUSIVE) ? "intention exclusive" :
5101 ((get_type() == MDL_SHARED) ? "shared" :
5102 ((get_type() == MDL_SHARED_HIGH_PRIO ? "shared high prio" :
5103 ((get_type() == MDL_SHARED_READ) ? "shared read" :
5104 ((get_type() == MDL_SHARED_WRITE) ? "shared write" :
5105 ((get_type() == MDL_SHARED_NO_WRITE) ? "shared no write" :
5106 ((get_type() == MDL_SHARED_NO_READ_WRITE) ? "shared no read write" :
5107 ((get_type() == MDL_EXCLUSIVE) ? "exclusive" :
5108 "UNKNOWN")))))))),
5109 (m_lock->key.mdl_namespace() == MDL_key::GLOBAL) ? "GLOBAL" :
5110 ((m_lock->key.mdl_namespace() == MDL_key::SCHEMA) ? "SCHEMA" :
5111 ((m_lock->key.mdl_namespace() == MDL_key::TABLE) ? "TABLE" :
5112 ((m_lock->key.mdl_namespace() == MDL_key::TABLE) ? "FUNCTION" :
5113 ((m_lock->key.mdl_namespace() == MDL_key::TABLE) ? "PROCEDURE" :
5114 ((m_lock->key.mdl_namespace() == MDL_key::TABLE) ? "TRIGGER" :
5115 ((m_lock->key.mdl_namespace() == MDL_key::TABLE) ? "EVENT" :
5116 ((m_lock->key.mdl_namespace() == MDL_key::COMMIT) ? "COMMIT" :
5117 (char *)"UNKNOWN"))))))),
5118 m_lock->key.db_name(),
5119 m_lock->key.name());
5120 }
5121 }
wsrep_has_explicit_locks()5122 bool MDL_context::wsrep_has_explicit_locks()
5123 {
5124 MDL_ticket *ticket = NULL;
5125
5126 Ticket_iterator it(m_tickets[MDL_EXPLICIT]);
5127
5128 while ((ticket = it++))
5129 {
5130 return true;
5131 }
5132
5133 return false;
5134 }
5135
5136 #endif /* WITH_WSREP */
5137