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