event_scheduler.cc 21.4 KB
Newer Older
1
/* Copyright (C) 2004, 2010 Oracle and/or its affiliates. All rights reserved.
2 3 4

   This program is free software; you can redistribute it and/or modify
   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 8 9 10 11 12 13

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   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 17 18 19 20
#include "mysql_priv.h"
#include "events.h"
#include "event_data_objects.h"
#include "event_scheduler.h"
#include "event_queue.h"
21
#include "event_db_repository.h"
22

23 24 25 26 27
/**
  @addtogroup Event_Scheduler
  @{
*/

28 29 30 31 32 33 34 35
#ifdef __GNUC__
#if __GNUC__ >= 2
#define SCHED_FUNC __FUNCTION__
#endif
#else
#define SCHED_FUNC "<unknown>"
#endif

36 37 38 39
#define LOCK_DATA()       lock_data(SCHED_FUNC, __LINE__)
#define UNLOCK_DATA()     unlock_data(SCHED_FUNC, __LINE__)
#define COND_STATE_WAIT(mythd, abstime, msg) \
        cond_wait(mythd, abstime, msg, SCHED_FUNC, __LINE__)
40 41 42

extern pthread_attr_t connection_attrib;

43 44 45 46

Event_db_repository *Event_worker_thread::db_repository;


47
static
48
const LEX_STRING scheduler_states_names[] =
49
{
50 51 52
  { C_STRING_WITH_LEN("INITIALIZED") },
  { C_STRING_WITH_LEN("RUNNING") },
  { C_STRING_WITH_LEN("STOPPING") }
53 54
};

55 56 57 58 59
struct scheduler_param {
  THD *thd;
  Event_scheduler *scheduler;
};

60 61 62 63 64 65 66 67 68 69 70 71

/*
  Prints the stack of infos, warnings, errors from thd to
  the console so it can be fetched by the logs-into-tables and
  checked later.

  SYNOPSIS
    evex_print_warnings
      thd  Thread used during the execution of the event
      et   The event itself
*/

72 73
void
Event_worker_thread::print_warnings(THD *thd, Event_job_data *et)
74 75 76
{
  MYSQL_ERROR *err;
  DBUG_ENTER("evex_print_warnings");
Marc Alff's avatar
Marc Alff committed
77
  if (thd->warning_info->is_empty())
78 79 80 81 82 83
    DBUG_VOID_RETURN;

  char msg_buf[10 * STRING_BUFFER_USUAL_SIZE];
  char prefix_buf[5 * STRING_BUFFER_USUAL_SIZE];
  String prefix(prefix_buf, sizeof(prefix_buf), system_charset_info);
  prefix.length(0);
84
  prefix.append("Event Scheduler: [");
85

86
  prefix.append(et->definer.str, et->definer.length, system_charset_info);
87
  prefix.append("][", 2);
88
  prefix.append(et->dbname.str, et->dbname.length, system_charset_info);
89
  prefix.append('.');
90
  prefix.append(et->name.str, et->name.length, system_charset_info);
91 92
  prefix.append("] ", 2);

Marc Alff's avatar
Marc Alff committed
93
  List_iterator_fast<MYSQL_ERROR> it(thd->warning_info->warn_list());
94 95 96 97 98 99
  while ((err= it++))
  {
    String err_msg(msg_buf, sizeof(msg_buf), system_charset_info);
    /* set it to 0 or we start adding at the end. That's the trick ;) */
    err_msg.length(0);
    err_msg.append(prefix);
Marc Alff's avatar
Marc Alff committed
100 101 102 103 104
    err_msg.append(err->get_message_text(),
                   err->get_message_octet_length(), system_charset_info);
    DBUG_ASSERT(err->get_level() < 3);
    (sql_print_message_handlers[err->get_level()])("%*s", err_msg.length(),
                                                   err_msg.c_ptr());
105 106 107 108 109
  }
  DBUG_VOID_RETURN;
}


110 111 112 113 114 115
/*
  Performs post initialization of structures in a new thread.

  SYNOPSIS
    post_init_event_thread()
      thd  Thread
116 117 118 119

  NOTES
      Before this is called, one should not do any DBUG_XXX() calls.

120 121 122 123 124
*/

