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