lock.cc
上传用户:tsgydb
上传日期:2007-04-14
资源大小:10674k
文件大小:13k
源码类别:

MySQL数据库

开发平台:

Visual C++

  1. /* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB
  2.    
  3.    This program is free software; you can redistribute it and/or modify
  4.    it under the terms of the GNU General Public License as published by
  5.    the Free Software Foundation; either version 2 of the License, or
  6.    (at your option) any later version.
  7.    
  8.    This program is distributed in the hope that it will be useful,
  9.    but WITHOUT ANY WARRANTY; without even the implied warranty of
  10.    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  11.    GNU General Public License for more details.
  12.    
  13.    You should have received a copy of the GNU General Public License
  14.    along with this program; if not, write to the Free Software
  15.    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */
  16. /* locking functions for mysql */
  17. /*
  18.   Because of the new concurrent inserts, we must first get external locks
  19.   before getting internal locks.  If we do it in the other order, the status
  20.   information is not up to date when called from the lock handler.
  21. TODO:
  22.   Change to use my_malloc() ONLY when using LOCK TABLES command or when
  23.   we are forced to use mysql_lock_merge.
  24. */
  25. #include "mysql_priv.h"
  26. #include <hash.h>
  27. extern HASH open_cache;
  28. static MYSQL_LOCK *get_lock_data(THD *thd, TABLE **table,uint count,
  29.  bool unlock, TABLE **write_locked);
  30. static int lock_external(TABLE **table,uint count);
  31. static int unlock_external(THD *thd, TABLE **table,uint count);
  32. MYSQL_LOCK *mysql_lock_tables(THD *thd,TABLE **tables,uint count)
  33. {
  34.   MYSQL_LOCK *sql_lock;
  35.   TABLE *write_lock_used;
  36.   DBUG_ENTER("mysql_lock_tables");
  37.   for (;;)
  38.   {
  39.     if (!(sql_lock = get_lock_data(thd,tables,count, 0,&write_lock_used)))
  40.       break;
  41.     if (global_read_lock && write_lock_used)
  42.     {
  43.       /*
  44. Someone has issued LOCK ALL TABLES FOR READ and we want a write lock
  45. Wait until the lock is gone
  46.       */
  47.       if (thd->global_read_lock) // This thread had the read locks
  48.       {
  49. my_error(ER_TABLE_NOT_LOCKED_FOR_WRITE,MYF(0),
  50.  write_lock_used->table_name);
  51. my_free((gptr) sql_lock,MYF(0));
  52. sql_lock=0;
  53. break;
  54.       }
  55.       pthread_mutex_lock(&LOCK_open);
  56.       pthread_mutex_lock(&thd->mysys_var->mutex);
  57.       thd->mysys_var->current_mutex= &LOCK_open;
  58.       thd->mysys_var->current_cond= &COND_refresh;
  59.       thd->proc_info="Waiting for table";
  60.       pthread_mutex_unlock(&thd->mysys_var->mutex);
  61.       while (global_read_lock && ! thd->killed &&
  62.      thd->version == refresh_version)
  63.       {
  64. (void) pthread_cond_wait(&COND_refresh,&LOCK_open);
  65.       }
  66.       pthread_mutex_unlock(&LOCK_open);
  67.       pthread_mutex_lock(&thd->mysys_var->mutex);
  68.       thd->mysys_var->current_mutex= 0;
  69.       thd->mysys_var->current_cond= 0;
  70.       thd->proc_info= 0;
  71.       pthread_mutex_unlock(&thd->mysys_var->mutex);
  72.       if (thd->version != refresh_version || thd->killed)
  73.       {
  74. my_free((gptr) sql_lock,MYF(0));
  75. goto retry;
  76.       }
  77.     }
  78.     thd->proc_info="System lock";
  79.     if (lock_external(tables,count))
  80.     {
  81.       my_free((gptr) sql_lock,MYF(0));
  82.       sql_lock=0;
  83.       thd->proc_info=0;
  84.       break;
  85.     }
  86.     thd->proc_info=0;
  87.     thd->locked=1;
  88.     if (thr_multi_lock(sql_lock->locks,sql_lock->lock_count))
  89.     {
  90.       thd->some_tables_deleted=1; // Try again
  91.       sql_lock->lock_count=0; // Locks are alread freed
  92.     }
  93.     else if (!thd->some_tables_deleted)
  94.     {
  95.       thd->locked=0;
  96.       break;
  97.     }
  98.     /* some table was altered or deleted. reopen tables marked deleted */
  99.     mysql_unlock_tables(thd,sql_lock);
  100.     thd->locked=0;
  101. retry:
  102.     sql_lock=0;
  103.     if (wait_for_tables(thd))
  104.       break; // Couldn't open tables
  105.   }
  106.   if (thd->killed)
  107.   {
  108.     my_error(ER_SERVER_SHUTDOWN,MYF(0));
  109.     if (sql_lock)
  110.     {
  111.       mysql_unlock_tables(thd,sql_lock);
  112.       sql_lock=0;
  113.     }
  114.   }
  115.   thd->lock_time();
  116.   DBUG_RETURN (sql_lock);
  117. }
  118. static int lock_external(TABLE **tables,uint count)
  119. {
  120.   reg1 uint i;
  121.   int lock_type,error;
  122.   THD *thd=current_thd;
  123.   DBUG_ENTER("lock_external");
  124.   for (i=1 ; i <= count ; i++, tables++)
  125.   {
  126.     lock_type=F_WRLCK; /* Lock exclusive */
  127.     if ((*tables)->db_stat & HA_READ_ONLY ||
  128. ((*tables)->reginfo.lock_type >= TL_READ &&
  129.  (*tables)->reginfo.lock_type <= TL_READ_NO_INSERT))
  130.       lock_type=F_RDLCK;
  131.     if ((error=(*tables)->file->external_lock(thd,lock_type)))
  132.     {
  133.       for ( ; i-- ; tables--)
  134.       {
  135. (*tables)->file->external_lock(thd, F_UNLCK);
  136. (*tables)->current_lock=F_UNLCK;
  137.       }
  138.       my_error(ER_CANT_LOCK,MYF(ME_BELL+ME_OLDWIN+ME_WAITTANG),error);
  139.       DBUG_RETURN(error);
  140.     }
  141.     else
  142.     {
  143.       (*tables)->db_stat &= ~ HA_BLOCK_LOCK;
  144.       (*tables)->current_lock= lock_type;
  145.     }
  146.   }
  147.   DBUG_RETURN(0);
  148. }
  149. void mysql_unlock_tables(THD *thd, MYSQL_LOCK *sql_lock)
  150. {
  151.   DBUG_ENTER("mysql_unlock_tables");
  152.   thr_multi_unlock(sql_lock->locks,sql_lock->lock_count);
  153.   VOID(unlock_external(thd,sql_lock->table,sql_lock->table_count));
  154.   my_free((gptr) sql_lock,MYF(0));
  155.   DBUG_VOID_RETURN;
  156. }
  157. /*
  158.   Unlock some of the tables locked by mysql_lock_tables
  159.   This will work even if get_lock_data fails (next unlock will free all)
  160.   */
  161. void mysql_unlock_some_tables(THD *thd, TABLE **table,uint count)
  162. {
  163.   MYSQL_LOCK *sql_lock;
  164.   TABLE *write_lock_used;
  165.   if ((sql_lock = get_lock_data(thd, table, count, 1, &write_lock_used)))
  166.     mysql_unlock_tables(thd, sql_lock);
  167. }
  168. /*
  169. ** unlock all tables locked for read.
  170. */
  171. void mysql_unlock_read_tables(THD *thd, MYSQL_LOCK *sql_lock)
  172. {
  173.   uint i,found;
  174.   DBUG_ENTER("mysql_unlock_read_tables");
  175.   /* Move all write locks first */
  176.   THR_LOCK_DATA **lock=sql_lock->locks;
  177.   for (i=found=0 ; i < sql_lock->lock_count ; i++)
  178.   {
  179.     if (sql_lock->locks[i]->type >= TL_WRITE_ALLOW_READ)
  180.     {
  181.       swap(THR_LOCK_DATA *,*lock,sql_lock->locks[i]);
  182.       lock++;
  183.       found++;
  184.     }
  185.   }
  186.   /* unlock the read locked tables */
  187.   if (i != found)
  188.   {
  189.     thr_multi_unlock(lock,i-found);
  190.     sql_lock->lock_count-=found;
  191.   }
  192.   /* Then to the same for the external locks */
  193.   /* Move all write locked tables first */
  194.   TABLE **table=sql_lock->table;
  195.   for (i=found=0 ; i < sql_lock->table_count ; i++)
  196.   {
  197.     if ((uint) sql_lock->table[i]->reginfo.lock_type >= TL_WRITE_ALLOW_READ)
  198.     {
  199.       swap(TABLE *,*table,sql_lock->table[i]);
  200.       table++;
  201.       found++;
  202.     }
  203.   }
  204.   /* Unlock all read locked tables */
  205.   if (i != found)
  206.   {
  207.     VOID(unlock_external(thd,table,i-found));
  208.     sql_lock->table_count-=found;
  209.   }
  210.   DBUG_VOID_RETURN;
  211. }
  212. void mysql_lock_remove(THD *thd, MYSQL_LOCK *locked,TABLE *table)
  213. {
  214.   mysql_unlock_some_tables(thd, &table,1);
  215.   if (locked)
  216.   {
  217.     reg1 uint i;
  218.     for (i=0; i < locked->table_count; i++)
  219.     {
  220.       if (locked->table[i] == table)
  221.       {
  222. locked->table_count--;
  223. bmove((char*) (locked->table+i),
  224.       (char*) (locked->table+i+1),
  225.       (locked->table_count-i)* sizeof(TABLE*));
  226. break;
  227.       }
  228.     }
  229.     THR_LOCK_DATA **prev=locked->locks;
  230.     for (i=0 ; i < locked->lock_count ; i++)
  231.     {
  232.       if (locked->locks[i]->type != TL_UNLOCK)
  233. *prev++ = locked->locks[i];
  234.     }
  235.     locked->lock_count=(uint) (prev - locked->locks);
  236.   }
  237. }
  238. /* abort all other threads waiting to get lock in table */
  239. void mysql_lock_abort(THD *thd, TABLE *table)
  240. {
  241.   MYSQL_LOCK *locked;
  242.   TABLE *write_lock_used;
  243.   if ((locked = get_lock_data(thd,&table,1,1,&write_lock_used)))
  244.   {
  245.     for (uint i=0; i < locked->lock_count; i++)
  246.       thr_abort_locks(locked->locks[i]->lock);
  247.     my_free((gptr) locked,MYF(0));
  248.   }
  249. }
  250. MYSQL_LOCK *mysql_lock_merge(MYSQL_LOCK *a,MYSQL_LOCK *b)
  251. {
  252.   MYSQL_LOCK *sql_lock;
  253.   DBUG_ENTER("mysql_lock_merge");
  254.   if (!(sql_lock= (MYSQL_LOCK*)
  255. my_malloc(sizeof(*sql_lock)+
  256.   sizeof(THR_LOCK_DATA*)*(a->lock_count+b->lock_count)+
  257.   sizeof(TABLE*)*(a->table_count+b->table_count),MYF(MY_WME))))
  258.     DBUG_RETURN(0); // Fatal error
  259.   sql_lock->lock_count=a->lock_count+b->lock_count;
  260.   sql_lock->table_count=a->table_count+b->table_count;
  261.   sql_lock->locks=(THR_LOCK_DATA**) (sql_lock+1);
  262.   sql_lock->table=(TABLE**) (sql_lock->locks+sql_lock->lock_count);
  263.   memcpy(sql_lock->locks,a->locks,a->lock_count*sizeof(*a->locks));
  264.   memcpy(sql_lock->locks+a->lock_count,b->locks,
  265.  b->lock_count*sizeof(*b->locks));
  266.   memcpy(sql_lock->table,a->table,a->table_count*sizeof(*a->table));
  267.   memcpy(sql_lock->table+a->table_count,b->table,
  268.  b->table_count*sizeof(*b->table));
  269.   my_free((gptr) a,MYF(0));
  270.   my_free((gptr) b,MYF(0));
  271.   DBUG_RETURN(sql_lock);
  272. }
  273. /* unlock a set of external */
  274. static int unlock_external(THD *thd, TABLE **table,uint count)
  275. {
  276.   int error,error_code;
  277.   DBUG_ENTER("unlock_external");
  278.   error_code=0;
  279.   for (; count-- ; table++)
  280.   {
  281.     if ((*table)->current_lock != F_UNLCK)
  282.     {
  283.       (*table)->current_lock = F_UNLCK;
  284.       if ((error=(*table)->file->external_lock(thd, F_UNLCK)))
  285. error_code=error;
  286.     }
  287.   }
  288.   if (error_code)
  289.     my_error(ER_CANT_LOCK,MYF(ME_BELL+ME_OLDWIN+ME_WAITTANG),error_code);
  290.   DBUG_RETURN(error_code);
  291. }
  292. /*
  293. ** Get lock structures from table structs and initialize locks
  294. */
  295. static MYSQL_LOCK *get_lock_data(THD *thd, TABLE **table_ptr, uint count,
  296.  bool get_old_locks, TABLE **write_lock_used)
  297. {
  298.   uint i,tables,lock_count;
  299.   MYSQL_LOCK *sql_lock;
  300.   THR_LOCK_DATA **locks;
  301.   TABLE **to;
  302.   *write_lock_used=0;
  303.   for (i=tables=lock_count=0 ; i < count ; i++)
  304.   {
  305.     if (!table_ptr[i]->tmp_table)
  306.     {
  307.       tables+=table_ptr[i]->file->lock_count();
  308.       lock_count++;
  309.     }
  310.   }
  311.   if (!(sql_lock= (MYSQL_LOCK*)
  312. my_malloc(sizeof(*sql_lock)+
  313.   sizeof(THR_LOCK_DATA*)*tables+sizeof(table_ptr)*lock_count,
  314.   MYF(0))))
  315.     return 0;
  316.   locks=sql_lock->locks=(THR_LOCK_DATA**) (sql_lock+1);
  317.   to=sql_lock->table=(TABLE**) (locks+tables);
  318.   sql_lock->table_count=lock_count;
  319.   sql_lock->lock_count=tables;
  320.   for (i=0 ; i < count ; i++)
  321.   {
  322.     TABLE *table;
  323.     if ((table=table_ptr[i])->tmp_table)
  324.       continue;
  325.     *to++=table;
  326.     enum thr_lock_type lock_type= table->reginfo.lock_type;
  327.     if (lock_type >= TL_WRITE_ALLOW_WRITE)
  328.     {
  329.       *write_lock_used=table;
  330.       if (table->db_stat & HA_READ_ONLY)
  331.       {
  332. my_error(ER_OPEN_AS_READONLY,MYF(0),table->table_name);
  333. my_free((gptr) sql_lock,MYF(0));
  334. return 0;
  335.       }
  336.     }
  337.     locks=table->file->store_lock(thd, locks, get_old_locks ? TL_IGNORE :
  338.   lock_type);
  339.   }
  340.   return sql_lock;
  341. }
  342. /*****************************************************************************
  343. **  Lock table based on the name.
  344. **  This is used when we need total access to a closed, not open table
  345. *****************************************************************************/
  346. /*
  347.   Put a not open table with an old refresh version in the table cache.
  348.   This will force any other threads that uses the table to release it
  349.   as soon as possible.
  350.   One must have a lock on LOCK_open !
  351.   Return values:
  352.    < 0 error
  353.    == 0 table locked
  354.    > 0  table locked, but someone is using it
  355. */
  356. int lock_table_name(THD *thd, TABLE_LIST *table_list)
  357. {
  358.   TABLE *table;
  359.   char  key[MAX_DBKEY_LENGTH];
  360.   uint  key_length;
  361.   DBUG_ENTER("lock_table_name");
  362.   key_length=(uint) (strmov(strmov(key,table_list->db)+1,table_list->name)
  363.      -key)+ 1;
  364.   /* Only insert the table if we haven't insert it already */
  365.   for (table=(TABLE*) hash_search(&open_cache,(byte*) key,key_length) ;
  366.        table ;
  367.        table = (TABLE*) hash_next(&open_cache,(byte*) key,key_length))
  368.     if (table->in_use == thd)
  369.       DBUG_RETURN(0);
  370.   /* Create a table entry with the right key and with an old refresh version */
  371.   /* Note that we must use my_malloc() here as this is freed by the table
  372.      cache */
  373.   if (!(table= (TABLE*) my_malloc(sizeof(*table)+key_length,
  374.   MYF(MY_WME | MY_ZEROFILL))))
  375.     DBUG_RETURN(-1);
  376.   memcpy((table->table_cache_key= (char*) (table+1)), key, key_length);
  377.   table->key_length=key_length;
  378.   table->in_use=thd;
  379.   table->locked_by_name=1;
  380.   table_list->table=table;
  381.   if (hash_insert(&open_cache, (byte*) table))
  382.   {
  383.     my_free((gptr) table,MYF(0));
  384.     DBUG_RETURN(-1);
  385.   }
  386.   if (remove_table_from_cache(thd, table_list->db, table_list->name))
  387.     DBUG_RETURN(1); // Table is in use
  388.   DBUG_RETURN(0);
  389. }
  390. void unlock_table_name(THD *thd, TABLE_LIST *table_list)
  391. {
  392.   if (table_list->table)
  393.   {
  394.     hash_delete(&open_cache, (byte*) table_list->table);
  395.     (void) pthread_cond_broadcast(&COND_refresh);
  396.   }
  397. }
  398. static bool locked_named_table(THD *thd, TABLE_LIST *table_list)
  399. {
  400.   for ( ; table_list ; table_list=table_list->next)
  401.   {
  402.     if (table_list->table && table_is_used(table_list->table,0))
  403.       return 1;
  404.   }
  405.   return 0; // All tables are locked
  406. }
  407. bool wait_for_locked_table_names(THD *thd, TABLE_LIST *table_list)
  408. {
  409.   bool result=0;
  410.   DBUG_ENTER("wait_for_locked_table_names");
  411.   while (locked_named_table(thd,table_list))
  412.   {
  413.     if (thd->killed)
  414.     {
  415.       result=1;
  416.       break;
  417.     }
  418.     wait_for_refresh(thd);
  419.     pthread_mutex_lock(&LOCK_open);
  420.   }
  421.   DBUG_RETURN(result);
  422. }