lock.cc 42 KB
Newer Older
unknown's avatar
unknown committed
1
/* Copyright (C) 2000-2006 MySQL AB
unknown's avatar
unknown committed
2

unknown's avatar
unknown committed
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
unknown's avatar
unknown committed
5
   the Free Software Foundation; version 2 of the License.
unknown's avatar
unknown committed
6

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

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

23 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
  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
63 64 65 66 67 68 69
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>
70
#include <assert.h>
unknown's avatar
unknown committed
71 72 73

extern HASH open_cache;

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

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

85 86 87 88 89 90 91 92 93 94 95
/*
  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.
96 97 98 99 100 101 102
      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.
103 104 105

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

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

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

121 122
  *need_reopen= FALSE;

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

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

unknown's avatar
WL#3602  
unknown committed
153 154 155 156 157
    if (   write_lock_used
        && opt_readonly
        && ! (thd->security_ctx->master_access & SUPER_ACL)
        && ! thd->slave_thread
       )
unknown's avatar
unknown committed
158 159 160 161 162 163 164 165 166 167 168 169
    {
      /*
	Someone has issued SET GLOBAL READ_ONLY=1 and we want a write lock.
        We do not wait for READ_ONLY=0, and fail.
      */
      reset_lock_data(sql_lock);
      my_free((gptr) sql_lock, MYF(0));
      sql_lock=0;
      my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--read-only");
      break;
    }

unknown's avatar
unknown committed
170
    thd->proc_info="System lock";
unknown's avatar
unknown committed
171
    DBUG_PRINT("info", ("thd->proc_info %s", thd->proc_info));
172
    if (lock_external(thd, tables, count))
unknown's avatar
unknown committed
173
    {
174 175
      /* Clear the lock type of all lock data to avoid reusage. */
      reset_lock_data(sql_lock);
unknown's avatar
unknown committed
176 177 178 179
      my_free((gptr) sql_lock,MYF(0));
      sql_lock=0;
      break;
    }
180
    thd->proc_info="Table lock";
unknown's avatar
unknown committed
181
    DBUG_PRINT("info", ("thd->proc_info %s", thd->proc_info));
unknown's avatar
unknown committed
182
    thd->locked=1;
183 184 185 186
    /* 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. */
187 188
    rc= thr_lock_errno_to_mysql[(int) thr_multi_lock(sql_lock->locks +
                                                     sql_lock->lock_count,
189 190 191 192 193 194 195 196 197 198
                                                     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
199 200
    {
      thd->some_tables_deleted=1;		// Try again
201
      sql_lock->lock_count= 0;                  // Locks are already freed
unknown's avatar
unknown committed
202
    }
203
    else if (!thd->some_tables_deleted || (flags & MYSQL_LOCK_IGNORE_FLUSH))
unknown's avatar
unknown committed
204 205 206 207
    {
      thd->locked=0;
      break;
    }
208 209 210 211 212 213 214
    else if (!thd->open_tables)
    {
      // Only using temporary tables, no need to unlock
      thd->some_tables_deleted=0;
      thd->locked=0;
      break;
    }
215
    thd->proc_info=0;
unknown's avatar
unknown committed
216 217 218 219 220 221

    /* some table was altered or deleted. reopen tables marked deleted */
    mysql_unlock_tables(thd,sql_lock);
    thd->locked=0;
retry:
    sql_lock=0;
222 223 224 225 226
    if (flags & MYSQL_LOCK_NOTIFY_IF_NEED_REOPEN)
    {
      *need_reopen= TRUE;
      break;
    }
unknown's avatar
unknown committed
227 228 229
    if (wait_for_tables(thd))
      break;					// Couldn't open tables
  }
230
  thd->proc_info=0;
unknown's avatar
unknown committed
231 232
  if (thd->killed)
  {
unknown's avatar
SCRUM  
unknown committed
233
    thd->send_kill_message();
unknown's avatar
unknown committed
234 235 236 237 238 239
    if (sql_lock)
    {
      mysql_unlock_tables(thd,sql_lock);
      sql_lock=0;
    }
  }
unknown's avatar
unknown committed
240

241
  thd->lock_time();
unknown's avatar
unknown committed
242 243 244 245
  DBUG_RETURN (sql_lock);
}


