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 #ifdef WITH_WSREP
334   thd->wsrep_query_state= QUERY_EXEC;
335 #endif /* WITH_WSREP */
336   res= job_data.execute(thd, event->dropped);
337 
338   print_warnings(thd, &job_data);
339 
340   if (res)
341     sql_print_information("Event Scheduler: "
342                           "[%s].[%s.%s] event execution failed.",
343                           job_data.definer.str,
344                           job_data.dbname.str, job_data.name.str);
345 end:
346   DBUG_ASSERT(thd->m_statement_psi == NULL);
347   DBUG_ASSERT(thd->m_digest == NULL);
348 
349   DBUG_PRINT("info", ("Done with Event %s.%s", event->dbname.str,
350              event->name.str));
351 
352   delete event;
353   deinit_event_thread(thd);
354 
355   DBUG_VOID_RETURN;
356 }
357 
358 
Event_scheduler(Event_queue * queue_arg)359 Event_scheduler::Event_scheduler(Event_queue *queue_arg)
360   :state(INITIALIZED),
361   scheduler_thd(NULL),
362   queue(queue_arg),
363   mutex_last_locked_at_line(0),
364   mutex_last_unlocked_at_line(0),
365   mutex_last_locked_in_func("n/a"),
366   mutex_last_unlocked_in_func("n/a"),
367   mutex_scheduler_data_locked(FALSE),
368   waiting_on_cond(FALSE),
369   started_events(0)
370 {
371   mysql_mutex_init(key_event_scheduler_LOCK_scheduler_state,
372                    &LOCK_scheduler_state, MY_MUTEX_INIT_FAST);
373   mysql_cond_init(key_event_scheduler_COND_state, &COND_state, NULL);
374 }
375 
376 
~Event_scheduler()377 Event_scheduler::~Event_scheduler()
378 {
379   stop();                                    /* does nothing if not running */
380   mysql_mutex_destroy(&LOCK_scheduler_state);
381   mysql_cond_destroy(&COND_state);
382 }
383 
384 
385 /**
386   Starts the scheduler (again). Creates a new THD and passes it to
387   a forked thread. Does not wait for acknowledgement from the new
388   thread that it has started. Asynchronous starting. Most of the
389   needed initializations are done in the current thread to minimize
390   the chance of failure in the spawned thread.
391 
392   @param[out] err_no - errno indicating type of error which caused
393                        failure to start scheduler thread.
394 
395   @return
396     @retval false Success.
397     @retval true  Error.
398 */
399 
400 bool
start(int * err_no)401 Event_scheduler::start(int *err_no)
402 {
403   THD *new_thd= NULL;
404   bool ret= false;
405   pthread_t th;
406   struct scheduler_param *scheduler_param_value;
407   DBUG_ENTER("Event_scheduler::start");
408 
409   LOCK_DATA();
410   DBUG_PRINT("info", ("state before action %s", scheduler_states_names[state].str));
411   if (state > INITIALIZED)
412     goto end;
413 
414   DBUG_EXECUTE_IF("event_scheduler_thread_create_failure", {
415                   *err_no= 11;
416                   Events::opt_event_scheduler= Events::EVENTS_OFF;
417                   ret= true;
418                   goto end; });
419 
420   if (!(new_thd= new THD))
421   {
422     sql_print_error("Event Scheduler: Cannot initialize the scheduler thread");
423     ret= true;
424     goto end;
425   }
426   pre_init_event_thread(new_thd);
427   new_thd->system_thread= SYSTEM_THREAD_EVENT_SCHEDULER;
428   new_thd->set_command(COM_DAEMON);
429 
430   /*
431     We should run the event scheduler thread under the super-user privileges.
432     In particular, this is needed to be able to lock the mysql.event table
433     for writing when the server is running in the read-only mode.
434 
435     Same goes for transaction access mode. Set it to read-write for this thd.
436   */
437   new_thd->security_ctx->master_access |= SUPER_ACL;
438   new_thd->variables.tx_read_only= false;
439   new_thd->tx_read_only= false;
440 
441   scheduler_param_value=
442     (struct scheduler_param *)my_malloc(sizeof(struct scheduler_param), MYF(0));
443   scheduler_param_value->thd= new_thd;
444   scheduler_param_value->scheduler= this;
445 
446   scheduler_thd= new_thd;
447   DBUG_PRINT("info", ("Setting state go RUNNING"));
448   state= RUNNING;
449   DBUG_PRINT("info", ("Forking new thread for scheduler. THD: 0x%lx", (long) new_thd));
450   if ((*err_no= mysql_thread_create(key_thread_event_scheduler,
451                                     &th, &connection_attrib,
452                                     event_scheduler_thread,
453                                     (void*)scheduler_param_value)))
454   {
455     DBUG_PRINT("error", ("cannot create a new thread"));
456     sql_print_error("Event scheduler: Failed to start scheduler,"
457                     " Can not create thread for event scheduler (errno=%d)",
458                     *err_no);
459 
460     new_thd->proc_info= "Clearing";
461     DBUG_ASSERT(new_thd->net.buff != 0);
462     net_end(&new_thd->net);
463 
464     state= INITIALIZED;
465     scheduler_thd= NULL;
466     delete new_thd;
467 
468     delete scheduler_param_value;
469     ret= true;
470   }
471 
472 end:
473   UNLOCK_DATA();
474   DBUG_RETURN(ret);
475 }
476 
477 
478 /*
479   The main loop of the scheduler.
480 
481   SYNOPSIS
482     Event_scheduler::run()
483       thd  Thread
484 
485   RETURN VALUE
486     FALSE  OK
487     TRUE   Error (Serious error)
488 */
489 
490 bool
run(THD * thd)491 Event_scheduler::run(THD *thd)
492 {
493   int res= FALSE;
494   DBUG_ENTER("Event_scheduler::run");
495 
496   sql_print_information("Event Scheduler: scheduler thread started with id %lu",
497                         thd->thread_id);
498   /*
499     Recalculate the values in the queue because there could have been stops
500     in executions of the scheduler and some times could have passed by.
501   */
502   queue->recalculate_activation_times(thd);
503 
504   while (is_running())
505   {
506     Event_queue_element_for_exec *event_name;
507 
508     /* Gets a minimized version */
509     if (queue->get_top_for_execution_if_time(thd, &event_name))
510     {
511       sql_print_information("Event Scheduler: "
512                             "Serious error during getting next "
513                             "event to execute. Stopping");
514       break;
515     }
516 
517     DBUG_PRINT("info", ("get_top_for_execution_if_time returned "
518                         "event_name=0x%lx", (long) event_name));
519     if (event_name)
520     {
521       if ((res= execute_top(event_name)))
522         break;
523     }
524     else
525     {
526       DBUG_ASSERT(thd->killed);
527       DBUG_PRINT("info", ("job_data is NULL, the thread was killed"));
528     }
529     DBUG_PRINT("info", ("state=%s", scheduler_states_names[state].str));
530     free_root(thd->mem_root, MYF(0));
531   }
532 
533   LOCK_DATA();
534   deinit_event_thread(thd);
535   scheduler_thd= NULL;
536   state= INITIALIZED;
537   DBUG_PRINT("info", ("Broadcasting COND_state back to the stoppers"));
538   mysql_cond_broadcast(&COND_state);
539   UNLOCK_DATA();
540 
541   DBUG_RETURN(res);
542 }
543 
544 
545 /*
546   Creates a new THD instance and then forks a new thread, while passing
547   the THD pointer and job_data to it.
548 
549   SYNOPSIS
550     Event_scheduler::execute_top()
551 
552   RETURN VALUE
553     FALSE  OK
554     TRUE   Error (Serious error)
555 */
556 
557 bool
execute_top(Event_queue_element_for_exec * event_name)558 Event_scheduler::execute_top(Event_queue_element_for_exec *event_name)
559 {
560   THD *new_thd;
561   pthread_t th;
562   int res= 0;
563   DBUG_ENTER("Event_scheduler::execute_top");
564   if (!(new_thd= new THD()))
565     goto error;
566 
567   pre_init_event_thread(new_thd);
568   new_thd->system_thread= SYSTEM_THREAD_EVENT_WORKER;
569   event_name->thd= new_thd;
570   DBUG_PRINT("info", ("Event %s@%s ready for start",
571              event_name->dbname.str, event_name->name.str));
572 
573   /*
574     TODO: should use thread pool here, preferably with an upper limit
575     on number of threads: if too many events are scheduled for the
576     same time, starting all of them at once won't help them run truly
577     in parallel (because of the great amount of synchronization), so
578     we may as well execute them in sequence, keeping concurrency at a
579     reasonable level.
580   */
581   /* Major failure */
582   if ((res= mysql_thread_create(key_thread_event_worker,
583                                 &th, &connection_attrib, event_worker_thread,
584                                 event_name)))
585   {
586     mysql_mutex_lock(&LOCK_global_system_variables);
587     Events::opt_event_scheduler= Events::EVENTS_OFF;
588     mysql_mutex_unlock(&LOCK_global_system_variables);
589 
590     sql_print_error("Event_scheduler::execute_top: Can not create event worker"
591                     " thread (errno=%d). Stopping event scheduler", res);
592 
593     new_thd->proc_info= "Clearing";
594     DBUG_ASSERT(new_thd->net.buff != 0);
595     net_end(&new_thd->net);
596 
597     goto error;
598   }
599 
600   ++started_events;
601 
602   DBUG_PRINT("info", ("Event is in THD: 0x%lx", (long) new_thd));
603   DBUG_RETURN(FALSE);
604 
605 error:
606   DBUG_PRINT("error", ("Event_scheduler::execute_top() res: %d", res));
607   if (new_thd)
608     delete new_thd;
609 
610   delete event_name;
611   DBUG_RETURN(TRUE);
612 }
613 
614 
615 /*
616   Checks whether the state of the scheduler is RUNNING
617 
618   SYNOPSIS
619     Event_scheduler::is_running()
620 
621   RETURN VALUE
622     TRUE   RUNNING
623     FALSE  Not RUNNING
624 */
625 
626 bool
is_running()627 Event_scheduler::is_running()
628 {
629   LOCK_DATA();
630   bool ret= (state == RUNNING);
631   UNLOCK_DATA();
632   return ret;
633 }
634 
635 
636 /**
637   Stops the scheduler (again). Waits for acknowledgement from the
638   scheduler that it has stopped - synchronous stopping.
639 
640   Already running events will not be stopped. If the user needs
641   them stopped manual intervention is needed.
642 
643   SYNOPSIS
644     Event_scheduler::stop()
645 
646   RETURN VALUE
647     FALSE  OK
648     TRUE   Error (not reported)
649 */
650 
651 bool
stop()652 Event_scheduler::stop()
653 {
654   THD *thd= current_thd;
655   DBUG_ENTER("Event_scheduler::stop");
656   DBUG_PRINT("enter", ("thd: 0x%lx", (long) thd));
657 
658   LOCK_DATA();
659   DBUG_PRINT("info", ("state before action %s", scheduler_states_names[state].str));
660   if (state != RUNNING)
661   {
662     /* Synchronously wait until the scheduler stops. */
663     while (state != INITIALIZED)
664       COND_STATE_WAIT(thd, NULL, &stage_waiting_for_scheduler_to_stop);
665     goto end;
666   }
667 
668   /* Guarantee we don't catch spurious signals */
669   do {
670     DBUG_PRINT("info", ("Waiting for COND_started_or_stopped from "
671                         "the scheduler thread.  Current value of state is %s . "
672                         "workers count=%d", scheduler_states_names[state].str,
673                         workers_count()));
674     /*
675       NOTE: We don't use kill_one_thread() because it can't kill COM_DEAMON
676       threads. In addition, kill_one_thread() requires THD but during shutdown
677       current_thd is NULL. Hence, if kill_one_thread should be used it has to
678       be modified to kill also daemons, by adding a flag, and also we have to
679       create artificial THD here. To save all this work, we just do what
680       kill_one_thread() does to kill a thread. See also sql_repl.cc for similar
681       usage.
682     */
683 
684     state= STOPPING;
685     DBUG_PRINT("info", ("Scheduler thread has id %lu",
686                         scheduler_thd->thread_id));
687     /* Lock from delete */
688     mysql_mutex_lock(&scheduler_thd->LOCK_thd_data);
689     /* This will wake up the thread if it waits on Queue's conditional */
690     sql_print_information("Event Scheduler: Killing the scheduler thread, "
691                           "thread id %lu",
692                           scheduler_thd->thread_id);
693     scheduler_thd->awake(THD::KILL_CONNECTION);
694     mysql_mutex_unlock(&scheduler_thd->LOCK_thd_data);
695 
696     /* thd could be 0x0, when shutting down */
697     sql_print_information("Event Scheduler: "
698                           "Waiting for the scheduler thread to reply");
699     COND_STATE_WAIT(thd, NULL, &stage_waiting_for_scheduler_to_stop);
700   } while (state == STOPPING);
701   DBUG_PRINT("info", ("Scheduler thread has cleaned up. Set state to INIT"));
702   sql_print_information("Event Scheduler: Stopped");
703 end:
704   UNLOCK_DATA();
705   DBUG_RETURN(FALSE);
706 }
707 
708 
709 /*
710   Returns the number of living event worker threads.
711 
712   SYNOPSIS
713     Event_scheduler::workers_count()
714 */
715 
716 uint
workers_count()717 Event_scheduler::workers_count()
718 {
719   uint count= 0;
720 
721   DBUG_ENTER("Event_scheduler::workers_count");
722   mysql_mutex_lock(&LOCK_thread_count);
723   Thread_iterator it= global_thread_list_begin();
724   Thread_iterator end= global_thread_list_end();
725   for (; it != end; ++it)
726     if ((*it)->system_thread == SYSTEM_THREAD_EVENT_WORKER)
727       ++count;
728   mysql_mutex_unlock(&LOCK_thread_count);
729   DBUG_PRINT("exit", ("%d", count));
730   DBUG_RETURN(count);
731 }
732 
733 
734 /*
735   Auxiliary function for locking LOCK_scheduler_state. Used
736   by the LOCK_DATA macro.
737 
738   SYNOPSIS
739     Event_scheduler::lock_data()
740       func  Which function is requesting mutex lock
741       line  On which line mutex lock is requested
742 */
743 
744 void
lock_data(const char * func,uint line)745 Event_scheduler::lock_data(const char *func, uint line)
746 {
747   DBUG_ENTER("Event_scheduler::lock_data");
748   DBUG_PRINT("enter", ("func=%s line=%u", func, line));
749   mysql_mutex_lock(&LOCK_scheduler_state);
750   mutex_last_locked_in_func= func;
751   mutex_last_locked_at_line= line;
752   mutex_scheduler_data_locked= TRUE;
753   DBUG_VOID_RETURN;
754 }
755 
756 
757 /*
758   Auxiliary function for unlocking LOCK_scheduler_state. Used
759   by the UNLOCK_DATA macro.
760 
761   SYNOPSIS
762     Event_scheduler::unlock_data()
763       func  Which function is requesting mutex unlock
764       line  On which line mutex unlock is requested
765 */
766 
767 void
unlock_data(const char * func,uint line)768 Event_scheduler::unlock_data(const char *func, uint line)
769 {
770   DBUG_ENTER("Event_scheduler::unlock_data");
771   DBUG_PRINT("enter", ("func=%s line=%u", func, line));
772   mutex_last_unlocked_at_line= line;
773   mutex_scheduler_data_locked= FALSE;
774   mutex_last_unlocked_in_func= func;
775   mysql_mutex_unlock(&LOCK_scheduler_state);
776   DBUG_VOID_RETURN;
777 }
778 
779 
780 /*
781   Wrapper for mysql_cond_wait/timedwait
782 
783   SYNOPSIS
784     Event_scheduler::cond_wait()
785       thd     Thread (Could be NULL during shutdown procedure)
786       abstime If not null then call mysql_cond_timedwait()
787       msg     Message for thd->proc_info
788       func    Which function is requesting cond_wait
789       line    On which line cond_wait is requested
790 */
791 
792 void
cond_wait(THD * thd,struct timespec * abstime,const PSI_stage_info * stage,const char * src_func,const char * src_file,uint src_line)793 Event_scheduler::cond_wait(THD *thd, struct timespec *abstime, const PSI_stage_info *stage,
794                            const char *src_func, const char *src_file, uint src_line)
795 {
796   DBUG_ENTER("Event_scheduler::cond_wait");
797   waiting_on_cond= TRUE;
798   mutex_last_unlocked_at_line= src_line;
799   mutex_scheduler_data_locked= FALSE;
800   mutex_last_unlocked_in_func= src_func;
801   if (thd)
802     thd->enter_cond(&COND_state, &LOCK_scheduler_state, stage,
803                     NULL, src_func, src_file, src_line);
804 
805   DBUG_PRINT("info", ("mysql_cond_%swait", abstime? "timed":""));
806   if (!abstime)
807     mysql_cond_wait(&COND_state, &LOCK_scheduler_state);
808   else
809     mysql_cond_timedwait(&COND_state, &LOCK_scheduler_state, abstime);
810   if (thd)
811   {
812     /*
813       This will free the lock so we need to relock. Not the best thing to
814       do but we need to obey cond_wait()
815     */
816     thd->exit_cond(NULL, src_func, src_file, src_line);
817     LOCK_DATA();
818   }
819   mutex_last_locked_in_func= src_func;
820   mutex_last_locked_at_line= src_line;
821   mutex_scheduler_data_locked= TRUE;
822   waiting_on_cond= FALSE;
823   DBUG_VOID_RETURN;
824 }
825 
826 
827 /*
828   Dumps the internal status of the scheduler
829 
830   SYNOPSIS
831     Event_scheduler::dump_internal_status()
832 */
833 
834 void
dump_internal_status()835 Event_scheduler::dump_internal_status()
836 {
837   DBUG_ENTER("Event_scheduler::dump_internal_status");
838 
839   puts("");
840   puts("Event scheduler status:");
841   printf("State      : %s\n", scheduler_states_names[state].str);
842   printf("Thread id  : %lu\n", scheduler_thd? scheduler_thd->thread_id : 0);
843   printf("LLA        : %s:%u\n", mutex_last_locked_in_func,
844                                  mutex_last_locked_at_line);
845   printf("LUA        : %s:%u\n", mutex_last_unlocked_in_func,
846                                  mutex_last_unlocked_at_line);
847   printf("WOC        : %s\n", waiting_on_cond? "YES":"NO");
848   printf("Workers    : %u\n", workers_count());
849   printf("Executed   : %lu\n", (ulong) started_events);
850   printf("Data locked: %s\n", mutex_scheduler_data_locked ? "YES":"NO");
851 
852   DBUG_VOID_RETURN;
853 }
854 
855 /**
856   @} (End of group Event_Scheduler)
857 */
858