hjl / source-git / glibc

Forked from source-git/glibc 3 years ago
Clone

Blame nptl/pthread_cond_broadcast.c

Packit 6c4009
/* Copyright (C) 2003-2018 Free Software Foundation, Inc.
Packit 6c4009
   This file is part of the GNU C Library.
Packit 6c4009
   Contributed by Martin Schwidefsky <schwidefsky@de.ibm.com>, 2003.
Packit 6c4009
Packit 6c4009
   The GNU C Library is free software; you can redistribute it and/or
Packit 6c4009
   modify it under the terms of the GNU Lesser General Public
Packit 6c4009
   License as published by the Free Software Foundation; either
Packit 6c4009
   version 2.1 of the License, or (at your option) any later version.
Packit 6c4009
Packit 6c4009
   The GNU C Library is distributed in the hope that it will be useful,
Packit 6c4009
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 6c4009
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the GNU
Packit 6c4009
   Lesser General Public License for more details.
Packit 6c4009
Packit 6c4009
   You should have received a copy of the GNU Lesser General Public
Packit 6c4009
   License along with the GNU C Library; if not, see
Packit 6c4009
   <http://www.gnu.org/licenses/>.  */
Packit 6c4009
Packit 6c4009
#include <endian.h>
Packit 6c4009
#include <errno.h>
Packit 6c4009
#include <sysdep.h>
Packit 6c4009
#include <futex-internal.h>
Packit 6c4009
#include <pthread.h>
Packit 6c4009
#include <pthreadP.h>
Packit 6c4009
#include <stap-probe.h>
Packit 6c4009
#include <atomic.h>
Packit 6c4009
Packit 6c4009
#include <shlib-compat.h>
Packit 6c4009
Packit 6c4009
#include "pthread_cond_common.c"
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* We do the following steps from __pthread_cond_signal in one critical
Packit 6c4009
   section: (1) signal all waiters in G1, (2) close G1 so that it can become
Packit 6c4009
   the new G2 and make G2 the new G1, and (3) signal all waiters in the new
Packit 6c4009
   G1.  We don't need to do all these steps if there are no waiters in G1
Packit 6c4009
   and/or G2.  See __pthread_cond_signal for further details.  */
Packit 6c4009
int
Packit 6c4009
__pthread_cond_broadcast (pthread_cond_t *cond)
Packit 6c4009
{
Packit 6c4009
  LIBC_PROBE (cond_broadcast, 1, cond);
Packit 6c4009
Packit 6c4009
  unsigned int wrefs = atomic_load_relaxed (&cond->__data.__wrefs);
Packit 6c4009
  if (wrefs >> 3 == 0)
Packit 6c4009
    return 0;
Packit 6c4009
  int private = __condvar_get_private (wrefs);
Packit 6c4009
Packit 6c4009
  __condvar_acquire_lock (cond, private);
Packit 6c4009
Packit 6c4009
  unsigned long long int wseq = __condvar_load_wseq_relaxed (cond);
Packit 6c4009
  unsigned int g2 = wseq & 1;
Packit 6c4009
  unsigned int g1 = g2 ^ 1;
Packit 6c4009
  wseq >>= 1;
Packit 6c4009
  bool do_futex_wake = false;
Packit 6c4009
Packit 6c4009
  /* Step (1): signal all waiters remaining in G1.  */
Packit 6c4009
  if (cond->__data.__g_size[g1] != 0)
Packit 6c4009
    {
Packit 6c4009
      /* Add as many signals as the remaining size of the group.  */
Packit 6c4009
      atomic_fetch_add_relaxed (cond->__data.__g_signals + g1,
Packit 6c4009
				cond->__data.__g_size[g1] << 1);
Packit 6c4009
      cond->__data.__g_size[g1] = 0;
Packit 6c4009
Packit 6c4009
      /* We need to wake G1 waiters before we quiesce G1 below.  */
Packit 6c4009
      /* TODO Only set it if there are indeed futex waiters.  We could
Packit 6c4009
	 also try to move this out of the critical section in cases when
Packit 6c4009
	 G2 is empty (and we don't need to quiesce).  */
Packit 6c4009
      futex_wake (cond->__data.__g_signals + g1, INT_MAX, private);
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  /* G1 is complete.  Step (2) is next unless there are no waiters in G2, in
Packit 6c4009
     which case we can stop.  */
Packit 6c4009
  if (__condvar_quiesce_and_switch_g1 (cond, wseq, &g1, private))
Packit 6c4009
    {
Packit 6c4009
      /* Step (3): Send signals to all waiters in the old G2 / new G1.  */
Packit 6c4009
      atomic_fetch_add_relaxed (cond->__data.__g_signals + g1,
Packit 6c4009
				cond->__data.__g_size[g1] << 1);
Packit 6c4009
      cond->__data.__g_size[g1] = 0;
Packit 6c4009
      /* TODO Only set it if there are indeed futex waiters.  */
Packit 6c4009
      do_futex_wake = true;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  __condvar_release_lock (cond, private);
Packit 6c4009
Packit 6c4009
  if (do_futex_wake)
Packit 6c4009
    futex_wake (cond->__data.__g_signals + g1, INT_MAX, private);
Packit 6c4009
Packit 6c4009
  return 0;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
versioned_symbol (libpthread, __pthread_cond_broadcast, pthread_cond_broadcast,
Packit 6c4009
		  GLIBC_2_3_2);