sem_wait

int
__new_sem_wait (sem_t *sem)
{
  /* We need to check whether we need to act upon a cancellation request here
     because POSIX specifies that cancellation points "shall occur" in
     sem_wait and sem_timedwait, which also means that they need to check
     this regardless whether they block or not (unlike "may occur"
     functions).  See the POSIX Rationale for this requirement: Section
     "Thread Cancellation Overview" [1] and austin group issue #1076 [2]
     for thoughs on why this may be a suboptimal design.

     [1] http://pubs.opengroup.org/onlinepubs/9699919799/xrat/V4_xsh_chap02.html
     [2] http://austingroupbugs.net/view.php?id=1076 for thoughts on why this
   */
  __pthread_testcancel ();

  if (__new_sem_wait_fast ((struct new_sem *) sem, 0) == 0)
    return 0;
  else
    return __new_sem_wait_slow64 ((struct new_sem *) sem,
                  CLOCK_REALTIME, NULL);
}
versioned_symbol (libc, __new_sem_wait, sem_wait, GLIBC_2_34);

/* Fast path: Try to grab a token without blocking.  */
static int
__new_sem_wait_fast (struct new_sem *sem, int definitive_result)
{
  /* We need acquire MO if we actually grab a token, so that this
     synchronizes with all token providers (i.e., the RMW operation we read
     from or all those before it in modification order; also see sem_post).
     We do not need to guarantee any ordering if we observed that there is
     no token (POSIX leaves it unspecified whether functions that fail
     synchronize memory); thus, relaxed MO is sufficient for the initial load
     and the failure path of the CAS.  If the weak CAS fails and we need a
     definitive result, retry.  */
#if __HAVE_64B_ATOMICS
  uint64_t d = atomic_load_relaxed (&sem->data);
  do
    {
      if ((d & SEM_VALUE_MASK) == 0)
    break;
      if (atomic_compare_exchange_weak_acquire (&sem->data, &d, d - 1))
    return 0;
    }
  while (definitive_result);
  return -1;
#else
  unsigned int v = atomic_load_relaxed (&sem->value);
  do
    {
      if ((v >> SEM_VALUE_SHIFT) == 0)
    break;
      if (atomic_compare_exchange_weak_acquire (&sem->value,
      &v, v - (1 << SEM_VALUE_SHIFT)))
    return 0;
    }
  while (definitive_result);
  return -1;
#endif
}