246
static int lock_external(THD *thd, TABLE **tables, uint count)
unknown's avatar
unknown committed
247 248 249 250 251
{
  reg1 uint i;
  int lock_type,error;
  DBUG_ENTER("lock_external");

unknown's avatar
unknown committed
252
  DBUG_PRINT("info", ("count %d", count));
unknown's avatar
unknown committed
253 254
  for (i=1 ; i <= count ; i++, tables++)
  {
unknown's avatar
unknown committed
255
    DBUG_ASSERT((*tables)->reginfo.lock_type >= TL_READ);
unknown's avatar
unknown committed
256 257 258 259 260
    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;
261
    if ((error=(*tables)->file->ha_external_lock(thd,lock_type)))
unknown's avatar
unknown committed
262
    {
263
      print_lock_error(error, (*tables)->file->table_type());
264
      for (; i-- ; tables--)
unknown's avatar
unknown committed
265
      {
266
	(*tables)->file->ha_external_lock(thd, F_UNLCK);
unknown's avatar
unknown committed
267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283
	(*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
284 285
  if (sql_lock->lock_count)
    thr_multi_unlock(sql_lock->locks,sql_lock->lock_count);
286
  if (sql_lock->table_count)
unknown's avatar
unknown committed
287
    VOID(unlock_external(thd,sql_lock->table,sql_lock->table_count));
unknown's avatar
unknown committed
288 289 290 291 292 293 294 295 296 297 298 299 300
  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;
301 302
  if ((sql_lock= get_lock_data(thd, table, count, GET_LOCK_UNLOCK,
                               &write_lock_used)))
unknown's avatar
unknown committed
303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321
    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)
    {
322
      swap_variables(THR_LOCK_DATA *, *lock, sql_lock->locks[i]);
unknown's avatar
unknown committed
323 324 325 326 327 328 329 330
      lock++;
      found++;
    }
  }
  /* unlock the read locked tables */
  if (i != found)
  {
    thr_multi_unlock(lock,i-found);
unknown's avatar
unknown committed
331
    sql_lock->lock_count= found;
unknown's avatar
unknown committed
332 333
  }

334
  /* Then do the same for the external locks */
unknown's avatar
unknown committed
335 336 337 338
  /* Move all write locked tables first */
  TABLE **table=sql_lock->table;
  for (i=found=0 ; i < sql_lock->table_count ; i++)
  {
339
    DBUG_ASSERT(sql_lock->table[i]->lock_position == i);
unknown's avatar
unknown committed
340 341
    if ((uint) sql_lock->table[i]->reginfo.lock_type >= TL_WRITE_ALLOW_READ)
    {
342
      swap_variables(TABLE *, *table, sql_lock->table[i]);
unknown's avatar
unknown committed
343 344 345 346 347 348 349 350
      table++;
      found++;
    }
  }
  /* Unlock all read locked tables */
  if (i != found)
  {
    VOID(unlock_external(thd,table,i-found));
unknown's avatar
unknown committed
351
    sql_lock->table_count=found;
unknown's avatar
unknown committed
352
  }
353 354 355 356 357 358 359 360 361 362 363
  /* 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
364 365 366 367 368 369 370 371 372 373 374 375 376 377 378
  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)
      {
379 380 381 382 383 384 385 386 387 388 389 390 391
        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
392 393
	bmove((char*) (locked->table+i),
	      (char*) (locked->table+i+1),
394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420
	      (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
421 422 423 424 425 426
	break;
      }
    }
  }
}

unknown's avatar
unknown committed
427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442
/* 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
443 444
/* abort all other threads waiting to get lock in table */

unknown's avatar
unknown committed
445
void mysql_lock_abort(THD *thd, TABLE *table, bool upgrade_lock)
unknown's avatar
unknown committed
446 447 448
{
  MYSQL_LOCK *locked;
  TABLE *write_lock_used;
unknown's avatar
unknown committed
449 450
  DBUG_ENTER("mysql_lock_abort");

451 452
  if ((locked= get_lock_data(thd, &table, 1, GET_LOCK_UNLOCK,
                             &write_lock_used)))
unknown's avatar
unknown committed
453 454
  {
    for (uint i=0; i < locked->lock_count; i++)
unknown's avatar
unknown committed
455
      thr_abort_locks(locked->locks[i]->lock, upgrade_lock);
unknown's avatar
unknown committed
456 457
    my_free((gptr) locked,MYF(0));
  }
unknown's avatar
unknown committed
458
  DBUG_VOID_RETURN;
unknown's avatar
unknown committed
459 460 461
}


unknown's avatar
unknown committed
462 463 464 465 466 467 468 469 470 471 472 473
/*
  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
*/
474

unknown's avatar
unknown committed
475
bool mysql_lock_abort_for_thread(THD *thd, TABLE *table)
476 477 478
{
  MYSQL_LOCK *locked;
  TABLE *write_lock_used;
unknown's avatar
unknown committed
479
  bool result= FALSE;
480 481
  DBUG_ENTER("mysql_lock_abort_for_thread");

482 483
  if ((locked= get_lock_data(thd, &table, 1, GET_LOCK_UNLOCK,
                             &write_lock_used)))
484 485
  {
    for (uint i=0; i < locked->lock_count; i++)
unknown's avatar
unknown committed
486
    {
unknown's avatar
unknown committed
487 488 489
      if (thr_abort_locks_for_thread(locked->locks[i]->lock,
                                     table->in_use->real_id))
        result= TRUE;
unknown's avatar
unknown committed
490
    }
491 492
    my_free((gptr) locked,MYF(0));
  }
unknown's avatar
unknown committed
493
  DBUG_RETURN(result);
494 495 496
}


unknown's avatar
unknown committed
497 498 499
MYSQL_LOCK *mysql_lock_merge(MYSQL_LOCK *a,MYSQL_LOCK *b)
{
  MYSQL_LOCK *sql_lock;
500
  TABLE **table, **end_table;
unknown's avatar
unknown committed
501
  DBUG_ENTER("mysql_lock_merge");
502

unknown's avatar
unknown committed
503 504 505 506 507 508 509 510 511 512 513 514 515 516 517
  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));
518 519 520 521 522 523 524 525 526 527 528 529 530 531 532

  /*
    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
533 534 535 536 537 538
  my_free((gptr) a,MYF(0));
  my_free((gptr) b,MYF(0));
  DBUG_RETURN(sql_lock);
}


539 540 541 542 543 544 545 546 547 548 549 550
/*
  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
551 552 553 554 555 556
    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.
557 558

  RETURN
559 560
    NULL        No duplicate lock found.
    ! NULL      First table from 'haystack' that matches a lock on 'needle'.
561 562 563 564 565
*/

TABLE_LIST *mysql_lock_have_duplicate(THD *thd, TABLE_LIST *needle,
                                      TABLE_LIST *haystack)
{
566 567 568 569 570 571 572
  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;
573 574 575 576 577
  THR_LOCK_DATA         **lock_data2;
  THR_LOCK_DATA         **end_data2;
  DBUG_ENTER("mysql_lock_have_duplicate");

  /*
578 579
    Table may not be defined for derived or view tables.
    Table may not be part of a lock for delayed operations.
580
  */
581
  if (! (table= needle->table) || ! table->lock_count)
582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599
    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. */
600 601
  DBUG_ASSERT((table->lock_position < mylock->table_count) &&
              (table == lock_tables[table->lock_position]));
602 603 604 605
  table_lock_data= lock_locks + table->lock_data_start;
  end_data= table_lock_data + table->lock_count;

  for (; haystack; haystack= haystack->next_global)
606
  {
607 608 609 610 611 612 613
    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. */
614 615
    DBUG_ASSERT((table2->lock_position < mylock->table_count) &&
                (table2 == lock_tables[table2->lock_position]));
616 617 618

    for (lock_data2=  lock_locks + table2->lock_data_start,
           end_data2= lock_data2 + table2->lock_count;
619 620 621
         lock_data2 < end_data2;
         lock_data2++)
    {
622 623 624 625 626 627
      THR_LOCK_DATA **lock_data;
      THR_LOCK *lock2= (*lock_data2)->lock;

      for (lock_data= table_lock_data;
           lock_data < end_data;
           lock_data++)
628
      {
629 630 631 632 633
        if ((*lock_data)->lock == lock2)
        {
          DBUG_PRINT("info", ("haystack match: '%s'", haystack->table_name));
          DBUG_RETURN(haystack);
        }
634 635 636 637 638
      }
    }
  }

 end:
639 640
  DBUG_PRINT("info", ("no duplicate found"));
  DBUG_RETURN(NULL);
641 642 643
}


unknown's avatar
unknown committed
644 645 646 647 648 649 650 651
	/* 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;
652
  do
unknown's avatar
unknown committed
653 654 655 656
  {
    if ((*table)->current_lock != F_UNLCK)
    {
      (*table)->current_lock = F_UNLCK;
657
      if ((error=(*table)->file->ha_external_lock(thd, F_UNLCK)))
658
      {
unknown's avatar
unknown committed
659
	error_code=error;
660 661
	print_lock_error(error_code, (*table)->file->table_type());
      }
unknown's avatar
unknown committed
662
    }
663 664
    table++;
  } while (--count);
unknown's avatar
unknown committed
665 666 667 668 669
  DBUG_RETURN(error_code);
}


/*
670 671 672 673 674 675 676 677 678 679 680
  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
681 682 683 684
*/


static MYSQL_LOCK *get_lock_data(THD *thd, TABLE **table_ptr, uint count,
685
				 uint flags, TABLE **write_lock_used)
unknown's avatar
unknown committed
686 687 688
{
  uint i,tables,lock_count;
  MYSQL_LOCK *sql_lock;
689 690
  THR_LOCK_DATA **locks, **locks_buf, **locks_start;
  TABLE **to, **table_buf;
unknown's avatar
unknown committed
691
  DBUG_ENTER("get_lock_data");
692

unknown's avatar
unknown committed
693
  DBUG_PRINT("info", ("count %d", count));
unknown's avatar
unknown committed
694 695 696
  *write_lock_used=0;
  for (i=tables=lock_count=0 ; i < count ; i++)
  {
697
    if (table_ptr[i]->s->tmp_table != TMP_TABLE)
unknown's avatar
unknown committed
698 699 700 701
    {
      tables+=table_ptr[i]->file->lock_count();
      lock_count++;
    }
702
    /*
703 704
      Check if we can lock the table. For some tables we cannot do that
      beacause of handler-specific locking issues.
705
    */
706 707 708 709
    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()) ||
710 711
                                      (thd == logger.get_slow_log_thd()) ||
                                      (thd == logger.get_privileged_thread())))
unknown's avatar
unknown committed
712
      DBUG_RETURN(0);
unknown's avatar
unknown committed
713 714
  }

