void thr_unlock(THR_LOCK_DATA *data) { THR_LOCK *lock=data->lock; enum thr_lock_type lock_type=data->type; DBUG_ENTER("thr_unlock"); DBUG_PRINT("lock",("data: 0x%lx thread: 0x%x lock: 0x%lx", (long) data, data->owner->thread_id, (long) lock)); mysql_mutex_lock(&lock->mutex); check_locks(lock,"start of release lock",0); if (((*data->prev)=data->next)) /* remove from lock-list */ data->next->prev= data->prev; else if (lock_type <= TL_READ_NO_INSERT) lock->read.last=data->prev; else lock->write.last=data->prev; if (lock_type >= TL_WRITE_CONCURRENT_INSERT) { if (lock->update_status) (*lock->update_status)(data->status_param); } else { if (lock->restore_status) (*lock->restore_status)(data->status_param); } if (lock_type == TL_READ_NO_INSERT) lock->read_no_write_count--; data->type=TL_UNLOCK; /* Mark unlocked */ MYSQL_UNLOCK_TABLE(data->m_psi); check_locks(lock,"after releasing lock",1); wake_up_waiters(lock); mysql_mutex_unlock(&lock->mutex); DBUG_VOID_RETURN; }
my_bool thr_abort_locks_for_thread(THR_LOCK *lock, pthread_t thread) { THR_LOCK_DATA *data; my_bool found= FALSE; DBUG_ENTER("thr_abort_locks_for_thread"); pthread_mutex_lock(&lock->mutex); for (data= lock->read_wait.data; data ; data= data->next) { if (pthread_equal(thread, data->owner->info->thread)) { DBUG_PRINT("info",("Aborting read-wait lock")); data->type= TL_UNLOCK; /* Mark killed */ /* It's safe to signal the cond first: we're still holding the mutex. */ found= TRUE; pthread_cond_signal(data->cond); data->cond= 0; /* Removed from list */ if (((*data->prev)= data->next)) data->next->prev= data->prev; else lock->read_wait.last= data->prev; } } for (data= lock->write_wait.data; data ; data= data->next) { if (pthread_equal(thread, data->owner->info->thread)) { DBUG_PRINT("info",("Aborting write-wait lock")); data->type= TL_UNLOCK; found= TRUE; pthread_cond_signal(data->cond); data->cond= 0; if (((*data->prev)= data->next)) data->next->prev= data->prev; else lock->write_wait.last= data->prev; } } wake_up_waiters(lock); pthread_mutex_unlock(&lock->mutex); DBUG_RETURN(found); }
void thr_unlock(THR_LOCK_DATA *data) { THR_LOCK *lock=data->lock; enum thr_lock_type lock_type=data->type; DBUG_ENTER("thr_unlock"); DBUG_PRINT("lock",("data: 0x%lx thread: %ld lock: 0x%lx", (long) data, data->owner->info->thread_id, (long) lock)); pthread_mutex_lock(&lock->mutex); check_locks(lock,"start of release lock",0); if (((*data->prev)=data->next)) /* remove from lock-list */ data->next->prev= data->prev; else if (lock_type <= TL_READ_NO_INSERT) lock->read.last=data->prev; else if (lock_type == TL_WRITE_DELAYED && data->cond) { /* This only happens in extreme circumstances when a write delayed lock that is waiting for a lock */ lock->write_wait.last=data->prev; /* Put it on wait queue */ } else lock->write.last=data->prev; if (lock_type >= TL_WRITE_CONCURRENT_INSERT) { if (lock->update_status) (*lock->update_status)(data->status_param); } else { if (lock->restore_status) (*lock->restore_status)(data->status_param); } if (lock_type == TL_READ_NO_INSERT) lock->read_no_write_count--; data->type=TL_UNLOCK; /* Mark unlocked */ check_locks(lock,"after releasing lock",1); wake_up_waiters(lock); pthread_mutex_unlock(&lock->mutex); DBUG_VOID_RETURN; }
void thr_abort_locks_for_thread(THR_LOCK *lock, my_thread_id thread_id) { THR_LOCK_DATA *data; DBUG_ENTER("thr_abort_locks_for_thread"); mysql_mutex_lock(&lock->mutex); for (data= lock->read_wait.data; data ; data= data->next) { if (data->owner->thread_id == thread_id) /* purecov: tested */ { DBUG_PRINT("info",("Aborting read-wait lock")); data->type= TL_UNLOCK; /* Mark killed */ /* It's safe to signal the cond first: we're still holding the mutex. */ mysql_cond_signal(data->cond); data->cond= 0; /* Removed from list */ if (((*data->prev)= data->next)) data->next->prev= data->prev; else lock->read_wait.last= data->prev; } } for (data= lock->write_wait.data; data ; data= data->next) { if (data->owner->thread_id == thread_id) /* purecov: tested */ { DBUG_PRINT("info",("Aborting write-wait lock")); data->type= TL_UNLOCK; mysql_cond_signal(data->cond); data->cond= 0; if (((*data->prev)= data->next)) data->next->prev= data->prev; else lock->write_wait.last= data->prev; } } wake_up_waiters(lock); mysql_mutex_unlock(&lock->mutex); DBUG_VOID_RETURN; }
static enum enum_thr_lock_result wait_for_lock(struct st_lock_list *wait, THR_LOCK_DATA *data, my_bool in_wait_list) { struct st_my_thread_var *thread_var= my_thread_var; pthread_cond_t *cond= &thread_var->suspend; struct timespec wait_timeout; enum enum_thr_lock_result result= THR_LOCK_ABORTED; my_bool can_deadlock= test(data->owner->info->n_cursors); if (!in_wait_list) { (*wait->last)=data; /* Wait for lock */ data->prev= wait->last; wait->last= &data->next; } /* Set up control struct to allow others to abort locks */ thread_var->current_mutex= &data->lock->mutex; thread_var->current_cond= cond; data->cond= cond; if (can_deadlock) set_timespec(wait_timeout, table_lock_wait_timeout); while (!thread_var->abort || in_wait_list) { int rc= (can_deadlock ? pthread_cond_timedwait(cond, &data->lock->mutex, &wait_timeout) : pthread_cond_wait(cond, &data->lock->mutex)); /* We must break the wait if one of the following occurs: - the connection has been aborted (!thread_var->abort), but this is not a delayed insert thread (in_wait_list). For a delayed insert thread the proper action at shutdown is, apparently, to acquire the lock and complete the insert. - the lock has been granted (data->cond is set to NULL by the granter), or the waiting has been aborted (additionally data->type is set to TL_UNLOCK). - the wait has timed out (rc == ETIMEDOUT) Order of checks below is important to not report about timeout if the predicate is true. */ if (data->cond == 0) break; if (rc == ETIMEDOUT || rc == ETIME) { result= THR_LOCK_WAIT_TIMEOUT; break; } } if (data->cond || data->type == TL_UNLOCK) { if (data->cond) /* aborted or timed out */ { if (((*data->prev)=data->next)) /* remove from wait-list */ data->next->prev= data->prev; else wait->last=data->prev; data->type= TL_UNLOCK; /* No lock */ check_locks(data->lock, "killed or timed out wait_for_lock", 1); wake_up_waiters(data->lock); } else { check_locks(data->lock, "aborted wait_for_lock", 0); } } else { result= THR_LOCK_SUCCESS; statistic_increment(locks_waited, &THR_LOCK_lock); if (data->lock->get_status) (*data->lock->get_status)(data->status_param, 0); check_locks(data->lock,"got wait_for_lock",0); } pthread_mutex_unlock(&data->lock->mutex); /* The following must be done after unlock of lock->mutex */ pthread_mutex_lock(&thread_var->mutex); thread_var->current_mutex= 0; thread_var->current_cond= 0; pthread_mutex_unlock(&thread_var->mutex); return result; }
static enum enum_thr_lock_result wait_for_lock(struct st_lock_list *wait, THR_LOCK_DATA *data, my_bool in_wait_list, ulong lock_wait_timeout, struct st_my_thread_var *thread_var) { struct timespec wait_timeout; enum enum_thr_lock_result result= THR_LOCK_ABORTED; PSI_stage_info old_stage; DBUG_ENTER("wait_for_lock"); /* One can use this to signal when a thread is going to wait for a lock. See debug_sync.cc. Beware of waiting for a signal here. The lock has aquired its mutex. While waiting on a signal here, the locking thread could not aquire the mutex to release the lock. One could lock up the table completely. In detail it works so: When thr_lock() tries to acquire a table lock, it locks the lock->mutex, checks if it can have the lock, and if not, it calls wait_for_lock(). Here it unlocks the table lock while waiting on a condition. The sync point is located before this wait for condition. If we have a waiting action here, we hold the the table locks mutex all the time. Any attempt to look at the table lock by another thread blocks it immediately on lock->mutex. This can easily become an unexpected and unobvious blockage. So be warned: Do not request a WAIT_FOR action for the 'wait_for_lock' sync point unless you really know what you do. */ DEBUG_SYNC_C("wait_for_lock"); if (!in_wait_list) { (*wait->last)=data; /* Wait for lock */ data->prev= wait->last; wait->last= &data->next; } locks_waited++; /* Set up control struct to allow others to abort locks */ data->cond= &thread_var->suspend; enter_cond_hook(NULL, data->cond, &data->lock->mutex, &stage_waiting_for_table_level_lock, &old_stage, __func__, __FILE__, __LINE__); /* Since before_lock_wait potentially can create more threads to scheduler work for, we don't want to call the before_lock_wait callback unless it will really start to wait. For similar reasons, we do not want to call before_lock_wait and after_lock_wait for each lap around the loop, so we restrict ourselves to call it before_lock_wait once before starting to wait and once after the thread has exited the wait loop. */ if ((!thread_var->abort || in_wait_list) && before_lock_wait) (*before_lock_wait)(); set_timespec(&wait_timeout, lock_wait_timeout); while (!thread_var->abort || in_wait_list) { int rc= mysql_cond_timedwait(data->cond, &data->lock->mutex, &wait_timeout); /* We must break the wait if one of the following occurs: - the connection has been aborted (!thread_var->abort), - the lock has been granted (data->cond is set to NULL by the granter), or the waiting has been aborted (additionally data->type is set to TL_UNLOCK). - the wait has timed out (rc == ETIMEDOUT) Order of checks below is important to not report about timeout if the predicate is true. */ if (data->cond == 0) { DBUG_PRINT("thr_lock", ("lock granted/aborted")); break; } if (rc == ETIMEDOUT || rc == ETIME) { /* purecov: begin inspected */ DBUG_PRINT("thr_lock", ("lock timed out")); result= THR_LOCK_WAIT_TIMEOUT; break; /* purecov: end */ } } /* We call the after_lock_wait callback once the wait loop has finished. */ if (after_lock_wait) (*after_lock_wait)(); DBUG_PRINT("thr_lock", ("aborted: %d in_wait_list: %d", thread_var->abort, in_wait_list)); if (data->cond || data->type == TL_UNLOCK) { if (data->cond) /* aborted or timed out */ { if (((*data->prev)=data->next)) /* remove from wait-list */ data->next->prev= data->prev; else wait->last=data->prev; data->type= TL_UNLOCK; /* No lock */ check_locks(data->lock, "killed or timed out wait_for_lock", 1); wake_up_waiters(data->lock); } else { DBUG_PRINT("thr_lock", ("lock aborted")); check_locks(data->lock, "aborted wait_for_lock", 0); } } else { result= THR_LOCK_SUCCESS; if (data->lock->get_status) (*data->lock->get_status)(data->status_param, 0); check_locks(data->lock,"got wait_for_lock",0); } mysql_mutex_unlock(&data->lock->mutex); exit_cond_hook(NULL, &old_stage, __func__, __FILE__, __LINE__); DBUG_RETURN(result); }