event_queue.cc 19.8 KB
Newer Older
1 2 3 4
/* Copyright (C) 2004-2006 MySQL AB

   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 14 15 16 17 18

   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
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */

#include "mysql_priv.h"
#include "event_queue.h"
#include "event_data_objects.h"
19

20

21 22
#define EVENT_QUEUE_INITIAL_SIZE 30
#define EVENT_QUEUE_EXTENT       30
23 24 25 26 27 28 29 30 31 32 33 34

#ifdef __GNUC__
#if __GNUC__ >= 2
#define SCHED_FUNC __FUNCTION__
#endif
#else
#define SCHED_FUNC "<unknown>"
#endif

#define LOCK_QUEUE_DATA()   lock_data(SCHED_FUNC, __LINE__)
#define UNLOCK_QUEUE_DATA() unlock_data(SCHED_FUNC, __LINE__)

35 36 37 38 39 40
struct event_queue_param
{
  THD *thd;
  Event_queue *queue;
  pthread_mutex_t LOCK_loaded;
  pthread_cond_t COND_loaded;
41
  bool loading_finished;
42 43
};

44 45

/*
46
  Compares the execute_at members of two Event_queue_element instances.
47 48 49 50
  Used as callback for the prioritized queue when shifting
  elements inside.

  SYNOPSIS
51
    event_queue_element_data_compare_q()
52 53 54
      vptr  Not used (set it to NULL)
      a     First Event_queue_element object
      b     Second Event_queue_element object
55 56

  RETURN VALUE
57 58 59
   -1   a->execute_at < b->execute_at
    0   a->execute_at == b->execute_at
    1   a->execute_at > b->execute_at
andrey@lmy004's avatar
andrey@lmy004 committed
60

61 62 63 64 65
  NOTES
    execute_at.second_part is not considered during comparison
*/

static int 
66
event_queue_element_compare_q(void *vptr, byte* a, byte *b)
67
{
68 69
  return my_time_compare(&((Event_queue_element *)a)->execute_at,
                         &((Event_queue_element *)b)->execute_at);
70 71 72 73 74 75 76 77 78 79 80
}


/*
  Constructor of class Event_queue.

  SYNOPSIS
    Event_queue::Event_queue()
*/

Event_queue::Event_queue()
81 82 83
  :mutex_last_unlocked_at_line(0), mutex_last_locked_at_line(0),
   mutex_last_attempted_lock_at_line(0),
   mutex_queue_data_locked(FALSE), mutex_queue_data_attempting_lock(FALSE)
84
{
85 86
  mutex_last_unlocked_in_func= mutex_last_locked_in_func=
    mutex_last_attempted_lock_in_func= "";
87
  set_zero_time(&next_activation_at, MYSQL_TIMESTAMP_DATETIME);
88 89
}

90 91 92 93 94 95 96 97 98 99 100 101

/*
  Inits mutexes.

  SYNOPSIS
    Event_queue::init_mutexes()
*/

void
Event_queue::init_mutexes()
{
  pthread_mutex_init(&LOCK_event_queue, MY_MUTEX_INIT_FAST);
102
  pthread_cond_init(&COND_queue_state, NULL);
103 104 105 106 107 108 109 110 111 112 113 114 115 116
}


/*
  Destroys mutexes.

  SYNOPSIS
    Event_queue::deinit_mutexes()
*/

void
Event_queue::deinit_mutexes()
{
  pthread_mutex_destroy(&LOCK_event_queue);
117
  pthread_cond_destroy(&COND_queue_state);
118 119 120 121
}


/*
122 123 124 125 126 127
  This is a queue's constructor. Until this method is called, the
  queue is unusable.  We don't use a C++ constructor instead in
  order to be able to check the return value. The queue is
  initialized once at server startup.  Initialization can fail in
  case of a failure reading events from the database or out of
  memory.
128 129 130 131 132 133 134 135 136 137

  SYNOPSIS
    Event_queue::init()

  RETURN VALUE
    FALSE  OK
    TRUE   Error
*/