715 716 717 718 719 720
  /*
    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
721
  if (!(sql_lock= (MYSQL_LOCK*)
722 723 724
	my_malloc(sizeof(*sql_lock) +
		  sizeof(THR_LOCK_DATA*) * tables * 2 +
                  sizeof(table_ptr) * lock_count,
unknown's avatar
unknown committed
725
		  MYF(0))))
unknown's avatar
unknown committed
726
    DBUG_RETURN(0);
727
  locks= locks_buf= sql_lock->locks= (THR_LOCK_DATA**) (sql_lock + 1);
728
  to= table_buf= sql_lock->table= (TABLE**) (locks + tables * 2);
unknown's avatar
unknown committed
729 730
  sql_lock->table_count=lock_count;
  sql_lock->lock_count=tables;
unknown's avatar
unknown committed
731 732
  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
733 734 735 736

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

739
    if ((table=table_ptr[i])->s->tmp_table == TMP_TABLE)
unknown's avatar
unknown committed
740
      continue;
741
    lock_type= table->reginfo.lock_type;
unknown's avatar
unknown committed
742 743 744 745 746
    if (lock_type >= TL_WRITE_ALLOW_WRITE)
    {
      *write_lock_used=table;
      if (table->db_stat & HA_READ_ONLY)
      {
747
	my_error(ER_OPEN_AS_READONLY,MYF(0),table->alias);
748 749 750
        /* 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
751
	my_free((gptr) sql_lock,MYF(0));
unknown's avatar
unknown committed
752
	DBUG_RETURN(0);
unknown's avatar
unknown committed
753 754
      }
    }
unknown's avatar
unknown committed
755
    THR_LOCK_DATA **org_locks = locks;
756 757 758 759 760 761 762 763 764 765 766
    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
767 768 769
    if (locks)
      for ( ; org_locks != locks ; org_locks++)
	(*org_locks)->debug_print_param= (void *) table;
unknown's avatar
unknown committed
770
  }
unknown's avatar
unknown committed
771
  DBUG_RETURN(sql_lock);
unknown's avatar
unknown committed
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 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816
/*
  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;
  }
}


817
/*****************************************************************************
818 819
  Lock table based on the name.
  This is used when we need total access to a closed, not open table
820 821
*****************************************************************************/

