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
test_timerfd_oneshot()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;
test_timerfd_unset()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
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
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
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
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
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
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
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 
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 
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
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
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
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
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
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
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
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
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
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
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