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