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; }
static my_bool wait_for_lock(struct st_lock_list *wait, THR_LOCK_DATA *data, my_bool in_wait_list) { pthread_cond_t *cond=get_cond(); struct st_my_thread_var *thread_var=my_thread_var; int result; 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; while (!thread_var->abort || in_wait_list) { pthread_cond_wait(cond,&data->lock->mutex); if (data->cond != cond) break; } if (data->cond || data->type == TL_UNLOCK) { if (data->cond) /* aborted */ { 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 */ result=1; /* Didn't get lock */ check_locks(data->lock,"failed wait_for_lock",0); } else { result=0; statistic_increment(locks_waited, &THR_LOCK_lock); if (data->lock->get_status) (*data->lock->get_status)(data->status_param); 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 inline void free_all_read_locks(THR_LOCK *lock, my_bool using_concurrent_insert) { THR_LOCK_DATA *data=lock->read_wait.data; check_locks(lock,"before freeing read locks",1); /* move all locks from read_wait list to read list */ (*lock->read.last)=data; data->prev=lock->read.last; lock->read.last=lock->read_wait.last; /* Clear read_wait list */ lock->read_wait.last= &lock->read_wait.data; do { mysql_cond_t *cond= data->cond; if ((int) data->type == (int) TL_READ_NO_INSERT) { if (using_concurrent_insert) { /* We can't free this lock; Link lock away from read chain back into read_wait chain */ if (((*data->prev)=data->next)) data->next->prev=data->prev; else lock->read.last=data->prev; *lock->read_wait.last= data; data->prev= lock->read_wait.last; lock->read_wait.last= &data->next; continue; } lock->read_no_write_count++; } /* purecov: begin inspected */ DBUG_PRINT("lock",("giving read lock to thread: 0x%x", data->owner->thread_id)); /* purecov: end */ data->cond=0; /* Mark thread free */ mysql_cond_signal(cond); } while ((data=data->next)); *lock->read_wait.last=0; if (!lock->read_wait.data) lock->write_lock_count=0; check_locks(lock,"after giving read locks",0); }
static void add_fetch_request(struct object *obj) { struct transfer_request *request; check_locks(); /* * Don't fetch the object if it's known to exist locally * or is already in the request queue */ if (remote_dir_exists[obj->oid.hash[0]] == -1) get_remote_object_list(obj->oid.hash[0]); if (obj->flags & (LOCAL | FETCHING)) return; obj->flags |= FETCHING; request = xmalloc(sizeof(*request)); request->obj = obj; request->url = NULL; request->lock = NULL; request->headers = NULL; request->state = NEED_FETCH; request->next = request_queue_head; request_queue_head = request; #ifdef USE_CURL_MULTI fill_active_slots(); step_active_slots(); #endif }
my_bool thr_upgrade_write_delay_lock(THR_LOCK_DATA *data) { THR_LOCK *lock=data->lock; DBUG_ENTER("thr_upgrade_write_delay_lock"); pthread_mutex_lock(&lock->mutex); if (data->type == TL_UNLOCK || data->type >= TL_WRITE_LOW_PRIORITY) { pthread_mutex_unlock(&lock->mutex); DBUG_RETURN(data->type == TL_UNLOCK); /* Test if Aborted */ } check_locks(lock,"before upgrading lock",0); /* TODO: Upgrade to TL_WRITE_CONCURRENT_INSERT in some cases */ data->type=TL_WRITE; /* Upgrade lock */ /* Check if someone has given us the lock */ if (!data->cond) { if (!lock->read.data) /* No read locks */ { /* We have the lock */ if (data->lock->get_status) (*data->lock->get_status)(data->status_param, 0); pthread_mutex_unlock(&lock->mutex); DBUG_RETURN(0); } if (((*data->prev)=data->next)) /* remove from lock-list */ data->next->prev= data->prev; else lock->write.last=data->prev; if ((data->next=lock->write_wait.data)) /* Put first in lock_list */ data->next->prev= &data->next; else lock->write_wait.last= &data->next; data->prev= &lock->write_wait.data; lock->write_wait.data=data; check_locks(lock,"upgrading lock",0); } else { check_locks(lock,"waiting for lock",0); } DBUG_RETURN(wait_for_lock(&lock->write_wait,data,1)); }
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_downgrade_write_lock(THR_LOCK_DATA *in_data, enum thr_lock_type new_lock_type) { THR_LOCK *lock=in_data->lock; #ifndef DBUG_OFF enum thr_lock_type old_lock_type= in_data->type; #endif DBUG_ENTER("thr_downgrade_write_only_lock"); mysql_mutex_lock(&lock->mutex); DBUG_ASSERT(old_lock_type == TL_WRITE_ONLY); DBUG_ASSERT(old_lock_type > new_lock_type); in_data->type= new_lock_type; check_locks(lock,"after downgrading lock",0); mysql_mutex_unlock(&lock->mutex); DBUG_VOID_RETURN; }
static int add_send_request(struct object *obj, struct remote_lock *lock) { struct transfer_request *request; struct packed_git *target; /* Keep locks active */ check_locks(); /* * Don't push the object if it's known to exist on the remote * or is already in the request queue */ if (remote_dir_exists[obj->oid.hash[0]] == -1) get_remote_object_list(obj->oid.hash[0]); if (obj->flags & (REMOTE | PUSHING)) return 0; target = find_sha1_pack(obj->oid.hash, repo->packs); if (target) { obj->flags |= REMOTE; return 0; } obj->flags |= PUSHING; request = xmalloc(sizeof(*request)); request->obj = obj; request->url = NULL; request->lock = lock; request->headers = NULL; request->state = NEED_PUSH; request->next = request_queue_head; request_queue_head = request; #ifdef USE_CURL_MULTI fill_active_slots(); step_active_slots(); #endif return 1; }
enum enum_thr_lock_result thr_lock(THR_LOCK_DATA *data, THR_LOCK_OWNER *owner, enum thr_lock_type lock_type) { THR_LOCK *lock=data->lock; enum enum_thr_lock_result result= THR_LOCK_SUCCESS; struct st_lock_list *wait_queue; THR_LOCK_DATA *lock_owner; DBUG_ENTER("thr_lock"); data->next=0; data->cond=0; /* safety */ data->type=lock_type; data->owner= owner; /* Must be reset ! */ VOID(pthread_mutex_lock(&lock->mutex)); DBUG_PRINT("lock",("data: 0x%lx thread: %ld lock: 0x%lx type: %d", (long) data, data->owner->info->thread_id, (long) lock, (int) lock_type)); check_locks(lock,(uint) lock_type <= (uint) TL_READ_NO_INSERT ? "enter read_lock" : "enter write_lock",0); if ((int) lock_type <= (int) TL_READ_NO_INSERT) { /* Request for READ lock */ if (lock->write.data) { /* We can allow a read lock even if there is already a write lock on the table in one the following cases: - This thread alread have a write lock on the table - The write lock is TL_WRITE_ALLOW_READ or TL_WRITE_DELAYED and the read lock is TL_READ_HIGH_PRIORITY or TL_READ - The write lock is TL_WRITE_CONCURRENT_INSERT or TL_WRITE_ALLOW_WRITE and the read lock is not TL_READ_NO_INSERT */ DBUG_PRINT("lock",("write locked by thread: %ld", lock->write.data->owner->info->thread_id)); if (thr_lock_owner_equal(data->owner, lock->write.data->owner) || (lock->write.data->type <= TL_WRITE_DELAYED && (((int) lock_type <= (int) TL_READ_HIGH_PRIORITY) || (lock->write.data->type != TL_WRITE_CONCURRENT_INSERT && lock->write.data->type != TL_WRITE_ALLOW_READ)))) { /* Already got a write lock */ (*lock->read.last)=data; /* Add to running FIFO */ data->prev=lock->read.last; lock->read.last= &data->next; if (lock_type == TL_READ_NO_INSERT) lock->read_no_write_count++; check_locks(lock,"read lock with old write lock",0); if (lock->get_status) (*lock->get_status)(data->status_param, 0); statistic_increment(locks_immediate,&THR_LOCK_lock); goto end; } if (lock->write.data->type == TL_WRITE_ONLY) { /* 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 */ goto end; } } else if (!lock->write_wait.data || lock->write_wait.data->type <= TL_WRITE_LOW_PRIORITY || lock_type == TL_READ_HIGH_PRIORITY || have_old_read_lock(lock->read.data, data->owner)) { /* No important write-locks */ (*lock->read.last)=data; /* Add to running FIFO */ data->prev=lock->read.last; lock->read.last= &data->next; if (lock->get_status) (*lock->get_status)(data->status_param, 0); if (lock_type == TL_READ_NO_INSERT) lock->read_no_write_count++; check_locks(lock,"read lock with no write locks",0); statistic_increment(locks_immediate,&THR_LOCK_lock); goto end; } /* We're here if there is an active write lock or no write lock but a high priority write waiting in the write_wait queue. In the latter case we should yield the lock to the writer. */ wait_queue= &lock->read_wait; } else /* Request for WRITE lock */ { if (lock_type == TL_WRITE_DELAYED) { if (lock->write.data && lock->write.data->type == TL_WRITE_ONLY) { data->type=TL_UNLOCK; result= THR_LOCK_ABORTED; /* Can't wait for this one */ goto end; } /* if there is a TL_WRITE_ALLOW_READ lock, we have to wait for a lock (TL_WRITE_ALLOW_READ is used for ALTER TABLE in MySQL) */ if ((!lock->write.data || lock->write.data->type != TL_WRITE_ALLOW_READ) && !have_specific_lock(lock->write_wait.data,TL_WRITE_ALLOW_READ) && (lock->write.data || lock->read.data)) { /* Add delayed write lock to write_wait queue, and return at once */ (*lock->write_wait.last)=data; data->prev=lock->write_wait.last; lock->write_wait.last= &data->next; data->cond=get_cond(); /* We don't have to do get_status here as we will do it when we change the delayed lock to a real write lock */ statistic_increment(locks_immediate,&THR_LOCK_lock); goto end; } } else if (lock_type == TL_WRITE_CONCURRENT_INSERT && ! lock->check_status) data->type=lock_type= thr_upgraded_concurrent_insert_lock; if (lock->write.data) /* If there is a write lock */ { if (lock->write.data->type == TL_WRITE_ONLY) { /* 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 */ goto end; } /* The following test will not work if the old lock was a TL_WRITE_ALLOW_WRITE, TL_WRITE_ALLOW_READ or TL_WRITE_DELAYED in the same thread, but this will never happen within MySQL. */ if (thr_lock_owner_equal(data->owner, lock->write.data->owner) || (lock_type == TL_WRITE_ALLOW_WRITE && !lock->write_wait.data && lock->write.data->type == TL_WRITE_ALLOW_WRITE)) { /* We have already got a write lock or all locks are TL_WRITE_ALLOW_WRITE */ DBUG_PRINT("info", ("write_wait.data: 0x%lx old_type: %d", (ulong) lock->write_wait.data, lock->write.data->type)); (*lock->write.last)=data; /* Add to running fifo */ data->prev=lock->write.last; lock->write.last= &data->next; check_locks(lock,"second write lock",0); if (data->lock->get_status) (*data->lock->get_status)(data->status_param, 0); statistic_increment(locks_immediate,&THR_LOCK_lock); goto end; } DBUG_PRINT("lock",("write locked by thread: %ld", lock->write.data->owner->info->thread_id)); } else { DBUG_PRINT("info", ("write_wait.data: 0x%lx", (ulong) lock->write_wait.data)); if (!lock->write_wait.data) { /* no scheduled write locks */ my_bool concurrent_insert= 0; if (lock_type == TL_WRITE_CONCURRENT_INSERT) { concurrent_insert= 1; if ((*lock->check_status)(data->status_param)) { concurrent_insert= 0; data->type=lock_type= thr_upgraded_concurrent_insert_lock; } } if (!lock->read.data || (lock_type <= TL_WRITE_DELAYED && ((lock_type != TL_WRITE_CONCURRENT_INSERT && lock_type != TL_WRITE_ALLOW_WRITE) || !lock->read_no_write_count))) { (*lock->write.last)=data; /* Add as current write lock */ data->prev=lock->write.last; lock->write.last= &data->next; if (data->lock->get_status) (*data->lock->get_status)(data->status_param, concurrent_insert); check_locks(lock,"only write lock",0); statistic_increment(locks_immediate,&THR_LOCK_lock); goto end; } } DBUG_PRINT("lock",("write locked by thread: %ld type: %d", lock->read.data->owner->info->thread_id, data->type)); } wait_queue= &lock->write_wait; } /* Try to detect a trivial deadlock when using cursors: attempt to lock a table that is already locked by an open cursor within the same connection. lock_owner can be zero if we succumbed to a high priority writer in the write_wait queue. */ lock_owner= lock->read.data ? lock->read.data : lock->write.data; if (lock_owner && lock_owner->owner->info == owner->info) { result= THR_LOCK_DEADLOCK; goto end; } /* Can't get lock yet; Wait for it */ DBUG_RETURN(wait_for_lock(wait_queue, data, 0)); end: pthread_mutex_unlock(&lock->mutex); DBUG_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) { 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; }
enum enum_thr_lock_result thr_lock(THR_LOCK_DATA *data, THR_LOCK_INFO *owner, enum thr_lock_type lock_type, ulong lock_wait_timeout, struct st_my_thread_var *thread_var) { THR_LOCK *lock=data->lock; enum enum_thr_lock_result result= THR_LOCK_SUCCESS; struct st_lock_list *wait_queue; MYSQL_TABLE_WAIT_VARIABLES(locker, state) /* no ';' */ DBUG_ENTER("thr_lock"); data->next=0; data->cond=0; /* safety */ data->type=lock_type; data->owner= owner; /* Must be reset ! */ MYSQL_START_TABLE_LOCK_WAIT(locker, &state, data->m_psi, PSI_TABLE_LOCK, lock_type); mysql_mutex_lock(&lock->mutex); DBUG_PRINT("lock",("data: 0x%lx thread: 0x%x lock: 0x%lx type: %d", (long) data, data->owner->thread_id, (long) lock, (int) lock_type)); check_locks(lock,(uint) lock_type <= (uint) TL_READ_NO_INSERT ? "enter read_lock" : "enter write_lock",0); if ((int) lock_type <= (int) TL_READ_NO_INSERT) { /* Request for READ lock */ if (lock->write.data) { /* We can allow a read lock even if there is already a write lock on the table if they are owned by the same thread or if they satisfy the following lock compatibility matrix: Request /------- H|++++ WRITE_ALLOW_WRITE e|+++- WRITE_CONCURRENT_INSERT l |||| d |||| |||\= READ_NO_INSERT ||\ = READ_HIGH_PRIORITY |\ = READ_WITH_SHARED_LOCKS \ = READ + = Request can be satisified. - = Request cannot be satisified. READ_NO_INSERT and WRITE_ALLOW_WRITE should in principle be incompatible. Before this could have caused starvation of LOCK TABLE READ in InnoDB under high write load. However now READ_NO_INSERT is only used for LOCK TABLES READ and this statement is handled by the MDL subsystem. See Bug#42147 for more information. */ DBUG_PRINT("lock",("write locked 1 by thread: 0x%x", lock->write.data->owner->thread_id)); if (thr_lock_owner_equal(data->owner, lock->write.data->owner) || (lock->write.data->type < TL_WRITE_CONCURRENT_INSERT) || ((lock->write.data->type == TL_WRITE_CONCURRENT_INSERT) && ((int) lock_type <= (int) TL_READ_HIGH_PRIORITY))) { /* Already got a write lock */ (*lock->read.last)=data; /* Add to running FIFO */ data->prev=lock->read.last; lock->read.last= &data->next; if (lock_type == TL_READ_NO_INSERT) lock->read_no_write_count++; check_locks(lock,"read lock with old write lock",0); if (lock->get_status) (*lock->get_status)(data->status_param, 0); locks_immediate++; goto end; } if (lock->write.data->type == TL_WRITE_ONLY) { /* 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 */ goto end; } } else if (!lock->write_wait.data || lock->write_wait.data->type <= TL_WRITE_LOW_PRIORITY || lock_type == TL_READ_HIGH_PRIORITY || has_old_lock(lock->read.data, data->owner)) /* Has old read lock */ { /* No important write-locks */ (*lock->read.last)=data; /* Add to running FIFO */ data->prev=lock->read.last; lock->read.last= &data->next; if (lock->get_status) (*lock->get_status)(data->status_param, 0); if (lock_type == TL_READ_NO_INSERT) lock->read_no_write_count++; check_locks(lock,"read lock with no write locks",0); locks_immediate++; goto end; } /* We're here if there is an active write lock or no write lock but a high priority write waiting in the write_wait queue. In the latter case we should yield the lock to the writer. */ wait_queue= &lock->read_wait; } else /* Request for WRITE lock */ { if (lock_type == TL_WRITE_CONCURRENT_INSERT && ! lock->check_status) data->type=lock_type= thr_upgraded_concurrent_insert_lock; if (lock->write.data) /* If there is a write lock */ { if (lock->write.data->type == TL_WRITE_ONLY) { /* purecov: begin tested */ /* Allow lock owner to bypass TL_WRITE_ONLY. */ if (!thr_lock_owner_equal(data->owner, lock->write.data->owner)) { /* 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 */ goto end; } /* purecov: end */ } /* The idea is to allow us to get a lock at once if we already have a write lock or if there is no pending write locks and if all write locks are of TL_WRITE_ALLOW_WRITE type. Note that, since lock requests for the same table are sorted in such way that requests with higher thr_lock_type value come first (with one exception (*)), lock being requested usually has equal or "weaker" type than one which thread might have already acquired. *) The only exception to this rule is case when type of old lock is TL_WRITE_LOW_PRIORITY and type of new lock is changed inside of thr_lock() from TL_WRITE_CONCURRENT_INSERT to TL_WRITE since engine turns out to be not supporting concurrent inserts. Note that since TL_WRITE has the same compatibility rules as TL_WRITE_LOW_PRIORITY (their only difference is priority), it is OK to grant new lock without additional checks in such situation. Therefore it is OK to allow acquiring write lock on the table if this thread already holds some write lock on it. (INSERT INTO t1 VALUES (f1()), where f1() is stored function which tries to update t1, is an example of statement which requests two different types of write lock on the same table). */ DBUG_ASSERT(! has_old_lock(lock->write.data, data->owner) || ((lock_type <= lock->write.data->type || (lock_type == TL_WRITE && lock->write.data->type == TL_WRITE_LOW_PRIORITY)))); if ((lock_type == TL_WRITE_ALLOW_WRITE && ! lock->write_wait.data && lock->write.data->type == TL_WRITE_ALLOW_WRITE) || has_old_lock(lock->write.data, data->owner)) { /* We have already got a write lock or all locks are TL_WRITE_ALLOW_WRITE */ DBUG_PRINT("info", ("write_wait.data: 0x%lx old_type: %d", (ulong) lock->write_wait.data, lock->write.data->type)); (*lock->write.last)=data; /* Add to running fifo */ data->prev=lock->write.last; lock->write.last= &data->next; check_locks(lock,"second write lock",0); if (data->lock->get_status) (*data->lock->get_status)(data->status_param, 0); locks_immediate++; goto end; } DBUG_PRINT("lock",("write locked 2 by thread: 0x%x", lock->write.data->owner->thread_id)); } else { DBUG_PRINT("info", ("write_wait.data: 0x%lx", (ulong) lock->write_wait.data)); if (!lock->write_wait.data) { /* no scheduled write locks */ my_bool concurrent_insert= 0; if (lock_type == TL_WRITE_CONCURRENT_INSERT) { concurrent_insert= 1; if ((*lock->check_status)(data->status_param)) { concurrent_insert= 0; data->type=lock_type= thr_upgraded_concurrent_insert_lock; } } if (!lock->read.data || (lock_type <= TL_WRITE_CONCURRENT_INSERT && ((lock_type != TL_WRITE_CONCURRENT_INSERT && lock_type != TL_WRITE_ALLOW_WRITE) || !lock->read_no_write_count))) { (*lock->write.last)=data; /* Add as current write lock */ data->prev=lock->write.last; lock->write.last= &data->next; if (data->lock->get_status) (*data->lock->get_status)(data->status_param, concurrent_insert); check_locks(lock,"only write lock",0); locks_immediate++; goto end; } } DBUG_PRINT("lock",("write locked 3 by thread: 0x%x type: %d", lock->read.data->owner->thread_id, data->type)); } wait_queue= &lock->write_wait; } /* Can't get lock yet; Wait for it */ result= wait_for_lock(wait_queue, data, 0, lock_wait_timeout, thread_var); MYSQL_END_TABLE_LOCK_WAIT(locker); DBUG_RETURN(result); end: mysql_mutex_unlock(&lock->mutex); MYSQL_END_TABLE_LOCK_WAIT(locker); DBUG_RETURN(result); }
int thr_lock(THR_LOCK_DATA *data,enum thr_lock_type lock_type) { THR_LOCK *lock=data->lock; int result=0; DBUG_ENTER("thr_lock"); data->next=0; data->cond=0; /* safety */ data->type=lock_type; data->thread=pthread_self(); /* Must be reset ! */ data->thread_id=my_thread_id(); /* Must be reset ! */ VOID(pthread_mutex_lock(&lock->mutex)); DBUG_PRINT("lock",("data: %lx thread: %ld lock: %lx type: %d", data,data->thread_id,lock,(int) lock_type)); check_locks(lock,(uint) lock_type <= (uint) TL_READ_NO_INSERT ? "enter read_lock" : "enter write_lock",0); if ((int) lock_type <= (int) TL_READ_NO_INSERT) { /* Request for READ lock */ if (lock->write.data) { /* We can allow a read lock even if there is already a write lock on the table in one the following cases: - This thread alread have a write lock on the table - The write lock is TL_WRITE_ALLOW_READ or TL_WRITE_DELAYED and the read lock is TL_READ_HIGH_PRIORITY or TL_READ - The write lock is TL_WRITE_CONCURRENT_INSERT or TL_WRITE_ALLOW_WRITE and the read lock is not TL_READ_NO_INSERT */ DBUG_PRINT("lock",("write locked by thread: %ld", lock->write.data->thread_id)); if (pthread_equal(data->thread,lock->write.data->thread) || (lock->write.data->type <= TL_WRITE_DELAYED && (((int) lock_type <= (int) TL_READ_HIGH_PRIORITY) || (lock->write.data->type != TL_WRITE_CONCURRENT_INSERT && lock->write.data->type != TL_WRITE_ALLOW_READ)))) { /* Already got a write lock */ (*lock->read.last)=data; /* Add to running FIFO */ data->prev=lock->read.last; lock->read.last= &data->next; if ((int) lock_type == (int) TL_READ_NO_INSERT) lock->read_no_write_count++; check_locks(lock,"read lock with old write lock",0); if (lock->get_status) (*lock->get_status)(data->status_param); statistic_increment(locks_immediate,&THR_LOCK_lock); goto end; } if (lock->write.data->type == TL_WRITE_ONLY) { /* We are not allowed to get a READ lock in this case */ data->type=TL_UNLOCK; result=1; /* Can't wait for this one */ goto end; } } else if (!lock->write_wait.data || lock->write_wait.data->type <= TL_WRITE_LOW_PRIORITY || lock_type == TL_READ_HIGH_PRIORITY || have_old_read_lock(lock->read.data,data->thread)) { /* No important write-locks */ (*lock->read.last)=data; /* Add to running FIFO */ data->prev=lock->read.last; lock->read.last= &data->next; if (lock->get_status) (*lock->get_status)(data->status_param); if ((int) lock_type == (int) TL_READ_NO_INSERT) lock->read_no_write_count++; check_locks(lock,"read lock with no write locks",0); statistic_increment(locks_immediate,&THR_LOCK_lock); goto end; } /* Can't get lock yet; Wait for it */ DBUG_RETURN(wait_for_lock(&lock->read_wait,data,0)); } else /* Request for WRITE lock */ { if (lock_type == TL_WRITE_DELAYED) { if (lock->write.data && lock->write.data->type == TL_WRITE_ONLY) { data->type=TL_UNLOCK; result=1; /* Can't wait for this one */ goto end; } /* if there is a TL_WRITE_ALLOW_READ lock, we have to wait for a lock (TL_WRITE_ALLOW_READ is used for ALTER TABLE in MySQL) */ if ((!lock->write.data || lock->write.data->type != TL_WRITE_ALLOW_READ) && !have_specific_lock(lock->write_wait.data,TL_WRITE_ALLOW_READ) && (lock->write.data || lock->read.data)) { /* Add delayed write lock to write_wait queue, and return at once */ (*lock->write_wait.last)=data; data->prev=lock->write_wait.last; lock->write_wait.last= &data->next; data->cond=get_cond(); if (lock->get_status) (*lock->get_status)(data->status_param); statistic_increment(locks_immediate,&THR_LOCK_lock); goto end; } } else if (lock_type == TL_WRITE_CONCURRENT_INSERT && ! lock->check_status) data->type=lock_type= thr_upgraded_concurrent_insert_lock; if (lock->write.data) /* If there is a write lock */ { if (lock->write.data->type == TL_WRITE_ONLY) { /* We are not allowed to get a lock in this case */ data->type=TL_UNLOCK; result=1; /* Can't wait for this one */ goto end; } /* The following test will not work if the old lock was a TL_WRITE_ALLOW_WRITE, TL_WRITE_ALLOW_READ or TL_WRITE_DELAYED in the same thread, but this will never happen within MySQL. */ if (pthread_equal(data->thread,lock->write.data->thread) || (lock_type == TL_WRITE_ALLOW_WRITE && !lock->write_wait.data && lock->write.data->type == TL_WRITE_ALLOW_WRITE)) { /* We have already got a write lock or all locks are TL_WRITE_ALLOW_WRITE */ (*lock->write.last)=data; /* Add to running fifo */ data->prev=lock->write.last; lock->write.last= &data->next; check_locks(lock,"second write lock",0); if (data->lock->get_status) (*data->lock->get_status)(data->status_param); statistic_increment(locks_immediate,&THR_LOCK_lock); goto end; } DBUG_PRINT("lock",("write locked by thread: %ld", lock->write.data->thread_id)); } else { if (!lock->write_wait.data) { /* no scheduled write locks */ if (lock_type == TL_WRITE_CONCURRENT_INSERT && (*lock->check_status)(data->status_param)) data->type=lock_type= thr_upgraded_concurrent_insert_lock; if (!lock->read.data || (lock_type <= TL_WRITE_DELAYED && ((lock_type != TL_WRITE_CONCURRENT_INSERT && lock_type != TL_WRITE_ALLOW_WRITE) || !lock->read_no_write_count))) { (*lock->write.last)=data; /* Add as current write lock */ data->prev=lock->write.last; lock->write.last= &data->next; if (data->lock->get_status) (*data->lock->get_status)(data->status_param); check_locks(lock,"only write lock",0); statistic_increment(locks_immediate,&THR_LOCK_lock); goto end; } } DBUG_PRINT("lock",("write locked by thread: %ld, type: %ld", lock->read.data->thread_id,data->type)); } DBUG_RETURN(wait_for_lock(&lock->write_wait,data,0)); } end: pthread_mutex_unlock(&lock->mutex); DBUG_RETURN(result); }
int damage(int hit, struct ship *ep, int s, struct damage *dam, int flag) { int i; int j; int k; float f1; /* Damage factor except for shields */ float f2 = 1.0; /* Shield damage factor */ int percent; struct ship *fed; fed = shiplist[0]; printf("hit %d on %s's shield %d\n", hit, ep->name, s); s--; /* * Note that if the shield is at 100% efficiency, no * damage at all will be taken (except to the shield itself) */ f1 = hit * (1.0 - ep->shields[s].eff * ep->shields[s].drain); if (f1 < 0) return 0; /* Calculate shield damage */ if (flag == D_ANTIMATTER) f2 = ep->tu_damage * 100; else if (flag == D_PHASER) f2 = ep->ph_damage * 100; if (s == 0) f2 *= SHIELD1; ep->shields[s].eff -= max(hit/f2, 0); if (ep->shields[s].eff < 0.0) ep->shields[s].eff = 0.0; /* Calculate loss of fuel, regeneration, etc. */ ep->eff += f1/dam->eff; ep->pods -= f1/dam->fuel; ep->energy -= f1/dam->fuel; ep->regen -= f1/dam->regen; if (ep->regen < 0.0) ep->regen = 0.0; if (ep->pods < 0.0) ep->pods = 0.0; if (ep->energy < 0.0) ep->energy = 0.0; if (ep->pods < ep->energy) ep->energy = ep->pods; /* Kill some crew */ if (ep->complement > 0) { j = f1 * dam->crew; if (j > 0) ep->complement -= randm(j); if (ep->complement < 0) ep->complement = 0; } /* Damage some weapons */ j = f1/dam->weapon; for(i=0; i<j; i++) { k = randm(ep->num_phasers + ep->num_tubes) - 1; if (k < ep->num_phasers) { if (ep->phasers[k].status & P_DAMAGED) continue; ep->phasers[k].status |= P_DAMAGED; ep->phasers[k].target = NULL; /* * Reroute the energy * back to the engines */ ep->energy = min(ep->pods, ep->energy + ep->phasers[k].load); ep->phasers[k].load = 0; ep->phasers[k].drain = 0; k++; if (ep == fed) printf(" phaser %d damaged\n", k); } else { k -= ep->num_phasers; if (ep->tubes[k].status & T_DAMAGED) continue; /* * If tubes are damaged, reroute the pods * back to the engines */ ep->pods += ep->tubes[k].load; ep->energy += ep->tubes[k].load; ep->tubes[k].load = 0; ep->tubes[k].status |= T_DAMAGED; ep->tubes[k].target = NULL; k++; if (ep == fed) printf(" tube %d damaged\n", k); } } /* Damage the different systems */ for (i=0; i<S_NUMSYSTEMS; i++) { if (is_dead(ep, i)) /* Don't damage a dead system */ continue; percent = 0; if (randm(dam->stats[i].roll) < f1) { /* A better method should be found */ percent = (int) randm((int) f1); /* * The expected value for the percent damage to each system is roughly * equal to: * f1 * f1 / (2 * dam->stats[i].roll) * Only these damages are proportional to hit squared. All others are * linearly proportional. This includes shield damage, ship's fuel * supply, consumption and regeneration rates, casualties, and weapons. * (When weapons are damaged, they are 100% damaged - the number of * weapons damaged is proportional to hit). I think the old way * decided whether or not to completely damage a system based on the * comparison "randm(dam->stats[i].roll) < f1". This is almost like * the weapons are still handled. Another possibility is to always * damage each system by: * 100 * randm((int)f1) / dam->stats[i].roll * percent. This adds some randomness and makes the approx. expected * value of the damage to each system: * 100 * f1 / (2 * dam->stats[i].roll) * percent. Perhaps this isn't such a good idea after all; this is * 100/f1 times the current expected value, often > 2. And it is * actually somewhat less random since each system gets damaged each * time. I had thought that the damage should be directly proportional * to f1, not to its square. But perhaps it makes sense that a hit * twice as big has an expected value of damage four times as big as * that from a smaller hit. The actual damage any given time is still * proportional to the hit, but the probability that any damage will be * done at all is also directly proportional to the hit. This is a * pretty good system after all. [RJN] */ ep->status[i] += percent; if (ep->status[i] > 100) ep->status[i] = 100; if (ep == fed) { if (is_dead(ep, i)) printf(" %s\n", dam->stats[i].mesg); else printf(" %s damaged.\n", sysname[i]); } /* Now check for the effects of the damage */ /* Do the effects of a totally destroyed system */ if (is_dead(ep, i)) { switch(i) { case S_SENSOR: case S_PROBE: /* No bookkeeping needed */ break; case S_WARP: /* Reduce max speed */ ep->max_speed = 1.0; break; case S_COMP: check_locks(ep, 100, fed); break; default: printf("How'd we get here?\n"); } } else { /* Now check partially damaged systems */ switch(i) { case S_SENSOR: case S_PROBE: /* No bookkeeping needed */ break; case S_WARP: f2 = percent * ep->orig_max / 100; ep->max_speed -= f2; if (ep->max_speed < 1.0) { ep->max_speed = 1.0; ep->status[S_WARP] = 100; } break; case S_COMP: check_locks(ep, percent, fed); break; default: printf("Oh, oh....\n"); } } } } #ifdef HISTORICAL /* * Historically, if more than 43 points of damage were done * to the ship, it would destroy itself. This led to much * abuse of probes and thus has been enclosed inside of * an #ifdef */ if (f1 > 43) ep->delay = 1.; #endif return 0; }
static void finish_request(struct transfer_request *request) { struct http_pack_request *preq; struct http_object_request *obj_req; request->curl_result = request->slot->curl_result; request->http_code = request->slot->http_code; request->slot = NULL; /* Keep locks active */ check_locks(); if (request->headers != NULL) curl_slist_free_all(request->headers); /* URL is reused for MOVE after PUT */ if (request->state != RUN_PUT) { FREE_AND_NULL(request->url); } if (request->state == RUN_MKCOL) { if (request->curl_result == CURLE_OK || request->http_code == 405) { remote_dir_exists[request->obj->oid.hash[0]] = 1; start_put(request); } else { fprintf(stderr, "MKCOL %s failed, aborting (%d/%ld)\n", oid_to_hex(&request->obj->oid), request->curl_result, request->http_code); request->state = ABORTED; aborted = 1; } } else if (request->state == RUN_PUT) { if (request->curl_result == CURLE_OK) { start_move(request); } else { fprintf(stderr, "PUT %s failed, aborting (%d/%ld)\n", oid_to_hex(&request->obj->oid), request->curl_result, request->http_code); request->state = ABORTED; aborted = 1; } } else if (request->state == RUN_MOVE) { if (request->curl_result == CURLE_OK) { if (push_verbosely) fprintf(stderr, " sent %s\n", oid_to_hex(&request->obj->oid)); request->obj->flags |= REMOTE; release_request(request); } else { fprintf(stderr, "MOVE %s failed, aborting (%d/%ld)\n", oid_to_hex(&request->obj->oid), request->curl_result, request->http_code); request->state = ABORTED; aborted = 1; } } else if (request->state == RUN_FETCH_LOOSE) { obj_req = (struct http_object_request *)request->userData; if (finish_http_object_request(obj_req) == 0) if (obj_req->rename == 0) request->obj->flags |= (LOCAL | REMOTE); /* Try fetching packed if necessary */ if (request->obj->flags & LOCAL) { release_http_object_request(obj_req); release_request(request); } else start_fetch_packed(request); } else if (request->state == RUN_FETCH_PACKED) { int fail = 1; if (request->curl_result != CURLE_OK) { fprintf(stderr, "Unable to get pack file %s\n%s", request->url, curl_errorstr); } else { preq = (struct http_pack_request *)request->userData; if (preq) { if (finish_http_pack_request(preq) == 0) fail = 0; release_http_pack_request(preq); } } if (fail) repo->can_update_info_refs = 0; release_request(request); } }
int cmd_main(int argc, const char **argv) { struct transfer_request *request; struct transfer_request *next_request; int nr_refspec = 0; const char **refspec = NULL; struct remote_lock *ref_lock = NULL; struct remote_lock *info_ref_lock = NULL; struct rev_info revs; int delete_branch = 0; int force_delete = 0; int objects_to_send; int rc = 0; int i; int new_refs; struct ref *ref, *local_refs; repo = xcalloc(1, sizeof(*repo)); argv++; for (i = 1; i < argc; i++, argv++) { const char *arg = *argv; if (*arg == '-') { if (!strcmp(arg, "--all")) { push_all = MATCH_REFS_ALL; continue; } if (!strcmp(arg, "--force")) { force_all = 1; continue; } if (!strcmp(arg, "--dry-run")) { dry_run = 1; continue; } if (!strcmp(arg, "--helper-status")) { helper_status = 1; continue; } if (!strcmp(arg, "--verbose")) { push_verbosely = 1; http_is_verbose = 1; continue; } if (!strcmp(arg, "-d")) { delete_branch = 1; continue; } if (!strcmp(arg, "-D")) { delete_branch = 1; force_delete = 1; continue; } if (!strcmp(arg, "-h")) usage(http_push_usage); } if (!repo->url) { char *path = strstr(arg, "//"); str_end_url_with_slash(arg, &repo->url); repo->path_len = strlen(repo->url); if (path) { repo->path = strchr(path+2, '/'); if (repo->path) repo->path_len = strlen(repo->path); } continue; } refspec = argv; nr_refspec = argc - i; break; } #ifndef USE_CURL_MULTI die("git-push is not available for http/https repository when not compiled with USE_CURL_MULTI"); #endif if (!repo->url) usage(http_push_usage); if (delete_branch && nr_refspec != 1) die("You must specify only one branch name when deleting a remote branch"); setup_git_directory(); memset(remote_dir_exists, -1, 256); http_init(NULL, repo->url, 1); #ifdef USE_CURL_MULTI is_running_queue = 0; #endif /* Verify DAV compliance/lock support */ if (!locking_available()) { rc = 1; goto cleanup; } sigchain_push_common(remove_locks_on_signal); /* Check whether the remote has server info files */ repo->can_update_info_refs = 0; repo->has_info_refs = remote_exists("info/refs"); repo->has_info_packs = remote_exists("objects/info/packs"); if (repo->has_info_refs) { info_ref_lock = lock_remote("info/refs", LOCK_TIME); if (info_ref_lock) repo->can_update_info_refs = 1; else { error("cannot lock existing info/refs"); rc = 1; goto cleanup; } } if (repo->has_info_packs) fetch_indices(); /* Get a list of all local and remote heads to validate refspecs */ local_refs = get_local_heads(); fprintf(stderr, "Fetching remote heads...\n"); get_dav_remote_heads(); run_request_queue(); /* Remove a remote branch if -d or -D was specified */ if (delete_branch) { if (delete_remote_branch(refspec[0], force_delete) == -1) { fprintf(stderr, "Unable to delete remote branch %s\n", refspec[0]); if (helper_status) printf("error %s cannot remove\n", refspec[0]); } goto cleanup; } /* match them up */ if (match_push_refs(local_refs, &remote_refs, nr_refspec, (const char **) refspec, push_all)) { rc = -1; goto cleanup; } if (!remote_refs) { fprintf(stderr, "No refs in common and none specified; doing nothing.\n"); if (helper_status) printf("error null no match\n"); rc = 0; goto cleanup; } new_refs = 0; for (ref = remote_refs; ref; ref = ref->next) { struct argv_array commit_argv = ARGV_ARRAY_INIT; if (!ref->peer_ref) continue; if (is_null_oid(&ref->peer_ref->new_oid)) { if (delete_remote_branch(ref->name, 1) == -1) { error("Could not remove %s", ref->name); if (helper_status) printf("error %s cannot remove\n", ref->name); rc = -4; } else if (helper_status) printf("ok %s\n", ref->name); new_refs++; continue; } if (!oidcmp(&ref->old_oid, &ref->peer_ref->new_oid)) { if (push_verbosely) fprintf(stderr, "'%s': up-to-date\n", ref->name); if (helper_status) printf("ok %s up to date\n", ref->name); continue; } if (!force_all && !is_null_oid(&ref->old_oid) && !ref->force) { if (!has_object_file(&ref->old_oid) || !ref_newer(&ref->peer_ref->new_oid, &ref->old_oid)) { /* * We do not have the remote ref, or * we know that the remote ref is not * an ancestor of what we are trying to * push. Either way this can be losing * commits at the remote end and likely * we were not up to date to begin with. */ error("remote '%s' is not an ancestor of\n" "local '%s'.\n" "Maybe you are not up-to-date and " "need to pull first?", ref->name, ref->peer_ref->name); if (helper_status) printf("error %s non-fast forward\n", ref->name); rc = -2; continue; } } oidcpy(&ref->new_oid, &ref->peer_ref->new_oid); new_refs++; fprintf(stderr, "updating '%s'", ref->name); if (strcmp(ref->name, ref->peer_ref->name)) fprintf(stderr, " using '%s'", ref->peer_ref->name); fprintf(stderr, "\n from %s\n to %s\n", oid_to_hex(&ref->old_oid), oid_to_hex(&ref->new_oid)); if (dry_run) { if (helper_status) printf("ok %s\n", ref->name); continue; } /* Lock remote branch ref */ ref_lock = lock_remote(ref->name, LOCK_TIME); if (ref_lock == NULL) { fprintf(stderr, "Unable to lock remote branch %s\n", ref->name); if (helper_status) printf("error %s lock error\n", ref->name); rc = 1; continue; } /* Set up revision info for this refspec */ argv_array_push(&commit_argv, ""); /* ignored */ argv_array_push(&commit_argv, "--objects"); argv_array_push(&commit_argv, oid_to_hex(&ref->new_oid)); if (!push_all && !is_null_oid(&ref->old_oid)) argv_array_pushf(&commit_argv, "^%s", oid_to_hex(&ref->old_oid)); init_revisions(&revs, setup_git_directory()); setup_revisions(commit_argv.argc, commit_argv.argv, &revs, NULL); revs.edge_hint = 0; /* just in case */ /* Generate a list of objects that need to be pushed */ pushing = 0; if (prepare_revision_walk(&revs)) die("revision walk setup failed"); mark_edges_uninteresting(&revs, NULL); objects_to_send = get_delta(&revs, ref_lock); finish_all_active_slots(); /* Push missing objects to remote, this would be a convenient time to pack them first if appropriate. */ pushing = 1; if (objects_to_send) fprintf(stderr, " sending %d objects\n", objects_to_send); run_request_queue(); /* Update the remote branch if all went well */ if (aborted || !update_remote(ref->new_oid.hash, ref_lock)) rc = 1; if (!rc) fprintf(stderr, " done\n"); if (helper_status) printf("%s %s\n", !rc ? "ok" : "error", ref->name); unlock_remote(ref_lock); check_locks(); argv_array_clear(&commit_argv); } /* Update remote server info if appropriate */ if (repo->has_info_refs && new_refs) { if (info_ref_lock && repo->can_update_info_refs) { fprintf(stderr, "Updating remote server info\n"); if (!dry_run) update_remote_info_refs(info_ref_lock); } else { fprintf(stderr, "Unable to update server info\n"); } } cleanup: if (info_ref_lock) unlock_remote(info_ref_lock); free(repo); http_cleanup(); request = request_queue_head; while (request != NULL) { next_request = request->next; release_request(request); request = next_request; } return rc; }
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: %lx thread: %ld lock: %lx", data,data->thread_id,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 && lock->update_status) (*lock->update_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); if (!lock->write.data) /* If no active write locks */ { data=lock->write_wait.data; if (!lock->read.data) /* If no more locks in use */ { /* Release write-locks with TL_WRITE or TL_WRITE_ONLY priority first */ if (data && (data->type != TL_WRITE_LOW_PRIORITY || !lock->read_wait.data || lock->read_wait.data->type < TL_READ_HIGH_PRIORITY)) { if (lock->write_lock_count++ > max_write_lock_count) { /* Too many write locks in a row; Release all waiting read locks */ lock->write_lock_count=0; if (lock->read_wait.data) { DBUG_PRINT("info",("Freeing all read_locks because of max_write_lock_count")); free_all_read_locks(lock,0); goto end; } } for (;;) { if (((*data->prev)=data->next)) /* remove from wait-list */ data->next->prev= data->prev; else lock->write_wait.last=data->prev; (*lock->write.last)=data; /* Put in execute list */ data->prev=lock->write.last; data->next=0; lock->write.last= &data->next; if (data->type == TL_WRITE_CONCURRENT_INSERT && (*lock->check_status)(data->status_param)) data->type=TL_WRITE; /* Upgrade lock */ DBUG_PRINT("lock",("giving write lock of type %d to thread: %ld", data->type,data->thread_id)); { pthread_cond_t *cond=data->cond; data->cond=0; /* Mark thread free */ VOID(pthread_cond_signal(cond)); /* Start waiting thread */ } if (data->type != TL_WRITE_ALLOW_WRITE || !lock->write_wait.data || lock->write_wait.data->type != TL_WRITE_ALLOW_WRITE) break; data=lock->write_wait.data; /* Free this too */ } if (data->type >= TL_WRITE_LOW_PRIORITY) { check_locks(lock,"giving write lock",0); pthread_mutex_unlock(&lock->mutex); DBUG_VOID_RETURN; } /* Release possible read locks together with the write lock */ } if (lock->read_wait.data) free_all_read_locks(lock, data && (data->type == TL_WRITE_CONCURRENT_INSERT || data->type == TL_WRITE_ALLOW_WRITE)); else { DBUG_PRINT("lock",("No waiting read locks to free")); } } else if (data && (lock_type=data->type) <= TL_WRITE_DELAYED && ((lock_type != TL_WRITE_CONCURRENT_INSERT && lock_type != TL_WRITE_ALLOW_WRITE) || !lock->read_no_write_count)) { /* For DELAYED, ALLOW_READ, WRITE_ALLOW_WRITE or CONCURRENT_INSERT locks start WRITE locks together with the READ locks */ if (lock_type == TL_WRITE_CONCURRENT_INSERT && (*lock->check_status)(data->status_param)) { data->type=TL_WRITE; /* Upgrade lock */ if (lock->read_wait.data) free_all_read_locks(lock,0); goto end; } do { pthread_cond_t *cond=data->cond; if (((*data->prev)=data->next)) /* remove from wait-list */ data->next->prev= data->prev; else lock->write_wait.last=data->prev; (*lock->write.last)=data; /* Put in execute list */ data->prev=lock->write.last; lock->write.last= &data->next; data->next=0; /* Only one write lock */ data->cond=0; /* Mark thread free */ VOID(pthread_cond_signal(cond)); /* Start waiting thread */ } while (lock_type == TL_WRITE_ALLOW_WRITE && (data=lock->write_wait.data) && data->type == TL_WRITE_ALLOW_WRITE); if (lock->read_wait.data) free_all_read_locks(lock, (lock_type == TL_WRITE_CONCURRENT_INSERT || lock_type == TL_WRITE_ALLOW_WRITE)); } else if (!data && lock->read_wait.data) free_all_read_locks(lock,0); } end: check_locks(lock,"thr_unlock",0); pthread_mutex_unlock(&lock->mutex); DBUG_VOID_RETURN; }
static void wake_up_waiters(THR_LOCK *lock) { THR_LOCK_DATA *data; enum thr_lock_type lock_type; DBUG_ENTER("wake_up_waiters"); if (!lock->write.data) /* If no active write locks */ { data=lock->write_wait.data; if (!lock->read.data) /* If no more locks in use */ { /* Release write-locks with TL_WRITE or TL_WRITE_ONLY priority first */ if (data && (data->type != TL_WRITE_LOW_PRIORITY || !lock->read_wait.data || lock->read_wait.data->type < TL_READ_HIGH_PRIORITY)) { if (lock->write_lock_count++ > max_write_lock_count) { /* Too many write locks in a row; Release all waiting read locks */ lock->write_lock_count=0; if (lock->read_wait.data) { DBUG_PRINT("info",("Freeing all read_locks because of max_write_lock_count")); free_all_read_locks(lock,0); goto end; } } for (;;) { if (((*data->prev)=data->next)) /* remove from wait-list */ data->next->prev= data->prev; else lock->write_wait.last=data->prev; (*lock->write.last)=data; /* Put in execute list */ data->prev=lock->write.last; data->next=0; lock->write.last= &data->next; if (data->type == TL_WRITE_CONCURRENT_INSERT && (*lock->check_status)(data->status_param)) data->type=TL_WRITE; /* Upgrade lock */ DBUG_PRINT("lock",("giving write lock of type %d to thread: %ld", data->type, data->owner->info->thread_id)); { pthread_cond_t *cond=data->cond; data->cond=0; /* Mark thread free */ VOID(pthread_cond_signal(cond)); /* Start waiting thread */ } if (data->type != TL_WRITE_ALLOW_WRITE || !lock->write_wait.data || lock->write_wait.data->type != TL_WRITE_ALLOW_WRITE) break; data=lock->write_wait.data; /* Free this too */ } if (data->type >= TL_WRITE_LOW_PRIORITY) goto end; /* Release possible read locks together with the write lock */ } if (lock->read_wait.data) free_all_read_locks(lock, data && (data->type == TL_WRITE_CONCURRENT_INSERT || data->type == TL_WRITE_ALLOW_WRITE)); else { DBUG_PRINT("lock",("No waiting read locks to free")); } } else if (data && (lock_type=data->type) <= TL_WRITE_DELAYED && ((lock_type != TL_WRITE_CONCURRENT_INSERT && lock_type != TL_WRITE_ALLOW_WRITE) || !lock->read_no_write_count)) { /* For DELAYED, ALLOW_READ, WRITE_ALLOW_WRITE or CONCURRENT_INSERT locks start WRITE locks together with the READ locks */ if (lock_type == TL_WRITE_CONCURRENT_INSERT && (*lock->check_status)(data->status_param)) { data->type=TL_WRITE; /* Upgrade lock */ if (lock->read_wait.data) free_all_read_locks(lock,0); goto end; } do { pthread_cond_t *cond=data->cond; if (((*data->prev)=data->next)) /* remove from wait-list */ data->next->prev= data->prev; else lock->write_wait.last=data->prev; (*lock->write.last)=data; /* Put in execute list */ data->prev=lock->write.last; lock->write.last= &data->next; data->next=0; /* Only one write lock */ data->cond=0; /* Mark thread free */ VOID(pthread_cond_signal(cond)); /* Start waiting thread */ } while (lock_type == TL_WRITE_ALLOW_WRITE && (data=lock->write_wait.data) && data->type == TL_WRITE_ALLOW_WRITE); if (lock->read_wait.data) free_all_read_locks(lock, (lock_type == TL_WRITE_CONCURRENT_INSERT || lock_type == TL_WRITE_ALLOW_WRITE)); } else if (!data && lock->read_wait.data) free_all_read_locks(lock,0); } end: check_locks(lock, "after waking up waiters", 0); 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, 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); }