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