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