| pthread_cond_timedwait用于阻塞线程,实现线程等待,  代码在glibc的pthread_cond_timedwait.c文件中,代码较长,你可以先简单过一遍,看完下面的分析再重新读一遍代码 int int __pthread_cond_timedwait (cond, mutex, abstime)  pthread_cond_t *cond;  pthread_mutex_t *mutex;  const struct timespec *abstime; {  struct _pthread_cleanup_buffer buffer;  struct _condvar_cleanup_buffer cbuffer;  int result = 0;  /* Catch invalid parameters. */  if (abstime->tv_nsec < 0 || abstime->tv_nsec >= 1000000000)  return EINVAL;  int pshared = (cond->__data.__mutex == (void *) ~0l)  ? LLL_SHARED : LLL_PRIVATE;  //1.获得cond锁  lll_lock (cond->__data.__lock, pshared);  //2.释放mutex锁  int err = __pthread_mutex_unlock_usercnt (mutex, 0);  if (err)  {  lll_unlock (cond->__data.__lock, pshared);  return err;  }  /* We have one new user of the condvar. */  //每执行一次wait(pthread_cond_timedwait/pthread_cond_wait),__total_seq就会+1  ++cond->__data.__total_seq;  //用来执行futex_wait的变量  ++cond->__data.__futex;  //标识该cond还有多少线程在使用,pthread_cond_destroy需要等待所有的操作完成  cond->__data.__nwaiters += 1 << COND_NWAITERS_SHIFT;  /* Remember the mutex we are using here. If there is already a  different address store this is a bad user bug. Do not store  anything for pshared condvars. */  //保存mutex锁  if (cond->__data.__mutex != (void *) ~0l)  cond->__data.__mutex = mutex;  /* Prepare structure passed to cancellation handler. */  cbuffer.cond = cond;  cbuffer.mutex = mutex;  /* Before we block we enable cancellation. Therefore we have to  install a cancellation handler. */  __pthread_cleanup_push (&buffer, __condvar_cleanup, &cbuffer);  /* The current values of the wakeup counter. The "woken" counter  must exceed this value. */  //记录futex_wait前的__wakeup_seq(为该cond上执行了多少次sign操作+timeout次数)和__broadcast_seq(代表在该cond上执行了多少次broadcast)  unsigned long long int val;  unsigned long long int seq;  val = seq = cond->__data.__wakeup_seq;  /* Remember the broadcast counter. */  cbuffer.bc_seq = cond->__data.__broadcast_seq;  while (1)  {  //3.计算要wait的相对时间  struct timespec rt;  { #ifdef __NR_clock_gettime  INTERNAL_SYSCALL_DECL (err);  int ret;  ret = INTERNAL_VSYSCALL (clock_gettime, err, 2,  (cond->__data.__nwaiters  & ((1 << COND_NWAITERS_SHIFT) - 1)),  &rt); # ifndef __ASSUME_POSIX_TIMERS  if (__builtin_expect (INTERNAL_SYSCALL_ERROR_P (ret, err), 0))  {  struct timeval tv;  (void) gettimeofday (&tv, NULL);  /* Convert the absolute timeout value to a relative timeout. */  rt.tv_sec = abstime->tv_sec - tv.tv_sec;  rt.tv_nsec = abstime->tv_nsec - tv.tv_usec * 1000;  }  else # endif  {  /* Convert the absolute timeout value to a relative timeout. */  rt.tv_sec = abstime->tv_sec - rt.tv_sec;  rt.tv_nsec = abstime->tv_nsec - rt.tv_nsec;  } #else  /* Get the current time. So far we support only one clock. */  struct timeval tv;  (void) gettimeofday (&tv, NULL);  /* Convert the absolute timeout value to a relative timeout. */  rt.tv_sec = abstime->tv_sec - tv.tv_sec;  rt.tv_nsec = abstime->tv_nsec - tv.tv_usec * 1000; #endif  }  if (rt.tv_nsec < 0)  {  rt.tv_nsec += 1000000000;  --rt.tv_sec;  }  /*---计算要wait的相对时间 end---- */  //是否超时  /* Did we already time out? */  if (__builtin_expect (rt.tv_sec < 0, 0))  {  //被broadcast唤醒,这里疑问的是,为什么不需要判断__wakeup_seq?  if (cbuffer.bc_seq != cond->__data.__broadcast_seq)  goto bc_out;  goto timeout;  }  unsigned int futex_val = cond->__data.__futex;  //4.释放cond锁,准备wait  lll_unlock (cond->__data.__lock, pshared);  /* Enable asynchronous cancellation. Required by the standard. */  cbuffer.oldtype = __pthread_enable_asynccancel ();  //5.调用futex_wait  /* Wait until woken by signal or broadcast. */  err = lll_futex_timed_wait (&cond->__data.__futex,  futex_val, &rt, pshared);  /* Disable asynchronous cancellation. */  __pthread_disable_asynccancel (cbuffer.oldtype);  //6.重新获得cond锁,因为又要访问&修改cond的数据了  lll_lock (cond->__data.__lock, pshared);  //__broadcast_seq值发生改变,代表发生了有线程调用了广播  if (cbuffer.bc_seq != cond->__data.__broadcast_seq)  goto bc_out;  //判断是否是被sign唤醒的,sign会增加__wakeup_seq  //第二个条件cond->__data.__woken_seq != val的意义在于  //可能两个线程A、B在wait,一个线程调用了sign导致A被唤醒,这时B因为超时被唤醒  //对于B线程来说,执行到这里时第一个条件也是满足的,从而导致上层拿到的result不是超时  //所以这里需要判断下__woken_seq(即该cond已经被唤醒的线程数)是否等于__wakeup_seq(sign执行次数+timeout次数)  val = cond->__data.__wakeup_seq;  if (val != seq && cond->__data.__woken_seq != val)  break;  /* Not woken yet. Maybe the time expired? */  if (__builtin_expect (err == -ETIMEDOUT, 0))  {  timeout:  /* Yep. Adjust the counters. */  ++cond->__data.__wakeup_seq;  ++cond->__data.__futex;  /* The error value. */  result = ETIMEDOUT;  break;  }  }  //一个线程已经醒了所以这里__woken_seq +1  ++cond->__data.__woken_seq;  bc_out:  //  cond->__data.__nwaiters -= 1 << COND_NWAITERS_SHIFT;  /* If pthread_cond_destroy was called on this variable already,  notify the pthread_cond_destroy caller all waiters have left  and it can be successfully destroyed. */  if (cond->__data.__total_seq == -1ULL  && cond->__data.__nwaiters < (1 << COND_NWAITERS_SHIFT))  lll_futex_wake (&cond->__data.__nwaiters, 1, pshared);  //9.cond数据修改完毕,释放锁  lll_unlock (cond->__data.__lock, pshared);  /* The cancellation handling is back to normal, remove the handler. */  __pthread_cleanup_pop (&buffer, 0);  //10.重新获得mutex锁  err = __pthread_mutex_cond_lock (mutex);  return err ?: result; } 
 (编辑:南平站长网) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |