• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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