bool
138
Event_queue::init_queue(THD *thd)
139 140
{
  DBUG_ENTER("Event_queue::init_queue");
141
  DBUG_PRINT("enter", ("this: 0x%lx", (long) this));
142 143 144

  LOCK_QUEUE_DATA();

145
  if (init_queue_ex(&queue, EVENT_QUEUE_INITIAL_SIZE , 0 /*offset*/,
146
                    0 /*max_on_top*/, event_queue_element_compare_q,
147
                    NULL, EVENT_QUEUE_EXTENT))
148 149
  {
    sql_print_error("SCHEDULER: Can't initialize the execution queue");
150
    goto err;
151 152 153
  }

  UNLOCK_QUEUE_DATA();
154
  DBUG_RETURN(FALSE);
155 156 157 158

err:
  UNLOCK_QUEUE_DATA();
  DBUG_RETURN(TRUE);
159 160 161 162
}


/*
163 164
  Deinits the queue. Remove all elements from it and destroys them
  too.
165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183

  SYNOPSIS
    Event_queue::deinit_queue()
*/

void
Event_queue::deinit_queue()
{
  DBUG_ENTER("Event_queue::deinit_queue");

  LOCK_QUEUE_DATA();
  empty_queue();
  delete_queue(&queue);
  UNLOCK_QUEUE_DATA();

  DBUG_VOID_RETURN;
}


184
/*
185
  Adds an event to the queue.
186 187 188

  SYNOPSIS
    Event_queue::create_event()
189 190
      dbname  The schema of the new event
      name    The name of the new event
191 192
*/

193 194
void
Event_queue::create_event(THD *thd, Event_queue_element *new_element)
195 196
{
  DBUG_ENTER("Event_queue::create_event");
197
  DBUG_PRINT("enter", ("thd: 0x%lx et=%s.%s", (long) thd,
198
             new_element->dbname.str, new_element->name.str));
199

200
  if (new_element->status == Event_queue_element::DISABLED)
201
    delete new_element;
202
  else
203
  {
204
    new_element->compute_next_execution_time();
205
    DBUG_PRINT("info", ("new event in the queue: 0x%lx", (long) new_element));
206 207

    LOCK_QUEUE_DATA();
208
    queue_insert_safe(&queue, (byte *) new_element);
209
    dbug_dump_queue(thd->query_start());
210
    pthread_cond_broadcast(&COND_queue_state);  
211
    UNLOCK_QUEUE_DATA();
212
  }
213
  DBUG_VOID_RETURN;
214 215 216 217 218 219 220
}


/*
  Updates an event from the scheduler queue

  SYNOPSIS
221
    Event_queue::update_event()
222
      thd        Thread
223 224 225 226
      dbname     Schema of the event
      name       Name of the event
      new_schema New schema, in case of RENAME TO, otherwise NULL
      new_name   New name, in case of RENAME TO, otherwise NULL
227 228
*/

229
void
230
Event_queue::update_event(THD *thd, LEX_STRING dbname, LEX_STRING name,
231
                          Event_queue_element *new_element)
232 233
{
  DBUG_ENTER("Event_queue::update_event");
234
  DBUG_PRINT("enter", ("thd: 0x%lx  et=[%s.%s]", (long) thd, dbname.str, name.str));
235

236
  if (new_element->status == Event_queue_element::DISABLED)
237 238 239 240 241 242
  {
    DBUG_PRINT("info", ("The event is disabled."));
    /*
      Destroy the object but don't skip to end: because we may have to remove
      object from the cache.
    */
243 244
    delete new_element;
    new_element= NULL;
245 246
  }
  else
247
    new_element->compute_next_execution_time();
248

249
  LOCK_QUEUE_DATA();
250 251
  find_n_remove_event(dbname, name);

252
  /* If not disabled event */
253
  if (new_element)
254
  {
255
    DBUG_PRINT("info", ("new event in the queue: 0x%lx", (long) new_element));
256
    queue_insert_safe(&queue, (byte *) new_element);
257
    pthread_cond_broadcast(&COND_queue_state);  
258
  }
259

260
  dbug_dump_queue(thd->query_start());
261 262
  UNLOCK_QUEUE_DATA();

263
  DBUG_VOID_RETURN;
264 265 266 267
}


/*
268
  Drops an event from the queue
269 270 271

  SYNOPSIS
    Event_queue::drop_event()
272 273 274
      thd     Thread
      dbname  Schema of the event to drop
      name    Name of the event to drop
275 276
*/

