Esempio n. 1
0
enum enum_thr_lock_result
thr_multi_lock(THR_LOCK_DATA **data, uint count, THR_LOCK_INFO *owner,
               ulong lock_wait_timeout, struct st_my_thread_var *thread_var)
{
  THR_LOCK_DATA **pos,**end;
  DBUG_ENTER("thr_multi_lock");
  DBUG_PRINT("lock",("data: 0x%lx  count: %d", (long) data, count));
  if (count > 1)
    sort_locks(data,count);
  /* lock everything */
  for (pos=data,end=data+count; pos < end ; pos++)
  {
    enum enum_thr_lock_result result= thr_lock(*pos, owner, (*pos)->type,
                                               lock_wait_timeout, thread_var);
    if (result != THR_LOCK_SUCCESS)
    {						/* Aborted */
      thr_multi_unlock(data,(uint) (pos-data));
      DBUG_RETURN(result);
    }
    DEBUG_SYNC_C("thr_multi_lock_after_thr_lock");
#ifdef MAIN
    printf("Thread: T@%u  Got lock: 0x%lx  type: %d\n",
           pos[0]->owner->thread_id, (long) pos[0]->lock, pos[0]->type);
    fflush(stdout);
#endif
  }
  thr_lock_merge_status(data, count);
  DBUG_RETURN(THR_LOCK_SUCCESS);
}
Esempio n. 2
0
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);
}
int mi_assign_to_key_cache(MI_INFO *info,
			   ulonglong key_map __attribute__((unused)),
			   KEY_CACHE *new_key_cache)
{
  int error= 0;
  MYISAM_SHARE* share= info->s;
  KEY_CACHE *old_key_cache= share->key_cache;
  DBUG_ENTER("mi_assign_to_key_cache");
  DBUG_PRINT("enter",("old_key_cache_handle: %p  new_key_cache_handle: %p",
                      old_key_cache, new_key_cache));

  /*
    Skip operation if we didn't change key cache. This can happen if we
    call this for all open instances of the same table
  */
  if (old_key_cache == new_key_cache)
    DBUG_RETURN(0);

  /*
    First flush all blocks for the table in the old key cache.
    This is to ensure that the disk is consistent with the data pages
    in memory (which may not be the case if the table uses delayed_key_write)

    Note that some other read thread may still fill in the key cache with
    new blocks during this call and after, but this doesn't matter as
    all threads will start using the new key cache for their next call to
    myisam library and we know that there will not be any changed blocks
    in the old key cache.
  */

  pthread_mutex_lock(&old_key_cache->op_lock);
  DEBUG_SYNC_C("assign_key_cache_op_lock");
  if (flush_key_blocks(old_key_cache, share->kfile, &share->dirty_part_map,
                       FLUSH_RELEASE))
  {
    error= my_errno;
    mi_print_error(info->s, HA_ERR_CRASHED);
    mi_mark_crashed(info);		/* Mark that table must be checked */
  }
  pthread_mutex_unlock(&old_key_cache->op_lock);
  DEBUG_SYNC_C("assign_key_cache_op_unlock");

  /*
    Flush the new key cache for this file.  This is needed to ensure
    that there is no old blocks (with outdated data) left in the new key
    cache from an earlier assign_to_keycache operation

    (This can never fail as there is never any not written data in the
    new key cache)
  */
  (void) flush_key_blocks(new_key_cache, share->kfile, &share->dirty_part_map,
                          FLUSH_RELEASE);

  /*
    ensure that setting the key cache and changing the multi_key_cache
    is done atomicly
  */
  mysql_mutex_lock(&share->intern_lock);
  /*
    Tell all threads to use the new key cache
    This should be seen at the lastes for the next call to an myisam function.
  */
  share->key_cache= new_key_cache;
  share->dirty_part_map= 0;

  /* store the key cache in the global hash structure for future opens */
  if (multi_key_cache_set((uchar*) share->unique_file_name,
                          share->unique_name_length,
			  new_key_cache))
    error= my_errno;
  mysql_mutex_unlock(&share->intern_lock);
  DBUG_RETURN(error);
}