lock.cc 40.5 KB
Newer Older
unknown's avatar
unknown committed
1
/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB
unknown's avatar
unknown committed
2

unknown's avatar
unknown committed
3 4 5 6
   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
   the Free Software Foundation; either version 2 of the License, or
   (at your option) any later version.
unknown's avatar
unknown committed
7

unknown's avatar
unknown committed
8 9 10 11
   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.
unknown's avatar
unknown committed
12

unknown's avatar
unknown committed
13 14 15 16 17 18 19 20 21 22 23
   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 */


/* locking functions for mysql */
/*
  Because of the new concurrent inserts, we must first get external locks
  before getting internal locks.  If we do it in the other order, the status
  information is not up to date when called from the lock handler.

24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
  GENERAL DESCRIPTION OF LOCKING

  When not using LOCK TABLES:

  - For each SQL statement mysql_lock_tables() is called for all involved
    tables.
    - mysql_lock_tables() will call
      table_handler->external_lock(thd,locktype) for each table.
      This is followed by a call to thr_multi_lock() for all tables.

  - When statement is done, we call mysql_unlock_tables().
    This will call thr_multi_unlock() followed by
    table_handler->external_lock(thd, F_UNLCK) for each table.

  - Note that mysql_unlock_tables() may be called several times as
    MySQL in some cases can free some tables earlier than others.

  - The above is true both for normal and temporary tables.

  - Temporary non transactional tables are never passed to thr_multi_lock()
    and we never call external_lock(thd, F_UNLOCK) on these.

  When using LOCK TABLES:

  - LOCK TABLE will call mysql_lock_tables() for all tables.
    mysql_lock_tables() will call
    table_handler->external_lock(thd,locktype) for each table.
    This is followed by a call to thr_multi_lock() for all tables.

  - For each statement, we will call table_handler->start_stmt(THD)
    to inform the table handler that we are using the table.

    The tables used can only be tables used in LOCK TABLES or a
    temporary table.

  - When statement is done, we will call ha_commit_stmt(thd);

  - When calling UNLOCK TABLES we call mysql_unlock_tables() for all
    tables used in LOCK TABLES

unknown's avatar
unknown committed
64 65 66 67 68 69 70
TODO:
  Change to use my_malloc() ONLY when using LOCK TABLES command or when
  we are forced to use mysql_lock_merge.
*/

#include "mysql_priv.h"
#include <hash.h>
71
#include <assert.h>
unknown's avatar
unknown committed
72 73 74

extern HASH open_cache;

75 76 77
/* flags for get_lock_data */
#define GET_LOCK_UNLOCK         1
#define GET_LOCK_STORE_LOCKS    2
unknown's avatar
unknown committed
78 79

static MYSQL_LOCK *get_lock_data(THD *thd, TABLE **table,uint count,
80
				 uint flags, TABLE **write_locked);
81
static void reset_lock_data(MYSQL_LOCK *sql_lock);
82
static int lock_external(THD *thd, TABLE **table,uint count);
unknown's avatar
unknown committed
83
static int unlock_external(THD *thd, TABLE **table,uint count);
84
static void print_lock_error(int error, const char *);
unknown's avatar
unknown committed
85

86 87 88 89 90 91 92 93 94 95 96
/*
  Lock tables.

  SYNOPSIS
    mysql_lock_tables()
    thd                         The current thread.
    tables                      An array of pointers to the tables to lock.
    count                       The number of tables to lock.
    flags                       Options:
      MYSQL_LOCK_IGNORE_GLOBAL_READ_LOCK      Ignore a global read lock
      MYSQL_LOCK_IGNORE_FLUSH                 Ignore a flush tables.
97 98 99 100 101 102 103
      MYSQL_LOCK_NOTIFY_IF_NEED_REOPEN        Instead of reopening altered
                                              or dropped tables by itself,
                                              mysql_lock_tables() should
                                              notify upper level and rely
                                              on caller doing this.
    need_reopen                 Out parameter, TRUE if some tables were altered
                                or deleted and should be reopened by caller.
104 105 106

  RETURN
    A lock structure pointer on success.
107
    NULL on error or if some tables should be reopen.
108 109
*/

110
/* Map the return value of thr_lock to an error from errmsg.txt */
unknown's avatar
unknown committed
111 112 113
static int thr_lock_errno_to_mysql[]=
{ 0, 1, ER_LOCK_WAIT_TIMEOUT, ER_LOCK_DEADLOCK };

114 115
MYSQL_LOCK *mysql_lock_tables(THD *thd, TABLE **tables, uint count,
                              uint flags, bool *need_reopen)
unknown's avatar
unknown committed
116 117 118
{
  MYSQL_LOCK *sql_lock;
  TABLE *write_lock_used;
119
  int rc;
unknown's avatar
unknown committed
120 121
  DBUG_ENTER("mysql_lock_tables");

122 123
  *need_reopen= FALSE;

unknown's avatar
unknown committed
124 125
  for (;;)
  {
126 127
    if (! (sql_lock= get_lock_data(thd, tables, count, GET_LOCK_STORE_LOCKS,
                                   &write_lock_used)))
unknown's avatar
unknown committed
128 129
      break;

130 131
    if (global_read_lock && write_lock_used &&
        ! (flags & MYSQL_LOCK_IGNORE_GLOBAL_READ_LOCK))
unknown's avatar
unknown committed
132 133 134 135 136
    {
      /*
	Someone has issued LOCK ALL TABLES FOR READ and we want a write lock
	Wait until the lock is gone
      */
137
      if (wait_if_global_read_lock(thd, 1, 1))
unknown's avatar
unknown committed
138
      {
139 140
        /* Clear the lock type of all lock data to avoid reusage. */
        reset_lock_data(sql_lock);
unknown's avatar
unknown committed
141 142 143
	my_free((gptr) sql_lock,MYF(0));
	sql_lock=0;
	break;
144
      }
145
      if (thd->version != refresh_version)
unknown's avatar
unknown committed
146
      {
147 148
        /* Clear the lock type of all lock data to avoid reusage. */
        reset_lock_data(sql_lock);
unknown's avatar
unknown committed
149 150 151 152 153 154
	my_free((gptr) sql_lock,MYF(0));
	goto retry;
      }
    }

    thd->proc_info="System lock";
unknown's avatar
unknown committed
155
    DBUG_PRINT("info", ("thd->proc_info %s", thd->proc_info));
156
    if (lock_external(thd, tables, count))
unknown's avatar
unknown committed
157
    {
158 159
      /* Clear the lock type of all lock data to avoid reusage. */
      reset_lock_data(sql_lock);
unknown's avatar
unknown committed
160 161 162 163
      my_free((gptr) sql_lock,MYF(0));
      sql_lock=0;
      break;
    }
164
    thd->proc_info="Table lock";
unknown's avatar
unknown committed
165
    DBUG_PRINT("info", ("thd->proc_info %s", thd->proc_info));
unknown's avatar
unknown committed
166
    thd->locked=1;
167 168 169 170
    /* Copy the lock data array. thr_multi_lock() reorders its contens. */
    memcpy(sql_lock->locks + sql_lock->lock_count, sql_lock->locks,
           sql_lock->lock_count * sizeof(*sql_lock->locks));
    /* Lock on the copied half of the lock data array. */
171 172
    rc= thr_lock_errno_to_mysql[(int) thr_multi_lock(sql_lock->locks +
                                                     sql_lock->lock_count,
173 174 175 176 177 178 179 180 181 182
                                                     sql_lock->lock_count,
                                                     thd->lock_id)];
    if (rc > 1)                                 /* a timeout or a deadlock */
    {
      my_error(rc, MYF(0));
      my_free((gptr) sql_lock,MYF(0));
      sql_lock= 0;
      break;
    }
    else if (rc == 1)                           /* aborted */
unknown's avatar
unknown committed
183 184
    {
      thd->some_tables_deleted=1;		// Try again
185
      sql_lock->lock_count= 0;                  // Locks are already freed
unknown's avatar
unknown committed
186
    }
187
    else if (!thd->some_tables_deleted || (flags & MYSQL_LOCK_IGNORE_FLUSH))
unknown's avatar
unknown committed
188 189 190 191
    {
      thd->locked=0;
      break;
    }
192 193 194 195 196 197 198
    else if (!thd->open_tables)
    {
      // Only using temporary tables, no need to unlock
      thd->some_tables_deleted=0;
      thd->locked=0;
      break;
    }
199
    thd->proc_info=0;
unknown's avatar
unknown committed
200 201 202 203 204 205

    /* some table was altered or deleted. reopen tables marked deleted */
    mysql_unlock_tables(thd,sql_lock);
    thd->locked=0;
retry:
    sql_lock=0;
206 207 208 209 210
    if (flags & MYSQL_LOCK_NOTIFY_IF_NEED_REOPEN)
    {
      *need_reopen= TRUE;
      break;
    }
unknown's avatar
unknown committed
211 212 213
    if (wait_for_tables(thd))
      break;					// Couldn't open tables
  }
214
  thd->proc_info=0;
unknown's avatar
unknown committed
215 216
  if (thd->killed)
  {
unknown's avatar
SCRUM  
unknown committed
217
    thd->send_kill_message();
unknown's avatar
unknown committed
218 219 220 221 222 223
    if (sql_lock)
    {
      mysql_unlock_tables(thd,sql_lock);
      sql_lock=0;
    }
  }
unknown's avatar
unknown committed
224

225
  thd->lock_time();
unknown's avatar
unknown committed
226 227 228 229
  DBUG_RETURN (sql_lock);
}


