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