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