822 823
/*
  Lock and wait for the named lock.
824 825 826 827 828 829 830 831 832 833 834 835 836

  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
837 838 839 840 841 842 843 844
*/

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");

845
  if (wait_if_global_read_lock(thd, 0, 1))
846 847
    DBUG_RETURN(1);
  VOID(pthread_mutex_lock(&LOCK_open));
848
  if ((lock_retcode = lock_table_name(thd, table_list, TRUE)) < 0)
849 850 851 852 853 854 855 856 857 858
    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
859
  start_waiting_global_read_lock(thd);
860 861 862 863
  DBUG_RETURN(error);
}


864 865
/*
  Put a not open table with an old refresh version in the table cache.
866 867 868 869 870

  SYNPOSIS
    lock_table_name()
    thd			Thread handler
    table_list		Lock first table in this list
871
    check_in_use        Do we need to check if table already in use by us
872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888

  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
889 890
*/

891
int lock_table_name(THD *thd, TABLE_LIST *table_list, bool check_in_use)
892 893
{
  TABLE *table;
894 895
  TABLE_SHARE *share;
  char *key_buff;
896
  char  key[MAX_DBKEY_LENGTH];
unknown's avatar
unknown committed
897
  char *db= table_list->db;
898
  uint  key_length;
899
  HASH_SEARCH_STATE state;
unknown's avatar
unknown committed
900
  DBUG_ENTER("lock_table_name");
901
  DBUG_PRINT("enter",("db: %s  name: %s", db, table_list->table_name));
902

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

905
  if (check_in_use)
unknown's avatar
unknown committed
906
  {
907 908 909 910 911 912
    /* 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
913
    {
914 915 916 917 918 919 920
      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
921 922
    }
  }
unknown's avatar
unknown committed
923 924
  /*
    Create a table entry with the right key and with an old refresh version
925 926
    Note that we must use my_multi_malloc() here as this is freed by the
    table cache
unknown's avatar
unknown committed
927
  */
928 929 930 931 932
  if (!my_multi_malloc(MYF(MY_WME | MY_ZEROFILL),
                       &table, sizeof(*table),
                       &share, sizeof(*share),
                       &key_buff, key_length,
                       NULL))
unknown's avatar
unknown committed
933
    DBUG_RETURN(-1);
934 935 936
  table->s= share;
  share->set_table_cache_key(key_buff, key, key_length);
  share->tmp_table= INTERNAL_TMP_TABLE;  // for intern_close_table
unknown's avatar
unknown committed
937
  table->in_use= thd;
unknown's avatar
unknown committed
938
  table->locked_by_name=1;
939 940
  table_list->table=table;

unknown's avatar
SCRUM  
unknown committed
941
  if (my_hash_insert(&open_cache, (byte*) table))
942 943
  {
    my_free((gptr) table,MYF(0));
unknown's avatar
unknown committed
944
    DBUG_RETURN(-1);
945
  }
946

unknown's avatar
unknown committed
947
  /* Return 1 if table is in use */
unknown's avatar
unknown committed
948
  DBUG_RETURN(test(remove_table_from_cache(thd, db, table_list->table_name,
949
             check_in_use ? RTFC_NO_FLAG : RTFC_WAIT_OTHER_THREAD_FLAG)));
950 951
}

952

953 954 955
void unlock_table_name(THD *thd, TABLE_LIST *table_list)
{
  if (table_list->table)
unknown's avatar
unknown committed
956
  {
unknown's avatar
unknown committed
957
    hash_delete(&open_cache, (byte*) table_list->table);
958
    broadcast_refresh();
unknown's avatar
unknown committed
959
  }
960 961
}

962

963 964
static bool locked_named_table(THD *thd, TABLE_LIST *table_list)
{
unknown's avatar
VIEW  
unknown committed
965
  for (; table_list ; table_list=table_list->next_local)
966
  {
unknown's avatar
unknown committed
967 968 969 970 971 972 973 974 975 976 977
    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;
    }
978 979 980 981 982 983 984 985
  }
  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
986
  DBUG_ENTER("wait_for_locked_table_names");
unknown's avatar
unknown committed
987

988
  safe_mutex_assert_owner(&LOCK_open);
989 990 991 992 993 994 995 996

  while (locked_named_table(thd,table_list))
  {
    if (thd->killed)
    {
      result=1;
      break;
    }
unknown's avatar
unknown committed
997
    wait_for_condition(thd, &LOCK_open, &COND_refresh);
unknown's avatar
unknown committed
998
    pthread_mutex_lock(&LOCK_open);
999
  }
unknown's avatar
unknown committed
1000
  DBUG_RETURN(result);
1001
}
1002

