summaryrefslogtreecommitdiff
path: root/sql
diff options
context:
space:
mode:
authorunknown <ingo@mysql.com>2005-04-27 23:00:17 +0200
committerunknown <ingo@mysql.com>2005-04-27 23:00:17 +0200
commit92db5489ff44c1b040efeb001beb65f4c861fb84 (patch)
tree24ff5ea84a7e3470f444cbeb965f30fccaf0d84d /sql
parenta1b0139fe7bfcd65312b07d267d40761eac1ca67 (diff)
parent2cd93843bfbbf5e437f89ca76069a37393b712ed (diff)
downloadmariadb-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.cc51
-rw-r--r--sql/mysql_priv.h5
-rw-r--r--sql/sql_insert.cc75
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;