bool
post_init_event_thread(THD *thd)
{
125
  (void) init_new_connection_handler_thread();
126 127 128 129 130 131
  if (init_thr_lock() || thd->store_globals())
  {
    thd->cleanup();
    return TRUE;
  }

Marc Alff's avatar
Marc Alff committed
132
  mysql_mutex_lock(&LOCK_thread_count);
133 134
  threads.append(thd);
  thread_count++;
135
  inc_thread_running();
Marc Alff's avatar
Marc Alff committed
136
  mysql_mutex_unlock(&LOCK_thread_count);
137
  return FALSE;
138 139 140 141 142 143 144 145 146 147 148
}


/*
  Cleans up the THD and the threaded environment of the thread.

  SYNOPSIS
    deinit_event_thread()
      thd  Thread
*/

149
void
150 151 152 153 154
deinit_event_thread(THD *thd)
{
  thd->proc_info= "Clearing";
  DBUG_ASSERT(thd->net.buff != 0);
  net_end(&thd->net);
155
  DBUG_PRINT("exit", ("Event thread finishing"));
Marc Alff's avatar
Marc Alff committed
156
  mysql_mutex_lock(&LOCK_thread_count);
157
  thread_count--;
158
  dec_thread_running();
Mikael Ronstrom's avatar
Mikael Ronstrom committed
159
  delete thd;
Marc Alff's avatar
Marc Alff committed
160 161
  mysql_cond_broadcast(&COND_thread_count);
  mysql_mutex_unlock(&LOCK_thread_count);
162 163 164
}


165
/*
Marc Alff's avatar
Marc Alff committed
166
  Performs pre- mysql_thread_create() initialisation of THD. Do this
167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190
  in the thread that will pass THD to the child thread. In the
  child thread call post_init_event_thread().

  SYNOPSIS
    pre_init_event_thread()
      thd  The THD of the thread. Has to be allocated by the caller.

  NOTES
    1. The host of the thead is my_localhost
    2. thd->net is initted with NULL - no communication.
*/

void
pre_init_event_thread(THD* thd)
{
  DBUG_ENTER("pre_init_event_thread");
  thd->client_capabilities= 0;
  thd->security_ctx->master_access= 0;
  thd->security_ctx->db_access= 0;
  thd->security_ctx->host_or_ip= (char*)my_localhost;
  my_net_init(&thd->net, NULL);
  thd->security_ctx->set_user((char*)"event_scheduler");
  thd->net.read_timeout= slave_net_timeout;
  thd->slave_thread= 0;
191
  thd->variables.option_bits|= OPTION_AUTO_IS_NULL;
192
  thd->client_capabilities|= CLIENT_MULTI_RESULTS;
Marc Alff's avatar
Marc Alff committed
193
  mysql_mutex_lock(&LOCK_thread_count);
194
  thd->thread_id= thd->variables.pseudo_thread_id= thread_id++;
Marc Alff's avatar
Marc Alff committed
195
  mysql_mutex_unlock(&LOCK_thread_count);
196 197 198 199 200 201 202 203 204 205

  /*
    Guarantees that we will see the thread in SHOW PROCESSLIST though its
    vio is NULL.
  */

  thd->proc_info= "Initialized";
  thd->version= refresh_version;
  thd->set_time();

206 207 208
  /* Do not use user-supplied timeout value for system threads. */
  thd->variables.lock_wait_timeout= LONG_TIMEOUT;

209 210 211 212
  DBUG_VOID_RETURN;
}


213 214 215 216 217 218 219 220 221 222 223 224 225 226 227
/*
  Function that executes the scheduler,

  SYNOPSIS
    event_scheduler_thread()
      arg  Pointer to `struct scheduler_param`

  RETURN VALUE
    0  OK
*/

pthread_handler_t
event_scheduler_thread(void *arg)
{
  /* needs to be first for thread_stack */
228
  THD *thd= (THD *) ((struct scheduler_param *) arg)->thd;
229
  Event_scheduler *scheduler= ((struct scheduler_param *) arg)->scheduler;
230
  bool res;
231

232
  thd->thread_stack= (char *)&thd;              // remember where our stack is
Marc Alff's avatar
Marc Alff committed
233 234 235

  mysql_thread_set_psi_id(thd->thread_id);

236
  res= post_init_event_thread(thd);
237

238
  DBUG_ENTER("event_scheduler_thread");
239 240
  my_free((char*)arg, MYF(0));
  if (!res)
241
    scheduler->run(thd);
242

243
  DBUG_LEAVE;                               // Against gcc warnings
244
  my_thread_end();
245
  return 0;
246 247 248
}


