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