Ne.app NeANT
The Ne.app NeANT User module of the NeANT distribution.
atomic_xchg.cpp
Go to the documentation of this file.
1
30#include <stdio.h>
31#include <string.h>
32
33#include <rw/_mutex.h>
34
35#include <rw_any.h>
36#include <rw_cmdopt.h>
37#include <rw_driver.h>
38#include <rw_thread.h> // for rw_thread_create()
39
40
42struct thr_args_base
43{
44 static enum tag_t {
48 } type_tag_;
49
50 unsigned long threadno_; // thread ordinal number
51 unsigned long niter_; // number of iterations
52 unsigned long nxchg_; // number of exchanges
53};
54
56
57template <class intT>
59{
60 static unsigned long nincr_; // number of increments
61 static intT *shared_; // shared variables
62 static unsigned long nthreads_; // number of threads
63
64 static intT* get_array ();
65};
66
67template <class intT>
68unsigned long thr_args<intT>::nincr_;
69
70template <class intT>
72
73template <class intT>
74unsigned long thr_args<intT>::nthreads_;
75
76// working around compiler bugs that prevent us from defining
77// a static array data member of a class template (PR #30009)
78template <class intT>
79/* static */ intT* thr_args<intT>::get_array ()
80{
81 static intT array [2];
82 return array;
83}
84
85
87template <class intT>
88intT exchange (intT &x, intT y)
89{
90#ifndef _RWSTD_REENTRANT
91
92 intT save (x);
93
94 x = y;
95
96 return save;
97
98#else // if defined (_RWSTD_REENTRANT)
99
100 return _RW::__rw_atomic_exchange (x, y, false);
101
102#endif // _RWSTD_REENTRANT
103}
104
105
107template <class intT>
109{
110 // each thread operates on one of two shared values to exercise
111 // problems due to operating on adjacent bytes or half-words
112 const unsigned long inx = args->threadno_ % 2;
113
114 static volatile int failed;
115
116 // exercise atomic_exchange() in a tight loop
117
118 // perform the requested number increments, or until the
119 // shared `failed' variable is set to a non-zero value
120
121 for (unsigned long i = 0; i != args->nincr_ && !failed; ++i) {
122
123 for (unsigned long j = 0; !failed; ++j) {
124
125 // increment the number of iterations of this thread
126 ++args->niter_;
127
128 // use intT() as a special "lock" value
129 const intT old = exchange (args->shared_ [inx], intT ());
130
131 // increment the number of exchanges performed by this thread
132 ++args->nxchg_;
133
134 if (intT () != old) {
135
136 // shared variable was not locked by any other thread
137
138 // increment the value of the shared variable, taking
139 // care to avoid the special "lock" value of intT()
140 intT newval = intT (old + 1);
141
142 if (intT () == newval)
143 ++newval;
144
145 const intT lock = exchange (args->shared_ [inx], newval);
146
147 // increment the number of exchanges
148 ++args->nxchg_;
149
150 // the returned value must be the special "lock" value
151 if (intT () == lock)
152 break;
153
154 // fail by setting the shared failed variable (to
155 // prevent deadlock) if the returned value is not
156 // the special "lock" value
157
158 printf ("*** line %d: error: thread %lu failed "
159 "at increment %lu after %lu iterations\n",
160 __LINE__, args->threadno_, i, args->niter_);
161 failed = 1;
162 return 0;
163 }
164
165 if (100UL * args->nincr_ == j) {
166
167 // fail by setting the shared failed variable (to
168 // prevent deadlock) if the number of failed attempts
169 // to lock the shared variable reaches the requested
170 // number of increments * 100 (an arbitrary number)
171
172 printf ("*** line %d: error thread %lu \"timed out\" after "
173 "%lu increments and %lu iterations\n",
174 __LINE__, args->threadno_, i, args->niter_);
175 failed = 1;
176 return 0;
177 }
178 }
179 }
180
181 return 0;
182}
183
184
186extern "C" void* thread_routine (void *arg)
187{
188 thr_args_base* const args = (thr_args_base*)arg;
189
190 printf ("thread %lu starting\n", args->threadno_);
191
192 switch (args->type_tag_) {
193
195 return thread_routine ((thr_args<char>*)(arg));
200
205
207 return thread_routine ((thr_args<int>*)(arg));
210
212 return thread_routine ((thr_args<long>*)(arg));
215
216#ifdef _RWSTD_LONG_LONG
217
222
223#endif // _RWSTD_LONG_LONG
224
225 };
226
227 return 0;
228}
229
230
232/* extern */ int rw_opt_nloops = 1024 * 1024;
233/* extern */ int rw_opt_nthreads = 4;
234
235#define MAX_THREADS 32
236
237
238template <class intT>
240{
241 static const char* const tname = rw_any_t (intT ()).type_name ();
242
243 if (!rw_enabled (tname)) {
244 rw_note (0, 0, 0, "%s test disabled", tname);
245 return;
246 }
247
248#ifdef _RWSTD_REENTRANT
249
250 static const char* const fun = "__rw_atomic_exchange";
251
252 rw_info (0, 0, 0, "__rw::%s (%s&, %2$s): %d iterations in %d threads",
254
256
257 typedef thr_args<intT> Args;
258
259 Args::nthreads_ = unsigned (rw_opt_nthreads);
260 Args::type_tag_ = tag;
261 Args::nincr_ = unsigned (rw_opt_nloops);
262 Args::shared_ [0] = intT (1);
263 Args::shared_ [1] = intT (1);
264
265 _RWSTD_ASSERT (Args::nthreads_ < sizeof tid / sizeof *tid);
266
267 Args args [sizeof tid / sizeof *tid];
268
269 for (unsigned long i = 0; i != Args::nthreads_; ++i) {
270
271 args [i].threadno_ = i;
272 args [i].niter_ = 0;
273 args [i].nxchg_ = 0;
274
275 rw_fatal (0 == rw_thread_create (tid + i, 0, thread_routine, args + i),
276 0, __LINE__, "thread_create() failed");
277 }
278
279 for (unsigned long i = 0; i != Args::nthreads_; ++i) {
280
281 rw_error (0 == rw_thread_join (tid [i], 0), 0, __LINE__,
282 "thread_join() failed");
283
284 if (args [i].niter_) {
285 // compute the percantage of thread iterations that resulted
286 // in increments of one of the shared variables
287 const unsigned long incrpcnt =
288 (100U * Args::nincr_) / args [i].niter_;
289
290 printf ("thread %lu performed %lu exchanges in %lu iterations "
291 "(%lu%% increments)\n",
292 args [i].threadno_, args [i].nxchg_,
293 args [i].niter_, incrpcnt);
294 }
295 }
296
297 // compute the expected result, "skipping" zeros by incrementing
298 // expect twice when it overflows and wraps around to 0 (zero is
299 // used as the lock variable in thread_routine() above)
300 intT expect = intT (1);
301
302 const unsigned long nincr = (Args::nthreads_ * Args::nincr_) / 2U;
303
304 for (unsigned long i = 0; i != nincr; ++i) {
305 if (intT () == ++expect)
306 ++expect;
307 }
308
309 // verify that the final value of the variables shared among all
310 // threads equals the number of increments performed by the threads
311 rw_assert (Args::shared_ [0] == expect, 0, __LINE__,
312 "1. %s (%s&, %2$s); %s == %s failed",
313 fun, tname, TOSTR (Args::shared_ [0]), TOSTR (expect));
314
315 rw_assert (Args::shared_ [1] == expect, 0, __LINE__,
316 "2. %s (%s&, %2$s); %s == %s failed",
317 fun, tname, TOSTR (Args::shared_ [1]), TOSTR (expect));
318
319#else // if !defined (_RWSTD_REENTRANT)
320
322
323#endif // _RWSTD_REENTRANT
324}
325
326
328static int
329run_test (int, char**)
330{
331 // exercise atomic exchange
332 run_test ((char)0, thr_args_base::Char);
333 run_test ((signed char)0, thr_args_base::SChar);
334 run_test ((unsigned char)0, thr_args_base::UChar);
335
336 run_test ((short)0, thr_args_base::Short);
337 run_test ((unsigned short)0, thr_args_base::UShort);
338
340 run_test ((unsigned int)0, thr_args_base::UInt);
341
342 run_test ((long)0, thr_args_base::Long);
343 run_test ((unsigned long)0, thr_args_base::ULong);
344
345#ifdef _RWSTD_LONG_LONG
346
349
350#endif // _RWSTD_LONG_LONG
351
352 return 0;
353
354}
355
356
358int main (int argc, char *argv[])
359{
360 return rw_test (argc, argv, __FILE__,
361 0 /* no clause */,
362 0 /* no comment */, run_test,
363 "|-nloops#0 " // must be non-negative
364 "|-nthreads#0-*", // must be in [0, MAX_THREADS]
366 int (MAX_THREADS),
368}
#define i
Definition 17.names.cpp:126
#define y
Definition 17.names.cpp:141
#define fun
Definition 17.names.cpp:471
#define x
Definition 17.names.cpp:140
#define array
Definition 17.names.cpp:447
#define j
Definition 17.names.cpp:127
#define save
Definition 17.names.cpp:538
#define inx
Definition 17.names.cpp:484
#define old
Definition 17.names.cpp:509
#define tag
Definition 17.names.cpp:551
#define main
IMPORT_C SInt32 printf(const Char *,...)
#define _RWSTD_LONG_LONG
#define _RWSTD_UNUSED(arg)
Definition _defs.h:972
#define _RWSTD_ASSERT(ignore)
Definition _defs.h:1108
int rw_opt_nthreads
void run_test(intT, thr_args_base::tag_t tag)
intT exchange(intT &x, intT y)
#define MAX_THREADS
void * thread_routine(thr_args< intT > *args)
int rw_opt_nloops
A TOML array.
Definition array.hpp:258
const char * type_name() const
Definition any.cpp:310
#define TOSTR(x)
Definition rw_any.h:148
_TEST_EXPORT int rw_enabled(const char *name)
_TEST_EXPORT int rw_test(int argc, char *argv[], const char *filename, const char *clause, const char *comment, int(*testfun)(int, char **), const char *optspec,...)
_TEST_EXPORT int rw_info(int, const char *, int, const char *,...)
Definition driver.cpp:1681
_TEST_EXPORT int rw_fatal(int expr, const char *filename, int line, const char *fmtspec,...)
Definition driver.cpp:1596
_TEST_EXPORT int rw_note(int, const char *, int, const char *,...)
Definition driver.cpp:1664
_TEST_EXPORT int rw_assert(int, const char *, int, const char *,...)
Definition driver.cpp:1630
_TEST_EXPORT int rw_error(int, const char *, int, const char *,...)
Definition driver.cpp:1613
_TEST_EXPORT int rw_thread_create(rw_thread_t *, rw_thread_attr_t *, rw_thread_proc *, void *)
Definition thread.cpp:379
_TEST_EXPORT int rw_thread_join(rw_thread_t, void **)
Definition thread.cpp:391
unsigned long nxchg_
unsigned long threadno_
static enum thr_args_base::tag_t type_tag_
unsigned long niter_
static unsigned long nincr_
static intT * get_array()
static unsigned long nthreads_
unsigned threadno_
static intT * get_array()
static intT * shared_
int arg
Definition locale.cpp:2806