1003 1004 1005 1006 1007 1008 1009 1010 1011 1012

/*
  Lock all tables in list with a name lock

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

  NOTES
1013 1014 1015 1016
    If you are just locking one table, you should use
    lock_and_wait_for_table_name().

  REQUIREMENTS
1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028
    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
1029
  for (lock_table= table_list; lock_table; lock_table= lock_table->next_local)
1030 1031
  {
    int got_lock;
1032
    if ((got_lock=lock_table_name(thd,lock_table, TRUE)) < 0)
1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059
      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
1060 1061 1062
    One must have a lock on LOCK_open when calling this.
    This function will broadcast refresh signals to inform other threads
    that the name locks are removed.
1063 1064 1065 1066 1067 1068 1069 1070 1071

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

void unlock_table_names(THD *thd, TABLE_LIST *table_list,
			TABLE_LIST *last_table)
{
unknown's avatar
unknown committed
1072
  DBUG_ENTER("unlock_table_names");
unknown's avatar
VIEW  
unknown committed
1073 1074 1075
  for (TABLE_LIST *table= table_list;
       table != last_table;
       table= table->next_local)
1076
    unlock_table_name(thd,table);
1077
  broadcast_refresh();
unknown's avatar
unknown committed
1078
  DBUG_VOID_RETURN;
1079 1080 1081
}


1082
static void print_lock_error(int error, const char *table)
1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093
{
  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;
1094 1095 1096
  case HA_ERR_LOCK_DEADLOCK:
    textno=ER_LOCK_DEADLOCK;
    break;
1097 1098 1099
  case HA_ERR_WRONG_COMMAND:
    textno=ER_ILLEGAL_HA;
    break;
1100 1101 1102 1103
  default:
    textno=ER_CANT_LOCK;
    break;
  }
1104 1105 1106 1107 1108 1109

  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);

1110 1111 1112
  DBUG_VOID_RETURN;
}

1113 1114 1115 1116

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

1117 1118 1119 1120
  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().

1121 1122
  The global locks are handled through the global variables:
  global_read_lock
1123 1124
    count of threads which have the global read lock (i.e. have completed at
    least the first step above)
1125
  global_read_lock_blocks_commit
1126
    count of threads which have the global read lock and block
1127
    commits (i.e. are in or have completed the second step above)
1128 1129
  waiting_for_read_lock
    count of threads which want to take a global read lock but cannot
1130
  protect_against_global_read_lock
1131 1132
    count of threads which have set protection against global read lock.

unknown's avatar
unknown committed
1133 1134
  access to them is protected with a mutex LOCK_global_read_lock

1135 1136 1137 1138 1139 1140 1141
  (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
1142

1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179
  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
1180

1181 1182 1183 1184 1185 1186
  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.

1187 1188 1189
****************************************************************************/

