1 /* Copyright (c) 2006, 2019, Oracle and/or its affiliates. All rights reserved.
2
3 This program is free software; you can redistribute it and/or modify
4 it under the terms of the GNU General Public License, version 2.0,
5 as published by the Free Software Foundation.
6
7 This program is also distributed with certain software (including
8 but not limited to OpenSSL) that is licensed under separate terms,
9 as designated in a particular file or component or in included license
10 documentation. The authors of MySQL hereby grant you an additional
11 permission to link the program and your derivative works with the
12 separately licensed software that they have included with MySQL.
13
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License, version 2.0, for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software
21 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
22
23 #include "sql_priv.h"
24 #include "unireg.h"
25 #include "event_scheduler.h"
26 #include "events.h"
27 #include "event_data_objects.h"
28 #include "event_queue.h"
29 #include "event_db_repository.h"
30 #include "sql_connect.h" // init_new_connection_handler_thread
31 #include "sql_acl.h" // SUPER_ACL
32 #include "global_threads.h"
33
34 /**
35 @addtogroup Event_Scheduler
36 @{
37 */
38
39 #ifdef __GNUC__
40 #if __GNUC__ >= 2
41 #define SCHED_FUNC __FUNCTION__
42 #endif
43 #else
44 #define SCHED_FUNC "<unknown>"
45 #endif
46
47 #define LOCK_DATA() lock_data(SCHED_FUNC, __LINE__)
48 #define UNLOCK_DATA() unlock_data(SCHED_FUNC, __LINE__)
49 #define COND_STATE_WAIT(mythd, abstime, stage) \
50 cond_wait(mythd, abstime, stage, SCHED_FUNC, __FILE__, __LINE__)
51
52 extern pthread_attr_t connection_attrib;
53
54
55 Event_db_repository *Event_worker_thread::db_repository;
56
57
58 static
59 const LEX_STRING scheduler_states_names[] =
60 {
61 { C_STRING_WITH_LEN("INITIALIZED") },
62 { C_STRING_WITH_LEN("RUNNING") },
63 { C_STRING_WITH_LEN("STOPPING") }
64 };
65
66 struct scheduler_param {
67 THD *thd;
68 Event_scheduler *scheduler;
69 };
70
71
72 /*
73 Prints the stack of infos, warnings, errors from thd to
74 the console so it can be fetched by the logs-into-tables and
75 checked later.
76
77 SYNOPSIS
78 evex_print_warnings
79 thd Thread used during the execution of the event
80 et The event itself
81 */
82
83 void
print_warnings(THD * thd,Event_job_data * et)84 Event_worker_thread::print_warnings(THD *thd, Event_job_data *et)
85 {
86 const Sql_condition *err;
87 DBUG_ENTER("evex_print_warnings");
88 if (thd->get_stmt_da()->is_warning_info_empty())
89 DBUG_VOID_RETURN;
90
91 char msg_buf[10 * STRING_BUFFER_USUAL_SIZE];
92 char prefix_buf[5 * STRING_BUFFER_USUAL_SIZE];
93 String prefix(prefix_buf, sizeof(prefix_buf), system_charset_info);
94 prefix.length(0);
95 prefix.append("Event Scheduler: [");
96
97 prefix.append(et->definer.str, et->definer.length, system_charset_info);
98 prefix.append("][", 2);
99 prefix.append(et->dbname.str, et->dbname.length, system_charset_info);
100 prefix.append('.');
101 prefix.append(et->name.str, et->name.length, system_charset_info);
102 prefix.append("] ", 2);
103
104 Diagnostics_area::Sql_condition_iterator it=
105 thd->get_stmt_da()->sql_conditions();
106 while ((err= it++))
107 {
108 String err_msg(msg_buf, sizeof(msg_buf), system_charset_info);
109 /* set it to 0 or we start adding at the end. That's the trick ;) */
110 err_msg.length(0);
111 err_msg.append(prefix);
112 err_msg.append(err->get_message_text(),
113 err->get_message_octet_length(), system_charset_info);
114 DBUG_ASSERT(err->get_level() < 3);
115 (sql_print_message_handlers[err->get_level()])("%*s", err_msg.length(),
116 err_msg.c_ptr());
117 }
118 DBUG_VOID_RETURN;
119 }
120
121
122 /*
123 Performs post initialization of structures in a new thread.
124
125 SYNOPSIS
126 post_init_event_thread()
127 thd Thread
128
129 NOTES
130 Before this is called, one should not do any DBUG_XXX() calls.
131
132 */
133
134 bool
post_init_event_thread(THD * thd)135 post_init_event_thread(THD *thd)
136 {
137 (void) init_new_connection_handler_thread();
138 if (init_thr_lock() || thd->store_globals())
139 {
140 return TRUE;
141 }
142
143 inc_thread_running();
144 mysql_mutex_lock(&LOCK_thread_count);
145 add_global_thread(thd);
146 mysql_mutex_unlock(&LOCK_thread_count);
147 return FALSE;
148 }
149
150
151 /*
152 Cleans up the THD and the threaded environment of the thread.
153
154 SYNOPSIS
155 deinit_event_thread()
156 thd Thread
157 */
158
159 void
deinit_event_thread(THD * thd)160 deinit_event_thread(THD *thd)
161 {
162 thd->proc_info= "Clearing";
163 DBUG_ASSERT(thd->net.buff != 0);
164 net_end(&thd->net);
165 DBUG_PRINT("exit", ("Event thread finishing"));
166
167 dec_thread_running();
168 thd->release_resources();
169 remove_global_thread(thd);
170 delete thd;
171 }
172
173
174 /*
175 Performs pre- mysql_thread_create() initialisation of THD. Do this
176 in the thread that will pass THD to the child thread. In the
177 child thread call post_init_event_thread().
178
179 SYNOPSIS
180 pre_init_event_thread()
181 thd The THD of the thread. Has to be allocated by the caller.
182
183 NOTES
184 1. The host of the thead is my_localhost
185 2. thd->net is initted with NULL - no communication.
186 */
187
188 void
pre_init_event_thread(THD * thd)189 pre_init_event_thread(THD* thd)
190 {
191 DBUG_ENTER("pre_init_event_thread");
192 thd->client_capabilities= 0;
193 thd->security_ctx->master_access= 0;
194 thd->security_ctx->db_access= 0;
195 thd->security_ctx->host_or_ip= (char*)my_localhost;
196 my_net_init(&thd->net, NULL);
197 thd->security_ctx->set_user((char*)"event_scheduler");
198 thd->net.read_timeout= slave_net_timeout;
199 thd->slave_thread= 0;
200 thd->variables.option_bits|= OPTION_AUTO_IS_NULL;
201 thd->client_capabilities|= CLIENT_MULTI_RESULTS;
202 mysql_mutex_lock(&LOCK_thread_count);
203 thd->thread_id= thd->variables.pseudo_thread_id= thread_id++;
204 mysql_mutex_unlock(&LOCK_thread_count);
205
206 /*
207 Guarantees that we will see the thread in SHOW PROCESSLIST though its
208 vio is NULL.
209 */
210
211 thd->proc_info= "Initialized";
212 thd->set_time();
213
214 /* Do not use user-supplied timeout value for system threads. */
215 thd->variables.lock_wait_timeout= LONG_TIMEOUT;
216
217 DBUG_VOID_RETURN;
218 }
219
220
221 /*
222 Function that executes the scheduler,
223
224 SYNOPSIS
225 event_scheduler_thread()
226 arg Pointer to `struct scheduler_param`
227
228 RETURN VALUE
229 0 OK
230 */
231
232 pthread_handler_t
event_scheduler_thread(void * arg)233 event_scheduler_thread(void *arg)
234 {
235 /* needs to be first for thread_stack */
236 THD *thd= (THD *) ((struct scheduler_param *) arg)->thd;
237 Event_scheduler *scheduler= ((struct scheduler_param *) arg)->scheduler;
238 bool res;
239
240 thd->thread_stack= (char *)&thd; // remember where our stack is
241
242 mysql_thread_set_psi_id(thd->thread_id);
243
244 res= post_init_event_thread(thd);
245
246 DBUG_ENTER("event_scheduler_thread");
247 my_free(arg);
248 if (!res)
249 scheduler->run(thd);
250 else
251 {
252 thd->proc_info= "Clearing";
253 net_end(&thd->net);
254 delete thd;
255 }
256
257 DBUG_LEAVE; // Against gcc warnings
258 my_thread_end();
259 return 0;
260 }
261
262
263 /**
264 Function that executes an event in a child thread. Setups the
265 environment for the event execution and cleans after that.
266
267 SYNOPSIS
268 event_worker_thread()
269 arg The Event_job_data object to be processed
270
271 RETURN VALUE
272 0 OK
273 */
274
275 pthread_handler_t
event_worker_thread(void * arg)276 event_worker_thread(void *arg)
277 {
278 THD *thd;
279 Event_queue_element_for_exec *event= (Event_queue_element_for_exec *)arg;
280
281 thd= event->thd;
282
283 mysql_thread_set_psi_id(thd->thread_id);
284
285 Event_worker_thread worker_thread;
286 worker_thread.run(thd, event);
287
288 my_thread_end();
289 return 0; // Can't return anything here
290 }
291
292
293 /**
294 Function that executes an event in a child thread. Setups the
295 environment for the event execution and cleans after that.
296
297 SYNOPSIS
298 Event_worker_thread::run()
299 thd Thread context
300 event The Event_queue_element_for_exec object to be processed
301 */
302
303 void
run(THD * thd,Event_queue_element_for_exec * event)304 Event_worker_thread::run(THD *thd, Event_queue_element_for_exec *event)
305 {
306 /* needs to be first for thread_stack */
307 char my_stack;
308 Event_job_data job_data;
309 bool res;
310
311 DBUG_ASSERT(thd->m_digest == NULL);
312 DBUG_ASSERT(thd->m_statement_psi == NULL);
313
314
315 thd->thread_stack= &my_stack; // remember where our stack is
316 res= post_init_event_thread(thd);
317
318 DBUG_ENTER("Event_worker_thread::run");
319 DBUG_PRINT("info", ("Time is %ld, THD: 0x%lx", (long) my_time(0), (long) thd));
320
321 if (res)
322 goto end;
323
324 if ((res= db_repository->load_named_event(thd, event->dbname, event->name,
325 &job_data)))
326 {
327 DBUG_PRINT("error", ("Got error from load_named_event"));
328 goto end;
329 }
330
331 thd->enable_slow_log= TRUE;
332
333 res= job_data.execute(thd, event->dropped);
334
335 print_warnings(thd, &job_data);
336
337 if (res)
338 sql_print_information("Event Scheduler: "
339 "[%s].[%s.%s] event execution failed.",
340 job_data.definer.str,
341 job_data.dbname.str, job_data.name.str);
342 end:
343 DBUG_ASSERT(thd->m_statement_psi == NULL);
344 DBUG_ASSERT(thd->m_digest == NULL);
345
346 DBUG_PRINT("info", ("Done with Event %s.%s", event->dbname.str,
347 event->name.str));
348
349 delete event;
350 deinit_event_thread(thd);
351
352 DBUG_VOID_RETURN;
353 }
354
355
Event_scheduler(Event_queue * queue_arg)356 Event_scheduler::Event_scheduler(Event_queue *queue_arg)
357 :state(INITIALIZED),
358 scheduler_thd(NULL),
359 queue(queue_arg),
360 mutex_last_locked_at_line(0),
361 mutex_last_unlocked_at_line(0),
362 mutex_last_locked_in_func("n/a"),
363 mutex_last_unlocked_in_func("n/a"),
364 mutex_scheduler_data_locked(FALSE),
365 waiting_on_cond(FALSE),
366 started_events(0)
367 {
368 mysql_mutex_init(key_event_scheduler_LOCK_scheduler_state,
369 &LOCK_scheduler_state, MY_MUTEX_INIT_FAST);
370 mysql_cond_init(key_event_scheduler_COND_state, &COND_state, NULL);
371 }
372
373
~Event_scheduler()374 Event_scheduler::~Event_scheduler()
375 {
376 stop(); /* does nothing if not running */
377 mysql_mutex_destroy(&LOCK_scheduler_state);
378 mysql_cond_destroy(&COND_state);
379 }
380
381
382 /**
383 Starts the scheduler (again). Creates a new THD and passes it to
384 a forked thread. Does not wait for acknowledgement from the new
385 thread that it has started. Asynchronous starting. Most of the
386 needed initializations are done in the current thread to minimize
387 the chance of failure in the spawned thread.
388
389 @param[out] err_no - errno indicating type of error which caused
390 failure to start scheduler thread.
391
392 @return
393 @retval false Success.
394 @retval true Error.
395 */
396
397 bool
start(int * err_no)398 Event_scheduler::start(int *err_no)
399 {
400 THD *new_thd= NULL;
401 bool ret= false;
402 pthread_t th;
403 struct scheduler_param *scheduler_param_value;
404 DBUG_ENTER("Event_scheduler::start");
405
406 LOCK_DATA();
407 DBUG_PRINT("info", ("state before action %s", scheduler_states_names[state].str));
408 if (state > INITIALIZED)
409 goto end;
410
411 DBUG_EXECUTE_IF("event_scheduler_thread_create_failure", {
412 *err_no= 11;
413 Events::opt_event_scheduler= Events::EVENTS_OFF;
414 ret= true;
415 goto end; });
416
417 if (!(new_thd= new THD))
418 {
419 sql_print_error("Event Scheduler: Cannot initialize the scheduler thread");
420 ret= true;
421 goto end;
422 }
423 pre_init_event_thread(new_thd);
424 new_thd->system_thread= SYSTEM_THREAD_EVENT_SCHEDULER;
425 new_thd->set_command(COM_DAEMON);
426
427 /*
428 We should run the event scheduler thread under the super-user privileges.
429 In particular, this is needed to be able to lock the mysql.event table
430 for writing when the server is running in the read-only mode.
431
432 Same goes for transaction access mode. Set it to read-write for this thd.
433 */
434 new_thd->security_ctx->master_access |= SUPER_ACL;
435 new_thd->variables.tx_read_only= false;
436 new_thd->tx_read_only= false;
437
438 scheduler_param_value=
439 (struct scheduler_param *)my_malloc(sizeof(struct scheduler_param), MYF(0));
440 scheduler_param_value->thd= new_thd;
441 scheduler_param_value->scheduler= this;
442
443 scheduler_thd= new_thd;
444 DBUG_PRINT("info", ("Setting state go RUNNING"));
445 state= RUNNING;
446 DBUG_PRINT("info", ("Forking new thread for scheduler. THD: 0x%lx", (long) new_thd));
447 if ((*err_no= mysql_thread_create(key_thread_event_scheduler,
448 &th, &connection_attrib,
449 event_scheduler_thread,
450 (void*)scheduler_param_value)))
451 {
452 DBUG_PRINT("error", ("cannot create a new thread"));
453 sql_print_error("Event scheduler: Failed to start scheduler,"
454 " Can not create thread for event scheduler (errno=%d)",
455 *err_no);
456
457 new_thd->proc_info= "Clearing";
458 DBUG_ASSERT(new_thd->net.buff != 0);
459 net_end(&new_thd->net);
460
461 state= INITIALIZED;
462 scheduler_thd= NULL;
463 delete new_thd;
464
465 delete scheduler_param_value;
466 ret= true;
467 }
468
469 end:
470 UNLOCK_DATA();
471 DBUG_RETURN(ret);
472 }
473
474
475 /*
476 The main loop of the scheduler.
477
478 SYNOPSIS
479 Event_scheduler::run()
480 thd Thread
481
482 RETURN VALUE
483 FALSE OK
484 TRUE Error (Serious error)
485 */
486
487 bool
run(THD * thd)488 Event_scheduler::run(THD *thd)
489 {
490 int res= FALSE;
491 DBUG_ENTER("Event_scheduler::run");
492
493 sql_print_information("Event Scheduler: scheduler thread started with id %lu",
494 thd->thread_id);
495 /*
496 Recalculate the values in the queue because there could have been stops
497 in executions of the scheduler and some times could have passed by.
498 */
499 queue->recalculate_activation_times(thd);
500
501 while (is_running())
502 {
503 Event_queue_element_for_exec *event_name;
504
505 /* Gets a minimized version */
506 if (queue->get_top_for_execution_if_time(thd, &event_name))
507 {
508 sql_print_information("Event Scheduler: "
509 "Serious error during getting next "
510 "event to execute. Stopping");
511 break;
512 }
513
514 DBUG_PRINT("info", ("get_top_for_execution_if_time returned "
515 "event_name=0x%lx", (long) event_name));
516 if (event_name)
517 {
518 if ((res= execute_top(event_name)))
519 break;
520 }
521 else
522 {
523 DBUG_ASSERT(thd->killed);
524 DBUG_PRINT("info", ("job_data is NULL, the thread was killed"));
525 }
526 DBUG_PRINT("info", ("state=%s", scheduler_states_names[state].str));
527 free_root(thd->mem_root, MYF(0));
528 }
529
530 LOCK_DATA();
531 deinit_event_thread(thd);
532 scheduler_thd= NULL;
533 state= INITIALIZED;
534 DBUG_PRINT("info", ("Broadcasting COND_state back to the stoppers"));
535 mysql_cond_broadcast(&COND_state);
536 UNLOCK_DATA();
537
538 DBUG_RETURN(res);
539 }
540
541
542 /*
543 Creates a new THD instance and then forks a new thread, while passing
544 the THD pointer and job_data to it.
545
546 SYNOPSIS
547 Event_scheduler::execute_top()
548
549 RETURN VALUE
550 FALSE OK
551 TRUE Error (Serious error)
552 */
553
554 bool
execute_top(Event_queue_element_for_exec * event_name)555 Event_scheduler::execute_top(Event_queue_element_for_exec *event_name)
556 {
557 THD *new_thd;
558 pthread_t th;
559 int res= 0;
560 DBUG_ENTER("Event_scheduler::execute_top");
561 if (!(new_thd= new THD()))
562 goto error;
563
564 pre_init_event_thread(new_thd);
565 new_thd->system_thread= SYSTEM_THREAD_EVENT_WORKER;
566 event_name->thd= new_thd;
567 DBUG_PRINT("info", ("Event %s@%s ready for start",
568 event_name->dbname.str, event_name->name.str));
569
570 /*
571 TODO: should use thread pool here, preferably with an upper limit
572 on number of threads: if too many events are scheduled for the
573 same time, starting all of them at once won't help them run truly
574 in parallel (because of the great amount of synchronization), so
575 we may as well execute them in sequence, keeping concurrency at a
576 reasonable level.
577 */
578 /* Major failure */
579 if ((res= mysql_thread_create(key_thread_event_worker,
580 &th, &connection_attrib, event_worker_thread,
581 event_name)))
582 {
583 mysql_mutex_lock(&LOCK_global_system_variables);
584 Events::opt_event_scheduler= Events::EVENTS_OFF;
585 mysql_mutex_unlock(&LOCK_global_system_variables);
586
587 sql_print_error("Event_scheduler::execute_top: Can not create event worker"
588 " thread (errno=%d). Stopping event scheduler", res);
589
590 new_thd->proc_info= "Clearing";
591 DBUG_ASSERT(new_thd->net.buff != 0);
592 net_end(&new_thd->net);
593
594 goto error;
595 }
596
597 ++started_events;
598
599 DBUG_PRINT("info", ("Event is in THD: 0x%lx", (long) new_thd));
600 DBUG_RETURN(FALSE);
601
602 error:
603 DBUG_PRINT("error", ("Event_scheduler::execute_top() res: %d", res));
604 if (new_thd)
605 delete new_thd;
606
607 delete event_name;
608 DBUG_RETURN(TRUE);
609 }
610
611
612 /*
613 Checks whether the state of the scheduler is RUNNING
614
615 SYNOPSIS
616 Event_scheduler::is_running()
617
618 RETURN VALUE
619 TRUE RUNNING
620 FALSE Not RUNNING
621 */
622
623 bool
is_running()624 Event_scheduler::is_running()
625 {
626 LOCK_DATA();
627 bool ret= (state == RUNNING);
628 UNLOCK_DATA();
629 return ret;
630 }
631
632
633 /**
634 Stops the scheduler (again). Waits for acknowledgement from the
635 scheduler that it has stopped - synchronous stopping.
636
637 Already running events will not be stopped. If the user needs
638 them stopped manual intervention is needed.
639
640 SYNOPSIS
641 Event_scheduler::stop()
642
643 RETURN VALUE
644 FALSE OK
645 TRUE Error (not reported)
646 */
647
648 bool
stop()649 Event_scheduler::stop()
650 {
651 THD *thd= current_thd;
652 DBUG_ENTER("Event_scheduler::stop");
653 DBUG_PRINT("enter", ("thd: 0x%lx", (long) thd));
654
655 LOCK_DATA();
656 DBUG_PRINT("info", ("state before action %s", scheduler_states_names[state].str));
657 if (state != RUNNING)
658 {
659 /* Synchronously wait until the scheduler stops. */
660 while (state != INITIALIZED)
661 COND_STATE_WAIT(thd, NULL, &stage_waiting_for_scheduler_to_stop);
662 goto end;
663 }
664
665 /* Guarantee we don't catch spurious signals */
666 do {
667 DBUG_PRINT("info", ("Waiting for COND_started_or_stopped from "
668 "the scheduler thread. Current value of state is %s . "
669 "workers count=%d", scheduler_states_names[state].str,
670 workers_count()));
671 /*
672 NOTE: We don't use kill_one_thread() because it can't kill COM_DEAMON
673 threads. In addition, kill_one_thread() requires THD but during shutdown
674 current_thd is NULL. Hence, if kill_one_thread should be used it has to
675 be modified to kill also daemons, by adding a flag, and also we have to
676 create artificial THD here. To save all this work, we just do what
677 kill_one_thread() does to kill a thread. See also sql_repl.cc for similar
678 usage.
679 */
680
681 state= STOPPING;
682 DBUG_PRINT("info", ("Scheduler thread has id %lu",
683 scheduler_thd->thread_id));
684 /* Lock from delete */
685 mysql_mutex_lock(&scheduler_thd->LOCK_thd_data);
686 /* This will wake up the thread if it waits on Queue's conditional */
687 sql_print_information("Event Scheduler: Killing the scheduler thread, "
688 "thread id %lu",
689 scheduler_thd->thread_id);
690 scheduler_thd->awake(THD::KILL_CONNECTION);
691 mysql_mutex_unlock(&scheduler_thd->LOCK_thd_data);
692
693 /* thd could be 0x0, when shutting down */
694 sql_print_information("Event Scheduler: "
695 "Waiting for the scheduler thread to reply");
696 COND_STATE_WAIT(thd, NULL, &stage_waiting_for_scheduler_to_stop);
697 } while (state == STOPPING);
698 DBUG_PRINT("info", ("Scheduler thread has cleaned up. Set state to INIT"));
699 sql_print_information("Event Scheduler: Stopped");
700 end:
701 UNLOCK_DATA();
702 DBUG_RETURN(FALSE);
703 }
704
705
706 /*
707 Returns the number of living event worker threads.
708
709 SYNOPSIS
710 Event_scheduler::workers_count()
711 */
712
713 uint
workers_count()714 Event_scheduler::workers_count()
715 {
716 uint count= 0;
717
718 DBUG_ENTER("Event_scheduler::workers_count");
719 mysql_mutex_lock(&LOCK_thread_count);
720 Thread_iterator it= global_thread_list_begin();
721 Thread_iterator end= global_thread_list_end();
722 for (; it != end; ++it)
723 if ((*it)->system_thread == SYSTEM_THREAD_EVENT_WORKER)
724 ++count;
725 mysql_mutex_unlock(&LOCK_thread_count);
726 DBUG_PRINT("exit", ("%d", count));
727 DBUG_RETURN(count);
728 }
729
730
731 /*
732 Auxiliary function for locking LOCK_scheduler_state. Used
733 by the LOCK_DATA macro.
734
735 SYNOPSIS
736 Event_scheduler::lock_data()
737 func Which function is requesting mutex lock
738 line On which line mutex lock is requested
739 */
740
741 void
lock_data(const char * func,uint line)742 Event_scheduler::lock_data(const char *func, uint line)
743 {
744 DBUG_ENTER("Event_scheduler::lock_data");
745 DBUG_PRINT("enter", ("func=%s line=%u", func, line));
746 mysql_mutex_lock(&LOCK_scheduler_state);
747 mutex_last_locked_in_func= func;
748 mutex_last_locked_at_line= line;
749 mutex_scheduler_data_locked= TRUE;
750 DBUG_VOID_RETURN;
751 }
752
753
754 /*
755 Auxiliary function for unlocking LOCK_scheduler_state. Used
756 by the UNLOCK_DATA macro.
757
758 SYNOPSIS
759 Event_scheduler::unlock_data()
760 func Which function is requesting mutex unlock
761 line On which line mutex unlock is requested
762 */
763
764 void
unlock_data(const char * func,uint line)765 Event_scheduler::unlock_data(const char *func, uint line)
766 {
767 DBUG_ENTER("Event_scheduler::unlock_data");
768 DBUG_PRINT("enter", ("func=%s line=%u", func, line));
769 mutex_last_unlocked_at_line= line;
770 mutex_scheduler_data_locked= FALSE;
771 mutex_last_unlocked_in_func= func;
772 mysql_mutex_unlock(&LOCK_scheduler_state);
773 DBUG_VOID_RETURN;
774 }
775
776
777 /*
778 Wrapper for mysql_cond_wait/timedwait
779
780 SYNOPSIS
781 Event_scheduler::cond_wait()
782 thd Thread (Could be NULL during shutdown procedure)
783 abstime If not null then call mysql_cond_timedwait()
784 msg Message for thd->proc_info
785 func Which function is requesting cond_wait
786 line On which line cond_wait is requested
787 */
788
789 void
cond_wait(THD * thd,struct timespec * abstime,const PSI_stage_info * stage,const char * src_func,const char * src_file,uint src_line)790 Event_scheduler::cond_wait(THD *thd, struct timespec *abstime, const PSI_stage_info *stage,
791 const char *src_func, const char *src_file, uint src_line)
792 {
793 DBUG_ENTER("Event_scheduler::cond_wait");
794 waiting_on_cond= TRUE;
795 mutex_last_unlocked_at_line= src_line;
796 mutex_scheduler_data_locked= FALSE;
797 mutex_last_unlocked_in_func= src_func;
798 if (thd)
799 thd->enter_cond(&COND_state, &LOCK_scheduler_state, stage,
800 NULL, src_func, src_file, src_line);
801
802 DBUG_PRINT("info", ("mysql_cond_%swait", abstime? "timed":""));
803 if (!abstime)
804 mysql_cond_wait(&COND_state, &LOCK_scheduler_state);
805 else
806 mysql_cond_timedwait(&COND_state, &LOCK_scheduler_state, abstime);
807 if (thd)
808 {
809 /*
810 This will free the lock so we need to relock. Not the best thing to
811 do but we need to obey cond_wait()
812 */
813 thd->exit_cond(NULL, src_func, src_file, src_line);
814 LOCK_DATA();
815 }
816 mutex_last_locked_in_func= src_func;
817 mutex_last_locked_at_line= src_line;
818 mutex_scheduler_data_locked= TRUE;
819 waiting_on_cond= FALSE;
820 DBUG_VOID_RETURN;
821 }
822
823
824 /*
825 Dumps the internal status of the scheduler
826
827 SYNOPSIS
828 Event_scheduler::dump_internal_status()
829 */
830
831 void
dump_internal_status()832 Event_scheduler::dump_internal_status()
833 {
834 DBUG_ENTER("Event_scheduler::dump_internal_status");
835
836 puts("");
837 puts("Event scheduler status:");
838 printf("State : %s\n", scheduler_states_names[state].str);
839 printf("Thread id : %lu\n", scheduler_thd? scheduler_thd->thread_id : 0);
840 printf("LLA : %s:%u\n", mutex_last_locked_in_func,
841 mutex_last_locked_at_line);
842 printf("LUA : %s:%u\n", mutex_last_unlocked_in_func,
843 mutex_last_unlocked_at_line);
844 printf("WOC : %s\n", waiting_on_cond? "YES":"NO");
845 printf("Workers : %u\n", workers_count());
846 printf("Executed : %lu\n", (ulong) started_events);
847 printf("Data locked: %s\n", mutex_scheduler_data_locked ? "YES":"NO");
848
849 DBUG_VOID_RETURN;
850 }
851
852 /**
853 @} (End of group Event_Scheduler)
854 */
855