1 /*
2 * C Extension module to smoke test pyatomic.h API.
3 *
4 * This only tests basic functionality, not any synchronizing ordering.
5 */
6
7 #include "parts.h"
8
9 // We define atomic bitwise operations on these types
10 #define FOR_BITWISE_TYPES(V) \
11 V(uint8, uint8_t) \
12 V(uint16, uint16_t) \
13 V(uint32, uint32_t) \
14 V(uint64, uint64_t) \
15 V(uintptr, uintptr_t)
16
17 // We define atomic addition on these types
18 #define FOR_ARITHMETIC_TYPES(V) \
19 FOR_BITWISE_TYPES(V) \
20 V(int, int) \
21 V(uint, unsigned int) \
22 V(int8, int8_t) \
23 V(int16, int16_t) \
24 V(int32, int32_t) \
25 V(int64, int64_t) \
26 V(intptr, intptr_t) \
27 V(ssize, Py_ssize_t)
28
29 // We define atomic load, store, exchange, and compare_exchange on these types
30 #define FOR_ALL_TYPES(V) \
31 FOR_ARITHMETIC_TYPES(V) \
32 V(ptr, void*)
33
34 #define IMPL_TEST_ADD(suffix, dtype) \
35 static PyObject * \
36 test_atomic_add_##suffix(PyObject *self, PyObject *obj) { \
37 dtype x = 0; \
38 assert(_Py_atomic_add_##suffix(&x, 1) == 0); \
39 assert(x == 1); \
40 assert(_Py_atomic_add_##suffix(&x, 2) == 1); \
41 assert(x == 3); \
42 assert(_Py_atomic_add_##suffix(&x, -2) == 3); \
43 assert(x == 1); \
44 assert(_Py_atomic_add_##suffix(&x, -1) == 1); \
45 assert(x == 0); \
46 assert(_Py_atomic_add_##suffix(&x, -1) == 0); \
47 assert(x == (dtype)-1); \
48 assert(_Py_atomic_add_##suffix(&x, -2) == (dtype)-1); \
49 assert(x == (dtype)-3); \
50 assert(_Py_atomic_add_##suffix(&x, 2) == (dtype)-3); \
51 assert(x == (dtype)-1); \
52 Py_RETURN_NONE; \
53 }
54 FOR_ARITHMETIC_TYPES(IMPL_TEST_ADD)
55
56 #define IMPL_TEST_COMPARE_EXCHANGE(suffix, dtype) \
57 static PyObject * \
58 test_atomic_compare_exchange_##suffix(PyObject *self, PyObject *obj) { \
59 dtype x = (dtype)0; \
60 dtype y = (dtype)1; \
61 dtype z = (dtype)2; \
62 assert(_Py_atomic_compare_exchange_##suffix(&x, &y, z) == 0); \
63 assert(x == 0); \
64 assert(y == 0); \
65 assert(_Py_atomic_compare_exchange_##suffix(&x, &y, z) == 1); \
66 assert(x == z); \
67 assert(y == 0); \
68 assert(_Py_atomic_compare_exchange_##suffix(&x, &y, z) == 0); \
69 assert(x == z); \
70 assert(y == z); \
71 Py_RETURN_NONE; \
72 }
FOR_ALL_TYPES(IMPL_TEST_COMPARE_EXCHANGE)73 FOR_ALL_TYPES(IMPL_TEST_COMPARE_EXCHANGE)
74
75 #define IMPL_TEST_EXCHANGE(suffix, dtype) \
76 static PyObject * \
77 test_atomic_exchange_##suffix(PyObject *self, PyObject *obj) { \
78 dtype x = (dtype)0; \
79 dtype y = (dtype)1; \
80 dtype z = (dtype)2; \
81 assert(_Py_atomic_exchange_##suffix(&x, y) == (dtype)0); \
82 assert(x == (dtype)1); \
83 assert(_Py_atomic_exchange_##suffix(&x, z) == (dtype)1); \
84 assert(x == (dtype)2); \
85 assert(_Py_atomic_exchange_##suffix(&x, y) == (dtype)2); \
86 assert(x == (dtype)1); \
87 Py_RETURN_NONE; \
88 }
89 FOR_ALL_TYPES(IMPL_TEST_EXCHANGE)
90
91 #define IMPL_TEST_LOAD_STORE(suffix, dtype) \
92 static PyObject * \
93 test_atomic_load_store_##suffix(PyObject *self, PyObject *obj) { \
94 dtype x = (dtype)0; \
95 dtype y = (dtype)1; \
96 dtype z = (dtype)2; \
97 assert(_Py_atomic_load_##suffix(&x) == (dtype)0); \
98 assert(x == (dtype)0); \
99 _Py_atomic_store_##suffix(&x, y); \
100 assert(_Py_atomic_load_##suffix(&x) == (dtype)1); \
101 assert(x == (dtype)1); \
102 _Py_atomic_store_##suffix##_relaxed(&x, z); \
103 assert(_Py_atomic_load_##suffix##_relaxed(&x) == (dtype)2); \
104 assert(x == (dtype)2); \
105 Py_RETURN_NONE; \
106 }
107 FOR_ALL_TYPES(IMPL_TEST_LOAD_STORE)
108
109 #define IMPL_TEST_AND_OR(suffix, dtype) \
110 static PyObject * \
111 test_atomic_and_or_##suffix(PyObject *self, PyObject *obj) { \
112 dtype x = (dtype)0; \
113 dtype y = (dtype)1; \
114 dtype z = (dtype)3; \
115 assert(_Py_atomic_or_##suffix(&x, z) == (dtype)0); \
116 assert(x == (dtype)3); \
117 assert(_Py_atomic_and_##suffix(&x, y) == (dtype)3); \
118 assert(x == (dtype)1); \
119 Py_RETURN_NONE; \
120 }
121 FOR_BITWISE_TYPES(IMPL_TEST_AND_OR)
122
123 static PyObject *
124 test_atomic_fences(PyObject *self, PyObject *obj) {
125 // Just make sure that the fences compile. We are not
126 // testing any synchronizing ordering.
127 _Py_atomic_fence_seq_cst();
128 _Py_atomic_fence_acquire();
129 _Py_atomic_fence_release();
130 Py_RETURN_NONE;
131 }
132
133 static PyObject *
test_atomic_release_acquire(PyObject * self,PyObject * obj)134 test_atomic_release_acquire(PyObject *self, PyObject *obj) {
135 void *x = NULL;
136 void *y = &y;
137 assert(_Py_atomic_load_ptr_acquire(&x) == NULL);
138 _Py_atomic_store_ptr_release(&x, y);
139 assert(x == y);
140 assert(_Py_atomic_load_ptr_acquire(&x) == y);
141 Py_RETURN_NONE;
142 }
143
144 static PyObject *
test_atomic_load_store_int_release_acquire(PyObject * self,PyObject * obj)145 test_atomic_load_store_int_release_acquire(PyObject *self, PyObject *obj) { \
146 int x = 0;
147 int y = 1;
148 int z = 2;
149 assert(_Py_atomic_load_int_acquire(&x) == 0);
150 _Py_atomic_store_int_release(&x, y);
151 assert(x == y);
152 assert(_Py_atomic_load_int_acquire(&x) == y);
153 _Py_atomic_store_int_release(&x, z);
154 assert(x == z);
155 assert(_Py_atomic_load_int_acquire(&x) == z);
156 Py_RETURN_NONE;
157 }
158
159 // NOTE: all tests should start with "test_atomic_" to be included
160 // in test_pyatomic.py
161
162 #define BIND_TEST_ADD(suffix, dtype) \
163 {"test_atomic_add_" #suffix, test_atomic_add_##suffix, METH_NOARGS},
164 #define BIND_TEST_COMPARE_EXCHANGE(suffix, dtype) \
165 {"test_atomic_compare_exchange_" #suffix, test_atomic_compare_exchange_##suffix, METH_NOARGS},
166 #define BIND_TEST_EXCHANGE(suffix, dtype) \
167 {"test_atomic_exchange_" #suffix, test_atomic_exchange_##suffix, METH_NOARGS},
168 #define BIND_TEST_LOAD_STORE(suffix, dtype) \
169 {"test_atomic_load_store_" #suffix, test_atomic_load_store_##suffix, METH_NOARGS},
170 #define BIND_TEST_AND_OR(suffix, dtype) \
171 {"test_atomic_and_or_" #suffix, test_atomic_and_or_##suffix, METH_NOARGS},
172
173 static PyMethodDef test_methods[] = {
174 FOR_ARITHMETIC_TYPES(BIND_TEST_ADD)
175 FOR_ALL_TYPES(BIND_TEST_COMPARE_EXCHANGE)
176 FOR_ALL_TYPES(BIND_TEST_EXCHANGE)
177 FOR_ALL_TYPES(BIND_TEST_LOAD_STORE)
178 FOR_BITWISE_TYPES(BIND_TEST_AND_OR)
179 {"test_atomic_fences", test_atomic_fences, METH_NOARGS},
180 {"test_atomic_release_acquire", test_atomic_release_acquire, METH_NOARGS},
181 {"test_atomic_load_store_int_release_acquire", test_atomic_load_store_int_release_acquire, METH_NOARGS},
182 {NULL, NULL} /* sentinel */
183 };
184
185 int
_PyTestCapi_Init_PyAtomic(PyObject * mod)186 _PyTestCapi_Init_PyAtomic(PyObject *mod)
187 {
188 if (PyModule_AddFunctions(mod, test_methods) < 0) {
189 return -1;
190 }
191 return 0;
192 }
193