277 278
void
Event_queue::drop_event(THD *thd, LEX_STRING dbname, LEX_STRING name)
279 280
{
  DBUG_ENTER("Event_queue::drop_event");
281 282
  DBUG_PRINT("enter", ("thd: 0x%lx  db :%s  name: %s", (long) thd,
                       dbname.str, name.str));
283 284

  LOCK_QUEUE_DATA();
285
  find_n_remove_event(dbname, name);
286
  dbug_dump_queue(thd->query_start());
287
  UNLOCK_QUEUE_DATA();
288
  
289 290 291 292
  /*
    We don't signal here because the scheduler will catch the change
    next time it wakes up.
  */
293

294
  DBUG_VOID_RETURN;
295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316
}


/*
  Drops all events from the in-memory queue and disk that match
  certain pattern evaluated by a comparator function

  SYNOPSIS
    Event_queue::drop_matching_events()
      thd            THD
      pattern        A pattern string
      comparator     The function to use for comparing

  RETURN VALUE
    >=0  Number of dropped events
    
  NOTE
    Expected is the caller to acquire lock on LOCK_event_queue
*/

void
Event_queue::drop_matching_events(THD *thd, LEX_STRING pattern,
317
                           bool (*comparator)(LEX_STRING, Event_basic *))
318
{
319
  uint i= 0;
320
  DBUG_ENTER("Event_queue::drop_matching_events");
321
  DBUG_PRINT("enter", ("pattern=%s", pattern.str));
322 323 324

  while (i < queue.elements)
  {
325
    Event_queue_element *et= (Event_queue_element *) queue_element(&queue, i);
326
    DBUG_PRINT("info", ("[%s.%s]?", et->dbname.str, et->name.str));
327
    if (comparator(pattern, et))
328 329
    {
      /*
330 331 332 333
        The queue is ordered. If we remove an element, then all elements
        after it will shift one position to the left, if we imagine it as
        an array from left to the right. In this case we should not
        increment the counter and the (i < queue.elements) condition is ok.
334 335
      */
      queue_remove(&queue, i);
336
      delete et;
337 338 339 340 341
    }
    else
      i++;
  }
  /*
342 343
    We don't call pthread_cond_broadcast(&COND_queue_state);  
    If we remove the top event:
344 345 346 347 348 349
    1. The queue is empty. The scheduler will wake up at some time and
       realize that the queue is empty. If create_event() comes inbetween
       it will signal the scheduler
    2. The queue is not empty, but the next event after the previous top,
       won't be executed any time sooner than the element we removed. Hence,
       we may not notify the scheduler and it will realize the change when it
350
       wakes up from timedwait.
351
  */
andrey@lmy004's avatar
andrey@lmy004 committed
352

353 354 355 356 357 358 359 360 361 362
  DBUG_VOID_RETURN;
}


/*
  Drops all events from the in-memory queue and disk that are from
  certain schema.

  SYNOPSIS
    Event_queue::drop_schema_events()
363 364
      thd        HD
      schema    The schema name
365 366
*/

367
void
368 369 370 371
Event_queue::drop_schema_events(THD *thd, LEX_STRING schema)
{
  DBUG_ENTER("Event_queue::drop_schema_events");
  LOCK_QUEUE_DATA();
372
  drop_matching_events(thd, schema, event_basic_db_equal);
373
  UNLOCK_QUEUE_DATA();
374
  DBUG_VOID_RETURN;
375 376 377
}


378 379 380 381 382 383 384
/*
  Searches for an event in the queue

  SYNOPSIS
    Event_queue::find_n_remove_event()
      db    The schema of the event to find
      name  The event to find
385

386 387 388
  NOTE
    The caller should do the locking also the caller is responsible for
    actual signalling in case an event is removed from the queue.
389 390
*/

