1 /* Copyright (c) 2014, 2020, Oracle and/or its affiliates. All rights reserved. 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 21 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ 22 23 #ifndef PLUGIN_UTILS_INCLUDED 24 #define PLUGIN_UTILS_INCLUDED 25 26 #include <errno.h> 27 #include <mysql/group_replication_priv.h> 28 #include <stddef.h> 29 #include <map> 30 #include <queue> 31 #include <string> 32 #include <vector> 33 34 #include "my_dbug.h" 35 #include "my_systime.h" 36 #include "plugin/group_replication/include/plugin_psi.h" 37 38 void log_primary_member_details(); 39 40 void abort_plugin_process(const char *message); 41 42 struct thread_state { 43 /** 44 * @enum thread_state_enum 45 * @brief Maintains thread status 46 */ 47 enum thread_state_enum { 48 THREAD_NONE = 0, /**< THREAD_NOT_CREATED */ 49 THREAD_CREATED, /**< THREAD_CREATED */ 50 THREAD_INIT, /**< THREAD_INIT */ 51 52 THREAD_RUNNING, /**< THREAD_RUNNING */ 53 54 THREAD_TERMINATED, /**< THREAD_EXIT */ 55 THREAD_END /**< END OF ENUM */ 56 }; 57 58 private: 59 thread_state_enum thread_state_var; 60 61 public: thread_statethread_state62 thread_state() : thread_state_var(thread_state_enum::THREAD_NONE) {} 63 set_runningthread_state64 void set_running() { thread_state_var = thread_state_enum::THREAD_RUNNING; } 65 set_terminatedthread_state66 void set_terminated() { 67 thread_state_var = thread_state_enum::THREAD_TERMINATED; 68 } 69 set_initializedthread_state70 void set_initialized() { thread_state_var = thread_state_enum::THREAD_INIT; } 71 set_createdthread_state72 void set_created() { thread_state_var = thread_state_enum::THREAD_CREATED; } 73 is_initializedthread_state74 bool is_initialized() const { 75 return ((thread_state_var >= thread_state_enum::THREAD_INIT) && 76 (thread_state_var < thread_state_enum::THREAD_TERMINATED)); 77 } 78 is_runningthread_state79 bool is_running() const { 80 return thread_state_var == thread_state_enum::THREAD_RUNNING; 81 } 82 is_alive_not_runningthread_state83 bool is_alive_not_running() const { 84 return thread_state_var < thread_state_enum::THREAD_RUNNING; 85 } 86 is_thread_alivethread_state87 bool is_thread_alive() const { 88 return ((thread_state_var >= thread_state_enum::THREAD_CREATED) && 89 (thread_state_var < thread_state_enum::THREAD_TERMINATED)); 90 } 91 is_thread_deadthread_state92 bool is_thread_dead() const { return !is_thread_alive(); } 93 }; 94 95 class Blocked_transaction_handler { 96 public: 97 Blocked_transaction_handler(); 98 virtual ~Blocked_transaction_handler(); 99 100 /** 101 This method instructs all local transactions to rollback when certification 102 is no longer possible. 103 */ 104 void unblock_waiting_transactions(); 105 106 private: 107 /* The lock that disallows concurrent method executions */ 108 mysql_mutex_t unblocking_process_lock; 109 }; 110 111 /** 112 @class Synchronized_queue_interface 113 114 Interface that defines a queue protected against multi thread access. 115 116 */ 117 118 template <typename T> 119 class Synchronized_queue_interface { 120 public: ~Synchronized_queue_interface()121 virtual ~Synchronized_queue_interface() {} 122 123 /** 124 Checks if the queue is empty 125 @return if is empty 126 @retval true empty 127 @retval false not empty 128 */ 129 virtual bool empty() = 0; 130 131 /** 132 Inserts an element in the queue. 133 Alerts any other thread lock on pop() or front() 134 @param value The value to insert 135 136 @return false, operation always succeeded 137 */ 138 virtual bool push(const T &value) = 0; 139 140 /** 141 Fetches the front of the queue and removes it. 142 @note The method will block if the queue is empty until a element is pushed 143 144 @param out The fetched reference. 145 146 @return false, operation always succeeded 147 */ 148 virtual bool pop(T *out) = 0; 149 150 /** 151 Pops the front of the queue removing it. 152 @note The method will block if the queue is empty until a element is pushed 153 154 @return true if method was aborted, false otherwise 155 */ 156 virtual bool pop() = 0; 157 158 /** 159 Fetches the front of the queue but does not remove it. 160 @note The method will block if the queue is empty until a element is pushed 161 162 @param out The fetched reference. 163 164 @return false, operation always succeeded 165 */ 166 virtual bool front(T *out) = 0; 167 168 /** 169 Checks the queue size 170 @return the size of the queue 171 */ 172 virtual size_t size() = 0; 173 }; 174 175 template <typename T> 176 class Synchronized_queue : public Synchronized_queue_interface<T> { 177 public: Synchronized_queue()178 Synchronized_queue() { 179 mysql_mutex_init(key_GR_LOCK_synchronized_queue, &lock, MY_MUTEX_INIT_FAST); 180 mysql_cond_init(key_GR_COND_synchronized_queue, &cond); 181 } 182 ~Synchronized_queue()183 virtual ~Synchronized_queue() { mysql_mutex_destroy(&lock); } 184 empty()185 bool empty() { 186 bool res = true; 187 mysql_mutex_lock(&lock); 188 res = queue.empty(); 189 mysql_mutex_unlock(&lock); 190 191 return res; 192 } 193 push(const T & value)194 virtual bool push(const T &value) { 195 mysql_mutex_lock(&lock); 196 queue.push(value); 197 mysql_cond_broadcast(&cond); 198 mysql_mutex_unlock(&lock); 199 200 return false; 201 } 202 pop(T * out)203 virtual bool pop(T *out) { 204 *out = NULL; 205 mysql_mutex_lock(&lock); 206 while (queue.empty()) 207 mysql_cond_wait(&cond, &lock); /* purecov: inspected */ 208 *out = queue.front(); 209 queue.pop(); 210 mysql_mutex_unlock(&lock); 211 212 return false; 213 } 214 pop()215 virtual bool pop() { 216 mysql_mutex_lock(&lock); 217 while (queue.empty()) 218 mysql_cond_wait(&cond, &lock); /* purecov: inspected */ 219 queue.pop(); 220 mysql_mutex_unlock(&lock); 221 222 return false; 223 } 224 front(T * out)225 virtual bool front(T *out) { 226 *out = NULL; 227 mysql_mutex_lock(&lock); 228 while (queue.empty()) mysql_cond_wait(&cond, &lock); 229 *out = queue.front(); 230 mysql_mutex_unlock(&lock); 231 232 return false; 233 } 234 size()235 size_t size() { 236 size_t qsize = 0; 237 mysql_mutex_lock(&lock); 238 qsize = queue.size(); 239 mysql_mutex_unlock(&lock); 240 241 return qsize; 242 } 243 244 protected: 245 mysql_mutex_t lock; 246 mysql_cond_t cond; 247 std::queue<T> queue; 248 }; 249 250 /** 251 Abortable synchronized queue extends synchronized queue allowing to 252 abort methods waiting for elements on queue. 253 */ 254 255 template <typename T> 256 class Abortable_synchronized_queue : public Synchronized_queue<T> { 257 public: Abortable_synchronized_queue()258 Abortable_synchronized_queue() : Synchronized_queue<T>(), m_abort(false) {} 259 ~Abortable_synchronized_queue()260 ~Abortable_synchronized_queue() {} 261 262 /** 263 Inserts an element in the queue. 264 Alerts any other thread lock on pop() or front() 265 @note The method will not push if abort was executed. 266 267 @param value The value to insert 268 269 @return false, operation always succeeded 270 */ 271 push(const T & value)272 bool push(const T &value) { 273 bool res = false; 274 mysql_mutex_lock(&this->lock); 275 if (m_abort) { 276 res = true; 277 } else { 278 this->queue.push(value); 279 mysql_cond_broadcast(&this->cond); 280 } 281 282 mysql_mutex_unlock(&this->lock); 283 return res; 284 } 285 286 /** 287 Fetches the front of the queue and removes it. 288 @note The method will block if the queue is empty until a element is pushed 289 or abort is executed 290 291 @param out The fetched reference. 292 293 @return true if method was aborted, false otherwise 294 */ pop(T * out)295 bool pop(T *out) { 296 *out = nullptr; 297 mysql_mutex_lock(&this->lock); 298 while (this->queue.empty() && !m_abort) 299 mysql_cond_wait(&this->cond, &this->lock); /* purecov: inspected */ 300 301 if (!m_abort) { 302 *out = this->queue.front(); 303 this->queue.pop(); 304 } 305 306 const bool result = m_abort; 307 mysql_mutex_unlock(&this->lock); 308 return result; 309 } 310 311 /** 312 Pops the front of the queue removing it. 313 @note The method will block if the queue is empty until a element is pushed 314 or abort is executed 315 316 @return false, operation always succeeded 317 */ pop()318 bool pop() { 319 mysql_mutex_lock(&this->lock); 320 while (this->queue.empty() && !m_abort) 321 mysql_cond_wait(&this->cond, &this->lock); 322 323 if (!m_abort) { 324 this->queue.pop(); 325 } 326 327 const bool result = m_abort; 328 mysql_mutex_unlock(&this->lock); 329 return result; 330 } 331 332 /** 333 Fetches the front of the queue but does not remove it. 334 @note The method will block if the queue is empty until a element is pushed 335 or abort is executed 336 337 @param out The fetched reference. 338 339 @return true if method was aborted, false otherwise 340 */ front(T * out)341 bool front(T *out) { 342 *out = nullptr; 343 mysql_mutex_lock(&this->lock); 344 while (this->queue.empty() && !m_abort) 345 mysql_cond_wait(&this->cond, &this->lock); 346 347 if (!m_abort) { 348 *out = this->queue.front(); 349 } 350 351 const bool result = m_abort; 352 mysql_mutex_unlock(&this->lock); 353 return result; 354 } 355 356 /** 357 Remove all elements, abort current and future waits on retrieving elements 358 from queue. 359 */ abort()360 void abort() { 361 mysql_mutex_lock(&this->lock); 362 while (this->queue.size()) { 363 T elem; 364 elem = this->queue.front(); 365 this->queue.pop(); 366 delete elem; 367 } 368 m_abort = true; 369 mysql_cond_broadcast(&this->cond); 370 mysql_mutex_unlock(&this->lock); 371 } 372 373 private: 374 bool m_abort; 375 }; 376 377 /** 378 Synchronization auxiliary class that allows one or more threads 379 to wait on a given number of requirements. 380 381 Usage: 382 CountDownLatch(count): 383 Create the latch with the number of requirements to wait. 384 wait(): 385 Block until the number of requirements reaches zero. 386 countDown(): 387 Decrease the number of requirements by one. 388 */ 389 class CountDownLatch { 390 public: 391 /** 392 Create the latch with the number of requirements to wait. 393 394 @param count The number of requirements to wait 395 */ CountDownLatch(uint count)396 CountDownLatch(uint count) : count(count), error(false) { 397 mysql_mutex_init(key_GR_LOCK_count_down_latch, &lock, MY_MUTEX_INIT_FAST); 398 mysql_cond_init(key_GR_COND_count_down_latch, &cond); 399 } 400 ~CountDownLatch()401 virtual ~CountDownLatch() { 402 mysql_cond_destroy(&cond); 403 mysql_mutex_destroy(&lock); 404 } 405 406 /** 407 Block until the number of requirements reaches zero. 408 */ 409 void wait(ulong timeout = 0) { 410 mysql_mutex_lock(&lock); 411 412 if (timeout > 0) { 413 ulong time_lapsed = 0; 414 struct timespec abstime; 415 416 while (count > 0 && timeout > time_lapsed) { 417 set_timespec(&abstime, 1); 418 mysql_cond_timedwait(&cond, &lock, &abstime); 419 time_lapsed++; 420 } 421 422 if (count > 0 && timeout == time_lapsed) { 423 error = true; 424 } 425 } else { 426 while (count > 0) mysql_cond_wait(&cond, &lock); 427 } 428 429 mysql_mutex_unlock(&lock); 430 } 431 432 /** 433 Decrease the number of requirements by one. 434 */ countDown()435 void countDown() { 436 mysql_mutex_lock(&lock); 437 --count; 438 if (count == 0) mysql_cond_broadcast(&cond); 439 mysql_mutex_unlock(&lock); 440 } 441 442 /** 443 Get current number requirements. 444 445 @return the number of requirements 446 */ getCount()447 uint getCount() { 448 uint res = 0; 449 mysql_mutex_lock(&lock); 450 res = count; 451 mysql_mutex_unlock(&lock); 452 return res; 453 } 454 455 /** 456 Set error flag, once this latch is release the waiter can check 457 if it was due to a error or due to correct termination. 458 */ set_error()459 void set_error() { error = true; } 460 461 /** 462 Get latch release reason. 463 464 @return true the latch was released due to a error 465 false the latch was released on correct termination 466 */ get_error()467 bool get_error() { return error; } 468 469 private: 470 mysql_mutex_t lock; 471 mysql_cond_t cond; 472 int count; 473 bool error; 474 }; 475 476 /** 477 Ticket register/wait auxiliary class. 478 Usage: 479 registerTicket(k): 480 create a ticket with key k with status ongoing. 481 releaseTicket(k): 482 set ticket with key k status to done. 483 waitTicket(k): 484 wait until ticket with key k status is changed to done. 485 */ 486 template <typename K> 487 class Wait_ticket { 488 public: Wait_ticket()489 Wait_ticket() : blocked(false), waiting(false) { 490 mysql_mutex_init(key_GR_LOCK_wait_ticket, &lock, MY_MUTEX_INIT_FAST); 491 mysql_cond_init(key_GR_COND_wait_ticket, &cond); 492 } 493 ~Wait_ticket()494 virtual ~Wait_ticket() { 495 clear(); 496 mysql_cond_destroy(&cond); 497 mysql_mutex_destroy(&lock); 498 } 499 clear()500 void clear() { 501 mysql_mutex_lock(&lock); 502 DBUG_ASSERT(false == blocked); 503 DBUG_ASSERT(false == waiting); 504 505 for (typename std::map<K, CountDownLatch *>::iterator it = map.begin(); 506 it != map.end(); ++it) 507 delete it->second; /* purecov: inspected */ 508 map.clear(); 509 mysql_mutex_unlock(&lock); 510 } 511 512 /** 513 Check if there are waiting tickets. 514 515 @retval true empty 516 @retval false otherwise 517 */ empty()518 bool empty() { 519 bool result = false; 520 521 mysql_mutex_lock(&lock); 522 result = map.empty(); 523 mysql_mutex_unlock(&lock); 524 525 return result; 526 } 527 528 /** 529 Register ticker with status ongoing. 530 531 @param key The key that identifies the ticket 532 @retval 0 success 533 @retval !=0 key already exists, error on insert or it is blocked 534 */ registerTicket(const K & key)535 int registerTicket(const K &key) { 536 int error = 0; 537 538 mysql_mutex_lock(&lock); 539 540 if (blocked) { 541 mysql_mutex_unlock(&lock); /* purecov: inspected */ 542 return 1; /* purecov: inspected */ 543 } 544 545 typename std::map<K, CountDownLatch *>::iterator it = map.find(key); 546 if (it != map.end()) { 547 mysql_mutex_unlock(&lock); /* purecov: inspected */ 548 return 1; /* purecov: inspected */ 549 } 550 551 CountDownLatch *cdl = new CountDownLatch(1); 552 std::pair<typename std::map<K, CountDownLatch *>::iterator, bool> ret; 553 ret = map.insert(std::pair<K, CountDownLatch *>(key, cdl)); 554 if (ret.second == false) { 555 error = 1; /* purecov: inspected */ 556 delete cdl; /* purecov: inspected */ 557 } 558 559 mysql_mutex_unlock(&lock); 560 return error; 561 } 562 563 /** 564 Wait until ticket status is done. 565 @note The ticket is removed after the wait. 566 567 @param key The key that identifies the ticket 568 @param timeout maximum time in seconds to wait 569 by default is 0, which means no timeout 570 @retval 0 success 571 @retval !=0 key doesn't exist, or the Ticket is blocked 572 */ 573 int waitTicket(const K &key, ulong timeout = 0) { 574 int error = 0; 575 CountDownLatch *cdl = nullptr; 576 577 mysql_mutex_lock(&lock); 578 579 if (blocked) { 580 mysql_mutex_unlock(&lock); /* purecov: inspected */ 581 return 1; /* purecov: inspected */ 582 } 583 584 typename std::map<K, CountDownLatch *>::iterator it = map.find(key); 585 if (it == map.end()) 586 error = 1; 587 else 588 cdl = it->second; 589 mysql_mutex_unlock(&lock); 590 591 if (cdl != nullptr) { 592 cdl->wait(timeout); 593 error = cdl->get_error() ? 1 : 0; 594 595 mysql_mutex_lock(&lock); 596 delete cdl; 597 map.erase(it); 598 599 if (waiting) { 600 if (map.empty()) { 601 mysql_cond_broadcast(&cond); 602 } 603 } 604 mysql_mutex_unlock(&lock); 605 } 606 607 return error; 608 } 609 610 /** 611 Set ticket status to done. 612 613 @param key The key that identifies the ticket 614 @param release_due_to_error Inform the thread waiting that the 615 release is due to a error 616 @retval 0 success 617 @retval !=0 (key doesn't exist) 618 */ 619 int releaseTicket(const K &key, bool release_due_to_error = false) { 620 int error = 0; 621 622 mysql_mutex_lock(&lock); 623 typename std::map<K, CountDownLatch *>::iterator it = map.find(key); 624 if (it == map.end()) 625 error = 1; 626 else { 627 if (release_due_to_error) { 628 it->second->set_error(); 629 } 630 it->second->countDown(); 631 } 632 mysql_mutex_unlock(&lock); 633 634 return error; 635 } 636 637 /** 638 Gets all the waiting keys. 639 640 @param[out] key_list all the keys to return 641 */ get_all_waiting_keys(std::vector<K> & key_list)642 void get_all_waiting_keys(std::vector<K> &key_list) { 643 mysql_mutex_lock(&lock); 644 for (typename std::map<K, CountDownLatch *>::iterator iter = map.begin(); 645 iter != map.end(); ++iter) { 646 K key = iter->first; 647 key_list.push_back(key); 648 } 649 mysql_mutex_unlock(&lock); 650 } 651 652 /** 653 Blocks or unblocks the class from receiving waiting requests. 654 655 @param[in] blocked_flag if the class should block or not 656 */ set_blocked_status(bool blocked_flag)657 void set_blocked_status(bool blocked_flag) { 658 mysql_mutex_lock(&lock); 659 blocked = blocked_flag; 660 mysql_mutex_unlock(&lock); 661 } 662 block_until_empty(int timeout)663 int block_until_empty(int timeout) { 664 mysql_mutex_lock(&lock); 665 waiting = true; 666 while (!map.empty()) { 667 struct timespec abstime; 668 set_timespec(&abstime, 1); 669 #ifndef DBUG_OFF 670 int error = 671 #endif 672 mysql_cond_timedwait(&cond, &lock, &abstime); 673 DBUG_ASSERT(error == ETIMEDOUT || error == 0); 674 if (timeout >= 1) { 675 timeout = timeout - 1; 676 } else if (!map.empty()) { 677 // time out 678 waiting = false; 679 mysql_mutex_unlock(&lock); 680 return 1; 681 } 682 } 683 waiting = false; 684 mysql_mutex_unlock(&lock); 685 return 0; 686 } 687 688 private: 689 mysql_mutex_t lock; 690 mysql_cond_t cond; 691 std::map<K, CountDownLatch *> map; 692 bool blocked; 693 bool waiting; 694 }; 695 696 class Shared_writelock { 697 public: Shared_writelock(Checkable_rwlock * arg)698 Shared_writelock(Checkable_rwlock *arg) 699 : shared_write_lock(arg), write_lock_in_use(false) { 700 DBUG_TRACE; 701 702 DBUG_ASSERT(arg != nullptr); 703 704 mysql_mutex_init(key_GR_LOCK_write_lock_protection, &write_lock, 705 MY_MUTEX_INIT_FAST); 706 mysql_cond_init(key_GR_COND_write_lock_protection, &write_lock_protection); 707 708 return; 709 } 710 ~Shared_writelock()711 virtual ~Shared_writelock() { 712 mysql_mutex_destroy(&write_lock); 713 mysql_cond_destroy(&write_lock_protection); 714 } 715 try_grab_write_lock()716 int try_grab_write_lock() { 717 int res = 0; 718 mysql_mutex_lock(&write_lock); 719 720 if (write_lock_in_use) 721 res = 1; /* purecov: inspected */ 722 else { 723 shared_write_lock->wrlock(); 724 write_lock_in_use = true; 725 } 726 727 mysql_mutex_unlock(&write_lock); 728 return res; 729 } 730 grab_write_lock()731 void grab_write_lock() { 732 mysql_mutex_lock(&write_lock); 733 DBUG_EXECUTE_IF("group_replication_continue_kill_pending_transaction", { 734 const char act[] = "now SIGNAL signal.gr_applier_early_failure"; 735 DBUG_ASSERT(!debug_sync_set_action(current_thd, STRING_WITH_LEN(act))); 736 };); 737 while (write_lock_in_use == true) 738 mysql_cond_wait(&write_lock_protection, &write_lock); 739 740 shared_write_lock->wrlock(); 741 write_lock_in_use = true; 742 mysql_mutex_unlock(&write_lock); 743 } 744 release_write_lock()745 void release_write_lock() { 746 mysql_mutex_lock(&write_lock); 747 shared_write_lock->unlock(); 748 write_lock_in_use = false; 749 mysql_cond_broadcast(&write_lock_protection); 750 mysql_mutex_unlock(&write_lock); 751 } 752 753 /** 754 Grab a read lock only if there is no write lock acquired. 755 756 @retval 0 read lock acquired 757 @retval !=0 there is a write lock acquired 758 */ try_grab_read_lock()759 int try_grab_read_lock() { 760 int res = 0; 761 mysql_mutex_lock(&write_lock); 762 763 if (write_lock_in_use) 764 res = 1; 765 else 766 shared_write_lock->rdlock(); 767 768 mysql_mutex_unlock(&write_lock); 769 return res; 770 } 771 grab_read_lock()772 void grab_read_lock() { shared_write_lock->rdlock(); } 773 release_read_lock()774 void release_read_lock() { shared_write_lock->unlock(); } 775 776 private: 777 Checkable_rwlock *shared_write_lock; 778 mysql_mutex_t write_lock; 779 mysql_cond_t write_lock_protection; 780 bool write_lock_in_use; 781 }; 782 783 class Plugin_waitlock { 784 public: 785 /** 786 Constructor. 787 Instatiate the mutex lock, mutex condition, 788 mutex and condition key. 789 790 @param lock the mutex lock for access to class and condition variables 791 @param cond the condition variable calling thread will wait on 792 @param lock_key mutex instrumentation key 793 @param cond_key cond instrumentation key 794 */ Plugin_waitlock(mysql_mutex_t * lock,mysql_cond_t * cond,PSI_mutex_key lock_key,PSI_cond_key cond_key)795 Plugin_waitlock(mysql_mutex_t *lock, mysql_cond_t *cond, 796 PSI_mutex_key lock_key, PSI_cond_key cond_key) 797 : wait_lock(lock), 798 wait_cond(cond), 799 key_lock(lock_key), 800 key_cond(cond_key), 801 wait_status(false) { 802 DBUG_TRACE; 803 804 mysql_mutex_init(key_lock, wait_lock, MY_MUTEX_INIT_FAST); 805 mysql_cond_init(key_cond, wait_cond); 806 807 return; 808 } 809 810 /** 811 Destructor. 812 Destroys the mutex and condition objects. 813 */ ~Plugin_waitlock()814 virtual ~Plugin_waitlock() { 815 mysql_mutex_destroy(wait_lock); 816 mysql_cond_destroy(wait_cond); 817 } 818 819 /** 820 Set condition to block or unblock the calling threads 821 822 @param[in] status if the thread should be blocked or not 823 */ set_wait_lock(bool status)824 void set_wait_lock(bool status) { 825 mysql_mutex_lock(wait_lock); 826 wait_status = status; 827 mysql_mutex_unlock(wait_lock); 828 } 829 830 /** 831 Blocks the calling thread 832 */ start_waitlock()833 void start_waitlock() { 834 DBUG_TRACE; 835 mysql_mutex_lock(wait_lock); 836 while (wait_status) { 837 DBUG_PRINT("sleep", ("Waiting in Plugin_waitlock::start_waitlock()")); 838 mysql_cond_wait(wait_cond, wait_lock); 839 } 840 mysql_mutex_unlock(wait_lock); 841 return; 842 } 843 844 /** 845 Release the blocked thread 846 */ end_wait_lock()847 void end_wait_lock() { 848 mysql_mutex_lock(wait_lock); 849 wait_status = false; 850 mysql_cond_broadcast(wait_cond); 851 mysql_mutex_unlock(wait_lock); 852 } 853 854 /** 855 Checks whether thread should be blocked 856 857 @retval true thread should be blocked 858 @retval false thread should not be blocked 859 */ is_waiting()860 bool is_waiting() { 861 mysql_mutex_lock(wait_lock); 862 bool result = wait_status; 863 mysql_mutex_unlock(wait_lock); 864 return result; 865 } 866 867 private: 868 /** the mutex lock for access to class and condition variables */ 869 mysql_mutex_t *wait_lock; 870 /** the condition variable calling thread will wait on */ 871 mysql_cond_t *wait_cond; 872 /** mutex instrumentation key */ 873 PSI_mutex_key key_lock; 874 /** cond instrumentation key */ 875 PSI_cond_key key_cond; 876 /** determine whether calling thread should be blocked or not */ 877 bool wait_status; 878 }; 879 880 /** 881 Simple method to escape character on a string 882 883 @note based on escape_string_for_mysql 884 @note the result is stored in the parameter string 885 886 @param[in,out] string_to_escape the string to escape 887 */ 888 void plugin_escape_string(std::string &string_to_escape); 889 890 #endif /* PLUGIN_UTILS_INCLUDED */ 891