root/maint/gnulib/tests/test-pthread-once2.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. once_execute
  2. once_contender_thread
  3. test_once
  4. main
  5. main

   1 /* Test of once-only execution in multithreaded situations.
   2    Copyright (C) 2005, 2008-2021 Free Software Foundation, Inc.
   3 
   4    This program is free software: you can redistribute it and/or modify
   5    it under the terms of the GNU General Public License as published by
   6    the Free Software Foundation; either version 3 of the License, or
   7    (at your option) any later version.
   8 
   9    This program is distributed in the hope that it will be useful,
  10    but WITHOUT ANY WARRANTY; without even the implied warranty of
  11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12    GNU General Public License for more details.
  13 
  14    You should have received a copy of the GNU General Public License
  15    along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
  16 
  17 /* Written by Bruno Haible <bruno@clisp.org>, 2005.  */
  18 
  19 #include <config.h>
  20 
  21 #if USE_ISOC_THREADS || USE_POSIX_THREADS || USE_ISOC_AND_POSIX_THREADS || USE_WINDOWS_THREADS
  22 
  23 /* Whether to enable locking.
  24    Uncomment this to get a test program without locking, to verify that
  25    it crashes.  */
  26 #define ENABLE_LOCKING 1
  27 
  28 /* Whether to help the scheduler through explicit sched_yield().
  29    Uncomment this to see if the operating system has a fair scheduler.  */
  30 #define EXPLICIT_YIELD 1
  31 
  32 /* Whether to print debugging messages.  */
  33 #define ENABLE_DEBUGGING 0
  34 
  35 /* Number of simultaneous threads.  */
  36 #define THREAD_COUNT 10
  37 
  38 /* Number of operations performed in each thread.
  39    This is quite high, because with a smaller count, say 5000, we often get
  40    an "OK" result even without ENABLE_LOCKING (on Linux/x86).  */
  41 #define REPEAT_COUNT 50000
  42 
  43 #include <pthread.h>
  44 #include <stdint.h>
  45 #include <stdio.h>
  46 #include <stdlib.h>
  47 #include <string.h>
  48 
  49 #if EXPLICIT_YIELD
  50 # include <sched.h>
  51 #endif
  52 
  53 #if HAVE_DECL_ALARM
  54 # include <signal.h>
  55 # include <unistd.h>
  56 #endif
  57 
  58 #include "macros.h"
  59 
  60 #if ENABLE_DEBUGGING
  61 # define dbgprintf printf
  62 #else
  63 # define dbgprintf if (0) printf
  64 #endif
  65 
  66 #if EXPLICIT_YIELD
  67 # define yield() sched_yield ()
  68 #else
  69 # define yield()
  70 #endif
  71 
  72 /* Returns a reference to the current thread as a pointer, for debugging.  */
  73 #if defined __MVS__
  74   /* On IBM z/OS, pthread_t is a struct with an 8-byte '__' field.
  75      The first three bytes of this field appear to uniquely identify a
  76      pthread_t, though not necessarily representing a pointer.  */
  77 # define pthread_self_pointer() (*((void **) pthread_self ().__))
  78 #else
  79 # define pthread_self_pointer() ((void *) (uintptr_t) pthread_self ())
  80 #endif
  81 
  82 
  83 /* ------------------------ Test once-only execution ------------------------ */
  84 
  85 /* Test once-only execution by having several threads attempt to grab a
  86    once-only task simultaneously (triggered by releasing a read-write lock).  */
  87 
  88 static pthread_once_t fresh_once = PTHREAD_ONCE_INIT;
  89 static int ready[THREAD_COUNT];
  90 static pthread_mutex_t ready_lock[THREAD_COUNT];
  91 #if ENABLE_LOCKING
  92 static pthread_rwlock_t fire_signal[REPEAT_COUNT];
  93 #else
  94 static volatile int fire_signal_state;
  95 #endif
  96 static pthread_once_t once_control;
  97 static int performed;
  98 static pthread_mutex_t performed_lock;
  99 
 100 static void
 101 once_execute (void)
     /* [previous][next][first][last][top][bottom][index][help] */
 102 {
 103   ASSERT (pthread_mutex_lock (&performed_lock) == 0);
 104   performed++;
 105   ASSERT (pthread_mutex_unlock (&performed_lock) == 0);
 106 }
 107 
 108 static void *
 109 once_contender_thread (void *arg)
     /* [previous][next][first][last][top][bottom][index][help] */
 110 {
 111   int id = (int) (intptr_t) arg;
 112   int repeat;
 113 
 114   for (repeat = 0; repeat <= REPEAT_COUNT; repeat++)
 115     {
 116       /* Tell the main thread that we're ready.  */
 117       ASSERT (pthread_mutex_lock (&ready_lock[id]) == 0);
 118       ready[id] = 1;
 119       ASSERT (pthread_mutex_unlock (&ready_lock[id]) == 0);
 120 
 121       if (repeat == REPEAT_COUNT)
 122         break;
 123 
 124       dbgprintf ("Contender %p waiting for signal for round %d\n",
 125                  pthread_self_pointer (), repeat);
 126 #if ENABLE_LOCKING
 127       /* Wait for the signal to go.  */
 128       ASSERT (pthread_rwlock_rdlock (&fire_signal[repeat]) == 0);
 129       /* And don't hinder the others (if the scheduler is unfair).  */
 130       ASSERT (pthread_rwlock_unlock (&fire_signal[repeat]) == 0);
 131 #else
 132       /* Wait for the signal to go.  */
 133       while (fire_signal_state <= repeat)
 134         yield ();
 135 #endif
 136       dbgprintf ("Contender %p got the     signal for round %d\n",
 137                  pthread_self_pointer (), repeat);
 138 
 139       /* Contend for execution.  */
 140       ASSERT (pthread_once (&once_control, once_execute) == 0);
 141     }
 142 
 143   return NULL;
 144 }
 145 
 146 static void
 147 test_once (void)
     /* [previous][next][first][last][top][bottom][index][help] */
 148 {
 149   int i, repeat;
 150   pthread_t threads[THREAD_COUNT];
 151 
 152   /* Initialize all variables.  */
 153   for (i = 0; i < THREAD_COUNT; i++)
 154     {
 155       pthread_mutexattr_t attr;
 156 
 157       ready[i] = 0;
 158       ASSERT (pthread_mutexattr_init (&attr) == 0);
 159       ASSERT (pthread_mutexattr_settype (&attr, PTHREAD_MUTEX_NORMAL) == 0);
 160       ASSERT (pthread_mutex_init (&ready_lock[i], &attr) == 0);
 161       ASSERT (pthread_mutexattr_destroy (&attr) == 0);
 162     }
 163 #if ENABLE_LOCKING
 164   for (i = 0; i < REPEAT_COUNT; i++)
 165     ASSERT (pthread_rwlock_init (&fire_signal[i], NULL) == 0);
 166 #else
 167   fire_signal_state = 0;
 168 #endif
 169 
 170 #if ENABLE_LOCKING
 171   /* Block all fire_signals.  */
 172   for (i = REPEAT_COUNT-1; i >= 0; i--)
 173     ASSERT (pthread_rwlock_wrlock (&fire_signal[i]) == 0);
 174 #endif
 175 
 176   /* Spawn the threads.  */
 177   for (i = 0; i < THREAD_COUNT; i++)
 178     ASSERT (pthread_create (&threads[i], NULL,
 179                             once_contender_thread, (void *) (intptr_t) i)
 180             == 0);
 181 
 182   for (repeat = 0; repeat <= REPEAT_COUNT; repeat++)
 183     {
 184       /* Wait until every thread is ready.  */
 185       dbgprintf ("Main thread before synchronizing for round %d\n", repeat);
 186       for (;;)
 187         {
 188           int ready_count = 0;
 189           for (i = 0; i < THREAD_COUNT; i++)
 190             {
 191               ASSERT (pthread_mutex_lock (&ready_lock[i]) == 0);
 192               ready_count += ready[i];
 193               ASSERT (pthread_mutex_unlock (&ready_lock[i]) == 0);
 194             }
 195           if (ready_count == THREAD_COUNT)
 196             break;
 197           yield ();
 198         }
 199       dbgprintf ("Main thread after  synchronizing for round %d\n", repeat);
 200 
 201       if (repeat > 0)
 202         {
 203           /* Check that exactly one thread executed the once_execute()
 204              function.  */
 205           if (performed != 1)
 206             abort ();
 207         }
 208 
 209       if (repeat == REPEAT_COUNT)
 210         break;
 211 
 212       /* Preparation for the next round: Initialize once_control.  */
 213       memcpy (&once_control, &fresh_once, sizeof (pthread_once_t));
 214 
 215       /* Preparation for the next round: Reset the performed counter.  */
 216       performed = 0;
 217 
 218       /* Preparation for the next round: Reset the ready flags.  */
 219       for (i = 0; i < THREAD_COUNT; i++)
 220         {
 221           ASSERT (pthread_mutex_lock (&ready_lock[i]) == 0);
 222           ready[i] = 0;
 223           ASSERT (pthread_mutex_unlock (&ready_lock[i]) == 0);
 224         }
 225 
 226       /* Signal all threads simultaneously.  */
 227       dbgprintf ("Main thread giving signal for round %d\n", repeat);
 228 #if ENABLE_LOCKING
 229       ASSERT (pthread_rwlock_unlock (&fire_signal[repeat]) == 0);
 230 #else
 231       fire_signal_state = repeat + 1;
 232 #endif
 233     }
 234 
 235   /* Wait for the threads to terminate.  */
 236   for (i = 0; i < THREAD_COUNT; i++)
 237     ASSERT (pthread_join (threads[i], NULL) == 0);
 238 }
 239 
 240 
 241 /* -------------------------------------------------------------------------- */
 242 
 243 int
 244 main ()
     /* [previous][next][first][last][top][bottom][index][help] */
 245 {
 246 #if HAVE_DECL_ALARM
 247   /* Declare failure if test takes too long, by using default abort
 248      caused by SIGALRM.  */
 249   int alarm_value = 600;
 250   signal (SIGALRM, SIG_DFL);
 251   alarm (alarm_value);
 252 #endif
 253 
 254   {
 255     pthread_mutexattr_t attr;
 256 
 257     ASSERT (pthread_mutexattr_init (&attr) == 0);
 258     ASSERT (pthread_mutexattr_settype (&attr, PTHREAD_MUTEX_NORMAL) == 0);
 259     ASSERT (pthread_mutex_init (&performed_lock, &attr) == 0);
 260     ASSERT (pthread_mutexattr_destroy (&attr) == 0);
 261   }
 262 
 263   printf ("Starting test_once ..."); fflush (stdout);
 264   test_once ();
 265   printf (" OK\n"); fflush (stdout);
 266 
 267   return 0;
 268 }
 269 
 270 #else
 271 
 272 /* No multithreading available.  */
 273 
 274 #include <stdio.h>
 275 
 276 int
 277 main ()
     /* [previous][next][first][last][top][bottom][index][help] */
 278 {
 279   fputs ("Skipping test: multithreading not enabled\n", stderr);
 280   return 77;
 281 }
 282 
 283 #endif

/* [previous][next][first][last][top][bottom][index][help] */