391
void
392
Event_queue::find_n_remove_event(LEX_STRING db, LEX_STRING name)
393
{
394 395 396 397 398 399 400 401 402 403 404
  uint i;
  DBUG_ENTER("Event_queue::find_n_remove_event");

  for (i= 0; i < queue.elements; ++i)
  {
    Event_queue_element *et= (Event_queue_element *) queue_element(&queue, i);
    DBUG_PRINT("info", ("[%s.%s]==[%s.%s]?", db.str, name.str,
                        et->dbname.str, et->name.str));
    if (event_basic_identifier_equal(db, name, et))
    {
      queue_remove(&queue, i);
405 406
      delete et;
      break;
407 408 409
    }
  }

410
  DBUG_VOID_RETURN;
411 412 413 414
}


/*
415 416 417 418 419 420
  Recalculates activation times in the queue. There is one reason for
  that. Because the values (execute_at) by which the queue is ordered are
  changed by calls to compute_next_execution_time() on a request from the
  scheduler thread, if it is not running then the values won't be updated.
  Once the scheduler is started again the values has to be recalculated
  so they are right for the current time.
421 422

  SYNOPSIS
423 424
    Event_queue::recalculate_activation_times()
      thd  Thread
425 426 427
*/

void
428
Event_queue::recalculate_activation_times(THD *thd)
429
{
430 431
  uint i;
  DBUG_ENTER("Event_queue::recalculate_activation_times");
432 433

  LOCK_QUEUE_DATA();
434 435
  DBUG_PRINT("info", ("%u loaded events to be recalculated", queue.elements));
  for (i= 0; i < queue.elements; i++)
436
  {
437 438
    ((Event_queue_element*)queue_element(&queue, i))->compute_next_execution_time();
    ((Event_queue_element*)queue_element(&queue, i))->update_timing_fields(thd);
439
  }
440
  queue_fix(&queue);
441 442 443 444 445 446
  UNLOCK_QUEUE_DATA();

  DBUG_VOID_RETURN;
}


447 448 449
/*
  Empties the queue and destroys the Event_queue_element objects in the
  queue.
450

451 452 453 454
  SYNOPSIS
    Event_queue::empty_queue()

  NOTE
455
    Should be called with LOCK_event_queue locked
456
*/
457 458 459 460

void
Event_queue::empty_queue()
{
461
  uint i;
462
  DBUG_ENTER("Event_queue::empty_queue");
463
  DBUG_PRINT("enter", ("Purging the queue. %u element(s)", queue.elements));
464
  sql_print_information("SCHEDULER: Purging queue. %u events", queue.elements);
465
  /* empty the queue */
466
  for (i= 0; i < queue.elements; ++i)
467
  {
468
    Event_queue_element *et= (Event_queue_element *) queue_element(&queue, i);
469 470 471
    delete et;
  }
  resize_queue(&queue, 0);
472
  DBUG_VOID_RETURN;
473
}
474 475


476 477 478 479 480 481 482 483
/*
  Dumps the queue to the trace log.

  SYNOPSIS
    Event_queue::dbug_dump_queue()
      now  Current timestamp
*/

484
void
485 486 487
Event_queue::dbug_dump_queue(time_t now)
{
#ifndef DBUG_OFF
488
  Event_queue_element *et;
489
  uint i;
490
  DBUG_ENTER("Event_queue::dbug_dump_queue");
491 492 493
  DBUG_PRINT("info", ("Dumping queue . Elements=%u", queue.elements));
  for (i = 0; i < queue.elements; i++)
  {
494
    et= ((Event_queue_element*)queue_element(&queue, i));
495 496 497 498 499 500 501 502 503 504 505 506 507 508
    DBUG_PRINT("info", ("et: 0x%lx  name: %s.%s", (long) et,
                        et->dbname.str, et->name.str));
    DBUG_PRINT("info", ("exec_at: %lu  starts: %lu  ends: %lu  execs_so_far: %u  "
                        "expr: %ld  et.exec_at: %ld  now: %ld  "
                        "(et.exec_at - now): %d  if: %d",
                        (long) TIME_to_ulonglong_datetime(&et->execute_at),
                        (long) TIME_to_ulonglong_datetime(&et->starts),
                        (long) TIME_to_ulonglong_datetime(&et->ends),
                        et->execution_count,
                        (long) et->expression,
                        (long) (sec_since_epoch_TIME(&et->execute_at)),
                        (long) now,
                        (int) (sec_since_epoch_TIME(&et->execute_at) - now),
                        sec_since_epoch_TIME(&et->execute_at) <= now));
509
  }
510
  DBUG_VOID_RETURN;
511 512 513
#endif
}