/* Slow path that blocks.  */
static int
__attribute__ ((noinline))
__new_sem_wait_slow64 (struct new_sem *sem, clockid_t clockid,
               const struct __timespec64 *abstime)
{
  int err = 0;

#if __HAVE_64B_ATOMICS
  /* Add a waiter.  Relaxed MO is sufficient because we can rely on the
     ordering provided by the RMW operations we use.  */
  uint64_t d = atomic_fetch_add_relaxed (&sem->data,
      (uint64_t) 1 << SEM_NWAITERS_SHIFT);

  pthread_cleanup_push (__sem_wait_cleanup, sem);

  /* Wait for a token to be available.  Retry until we can grab one.  */
  for (;;)
    {
      /* If there is no token available, sleep until there is.  */
      if ((d & SEM_VALUE_MASK) == 0)
    {
      err = do_futex_wait (sem, clockid, abstime);
      /* A futex return value of 0 or EAGAIN is due to a real or spurious
         wake-up, or due to a change in the number of tokens.  We retry in
         these cases.
         If we timed out, forward this to the caller.
         EINTR is returned if we are interrupted by a signal; we
         forward this to the caller.  (See futex_wait and related
         documentation.  Before Linux 2.6.22, EINTR was also returned on
         spurious wake-ups; we only support more recent Linux versions,
         so do not need to consider this here.)  */
      if (err == ETIMEDOUT || err == EINTR || err == EOVERFLOW)
        {
          __set_errno (err);
          err = -1;
          /* Stop being registered as a waiter.  */
          atomic_fetch_add_relaxed (&sem->data,
          -((uint64_t) 1 << SEM_NWAITERS_SHIFT));
          break;
        }
      /* Relaxed MO is sufficient; see below.  */
      d = atomic_load_relaxed (&sem->data);
    }
      else
    {
      /* Try to grab both a token and stop being a waiter.  We need
         acquire MO so this synchronizes with all token providers (i.e.,
         the RMW operation we read from or all those before it in
         modification order; also see sem_post).  On the failure path,
         relaxed MO is sufficient because we only eventually need the
         up-to-date value; the futex_wait or the CAS perform the real
         work.  */
      if (atomic_compare_exchange_weak_acquire (&sem->data,
          &d, d - 1 - ((uint64_t) 1 << SEM_NWAITERS_SHIFT)))
        {
          err = 0;
          break;
        }
    }
    }

  pthread_cleanup_pop (0);
#else
  /* The main difference to the 64b-atomics implementation is that we need to
     access value and nwaiters in separate steps, and that the nwaiters bit
     in the value can temporarily not be set even if nwaiters is nonzero.
     We work around incorrectly unsetting the nwaiters bit by letting sem_wait
     set the bit again and waking the number of waiters that could grab a
     token.  There are two additional properties we need to ensure:
     (1) We make sure that whenever unsetting the bit, we see the increment of
     nwaiters by the other thread that set the bit.  IOW, we will notice if
     we make a mistake.
     (2) When setting the nwaiters bit, we make sure that we see the unsetting
     of the bit by another waiter that happened before us.  This avoids having
     to blindly set the bit whenever we need to block on it.  We set/unset
     the bit while having incremented nwaiters (i.e., are a registered
     waiter), and the problematic case only happens when one waiter indeed
     followed another (i.e., nwaiters was never larger than 1); thus, this
     works similarly as with a critical section using nwaiters (see the MOs
     and related comments below).

     An alternative approach would be to unset the bit after decrementing
     nwaiters; however, that would result in needing Dekker-like
     synchronization and thus full memory barriers.  We also would not be able
     to prevent misspeculation, so this alternative scheme does not seem
     beneficial.  */
  unsigned int v;

  /* Add a waiter.  We need acquire MO so this synchronizes with the release
     MO we use when decrementing nwaiters below; it ensures that if another
     waiter unset the bit before us, we see that and set it again.  Also see
     property (2) above.  */
  atomic_fetch_add_acquire (&sem->nwaiters, 1);

  pthread_cleanup_push (__sem_wait_cleanup, sem);

  /* Wait for a token to be available.  Retry until we can grab one.  */
  /* We do not need any ordering wrt. to this load's reads-from, so relaxed
     MO is sufficient.  The acquire MO above ensures that in the problematic
     case, we do see the unsetting of the bit by another waiter.  */
  v = atomic_load_relaxed (&sem->value);
  do
    {
      do
    {
      /* We are about to block, so make sure that the nwaiters bit is
         set.  We need release MO on the CAS to ensure that when another
         waiter unsets the nwaiters bit, it will also observe that we
         incremented nwaiters in the meantime (also see the unsetting of
         the bit below).  Relaxed MO on CAS failure is sufficient (see
         above).  */
      do
        {
          if ((v & SEM_NWAITERS_MASK) != 0)
        break;
        }
      while (!atomic_compare_exchange_weak_release (&sem->value,
          &v, v | SEM_NWAITERS_MASK));
      /* If there is no token, wait.  */
      if ((v >> SEM_VALUE_SHIFT) == 0)
        {
          /* See __HAVE_64B_ATOMICS variant.  */
          err = do_futex_wait (sem, clockid, abstime);
          if (err == ETIMEDOUT || err == EINTR)
        {
          __set_errno (err);
          err = -1;
          goto error;
        }
          err = 0;
          /* We blocked, so there might be a token now.  Relaxed MO is
         sufficient (see above).  */
          v = atomic_load_relaxed (&sem->value);
        }
    }
      /* If there is no token, we must not try to grab one.  */
      while ((v >> SEM_VALUE_SHIFT) == 0);
    }
  /* Try to grab a token.  We need acquire MO so this synchronizes with
     all token providers (i.e., the RMW operation we read from or all those
     before it in modification order; also see sem_post).  */
  while (!atomic_compare_exchange_weak_acquire (&sem->value,
      &v, v - (1 << SEM_VALUE_SHIFT)));

error:
  pthread_cleanup_pop (0);

  __sem_wait_32_finish (sem);
#endif

  return err;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
`sem_wait()` 是 POSIX 信号量的一个函数,用于等待一个信号量变为可用。如果该信号量当前不可用,则 `sem_wait()` 将阻塞调用它的线程,直到该信号量可用为止。 下面是一个 `sem_wait()` 的示例: ```c #include <stdio.h> #include <stdlib.h> #include <pthread.h> #include <semaphore.h> #define MAX_ITEMS 5 int buffer[MAX_ITEMS]; int fill = 0; int use = 0; sem_t empty_sem; sem_t full_sem; pthread_mutex_t buffer_mutex; void put(int value) { buffer[fill] = value; fill = (fill + 1) % MAX_ITEMS; } int get() { int tmp = buffer[use]; use = (use + 1) % MAX_ITEMS; return tmp; } void *producer(void *arg) { int i; for (i = 0; i < MAX_ITEMS * 2; i++) { sem_wait(&empty_sem); pthread_mutex_lock(&buffer_mutex); put(i); pthread_mutex_unlock(&buffer_mutex); sem_post(&full_sem); } return NULL; } void *consumer(void *arg) { int i, tmp = 0; while (tmp != MAX_ITEMS * 2 - 1) { sem_wait(&full_sem); pthread_mutex_lock(&buffer_mutex); tmp = get(); printf("Got: %d\n", tmp); pthread_mutex_unlock(&buffer_mutex); sem_post(&empty_sem); } return NULL; } int main(int argc, char *argv[]) { pthread_t prod, cons; sem_init(&empty_sem, 0, MAX_ITEMS); sem_init(&full_sem, 0, 0); pthread_mutex_init(&buffer_mutex, NULL); pthread_create(&prod, NULL, producer, NULL); pthread_create(&cons, NULL, consumer, NULL); pthread_join(prod, NULL); pthread_join(cons, NULL); sem_destroy(&empty_sem); sem_destroy(&full_sem); pthread_mutex_destroy(&buffer_mutex); return 0; } ``` 这个示例演示了如何使用 `sem_wait()` 和 `sem_post()` 函数来实现线程之间的同步。在这个示例中,生产者线程往缓冲区中写入数据,消费者线程从缓冲区中读取数据,通过使用信号量和互斥锁来保证线程之间的同步。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值