Blame nptl/tst-rwlock-trywrlock-stall.c

Packit Service 2ead18
/* Bug 23844: Test for pthread_rwlock_trywrlock stalls.
Packit Service 2ead18
   Copyright (C) 2019 Free Software Foundation, Inc.
Packit Service 2ead18
   This file is part of the GNU C Library.
Packit Service 2ead18
Packit Service 2ead18
   The GNU C Library is free software; you can redistribute it and/or
Packit Service 2ead18
   modify it under the terms of the GNU Lesser General Public
Packit Service 2ead18
   License as published by the Free Software Foundation; either
Packit Service 2ead18
   version 2.1 of the License, or (at your option) any later version.
Packit Service 2ead18
Packit Service 2ead18
   The GNU C Library is distributed in the hope that it will be useful,
Packit Service 2ead18
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit Service 2ead18
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit Service 2ead18
   Lesser General Public License for more details.
Packit Service 2ead18
Packit Service 2ead18
   You should have received a copy of the GNU Lesser General Public
Packit Service 2ead18
   License along with the GNU C Library; if not, see
Packit Service 2ead18
   <http://www.gnu.org/licenses/>.  */
Packit Service 2ead18
Packit Service 2ead18
/* For a full analysis see comments in tst-rwlock-tryrdlock-stall.c.
Packit Service 2ead18
Packit Service 2ead18
   Summary for the pthread_rwlock_trywrlock() stall:
Packit Service 2ead18
Packit Service 2ead18
   The stall is caused by pthread_rwlock_trywrlock setting
Packit Service 2ead18
   __wrphase_futex futex to 1 and loosing the
Packit Service 2ead18
   PTHREAD_RWLOCK_FUTEX_USED bit.
Packit Service 2ead18
Packit Service 2ead18
   The fix for bug 23844 ensures that waiters on __wrphase_futex are
Packit Service 2ead18
   correctly woken.  Before the fix the test stalls as readers can
Packit Service 2ead18
   wait forever on  __wrphase_futex.  */
Packit Service 2ead18
Packit Service 2ead18
#include <stdio.h>
Packit Service 2ead18
#include <stdlib.h>
Packit Service 2ead18
#include <unistd.h>
Packit Service 2ead18
#include <pthread.h>
Packit Service 2ead18
#include <support/xthread.h>
Packit Service 2ead18
#include <errno.h>
Packit Service 2ead18
Packit Service 2ead18
/* We need only one lock to reproduce the issue. We will need multiple
Packit Service 2ead18
   threads to get the exact case where we have a read, try, and unlock
Packit Service 2ead18
   all interleaving to produce the case where the readers are waiting
Packit Service 2ead18
   and the try clears the PTHREAD_RWLOCK_FUTEX_USED bit and a
Packit Service 2ead18
   subsequent unlock fails to wake them.  */
Packit Service 2ead18
pthread_rwlock_t onelock;
Packit Service 2ead18
Packit Service 2ead18
/* The number of threads is arbitrary but empirically chosen to have
Packit Service 2ead18
   enough threads that we see the condition where waiting readers are
Packit Service 2ead18
   not woken by a successful unlock.  */
Packit Service 2ead18
#define NTHREADS 32
Packit Service 2ead18
Packit Service 2ead18
_Atomic int do_exit;
Packit Service 2ead18
Packit Service 2ead18
void *
Packit Service 2ead18
run_loop (void *arg)
Packit Service 2ead18
{
Packit Service 2ead18
  int i = 0, ret;
Packit Service 2ead18
  while (!do_exit)
Packit Service 2ead18
    {
Packit Service 2ead18
      /* Arbitrarily choose if we are the writer or reader.  Choose a
Packit Service 2ead18
	 high enough ratio of readers to writers to make it likely
Packit Service 2ead18
	 that readers block (and eventually are susceptable to
Packit Service 2ead18
	 stalling).
Packit Service 2ead18
Packit Service 2ead18
         If we are a writer, take the write lock, and then unlock.
Packit Service 2ead18
	 If we are a reader, try the lock, then lock, then unlock.  */
Packit Service 2ead18
      if ((i % 8) != 0)
Packit Service 2ead18
	{
Packit Service 2ead18
	  if ((ret = pthread_rwlock_trywrlock (&onelock)) != 0)
Packit Service 2ead18
	    {
Packit Service 2ead18
	      if (ret == EBUSY)
Packit Service 2ead18
		xpthread_rwlock_wrlock (&onelock);
Packit Service 2ead18
	      else
Packit Service 2ead18
		exit (EXIT_FAILURE);
Packit Service 2ead18
	    }
Packit Service 2ead18
	}
Packit Service 2ead18
      else
Packit Service 2ead18
	xpthread_rwlock_rdlock (&onelock);
Packit Service 2ead18
      /* Thread does some work and then unlocks.  */
Packit Service 2ead18
      xpthread_rwlock_unlock (&onelock);
Packit Service 2ead18
      i++;
Packit Service 2ead18
    }
Packit Service 2ead18
  return NULL;
Packit Service 2ead18
}
Packit Service 2ead18
Packit Service 2ead18
int
Packit Service 2ead18
do_test (void)
Packit Service 2ead18
{
Packit Service 2ead18
  int i;
Packit Service 2ead18
  pthread_t tids[NTHREADS];
Packit Service 2ead18
  xpthread_rwlock_init (&onelock, NULL);
Packit Service 2ead18
  for (i = 0; i < NTHREADS; i++)
Packit Service 2ead18
    tids[i] = xpthread_create (NULL, run_loop, NULL);
Packit Service 2ead18
  /* Run for some amount of time.  The pthread_rwlock_tryrwlock stall
Packit Service 2ead18
     is very easy to trigger and happens in seconds under the test
Packit Service 2ead18
     conditions.  */
Packit Service 2ead18
  sleep (10);
Packit Service 2ead18
  /* Then exit.  */
Packit Service 2ead18
  printf ("INFO: Exiting...\n");
Packit Service 2ead18
  do_exit = 1;
Packit Service 2ead18
  /* If any readers stalled then we will timeout waiting for them.  */
Packit Service 2ead18
  for (i = 0; i < NTHREADS; i++)
Packit Service 2ead18
    xpthread_join (tids[i]);
Packit Service 2ead18
  printf ("INFO: Done.\n");
Packit Service 2ead18
  xpthread_rwlock_destroy (&onelock);
Packit Service 2ead18
  printf ("PASS: No pthread_rwlock_tryrwlock stalls detected.\n");
Packit Service 2ead18
  return 0;
Packit Service 2ead18
}
Packit Service 2ead18
Packit Service 2ead18
#include <support/test-driver.c>