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