249
/**
250
  Function that executes an event in a child thread. Setups the
251 252 253 254 255 256 257 258 259 260 261 262 263
  environment for the event execution and cleans after that.

  SYNOPSIS
    event_worker_thread()
      arg  The Event_job_data object to be processed

  RETURN VALUE
    0  OK
*/

pthread_handler_t
event_worker_thread(void *arg)
{
264
  THD *thd;
265
  Event_queue_element_for_exec *event= (Event_queue_element_for_exec *)arg;
266 267 268

  thd= event->thd;

Marc Alff's avatar
Marc Alff committed
269 270
  mysql_thread_set_psi_id(thd->thread_id);

271
  Event_worker_thread worker_thread;
272
  worker_thread.run(thd, event);
273

274
  my_thread_end();
275 276 277 278
  return 0;                                     // Can't return anything here
}


279
/**
280
  Function that executes an event in a child thread. Setups the
281 282 283 284 285 286 287 288 289 290 291
  environment for the event execution and cleans after that.

  SYNOPSIS
    Event_worker_thread::run()
      thd    Thread context
      event  The Event_queue_element_for_exec object to be processed
*/

void
Event_worker_thread::run(THD *thd, Event_queue_element_for_exec *event)
{
292 293
  /* needs to be first for thread_stack */
  char my_stack;
294
  Event_job_data job_data;
295 296 297 298 299
  bool res;

  thd->thread_stack= &my_stack;                // remember where our stack is
  res= post_init_event_thread(thd);

300
  DBUG_ENTER("Event_worker_thread::run");
301
  DBUG_PRINT("info", ("Time is %ld, THD: 0x%lx", (long) my_time(0), (long) thd));
302 303

  if (res)
304 305
    goto end;

306 307
  if ((res= db_repository->load_named_event(thd, event->dbname, event->name,
                                            &job_data)))
308
  {
309
    DBUG_PRINT("error", ("Got error from load_named_event"));
310 311 312 313 314
    goto end;
  }

  thd->enable_slow_log= TRUE;

315
  res= job_data.execute(thd, event->dropped);
316

317
  print_warnings(thd, &job_data);
318

319 320 321 322 323
  if (res)
    sql_print_information("Event Scheduler: "
                          "[%s].[%s.%s] event execution failed.",
                          job_data.definer.str,
                          job_data.dbname.str, job_data.name.str);
324
end:
325
  DBUG_PRINT("info", ("Done with Event %s.%s", event->dbname.str,
326 327
             event->name.str));

328
  delete event;
329
  deinit_event_thread(thd);
330 331

  DBUG_VOID_RETURN;
332 333 334
}


335
Event_scheduler::Event_scheduler(Event_queue *queue_arg)
336
  :state(INITIALIZED),
337 338
  scheduler_thd(NULL),
  queue(queue_arg),
339 340 341 342 343 344
  mutex_last_locked_at_line(0),
  mutex_last_unlocked_at_line(0),
  mutex_last_locked_in_func("n/a"),
  mutex_last_unlocked_in_func("n/a"),
  mutex_scheduler_data_locked(FALSE),
  waiting_on_cond(FALSE),
345
  started_events(0)
346
{
Marc Alff's avatar
Marc Alff committed
347 348 349
  mysql_mutex_init(key_event_scheduler_LOCK_scheduler_state,
                   &LOCK_scheduler_state, MY_MUTEX_INIT_FAST);
  mysql_cond_init(key_event_scheduler_COND_state, &COND_state, NULL);
350 351 352
}


353
Event_scheduler::~Event_scheduler()
354
{
355
  stop();                                    /* does nothing if not running */
Marc Alff's avatar
Marc Alff committed
356 357
  mysql_mutex_destroy(&LOCK_scheduler_state);
  mysql_cond_destroy(&COND_state);
358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381
}


/*
  Starts the scheduler (again). Creates a new THD and passes it to
  a forked thread. Does not wait for acknowledgement from the new
  thread that it has started. Asynchronous starting. Most of the
  needed initializations are done in the current thread to minimize
  the chance of failure in the spawned thread.

  SYNOPSIS
    Event_scheduler::start()

  RETURN VALUE
    FALSE  OK
    TRUE   Error (not reported)
*/

