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