volatile uint global_read_lock=0;
1190
volatile uint global_read_lock_blocks_commit=0;
1191 1192 1193
static volatile uint protect_against_global_read_lock=0;
static volatile uint waiting_for_read_lock=0;

1194 1195 1196
#define GOT_GLOBAL_READ_LOCK               1
#define MADE_GLOBAL_READ_LOCK_BLOCK_COMMIT 2

1197 1198 1199 1200 1201 1202
bool lock_global_read_lock(THD *thd)
{
  DBUG_ENTER("lock_global_read_lock");

  if (!thd->global_read_lock)
  {
unknown's avatar
unknown committed
1203
    const char *old_message;
1204
    (void) pthread_mutex_lock(&LOCK_global_read_lock);
unknown's avatar
unknown committed
1205 1206
    old_message=thd->enter_cond(&COND_global_read_lock, &LOCK_global_read_lock,
                                "Waiting to get readlock");
unknown's avatar
unknown committed
1207 1208 1209 1210
    DBUG_PRINT("info",
	       ("waiting_for: %d  protect_against: %d",
		waiting_for_read_lock, protect_against_global_read_lock));

1211 1212
    waiting_for_read_lock++;
    while (protect_against_global_read_lock && !thd->killed)
unknown's avatar
unknown committed
1213
      pthread_cond_wait(&COND_global_read_lock, &LOCK_global_read_lock);
1214 1215 1216
    waiting_for_read_lock--;
    if (thd->killed)
    {
unknown's avatar
unknown committed
1217
      thd->exit_cond(old_message);
1218 1219
      DBUG_RETURN(1);
    }
1220
    thd->global_read_lock= GOT_GLOBAL_READ_LOCK;
1221
    global_read_lock++;
unknown's avatar
unknown committed
1222
    thd->exit_cond(old_message); // this unlocks LOCK_global_read_lock
1223
  }
1224 1225 1226 1227 1228 1229 1230 1231
  /*
    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).
  */
1232 1233 1234
  DBUG_RETURN(0);
}

