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