diff options
author | unknown <ingo@mysql.com> | 2005-04-27 23:00:17 +0200 |
---|---|---|
committer | unknown <ingo@mysql.com> | 2005-04-27 23:00:17 +0200 |
commit | 92db5489ff44c1b040efeb001beb65f4c861fb84 (patch) | |
tree | 24ff5ea84a7e3470f444cbeb965f30fccaf0d84d /sql | |
parent | a1b0139fe7bfcd65312b07d267d40761eac1ca67 (diff) | |
parent | 2cd93843bfbbf5e437f89ca76069a37393b712ed (diff) | |
download | mariadb-git-92db5489ff44c1b040efeb001beb65f4c861fb84.tar.gz |
Merge mysql.com:/home/mydev/mysql-5.0
into mysql.com:/home/mydev/mysql-5.0-5000
configure.in:
Auto merged
Diffstat (limited to 'sql')
-rw-r--r-- | sql/lock.cc | 51 | ||||
-rw-r--r-- | sql/mysql_priv.h | 5 | ||||
-rw-r--r-- | sql/sql_insert.cc | 75 |
3 files changed, 108 insertions, 23 deletions
diff --git a/sql/lock.cc b/sql/lock.cc index a8ccba32d4f..83a39005cd8 100644 --- a/sql/lock.cc +++ b/sql/lock.cc @@ -82,7 +82,8 @@ static int unlock_external(THD *thd, TABLE **table,uint count); static void print_lock_error(int error, const char *); -MYSQL_LOCK *mysql_lock_tables(THD *thd,TABLE **tables,uint count) +MYSQL_LOCK *mysql_lock_tables(THD *thd, TABLE **tables, uint count, + bool ignore_global_read_lock) { MYSQL_LOCK *sql_lock; TABLE *write_lock_used; @@ -93,7 +94,7 @@ MYSQL_LOCK *mysql_lock_tables(THD *thd,TABLE **tables,uint count) if (!(sql_lock = get_lock_data(thd,tables,count, 0,&write_lock_used))) break; - if (global_read_lock && write_lock_used) + if (global_read_lock && write_lock_used && ! ignore_global_read_lock) { /* Someone has issued LOCK ALL TABLES FOR READ and we want a write lock @@ -949,3 +950,49 @@ bool make_global_read_lock_block_commit(THD *thd) DBUG_RETURN(error); } + + +/* + Set protection against global read lock. + + SYNOPSIS + set_protect_against_global_read_lock() + void + + RETURN + FALSE OK, no global read lock exists. + TRUE Error, global read lock exists already. +*/ + +bool set_protect_against_global_read_lock(void) +{ + bool global_read_lock_exists; + + pthread_mutex_lock(&LOCK_open); + if (! (global_read_lock_exists= test(global_read_lock))) + protect_against_global_read_lock++; + pthread_mutex_unlock(&LOCK_open); + return global_read_lock_exists; +} + + +/* + Unset protection against global read lock. + + SYNOPSIS + unset_protect_against_global_read_lock() + void + + RETURN + void +*/ + +void unset_protect_against_global_read_lock(void) +{ + pthread_mutex_lock(&LOCK_open); + protect_against_global_read_lock--; + pthread_mutex_unlock(&LOCK_open); + pthread_cond_broadcast(&COND_refresh); +} + + diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index 56fbd993aed..06c946114eb 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -1151,7 +1151,8 @@ extern pthread_t signal_thread; extern struct st_VioSSLAcceptorFd * ssl_acceptor_fd; #endif /* HAVE_OPENSSL */ -MYSQL_LOCK *mysql_lock_tables(THD *thd,TABLE **table,uint count); +MYSQL_LOCK *mysql_lock_tables(THD *thd, TABLE **table, uint count, + bool ignore_global_read_lock= FALSE); void mysql_unlock_tables(THD *thd, MYSQL_LOCK *sql_lock); void mysql_unlock_read_tables(THD *thd, MYSQL_LOCK *sql_lock); void mysql_unlock_some_tables(THD *thd, TABLE **table,uint count); @@ -1165,6 +1166,8 @@ bool wait_if_global_read_lock(THD *thd, bool abort_on_refresh, bool is_not_commit); void start_waiting_global_read_lock(THD *thd); bool make_global_read_lock_block_commit(THD *thd); +bool set_protect_against_global_read_lock(void); +void unset_protect_against_global_read_lock(void); /* Lock based on name */ int lock_and_wait_for_table_name(THD *thd, TABLE_LIST *table_list); diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index 9a7a3d64de5..ac8f5a06745 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -1117,27 +1117,42 @@ static TABLE *delayed_get_table(THD *thd,TABLE_LIST *table_list) { int error; delayed_insert *tmp; + TABLE *table; DBUG_ENTER("delayed_get_table"); if (!table_list->db) table_list->db=thd->db; - /* no match; create a new thread to handle the table */ + /* Find the thread which handles this table. */ if (!(tmp=find_handler(thd,table_list))) { - /* Don't create more than max_insert_delayed_threads */ + /* + No match. Create a new thread to handle the table, but + no more than max_insert_delayed_threads. + */ if (delayed_insert_threads >= thd->variables.max_insert_delayed_threads) DBUG_RETURN(0); thd->proc_info="Creating delayed handler"; pthread_mutex_lock(&LOCK_delayed_create); - if (!(tmp=find_handler(thd,table_list))) // Was just created + /* + The first search above was done without LOCK_delayed_create. + Another thread might have created the handler in between. Search again. + */ + if (! (tmp= find_handler(thd, table_list))) { + /* + Avoid that a global read lock steps in while we are creating the + new thread. It would block trying to open the table. Hence, the + DI thread and this thread would wait until after the global + readlock is gone. If the read lock exists already, we leave with + no table and then switch to non-delayed insert. + */ + if (set_protect_against_global_read_lock()) + goto err; if (!(tmp=new delayed_insert())) { - thd->fatal_error(); my_error(ER_OUTOFMEMORY,MYF(0),sizeof(delayed_insert)); - pthread_mutex_unlock(&LOCK_delayed_create); - DBUG_RETURN(0); + goto err1; } pthread_mutex_lock(&LOCK_thread_count); thread_count++; @@ -1146,10 +1161,8 @@ static TABLE *delayed_get_table(THD *thd,TABLE_LIST *table_list) !(tmp->thd.query=my_strdup(table_list->table_name,MYF(MY_WME)))) { delete tmp; - thd->fatal_error(); my_message(ER_OUT_OF_RESOURCES, ER(ER_OUT_OF_RESOURCES), MYF(0)); - pthread_mutex_unlock(&LOCK_delayed_create); - DBUG_RETURN(0); + goto err1; } tmp->table_list= *table_list; // Needed to open table tmp->table_list.db= tmp->thd.db; @@ -1165,10 +1178,8 @@ static TABLE *delayed_get_table(THD *thd,TABLE_LIST *table_list) pthread_mutex_unlock(&tmp->mutex); tmp->unlock(); delete tmp; - thd->fatal_error(); - pthread_mutex_unlock(&LOCK_delayed_create); my_error(ER_CANT_CREATE_THREAD, MYF(0), error); - DBUG_RETURN(0); + goto err1; } /* Wait until table is open */ @@ -1178,6 +1189,7 @@ static TABLE *delayed_get_table(THD *thd,TABLE_LIST *table_list) pthread_cond_wait(&tmp->cond_client,&tmp->mutex); } pthread_mutex_unlock(&tmp->mutex); + unset_protect_against_global_read_lock(); thd->proc_info="got old table"; if (tmp->thd.killed) { @@ -1189,28 +1201,34 @@ static TABLE *delayed_get_table(THD *thd,TABLE_LIST *table_list) thd->net.last_errno=tmp->thd.net.last_errno; } tmp->unlock(); - pthread_mutex_unlock(&LOCK_delayed_create); - DBUG_RETURN(0); // Continue with normal insert + goto err; } if (thd->killed) { tmp->unlock(); - pthread_mutex_unlock(&LOCK_delayed_create); - DBUG_RETURN(0); + goto err; } } pthread_mutex_unlock(&LOCK_delayed_create); } pthread_mutex_lock(&tmp->mutex); - TABLE *table=tmp->get_local_table(thd); + table= tmp->get_local_table(thd); pthread_mutex_unlock(&tmp->mutex); - tmp->unlock(); if (table) thd->di=tmp; else if (tmp->thd.is_fatal_error) thd->fatal_error(); + /* Unlock the delayed insert object after its last access. */ + tmp->unlock(); DBUG_RETURN((table_list->table=table)); + + err1: + thd->fatal_error(); + unset_protect_against_global_read_lock(); + err: + pthread_mutex_unlock(&LOCK_delayed_create); + DBUG_RETURN(0); // Continue with normal insert } @@ -1433,6 +1451,14 @@ extern "C" pthread_handler_decl(handle_delayed_insert,arg) thd->killed=abort_loop ? THD::KILL_CONNECTION : THD::NOT_KILLED; pthread_mutex_unlock(&LOCK_thread_count); + /* + Wait until the client runs into pthread_cond_wait(), + where we free it after the table is opened and di linked in the list. + If we did not wait here, the client might detect the opened table + before it is linked to the list. It would release LOCK_delayed_create + and allow another thread to create another handler for the same table, + since it does not find one in the list. + */ pthread_mutex_lock(&di->mutex); #if !defined( __WIN__) && !defined(OS2) /* Win32 calls this in pthread_create */ if (my_thread_init()) @@ -1547,8 +1573,17 @@ extern "C" pthread_handler_decl(handle_delayed_insert,arg) if (di->tables_in_use && ! thd->lock) { - /* request for new delayed insert */ - if (!(thd->lock=mysql_lock_tables(thd,&di->table,1))) + /* + Request for new delayed insert. + Lock the table, but avoid to be blocked by a global read lock. + If we got here while a global read lock exists, then one or more + inserts started before the lock was requested. These are allowed + to complete their work before the server returns control to the + client which requested the global read lock. The delayed insert + handler will close the table and finish when the outstanding + inserts are done. + */ + if (! (thd->lock= mysql_lock_tables(thd, &di->table, 1, TRUE))) { /* Fatal error */ di->dead= 1; |