230
static int lock_external(THD *thd, TABLE **tables, uint count)
unknown's avatar
unknown committed
231 232 233 234 235
{
  reg1 uint i;
  int lock_type,error;
  DBUG_ENTER("lock_external");

unknown's avatar
unknown committed
236
  DBUG_PRINT("info", ("count %d", count));
unknown's avatar
unknown committed
237 238
  for (i=1 ; i <= count ; i++, tables++)
  {
unknown's avatar
unknown committed
239
    DBUG_ASSERT((*tables)->reginfo.lock_type >= TL_READ);
unknown's avatar
unknown committed
240 241 242 243 244
    lock_type=F_WRLCK;				/* Lock exclusive */
    if ((*tables)->db_stat & HA_READ_ONLY ||
	((*tables)->reginfo.lock_type >= TL_READ &&
	 (*tables)->reginfo.lock_type <= TL_READ_NO_INSERT))
      lock_type=F_RDLCK;
245
    if ((error=(*tables)->file->ha_external_lock(thd,lock_type)))
unknown's avatar
unknown committed
246
    {
247
      print_lock_error(error, (*tables)->file->table_type());
248
      for (; i-- ; tables--)
unknown's avatar
unknown committed
249
      {
250
	(*tables)->file->ha_external_lock(thd, F_UNLCK);
unknown's avatar
unknown committed
251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267
	(*tables)->current_lock=F_UNLCK;
      }
      DBUG_RETURN(error);
    }
    else
    {
      (*tables)->db_stat &= ~ HA_BLOCK_LOCK;
      (*tables)->current_lock= lock_type;
    }
  }
  DBUG_RETURN(0);
}


void mysql_unlock_tables(THD *thd, MYSQL_LOCK *sql_lock)
{
  DBUG_ENTER("mysql_unlock_tables");
unknown's avatar
unknown committed
268 269
  if (sql_lock->lock_count)
    thr_multi_unlock(sql_lock->locks,sql_lock->lock_count);
270
  if (sql_lock->table_count)
unknown's avatar
unknown committed
271
    VOID(unlock_external(thd,sql_lock->table,sql_lock->table_count));
unknown's avatar
unknown committed
272 273 274 275 276 277 278 279 280 281 282 283 284
  my_free((gptr) sql_lock,MYF(0));
  DBUG_VOID_RETURN;
}

/*
  Unlock some of the tables locked by mysql_lock_tables
  This will work even if get_lock_data fails (next unlock will free all)
  */

void mysql_unlock_some_tables(THD *thd, TABLE **table,uint count)
{
  MYSQL_LOCK *sql_lock;
  TABLE *write_lock_used;
285 286
  if ((sql_lock= get_lock_data(thd, table, count, GET_LOCK_UNLOCK,
                               &write_lock_used)))
unknown's avatar
unknown committed
287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305
    mysql_unlock_tables(thd, sql_lock);
}


/*
** unlock all tables locked for read.
*/

void mysql_unlock_read_tables(THD *thd, MYSQL_LOCK *sql_lock)
{
  uint i,found;
  DBUG_ENTER("mysql_unlock_read_tables");

  /* Move all write locks first */
  THR_LOCK_DATA **lock=sql_lock->locks;
  for (i=found=0 ; i < sql_lock->lock_count ; i++)
  {
    if (sql_lock->locks[i]->type >= TL_WRITE_ALLOW_READ)
    {
306
      swap_variables(THR_LOCK_DATA *, *lock, sql_lock->locks[i]);
unknown's avatar
unknown committed
307 308 309 310 311 312 313 314
      lock++;
      found++;
    }
  }
  /* unlock the read locked tables */
  if (i != found)
  {
    thr_multi_unlock(lock,i-found);
unknown's avatar
unknown committed
315
    sql_lock->lock_count= found;
unknown's avatar
unknown committed
316 317
  }

318
  /* Then do the same for the external locks */
unknown's avatar
unknown committed
319 320 321 322
  /* Move all write locked tables first */
  TABLE **table=sql_lock->table;
  for (i=found=0 ; i < sql_lock->table_count ; i++)
  {
323
    DBUG_ASSERT(sql_lock->table[i]->lock_position == i);
unknown's avatar
unknown committed
324 325
    if ((uint) sql_lock->table[i]->reginfo.lock_type >= TL_WRITE_ALLOW_READ)
    {
326
      swap_variables(TABLE *, *table, sql_lock->table[i]);
unknown's avatar
unknown committed
327 328 329 330 331 332 333 334
      table++;
      found++;
    }
  }
  /* Unlock all read locked tables */
  if (i != found)
  {
    VOID(unlock_external(thd,table,i-found));
unknown's avatar
unknown committed
335
    sql_lock->table_count=found;
unknown's avatar
unknown committed
336
  }
337 338 339 340 341 342 343 344 345 346 347
  /* Fix the lock positions in TABLE */
  table= sql_lock->table;
  found= 0;
  for (i= 0; i < sql_lock->table_count; i++)
  {
    TABLE *tbl= *table;
    tbl->lock_position= table - sql_lock->table;
    tbl->lock_data_start= found;
    found+= tbl->lock_count;
    table++;
  }
unknown's avatar
unknown committed
348 349 350 351 352 353 354 355 356 357 358 359 360 361 362
  DBUG_VOID_RETURN;
}



