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