bool
Event_scheduler::start()
{
  THD *new_thd= NULL;
  bool ret= FALSE;
  pthread_t th;
382
  struct scheduler_param *scheduler_param_value;
383 384
  DBUG_ENTER("Event_scheduler::start");

385
  LOCK_DATA();
386
  DBUG_PRINT("info", ("state before action %s", scheduler_states_names[state].str));
387 388 389
  if (state > INITIALIZED)
    goto end;

390
  if (!(new_thd= new THD))
391
  {
392
    sql_print_error("Event Scheduler: Cannot initialize the scheduler thread");
393 394 395
    ret= TRUE;
    goto end;
  }
396
  pre_init_event_thread(new_thd);
397 398 399
  new_thd->system_thread= SYSTEM_THREAD_EVENT_SCHEDULER;
  new_thd->command= COM_DAEMON;

400 401 402 403 404 405 406
  /*
    We should run the event scheduler thread under the super-user privileges.
    In particular, this is needed to be able to lock the mysql.event table
    for writing when the server is running in the read-only mode.
  */
  new_thd->security_ctx->master_access |= SUPER_ACL;

407 408 409 410
  scheduler_param_value=
    (struct scheduler_param *)my_malloc(sizeof(struct scheduler_param), MYF(0));
  scheduler_param_value->thd= new_thd;
  scheduler_param_value->scheduler= this;
411

412 413 414
  scheduler_thd= new_thd;
  DBUG_PRINT("info", ("Setting state go RUNNING"));
  state= RUNNING;
415
  DBUG_PRINT("info", ("Forking new thread for scheduler. THD: 0x%lx", (long) new_thd));
Marc Alff's avatar
Marc Alff committed
416 417 418
  if (mysql_thread_create(key_thread_event_scheduler,
                          &th, &connection_attrib, event_scheduler_thread,
                          (void*)scheduler_param_value))
419 420 421
  {
    DBUG_PRINT("error", ("cannot create a new thread"));
    state= INITIALIZED;
422
    scheduler_thd= NULL;
423 424 425 426 427
    ret= TRUE;

    new_thd->proc_info= "Clearing";
    DBUG_ASSERT(new_thd->net.buff != 0);
    net_end(&new_thd->net);
Marc Alff's avatar
Marc Alff committed
428
    mysql_mutex_lock(&LOCK_thread_count);
429
    thread_count--;
430
    dec_thread_running();
Mikael Ronstrom's avatar
Mikael Ronstrom committed
431
    delete new_thd;
Marc Alff's avatar
Marc Alff committed
432 433
    mysql_cond_broadcast(&COND_thread_count);
    mysql_mutex_unlock(&LOCK_thread_count);
434
  }
435 436 437
end:
  UNLOCK_DATA();

438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456
  DBUG_RETURN(ret);
}


/*
  The main loop of the scheduler.

  SYNOPSIS
    Event_scheduler::run()
      thd  Thread

  RETURN VALUE
    FALSE  OK
    TRUE   Error (Serious error)
*/

bool
Event_scheduler::run(THD *thd)
{
457
  int res= FALSE;
458 459
  DBUG_ENTER("Event_scheduler::run");

460
  sql_print_information("Event Scheduler: scheduler thread started with id %lu",
461
                        thd->thread_id);
462 463 464 465 466
  /*
    Recalculate the values in the queue because there could have been stops
    in executions of the scheduler and some times could have passed by.
  */
  queue->recalculate_activation_times(thd);
467 468

  while (is_running())
469
  {
470 471
    Event_queue_element_for_exec *event_name;

472
    /* Gets a minimized version */
473
    if (queue->get_top_for_execution_if_time(thd, &event_name))
474
    {
475 476
      sql_print_information("Event Scheduler: "
                            "Serious error during getting next "
477
                            "event to execute. Stopping");
478 479 480
      break;
    }

481 482
    DBUG_PRINT("info", ("get_top_for_execution_if_time returned "
                        "event_name=0x%lx", (long) event_name));
483
    if (event_name)
484
    {
485
      if ((res= execute_top(event_name)))
486
        break;
487 488 489
    }
    else
    {
490 491
      DBUG_ASSERT(thd->killed);
      DBUG_PRINT("info", ("job_data is NULL, the thread was killed"));
492 493 494
    }
    DBUG_PRINT("info", ("state=%s", scheduler_states_names[state].str));
  }
495

496
  LOCK_DATA();
497 498
  deinit_event_thread(thd);
  scheduler_thd= NULL;
499
  state= INITIALIZED;
500 501
  DBUG_PRINT("info", ("Broadcasting COND_state back to the stoppers"));
  mysql_cond_broadcast(&COND_state);
502
  UNLOCK_DATA();
503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520

  DBUG_RETURN(res);
}