void mysql_lock_remove(THD *thd, MYSQL_LOCK *locked,TABLE *table)
{
  mysql_unlock_some_tables(thd, &table,1);
  if (locked)
  {
    reg1 uint i;
    for (i=0; i < locked->table_count; i++)
    {
      if (locked->table[i] == table)
      {
363 364 365 366 367 368 369 370 371 372 373 374 375
        uint  j, removed_locks, old_tables;
        TABLE *tbl;
        uint lock_data_end;

        DBUG_ASSERT(table->lock_position == i);

        /* Decrement table_count in advance, making below expressions easier */
        old_tables= --locked->table_count;

        /* The table has 'removed_locks' lock data elements in locked->locks */
        removed_locks= table->lock_count;

        /* Move down all table pointers above 'i'. */
unknown's avatar
unknown committed
376 377
	bmove((char*) (locked->table+i),
	      (char*) (locked->table+i+1),
378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404
	      (old_tables - i) * sizeof(TABLE*));

        lock_data_end= table->lock_data_start + table->lock_count;
        /* Move down all lock data pointers above 'table->lock_data_end-1' */
        bmove((char*) (locked->locks + table->lock_data_start),
              (char*) (locked->locks + lock_data_end),
              (locked->lock_count - lock_data_end) *
              sizeof(THR_LOCK_DATA*));

        /*
          Fix moved table elements.
          lock_position is the index in the 'locked->table' array,
          it must be fixed by one.
          table->lock_data_start is pointer to the lock data for this table
          in the 'locked->locks' array, they must be fixed by 'removed_locks',
          the lock data count of the removed table.
        */
        for (j= i ; j < old_tables; j++)
        {
          tbl= locked->table[j];
          tbl->lock_position--;
          DBUG_ASSERT(tbl->lock_position == j);
          tbl->lock_data_start-= removed_locks;
        }

        /* Finally adjust lock_count. */
        locked->lock_count-= removed_locks;
unknown's avatar
unknown committed
405 406 407 408 409 410
	break;
      }
    }
  }
}

unknown's avatar
unknown committed
411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426
/* Downgrade all locks on a table to new WRITE level from WRITE_ONLY */

void mysql_lock_downgrade_write(THD *thd, TABLE *table,
                                thr_lock_type new_lock_type)
{
  MYSQL_LOCK *locked;
  TABLE *write_lock_used;
  if ((locked = get_lock_data(thd,&table,1,1,&write_lock_used)))
  {
    for (uint i=0; i < locked->lock_count; i++)
      thr_downgrade_write_lock(locked->locks[i], new_lock_type);
    my_free((gptr) locked,MYF(0));
  }
}


unknown's avatar
unknown committed
427 428
/* abort all other threads waiting to get lock in table */

unknown's avatar
unknown committed
429
void mysql_lock_abort(THD *thd, TABLE *table, bool upgrade_lock)
unknown's avatar
unknown committed
430 431 432
{
  MYSQL_LOCK *locked;
  TABLE *write_lock_used;
unknown's avatar
unknown committed
433 434
  DBUG_ENTER("mysql_lock_abort");

435 436
  if ((locked= get_lock_data(thd, &table, 1, GET_LOCK_UNLOCK,
                             &write_lock_used)))
unknown's avatar
unknown committed
437 438
  {
    for (uint i=0; i < locked->lock_count; i++)
unknown's avatar
unknown committed
439
      thr_abort_locks(locked->locks[i]->lock, upgrade_lock);
unknown's avatar
unknown committed
440 441
    my_free((gptr) locked,MYF(0));
  }
unknown's avatar
unknown committed
442
  DBUG_VOID_RETURN;
unknown's avatar
unknown committed
443 444 445
}


unknown's avatar
unknown committed
446 447 448 449 450 451 452 453 454 455 456 457
/*
  Abort one thread / table combination

  SYNOPSIS
    mysql_lock_abort_for_thread()
    thd		Thread handler
    table	Table that should be removed from lock queue

  RETURN
    0  Table was not locked by another thread
    1  Table was locked by at least one other thread
*/
458

unknown's avatar
unknown committed
459
bool mysql_lock_abort_for_thread(THD *thd, TABLE *table)
460 461 462
{
  MYSQL_LOCK *locked;
  TABLE *write_lock_used;
unknown's avatar
unknown committed
463
  bool result= FALSE;
464 465
  DBUG_ENTER("mysql_lock_abort_for_thread");

466 467
  if ((locked= get_lock_data(thd, &table, 1, GET_LOCK_UNLOCK,
                             &write_lock_used)))
468 469
  {
    for (uint i=0; i < locked->lock_count; i++)
unknown's avatar
unknown committed
470
    {
unknown's avatar
unknown committed
471 472 473
      if (thr_abort_locks_for_thread(locked->locks[i]->lock,
                                     table->in_use->real_id))
        result= TRUE;
unknown's avatar
unknown committed
474
    }
475 476
    my_free((gptr) locked,MYF(0));
  }
unknown's avatar
unknown committed
477
  DBUG_RETURN(result);
478 479 480
}


unknown's avatar
unknown committed
481 482 483
MYSQL_LOCK *mysql_lock_merge(MYSQL_LOCK *a,MYSQL_LOCK *b)
{
  MYSQL_LOCK *sql_lock;
484
  TABLE **table, **end_table;
unknown's avatar
unknown committed
485
  DBUG_ENTER("mysql_lock_merge");
486

unknown's avatar
unknown committed
487 488 489 490 491 492 493 494 495 496 497 498 499 500 501
  if (!(sql_lock= (MYSQL_LOCK*)
	my_malloc(sizeof(*sql_lock)+
		  sizeof(THR_LOCK_DATA*)*(a->lock_count+b->lock_count)+
		  sizeof(TABLE*)*(a->table_count+b->table_count),MYF(MY_WME))))
    DBUG_RETURN(0);				// Fatal error
  sql_lock->lock_count=a->lock_count+b->lock_count;
  sql_lock->table_count=a->table_count+b->table_count;
  sql_lock->locks=(THR_LOCK_DATA**) (sql_lock+1);
  sql_lock->table=(TABLE**) (sql_lock->locks+sql_lock->lock_count);
  memcpy(sql_lock->locks,a->locks,a->lock_count*sizeof(*a->locks));
  memcpy(sql_lock->locks+a->lock_count,b->locks,
	 b->lock_count*sizeof(*b->locks));
  memcpy(sql_lock->table,a->table,a->table_count*sizeof(*a->table));
  memcpy(sql_lock->table+a->table_count,b->table,
	 b->table_count*sizeof(*b->table));
502 503 504 505 506 507 508 509 510 511 512 513 514 515 516

  /*
    Now adjust lock_position and lock_data_start for all objects that was
    moved in 'b' (as there is now all objects in 'a' before these).
  */
  for (table= sql_lock->table + a->table_count,
         end_table= table + b->table_count;
       table < end_table;
       table++)
  {
    (*table)->lock_position+=   a->table_count;
    (*table)->lock_data_start+= a->lock_count;
  }

  /* Delete old, not needed locks */
unknown's avatar
unknown committed
517 518 519 520 521 522
  my_free((gptr) a,MYF(0));
  my_free((gptr) b,MYF(0));
  DBUG_RETURN(sql_lock);
}