514 515
static const char *queue_empty_msg= "Waiting on empty queue";
static const char *queue_wait_msg= "Waiting for next activation";
516 517 518 519 520 521 522

/*
  Checks whether the top of the queue is elligible for execution and
  returns an Event_job_data instance in case it should be executed.
  `now` is compared against `execute_at` of the top element in the queue.

  SYNOPSIS
523
    Event_queue::get_top_for_execution_if_time()
524 525
      thd        [in]  Thread
      event_name [out] The object to execute
526 527

  RETURN VALUE
528 529
    FALSE  No error. event_name != NULL
    TRUE   Serious error
530 531 532
*/

bool
533 534
Event_queue::get_top_for_execution_if_time(THD *thd,
                Event_queue_element_for_exec **event_name)
535
{
536
  bool ret= FALSE;
537
  struct timespec top_time;
538
  *event_name= NULL;
539
  DBUG_ENTER("Event_queue::get_top_for_execution_if_time");
540

541
  LOCK_QUEUE_DATA();
542 543
  for (;;)
  {
544
    Event_queue_element *top= NULL;
545

546 547 548 549 550 551
    /* Break loop if thd has been killed */
    if (thd->killed)
    {
      DBUG_PRINT("info", ("thd->killed=%d", thd->killed));
      goto end;
    }
552

553
    if (!queue.elements)
554
    {
555 556 557 558 559
      /* There are no events in the queue */
      set_zero_time(&next_activation_at, MYSQL_TIMESTAMP_DATETIME);

      /* Wait on condition until signaled. Release LOCK_queue while waiting. */
      cond_wait(thd, NULL, queue_empty_msg, SCHED_FUNC, __LINE__);
560

561
      continue;
562 563
    }

564
    top= ((Event_queue_element*) queue_element(&queue, 0));
565

566 567 568 569 570 571 572
    thd->end_time(); /* Get current time */

    time_t seconds_to_next_event= 
      sec_since_epoch_TIME(&top->execute_at) - thd->query_start();
    next_activation_at= top->execute_at;
    if (seconds_to_next_event > 0)
    {
573
      /*
574 575
        Not yet time for top event, wait on condition with
        time or until signaled. Release LOCK_queue while waiting.
576
      */
577 578 579
      set_timespec(top_time, seconds_to_next_event);
      cond_wait(thd, &top_time, queue_wait_msg, SCHED_FUNC, __LINE__);

580
      continue;
581
    }
582

583 584
    if (!(*event_name= new Event_queue_element_for_exec()) ||
        (*event_name)->init(top->dbname, top->name))
585 586 587 588 589
    {
      ret= TRUE;
      break;
    }

590
    DBUG_PRINT("info", ("Ready for execution"));
591 592 593
    top->mark_last_executed(thd);
    if (top->compute_next_execution_time())
      top->status= Event_queue_element::DISABLED;
594 595
    DBUG_PRINT("info", ("event %s status is %d", top->name.str, top->status));

596 597
    top->execution_count++;
    (*event_name)->dropped= top->dropped;
598 599

    top->update_timing_fields(thd);
600
    if (top->status == Event_queue_element::DISABLED)
601 602
    {
      DBUG_PRINT("info", ("removing from the queue"));
603 604 605
      sql_print_information("SCHEDULER: Last execution of %s.%s. %s",
                            top->dbname.str, top->name.str,
                            top->dropped? "Dropping.":"");
606
      delete top;
607 608 609 610
      queue_remove(&queue, 0);
    }
    else
      queue_replaced(&queue);
611

612
    dbug_dump_queue(thd->query_start());
613 614 615
    break;
  }
end:
616
  UNLOCK_QUEUE_DATA();
617

618 619
  DBUG_PRINT("info", ("returning %d  et_new: 0x%lx ",
                      ret, (long) *event_name));
620

621 622 623
  if (*event_name)
    DBUG_PRINT("info", ("db: %s  name: %s",
                        (*event_name)->dbname.str, (*event_name)->name.str));
624 625

  DBUG_RETURN(ret);
626
}
627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643


