1 /* Copyright (c) 2004, 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 #include "event_queue.h"
24 
25 #include "event_data_objects.h"
26 #include "event_db_repository.h"
27 #include "events.h"
28 #include "sql_audit.h"
29 #include "tztime.h"     // my_tz_find, my_tz_OFFSET0, struct Time_zone
30 #include "log.h"        // sql_print_error
31 #include "sql_class.h"  // struct THD
32 #include "mysql/psi/mysql_sp.h"
33 #include "sql_table.h"  // write_bin_log
34 
35 /**
36   @addtogroup Event_Scheduler
37   @{
38 */
39 
40 #define EVENT_QUEUE_INITIAL_SIZE 30
41 
42 #define LOCK_QUEUE_DATA()   lock_data(__func__, __LINE__)
43 #define UNLOCK_QUEUE_DATA() unlock_data(__func__, __LINE__)
44 
45 
46 /*
47   Constructor of class Event_queue.
48 
49   SYNOPSIS
50     Event_queue::Event_queue()
51 */
52 
Event_queue()53 Event_queue::Event_queue()
54   :queue(Event_queue_less(),
55          Malloc_allocator<Event_queue_element*>
56          (key_memory_Event_scheduler_scheduler_param)),
57    next_activation_at(0),
58    mutex_last_locked_at_line(0),
59    mutex_last_unlocked_at_line(0),
60    mutex_last_attempted_lock_at_line(0),
61    mutex_last_locked_in_func("n/a"),
62    mutex_last_unlocked_in_func("n/a"),
63    mutex_last_attempted_lock_in_func("n/a"),
64    mutex_queue_data_locked(FALSE),
65    mutex_queue_data_attempting_lock(FALSE),
66    waiting_on_cond(FALSE)
67 {
68   mysql_mutex_init(key_LOCK_event_queue, &LOCK_event_queue, MY_MUTEX_INIT_FAST);
69   mysql_cond_init(key_COND_queue_state, &COND_queue_state);
70 }
71 
72 
~Event_queue()73 Event_queue::~Event_queue()
74 {
75   deinit_queue();
76   mysql_mutex_destroy(&LOCK_event_queue);
77   mysql_cond_destroy(&COND_queue_state);
78 }
79 
80 
81 /*
82   This is a queue's constructor. Until this method is called, the
83   queue is unusable.  We don't use a C++ constructor instead in
84   order to be able to check the return value. The queue is
85   initialized once at server startup.  Initialization can fail in
86   case of a failure reading events from the database or out of
87   memory.
88 
89   SYNOPSIS
90     Event_queue::init()
91 
92   RETURN VALUE
93     FALSE  OK
94     TRUE   Error
95 */
96 
97 bool
init_queue(THD * thd)98 Event_queue::init_queue(THD *thd)
99 {
100   DBUG_ENTER("Event_queue::init_queue");
101   DBUG_PRINT("enter", ("this: 0x%lx", (long) this));
102 
103   LOCK_QUEUE_DATA();
104 
105   if (queue.reserve(EVENT_QUEUE_INITIAL_SIZE))
106   {
107     sql_print_error("Event Scheduler: Can't initialize the execution queue");
108     goto err;
109   }
110 
111   UNLOCK_QUEUE_DATA();
112   DBUG_RETURN(FALSE);
113 
114 err:
115   UNLOCK_QUEUE_DATA();
116   DBUG_RETURN(TRUE);
117 }
118 
119 
120 /*
121   Deinits the queue. Remove all elements from it and destroys them
122   too.
123 
124   SYNOPSIS
125     Event_queue::deinit_queue()
126 */
127 
128 void
deinit_queue()129 Event_queue::deinit_queue()
130 {
131   DBUG_ENTER("Event_queue::deinit_queue");
132 
133   LOCK_QUEUE_DATA();
134   empty_queue();
135   UNLOCK_QUEUE_DATA();
136 
137   DBUG_VOID_RETURN;
138 }
139 
140 
141 /**
142   Adds an event to the queue.
143 
144   Compute the next execution time for an event, and if it is still
145   active, add it to the queue. Otherwise delete it.
146   The object is left intact in case of an error. Otherwise
147   the queue container assumes ownership of it.
148 
149   @param[in]  thd      thread handle
150   @param[in]  new_element a new element to add to the queue
151   @param[out] created  set to TRUE if no error and the element is
152                        added to the queue, FALSE otherwise
153 
154   @retval TRUE  an error occured. The value of created is undefined,
155                 the element was not deleted.
156   @retval FALSE success
157 */
158 
159 bool
create_event(THD * thd,Event_queue_element * new_element,bool * created)160 Event_queue::create_event(THD *thd, Event_queue_element *new_element,
161                           bool *created)
162 {
163   DBUG_ENTER("Event_queue::create_event");
164   DBUG_PRINT("enter", ("thd: 0x%lx et=%s.%s", (long) thd,
165              new_element->dbname.str, new_element->name.str));
166 
167   /* Will do nothing if the event is disabled */
168   new_element->compute_next_execution_time();
169   if (new_element->status != Event_parse_data::ENABLED)
170   {
171     delete new_element;
172     *created= FALSE;
173     DBUG_RETURN(FALSE);
174   }
175 
176   DBUG_PRINT("info", ("new event in the queue: 0x%lx", (long) new_element));
177 
178   LOCK_QUEUE_DATA();
179   *created= (queue.push(new_element) == false);
180   dbug_dump_queue(thd->query_start());
181   mysql_cond_broadcast(&COND_queue_state);
182   UNLOCK_QUEUE_DATA();
183 
184   DBUG_RETURN(!*created);
185 }
186 
187 
188 /*
189   Updates an event from the scheduler queue
190 
191   SYNOPSIS
192     Event_queue::update_event()
193       thd        Thread
194       dbname     Schema of the event
195       name       Name of the event
196       new_schema New schema, in case of RENAME TO, otherwise NULL
197       new_name   New name, in case of RENAME TO, otherwise NULL
198 */
199 
200 void
update_event(THD * thd,LEX_STRING dbname,LEX_STRING name,Event_queue_element * new_element)201 Event_queue::update_event(THD *thd, LEX_STRING dbname, LEX_STRING name,
202                           Event_queue_element *new_element)
203 {
204   DBUG_ENTER("Event_queue::update_event");
205   DBUG_PRINT("enter", ("thd: 0x%lx  et=[%s.%s]", (long) thd, dbname.str, name.str));
206 
207   if ((new_element->status == Event_parse_data::DISABLED) ||
208       (new_element->status == Event_parse_data::SLAVESIDE_DISABLED))
209   {
210     DBUG_PRINT("info", ("The event is disabled."));
211     /*
212       Destroy the object but don't skip to end: because we may have to remove
213       object from the cache.
214     */
215     delete new_element;
216     new_element= NULL;
217   }
218   else
219     new_element->compute_next_execution_time();
220 
221   LOCK_QUEUE_DATA();
222   find_n_remove_event(dbname, name);
223 
224   /* If not disabled event */
225   if (new_element)
226   {
227     DBUG_PRINT("info", ("new event in the queue: 0x%lx", (long) new_element));
228     queue.push(new_element);
229     mysql_cond_broadcast(&COND_queue_state);
230   }
231 
232   dbug_dump_queue(thd->query_start());
233   UNLOCK_QUEUE_DATA();
234 
235   DBUG_VOID_RETURN;
236 }
237 
238 
239 /*
240   Drops an event from the queue
241 
242   SYNOPSIS
243     Event_queue::drop_event()
244       thd     Thread
245       dbname  Schema of the event to drop
246       name    Name of the event to drop
247 */
248 
249 void
drop_event(THD * thd,LEX_STRING dbname,LEX_STRING name)250 Event_queue::drop_event(THD *thd, LEX_STRING dbname, LEX_STRING name)
251 {
252   DBUG_ENTER("Event_queue::drop_event");
253   DBUG_PRINT("enter", ("thd: 0x%lx  db :%s  name: %s", (long) thd,
254                        dbname.str, name.str));
255 
256   LOCK_QUEUE_DATA();
257   find_n_remove_event(dbname, name);
258   dbug_dump_queue(thd->query_start());
259   UNLOCK_QUEUE_DATA();
260 
261   /*
262     We don't signal here because the scheduler will catch the change
263     next time it wakes up.
264   */
265 
266   DBUG_VOID_RETURN;
267 }
268 
269 
270 /*
271   Drops all events from the in-memory queue and disk that match
272   certain pattern evaluated by a comparator function
273 
274   SYNOPSIS
275     Event_queue::drop_matching_events()
276       thd            THD
277       pattern        A pattern string
278       comparator     The function to use for comparing
279 
280   RETURN VALUE
281     >=0  Number of dropped events
282 
283   NOTE
284     Expected is the caller to acquire lock on LOCK_event_queue
285 */
286 
287 void
drop_matching_events(THD * thd,LEX_STRING pattern,bool (* comparator)(LEX_STRING,Event_basic *))288 Event_queue::drop_matching_events(THD *thd, LEX_STRING pattern,
289                            bool (*comparator)(LEX_STRING, Event_basic *))
290 {
291   size_t i= 0;
292   DBUG_ENTER("Event_queue::drop_matching_events");
293   DBUG_PRINT("enter", ("pattern=%s", pattern.str));
294 
295   while (i < queue.size())
296   {
297     Event_queue_element *et= queue[i];
298     DBUG_PRINT("info", ("[%s.%s]?", et->dbname.str, et->name.str));
299     if (comparator(pattern, et))
300     {
301       /*
302         The queue is ordered. If we remove an element, then all elements
303         after it will shift one position to the left, if we imagine it as
304         an array from left to the right. In this case we should not
305         increment the counter and the (i < queue.elements) condition is ok.
306       */
307       queue.remove(i);
308 #ifdef HAVE_PSI_SP_INTERFACE
309       /* Drop statistics for this stored program from performance schema. */
310       MYSQL_DROP_SP(SP_TYPE_EVENT,
311                     et->dbname.str, et->dbname.length,
312                     et->name.str, et->name.length);
313 #endif
314       delete et;
315     }
316     else
317       i++;
318   }
319   /*
320     We don't call mysql_cond_broadcast(&COND_queue_state);
321     If we remove the top event:
322     1. The queue is empty. The scheduler will wake up at some time and
323        realize that the queue is empty. If create_event() comes inbetween
324        it will signal the scheduler
325     2. The queue is not empty, but the next event after the previous top,
326        won't be executed any time sooner than the element we removed. Hence,
327        we may not notify the scheduler and it will realize the change when it
328        wakes up from timedwait.
329   */
330 
331   DBUG_VOID_RETURN;
332 }
333 
334 
335 /*
336   Drops all events from the in-memory queue and disk that are from
337   certain schema.
338 
339   SYNOPSIS
340     Event_queue::drop_schema_events()
341       thd        HD
342       schema    The schema name
343 */
344 
345 void
drop_schema_events(THD * thd,LEX_STRING schema)346 Event_queue::drop_schema_events(THD *thd, LEX_STRING schema)
347 {
348   DBUG_ENTER("Event_queue::drop_schema_events");
349   LOCK_QUEUE_DATA();
350   drop_matching_events(thd, schema, event_basic_db_equal);
351   UNLOCK_QUEUE_DATA();
352   DBUG_VOID_RETURN;
353 }
354 
355 
356 /*
357   Searches for an event in the queue
358 
359   SYNOPSIS
360     Event_queue::find_n_remove_event()
361       db    The schema of the event to find
362       name  The event to find
363 
364   NOTE
365     The caller should do the locking also the caller is responsible for
366     actual signalling in case an event is removed from the queue.
367 */
368 
369 void
find_n_remove_event(LEX_STRING db,LEX_STRING name)370 Event_queue::find_n_remove_event(LEX_STRING db, LEX_STRING name)
371 {
372   DBUG_ENTER("Event_queue::find_n_remove_event");
373 
374   for (size_t i= 0; i < queue.size(); ++i)
375   {
376     Event_queue_element *et= queue[i];
377     DBUG_PRINT("info", ("[%s.%s]==[%s.%s]?", db.str, name.str,
378                         et->dbname.str, et->name.str));
379     if (event_basic_identifier_equal(db, name, et))
380     {
381       queue.remove(i);
382       delete et;
383       break;
384     }
385   }
386 
387   DBUG_VOID_RETURN;
388 }
389 
390 
391 /*
392   Recalculates activation times in the queue. There is one reason for
393   that. Because the values (execute_at) by which the queue is ordered are
394   changed by calls to compute_next_execution_time() on a request from the
395   scheduler thread, if it is not running then the values won't be updated.
396   Once the scheduler is started again the values has to be recalculated
397   so they are right for the current time.
398 
399   SYNOPSIS
400     Event_queue::recalculate_activation_times()
401       thd  Thread
402 */
403 
404 void
recalculate_activation_times(THD * thd)405 Event_queue::recalculate_activation_times(THD *thd)
406 {
407   Event_db_repository *db_repository= Events::get_db_repository();
408   DBUG_ENTER("Event_queue::recalculate_activation_times");
409 
410   LOCK_QUEUE_DATA();
411   DBUG_PRINT("info", ("%u loaded events to be recalculated",
412                       static_cast<unsigned>(queue.size())));
413   for (size_t i= 0; i < queue.size(); i++)
414   {
415     queue[i]->compute_next_execution_time();
416   }
417   queue.build_heap();
418   /*
419     The disabled elements are moved to the end during the `fix`.
420     Start from the end and remove all of the elements which are
421     disabled. When we find the first non-disabled one we break, as we
422     have removed all. The queue has been ordered in a way the disabled
423     events are at the end.
424   */
425   for (size_t i= queue.size(); i > 0; i--)
426   {
427     Event_queue_element *element = queue[i - 1];
428     if (element->status != Event_parse_data::DISABLED)
429       break;
430     if (lock_object_name(thd, MDL_key::EVENT,
431                          element->dbname.str, element->name.str))
432       break;
433     /*
434       This won't cause queue re-order, because we remove
435       always the last element.
436     */
437     queue.remove(i - 1);
438     /*
439       Dropping the event from mysql.event table
440     */
441     if (element->dropped)
442     {
443       db_repository->drop_event(thd, element->dbname, element->name, false);
444 
445       String sp_sql;
446       if (construct_drop_event_sql(thd, &sp_sql,
447                                    element->dbname,
448                                    element->name))
449       {
450         sql_print_warning("Unable to construct DROP EVENT SQL query string");
451       }
452       else
453       {
454         // Write drop event to bin log.
455         thd->add_to_binlog_accessed_dbs(element->dbname.str);
456         if (write_bin_log(thd, true, sp_sql.c_ptr_safe(), sp_sql.length()))
457         {
458           sql_print_warning("Unable to binlog drop event %s.%s.",
459                             element->dbname.str,
460                             element->name.str);
461         }
462       }
463     }
464     delete element;
465   }
466   // Release locks taken before drop_event()
467   thd->mdl_context.release_transactional_locks();
468   UNLOCK_QUEUE_DATA();
469 
470   /*
471     XXX: The events are dropped only from memory and not from disk
472          even if `drop_list[j]->dropped` is TRUE. There will be still on the
473          disk till next server restart.
474          Please add code here to do it.
475   */
476 
477   DBUG_VOID_RETURN;
478 }
479 
480 
481 /*
482   Empties the queue and destroys the Event_queue_element objects in the
483   queue.
484 
485   SYNOPSIS
486     Event_queue::empty_queue()
487 
488   NOTE
489     Should be called with LOCK_event_queue locked
490 */
491 
492 void
empty_queue()493 Event_queue::empty_queue()
494 {
495   DBUG_ENTER("Event_queue::empty_queue");
496   DBUG_PRINT("enter", ("Purging the queue. %u element(s)",
497                        static_cast<unsigned>(queue.size())));
498   sql_print_information("Event Scheduler: Purging the queue. %u events",
499                         static_cast<unsigned>(queue.size()));
500   /* empty the queue */
501   queue.delete_elements();
502 
503   DBUG_VOID_RETURN;
504 }
505 
506 
507 /*
508   Dumps the queue to the trace log.
509 
510   SYNOPSIS
511     Event_queue::dbug_dump_queue()
512       now  Current timestamp
513 */
514 
515 void
dbug_dump_queue(time_t now)516 Event_queue::dbug_dump_queue(time_t now)
517 {
518 #ifndef NDEBUG
519   DBUG_ENTER("Event_queue::dbug_dump_queue");
520   DBUG_PRINT("info", ("Dumping queue . Elements=%u",
521                       static_cast<unsigned>(queue.size())));
522   for (size_t i = 0; i < queue.size(); i++)
523   {
524     Event_queue_element *et= queue[i];
525     DBUG_PRINT("info", ("et: 0x%lx  name: %s.%s", (long) et,
526                         et->dbname.str, et->name.str));
527     DBUG_PRINT("info", ("exec_at: %lu  starts: %lu  ends: %lu  execs_so_far: %u  "
528                         "expr: %ld  et.exec_at: %ld  now: %ld  "
529                         "(et.exec_at - now): %d  if: %d",
530                         (long) et->execute_at, (long) et->starts,
531                         (long) et->ends, et->execution_count,
532                         (long) et->expression, (long) et->execute_at,
533                         (long) now, (int) (et->execute_at - now),
534                         et->execute_at <= now));
535   }
536   DBUG_VOID_RETURN;
537 #endif
538 }
539 
540 /*
541   Checks whether the top of the queue is elligible for execution and
542   returns an Event_job_data instance in case it should be executed.
543   `now` is compared against `execute_at` of the top element in the queue.
544 
545   SYNOPSIS
546     Event_queue::get_top_for_execution_if_time()
547       thd        [in]  Thread
548       event_name [out] The object to execute
549 
550   RETURN VALUE
551     FALSE  No error. event_name != NULL
552     TRUE   Serious error
553 */
554 
555 bool
get_top_for_execution_if_time(THD * thd,Event_queue_element_for_exec ** event_name)556 Event_queue::get_top_for_execution_if_time(THD *thd,
557                 Event_queue_element_for_exec **event_name)
558 {
559   bool ret= FALSE;
560   *event_name= NULL;
561   my_time_t last_executed= 0;
562   int status= 0;
563   DBUG_ENTER("Event_queue::get_top_for_execution_if_time");
564 
565   LOCK_QUEUE_DATA();
566   for (;;)
567   {
568     Event_queue_element *top= NULL;
569 
570     /* Break loop if thd has been killed */
571     if (thd->killed)
572     {
573       DBUG_PRINT("info", ("thd->killed=%d", thd->killed));
574       goto end;
575     }
576 
577     if (queue.empty())
578     {
579       /* There are no events in the queue */
580       next_activation_at= 0;
581 
582       /* Release any held audit resources before waiting */
583       mysql_audit_release(thd);
584 
585       /* Wait on condition until signaled. Release LOCK_queue while waiting. */
586       cond_wait(thd, NULL, & stage_waiting_on_empty_queue, __func__, __FILE__, __LINE__);
587 
588       continue;
589     }
590 
591     top= queue.top();
592 
593     thd->set_current_time(); /* Get current time */
594 
595     next_activation_at= top->execute_at;
596     if (next_activation_at > thd->query_start())
597     {
598       /*
599         Not yet time for top event, wait on condition with
600         time or until signaled. Release LOCK_queue while waiting.
601       */
602       struct timespec top_time;
603       set_timespec(&top_time, next_activation_at - thd->query_start());
604 
605       /* Release any held audit resources before waiting */
606       mysql_audit_release(thd);
607 
608       cond_wait(thd, &top_time, &stage_waiting_for_next_activation, __func__, __FILE__, __LINE__);
609 
610       continue;
611     }
612 
613     if (!(*event_name= new Event_queue_element_for_exec()) ||
614         (*event_name)->init(top->dbname, top->name))
615     {
616       ret= TRUE;
617       break;
618     }
619 
620     DBUG_PRINT("info", ("Ready for execution"));
621     top->mark_last_executed(thd);
622     if (top->compute_next_execution_time())
623       top->status= Event_parse_data::DISABLED;
624     DBUG_PRINT("info", ("event %s status is %d", top->name.str, top->status));
625 
626     top->execution_count++;
627     (*event_name)->dropped= top->dropped;
628     /*
629       Save new values of last_executed timestamp and event status on stack
630       in order to be able to update event description in system table once
631       QUEUE_DATA lock is released.
632     */
633     last_executed= top->last_executed;
634     status= top->status;
635 
636     if (top->status == Event_parse_data::DISABLED)
637     {
638       DBUG_PRINT("info", ("removing from the queue"));
639       sql_print_information("Event Scheduler: Last execution of %s.%s. %s",
640                             top->dbname.str, top->name.str,
641                             top->dropped? "Dropping.":"");
642       delete top;
643       queue.pop();
644       /*
645        This event will get dropped from mysql.event table in
646        Event_job_data::execute() function eventually.
647        So no need add check to drop it from mysql.event table here.
648       */
649     }
650     else
651       queue.update_top();
652 
653     dbug_dump_queue(thd->query_start());
654     break;
655   }
656 end:
657   UNLOCK_QUEUE_DATA();
658 
659   DBUG_PRINT("info", ("returning %d  et_new: 0x%lx ",
660                       ret, (long) *event_name));
661 
662   if (*event_name)
663   {
664     DBUG_PRINT("info", ("db: %s  name: %s",
665                         (*event_name)->dbname.str, (*event_name)->name.str));
666 
667     Event_db_repository *db_repository= Events::get_db_repository();
668     (void) db_repository->update_timing_fields_for_event(thd,
669                             (*event_name)->dbname, (*event_name)->name,
670                             last_executed, (ulonglong) status);
671   }
672 
673   DBUG_RETURN(ret);
674 }
675 
676 
677 /*
678   Auxiliary function for locking LOCK_event_queue. Used by the
679   LOCK_QUEUE_DATA macro
680 
681   SYNOPSIS
682     Event_queue::lock_data()
683       func  Which function is requesting mutex lock
684       line  On which line mutex lock is requested
685 */
686 
687 void
lock_data(const char * func,uint line)688 Event_queue::lock_data(const char *func, uint line)
689 {
690   DBUG_ENTER("Event_queue::lock_data");
691   DBUG_PRINT("enter", ("func=%s line=%u", func, line));
692   mutex_last_attempted_lock_in_func= func;
693   mutex_last_attempted_lock_at_line= line;
694   mutex_queue_data_attempting_lock= TRUE;
695   mysql_mutex_lock(&LOCK_event_queue);
696   mutex_last_attempted_lock_in_func= "";
697   mutex_last_attempted_lock_at_line= 0;
698   mutex_queue_data_attempting_lock= FALSE;
699 
700   mutex_last_locked_in_func= func;
701   mutex_last_locked_at_line= line;
702   mutex_queue_data_locked= TRUE;
703 
704   DBUG_VOID_RETURN;
705 }
706 
707 
708 /*
709   Auxiliary function for unlocking LOCK_event_queue. Used by the
710   UNLOCK_QUEUE_DATA macro
711 
712   SYNOPSIS
713     Event_queue::unlock_data()
714       func  Which function is requesting mutex unlock
715       line  On which line mutex unlock is requested
716 */
717 
718 void
unlock_data(const char * func,uint line)719 Event_queue::unlock_data(const char *func, uint line)
720 {
721   DBUG_ENTER("Event_queue::unlock_data");
722   DBUG_PRINT("enter", ("func=%s line=%u", func, line));
723   mutex_last_unlocked_at_line= line;
724   mutex_queue_data_locked= FALSE;
725   mutex_last_unlocked_in_func= func;
726   mysql_mutex_unlock(&LOCK_event_queue);
727   DBUG_VOID_RETURN;
728 }
729 
730 
731 /*
732   Wrapper for mysql_cond_wait/timedwait
733 
734   SYNOPSIS
735     Event_queue::cond_wait()
736       thd     Thread (Could be NULL during shutdown procedure)
737       msg     Message for thd->proc_info
738       abstime If not null then call mysql_cond_timedwait()
739       func    Which function is requesting cond_wait
740       line    On which line cond_wait is requested
741 */
742 
743 void
cond_wait(THD * thd,struct timespec * abstime,const PSI_stage_info * stage,const char * src_func,const char * src_file,uint src_line)744 Event_queue::cond_wait(THD *thd, struct timespec *abstime, const PSI_stage_info *stage,
745                        const char *src_func, const char *src_file, uint src_line)
746 {
747   DBUG_ENTER("Event_queue::cond_wait");
748   waiting_on_cond= TRUE;
749   mutex_last_unlocked_at_line= src_line;
750   mutex_queue_data_locked= FALSE;
751   mutex_last_unlocked_in_func= src_func;
752 
753   thd->enter_cond(&COND_queue_state, &LOCK_event_queue, stage, NULL, src_func, src_file, src_line);
754 
755   if (!thd->killed)
756   {
757     if (!abstime)
758       mysql_cond_wait(&COND_queue_state, &LOCK_event_queue);
759     else
760       mysql_cond_timedwait(&COND_queue_state, &LOCK_event_queue, abstime);
761   }
762 
763   mutex_last_locked_in_func= src_func;
764   mutex_last_locked_at_line= src_line;
765   mutex_queue_data_locked= TRUE;
766   waiting_on_cond= FALSE;
767 
768   /*
769     Need to unlock before exit_cond, so we need to relock.
770     Not the best thing to do but we need to obey cond_wait()
771   */
772   unlock_data(src_func, src_line);
773   thd->exit_cond(NULL, src_func, src_file, src_line);
774   lock_data(src_func, src_line);
775 
776   DBUG_VOID_RETURN;
777 }
778 
779 
780 /*
781   Dumps the internal status of the queue
782 
783   SYNOPSIS
784     Event_queue::dump_internal_status()
785 */
786 
787 void
dump_internal_status()788 Event_queue::dump_internal_status()
789 {
790   DBUG_ENTER("Event_queue::dump_internal_status");
791 
792   /* element count */
793   puts("");
794   puts("Event queue status:");
795   printf("Element count   : %u\n", static_cast<unsigned>(queue.size()));
796   printf("Data locked     : %s\n", mutex_queue_data_locked? "YES":"NO");
797   printf("Attempting lock : %s\n", mutex_queue_data_attempting_lock? "YES":"NO");
798   printf("LLA             : %s:%u\n", mutex_last_locked_in_func,
799                                         mutex_last_locked_at_line);
800   printf("LUA             : %s:%u\n", mutex_last_unlocked_in_func,
801                                         mutex_last_unlocked_at_line);
802   if (mutex_last_attempted_lock_at_line)
803     printf("Last lock attempt at: %s:%u\n", mutex_last_attempted_lock_in_func,
804                                             mutex_last_attempted_lock_at_line);
805   printf("WOC             : %s\n", waiting_on_cond? "YES":"NO");
806 
807   MYSQL_TIME time;
808   my_tz_OFFSET0->gmt_sec_to_TIME(&time, next_activation_at);
809   if (time.year != 1970)
810     printf("Next activation : %04d-%02d-%02d %02d:%02d:%02d\n",
811            time.year, time.month, time.day, time.hour, time.minute, time.second);
812   else
813     printf("Next activation : never");
814 
815   DBUG_VOID_RETURN;
816 }
817 
818 /**
819   @} (End of group Event_Scheduler)
820 */
821