523 524 525 526 527 528 529 530 531 532 533 534
/*
  Find duplicate lock in tables.

  SYNOPSIS
    mysql_lock_have_duplicate()
    thd                         The current thread.
    needle                      The table to check for duplicate lock.
    haystack                    The list of tables to search for the dup lock.

  NOTE
    This is mainly meant for MERGE tables in INSERT ... SELECT
    situations. The 'real', underlying tables can be found only after
535 536 537 538 539 540
    the MERGE tables are opened. This function assumes that the tables are
    already locked.

    Temporary tables are ignored here like they are ignored in
    get_lock_data(). If we allow two opens on temporary tables later,
    both functions should be checked.
541 542

  RETURN
543 544
    NULL        No duplicate lock found.
    ! NULL      First table from 'haystack' that matches a lock on 'needle'.
545 546 547 548 549
*/

TABLE_LIST *mysql_lock_have_duplicate(THD *thd, TABLE_LIST *needle,
                                      TABLE_LIST *haystack)
{
550 551 552 553 554 555 556
  MYSQL_LOCK            *mylock;
  TABLE                 **lock_tables;
  TABLE                 *table;
  TABLE                 *table2;
  THR_LOCK_DATA         **lock_locks;
  THR_LOCK_DATA         **table_lock_data;
  THR_LOCK_DATA         **end_data;
557 558 559 560 561
  THR_LOCK_DATA         **lock_data2;
  THR_LOCK_DATA         **end_data2;
  DBUG_ENTER("mysql_lock_have_duplicate");

  /*
562 563
    Table may not be defined for derived or view tables.
    Table may not be part of a lock for delayed operations.
564
  */
565
  if (! (table= needle->table) || ! table->lock_count)
566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583
    goto end;

  /* A temporary table does not have locks. */
  if (table->s->tmp_table == TMP_TABLE)
    goto end;

  /* Get command lock or LOCK TABLES lock. Maybe empty for INSERT DELAYED. */
  if (! (mylock= thd->lock ? thd->lock : thd->locked_tables))
    goto end;

  /* If we have less than two tables, we cannot have duplicates. */
  if (mylock->table_count < 2)
    goto end;

  lock_locks=  mylock->locks;
  lock_tables= mylock->table;

  /* Prepare table related variables that don't change in loop. */
584 585
  DBUG_ASSERT((table->lock_position < mylock->table_count) &&
              (table == lock_tables[table->lock_position]));
586 587 588 589
  table_lock_data= lock_locks + table->lock_data_start;
  end_data= table_lock_data + table->lock_count;

  for (; haystack; haystack= haystack->next_global)
590
  {
591 592 593 594 595 596 597
    if (haystack->placeholder() || haystack->schema_table)
      continue;
    table2= haystack->table;
    if (table2->s->tmp_table == TMP_TABLE)
      continue;

    /* All tables in list must be in lock. */
598 599
    DBUG_ASSERT((table2->lock_position < mylock->table_count) &&
                (table2 == lock_tables[table2->lock_position]));
600 601 602

    for (lock_data2=  lock_locks + table2->lock_data_start,
           end_data2= lock_data2 + table2->lock_count;
603 604 605
         lock_data2 < end_data2;
         lock_data2++)
    {
606 607 608 609 610 611
      THR_LOCK_DATA **lock_data;
      THR_LOCK *lock2= (*lock_data2)->lock;

      for (lock_data= table_lock_data;
           lock_data < end_data;
           lock_data++)
612
      {
613 614 615 616 617
        if ((*lock_data)->lock == lock2)
        {
          DBUG_PRINT("info", ("haystack match: '%s'", haystack->table_name));
          DBUG_RETURN(haystack);
        }
618 619 620 621 622
      }
    }
  }

 end:
623 624
  DBUG_PRINT("info", ("no duplicate found"));
  DBUG_RETURN(NULL);
625 626 627
}


unknown's avatar
unknown committed
628 629 630 631 632 633 634 635
	/* unlock a set of external */

static int unlock_external(THD *thd, TABLE **table,uint count)
{
  int error,error_code;
  DBUG_ENTER("unlock_external");

  error_code=0;
636
  do
unknown's avatar
unknown committed
637 638 639 640
  {
    if ((*table)->current_lock != F_UNLCK)
    {
      (*table)->current_lock = F_UNLCK;
641
      if ((error=(*table)->file->ha_external_lock(thd, F_UNLCK)))
642
      {
unknown's avatar
unknown committed
643
	error_code=error;
644 645
	print_lock_error(error_code, (*table)->file->table_type());
      }
unknown's avatar
unknown committed
646
    }
647 648
    table++;
  } while (--count);
unknown's avatar
unknown committed
649 650 651 652 653
  DBUG_RETURN(error_code);
}


/*
654 655 656 657 658 659 660 661 662 663 664
  Get lock structures from table structs and initialize locks

  SYNOPSIS
    get_lock_data()
    thd			Thread handler
    table_ptr		Pointer to tables that should be locks
    flags		One of:
			GET_LOCK_UNLOCK:      If we should send TL_IGNORE to
                        		      store lock
			GET_LOCK_STORE_LOCKS: Store lock info in TABLE
    write_lock_used	Store pointer to last table with WRITE_ALLOW_WRITE
unknown's avatar
unknown committed
665 666 667 668
*/


static MYSQL_LOCK *get_lock_data(THD *thd, TABLE **table_ptr, uint count,
669
				 uint flags, TABLE **write_lock_used)
