source: rtems/testsuites/smptests/smpatomic01/init.c @ 4e3d9a4d

4.115
Last change on this file since 4e3d9a4d was 4e3d9a4d, checked in by Sebastian Huber <sebastian.huber@…>, on Feb 18, 2015 at 4:46:37 PM

score: Make <rtems/score/atomic.h> available

Make <rtems/score/atomic.h> available for all RTEMS configurations. Use
inline functions instead of macros. Use ISR disable/enable on
uni-processor configurations to ensure atomicity.

Update #2273.

  • Property mode set to 100644
File size: 9.8 KB
Line 
1/*
2 * Copyright (c) 2013-2014 embedded brains GmbH.  All rights reserved.
3 *
4 *  embedded brains GmbH
5 *  Dornierstr. 4
6 *  82178 Puchheim
7 *  Germany
8 *  <rtems@embedded-brains.de>
9 *
10 * Copyright (c) 2013 Deng Hengyi.
11 *
12 * The license and distribution terms for this file may be
13 * found in the file LICENSE in this distribution or at
14 * http://www.rtems.org/license/LICENSE.
15 */
16
17#ifdef HAVE_CONFIG_H
18  #include "config.h"
19#endif
20
21#include <rtems/score/atomic.h>
22#include <rtems/score/smpbarrier.h>
23#include <rtems.h>
24#include <limits.h>
25#include <string.h>
26
27#include "tmacros.h"
28
29const char rtems_test_name[] = "SMPATOMIC 1";
30
31#define MASTER_PRIORITY 1
32
33#define WORKER_PRIORITY 2
34
35#define CPU_COUNT 32
36
37typedef struct {
38  Atomic_Ulong stop;
39  SMP_barrier_Control barrier;
40  size_t worker_count;
41  rtems_id stop_worker_timer_id;
42  Atomic_Uint atomic_int_value;
43  Atomic_Ulong atomic_value;
44  unsigned long per_worker_value[CPU_COUNT];
45  unsigned long normal_value;
46  char unused_space_for_cache_line_separation[128];
47  unsigned long second_value;
48  Atomic_Flag global_flag;
49} test_context;
50
51typedef struct {
52  void (*init)(test_context *ctx);
53  void (*body)(test_context *ctx, size_t worker_index);
54  void (*fini)(test_context *ctx);
55} test_case;
56
57static test_context test_instance = {
58  .stop = ATOMIC_INITIALIZER_ULONG(0),
59  .barrier = SMP_BARRIER_CONTROL_INITIALIZER
60};
61
62static bool stop(test_context *ctx)
63{
64  return _Atomic_Load_ulong(&ctx->stop, ATOMIC_ORDER_RELAXED) != 0;
65}
66
67static bool is_master_worker(size_t worker_index)
68{
69  return worker_index == 0;
70}
71
72static void test_fini(
73  test_context *ctx,
74  const char *test,
75  bool atomic
76)
77{
78  unsigned long expected_value = 0;
79  unsigned long actual_value;
80  size_t worker_index;
81
82  printf("=== atomic %s test case ===\n", test);
83
84  for (worker_index = 0; worker_index < ctx->worker_count; ++worker_index) {
85    unsigned long worker_value = ctx->per_worker_value[worker_index];
86
87    expected_value += worker_value;
88
89    printf(
90      "worker %zu value: %lu\n",
91      worker_index,
92      worker_value
93    );
94  }
95
96  if (atomic) {
97    actual_value = _Atomic_Load_ulong(&ctx->atomic_value, ATOMIC_ORDER_RELAXED);
98  } else {
99    actual_value = ctx->normal_value;
100  }
101
102  printf(
103    "atomic value: expected = %lu, actual = %lu\n",
104    expected_value,
105    actual_value
106  );
107
108  rtems_test_assert(expected_value == actual_value);
109}
110
111static void test_atomic_add_init(test_context *ctx)
112{
113  _Atomic_Init_ulong(&ctx->atomic_value, 0);
114}
115
116static void test_atomic_add_body(test_context *ctx, size_t worker_index)
117{
118  unsigned long counter = 0;
119
120  while (!stop(ctx)) {
121    ++counter;
122    _Atomic_Fetch_add_ulong(&ctx->atomic_value, 1, ATOMIC_ORDER_RELAXED);
123  }
124
125  ctx->per_worker_value[worker_index] = counter;
126}
127
128static void test_atomic_add_fini(test_context *ctx)
129{
130  test_fini(ctx, "add", true);
131}
132
133static void test_atomic_flag_init(test_context *ctx)
134{
135  _Atomic_Flag_clear(&ctx->global_flag, ATOMIC_ORDER_RELEASE);
136  ctx->normal_value = 0;
137}
138
139static void test_atomic_flag_body(test_context *ctx, size_t worker_index)
140{
141  unsigned long counter = 0;
142
143  while (!stop(ctx)) {
144    while (_Atomic_Flag_test_and_set(&ctx->global_flag, ATOMIC_ORDER_ACQUIRE)) {
145      /* Wait */
146    }
147
148    ++counter;
149    ++ctx->normal_value;
150
151    _Atomic_Flag_clear(&ctx->global_flag, ATOMIC_ORDER_RELEASE);
152  }
153
154  ctx->per_worker_value[worker_index] = counter;
155}
156
157static void test_atomic_flag_fini(test_context *ctx)
158{
159  test_fini(ctx, "flag", false);
160}
161
162static void test_atomic_sub_init(test_context *ctx)
163{
164  _Atomic_Init_ulong(&ctx->atomic_value, 0);
165}
166
167static void test_atomic_sub_body(test_context *ctx, size_t worker_index)
168{
169  unsigned long counter = 0;
170
171  while (!stop(ctx)) {
172    --counter;
173    _Atomic_Fetch_sub_ulong(&ctx->atomic_value, 1, ATOMIC_ORDER_RELAXED);
174  }
175
176  ctx->per_worker_value[worker_index] = counter;
177}
178
179static void test_atomic_sub_fini(test_context *ctx)
180{
181  test_fini(ctx, "sub", true);
182}
183
184static void test_atomic_compare_exchange_init(test_context *ctx)
185{
186  _Atomic_Init_ulong(&ctx->atomic_value, 0);
187  ctx->normal_value = 0;
188}
189
190static void test_atomic_compare_exchange_body(test_context *ctx, size_t worker_index)
191{
192  unsigned long counter = 0;
193
194  while (!stop(ctx)) {
195    bool success;
196
197    do {
198      unsigned long zero = 0;
199
200      success = _Atomic_Compare_exchange_ulong(
201        &ctx->atomic_value,
202        &zero,
203        1,
204        ATOMIC_ORDER_ACQUIRE,
205        ATOMIC_ORDER_RELAXED
206      );
207    } while (!success);
208
209    ++counter;
210    ++ctx->normal_value;
211
212    _Atomic_Store_ulong(&ctx->atomic_value, 0, ATOMIC_ORDER_RELEASE);
213  }
214
215  ctx->per_worker_value[worker_index] = counter;
216}
217
218static void test_atomic_compare_exchange_fini(test_context *ctx)
219{
220  test_fini(ctx, "compare exchange", false);
221}
222
223static void test_atomic_or_and_init(test_context *ctx)
224{
225  _Atomic_Init_ulong(&ctx->atomic_value, 0);
226}
227
228static void test_atomic_or_and_body(test_context *ctx, size_t worker_index)
229{
230  unsigned long the_bit = 1UL << worker_index;
231  unsigned long current_bit = 0;
232
233  while (!stop(ctx)) {
234    unsigned long previous;
235
236    if (current_bit != 0) {
237      previous = _Atomic_Fetch_and_ulong(
238        &ctx->atomic_value,
239        ~the_bit,
240        ATOMIC_ORDER_RELAXED
241      );
242      current_bit = 0;
243    } else {
244      previous = _Atomic_Fetch_or_ulong(
245        &ctx->atomic_value,
246        the_bit,
247        ATOMIC_ORDER_RELAXED
248      );
249      current_bit = the_bit;
250    }
251
252    rtems_test_assert((previous & the_bit) != current_bit);
253  }
254
255  ctx->per_worker_value[worker_index] = current_bit;
256}
257
258static void test_atomic_or_and_fini(test_context *ctx)
259{
260  test_fini(ctx, "or/and", true);
261}
262
263static void test_atomic_fence_init(test_context *ctx)
264{
265  ctx->normal_value = 0;
266  ctx->second_value = 0;
267  _Atomic_Fence(ATOMIC_ORDER_RELEASE);
268}
269
270static void test_atomic_fence_body(test_context *ctx, size_t worker_index)
271{
272  if (is_master_worker(worker_index)) {
273    unsigned long counter = 0;
274
275    while (!stop(ctx)) {
276      ++counter;
277      ctx->normal_value = counter;
278      _Atomic_Fence(ATOMIC_ORDER_RELEASE);
279      ctx->second_value = counter;
280    }
281  } else {
282    while (!stop(ctx)) {
283      unsigned long n;
284      unsigned long s;
285
286      s = ctx->second_value;
287      _Atomic_Fence(ATOMIC_ORDER_ACQUIRE);
288      n = ctx->normal_value;
289
290      rtems_test_assert(n - s < LONG_MAX);
291    }
292  }
293}
294
295static void test_atomic_fence_fini(test_context *ctx)
296{
297  printf(
298    "=== atomic fence test case ===\n"
299    "normal value = %lu, second value = %lu\n",
300    ctx->normal_value,
301    ctx->second_value
302  );
303}
304
305static const test_case test_cases[] = {
306  {
307    test_atomic_add_init,
308    test_atomic_add_body,
309    test_atomic_add_fini
310  }, {
311    test_atomic_flag_init,
312    test_atomic_flag_body,
313    test_atomic_flag_fini
314  }, {
315    test_atomic_sub_init,
316    test_atomic_sub_body,
317    test_atomic_sub_fini
318  }, {
319    test_atomic_compare_exchange_init,
320    test_atomic_compare_exchange_body,
321    test_atomic_compare_exchange_fini
322  }, {
323    test_atomic_or_and_init,
324    test_atomic_or_and_body,
325    test_atomic_or_and_fini
326  }, {
327    test_atomic_fence_init,
328    test_atomic_fence_body,
329    test_atomic_fence_fini
330  },
331};
332
333#define TEST_COUNT RTEMS_ARRAY_SIZE(test_cases)
334
335static void stop_worker_timer(rtems_id timer_id, void *arg)
336{
337  test_context *ctx = arg;
338
339  _Atomic_Store_ulong(&ctx->stop, 1, ATOMIC_ORDER_RELAXED);
340}
341
342static void start_worker_stop_timer(test_context *ctx)
343{
344  rtems_status_code sc;
345
346  _Atomic_Store_ulong(&ctx->stop, 0, ATOMIC_ORDER_RELEASE);
347
348  sc = rtems_timer_fire_after(
349    ctx->stop_worker_timer_id,
350    rtems_clock_get_ticks_per_second(),
351    stop_worker_timer,
352    ctx
353  );
354  rtems_test_assert(sc == RTEMS_SUCCESSFUL);
355}
356
357static void run_tests(test_context *ctx, size_t worker_index)
358{
359  SMP_barrier_State bs = SMP_BARRIER_STATE_INITIALIZER;
360  size_t test;
361
362  for (test = 0; test < TEST_COUNT; ++test) {
363    const test_case *tc = &test_cases[test];
364
365    if (is_master_worker(worker_index)) {
366      start_worker_stop_timer(ctx);
367      (*tc->init)(ctx);
368    }
369
370    _SMP_barrier_Wait(&ctx->barrier, &bs, ctx->worker_count);
371
372    (*tc->body)(ctx, worker_index);
373
374    _SMP_barrier_Wait(&ctx->barrier, &bs, ctx->worker_count);
375
376    if (is_master_worker(worker_index)) {
377      (*tc->fini)(ctx);
378    }
379  }
380}
381
382static void worker_task(size_t worker_index)
383{
384  test_context *ctx = &test_instance;
385
386  run_tests(ctx, worker_index);
387
388  (void) rtems_task_suspend(RTEMS_SELF);
389  rtems_test_assert(0);
390}
391
392static void test(void)
393{
394  test_context *ctx = &test_instance;
395  rtems_status_code sc;
396  size_t worker_index;
397
398  ctx->worker_count = rtems_get_processor_count();
399
400  sc = rtems_timer_create(
401    rtems_build_name('S', 'T', 'O', 'P'),
402    &ctx->stop_worker_timer_id
403  );
404  rtems_test_assert(sc == RTEMS_SUCCESSFUL);
405
406  for (worker_index = 1; worker_index < ctx->worker_count; ++worker_index) {
407    rtems_id worker_id;
408
409    sc = rtems_task_create(
410      rtems_build_name('W', 'O', 'R', 'K'),
411      WORKER_PRIORITY,
412      RTEMS_MINIMUM_STACK_SIZE,
413      RTEMS_DEFAULT_MODES,
414      RTEMS_DEFAULT_ATTRIBUTES,
415      &worker_id
416    );
417    rtems_test_assert(sc == RTEMS_SUCCESSFUL);
418
419    sc = rtems_task_start(worker_id, worker_task, worker_index);
420    rtems_test_assert(sc == RTEMS_SUCCESSFUL);
421  }
422
423  run_tests(ctx, 0);
424}
425
426static void Init(rtems_task_argument arg)
427{
428  TEST_BEGIN();
429
430  test();
431
432  TEST_END();
433  rtems_test_exit(0);
434}
435
436#define CONFIGURE_APPLICATION_NEEDS_CLOCK_DRIVER
437#define CONFIGURE_APPLICATION_NEEDS_CONSOLE_DRIVER
438
439#define CONFIGURE_SMP_APPLICATION
440
441#define CONFIGURE_SMP_MAXIMUM_PROCESSORS CPU_COUNT
442
443#define CONFIGURE_MAXIMUM_TASKS CPU_COUNT
444
445#define CONFIGURE_MAXIMUM_TIMERS 1
446
447#define CONFIGURE_INIT_TASK_PRIORITY MASTER_PRIORITY
448#define CONFIGURE_INIT_TASK_INITIAL_MODES RTEMS_DEFAULT_MODES
449#define CONFIGURE_INIT_TASK_ATTRIBUTES RTEMS_DEFAULT_ATTRIBUTES
450
451#define CONFIGURE_INITIAL_EXTENSIONS RTEMS_TEST_INITIAL_EXTENSION
452
453#define CONFIGURE_RTEMS_INIT_TASKS_TABLE
454
455#define CONFIGURE_INIT
456
457#include <rtems/confdefs.h>
Note: See TracBrowser for help on using the repository browser.