unknown's avatar
unknown committed
1235

1236 1237 1238
void unlock_global_read_lock(THD *thd)
{
  uint tmp;
unknown's avatar
unknown committed
1239 1240 1241 1242 1243
  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));

1244
  pthread_mutex_lock(&LOCK_global_read_lock);
1245
  tmp= --global_read_lock;
1246 1247
  if (thd->global_read_lock == MADE_GLOBAL_READ_LOCK_BLOCK_COMMIT)
    --global_read_lock_blocks_commit;
1248
  pthread_mutex_unlock(&LOCK_global_read_lock);
1249 1250
  /* Send the signal outside the mutex to avoid a context switch */
  if (!tmp)
unknown's avatar
unknown committed
1251 1252 1253 1254
  {
    DBUG_PRINT("signal", ("Broadcasting COND_global_read_lock"));
    pthread_cond_broadcast(&COND_global_read_lock);
  }
1255
  thd->global_read_lock= 0;
unknown's avatar
unknown committed
1256 1257

  DBUG_VOID_RETURN;
1258 1259
}

1260 1261 1262
#define must_wait (global_read_lock &&                             \
                   (is_not_commit ||                               \
                    global_read_lock_blocks_commit))
1263

1264 1265
bool wait_if_global_read_lock(THD *thd, bool abort_on_refresh,
                              bool is_not_commit)