unknown's avatar
unknown committed
670 671 672
{
  uint i,tables,lock_count;
  MYSQL_LOCK *sql_lock;
673 674
  THR_LOCK_DATA **locks, **locks_buf, **locks_start;
  TABLE **to, **table_buf;
unknown's avatar
unknown committed
675
  DBUG_ENTER("get_lock_data");
676

unknown's avatar
unknown committed
677
  DBUG_PRINT("info", ("count %d", count));
unknown's avatar
unknown committed
678 679 680
  *write_lock_used=0;
  for (i=tables=lock_count=0 ; i < count ; i++)
  {
681
    if (table_ptr[i]->s->tmp_table != TMP_TABLE)
unknown's avatar
unknown committed
682 683 684 685
    {
      tables+=table_ptr[i]->file->lock_count();
      lock_count++;
    }
686
    /*
687 688
      Check if we can lock the table. For some tables we cannot do that
      beacause of handler-specific locking issues.
689
    */
690 691 692 693 694
    if (!table_ptr[i]-> file->
          check_if_locking_is_allowed(thd->lex->sql_command, thd->lex->type,
                                      table_ptr[i], count,
                                      (thd == logger.get_general_log_thd()) ||
                                           (thd == logger.get_slow_log_thd())))
unknown's avatar
unknown committed
695
      DBUG_RETURN(0);
unknown's avatar
unknown committed
696 697
  }

698 699 700 701 702 703
  /*
    Allocating twice the number of pointers for lock data for use in
    thr_mulit_lock(). This function reorders the lock data, but cannot
    update the table values. So the second part of the array is copied
    from the first part immediately before calling thr_multi_lock().
  */
unknown's avatar
unknown committed
704
  if (!(sql_lock= (MYSQL_LOCK*)
705 706 707
	my_malloc(sizeof(*sql_lock) +
		  sizeof(THR_LOCK_DATA*) * tables * 2 +
                  sizeof(table_ptr) * lock_count,
unknown's avatar
unknown committed
708
		  MYF(0))))
unknown's avatar
unknown committed
709
    DBUG_RETURN(0);
710
  locks= locks_buf= sql_lock->locks= (THR_LOCK_DATA**) (sql_lock + 1);
711
  to= table_buf= sql_lock->table= (TABLE**) (locks + tables * 2);
unknown's avatar
unknown committed
712 713
  sql_lock->table_count=lock_count;
  sql_lock->lock_count=tables;
unknown's avatar
unknown committed
714 715
  DBUG_PRINT("info", ("sql_lock->table_count %d sql_lock->lock_count %d",
                      sql_lock->table_count, sql_lock->lock_count));
unknown's avatar
unknown committed
716 717 718 719

  for (i=0 ; i < count ; i++)
  {
    TABLE *table;
720 721
    enum thr_lock_type lock_type;

722
    if ((table=table_ptr[i])->s->tmp_table == TMP_TABLE)
unknown's avatar
unknown committed
723
      continue;
724
    lock_type= table->reginfo.lock_type;
unknown's avatar
unknown committed
725 726 727 728 729
    if (lock_type >= TL_WRITE_ALLOW_WRITE)
    {
      *write_lock_used=table;
      if (table->db_stat & HA_READ_ONLY)
      {
730
	my_error(ER_OPEN_AS_READONLY,MYF(0),table->alias);
731 732 733
        /* Clear the lock type of the lock data that are stored already. */
        sql_lock->lock_count= locks - sql_lock->locks;
        reset_lock_data(sql_lock);
unknown's avatar
unknown committed
734
	my_free((gptr) sql_lock,MYF(0));
unknown's avatar
unknown committed
735
	DBUG_RETURN(0);
unknown's avatar
unknown committed
736 737
      }
    }
unknown's avatar
unknown committed
738
    THR_LOCK_DATA **org_locks = locks;
739 740 741 742 743 744 745 746 747 748 749
    locks_start= locks;
    locks= table->file->store_lock(thd, locks,
                                   (flags & GET_LOCK_UNLOCK) ? TL_IGNORE :
                                   lock_type);
    if (flags & GET_LOCK_STORE_LOCKS)
    {
      table->lock_position=   (uint) (to - table_buf);
      table->lock_data_start= (uint) (locks_start - locks_buf);
      table->lock_count=      (uint) (locks - locks_start);
    }
    *to++= table;
unknown's avatar
unknown committed
750 751 752
    if (locks)
      for ( ; org_locks != locks ; org_locks++)
	(*org_locks)->debug_print_param= (void *) table;
unknown's avatar
unknown committed
753
  }
unknown's avatar
unknown committed
754
  DBUG_RETURN(sql_lock);
unknown's avatar
unknown committed
755
}
756

757

758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799
/*
  Reset lock type in lock data.

  SYNOPSIS
    reset_lock_data()
      sql_lock                  The MySQL lock.

  DESCRIPTION

    After a locking error we want to quit the locking of the table(s).
    The test case in the bug report for Bug #18544 has the following
    cases: 1. Locking error in lock_external() due to InnoDB timeout.
    2. Locking error in get_lock_data() due to missing write permission.
    3. Locking error in wait_if_global_read_lock() due to lock conflict.

    In all these cases we have already set the lock type into the lock
    data of the open table(s). If the table(s) are in the open table
    cache, they could be reused with the non-zero lock type set. This
    could lead to ignoring a different lock type with the next lock.

    Clear the lock type of all lock data. This ensures that the next
    lock request will set its lock type properly.

  RETURN
    void
*/

static void reset_lock_data(MYSQL_LOCK *sql_lock)
{
  THR_LOCK_DATA **ldata;
  THR_LOCK_DATA **ldata_end;

  for (ldata= sql_lock->locks, ldata_end= ldata + sql_lock->lock_count;
       ldata < ldata_end;
       ldata++)
  {
    /* Reset lock type. */
    (*ldata)->type= TL_UNLOCK;
  }
}


800
/*****************************************************************************
801 802
  Lock table based on the name.
  This is used when we need total access to a closed, not open table
803 804
*****************************************************************************/

805 806
/*
  Lock and wait for the named lock.
807 808 809 810 811 812 813 814 815 816 817 818 819

  SYNOPSIS
    lock_and_wait_for_table_name()
    thd			Thread handler
    table_list		Lock first table in this list


  NOTES
    Works together with global read lock.

  RETURN
    0	ok
    1	error
820 821 822 823 824 825 826 827
*/

int lock_and_wait_for_table_name(THD *thd, TABLE_LIST *table_list)
{
  int lock_retcode;
  int error= -1;
  DBUG_ENTER("lock_and_wait_for_table_name");

828
  if (wait_if_global_read_lock(thd, 0, 1))
829 830
    DBUG_RETURN(1);
  VOID(pthread_mutex_lock(&LOCK_open));
831
  if ((lock_retcode = lock_table_name(thd, table_list, TRUE)) < 0)
832 833 834 835 836 837 838 839 840 841
    goto end;
  if (lock_retcode && wait_for_locked_table_names(thd, table_list))
  {
    unlock_table_name(thd, table_list);
    goto end;
  }
  error=0;

end:
  pthread_mutex_unlock(&LOCK_open);
unknown's avatar
unknown committed
842
  start_waiting_global_read_lock(thd);
843 844 845 846
  DBUG_RETURN(error);
}


847 848
/*
  Put a not open table with an old refresh version in the table cache.
849 850 851 852 853

  SYNPOSIS
    lock_table_name()
    thd			Thread handler
    table_list		Lock first table in this list
854
    check_in_use        Do we need to check if table already in use by us
855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871

  WARNING
    If you are going to update the table, you should use
    lock_and_wait_for_table_name instead of this function as this works
    together with 'FLUSH TABLES WITH READ LOCK'

  NOTES
    This will force any other threads that uses the table to release it
    as soon as possible.

  REQUIREMENTS
    One must have a lock on LOCK_open !

  RETURN:
    < 0 error
    == 0 table locked
    > 0  table locked, but someone is using it
872 873
*/