/*
  Creates a new THD instance and then forks a new thread, while passing
  the THD pointer and job_data to it.

  SYNOPSIS
    Event_scheduler::execute_top()

  RETURN VALUE
    FALSE  OK
    TRUE   Error (Serious error)
*/

bool
521
Event_scheduler::execute_top(Event_queue_element_for_exec *event_name)
522 523 524 525 526
{
  THD *new_thd;
  pthread_t th;
  int res= 0;
  DBUG_ENTER("Event_scheduler::execute_top");
527
  if (!(new_thd= new THD()))
528 529
    goto error;

530
  pre_init_event_thread(new_thd);
531
  new_thd->system_thread= SYSTEM_THREAD_EVENT_WORKER;
532
  event_name->thd= new_thd;
533
  DBUG_PRINT("info", ("Event %s@%s ready for start",
534
             event_name->dbname.str, event_name->name.str));
535

536 537 538 539 540 541 542 543
  /*
    TODO: should use thread pool here, preferably with an upper limit
    on number of threads: if too many events are scheduled for the
    same time, starting all of them at once won't help them run truly
    in parallel (because of the great amount of synchronization), so
    we may as well execute them in sequence, keeping concurrency at a
    reasonable level.
  */
544
  /* Major failure */
Marc Alff's avatar
Marc Alff committed
545 546 547
  if ((res= mysql_thread_create(key_thread_event_worker,
                                &th, &connection_attrib, event_worker_thread,
                                event_name)))
548 549
    goto error;

550 551
  ++started_events;

552
  DBUG_PRINT("info", ("Event is in THD: 0x%lx", (long) new_thd));
553 554 555
  DBUG_RETURN(FALSE);

error:
556
  DBUG_PRINT("error", ("Event_scheduler::execute_top() res: %d", res));
557 558 559 560 561
  if (new_thd)
  {
    new_thd->proc_info= "Clearing";
    DBUG_ASSERT(new_thd->net.buff != 0);
    net_end(&new_thd->net);
Marc Alff's avatar
Marc Alff committed
562
    mysql_mutex_lock(&LOCK_thread_count);
563
    thread_count--;
564
    dec_thread_running();
Mikael Ronstrom's avatar
Mikael Ronstrom committed
565
    delete new_thd;
Marc Alff's avatar
Marc Alff committed
566 567
    mysql_cond_broadcast(&COND_thread_count);
    mysql_mutex_unlock(&LOCK_thread_count);
568
  }
569
  delete event_name;
570 571 572 573
  DBUG_RETURN(TRUE);
}


574
/*
andrey@example.com's avatar
andrey@example.com committed
575
  Checks whether the state of the scheduler is RUNNING
576 577 578 579 580 581 582 583 584

  SYNOPSIS
    Event_scheduler::is_running()

  RETURN VALUE
    TRUE   RUNNING
    FALSE  Not RUNNING
*/

585
bool
586 587 588 589 590 591 592 593 594
Event_scheduler::is_running()
{
  LOCK_DATA();
  bool ret= (state == RUNNING);
  UNLOCK_DATA();
  return ret;
}


595
/**
596 597
  Stops the scheduler (again). Waits for acknowledgement from the
  scheduler that it has stopped - synchronous stopping.
598

599 600 601
  Already running events will not be stopped. If the user needs
  them stopped manual intervention is needed.

602
  SYNOPSIS
603
    Event_scheduler::stop()
604 605

  RETURN VALUE
606 607
    FALSE  OK
    TRUE   Error (not reported)
608 609
*/