1266 1267
{
  const char *old_message;
unknown's avatar
unknown committed
1268
  bool result= 0, need_exit_cond;
1269 1270
  DBUG_ENTER("wait_if_global_read_lock");

unknown's avatar
unknown committed
1271
  LINT_INIT(old_message);
1272 1273 1274 1275 1276 1277 1278
  /*
    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);

1279
  (void) pthread_mutex_lock(&LOCK_global_read_lock);
1280
  if ((need_exit_cond= must_wait))
1281 1282 1283
  {
    if (thd->global_read_lock)		// This thread had the read locks
    {
1284
      if (is_not_commit)
unknown's avatar
merge  
unknown committed
1285 1286
        my_message(ER_CANT_UPDATE_WITH_READLOCK,
                   ER(ER_CANT_UPDATE_WITH_READLOCK), MYF(0));
1287
      (void) pthread_mutex_unlock(&LOCK_global_read_lock);
1288 1289 1290 1291 1292 1293
      /*
        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
1294
    }
unknown's avatar
unknown committed
1295
    old_message=thd->enter_cond(&COND_global_read_lock, &LOCK_global_read_lock,
1296
				"Waiting for release of readlock");
1297
    while (must_wait && ! thd->killed &&
1298
	   (!abort_on_refresh || thd->version == refresh_version))
unknown's avatar
unknown committed
1299 1300 1301 1302 1303
    {
      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"));
    }
1304 1305 1306 1307 1308
    if (thd->killed)
      result=1;
  }
  if (!abort_on_refresh && !result)
    protect_against_global_read_lock++;
1309 1310 1311 1312
  /*
    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
1313 1314
  if (unlikely(need_exit_cond))
    thd->exit_cond(old_message); // this unlocks LOCK_global_read_lock
unknown's avatar
unknown committed
1315
  else
1316
    pthread_mutex_unlock(&LOCK_global_read_lock);
1317 1318 1319 1320 1321 1322 1323
  DBUG_RETURN(result);
}


void start_waiting_global_read_lock(THD *thd)
{
  bool tmp;
unknown's avatar
unknown committed
1324
  DBUG_ENTER("start_waiting_global_read_lock");
1325 1326
  if (unlikely(thd->global_read_lock))
    DBUG_VOID_RETURN;
1327
  (void) pthread_mutex_lock(&LOCK_global_read_lock);
1328 1329
  tmp= (!--protect_against_global_read_lock &&
        (waiting_for_read_lock || global_read_lock_blocks_commit));
1330
  (void) pthread_mutex_unlock(&LOCK_global_read_lock);
1331
  if (tmp)
unknown's avatar
unknown committed
1332
    pthread_cond_broadcast(&COND_global_read_lock);
unknown's avatar
unknown committed
1333
  DBUG_VOID_RETURN;
1334
}
1335 1336


1337
bool make_global_read_lock_block_commit(THD *thd)
1338
{
1339 1340 1341
  bool error;
  const char *old_message;
  DBUG_ENTER("make_global_read_lock_block_commit");
1342 1343 1344 1345 1346
  /*
    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)
1347
    DBUG_RETURN(0);
1348
  pthread_mutex_lock(&LOCK_global_read_lock);
1349 1350
  /* increment this BEFORE waiting on cond (otherwise race cond) */
  global_read_lock_blocks_commit++;
1351 1352 1353
  /* 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
1354
  old_message= thd->enter_cond(&COND_global_read_lock, &LOCK_global_read_lock,
1355 1356
                               "Waiting for all running commits to finish");
  while (protect_against_global_read_lock && !thd->killed)
unknown's avatar
unknown committed
1357
    pthread_cond_wait(&COND_global_read_lock, &LOCK_global_read_lock);
1358 1359
  DBUG_EXECUTE_IF("make_global_read_lock_block_commit_loop",
                  protect_against_global_read_lock--;);
unknown's avatar
unknown committed
1360
  if ((error= test(thd->killed)))
1361 1362 1363
    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
1364
  thd->exit_cond(old_message); // this unlocks LOCK_global_read_lock
1365
  DBUG_RETURN(error);
1366
}
1367

1368

1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402
/*
  Broadcast COND_refresh and COND_global_read_lock.

  SYNOPSIS
    broadcast_refresh()
      void                      No parameters.

  DESCRIPTION
    Due to a bug in a threading library it could happen that a signal
    did not reach its target. A condition for this was that the same
    condition variable was used with different mutexes in
    pthread_cond_wait(). Some time ago we changed LOCK_open to
    LOCK_global_read_lock in global read lock handling. So COND_refresh
    was used with LOCK_open and LOCK_global_read_lock.

    We did now also change from COND_refresh to COND_global_read_lock
    in global read lock handling. But now it is necessary to signal
    both conditions at the same time.

  NOTE
    When signalling COND_global_read_lock within the global read lock
    handling, it is not necessary to also signal COND_refresh.

  RETURN
    void
*/

void broadcast_refresh(void)
{
  VOID(pthread_cond_broadcast(&COND_refresh));
  VOID(pthread_cond_broadcast(&COND_global_read_lock));
}