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, Suite 500, 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
67extern "C" int event_queue_element_compare_q(void *, uchar *, uchar *);
68
69int 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
93Event_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
110Event_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
134bool
135Event_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
153err:
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
167void
168Event_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
199bool
200Event_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
240void
241Event_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
291void
292Event_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
330void
331Event_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 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
385void
386Event_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
409void
410Event_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
448void
449Event_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
509void
510Event_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
538void
539Event_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
582bool
583Event_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 ret= TRUE;
643 break;
644 }
645
646 DBUG_PRINT("info", ("Ready for execution"));
647 top->mark_last_executed(thd);
648 if (top->compute_next_execution_time())
649 top->status= Event_parse_data::DISABLED;
650 DBUG_PRINT("info", ("event %s status is %d", top->name.str, top->status));
651
652 top->execution_count++;
653 (*event_name)->dropped= top->dropped;
654 /*
655 Save new values of last_executed timestamp and event status on stack
656 in order to be able to update event description in system table once
657 QUEUE_DATA lock is released.
658 */
659 last_executed= top->last_executed;
660 status= top->status;
661
662 if (top->status == Event_parse_data::DISABLED)
663 {
664 DBUG_PRINT("info", ("removing from the queue"));
665 sql_print_information("Event Scheduler: Last execution of %s.%s. %s",
666 top->dbname.str, top->name.str,
667 top->dropped? "Dropping.":"");
668 delete top;
669 queue_remove_top(&queue);
670 }
671 else
672 queue_replace_top(&queue);
673
674 dbug_dump_queue(thd->query_start());
675 break;
676 }
677end:
678 UNLOCK_QUEUE_DATA();
679
680 DBUG_PRINT("info", ("returning %d et_new: %p ",
681 ret, *event_name));
682
683 if (*event_name)
684 {
685 DBUG_PRINT("info", ("db: %s name: %s",
686 (*event_name)->dbname.str, (*event_name)->name.str));
687
688 Event_db_repository *db_repository= Events::get_db_repository();
689 (void) db_repository->update_timing_fields_for_event(thd,
690 &(*event_name)->dbname, &(*event_name)->name,
691 last_executed, (ulonglong) status);
692 }
693
694 DBUG_RETURN(ret);
695}
696
697
698/*
699 Auxiliary function for locking LOCK_event_queue. Used by the
700 LOCK_QUEUE_DATA macro
701
702 SYNOPSIS
703 Event_queue::lock_data()
704 func Which function is requesting mutex lock
705 line On which line mutex lock is requested
706*/
707
708void
709Event_queue::lock_data(const char *func, uint line)
710{
711 DBUG_ENTER("Event_queue::lock_data");
712 DBUG_PRINT("enter", ("func=%s line=%u", func, line));
713 mutex_last_attempted_lock_in_func= func;
714 mutex_last_attempted_lock_at_line= line;
715 mutex_queue_data_attempting_lock= TRUE;
716 mysql_mutex_lock(&LOCK_event_queue);
717 mutex_last_attempted_lock_in_func= "";
718 mutex_last_attempted_lock_at_line= 0;
719 mutex_queue_data_attempting_lock= FALSE;
720
721 mutex_last_locked_in_func= func;
722 mutex_last_locked_at_line= line;
723 mutex_queue_data_locked= TRUE;
724
725 DBUG_VOID_RETURN;
726}
727
728
729/*
730 Auxiliary function for unlocking LOCK_event_queue. Used by the
731 UNLOCK_QUEUE_DATA macro
732
733 SYNOPSIS
734 Event_queue::unlock_data()
735 func Which function is requesting mutex unlock
736 line On which line mutex unlock is requested
737*/
738
739void
740Event_queue::unlock_data(const char *func, uint line)
741{
742 DBUG_ENTER("Event_queue::unlock_data");
743 DBUG_PRINT("enter", ("func=%s line=%u", func, line));
744 mutex_last_unlocked_at_line= line;
745 mutex_queue_data_locked= FALSE;
746 mutex_last_unlocked_in_func= func;
747 mysql_mutex_unlock(&LOCK_event_queue);
748 DBUG_VOID_RETURN;
749}
750
751
752/*
753 Wrapper for mysql_cond_wait/timedwait
754
755 SYNOPSIS
756 Event_queue::cond_wait()
757 thd Thread (Could be NULL during shutdown procedure)
758 msg Message for thd->proc_info
759 abstime If not null then call mysql_cond_timedwait()
760 func Which function is requesting cond_wait
761 line On which line cond_wait is requested
762*/
763
764void
765Event_queue::cond_wait(THD *thd, struct timespec *abstime, const PSI_stage_info *stage,
766 const char *src_func, const char *src_file, uint src_line)
767{
768 DBUG_ENTER("Event_queue::cond_wait");
769 waiting_on_cond= TRUE;
770 mutex_last_unlocked_at_line= src_line;
771 mutex_queue_data_locked= FALSE;
772 mutex_last_unlocked_in_func= src_func;
773
774 thd->enter_cond(&COND_queue_state, &LOCK_event_queue, stage, NULL, src_func, src_file, src_line);
775
776 if (!thd->killed)
777 {
778 DBUG_PRINT("info", ("pthread_cond_%swait", abstime ? "timed" : ""));
779 if (!abstime)
780 mysql_cond_wait(&COND_queue_state, &LOCK_event_queue);
781 else
782 mysql_cond_timedwait(&COND_queue_state, &LOCK_event_queue, abstime);
783 }
784
785 mutex_last_locked_in_func= src_func;
786 mutex_last_locked_at_line= src_line;
787 mutex_queue_data_locked= TRUE;
788 waiting_on_cond= FALSE;
789
790 /*
791 This will free the lock so we need to relock. Not the best thing to
792 do but we need to obey cond_wait()
793 */
794 thd->exit_cond(NULL, src_func, src_file, src_line);
795 lock_data(src_func, src_line);
796
797 DBUG_VOID_RETURN;
798}
799
800
801/*
802 Dumps the internal status of the queue
803
804 SYNOPSIS
805 Event_queue::dump_internal_status()
806*/
807
808void
809Event_queue::dump_internal_status()
810{
811 DBUG_ENTER("Event_queue::dump_internal_status");
812
813 /* element count */
814 puts("");
815 puts("Event queue status:");
816 printf("Element count : %u\n", queue.elements);
817 printf("Data locked : %s\n", mutex_queue_data_locked? "YES":"NO");
818 printf("Attempting lock : %s\n", mutex_queue_data_attempting_lock? "YES":"NO");
819 printf("LLA : %s:%u\n", mutex_last_locked_in_func,
820 mutex_last_locked_at_line);
821 printf("LUA : %s:%u\n", mutex_last_unlocked_in_func,
822 mutex_last_unlocked_at_line);
823 if (mutex_last_attempted_lock_at_line)
824 printf("Last lock attempt at: %s:%u\n", mutex_last_attempted_lock_in_func,
825 mutex_last_attempted_lock_at_line);
826 printf("WOC : %s\n", waiting_on_cond? "YES":"NO");
827
828 MYSQL_TIME time;
829 my_tz_OFFSET0->gmt_sec_to_TIME(&time, next_activation_at);
830 if (time.year != 1970)
831 printf("Next activation : %04d-%02d-%02d %02d:%02d:%02d\n",
832 time.year, time.month, time.day, time.hour, time.minute, time.second);
833 else
834 printf("Next activation : never");
835
836 DBUG_VOID_RETURN;
837}
838
839/**
840 @} (End of group Event_Scheduler)
841*/
842