|
|
|
@ -664,6 +664,108 @@ wait_for_lock(struct st_lock_list *wait, THR_LOCK_DATA *data, |
|
|
|
DBUG_RETURN(result); |
|
|
|
} |
|
|
|
|
|
|
|
#ifdef WITH_WSREP |
|
|
|
/* |
|
|
|
* If brute force applier would need to wait for a thr lock, |
|
|
|
* it needs to make sure that it will get the lock without (too much) |
|
|
|
* delay. |
|
|
|
* We identify here the owners of blocking locks and ask them to |
|
|
|
* abort. We then put our lock request in the first place in the |
|
|
|
* wait queue. When lock holders abort (one by one) the lock release |
|
|
|
* algorithm should grant the lock to us. We rely on this and proceed |
|
|
|
* to wait_for_locks(). |
|
|
|
* wsrep_break_locks() should be called in all the cases, where lock |
|
|
|
* wait would happen. |
|
|
|
* |
|
|
|
* TODO: current implementation might not cover all possible lock wait |
|
|
|
* situations. This needs an review still. |
|
|
|
* TODO: lock release, might favor some other lock (instead our bf). |
|
|
|
* This needs an condition to check for bf locks first. |
|
|
|
* TODO: we still have a debug fprintf, this should be removed |
|
|
|
*/ |
|
|
|
static inline my_bool |
|
|
|
wsrep_break_lock( |
|
|
|
THR_LOCK_DATA *data, struct st_lock_list *lock_queue1, |
|
|
|
struct st_lock_list *lock_queue2, struct st_lock_list *wait_queue) |
|
|
|
{ |
|
|
|
if (wsrep_on(data->owner->mysql_thd) && |
|
|
|
wsrep_thd_is_brute_force && |
|
|
|
wsrep_thd_is_brute_force(data->owner->mysql_thd)) |
|
|
|
{ |
|
|
|
THR_LOCK_DATA *holder; |
|
|
|
|
|
|
|
/* if locking session conversion to transaction has been enabled, |
|
|
|
we know that this conflicting lock must be read lock and furthermore, |
|
|
|
lock holder is read-only. It is safe to wait for him. |
|
|
|
*/ |
|
|
|
#ifdef TODO |
|
|
|
if (wsrep_convert_LOCK_to_trx && |
|
|
|
(THD*)(data->owner->mysql_thd)->in_lock_tables) |
|
|
|
{ |
|
|
|
if (wsrep_debug) |
|
|
|
fprintf(stderr,"WSREP wsrep_break_lock read lock untouched\n"); |
|
|
|
return FALSE; |
|
|
|
} |
|
|
|
#endif |
|
|
|
if (wsrep_debug) |
|
|
|
fprintf(stderr,"WSREP wsrep_break_lock aborting locks\n"); |
|
|
|
|
|
|
|
/* aborting lock holder(s) here */ |
|
|
|
for (holder=(lock_queue1) ? lock_queue1->data : NULL; |
|
|
|
holder; |
|
|
|
holder=holder->next) |
|
|
|
{ |
|
|
|
if (!wsrep_thd_is_brute_force(holder->owner->mysql_thd)) |
|
|
|
{ |
|
|
|
wsrep_abort_thd(data->owner->mysql_thd, |
|
|
|
holder->owner->mysql_thd, FALSE); |
|
|
|
} |
|
|
|
else |
|
|
|
{ |
|
|
|
if (wsrep_debug) |
|
|
|
fprintf(stderr,"WSREP wsrep_break_lock skipping BF lock conflict\n"); |
|
|
|
return FALSE; |
|
|
|
} |
|
|
|
} |
|
|
|
for (holder=(lock_queue2) ? lock_queue2->data : NULL; |
|
|
|
holder; |
|
|
|
holder=holder->next) |
|
|
|
{ |
|
|
|
if (!wsrep_thd_is_brute_force(holder->owner->mysql_thd)) |
|
|
|
{ |
|
|
|
wsrep_abort_thd(data->owner->mysql_thd, |
|
|
|
holder->owner->mysql_thd, FALSE); |
|
|
|
} |
|
|
|
else |
|
|
|
{ |
|
|
|
if (wsrep_debug) |
|
|
|
fprintf(stderr,"WSREP wsrep_break_lock skipping BF lock conflict\n"); |
|
|
|
return FALSE; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/* Add our lock to the head of the wait queue */ |
|
|
|
if (*(wait_queue->last)==wait_queue->data) |
|
|
|
{ |
|
|
|
wait_queue->last=&data->next; |
|
|
|
assert(wait_queue->data==0); |
|
|
|
} |
|
|
|
else |
|
|
|
{ |
|
|
|
assert(wait_queue->data!=0); |
|
|
|
wait_queue->data->prev=&data->next; |
|
|
|
} |
|
|
|
data->next=wait_queue->data; |
|
|
|
data->prev=&wait_queue->data; |
|
|
|
wait_queue->data=data; |
|
|
|
data->cond=get_cond(); |
|
|
|
|
|
|
|
statistic_increment(locks_immediate,&THR_LOCK_lock); |
|
|
|
return TRUE; |
|
|
|
} |
|
|
|
return FALSE; |
|
|
|
} |
|
|
|
#endif |
|
|
|
|
|
|
|
static enum enum_thr_lock_result |
|
|
|
thr_lock(THR_LOCK_DATA *data, THR_LOCK_INFO *owner, ulong lock_wait_timeout) |
|
|
|
@ -672,6 +774,9 @@ thr_lock(THR_LOCK_DATA *data, THR_LOCK_INFO *owner, ulong lock_wait_timeout) |
|
|
|
enum enum_thr_lock_result result= THR_LOCK_SUCCESS; |
|
|
|
struct st_lock_list *wait_queue; |
|
|
|
enum thr_lock_type lock_type= data->type; |
|
|
|
#ifdef WITH_WSREP |
|
|
|
my_bool wsrep_lock_inserted= FALSE; |
|
|
|
#endif |
|
|
|
MYSQL_TABLE_WAIT_VARIABLES(locker, state) /* no ';' */ |
|
|
|
DBUG_ENTER("thr_lock"); |
|
|
|
|
|
|
|
@ -741,6 +846,14 @@ thr_lock(THR_LOCK_DATA *data, THR_LOCK_INFO *owner, ulong lock_wait_timeout) |
|
|
|
} |
|
|
|
if (lock->write.data->type == TL_WRITE_ONLY) |
|
|
|
{ |
|
|
|
#ifdef WITH_WSREP |
|
|
|
if (wsrep_break_lock(data, &lock->write, NULL, &lock->read_wait)) |
|
|
|
{ |
|
|
|
wsrep_lock_inserted= TRUE; |
|
|
|
goto wsrep_read_wait; |
|
|
|
} |
|
|
|
#endif |
|
|
|
|
|
|
|
/* We are not allowed to get a READ lock in this case */ |
|
|
|
data->type=TL_UNLOCK; |
|
|
|
result= THR_LOCK_ABORTED; /* Can't wait for this one */ |
|
|
|
@ -768,6 +881,14 @@ thr_lock(THR_LOCK_DATA *data, THR_LOCK_INFO *owner, ulong lock_wait_timeout) |
|
|
|
lock but a high priority write waiting in the write_wait queue. |
|
|
|
In the latter case we should yield the lock to the writer. |
|
|
|
*/ |
|
|
|
#ifdef WITH_WSREP |
|
|
|
if (wsrep_break_lock(data, &lock->write, NULL, &lock->read_wait)) |
|
|
|
{ |
|
|
|
wsrep_lock_inserted= TRUE; |
|
|
|
} |
|
|
|
wsrep_read_wait: |
|
|
|
#endif |
|
|
|
|
|
|
|
wait_queue= &lock->read_wait; |
|
|
|
} |
|
|
|
else /* Request for WRITE lock */ |
|
|
|
@ -776,12 +897,25 @@ thr_lock(THR_LOCK_DATA *data, THR_LOCK_INFO *owner, ulong lock_wait_timeout) |
|
|
|
{ |
|
|
|
if (lock->write.data && lock->write.data->type == TL_WRITE_ONLY) |
|
|
|
{ |
|
|
|
#ifdef WITH_WSREP |
|
|
|
if (wsrep_break_lock(data, &lock->write, NULL, &lock->write_wait)) |
|
|
|
{ |
|
|
|
wsrep_lock_inserted=TRUE; |
|
|
|
goto wsrep_write_wait; |
|
|
|
} |
|
|
|
#endif |
|
|
|
data->type=TL_UNLOCK; |
|
|
|
result= THR_LOCK_ABORTED; /* Can't wait for this one */ |
|
|
|
goto end; |
|
|
|
} |
|
|
|
if (lock->write.data || lock->read.data) |
|
|
|
{ |
|
|
|
#ifdef WITH_WSREP |
|
|
|
if (wsrep_break_lock(data, &lock->write, NULL, &lock->write_wait)) |
|
|
|
{ |
|
|
|
goto end; |
|
|
|
} |
|
|
|
#endif |
|
|
|
/* Add delayed write lock to write_wait queue, and return at once */ |
|
|
|
(*lock->write_wait.last)=data; |
|
|
|
data->prev=lock->write_wait.last; |
|
|
|
@ -806,6 +940,13 @@ thr_lock(THR_LOCK_DATA *data, THR_LOCK_INFO *owner, ulong lock_wait_timeout) |
|
|
|
/* Allow lock owner to bypass TL_WRITE_ONLY. */ |
|
|
|
if (!thr_lock_owner_equal(data->owner, lock->write.data->owner)) |
|
|
|
{ |
|
|
|
#ifdef WITH_WSREP |
|
|
|
if (wsrep_break_lock(data, &lock->write, NULL, &lock->write_wait)) |
|
|
|
{ |
|
|
|
wsrep_lock_inserted=TRUE; |
|
|
|
goto wsrep_write_wait; |
|
|
|
} |
|
|
|
#endif |
|
|
|
/* We are not allowed to get a lock in this case */ |
|
|
|
data->type=TL_UNLOCK; |
|
|
|
result= THR_LOCK_ABORTED; /* Can't wait for this one */ |
|
|
|
@ -909,9 +1050,22 @@ thr_lock(THR_LOCK_DATA *data, THR_LOCK_INFO *owner, ulong lock_wait_timeout) |
|
|
|
DBUG_PRINT("lock",("write locked 3 by thread: 0x%lx type: %d", |
|
|
|
lock->read.data->owner->thread_id, data->type)); |
|
|
|
} |
|
|
|
#ifdef WITH_WSREP |
|
|
|
if (wsrep_break_lock(data, &lock->write, NULL, &lock->write_wait)) |
|
|
|
{ |
|
|
|
wsrep_lock_inserted= TRUE; |
|
|
|
} |
|
|
|
wsrep_write_wait: |
|
|
|
|
|
|
|
#endif |
|
|
|
|
|
|
|
wait_queue= &lock->write_wait; |
|
|
|
} |
|
|
|
/* Can't get lock yet; Wait for it */ |
|
|
|
#ifdef WITH_WSREP |
|
|
|
if (wsrep_on(data->owner->mysql_thd) && wsrep_lock_inserted) |
|
|
|
DBUG_RETURN(wait_for_lock(wait_queue, data, 1, lock_wait_timeout)); |
|
|
|
#endif |
|
|
|
result= wait_for_lock(wait_queue, data, 0, lock_wait_timeout); |
|
|
|
MYSQL_END_TABLE_LOCK_WAIT(locker); |
|
|
|
DBUG_RETURN(result); |
|
|
|
@ -1174,109 +1328,6 @@ static void sort_locks(THR_LOCK_DATA **data,uint count) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
#ifdef WITH_WSREP |
|
|
|
/* |
|
|
|
* If brute force applier would need to wait for a thr lock, |
|
|
|
* it needs to make sure that it will get the lock without (too much) |
|
|
|
* delay. |
|
|
|
* We identify here the owners of blocking locks and ask them to |
|
|
|
* abort. We then put our lock request in the first place in the |
|
|
|
* wait queue. When lock holders abort (one by one) the lock release |
|
|
|
* algorithm should grant the lock to us. We rely on this and proceed |
|
|
|
* to wait_for_locks(). |
|
|
|
* wsrep_break_locks() should be called in all the cases, where lock |
|
|
|
* wait would happen. |
|
|
|
* |
|
|
|
* TODO: current implementation might not cover all possible lock wait |
|
|
|
* situations. This needs an review still. |
|
|
|
* TODO: lock release, might favor some other lock (instead our bf). |
|
|
|
* This needs an condition to check for bf locks first. |
|
|
|
* TODO: we still have a debug fprintf, this should be removed |
|
|
|
*/ |
|
|
|
static inline my_bool |
|
|
|
wsrep_break_lock( |
|
|
|
THR_LOCK_DATA *data, struct st_lock_list *lock_queue1, |
|
|
|
struct st_lock_list *lock_queue2, struct st_lock_list *wait_queue) |
|
|
|
{ |
|
|
|
if (wsrep_on(data->owner->mysql_thd) && |
|
|
|
wsrep_thd_is_brute_force && |
|
|
|
wsrep_thd_is_brute_force(data->owner->mysql_thd)) |
|
|
|
{ |
|
|
|
THR_LOCK_DATA *holder; |
|
|
|
|
|
|
|
/* if locking session conversion to transaction has been enabled, |
|
|
|
we know that this conflicting lock must be read lock and furthermore, |
|
|
|
lock holder is read-only. It is safe to wait for him. |
|
|
|
*/ |
|
|
|
#ifdef TODO |
|
|
|
if (wsrep_convert_LOCK_to_trx && |
|
|
|
(THD*)(data->owner->mysql_thd)->in_lock_tables) |
|
|
|
{ |
|
|
|
if (wsrep_debug) |
|
|
|
fprintf(stderr,"WSREP wsrep_break_lock read lock untouched\n"); |
|
|
|
return FALSE; |
|
|
|
} |
|
|
|
#endif |
|
|
|
if (wsrep_debug) |
|
|
|
fprintf(stderr,"WSREP wsrep_break_lock aborting locks\n"); |
|
|
|
|
|
|
|
/* aborting lock holder(s) here */ |
|
|
|
for (holder=(lock_queue1) ? lock_queue1->data : NULL; |
|
|
|
holder; |
|
|
|
holder=holder->next) |
|
|
|
{ |
|
|
|
if (!wsrep_thd_is_brute_force(holder->owner->mysql_thd)) |
|
|
|
{ |
|
|
|
wsrep_abort_thd(data->owner->mysql_thd, |
|
|
|
holder->owner->mysql_thd, FALSE); |
|
|
|
} |
|
|
|
else |
|
|
|
{ |
|
|
|
if (wsrep_debug) |
|
|
|
fprintf(stderr,"WSREP wsrep_break_lock skipping BF lock conflict\n"); |
|
|
|
return FALSE; |
|
|
|
} |
|
|
|
} |
|
|
|
for (holder=(lock_queue2) ? lock_queue2->data : NULL; |
|
|
|
holder; |
|
|
|
holder=holder->next) |
|
|
|
{ |
|
|
|
if (!wsrep_thd_is_brute_force(holder->owner->mysql_thd)) |
|
|
|
{ |
|
|
|
wsrep_abort_thd(data->owner->mysql_thd, |
|
|
|
holder->owner->mysql_thd, FALSE); |
|
|
|
} |
|
|
|
else |
|
|
|
{ |
|
|
|
if (wsrep_debug) |
|
|
|
fprintf(stderr,"WSREP wsrep_break_lock skipping BF lock conflict\n"); |
|
|
|
return FALSE; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/* Add our lock to the head of the wait queue */ |
|
|
|
if (*(wait_queue->last)==wait_queue->data) |
|
|
|
{ |
|
|
|
wait_queue->last=&data->next; |
|
|
|
assert(wait_queue->data==0); |
|
|
|
} |
|
|
|
else |
|
|
|
{ |
|
|
|
assert(wait_queue->data!=0); |
|
|
|
wait_queue->data->prev=&data->next; |
|
|
|
} |
|
|
|
data->next=wait_queue->data; |
|
|
|
data->prev=&wait_queue->data; |
|
|
|
wait_queue->data=data; |
|
|
|
data->cond=get_cond(); |
|
|
|
|
|
|
|
statistic_increment(locks_immediate,&THR_LOCK_lock); |
|
|
|
return TRUE; |
|
|
|
} |
|
|
|
return FALSE; |
|
|
|
} |
|
|
|
#endif |
|
|
|
|
|
|
|
enum enum_thr_lock_result |
|
|
|
thr_multi_lock(THR_LOCK_DATA **data, uint count, THR_LOCK_INFO *owner, |
|
|
|
ulong lock_wait_timeout) |
|
|
|
|