610 611
bool
Event_scheduler::stop()
612
{
613 614
  THD *thd= current_thd;
  DBUG_ENTER("Event_scheduler::stop");
615
  DBUG_PRINT("enter", ("thd: 0x%lx", (long) thd));
616 617

  LOCK_DATA();
618
  DBUG_PRINT("info", ("state before action %s", scheduler_states_names[state].str));
619
  if (state != RUNNING)
620 621 622 623
  {
    /* Synchronously wait until the scheduler stops. */
    while (state != INITIALIZED)
      COND_STATE_WAIT(thd, NULL, "Waiting for the scheduler to stop");
624
    goto end;
625
  }
626 627 628

  /* Guarantee we don't catch spurious signals */
  do {
629 630
    DBUG_PRINT("info", ("Waiting for COND_started_or_stopped from "
                        "the scheduler thread.  Current value of state is %s . "
631 632
                        "workers count=%d", scheduler_states_names[state].str,
                        workers_count()));
633 634 635 636 637 638 639 640 641 642 643
    /*
      NOTE: We don't use kill_one_thread() because it can't kill COM_DEAMON
      threads. In addition, kill_one_thread() requires THD but during shutdown
      current_thd is NULL. Hence, if kill_one_thread should be used it has to
      be modified to kill also daemons, by adding a flag, and also we have to
      create artificial THD here. To save all this work, we just do what
      kill_one_thread() does to kill a thread. See also sql_repl.cc for similar
      usage.
    */

    state= STOPPING;
644 645
    DBUG_PRINT("info", ("Scheduler thread has id %lu",
                        scheduler_thd->thread_id));
646
    /* Lock from delete */
Marc Alff's avatar
Marc Alff committed
647
    mysql_mutex_lock(&scheduler_thd->LOCK_thd_data);
648
    /* This will wake up the thread if it waits on Queue's conditional */
649 650
    sql_print_information("Event Scheduler: Killing the scheduler thread, "
                          "thread id %lu",
651 652
                          scheduler_thd->thread_id);
    scheduler_thd->awake(THD::KILL_CONNECTION);
Marc Alff's avatar
Marc Alff committed
653
    mysql_mutex_unlock(&scheduler_thd->LOCK_thd_data);
654

655
    /* thd could be 0x0, when shutting down */
656 657
    sql_print_information("Event Scheduler: "
                          "Waiting for the scheduler thread to reply");
658
    COND_STATE_WAIT(thd, NULL, "Waiting scheduler to stop");
659
  } while (state == STOPPING);
660
  DBUG_PRINT("info", ("Scheduler thread has cleaned up. Set state to INIT"));
661
  sql_print_information("Event Scheduler: Stopped");
662 663 664
end:
  UNLOCK_DATA();
  DBUG_RETURN(FALSE);
665 666 667 668 669 670 671 672 673 674 675 676 677 678 679
}


/*
  Returns the number of living event worker threads.

  SYNOPSIS
    Event_scheduler::workers_count()
*/

uint
Event_scheduler::workers_count()
{
  THD *tmp;
  uint count= 0;
680

681
  DBUG_ENTER("Event_scheduler::workers_count");
Marc Alff's avatar
Marc Alff committed
682
  mysql_mutex_lock(&LOCK_thread_count);       // For unlink from list
683 684 685 686
  I_List_iterator<THD> it(threads);
  while ((tmp=it++))
    if (tmp->system_thread == SYSTEM_THREAD_EVENT_WORKER)
      ++count;
Marc Alff's avatar
Marc Alff committed
687
  mysql_mutex_unlock(&LOCK_thread_count);
688 689 690 691 692 693 694
  DBUG_PRINT("exit", ("%d", count));
  DBUG_RETURN(count);
}


/*
  Auxiliary function for locking LOCK_scheduler_state. Used
695
  by the LOCK_DATA macro.
696 697 698 699 700 701 702 703 704 705 706 707

  SYNOPSIS
    Event_scheduler::lock_data()
      func  Which function is requesting mutex lock
      line  On which line mutex lock is requested
*/

void
Event_scheduler::lock_data(const char *func, uint line)
{
  DBUG_ENTER("Event_scheduler::lock_data");
  DBUG_PRINT("enter", ("func=%s line=%u", func, line));
Marc Alff's avatar
Marc Alff committed
708
  mysql_mutex_lock(&LOCK_scheduler_state);
709 710 711 712 713 714 715 716 717
  mutex_last_locked_in_func= func;
  mutex_last_locked_at_line= line;
  mutex_scheduler_data_locked= TRUE;
  DBUG_VOID_RETURN;
}


