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 #ifdef WITH_WSREP
334 thd->wsrep_query_state= QUERY_EXEC;
335 #endif /* WITH_WSREP */
336 res= job_data.execute(thd, event->dropped);
337
338 print_warnings(thd, &job_data);
339
340 if (res)
341 sql_print_information("Event Scheduler: "
342 "[%s].[%s.%s] event execution failed.",
343 job_data.definer.str,
344 job_data.dbname.str, job_data.name.str);
345 end:
346 DBUG_ASSERT(thd->m_statement_psi == NULL);
347 DBUG_ASSERT(thd->m_digest == NULL);
348
349 DBUG_PRINT("info", ("Done with Event %s.%s", event->dbname.str,
350 event->name.str));
351
352 delete event;
353 deinit_event_thread(thd);
354
355 DBUG_VOID_RETURN;
356 }
357
358
Event_scheduler(Event_queue * queue_arg)359 Event_scheduler::Event_scheduler(Event_queue *queue_arg)
360 :state(INITIALIZED),
361 scheduler_thd(NULL),
362 queue(queue_arg),
363 mutex_last_locked_at_line(0),
364 mutex_last_unlocked_at_line(0),
365 mutex_last_locked_in_func("n/a"),
366 mutex_last_unlocked_in_func("n/a"),
367 mutex_scheduler_data_locked(FALSE),
368 waiting_on_cond(FALSE),
369 started_events(0)
370 {
371 mysql_mutex_init(key_event_scheduler_LOCK_scheduler_state,
372 &LOCK_scheduler_state, MY_MUTEX_INIT_FAST);
373 mysql_cond_init(key_event_scheduler_COND_state, &COND_state, NULL);
374 }
375
376
~Event_scheduler()377 Event_scheduler::~Event_scheduler()
378 {
379 stop(); /* does nothing if not running */
380 mysql_mutex_destroy(&LOCK_scheduler_state);
381 mysql_cond_destroy(&COND_state);
382 }
383
384
385 /**
386 Starts the scheduler (again). Creates a new THD and passes it to
387 a forked thread. Does not wait for acknowledgement from the new
388 thread that it has started. Asynchronous starting. Most of the
389 needed initializations are done in the current thread to minimize
390 the chance of failure in the spawned thread.
391
392 @param[out] err_no - errno indicating type of error which caused
393 failure to start scheduler thread.
394
395 @return
396 @retval false Success.
397 @retval true Error.
398 */
399
400 bool
start(int * err_no)401 Event_scheduler::start(int *err_no)
402 {
403 THD *new_thd= NULL;
404 bool ret= false;
405 pthread_t th;
406 struct scheduler_param *scheduler_param_value;
407 DBUG_ENTER("Event_scheduler::start");
408
409 LOCK_DATA();
410 DBUG_PRINT("info", ("state before action %s", scheduler_states_names[state].str));
411 if (state > INITIALIZED)
412 goto end;
413
414 DBUG_EXECUTE_IF("event_scheduler_thread_create_failure", {
415 *err_no= 11;
416 Events::opt_event_scheduler= Events::EVENTS_OFF;
417 ret= true;
418 goto end; });
419
420 if (!(new_thd= new THD))
421 {
422 sql_print_error("Event Scheduler: Cannot initialize the scheduler thread");
423 ret= true;
424 goto end;
425 }
426 pre_init_event_thread(new_thd);
427 new_thd->system_thread= SYSTEM_THREAD_EVENT_SCHEDULER;
428 new_thd->set_command(COM_DAEMON);
429
430 /*
431 We should run the event scheduler thread under the super-user privileges.
432 In particular, this is needed to be able to lock the mysql.event table
433 for writing when the server is running in the read-only mode.
434
435 Same goes for transaction access mode. Set it to read-write for this thd.
436 */
437 new_thd->security_ctx->master_access |= SUPER_ACL;
438 new_thd->variables.tx_read_only= false;
439 new_thd->tx_read_only= false;
440
441 scheduler_param_value=
442 (struct scheduler_param *)my_malloc(sizeof(struct scheduler_param), MYF(0));
443 scheduler_param_value->thd= new_thd;
444 scheduler_param_value->scheduler= this;
445
446 scheduler_thd= new_thd;
447 DBUG_PRINT("info", ("Setting state go RUNNING"));
448 state= RUNNING;
449 DBUG_PRINT("info", ("Forking new thread for scheduler. THD: 0x%lx", (long) new_thd));
450 if ((*err_no= mysql_thread_create(key_thread_event_scheduler,
451 &th, &connection_attrib,
452 event_scheduler_thread,
453 (void*)scheduler_param_value)))
454 {
455 DBUG_PRINT("error", ("cannot create a new thread"));
456 sql_print_error("Event scheduler: Failed to start scheduler,"
457 " Can not create thread for event scheduler (errno=%d)",
458 *err_no);
459
460 new_thd->proc_info= "Clearing";
461 DBUG_ASSERT(new_thd->net.buff != 0);
462 net_end(&new_thd->net);
463
464 state= INITIALIZED;
465 scheduler_thd= NULL;
466 delete new_thd;
467
468 delete scheduler_param_value;
469 ret= true;
470 }
471
472 end:
473 UNLOCK_DATA();
474 DBUG_RETURN(ret);
475 }
476
477
478 /*
479 The main loop of the scheduler.
480
481 SYNOPSIS
482 Event_scheduler::run()
483 thd Thread
484
485 RETURN VALUE
486 FALSE OK
487 TRUE Error (Serious error)
488 */
489
490 bool
run(THD * thd)491 Event_scheduler::run(THD *thd)
492 {
493 int res= FALSE;
494 DBUG_ENTER("Event_scheduler::run");
495
496 sql_print_information("Event Scheduler: scheduler thread started with id %lu",
497 thd->thread_id);
498 /*
499 Recalculate the values in the queue because there could have been stops
500 in executions of the scheduler and some times could have passed by.
501 */
502 queue->recalculate_activation_times(thd);
503
504 while (is_running())
505 {
506 Event_queue_element_for_exec *event_name;
507
508 /* Gets a minimized version */
509 if (queue->get_top_for_execution_if_time(thd, &event_name))
510 {
511 sql_print_information("Event Scheduler: "
512 "Serious error during getting next "
513 "event to execute. Stopping");
514 break;
515 }
516
517 DBUG_PRINT("info", ("get_top_for_execution_if_time returned "
518 "event_name=0x%lx", (long) event_name));
519 if (event_name)
520 {
521 if ((res= execute_top(event_name)))
522 break;
523 }
524 else
525 {
526 DBUG_ASSERT(thd->killed);
527 DBUG_PRINT("info", ("job_data is NULL, the thread was killed"));
528 }
529 DBUG_PRINT("info", ("state=%s", scheduler_states_names[state].str));
530 free_root(thd->mem_root, MYF(0));
531 }
532
533 LOCK_DATA();
534 deinit_event_thread(thd);
535 scheduler_thd= NULL;
536 state= INITIALIZED;
537 DBUG_PRINT("info", ("Broadcasting COND_state back to the stoppers"));
538 mysql_cond_broadcast(&COND_state);
539 UNLOCK_DATA();
540
541 DBUG_RETURN(res);
542 }
543
544
545 /*
546 Creates a new THD instance and then forks a new thread, while passing
547 the THD pointer and job_data to it.
548
549 SYNOPSIS
550 Event_scheduler::execute_top()
551
552 RETURN VALUE
553 FALSE OK
554 TRUE Error (Serious error)
555 */
556
557 bool
execute_top(Event_queue_element_for_exec * event_name)558 Event_scheduler::execute_top(Event_queue_element_for_exec *event_name)
559 {
560 THD *new_thd;
561 pthread_t th;
562 int res= 0;
563 DBUG_ENTER("Event_scheduler::execute_top");
564 if (!(new_thd= new THD()))
565 goto error;
566
567 pre_init_event_thread(new_thd);
568 new_thd->system_thread= SYSTEM_THREAD_EVENT_WORKER;
569 event_name->thd= new_thd;
570 DBUG_PRINT("info", ("Event %s@%s ready for start",
571 event_name->dbname.str, event_name->name.str));
572
573 /*
574 TODO: should use thread pool here, preferably with an upper limit
575 on number of threads: if too many events are scheduled for the
576 same time, starting all of them at once won't help them run truly
577 in parallel (because of the great amount of synchronization), so
578 we may as well execute them in sequence, keeping concurrency at a
579 reasonable level.
580 */
581 /* Major failure */
582 if ((res= mysql_thread_create(key_thread_event_worker,
583 &th, &connection_attrib, event_worker_thread,
584 event_name)))
585 {
586 mysql_mutex_lock(&LOCK_global_system_variables);
587 Events::opt_event_scheduler= Events::EVENTS_OFF;
588 mysql_mutex_unlock(&LOCK_global_system_variables);
589
590 sql_print_error("Event_scheduler::execute_top: Can not create event worker"
591 " thread (errno=%d). Stopping event scheduler", res);
592
593 new_thd->proc_info= "Clearing";
594 DBUG_ASSERT(new_thd->net.buff != 0);
595 net_end(&new_thd->net);
596
597 goto error;
598 }
599
600 ++started_events;
601
602 DBUG_PRINT("info", ("Event is in THD: 0x%lx", (long) new_thd));
603 DBUG_RETURN(FALSE);
604
605 error:
606 DBUG_PRINT("error", ("Event_scheduler::execute_top() res: %d", res));
607 if (new_thd)
608 delete new_thd;
609
610 delete event_name;
611 DBUG_RETURN(TRUE);
612 }
613
614
615 /*
616 Checks whether the state of the scheduler is RUNNING
617
618 SYNOPSIS
619 Event_scheduler::is_running()
620
621 RETURN VALUE
622 TRUE RUNNING
623 FALSE Not RUNNING
624 */
625
626 bool
is_running()627 Event_scheduler::is_running()
628 {
629 LOCK_DATA();
630 bool ret= (state == RUNNING);
631 UNLOCK_DATA();
632 return ret;
633 }
634
635
636 /**
637 Stops the scheduler (again). Waits for acknowledgement from the
638 scheduler that it has stopped - synchronous stopping.
639
640 Already running events will not be stopped. If the user needs
641 them stopped manual intervention is needed.
642
643 SYNOPSIS
644 Event_scheduler::stop()
645
646 RETURN VALUE
647 FALSE OK
648 TRUE Error (not reported)
649 */
650
651 bool
stop()652 Event_scheduler::stop()
653 {
654 THD *thd= current_thd;
655 DBUG_ENTER("Event_scheduler::stop");
656 DBUG_PRINT("enter", ("thd: 0x%lx", (long) thd));
657
658 LOCK_DATA();
659 DBUG_PRINT("info", ("state before action %s", scheduler_states_names[state].str));
660 if (state != RUNNING)
661 {
662 /* Synchronously wait until the scheduler stops. */
663 while (state != INITIALIZED)
664 COND_STATE_WAIT(thd, NULL, &stage_waiting_for_scheduler_to_stop);
665 goto end;
666 }
667
668 /* Guarantee we don't catch spurious signals */
669 do {
670 DBUG_PRINT("info", ("Waiting for COND_started_or_stopped from "
671 "the scheduler thread. Current value of state is %s . "
672 "workers count=%d", scheduler_states_names[state].str,
673 workers_count()));
674 /*
675 NOTE: We don't use kill_one_thread() because it can't kill COM_DEAMON
676 threads. In addition, kill_one_thread() requires THD but during shutdown
677 current_thd is NULL. Hence, if kill_one_thread should be used it has to
678 be modified to kill also daemons, by adding a flag, and also we have to
679 create artificial THD here. To save all this work, we just do what
680 kill_one_thread() does to kill a thread. See also sql_repl.cc for similar
681 usage.
682 */
683
684 state= STOPPING;
685 DBUG_PRINT("info", ("Scheduler thread has id %lu",
686 scheduler_thd->thread_id));
687 /* Lock from delete */
688 mysql_mutex_lock(&scheduler_thd->LOCK_thd_data);
689 /* This will wake up the thread if it waits on Queue's conditional */
690 sql_print_information("Event Scheduler: Killing the scheduler thread, "
691 "thread id %lu",
692 scheduler_thd->thread_id);
693 scheduler_thd->awake(THD::KILL_CONNECTION);
694 mysql_mutex_unlock(&scheduler_thd->LOCK_thd_data);
695
696 /* thd could be 0x0, when shutting down */
697 sql_print_information("Event Scheduler: "
698 "Waiting for the scheduler thread to reply");
699 COND_STATE_WAIT(thd, NULL, &stage_waiting_for_scheduler_to_stop);
700 } while (state == STOPPING);
701 DBUG_PRINT("info", ("Scheduler thread has cleaned up. Set state to INIT"));
702 sql_print_information("Event Scheduler: Stopped");
703 end:
704 UNLOCK_DATA();
705 DBUG_RETURN(FALSE);
706 }
707
708
709 /*
710 Returns the number of living event worker threads.
711
712 SYNOPSIS
713 Event_scheduler::workers_count()
714 */
715
716 uint
workers_count()717 Event_scheduler::workers_count()
718 {
719 uint count= 0;
720
721 DBUG_ENTER("Event_scheduler::workers_count");
722 mysql_mutex_lock(&LOCK_thread_count);
723 Thread_iterator it= global_thread_list_begin();
724 Thread_iterator end= global_thread_list_end();
725 for (; it != end; ++it)
726 if ((*it)->system_thread == SYSTEM_THREAD_EVENT_WORKER)
727 ++count;
728 mysql_mutex_unlock(&LOCK_thread_count);
729 DBUG_PRINT("exit", ("%d", count));
730 DBUG_RETURN(count);
731 }
732
733
734 /*
735 Auxiliary function for locking LOCK_scheduler_state. Used
736 by the LOCK_DATA macro.
737
738 SYNOPSIS
739 Event_scheduler::lock_data()
740 func Which function is requesting mutex lock
741 line On which line mutex lock is requested
742 */
743
744 void
lock_data(const char * func,uint line)745 Event_scheduler::lock_data(const char *func, uint line)
746 {
747 DBUG_ENTER("Event_scheduler::lock_data");
748 DBUG_PRINT("enter", ("func=%s line=%u", func, line));
749 mysql_mutex_lock(&LOCK_scheduler_state);
750 mutex_last_locked_in_func= func;
751 mutex_last_locked_at_line= line;
752 mutex_scheduler_data_locked= TRUE;
753 DBUG_VOID_RETURN;
754 }
755
756
757 /*
758 Auxiliary function for unlocking LOCK_scheduler_state. Used
759 by the UNLOCK_DATA macro.
760
761 SYNOPSIS
762 Event_scheduler::unlock_data()
763 func Which function is requesting mutex unlock
764 line On which line mutex unlock is requested
765 */
766
767 void
unlock_data(const char * func,uint line)768 Event_scheduler::unlock_data(const char *func, uint line)
769 {
770 DBUG_ENTER("Event_scheduler::unlock_data");
771 DBUG_PRINT("enter", ("func=%s line=%u", func, line));
772 mutex_last_unlocked_at_line= line;
773 mutex_scheduler_data_locked= FALSE;
774 mutex_last_unlocked_in_func= func;
775 mysql_mutex_unlock(&LOCK_scheduler_state);
776 DBUG_VOID_RETURN;
777 }
778
779
780 /*
781 Wrapper for mysql_cond_wait/timedwait
782
783 SYNOPSIS
784 Event_scheduler::cond_wait()
785 thd Thread (Could be NULL during shutdown procedure)
786 abstime If not null then call mysql_cond_timedwait()
787 msg Message for thd->proc_info
788 func Which function is requesting cond_wait
789 line On which line cond_wait is requested
790 */
791
792 void
cond_wait(THD * thd,struct timespec * abstime,const PSI_stage_info * stage,const char * src_func,const char * src_file,uint src_line)793 Event_scheduler::cond_wait(THD *thd, struct timespec *abstime, const PSI_stage_info *stage,
794 const char *src_func, const char *src_file, uint src_line)
795 {
796 DBUG_ENTER("Event_scheduler::cond_wait");
797 waiting_on_cond= TRUE;
798 mutex_last_unlocked_at_line= src_line;
799 mutex_scheduler_data_locked= FALSE;
800 mutex_last_unlocked_in_func= src_func;
801 if (thd)
802 thd->enter_cond(&COND_state, &LOCK_scheduler_state, stage,
803 NULL, src_func, src_file, src_line);
804
805 DBUG_PRINT("info", ("mysql_cond_%swait", abstime? "timed":""));
806 if (!abstime)
807 mysql_cond_wait(&COND_state, &LOCK_scheduler_state);
808 else
809 mysql_cond_timedwait(&COND_state, &LOCK_scheduler_state, abstime);
810 if (thd)
811 {
812 /*
813 This will free the lock so we need to relock. Not the best thing to
814 do but we need to obey cond_wait()
815 */
816 thd->exit_cond(NULL, src_func, src_file, src_line);
817 LOCK_DATA();
818 }
819 mutex_last_locked_in_func= src_func;
820 mutex_last_locked_at_line= src_line;
821 mutex_scheduler_data_locked= TRUE;
822 waiting_on_cond= FALSE;
823 DBUG_VOID_RETURN;
824 }
825
826
827 /*
828 Dumps the internal status of the scheduler
829
830 SYNOPSIS
831 Event_scheduler::dump_internal_status()
832 */
833
834 void
dump_internal_status()835 Event_scheduler::dump_internal_status()
836 {
837 DBUG_ENTER("Event_scheduler::dump_internal_status");
838
839 puts("");
840 puts("Event scheduler status:");
841 printf("State : %s\n", scheduler_states_names[state].str);
842 printf("Thread id : %lu\n", scheduler_thd? scheduler_thd->thread_id : 0);
843 printf("LLA : %s:%u\n", mutex_last_locked_in_func,
844 mutex_last_locked_at_line);
845 printf("LUA : %s:%u\n", mutex_last_unlocked_in_func,
846 mutex_last_unlocked_at_line);
847 printf("WOC : %s\n", waiting_on_cond? "YES":"NO");
848 printf("Workers : %u\n", workers_count());
849 printf("Executed : %lu\n", (ulong) started_events);
850 printf("Data locked: %s\n", mutex_scheduler_data_locked ? "YES":"NO");
851
852 DBUG_VOID_RETURN;
853 }
854
855 /**
856 @} (End of group Event_Scheduler)
857 */
858