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