/*
  Auxiliary function for unlocking LOCK_scheduler_state. Used
718
  by the UNLOCK_DATA macro.
719 720 721 722 723 724 725 726 727 728 729 730 731 732 733

  SYNOPSIS
    Event_scheduler::unlock_data()
      func  Which function is requesting mutex unlock
      line  On which line mutex unlock is requested
*/

void
Event_scheduler::unlock_data(const char *func, uint line)
{
  DBUG_ENTER("Event_scheduler::unlock_data");
  DBUG_PRINT("enter", ("func=%s line=%u", func, line));
  mutex_last_unlocked_at_line= line;
  mutex_scheduler_data_locked= FALSE;
  mutex_last_unlocked_in_func= func;
Marc Alff's avatar
Marc Alff committed
734
  mysql_mutex_unlock(&LOCK_scheduler_state);
735 736 737 738 739
  DBUG_VOID_RETURN;
}


/*
Marc Alff's avatar
Marc Alff committed
740
  Wrapper for mysql_cond_wait/timedwait
741 742 743

  SYNOPSIS
    Event_scheduler::cond_wait()
744
      thd     Thread (Could be NULL during shutdown procedure)
Marc Alff's avatar
Marc Alff committed
745
      abstime If not null then call mysql_cond_timedwait()
746
      msg     Message for thd->proc_info
747 748
      func    Which function is requesting cond_wait
      line    On which line cond_wait is requested
749 750 751
*/

void
752 753
Event_scheduler::cond_wait(THD *thd, struct timespec *abstime, const char* msg,
                           const char *func, uint line)
754
{
755
  DBUG_ENTER("Event_scheduler::cond_wait");
756 757 758 759
  waiting_on_cond= TRUE;
  mutex_last_unlocked_at_line= line;
  mutex_scheduler_data_locked= FALSE;
  mutex_last_unlocked_in_func= func;
760 761 762
  if (thd)
    thd->enter_cond(&COND_state, &LOCK_scheduler_state, msg);

Marc Alff's avatar
Marc Alff committed
763
  DBUG_PRINT("info", ("mysql_cond_%swait", abstime? "timed":""));
764
  if (!abstime)
Marc Alff's avatar
Marc Alff committed
765
    mysql_cond_wait(&COND_state, &LOCK_scheduler_state);
766
  else
Marc Alff's avatar
Marc Alff committed
767
    mysql_cond_timedwait(&COND_state, &LOCK_scheduler_state, abstime);
768 769 770 771 772 773 774 775 776
  if (thd)
  {
    /*
      This will free the lock so we need to relock. Not the best thing to
      do but we need to obey cond_wait()
    */
    thd->exit_cond("");
    LOCK_DATA();
  }
777 778 779 780
  mutex_last_locked_in_func= func;
  mutex_last_locked_at_line= line;
  mutex_scheduler_data_locked= TRUE;
  waiting_on_cond= FALSE;
781
  DBUG_VOID_RETURN;
782
}
783

784

785 786 787 788 789 790 791
/*
  Dumps the internal status of the scheduler

  SYNOPSIS
    Event_scheduler::dump_internal_status()
*/

792 793
void
Event_scheduler::dump_internal_status()
794 795 796
{
  DBUG_ENTER("Event_scheduler::dump_internal_status");

797 798 799 800 801 802 803 804 805 806
  puts("");
  puts("Event scheduler status:");
  printf("State      : %s\n", scheduler_states_names[state].str);
  printf("Thread id  : %lu\n", scheduler_thd? scheduler_thd->thread_id : 0);
  printf("LLA        : %s:%u\n", mutex_last_locked_in_func,
                                 mutex_last_locked_at_line);
  printf("LUA        : %s:%u\n", mutex_last_unlocked_in_func,
                                 mutex_last_unlocked_at_line);
  printf("WOC        : %s\n", waiting_on_cond? "YES":"NO");
  printf("Workers    : %u\n", workers_count());
807
  printf("Executed   : %lu\n", (ulong) started_events);
808
  printf("Data locked: %s\n", mutex_scheduler_data_locked ? "YES":"NO");
809

810
  DBUG_VOID_RETURN;
811
}
812 813 814 815

/**
  @} (End of group Event_Scheduler)
*/