root/maint/gnulib/tests/test-simple-atomic.c

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

DEFINITIONS

This source file includes following definitions.
  1. int_mutator_thread
  2. ptr_mutator_thread
  3. main
  4. main

   1 /* Test of simple atomic operations for multithreading.
   2    Copyright (C) 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>, 2021.  */
  18 
  19 #include <config.h>
  20 
  21 #include "simple-atomic.h"
  22 
  23 #if USE_ISOC_THREADS || USE_POSIX_THREADS || USE_ISOC_AND_POSIX_THREADS || USE_WINDOWS_THREADS
  24 
  25 /* Whether to help the scheduler through explicit yield().
  26    Uncomment this to see if the operating system has a fair scheduler.  */
  27 #define EXPLICIT_YIELD 1
  28 
  29 /* Number of simultaneous threads.  */
  30 #define THREAD_COUNT 4
  31 
  32 /* Number of operations performed in each thread.
  33    With a smaller count, say 100, we often get an "OK" result even with the racy
  34    implementation.  */
  35 #define REPEAT_COUNT 1000
  36 
  37 #include "glthread/thread.h"
  38 #include "glthread/yield.h"
  39 
  40 #include "macros.h"
  41 
  42 #if EXPLICIT_YIELD
  43 # define yield() gl_thread_yield ()
  44 #else
  45 # define yield()
  46 #endif
  47 
  48 /* Counters for each thread.  */
  49 static unsigned int counter[THREAD_COUNT][5];
  50 
  51 /* A variable of type 'unsigned int'.  */
  52 static unsigned int int_variable;
  53 
  54 static void *
  55 int_mutator_thread (void *arg)
     /* [previous][next][first][last][top][bottom][index][help] */
  56 {
  57   int *pcounter = (int *) arg;
  58   int repeat;
  59 
  60   for (repeat = REPEAT_COUNT; repeat > 0; repeat--)
  61     {
  62       if (atomic_compare_and_swap (&int_variable, 0, 10) == 0)
  63         pcounter[0]++;
  64       yield ();
  65 
  66       if (atomic_compare_and_swap (&int_variable, 14, 17) == 14)
  67         pcounter[1]++;
  68       yield ();
  69 
  70       if (atomic_compare_and_swap (&int_variable, 20, 0) == 20)
  71         pcounter[2]++;
  72       yield ();
  73 
  74       if (atomic_compare_and_swap (&int_variable, 10, 14) == 10)
  75         pcounter[3]++;
  76       yield ();
  77 
  78       if (atomic_compare_and_swap (&int_variable, 17, 20) == 17)
  79         pcounter[4]++;
  80       yield ();
  81     }
  82 
  83   return NULL;
  84 }
  85 
  86 /* A variable of type 'uintptr_t'.  */
  87 static uintptr_t ptr_variable;
  88 
  89 static void *
  90 ptr_mutator_thread (void *arg)
     /* [previous][next][first][last][top][bottom][index][help] */
  91 {
  92   int *pcounter = (int *) arg;
  93   int repeat;
  94 
  95   for (repeat = REPEAT_COUNT; repeat > 0; repeat--)
  96     {
  97       if (atomic_compare_and_swap_ptr (&ptr_variable, 0, 10) == 0)
  98         pcounter[0]++;
  99       yield ();
 100 
 101       if (atomic_compare_and_swap_ptr (&ptr_variable, 14, 17) == 14)
 102         pcounter[1]++;
 103       yield ();
 104 
 105       if (atomic_compare_and_swap_ptr (&ptr_variable, 20, 0) == 20)
 106         pcounter[2]++;
 107       yield ();
 108 
 109       if (atomic_compare_and_swap_ptr (&ptr_variable, 10, 14) == 10)
 110         pcounter[3]++;
 111       yield ();
 112 
 113       if (atomic_compare_and_swap_ptr (&ptr_variable, 17, 20) == 17)
 114         pcounter[4]++;
 115       yield ();
 116     }
 117 
 118   return NULL;
 119 }
 120 
 121 int
 122 main ()
     /* [previous][next][first][last][top][bottom][index][help] */
 123 {
 124   /* Check simple uses of atomic_compare_and_swap.  */
 125   {
 126     unsigned int x[3] = { 0xDEADBEEFU, 11, 0xDEADBEEFU };
 127 
 128     ASSERT (atomic_compare_and_swap (&x[1], 0, 17) == 11);
 129     ASSERT (x[1] == 11);
 130 
 131     ASSERT (atomic_compare_and_swap (&x[1], 4, 11) == 11);
 132     ASSERT (x[1] == 11);
 133 
 134     ASSERT (atomic_compare_and_swap (&x[1], 11, 15) == 11);
 135     ASSERT (x[1] == 15);
 136 
 137     ASSERT (x[0] == 0xDEADBEEFU);
 138     ASSERT (x[2] == 0xDEADBEEFU);
 139   }
 140 
 141   /* Check simple uses of atomic_compare_and_swap_ptr.  */
 142   {
 143     uintptr_t v1 = ~(uintptr_t)0 / 3;
 144     uintptr_t v2 = ~(uintptr_t)0 / 5 * 4;
 145     uintptr_t v3 = ~(uintptr_t)0 / 7 * 3;
 146     uintptr_t x[3] = { 0xDEADBEEFU, v1, 0xDEADBEEFU };
 147 
 148     ASSERT (atomic_compare_and_swap_ptr (&x[1], 0, v3) == v1);
 149     ASSERT (x[1] == v1);
 150 
 151     ASSERT (atomic_compare_and_swap_ptr (&x[1], 4, v1) == v1);
 152     ASSERT (x[1] == v1);
 153 
 154     ASSERT (atomic_compare_and_swap_ptr (&x[1], v1, v2) == v1);
 155     ASSERT (x[1] == v2);
 156 
 157     ASSERT (x[0] == 0xDEADBEEFU);
 158     ASSERT (x[2] == 0xDEADBEEFU);
 159   }
 160 
 161   /* Check atomicity of atomic_compare_and_swap.  */
 162   {
 163     void * (*funcs[2]) (void *) = { int_mutator_thread, ptr_mutator_thread };
 164     int f;
 165     for (f = 0; f < 2; f++)
 166       {
 167         void * (*func) (void *) = funcs[f];
 168         int i, j;
 169         gl_thread_t threads[THREAD_COUNT];
 170 
 171         /* Initialization.  */
 172         for (i = 0; i < THREAD_COUNT; i++)
 173           for (j = 0; j < 5; j++)
 174             counter[i][j] = 0;
 175 
 176         /* Spawn the threads.  */
 177         for (i = 0; i < THREAD_COUNT; i++)
 178           threads[i] = gl_thread_create (func, &counter[i][0]);
 179 
 180         /* Wait for the threads to terminate.  */
 181         for (i = 0; i < THREAD_COUNT; i++)
 182           gl_thread_join (threads[i], NULL);
 183 
 184         /* Sum up the work that the threads have done.  */
 185         unsigned int sum[5];
 186         for (j = 0; j < 5; j++)
 187           {
 188             sum[j] = 0;
 189             for (i = 0; i < THREAD_COUNT; i++)
 190               sum[j] += counter[i][j];
 191           }
 192 
 193         /* If things went atomically, the threads have moved the variable's
 194            value through the cycle  0 -> 10 -> 14 -> 17 -> 20 -> 0 ...  a large
 195            number of times.
 196            sum[0] is the number of transitions 0 -> 10.
 197            sum[3] is the number of transitions 10 -> 14.
 198            sum[1] is the number of transitions 14 -> 17.
 199            sum[4] is the number of transitions 17 -> 20.
 200            sum[2] is the number of transitions 20 -> 0.
 201            Since the cycle started at 0 and ends anywhere (namely, when all
 202            threads when through their loop REPEAT_COUNT times), the sequence
 203              sum[0], sum[3], sum[1], sum[4], sum[2], sum[0] - 1
 204            must be monotonically decreasing.
 205 
 206            If things did not go atomically, the counters don't exhibit this
 207            pattern.  */
 208         printf ("Counters: %u %u %u %u %u\n",
 209                 sum[0], sum[3], sum[1], sum[4], sum[2]);
 210         ASSERT ((sum[0] == sum[3] || sum[0] == sum[3] + 1)
 211                 && (sum[3] == sum[1] || sum[3] == sum[1] + 1)
 212                 && (sum[1] == sum[4] || sum[1] == sum[4] + 1)
 213                 && (sum[4] == sum[2] || sum[4] == sum[2] + 1)
 214                 && (sum[2] + 1 == sum[0] || sum[2] == sum[0]));
 215       }
 216   }
 217 
 218   return 0;
 219 }
 220 
 221 #else
 222 
 223 /* No multithreading available.  */
 224 
 225 #include <stdio.h>
 226 
 227 int
 228 main ()
     /* [previous][next][first][last][top][bottom][index][help] */
 229 {
 230   fputs ("Skipping test: multithreading not enabled\n", stderr);
 231   return 77;
 232 }
 233 
 234 #endif

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