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