874
int lock_table_name(THD *thd, TABLE_LIST *table_list, bool check_in_use)
875 876 877
{
  TABLE *table;
  char  key[MAX_DBKEY_LENGTH];
unknown's avatar
unknown committed
878
  char *db= table_list->db;
879
  uint  key_length;
880
  HASH_SEARCH_STATE state;
unknown's avatar
unknown committed
881
  DBUG_ENTER("lock_table_name");
882
  DBUG_PRINT("enter",("db: %s  name: %s", db, table_list->table_name));
883

unknown's avatar
unknown committed
884
  key_length= create_table_def_key(thd, key, table_list, 0);
885

886
  if (check_in_use)
unknown's avatar
unknown committed
887
  {
888 889 890 891 892 893
    /* Only insert the table if we haven't insert it already */
    for (table=(TABLE*) hash_first(&open_cache, (byte*)key,
                                   key_length, &state);
         table ;
         table = (TABLE*) hash_next(&open_cache,(byte*) key,
                                    key_length, &state))
unknown's avatar
unknown committed
894
    {
895 896 897 898 899 900 901
      if (table->in_use == thd)
      {
        DBUG_PRINT("info", ("Table is in use"));
        table->s->version= 0;                  // Ensure no one can use this
        table->locked_by_name= 1;
        DBUG_RETURN(0);
      }
unknown's avatar
unknown committed
902 903
    }
  }
unknown's avatar
unknown committed
904 905 906 907 908
  /*
    Create a table entry with the right key and with an old refresh version
    Note that we must use my_malloc() here as this is freed by the table
    cache
  */
unknown's avatar
unknown committed
909 910
  if (!(table= (TABLE*) my_malloc(sizeof(*table)+ sizeof(TABLE_SHARE)+
                                  key_length, MYF(MY_WME | MY_ZEROFILL))))
unknown's avatar
unknown committed
911
    DBUG_RETURN(-1);
unknown's avatar
unknown committed
912 913 914 915 916 917
  table->s= (TABLE_SHARE*) (table+1);
  memcpy((table->s->table_cache_key.str= (char*) (table->s+1)), key,
         key_length);
  table->s->table_cache_key.length= key_length;
  table->s->tmp_table= INTERNAL_TMP_TABLE;  // for intern_close_table
  table->in_use= thd;
unknown's avatar
unknown committed
918
  table->locked_by_name=1;
919 920
  table_list->table=table;

unknown's avatar
SCRUM  
unknown committed
921
  if (my_hash_insert(&open_cache, (byte*) table))
922 923
  {
    my_free((gptr) table,MYF(0));
unknown's avatar
unknown committed
924
    DBUG_RETURN(-1);
925
  }
926

unknown's avatar
unknown committed
927
  /* Return 1 if table is in use */
unknown's avatar
unknown committed
928
  DBUG_RETURN(test(remove_table_from_cache(thd, db, table_list->table_name,
929
             check_in_use ? RTFC_NO_FLAG : RTFC_WAIT_OTHER_THREAD_FLAG)));
930 931
}

932

933 934 935
void unlock_table_name(THD *thd, TABLE_LIST *table_list)
{
  if (table_list->table)
unknown's avatar
unknown committed
936
  {
unknown's avatar
unknown committed
937
    hash_delete(&open_cache, (byte*) table_list->table);
unknown's avatar
unknown committed
938 939
    (void) pthread_cond_broadcast(&COND_refresh);
  }
940 941
}

942

943 944
static bool locked_named_table(THD *thd, TABLE_LIST *table_list)
{
unknown's avatar
VIEW  
unknown committed
945
  for (; table_list ; table_list=table_list->next_local)
946
  {
unknown's avatar
unknown committed
947 948 949 950 951 952 953 954 955 956 957
    TABLE *table= table_list->table;
    if (table)
    {
      TABLE *save_next= table->next;
      bool result;
      table->next= 0;
      result= table_is_used(table_list->table, 0);
      table->next= save_next;
      if (result)
        return 1;
    }
958 959 960 961 962 963 964 965
  }
  return 0;					// All tables are locked
}


bool wait_for_locked_table_names(THD *thd, TABLE_LIST *table_list)
{
  bool result=0;
unknown's avatar
unknown committed
966
  DBUG_ENTER("wait_for_locked_table_names");
unknown's avatar
unknown committed
967

968
  safe_mutex_assert_owner(&LOCK_open);
969 970 971 972 973 974 975 976

  while (locked_named_table(thd,table_list))
  {
    if (thd->killed)
    {
      result=1;
      break;
    }
unknown's avatar
unknown committed
977
    wait_for_condition(thd, &LOCK_open, &COND_refresh);
unknown's avatar
unknown committed
978
    pthread_mutex_lock(&LOCK_open);
979
  }
unknown's avatar
unknown committed
980
  DBUG_RETURN(result);
981
}
982

983 984 985 986 987 988 989 990 991 992

/*
  Lock all tables in list with a name lock

  SYNOPSIS
    lock_table_names()
    thd			Thread handle
    table_list		Names of tables to lock

  NOTES
993 994 995 996
    If you are just locking one table, you should use
    lock_and_wait_for_table_name().

  REQUIREMENTS
997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008
    One must have a lock on LOCK_open when calling this

  RETURN
    0	ok
    1	Fatal error (end of memory ?)
*/

bool lock_table_names(THD *thd, TABLE_LIST *table_list)
{
  bool got_all_locks=1;
  TABLE_LIST *lock_table;

unknown's avatar
VIEW  
unknown committed
1009
  for (lock_table= table_list; lock_table; lock_table= lock_table->next_local)
1010 1011
  {
    int got_lock;
1012
    if ((got_lock=lock_table_name(thd,lock_table, TRUE)) < 0)
1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051
      goto end;					// Fatal error
    if (got_lock)
      got_all_locks=0;				// Someone is using table
  }

  /* If some table was in use, wait until we got the lock */
  if (!got_all_locks && wait_for_locked_table_names(thd, table_list))
    goto end;
  return 0;

end:
  unlock_table_names(thd, table_list, lock_table);
  return 1;
}


/*
  Unlock all tables in list with a name lock

  SYNOPSIS
    unlock_table_names()
    thd			Thread handle
    table_list		Names of tables to unlock
    last_table		Don't unlock any tables after this one.
			(default 0, which will unlock all tables)

  NOTES
    One must have a lock on LOCK_open when calling this
    This function will send a COND_refresh signal to inform other threads
    that the name locks are removed

  RETURN
    0	ok
    1	Fatal error (end of memory ?)
*/

void unlock_table_names(THD *thd, TABLE_LIST *table_list,
			TABLE_LIST *last_table)
{
1052
  DBUG_ENTER("unlock_table_names");
unknown's avatar
VIEW  
unknown committed
1053 1054 1055
  for (TABLE_LIST *table= table_list;
       table != last_table;
       table= table->next_local)
1056 1057
    unlock_table_name(thd,table);
  pthread_cond_broadcast(&COND_refresh);
1058
  DBUG_VOID_RETURN;
1059 1060 1061
}


