1 /* Copyright (c) 2006, 2021, Oracle and/or its affiliates.
2 
3    This program is free software; you can redistribute it and/or modify
4    it under the terms of the GNU General Public License, version 2.0,
5    as published by the Free Software Foundation.
6 
7    This program is also distributed with certain software (including
8    but not limited to OpenSSL) that is licensed under separate terms,
9    as designated in a particular file or component or in included license
10    documentation.  The authors of MySQL hereby grant you an additional
11    permission to link the program and your derivative works with the
12    separately licensed software that they have included with MySQL.
13 
14    This program is distributed in the hope that it will be useful,
15    but WITHOUT ANY WARRANTY; without even the implied warranty of
16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17    GNU General Public License, version 2.0, for more details.
18 
19    You should have received a copy of the GNU General Public License
20    along with this program; if not, write to the Free Software
21    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA */
22 
23 // First include (the generated) my_config.h, to get correct platform defines.
24 #include "my_config.h"
25 #include <gtest/gtest.h>
26 
27 #include <my_global.h>
28 #include <my_sys.h>
29 #include <my_atomic.h>
30 
31 
32 namespace mysys_my_atomic_unittest {
33 
34 #include "thr_template.cc"
35 
36 volatile int32 b32;
37 volatile int32  c32;
38 
39 /* add and sub a random number in a loop. Must get 0 at the end */
test_atomic_add(void * arg)40 extern "C" void *test_atomic_add(void *arg)
41 {
42   int m= (*(int *)arg)/2;
43   int32 x;
44   for (x= ((int)(intptr)(&m)); m ; m--)
45   {
46     x= (x*m+0x87654321) & INT_MAX32;
47     my_atomic_add32(&bad, x);
48 
49     my_atomic_add32(&bad, -x);
50   }
51   mysql_mutex_lock(&mutex);
52   if (!--running_threads) mysql_cond_signal(&cond);
53   mysql_mutex_unlock(&mutex);
54   return 0;
55 }
56 
57 volatile int64 a64;
58 /* add and sub a random number in a loop. Must get 0 at the end */
test_atomic_add64(void * arg)59 extern "C" void *test_atomic_add64(void *arg)
60 {
61   int m= (*(int *)arg)/2;
62   int64 x;
63   for (x= ((int64)(intptr)(&m)); m ; m--)
64   {
65     x= (x*m+0xfdecba987654321LL) & INT_MAX64;
66     my_atomic_add64(&a64, x);
67 
68     my_atomic_add64(&a64, -x);
69   }
70   mysql_mutex_lock(&mutex);
71   if (!--running_threads)
72   {
73     bad= (a64 != 0);
74     mysql_cond_signal(&cond);
75   }
76   mysql_mutex_unlock(&mutex);
77   return 0;
78 }
79 
80 
81 /*
82   1. generate thread number 0..N-1 from b32
83   2. add it to bad
84   3. swap thread numbers in c32
85   4. (optionally) one more swap to avoid 0 as a result
86   5. subtract result from bad
87   must get 0 in bad at the end
88 */
test_atomic_fas(void * arg)89 extern "C" void *test_atomic_fas(void *arg)
90 {
91   int    m= *(int *)arg;
92   int32  x;
93 
94   x= my_atomic_add32(&b32, 1);
95 
96   my_atomic_add32(&bad, x);
97 
98   for (; m ; m--)
99   {
100     x= my_atomic_fas32(&c32, x);
101   }
102 
103   if (!x)
104   {
105     x= my_atomic_fas32(&c32, x);
106   }
107 
108   my_atomic_add32(&bad, -x);
109 
110   mysql_mutex_lock(&mutex);
111   if (!--running_threads) mysql_cond_signal(&cond);
112   mysql_mutex_unlock(&mutex);
113   return 0;
114 }
115 
116 /*
117   same as test_atomic_add, but my_atomic_add32 is emulated with
118   my_atomic_cas32 - notice that the slowdown is proportional to the
119   number of CPUs
120 */
test_atomic_cas(void * arg)121 extern "C" void *test_atomic_cas(void *arg)
122 {
123   int m= (*(int *)arg)/2, ok= 0;
124   int32 x, y;
125   for (x= ((int)(intptr)(&m)); m ; m--)
126   {
127     y= my_atomic_load32(&bad);
128     x= (x*m+0x87654321) & INT_MAX32;
129     do {
130       ok= my_atomic_cas32(&bad, &y, (uint32)y+x);
131     } while (!ok) ;
132     do {
133       ok= my_atomic_cas32(&bad, &y, y-x);
134     } while (!ok) ;
135   }
136   mysql_mutex_lock(&mutex);
137   if (!--running_threads) mysql_cond_signal(&cond);
138   mysql_mutex_unlock(&mutex);
139   return 0;
140 }
141 
142 
do_tests()143 void do_tests()
144 {
145   b32= c32= 0;
146   test_concurrently("my_atomic_add32", test_atomic_add, THREADS, CYCLES);
147   b32= c32= 0;
148   test_concurrently("my_atomic_fas32", test_atomic_fas, THREADS, CYCLES);
149   b32= c32= 0;
150   test_concurrently("my_atomic_cas32", test_atomic_cas, THREADS, CYCLES);
151 
152   {
153     /*
154       If b is not volatile, the wrong assembly code is generated on OSX Lion
155       as the variable is optimized away as a constant.
156       See Bug#62533 / Bug#13030056.
157       Another workaround is to specify architecture explicitly using e.g.
158       CFLAGS/CXXFLAGS= "-m64".
159     */
160     volatile int64 b=0x1000200030004000LL;
161     a64=0;
162     my_atomic_add64(&a64, b);
163     EXPECT_EQ(a64, b) << "add64";
164   }
165   a64=0;
166   test_concurrently("my_atomic_add64", test_atomic_add64, THREADS, CYCLES);
167 }
168 
169 
TEST(Mysys,Atomic)170 TEST(Mysys, Atomic)
171 {
172   mysql_mutex_init(0, &mutex, 0);
173   mysql_cond_init(0, &cond);
174   my_thread_attr_init(&thr_attr);
175   my_thread_attr_setdetachstate(&thr_attr, MY_THREAD_CREATE_DETACHED);
176 
177   do_tests();
178 
179   mysql_mutex_destroy(&mutex);
180   mysql_cond_destroy(&cond);
181   my_thread_attr_destroy(&thr_attr);
182 }
183 
184 }
185