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