1062
static void print_lock_error(int error, const char *table)
1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073
{
  int textno;
  DBUG_ENTER("print_lock_error");

  switch (error) {
  case HA_ERR_LOCK_WAIT_TIMEOUT:
    textno=ER_LOCK_WAIT_TIMEOUT;
    break;
  case HA_ERR_READ_ONLY_TRANSACTION:
    textno=ER_READ_ONLY_TRANSACTION;
    break;
1074 1075 1076
  case HA_ERR_LOCK_DEADLOCK:
    textno=ER_LOCK_DEADLOCK;
    break;
1077 1078 1079
  case HA_ERR_WRONG_COMMAND:
    textno=ER_ILLEGAL_HA;
    break;
1080 1081 1082 1083
  default:
    textno=ER_CANT_LOCK;
    break;
  }
1084 1085 1086 1087 1088 1089

  if ( textno == ER_ILLEGAL_HA )
    my_error(textno, MYF(ME_BELL+ME_OLDWIN+ME_WAITTANG), table);
  else
    my_error(textno, MYF(ME_BELL+ME_OLDWIN+ME_WAITTANG), error);

1090 1091 1092
  DBUG_VOID_RETURN;
}

1093 1094 1095 1096

/****************************************************************************
  Handling of global read locks

1097 1098 1099 1100
  Taking the global read lock is TWO steps (2nd step is optional; without
  it, COMMIT of existing transactions will be allowed):
  lock_global_read_lock() THEN make_global_read_lock_block_commit().

1101 1102
  The global locks are handled through the global variables:
  global_read_lock
1103 1104
    count of threads which have the global read lock (i.e. have completed at
    least the first step above)
1105
  global_read_lock_blocks_commit
1106
    count of threads which have the global read lock and block
1107
    commits (i.e. are in or have completed the second step above)
1108 1109
  waiting_for_read_lock
    count of threads which want to take a global read lock but cannot
1110
  protect_against_global_read_lock
1111 1112
    count of threads which have set protection against global read lock.

unknown's avatar
unknown committed
1113 1114
  access to them is protected with a mutex LOCK_global_read_lock

1115 1116 1117 1118 1119 1120 1121
  (XXX: one should never take LOCK_open if LOCK_global_read_lock is
  taken, otherwise a deadlock may occur. Other mutexes could be a
  problem too - grep the code for global_read_lock if you want to use
  any other mutex here) Also one must not hold LOCK_open when calling
  wait_if_global_read_lock(). When the thread with the global read lock
  tries to close its tables, it needs to take LOCK_open in
  close_thread_table().
unknown's avatar
unknown committed
1122

1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159
  How blocking of threads by global read lock is achieved: that's
  advisory. Any piece of code which should be blocked by global read lock must
  be designed like this:
  - call to wait_if_global_read_lock(). When this returns 0, no global read
  lock is owned; if argument abort_on_refresh was 0, none can be obtained.
  - job
  - if abort_on_refresh was 0, call to start_waiting_global_read_lock() to
  allow other threads to get the global read lock. I.e. removal of the
  protection.
  (Note: it's a bit like an implementation of rwlock).

  [ I am sorry to mention some SQL syntaxes below I know I shouldn't but found
  no better descriptive way ]

  Why does FLUSH TABLES WITH READ LOCK need to block COMMIT: because it's used
  to read a non-moving SHOW MASTER STATUS, and a COMMIT writes to the binary
  log.

  Why getting the global read lock is two steps and not one. Because FLUSH
  TABLES WITH READ LOCK needs to insert one other step between the two:
  flushing tables. So the order is
  1) lock_global_read_lock() (prevents any new table write locks, i.e. stalls
  all new updates)
  2) close_cached_tables() (the FLUSH TABLES), which will wait for tables
  currently opened and being updated to close (so it's possible that there is
  a moment where all new updates of server are stalled *and* FLUSH TABLES WITH
  READ LOCK is, too).
  3) make_global_read_lock_block_commit().
  If we have merged 1) and 3) into 1), we would have had this deadlock:
  imagine thread 1 and 2, in non-autocommit mode, thread 3, and an InnoDB
  table t.
  thd1: SELECT * FROM t FOR UPDATE;
  thd2: UPDATE t SET a=1; # blocked by row-level locks of thd1
  thd3: FLUSH TABLES WITH READ LOCK; # blocked in close_cached_tables() by the
  table instance of thd2
  thd1: COMMIT; # blocked by thd3.
  thd1 blocks thd2 which blocks thd3 which blocks thd1: deadlock.
unknown's avatar
unknown committed
1160

1161 1162 1163 1164 1165 1166
  Note that we need to support that one thread does
  FLUSH TABLES WITH READ LOCK; and then COMMIT;
  (that's what innobackup does, for some good reason).
  So in this exceptional case the COMMIT should not be blocked by the FLUSH
  TABLES WITH READ LOCK.

1167 1168 1169
****************************************************************************/

volatile uint global_read_lock=0;
1170
volatile uint global_read_lock_blocks_commit=0;
1171 1172 1173
static volatile uint protect_against_global_read_lock=0;
static volatile uint waiting_for_read_lock=0;

1174 1175 1176
#define GOT_GLOBAL_READ_LOCK               1
#define MADE_GLOBAL_READ_LOCK_BLOCK_COMMIT 2

1177 1178 1179 1180 1181 1182
bool lock_global_read_lock(THD *thd)
{
  DBUG_ENTER("lock_global_read_lock");

  if (!thd->global_read_lock)
  {
unknown's avatar
unknown committed
1183
    const char *old_message;
1184
    (void) pthread_mutex_lock(&LOCK_global_read_lock);
unknown's avatar
unknown committed
1185 1186
    old_message=thd->enter_cond(&COND_global_read_lock, &LOCK_global_read_lock,
                                "Waiting to get readlock");
unknown's avatar
unknown committed
1187 1188 1189 1190
    DBUG_PRINT("info",
	       ("waiting_for: %d  protect_against: %d",
		waiting_for_read_lock, protect_against_global_read_lock));

1191 1192
    waiting_for_read_lock++;
    while (protect_against_global_read_lock && !thd->killed)
unknown's avatar
unknown committed
1193
      pthread_cond_wait(&COND_global_read_lock, &LOCK_global_read_lock);
1194 1195 1196
    waiting_for_read_lock--;
    if (thd->killed)
    {
unknown's avatar
unknown committed
1197
      thd->exit_cond(old_message);
1198 1199
      DBUG_RETURN(1);
    }
1200
    thd->global_read_lock= GOT_GLOBAL_READ_LOCK;
1201
    global_read_lock++;
unknown's avatar
unknown committed
1202
    thd->exit_cond(old_message); // this unlocks LOCK_global_read_lock
1203
  }
1204 1205 1206 1207 1208 1209 1210 1211
  /*
    We DON'T set global_read_lock_blocks_commit now, it will be set after
    tables are flushed (as the present function serves for FLUSH TABLES WITH
    READ LOCK only). Doing things in this order is necessary to avoid
    deadlocks (we must allow COMMIT until all tables are closed; we should not
    forbid it before, or we can have a 3-thread deadlock if 2 do SELECT FOR
    UPDATE and one does FLUSH TABLES WITH READ LOCK).
  */
1212 1213 1214
  DBUG_RETURN(0);
}

