Blame nptl/pthread_cond_signal.c

Packit Service 82fcde
/* Copyright (C) 2003-2018 Free Software Foundation, Inc.
Packit Service 82fcde
   This file is part of the GNU C Library.
Packit Service 82fcde
   Contributed by Martin Schwidefsky <schwidefsky@de.ibm.com>, 2003.
Packit Service 82fcde
Packit Service 82fcde
   The GNU C Library is free software; you can redistribute it and/or
Packit Service 82fcde
   modify it under the terms of the GNU Lesser General Public
Packit Service 82fcde
   License as published by the Free Software Foundation; either
Packit Service 82fcde
   version 2.1 of the License, or (at your option) any later version.
Packit Service 82fcde
Packit Service 82fcde
   The GNU C Library is distributed in the hope that it will be useful,
Packit Service 82fcde
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit Service 82fcde
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the GNU
Packit Service 82fcde
   Lesser General Public License for more details.
Packit Service 82fcde
Packit Service 82fcde
   You should have received a copy of the GNU Lesser General Public
Packit Service 82fcde
   License along with the GNU C Library; if not, see
Packit Service 82fcde
   <http://www.gnu.org/licenses/>.  */
Packit Service 82fcde
Packit Service 82fcde
#include <endian.h>
Packit Service 82fcde
#include <errno.h>
Packit Service 82fcde
#include <sysdep.h>
Packit Service 82fcde
#include <futex-internal.h>
Packit Service 82fcde
#include <pthread.h>
Packit Service 82fcde
#include <pthreadP.h>
Packit Service 82fcde
#include <atomic.h>
Packit Service 82fcde
#include <stdint.h>
Packit Service 82fcde
Packit Service 82fcde
#include <shlib-compat.h>
Packit Service 82fcde
#include <stap-probe.h>
Packit Service 82fcde
Packit Service 82fcde
#include "pthread_cond_common.c"
Packit Service 82fcde
Packit Service 82fcde
/* See __pthread_cond_wait for a high-level description of the algorithm.  */
Packit Service 82fcde
int
Packit Service 82fcde
__pthread_cond_signal (pthread_cond_t *cond)
Packit Service 82fcde
{
Packit Service 82fcde
  LIBC_PROBE (cond_signal, 1, cond);
Packit Service 82fcde
Packit Service 82fcde
  /* First check whether there are waiters.  Relaxed MO is fine for that for
Packit Service 82fcde
     the same reasons that relaxed MO is fine when observing __wseq (see
Packit Service 82fcde
     below).  */
Packit Service 82fcde
  unsigned int wrefs = atomic_load_relaxed (&cond->__data.__wrefs);
Packit Service 82fcde
  if (wrefs >> 3 == 0)
Packit Service 82fcde
    return 0;
Packit Service 82fcde
  int private = __condvar_get_private (wrefs);
Packit Service 82fcde
Packit Service 82fcde
  __condvar_acquire_lock (cond, private);
Packit Service 82fcde
Packit Service 82fcde
  /* Load the waiter sequence number, which represents our relative ordering
Packit Service 82fcde
     to any waiters.  Relaxed MO is sufficient for that because:
Packit Service 82fcde
     1) We can pick any position that is allowed by external happens-before
Packit Service 82fcde
        constraints.  In particular, if another __pthread_cond_wait call
Packit Service 82fcde
        happened before us, this waiter must be eligible for being woken by
Packit Service 82fcde
        us.  The only way do establish such a happens-before is by signaling
Packit Service 82fcde
        while having acquired the mutex associated with the condvar and
Packit Service 82fcde
        ensuring that the signal's critical section happens after the waiter.
Packit Service 82fcde
        Thus, the mutex ensures that we see that waiter's __wseq increase.
Packit Service 82fcde
     2) Once we pick a position, we do not need to communicate this to the
Packit Service 82fcde
        program via a happens-before that we set up: First, any wake-up could
Packit Service 82fcde
        be a spurious wake-up, so the program must not interpret a wake-up as
Packit Service 82fcde
        an indication that the waiter happened before a particular signal;
Packit Service 82fcde
        second, a program cannot detect whether a waiter has not yet been
Packit Service 82fcde
        woken (i.e., it cannot distinguish between a non-woken waiter and one
Packit Service 82fcde
        that has been woken but hasn't resumed execution yet), and thus it
Packit Service 82fcde
        cannot try to deduce that a signal happened before a particular
Packit Service 82fcde
        waiter.  */
Packit Service 82fcde
  unsigned long long int wseq = __condvar_load_wseq_relaxed (cond);
Packit Service 82fcde
  unsigned int g1 = (wseq & 1) ^ 1;
Packit Service 82fcde
  wseq >>= 1;
Packit Service 82fcde
  bool do_futex_wake = false;
Packit Service 82fcde
Packit Service 82fcde
  /* If G1 is still receiving signals, we put the signal there.  If not, we
Packit Service 82fcde
     check if G2 has waiters, and if so, quiesce and switch G1 to the former
Packit Service 82fcde
     G2; if this results in a new G1 with waiters (G2 might have cancellations
Packit Service 82fcde
     already, see __condvar_quiesce_and_switch_g1), we put the signal in the
Packit Service 82fcde
     new G1.  */
Packit Service 82fcde
  if ((cond->__data.__g_size[g1] != 0)
Packit Service 82fcde
      || __condvar_quiesce_and_switch_g1 (cond, wseq, &g1, private))
Packit Service 82fcde
    {
Packit Service 82fcde
      /* Add a signal.  Relaxed MO is fine because signaling does not need to
Packit Service 82fcde
	 establish a happens-before relation (see above).  We do not mask the
Packit Service 82fcde
	 release-MO store when initializing a group in
Packit Service 82fcde
	 __condvar_quiesce_and_switch_g1 because we use an atomic
Packit Service 82fcde
	 read-modify-write and thus extend that store's release sequence.  */
Packit Service 82fcde
      atomic_fetch_add_relaxed (cond->__data.__g_signals + g1, 2);
Packit Service 82fcde
      cond->__data.__g_size[g1]--;
Packit Service 82fcde
      /* TODO Only set it if there are indeed futex waiters.  */
Packit Service 82fcde
      do_futex_wake = true;
Packit Service 82fcde
    }
Packit Service 82fcde
Packit Service 82fcde
  __condvar_release_lock (cond, private);
Packit Service 82fcde
Packit Service 82fcde
  if (do_futex_wake)
Packit Service 82fcde
    futex_wake (cond->__data.__g_signals + g1, 1, private);
Packit Service 82fcde
Packit Service 82fcde
  return 0;
Packit Service 82fcde
}
Packit Service 82fcde
Packit Service 82fcde
versioned_symbol (libpthread, __pthread_cond_signal, pthread_cond_signal,
Packit Service 82fcde
		  GLIBC_2_3_2);