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