hjl / source-git / glibc

Forked from source-git/glibc 3 years ago
Clone

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

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