Blame nptl/pthread_cond_signal.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 <atomic.h>
Packit 6c4009
#include <stdint.h>
Packit 6c4009
Packit 6c4009
#include <shlib-compat.h>
Packit 6c4009
#include <stap-probe.h>
Packit 6c4009
Packit 6c4009
#include "pthread_cond_common.c"
Packit 6c4009
Packit 6c4009
/* See __pthread_cond_wait for a high-level description of the algorithm.  */
Packit 6c4009
int
Packit 6c4009
__pthread_cond_signal (pthread_cond_t *cond)
Packit 6c4009
{
Packit 6c4009
  LIBC_PROBE (cond_signal, 1, cond);
Packit 6c4009
Packit 6c4009
  /* First check whether there are waiters.  Relaxed MO is fine for that for
Packit 6c4009
     the same reasons that relaxed MO is fine when observing __wseq (see
Packit 6c4009
     below).  */
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
  /* Load the waiter sequence number, which represents our relative ordering
Packit 6c4009
     to any waiters.  Relaxed MO is sufficient for that because:
Packit 6c4009
     1) We can pick any position that is allowed by external happens-before
Packit 6c4009
        constraints.  In particular, if another __pthread_cond_wait call
Packit 6c4009
        happened before us, this waiter must be eligible for being woken by
Packit 6c4009
        us.  The only way do establish such a happens-before is by signaling
Packit 6c4009
        while having acquired the mutex associated with the condvar and
Packit 6c4009
        ensuring that the signal's critical section happens after the waiter.
Packit 6c4009
        Thus, the mutex ensures that we see that waiter's __wseq increase.
Packit 6c4009
     2) Once we pick a position, we do not need to communicate this to the
Packit 6c4009
        program via a happens-before that we set up: First, any wake-up could
Packit 6c4009
        be a spurious wake-up, so the program must not interpret a wake-up as
Packit 6c4009
        an indication that the waiter happened before a particular signal;
Packit 6c4009
        second, a program cannot detect whether a waiter has not yet been
Packit 6c4009
        woken (i.e., it cannot distinguish between a non-woken waiter and one
Packit 6c4009
        that has been woken but hasn't resumed execution yet), and thus it
Packit 6c4009
        cannot try to deduce that a signal happened before a particular
Packit 6c4009
        waiter.  */
Packit 6c4009
  unsigned long long int wseq = __condvar_load_wseq_relaxed (cond);
Packit 6c4009
  unsigned int g1 = (wseq & 1) ^ 1;
Packit 6c4009
  wseq >>= 1;
Packit 6c4009
  bool do_futex_wake = false;
Packit 6c4009
Packit 6c4009
  /* If G1 is still receiving signals, we put the signal there.  If not, we
Packit 6c4009
     check if G2 has waiters, and if so, quiesce and switch G1 to the former
Packit 6c4009
     G2; if this results in a new G1 with waiters (G2 might have cancellations
Packit 6c4009
     already, see __condvar_quiesce_and_switch_g1), we put the signal in the
Packit 6c4009
     new G1.  */
Packit 6c4009
  if ((cond->__data.__g_size[g1] != 0)
Packit 6c4009
      || __condvar_quiesce_and_switch_g1 (cond, wseq, &g1, private))
Packit 6c4009
    {
Packit 6c4009
      /* Add a signal.  Relaxed MO is fine because signaling does not need to
Packit 6c4009
	 establish a happens-before relation (see above).  We do not mask the
Packit 6c4009
	 release-MO store when initializing a group in
Packit 6c4009
	 __condvar_quiesce_and_switch_g1 because we use an atomic
Packit 6c4009
	 read-modify-write and thus extend that store's release sequence.  */
Packit 6c4009
      atomic_fetch_add_relaxed (cond->__data.__g_signals + g1, 2);
Packit 6c4009
      cond->__data.__g_size[g1]--;
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, 1, private);
Packit 6c4009
Packit 6c4009
  return 0;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
versioned_symbol (libpthread, __pthread_cond_signal, pthread_cond_signal,
Packit 6c4009
		  GLIBC_2_3_2);