/*
  Auxiliary function for locking LOCK_event_queue. Used by the
  LOCK_QUEUE_DATA macro

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

void
Event_queue::lock_data(const char *func, uint line)
{
  DBUG_ENTER("Event_queue::lock_data");
  DBUG_PRINT("enter", ("func=%s line=%u", func, line));
644 645 646
  mutex_last_attempted_lock_in_func= func;
  mutex_last_attempted_lock_at_line= line;
  mutex_queue_data_attempting_lock= TRUE;
647
  pthread_mutex_lock(&LOCK_event_queue);
648 649 650 651
  mutex_last_attempted_lock_in_func= "";
  mutex_last_attempted_lock_at_line= 0;
  mutex_queue_data_attempting_lock= FALSE;

652 653 654
  mutex_last_locked_in_func= func;
  mutex_last_locked_at_line= line;
  mutex_queue_data_locked= TRUE;
655

656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682
  DBUG_VOID_RETURN;
}


/*
  Auxiliary function for unlocking LOCK_event_queue. Used by the
  UNLOCK_QUEUE_DATA macro

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

void
Event_queue::unlock_data(const char *func, uint line)
{
  DBUG_ENTER("Event_queue::unlock_data");
  DBUG_PRINT("enter", ("func=%s line=%u", func, line));
  mutex_last_unlocked_at_line= line;
  mutex_queue_data_locked= FALSE;
  mutex_last_unlocked_in_func= func;
  pthread_mutex_unlock(&LOCK_event_queue);
  DBUG_VOID_RETURN;
}


683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722
/*
  Wrapper for pthread_cond_wait/timedwait

  SYNOPSIS
    Event_queue::cond_wait()
      thd     Thread (Could be NULL during shutdown procedure)
      msg     Message for thd->proc_info
      abstime If not null then call pthread_cond_timedwait()
      func    Which function is requesting cond_wait
      line    On which line cond_wait is requested
*/

void
Event_queue::cond_wait(THD *thd, struct timespec *abstime, const char* msg,
                       const char *func, uint line)
{
  DBUG_ENTER("Event_queue::cond_wait");
  waiting_on_cond= TRUE;
  mutex_last_unlocked_at_line= line;
  mutex_queue_data_locked= FALSE;
  mutex_last_unlocked_in_func= func;

  thd->enter_cond(&COND_queue_state, &LOCK_event_queue, msg);

  DBUG_PRINT("info", ("pthread_cond_%swait", abstime? "timed":""));
  if (!abstime)
    pthread_cond_wait(&COND_queue_state, &LOCK_event_queue);
  else
    pthread_cond_timedwait(&COND_queue_state, &LOCK_event_queue, abstime);

  mutex_last_locked_in_func= func;
  mutex_last_locked_at_line= line;
  mutex_queue_data_locked= TRUE;
  waiting_on_cond= FALSE;

  /*
    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("");
723
  lock_data(func, line);
724 725 726 727 728

  DBUG_VOID_RETURN;
}


729 730 731 732 733 734 735
/*
  Dumps the internal status of the queue

  SYNOPSIS
    Event_queue::dump_internal_status()
*/

736 737
void
Event_queue::dump_internal_status()
738 739
{
  DBUG_ENTER("Event_queue::dump_internal_status");
740

741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760
  /* element count */
  puts("");
  puts("Event queue status:");
  printf("Element count   : %u\n", queue.elements);
  printf("Data locked     : %s\n", mutex_queue_data_locked? "YES":"NO");
  printf("Attempting lock : %s\n", mutex_queue_data_attempting_lock? "YES":"NO");
  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);
  if (mutex_last_attempted_lock_at_line)
    printf("Last lock attempt at: %s:%u\n", mutex_last_attempted_lock_in_func,
                                            mutex_last_attempted_lock_at_line);
  printf("WOC             : %s\n", waiting_on_cond? "YES":"NO");
  printf("Next activation : %04d-%02d-%02d %02d:%02d:%02d\n",
         next_activation_at.year, next_activation_at.month,
         next_activation_at.day, next_activation_at.hour,
         next_activation_at.minute, next_activation_at.second);

  DBUG_VOID_RETURN;
761
}