unknown's avatar
unknown committed
1215

1216 1217 1218
void unlock_global_read_lock(THD *thd)
{
  uint tmp;
unknown's avatar
unknown committed
1219 1220 1221 1222 1223
  DBUG_ENTER("unlock_global_read_lock");
  DBUG_PRINT("info",
             ("global_read_lock: %u  global_read_lock_blocks_commit: %u",
              global_read_lock, global_read_lock_blocks_commit));

1224
  pthread_mutex_lock(&LOCK_global_read_lock);
1225
  tmp= --global_read_lock;
1226 1227
  if (thd->global_read_lock == MADE_GLOBAL_READ_LOCK_BLOCK_COMMIT)
    --global_read_lock_blocks_commit;
1228
  pthread_mutex_unlock(&LOCK_global_read_lock);
1229 1230
  /* Send the signal outside the mutex to avoid a context switch */
  if (!tmp)
unknown's avatar
unknown committed
1231 1232 1233 1234
  {
    DBUG_PRINT("signal", ("Broadcasting COND_global_read_lock"));
    pthread_cond_broadcast(&COND_global_read_lock);
  }
1235
  thd->global_read_lock= 0;
unknown's avatar
unknown committed
1236 1237

  DBUG_VOID_RETURN;
1238 1239
}

1240 1241 1242
#define must_wait (global_read_lock &&                             \
                   (is_not_commit ||                               \
                    global_read_lock_blocks_commit))
1243

1244 1245
bool wait_if_global_read_lock(THD *thd, bool abort_on_refresh,
                              bool is_not_commit)
1246 1247
{
  const char *old_message;
unknown's avatar
unknown committed
1248
  bool result= 0, need_exit_cond;
1249 1250
  DBUG_ENTER("wait_if_global_read_lock");

unknown's avatar
unknown committed
1251
  LINT_INIT(old_message);
1252 1253 1254 1255 1256 1257 1258
  /*
    Assert that we do not own LOCK_open. If we would own it, other
    threads could not close their tables. This would make a pretty
    deadlock.
  */
  safe_mutex_assert_not_owner(&LOCK_open);

1259
  (void) pthread_mutex_lock(&LOCK_global_read_lock);
1260
  if ((need_exit_cond= must_wait))
1261 1262 1263
  {
    if (thd->global_read_lock)		// This thread had the read locks
    {
1264
      if (is_not_commit)
unknown's avatar
merge  
unknown committed
1265 1266
        my_message(ER_CANT_UPDATE_WITH_READLOCK,
                   ER(ER_CANT_UPDATE_WITH_READLOCK), MYF(0));
1267
      (void) pthread_mutex_unlock(&LOCK_global_read_lock);
1268 1269 1270 1271 1272 1273
      /*
        We allow FLUSHer to COMMIT; we assume FLUSHer knows what it does.
        This allowance is needed to not break existing versions of innobackup
        which do a BEGIN; INSERT; FLUSH TABLES WITH READ LOCK; COMMIT.
      */
      DBUG_RETURN(is_not_commit);
unknown's avatar
unknown committed
1274
    }
unknown's avatar
unknown committed
1275
    old_message=thd->enter_cond(&COND_global_read_lock, &LOCK_global_read_lock,
1276
				"Waiting for release of readlock");
1277
    while (must_wait && ! thd->killed &&
1278
	   (!abort_on_refresh || thd->version == refresh_version))
unknown's avatar
unknown committed
1279 1280 1281 1282 1283
    {
      DBUG_PRINT("signal", ("Waiting for COND_global_read_lock"));
      (void) pthread_cond_wait(&COND_global_read_lock, &LOCK_global_read_lock);
      DBUG_PRINT("signal", ("Got COND_global_read_lock"));
    }
1284 1285 1286 1287 1288
    if (thd->killed)
      result=1;
  }
  if (!abort_on_refresh && !result)
    protect_against_global_read_lock++;
1289 1290 1291 1292
  /*
    The following is only true in case of a global read locks (which is rare)
    and if old_message is set
  */
unknown's avatar
unknown committed
1293 1294
  if (unlikely(need_exit_cond))
    thd->exit_cond(old_message); // this unlocks LOCK_global_read_lock
unknown's avatar
unknown committed
1295
  else
1296
    pthread_mutex_unlock(&LOCK_global_read_lock);
1297 1298 1299 1300 1301 1302 1303
  DBUG_RETURN(result);
}


void start_waiting_global_read_lock(THD *thd)
{
  bool tmp;
unknown's avatar
unknown committed
1304
  DBUG_ENTER("start_waiting_global_read_lock");
1305 1306
  if (unlikely(thd->global_read_lock))
    DBUG_VOID_RETURN;
1307
  (void) pthread_mutex_lock(&LOCK_global_read_lock);
1308 1309
  tmp= (!--protect_against_global_read_lock &&
        (waiting_for_read_lock || global_read_lock_blocks_commit));
1310
  (void) pthread_mutex_unlock(&LOCK_global_read_lock);
1311
  if (tmp)
unknown's avatar
unknown committed
1312
    pthread_cond_broadcast(&COND_global_read_lock);
unknown's avatar
unknown committed
1313
  DBUG_VOID_RETURN;
1314
}
1315 1316


1317
bool make_global_read_lock_block_commit(THD *thd)
1318
{
1319 1320 1321
  bool error;
  const char *old_message;
  DBUG_ENTER("make_global_read_lock_block_commit");
1322 1323 1324 1325 1326
  /*
    If we didn't succeed lock_global_read_lock(), or if we already suceeded
    make_global_read_lock_block_commit(), do nothing.
  */
  if (thd->global_read_lock != GOT_GLOBAL_READ_LOCK)
1327
    DBUG_RETURN(0);
1328
  pthread_mutex_lock(&LOCK_global_read_lock);
1329 1330
  /* increment this BEFORE waiting on cond (otherwise race cond) */
  global_read_lock_blocks_commit++;
1331 1332 1333
  /* For testing we set up some blocking, to see if we can be killed */
  DBUG_EXECUTE_IF("make_global_read_lock_block_commit_loop",
                  protect_against_global_read_lock++;);
unknown's avatar
unknown committed
1334
  old_message= thd->enter_cond(&COND_global_read_lock, &LOCK_global_read_lock,
1335 1336
                               "Waiting for all running commits to finish");
  while (protect_against_global_read_lock && !thd->killed)
unknown's avatar
unknown committed
1337
    pthread_cond_wait(&COND_global_read_lock, &LOCK_global_read_lock);
1338 1339
  DBUG_EXECUTE_IF("make_global_read_lock_block_commit_loop",
                  protect_against_global_read_lock--;);
unknown's avatar
unknown committed
1340
  if ((error= test(thd->killed)))
1341 1342 1343
    global_read_lock_blocks_commit--; // undo what we did
  else
    thd->global_read_lock= MADE_GLOBAL_READ_LOCK_BLOCK_COMMIT;
unknown's avatar
unknown committed
1344
  thd->exit_cond(old_message); // this unlocks LOCK_global_read_lock
1345
  DBUG_RETURN(error);
1346
}