1
/* Copyright (C) 2000-2006 MySQL AB
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; version 2 of the License.
7
This program is distributed in the hope that it will be useful,
8
but WITHOUT ANY WARRANTY; without even the implied warranty of
9
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10
GNU General Public License for more details.
12
You should have received a copy of the GNU General Public License
13
along with this program; if not, write to the Free Software
14
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
20
Locking functions for mysql.
22
Because of the new concurrent inserts, we must first get external locks
23
before getting internal locks. If we do it in the other order, the status
24
information is not up to date when called from the lock handler.
26
GENERAL DESCRIPTION OF LOCKING
28
When not using LOCK TABLES:
30
- For each SQL statement mysql_lock_tables() is called for all involved
32
- mysql_lock_tables() will call
33
table_handler->external_lock(session,locktype) for each table.
34
This is followed by a call to thr_multi_lock() for all tables.
36
- When statement is done, we call mysql_unlock_tables().
37
This will call thr_multi_unlock() followed by
38
table_handler->external_lock(session, F_UNLCK) for each table.
40
- Note that mysql_unlock_tables() may be called several times as
41
MySQL in some cases can free some tables earlier than others.
43
- The above is true both for normal and temporary tables.
45
- Temporary non transactional tables are never passed to thr_multi_lock()
46
and we never call external_lock(session, F_UNLOCK) on these.
48
When using LOCK TABLES:
50
- LOCK Table will call mysql_lock_tables() for all tables.
51
mysql_lock_tables() will call
52
table_handler->external_lock(session,locktype) for each table.
53
This is followed by a call to thr_multi_lock() for all tables.
55
- For each statement, we will call table_handler->start_stmt(Session)
56
to inform the table handler that we are using the table.
58
The tables used can only be tables used in LOCK TABLES or a
61
- When statement is done, we will call ha_commit_stmt(session);
63
- When calling UNLOCK TABLES we call mysql_unlock_tables() for all
64
tables used in LOCK TABLES
66
If table_handler->external_lock(session, locktype) fails, we call
67
table_handler->external_lock(session, F_UNLCK) for each table that was locked,
68
excluding one that caused failure. That means handler must cleanup itself
69
in case external_lock() fails.
72
Change to use malloc() ONLY when using LOCK TABLES command or when
73
we are forced to use mysql_lock_merge.
77
#include <drizzled/error.h>
78
#include <drizzled/my_hash.h>
79
#include <drizzled/thr_lock.h>
80
#include <drizzled/session.h>
81
#include <drizzled/sql_base.h>
82
#include <drizzled/lock.h>
83
#include "drizzled/pthread_globals.h"
84
#include "drizzled/internal/my_sys.h"
97
@defgroup Locking Locking
101
extern HASH open_cache;
103
static DRIZZLE_LOCK *get_lock_data(Session *session, Table **table,
105
bool should_lock, Table **write_locked);
106
static int lock_external(Session *session, Table **table,uint32_t count);
107
static int unlock_external(Session *session, Table **table,uint32_t count);
108
static void print_lock_error(int error, const char *);
115
session The current thread.
116
tables An array of pointers to the tables to lock.
117
count The number of tables to lock.
119
DRIZZLE_LOCK_IGNORE_GLOBAL_READ_LOCK Ignore a global read lock
120
DRIZZLE_LOCK_IGNORE_FLUSH Ignore a flush tables.
121
DRIZZLE_LOCK_NOTIFY_IF_NEED_REOPEN Instead of reopening altered
122
or dropped tables by itself,
123
mysql_lock_tables() should
124
notify upper level and rely
125
on caller doing this.
126
need_reopen Out parameter, TRUE if some tables were altered
127
or deleted and should be reopened by caller.
130
A lock structure pointer on success.
131
NULL on error or if some tables should be reopen.
134
/* Map the return value of thr_lock to an error from errmsg.txt */
135
static int thr_lock_errno_to_mysql[]=
136
{ 0, 1, ER_LOCK_WAIT_TIMEOUT, ER_LOCK_DEADLOCK };
140
Reset lock type in lock data and free.
142
@param mysql_lock Lock structures to reset.
144
@note After a locking error we want to quit the locking of the table(s).
145
The test case in the bug report for Bug #18544 has the following
146
cases: 1. Locking error in lock_external() due to InnoDB timeout.
147
2. Locking error in get_lock_data() due to missing write permission.
148
3. Locking error in wait_if_global_read_lock() due to lock conflict.
150
@note In all these cases we have already set the lock type into the lock
151
data of the open table(s). If the table(s) are in the open table
152
cache, they could be reused with the non-zero lock type set. This
153
could lead to ignoring a different lock type with the next lock.
155
@note Clear the lock type of all lock data. This ensures that the next
156
lock request will set its lock type properly.
159
static void reset_lock_data_and_free(DRIZZLE_LOCK **mysql_lock)
161
DRIZZLE_LOCK *sql_lock= *mysql_lock;
162
THR_LOCK_DATA **ldata, **ldata_end;
164
/* Clear the lock type of all lock data to avoid reusage. */
165
for (ldata= sql_lock->locks, ldata_end= ldata + sql_lock->lock_count;
169
/* Reset lock type. */
170
(*ldata)->type= TL_UNLOCK;
172
free((unsigned char*) sql_lock);
177
DRIZZLE_LOCK *mysql_lock_tables(Session *session, Table **tables, uint32_t count,
178
uint32_t flags, bool *need_reopen)
180
DRIZZLE_LOCK *sql_lock;
181
Table *write_lock_used;
182
vector<plugin::StorageEngine *> involved_engines;
189
if (! (sql_lock= get_lock_data(session, tables, count, true,
193
if (global_read_lock && write_lock_used &&
194
! (flags & DRIZZLE_LOCK_IGNORE_GLOBAL_READ_LOCK))
197
Someone has issued LOCK ALL TABLES FOR READ and we want a write lock
198
Wait until the lock is gone
200
if (wait_if_global_read_lock(session, 1, 1))
202
/* Clear the lock type of all lock data to avoid reusage. */
203
reset_lock_data_and_free(&sql_lock);
206
if (session->version != refresh_version)
208
/* Clear the lock type of all lock data to avoid reusage. */
209
reset_lock_data_and_free(&sql_lock);
214
session->set_proc_info("Notify start statement");
216
* Here, we advise all storage engines involved in the
217
* statement that we are starting a new statement
219
if (sql_lock->table_count)
221
size_t num_tables= sql_lock->table_count;
222
plugin::StorageEngine *engine;
223
set<size_t> involved_slots;
224
for (size_t x= 1; x <= num_tables; x++, tables++)
226
engine= (*tables)->cursor->engine;
227
if (involved_slots.count(engine->getId()) > 0)
228
continue; /* already added to involved engines */
229
involved_engines.push_back(engine);
230
involved_slots.insert(engine->getId());
233
for_each(involved_engines.begin(),
234
involved_engines.end(),
235
bind2nd(mem_fun(&plugin::StorageEngine::startStatement), session));
238
session->set_proc_info("External lock");
240
* Here, the call to lock_external() informs the
241
* all engines for all tables used in this statement
242
* of the type of lock that Drizzle intends to take on a
245
if (sql_lock->table_count && lock_external(session, sql_lock->table,
246
sql_lock->table_count))
248
/* Clear the lock type of all lock data to avoid reusage. */
249
reset_lock_data_and_free(&sql_lock);
252
session->set_proc_info("Table lock");
253
/* Copy the lock data array. thr_multi_lock() reorders its contens. */
254
memcpy(sql_lock->locks + sql_lock->lock_count, sql_lock->locks,
255
sql_lock->lock_count * sizeof(*sql_lock->locks));
256
/* Lock on the copied half of the lock data array. */
257
rc= thr_lock_errno_to_mysql[(int) thr_multi_lock(sql_lock->locks +
258
sql_lock->lock_count,
259
sql_lock->lock_count,
261
if (rc > 1) /* a timeout or a deadlock */
263
if (sql_lock->table_count)
264
unlock_external(session, sql_lock->table, sql_lock->table_count);
265
reset_lock_data_and_free(&sql_lock);
266
my_error(rc, MYF(0));
269
else if (rc == 1) /* aborted */
271
session->some_tables_deleted=1; // Try again
272
sql_lock->lock_count= 0; // Locks are already freed
273
// Fall through: unlock, reset lock data, free and retry
275
else if (!session->some_tables_deleted || (flags & DRIZZLE_LOCK_IGNORE_FLUSH))
278
Thread was killed or lock aborted. Let upper level close all
279
used tables and retry or give error.
283
else if (!session->open_tables)
285
// Only using temporary tables, no need to unlock
286
session->some_tables_deleted= 0;
289
session->set_proc_info(0);
291
/* going to retry, unlock all tables */
292
if (sql_lock->lock_count)
293
thr_multi_unlock(sql_lock->locks, sql_lock->lock_count);
295
if (sql_lock->table_count)
296
unlock_external(session, sql_lock->table, sql_lock->table_count);
299
If thr_multi_lock fails it resets lock type for tables, which
300
were locked before (and including) one that caused error. Lock
301
type for other tables preserved.
303
reset_lock_data_and_free(&sql_lock);
306
* Notify all involved engines that the
307
* SQL statement has ended
309
for_each(involved_engines.begin(),
310
involved_engines.end(),
311
bind2nd(mem_fun(&plugin::StorageEngine::endStatement), session));
313
if (flags & DRIZZLE_LOCK_NOTIFY_IF_NEED_REOPEN)
318
if (wait_for_tables(session))
319
break; // Couldn't open tables
321
session->set_proc_info(0);
324
session->send_kill_message();
327
mysql_unlock_tables(session,sql_lock);
331
session->set_time_after_lock();
336
static int lock_external(Session *session, Table **tables, uint32_t count)
340
for (i=1 ; i <= count ; i++, tables++)
342
assert((*tables)->reginfo.lock_type >= TL_READ);
343
lock_type=F_WRLCK; /* Lock exclusive */
344
if ((*tables)->db_stat & HA_READ_ONLY ||
345
((*tables)->reginfo.lock_type >= TL_READ &&
346
(*tables)->reginfo.lock_type <= TL_READ_NO_INSERT))
349
if ((error=(*tables)->cursor->ha_external_lock(session,lock_type)))
351
print_lock_error(error, (*tables)->cursor->engine->getName().c_str());
355
(*tables)->cursor->ha_external_lock(session, F_UNLCK);
356
(*tables)->current_lock=F_UNLCK;
362
(*tables)->db_stat &= ~ HA_BLOCK_LOCK;
363
(*tables)->current_lock= lock_type;
370
void mysql_unlock_tables(Session *session, DRIZZLE_LOCK *sql_lock)
372
if (sql_lock->lock_count)
373
thr_multi_unlock(sql_lock->locks,sql_lock->lock_count);
374
if (sql_lock->table_count)
375
unlock_external(session,sql_lock->table,sql_lock->table_count);
376
free((unsigned char*) sql_lock);
381
Unlock some of the tables locked by mysql_lock_tables.
383
This will work even if get_lock_data fails (next unlock will free all)
386
void mysql_unlock_some_tables(Session *session, Table **table, uint32_t count)
388
DRIZZLE_LOCK *sql_lock;
389
Table *write_lock_used;
390
if ((sql_lock= get_lock_data(session, table, count, false,
392
mysql_unlock_tables(session, sql_lock);
397
unlock all tables locked for read.
400
void mysql_unlock_read_tables(Session *session, DRIZZLE_LOCK *sql_lock)
404
/* Move all write locks first */
405
THR_LOCK_DATA **lock=sql_lock->locks;
406
for (i=found=0 ; i < sql_lock->lock_count ; i++)
408
if (sql_lock->locks[i]->type >= TL_WRITE_ALLOW_READ)
410
std::swap(*lock, sql_lock->locks[i]);
415
/* unlock the read locked tables */
418
thr_multi_unlock(lock,i-found);
419
sql_lock->lock_count= found;
422
/* Then do the same for the external locks */
423
/* Move all write locked tables first */
424
Table **table=sql_lock->table;
425
for (i=found=0 ; i < sql_lock->table_count ; i++)
427
assert(sql_lock->table[i]->lock_position == i);
428
if ((uint32_t) sql_lock->table[i]->reginfo.lock_type >= TL_WRITE_ALLOW_READ)
430
std::swap(*table, sql_lock->table[i]);
435
/* Unlock all read locked tables */
438
unlock_external(session,table,i-found);
439
sql_lock->table_count=found;
441
/* Fix the lock positions in Table */
442
table= sql_lock->table;
444
for (i= 0; i < sql_lock->table_count; i++)
447
tbl->lock_position= table - sql_lock->table;
448
tbl->lock_data_start= found;
449
found+= tbl->lock_count;
457
Try to find the table in the list of locked tables.
458
In case of success, unlock the table and remove it from this list.
460
@note This function has a legacy side effect: the table is
461
unlocked even if it is not found in the locked list.
462
It's not clear if this side effect is intentional or still
463
desirable. It might lead to unmatched calls to
464
unlock_external(). Moreover, a discrepancy can be left
465
unnoticed by the storage engine, because in
466
unlock_external() we call handler::external_lock(F_UNLCK) only
467
if table->current_lock is not F_UNLCK.
469
@param session thread context
470
@param locked list of locked tables
471
@param table the table to unlock
472
@param always_unlock specify explicitly if the legacy side
476
void mysql_lock_remove(Session *session, Table *table)
478
mysql_unlock_some_tables(session, &table, /* table count */ 1);
482
/** Abort all other threads waiting to get lock in table. */
484
void mysql_lock_abort(Session *session, Table *table)
486
DRIZZLE_LOCK *locked;
487
Table *write_lock_used;
489
if ((locked= get_lock_data(session, &table, 1, false,
492
for (uint32_t x= 0; x < locked->lock_count; x++)
493
thr_abort_locks(locked->locks[x]->lock);
494
free((unsigned char*) locked);
500
Abort one thread / table combination.
502
@param session Thread handler
503
@param table Table that should be removed from lock queue
506
0 Table was not locked by another thread
508
1 Table was locked by at least one other thread
511
bool mysql_lock_abort_for_thread(Session *session, Table *table)
513
DRIZZLE_LOCK *locked;
514
Table *write_lock_used;
517
if ((locked= get_lock_data(session, &table, 1, false,
520
for (uint32_t i=0; i < locked->lock_count; i++)
522
if (thr_abort_locks_for_thread(locked->locks[i]->lock,
523
table->in_use->thread_id))
526
free((unsigned char*) locked);
531
/** Unlock a set of external. */
533
static int unlock_external(Session *session, Table **table,uint32_t count)
535
int error,error_code;
540
if ((*table)->current_lock != F_UNLCK)
542
(*table)->current_lock = F_UNLCK;
543
if ((error=(*table)->cursor->ha_external_lock(session, F_UNLCK)))
546
print_lock_error(error_code, (*table)->cursor->engine->getName().c_str());
556
Get lock structures from table structs and initialize locks.
558
@param session Thread handler
559
@param table_ptr Pointer to tables that should be locks
560
@param should_lock One of:
561
- false : If we should send TL_IGNORE to store lock
562
- true : Store lock info in Table
563
@param write_lock_used Store pointer to last table with WRITE_ALLOW_WRITE
566
static DRIZZLE_LOCK *get_lock_data(Session *session, Table **table_ptr, uint32_t count,
567
bool should_lock, Table **write_lock_used)
569
uint32_t i,tables,lock_count;
570
DRIZZLE_LOCK *sql_lock;
571
THR_LOCK_DATA **locks, **locks_buf, **locks_start;
572
Table **to, **table_buf;
575
for (i= tables= lock_count= 0 ; i < count ; i++)
577
Table *t= table_ptr[i];
579
if (! (t->getEngine()->check_flag(HTON_BIT_SKIP_STORE_LOCK)))
587
Allocating twice the number of pointers for lock data for use in
588
thr_mulit_lock(). This function reorders the lock data, but cannot
589
update the table values. So the second part of the array is copied
590
from the first part immediately before calling thr_multi_lock().
592
if (!(sql_lock= (DRIZZLE_LOCK*)
593
malloc(sizeof(*sql_lock) +
594
sizeof(THR_LOCK_DATA*) * tables * 2 +
595
sizeof(table_ptr) * lock_count)))
597
locks= locks_buf= sql_lock->locks= (THR_LOCK_DATA**) (sql_lock + 1);
598
to= table_buf= sql_lock->table= (Table**) (locks + tables * 2);
599
sql_lock->table_count= lock_count;
601
for (i=0 ; i < count ; i++)
604
enum thr_lock_type lock_type;
606
if (table_ptr[i]->getEngine()->check_flag(HTON_BIT_SKIP_STORE_LOCK))
610
lock_type= table->reginfo.lock_type;
611
assert (lock_type != TL_WRITE_DEFAULT);
612
if (lock_type >= TL_WRITE_ALLOW_WRITE)
614
*write_lock_used=table;
615
if (table->db_stat & HA_READ_ONLY)
617
my_error(ER_OPEN_AS_READONLY,MYF(0),table->alias);
618
/* Clear the lock type of the lock data that are stored already. */
619
sql_lock->lock_count= locks - sql_lock->locks;
620
reset_lock_data_and_free(&sql_lock);
625
locks= table->cursor->store_lock(session, locks,
626
should_lock == false ? TL_IGNORE : lock_type);
629
table->lock_position= (uint32_t) (to - table_buf);
630
table->lock_data_start= (uint32_t) (locks_start - locks_buf);
631
table->lock_count= (uint32_t) (locks - locks_start);
632
assert(table->lock_count == 1);
637
We do not use 'tables', because there are cases where store_lock()
638
returns less locks than lock_count() claimed. This can happen when
639
a FLUSH TABLES tries to abort locks from a MERGE table of another
640
thread. When that thread has just opened the table, but not yet
641
attached its children, it cannot return the locks. lock_count()
642
always returns the number of locks that an attached table has.
643
This is done to avoid the reverse situation: If lock_count() would
644
return 0 for a non-attached MERGE table, and that table becomes
645
attached between the calls to lock_count() and store_lock(), then
646
we would have allocated too little memory for the lock data. Now
647
we may allocate too much, but better safe than memory overrun.
648
And in the FLUSH case, the memory is released quickly anyway.
650
sql_lock->lock_count= locks - locks_buf;
657
Put a not open table with an old refresh version in the table cache.
659
@param session Thread handler
660
@param table_list Lock first table in this list
661
@param check_in_use Do we need to check if table already in use by us
664
One must have a lock on LOCK_open!
667
If you are going to update the table, you should use
668
lock_and_wait_for_table_name(removed) instead of this function as this works
669
together with 'FLUSH TABLES WITH READ LOCK'
672
This will force any other threads that uses the table to release it
680
> 0 table locked, but someone is using it
683
int lock_table_name(Session *session, TableList *table_list, bool check_in_use)
686
char key[MAX_DBKEY_LENGTH];
687
char *db= table_list->db;
689
bool found_locked_table= false;
690
HASH_SEARCH_STATE state;
692
key_length= table_list->create_table_def_key(key);
696
/* Only insert the table if we haven't insert it already */
697
for (table=(Table*) hash_first(&open_cache, (unsigned char*)key,
700
table = (Table*) hash_next(&open_cache,(unsigned char*) key,
703
if (table->reginfo.lock_type < TL_WRITE)
705
if (table->in_use == session)
706
found_locked_table= true;
710
if (table->in_use == session)
712
table->s->version= 0; // Ensure no one can use this
713
table->locked_by_name= 1;
719
if (!(table= session->table_cache_insert_placeholder(key, key_length)))
722
table_list->table=table;
724
/* Return 1 if table is in use */
725
return(test(remove_table_from_cache(session, db, table_list->table_name,
726
check_in_use ? RTFC_NO_FLAG : RTFC_WAIT_OTHER_THREAD_FLAG)));
730
void unlock_table_name(TableList *table_list)
732
if (table_list->table)
734
hash_delete(&open_cache, (unsigned char*) table_list->table);
740
static bool locked_named_table(TableList *table_list)
742
for (; table_list ; table_list=table_list->next_local)
744
Table *table= table_list->table;
747
Table *save_next= table->next;
750
result= table_is_used(table_list->table, 0);
751
table->next= save_next;
756
return 0; // All tables are locked
760
bool wait_for_locked_table_names(Session *session, TableList *table_list)
764
safe_mutex_assert_owner(&LOCK_open);
766
while (locked_named_table(table_list))
773
session->wait_for_condition(&LOCK_open, &COND_refresh);
774
pthread_mutex_lock(&LOCK_open); /* Wait for a table to unlock and then lock it */
781
Lock all tables in list with a name lock.
784
- One must have a lock on LOCK_open when calling this
786
@param table_list Names of tables to lock
791
1 Fatal error (end of memory ?)
794
bool lock_table_names(Session *session, TableList *table_list)
796
bool got_all_locks=1;
797
TableList *lock_table;
799
for (lock_table= table_list; lock_table; lock_table= lock_table->next_local)
802
if ((got_lock= lock_table_name(session,lock_table, true)) < 0)
803
goto end; // Fatal error
805
got_all_locks=0; // Someone is using table
808
/* If some table was in use, wait until we got the lock */
809
if (!got_all_locks && wait_for_locked_table_names(session, table_list))
814
unlock_table_names(table_list, lock_table);
821
Unlock all tables in list with a name lock.
823
@param session Thread handle.
824
@param table_list Names of tables to lock.
827
This function needs to be protected by LOCK_open. If we're
828
under LOCK TABLES, this function does not work as advertised. Namely,
829
it does not exclude other threads from using this table and does not
830
put an exclusive name lock on this table into the table cache.
832
@see lock_table_names
833
@see unlock_table_names
835
@retval TRUE An error occured.
836
@retval FALSE Name lock successfully acquired.
839
bool lock_table_names_exclusively(Session *session, TableList *table_list)
841
if (lock_table_names(session, table_list))
845
Upgrade the table name locks from semi-exclusive to exclusive locks.
847
for (TableList *table= table_list; table; table= table->next_global)
850
table->table->open_placeholder= 1;
857
Unlock all tables in list with a name lock.
860
table_list Names of tables to unlock
862
last_table Don't unlock any tables after this one.
863
(default 0, which will unlock all tables)
866
One must have a lock on LOCK_open when calling this.
869
This function will broadcast refresh signals to inform other threads
870
that the name locks are removed.
875
1 Fatal error (end of memory ?)
878
void unlock_table_names(TableList *table_list, TableList *last_table)
880
for (TableList *table= table_list;
882
table= table->next_local)
883
unlock_table_name(table);
888
static void print_lock_error(int error, const char *table)
893
case HA_ERR_LOCK_WAIT_TIMEOUT:
894
textno=ER_LOCK_WAIT_TIMEOUT;
896
case HA_ERR_READ_ONLY_TRANSACTION:
897
textno=ER_READ_ONLY_TRANSACTION;
899
case HA_ERR_LOCK_DEADLOCK:
900
textno=ER_LOCK_DEADLOCK;
902
case HA_ERR_WRONG_COMMAND:
903
textno=ER_ILLEGAL_HA;
910
if ( textno == ER_ILLEGAL_HA )
911
my_error(textno, MYF(ME_BELL+ME_OLDWIN+ME_WAITTANG), table);
913
my_error(textno, MYF(ME_BELL+ME_OLDWIN+ME_WAITTANG), error);
917
/****************************************************************************
918
Handling of global read locks
920
Taking the global read lock is TWO steps (2nd step is optional; without
921
it, COMMIT of existing transactions will be allowed):
922
lock_global_read_lock() THEN make_global_read_lock_block_commit().
924
The global locks are handled through the global variables:
926
count of threads which have the global read lock (i.e. have completed at
927
least the first step above)
928
global_read_lock_blocks_commit
929
count of threads which have the global read lock and block
930
commits (i.e. are in or have completed the second step above)
931
waiting_for_read_lock
932
count of threads which want to take a global read lock but cannot
933
protect_against_global_read_lock
934
count of threads which have set protection against global read lock.
936
access to them is protected with a mutex LOCK_global_read_lock
938
(XXX: one should never take LOCK_open if LOCK_global_read_lock is
939
taken, otherwise a deadlock may occur. Other mutexes could be a
940
problem too - grep the code for global_read_lock if you want to use
941
any other mutex here) Also one must not hold LOCK_open when calling
942
wait_if_global_read_lock(). When the thread with the global read lock
943
tries to close its tables, it needs to take LOCK_open in
944
close_thread_table().
946
How blocking of threads by global read lock is achieved: that's
947
advisory. Any piece of code which should be blocked by global read lock must
948
be designed like this:
949
- call to wait_if_global_read_lock(). When this returns 0, no global read
950
lock is owned; if argument abort_on_refresh was 0, none can be obtained.
952
- if abort_on_refresh was 0, call to start_waiting_global_read_lock() to
953
allow other threads to get the global read lock. I.e. removal of the
955
(Note: it's a bit like an implementation of rwlock).
957
[ I am sorry to mention some SQL syntaxes below I know I shouldn't but found
958
no better descriptive way ]
960
Why does FLUSH TABLES WITH READ LOCK need to block COMMIT: because it's used
961
to read a non-moving SHOW MASTER STATUS, and a COMMIT writes to the binary
964
Why getting the global read lock is two steps and not one. Because FLUSH
965
TABLES WITH READ LOCK needs to insert one other step between the two:
966
flushing tables. So the order is
967
1) lock_global_read_lock() (prevents any new table write locks, i.e. stalls
969
2) close_cached_tables() (the FLUSH TABLES), which will wait for tables
970
currently opened and being updated to close (so it's possible that there is
971
a moment where all new updates of server are stalled *and* FLUSH TABLES WITH
973
3) make_global_read_lock_block_commit().
974
If we have merged 1) and 3) into 1), we would have had this deadlock:
975
imagine thread 1 and 2, in non-autocommit mode, thread 3, and an InnoDB
977
session1: SELECT * FROM t FOR UPDATE;
978
session2: UPDATE t SET a=1; # blocked by row-level locks of session1
979
session3: FLUSH TABLES WITH READ LOCK; # blocked in close_cached_tables() by the
980
table instance of session2
981
session1: COMMIT; # blocked by session3.
982
session1 blocks session2 which blocks session3 which blocks session1: deadlock.
984
Note that we need to support that one thread does
985
FLUSH TABLES WITH READ LOCK; and then COMMIT;
986
(that's what innobackup does, for some good reason).
987
So in this exceptional case the COMMIT should not be blocked by the FLUSH
988
TABLES WITH READ LOCK.
990
****************************************************************************/
992
volatile uint32_t global_read_lock=0;
993
volatile uint32_t global_read_lock_blocks_commit=0;
994
static volatile uint32_t protect_against_global_read_lock=0;
995
static volatile uint32_t waiting_for_read_lock=0;
997
#define GOT_GLOBAL_READ_LOCK 1
998
#define MADE_GLOBAL_READ_LOCK_BLOCK_COMMIT 2
1000
bool lock_global_read_lock(Session *session)
1002
if (!session->global_read_lock)
1004
const char *old_message;
1005
(void) pthread_mutex_lock(&LOCK_global_read_lock);
1006
old_message=session->enter_cond(&COND_global_read_lock, &LOCK_global_read_lock,
1007
"Waiting to get readlock");
1009
waiting_for_read_lock++;
1010
while (protect_against_global_read_lock && !session->killed)
1011
pthread_cond_wait(&COND_global_read_lock, &LOCK_global_read_lock);
1012
waiting_for_read_lock--;
1013
if (session->killed)
1015
session->exit_cond(old_message);
1018
session->global_read_lock= GOT_GLOBAL_READ_LOCK;
1020
session->exit_cond(old_message); // this unlocks LOCK_global_read_lock
1023
We DON'T set global_read_lock_blocks_commit now, it will be set after
1024
tables are flushed (as the present function serves for FLUSH TABLES WITH
1025
READ LOCK only). Doing things in this order is necessary to avoid
1026
deadlocks (we must allow COMMIT until all tables are closed; we should not
1027
forbid it before, or we can have a 3-thread deadlock if 2 do SELECT FOR
1028
UPDATE and one does FLUSH TABLES WITH READ LOCK).
1034
void unlock_global_read_lock(Session *session)
1038
pthread_mutex_lock(&LOCK_global_read_lock);
1039
tmp= --global_read_lock;
1040
if (session->global_read_lock == MADE_GLOBAL_READ_LOCK_BLOCK_COMMIT)
1041
--global_read_lock_blocks_commit;
1042
pthread_mutex_unlock(&LOCK_global_read_lock);
1043
/* Send the signal outside the mutex to avoid a context switch */
1046
pthread_cond_broadcast(&COND_global_read_lock);
1048
session->global_read_lock= 0;
1051
#define must_wait (global_read_lock && \
1053
global_read_lock_blocks_commit))
1055
bool wait_if_global_read_lock(Session *session, bool abort_on_refresh,
1058
const char *old_message= NULL;
1059
bool result= 0, need_exit_cond;
1062
Assert that we do not own LOCK_open. If we would own it, other
1063
threads could not close their tables. This would make a pretty
1066
safe_mutex_assert_not_owner(&LOCK_open);
1068
(void) pthread_mutex_lock(&LOCK_global_read_lock);
1069
if ((need_exit_cond= must_wait))
1071
if (session->global_read_lock) // This thread had the read locks
1074
my_message(ER_CANT_UPDATE_WITH_READLOCK,
1075
ER(ER_CANT_UPDATE_WITH_READLOCK), MYF(0));
1076
(void) pthread_mutex_unlock(&LOCK_global_read_lock);
1078
We allow FLUSHer to COMMIT; we assume FLUSHer knows what it does.
1079
This allowance is needed to not break existing versions of innobackup
1080
which do a BEGIN; INSERT; FLUSH TABLES WITH READ LOCK; COMMIT.
1082
return is_not_commit;
1084
old_message=session->enter_cond(&COND_global_read_lock, &LOCK_global_read_lock,
1085
"Waiting for release of readlock");
1086
while (must_wait && ! session->killed &&
1087
(!abort_on_refresh || session->version == refresh_version))
1089
(void) pthread_cond_wait(&COND_global_read_lock, &LOCK_global_read_lock);
1091
if (session->killed)
1094
if (!abort_on_refresh && !result)
1095
protect_against_global_read_lock++;
1097
The following is only true in case of a global read locks (which is rare)
1098
and if old_message is set
1100
if (unlikely(need_exit_cond))
1101
session->exit_cond(old_message); // this unlocks LOCK_global_read_lock
1103
pthread_mutex_unlock(&LOCK_global_read_lock);
1108
void start_waiting_global_read_lock(Session *session)
1111
if (unlikely(session->global_read_lock))
1113
(void) pthread_mutex_lock(&LOCK_global_read_lock);
1114
tmp= (!--protect_against_global_read_lock &&
1115
(waiting_for_read_lock || global_read_lock_blocks_commit));
1116
(void) pthread_mutex_unlock(&LOCK_global_read_lock);
1118
pthread_cond_broadcast(&COND_global_read_lock);
1123
bool make_global_read_lock_block_commit(Session *session)
1126
const char *old_message;
1128
If we didn't succeed lock_global_read_lock(), or if we already suceeded
1129
make_global_read_lock_block_commit(), do nothing.
1131
if (session->global_read_lock != GOT_GLOBAL_READ_LOCK)
1133
pthread_mutex_lock(&LOCK_global_read_lock);
1134
/* increment this BEFORE waiting on cond (otherwise race cond) */
1135
global_read_lock_blocks_commit++;
1136
old_message= session->enter_cond(&COND_global_read_lock, &LOCK_global_read_lock,
1137
"Waiting for all running commits to finish");
1138
while (protect_against_global_read_lock && !session->killed)
1139
pthread_cond_wait(&COND_global_read_lock, &LOCK_global_read_lock);
1140
if ((error= test(session->killed)))
1141
global_read_lock_blocks_commit--; // undo what we did
1143
session->global_read_lock= MADE_GLOBAL_READ_LOCK_BLOCK_COMMIT;
1144
session->exit_cond(old_message); // this unlocks LOCK_global_read_lock
1150
Broadcast COND_refresh and COND_global_read_lock.
1152
Due to a bug in a threading library it could happen that a signal
1153
did not reach its target. A condition for this was that the same
1154
condition variable was used with different mutexes in
1155
pthread_cond_wait(). Some time ago we changed LOCK_open to
1156
LOCK_global_read_lock in global read lock handling. So COND_refresh
1157
was used with LOCK_open and LOCK_global_read_lock.
1159
We did now also change from COND_refresh to COND_global_read_lock
1160
in global read lock handling. But now it is necessary to signal
1161
both conditions at the same time.
1164
When signalling COND_global_read_lock within the global read lock
1165
handling, it is not necessary to also signal COND_refresh.
1168
void broadcast_refresh(void)
1170
pthread_cond_broadcast(&COND_refresh);
1171
pthread_cond_broadcast(&COND_global_read_lock);
1176
@} (end of group Locking)
1179
} /* namespace drizzled */