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