source: rtems/testsuites/smptests/smpthreadlife01/init.c @ 258ad71

5
Last change on this file since 258ad71 was 258ad71, checked in by Sebastian Huber <sebastian.huber@…>, on 09/25/15 at 12:34:24

SMP: Fix and optimize thread dispatching

According to the C11 and C++11 memory models only a read-modify-write
operation guarantees that we read the last value written in modification
order. Avoid the sequential consistent thread fence and instead use the
inter-processor interrupt to set the thread dispatch necessary
indicator.

  • Property mode set to 100644
File size: 11.3 KB
Line 
1/*
2 * Copyright (c) 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 * The license and distribution terms for this file may be
11 * found in the file LICENSE in this distribution or at
12 * http://www.rtems.org/license/LICENSE.
13 */
14
15#ifdef HAVE_CONFIG_H
16  #include "config.h"
17#endif
18
19#include "tmacros.h"
20
21#include <rtems.h>
22#include <rtems/counter.h>
23#include <rtems/libcsupport.h>
24#include <rtems/score/profiling.h>
25#include <rtems/score/smpbarrier.h>
26#include <rtems/score/threadimpl.h>
27
28const char rtems_test_name[] = "SMPTHREADLIFE 1";
29
30#define CPU_COUNT 2
31
32typedef struct {
33  volatile rtems_task_argument main_arg;
34  volatile rtems_task_argument worker_arg;
35  volatile bool terminated;
36  SMP_barrier_Control barrier;
37  SMP_barrier_State main_barrier_state;
38  SMP_barrier_State worker_barrier_state;
39  Thread_Control *delay_switch_for_executing;
40  rtems_id worker_id;
41} test_context;
42
43static test_context test_instance = {
44  .barrier = SMP_BARRIER_CONTROL_INITIALIZER,
45  .main_barrier_state = SMP_BARRIER_STATE_INITIALIZER,
46  .worker_barrier_state = SMP_BARRIER_STATE_INITIALIZER
47};
48
49static void barrier(test_context *ctx, SMP_barrier_State *state)
50{
51  _SMP_barrier_Wait(&ctx->barrier, state, CPU_COUNT);
52}
53
54static void restart_extension(
55  Thread_Control *executing,
56  Thread_Control *restarted
57)
58{
59  rtems_test_assert(executing == restarted);
60}
61
62static void delete_extension(
63  Thread_Control *executing,
64  Thread_Control *deleted
65)
66{
67  rtems_test_assert(executing != deleted);
68}
69
70static void terminate_extension(Thread_Control *executing)
71{
72  test_context *ctx = &test_instance;
73
74  ctx->terminated = true;
75}
76
77static void switch_extension(Thread_Control *executing, Thread_Control *heir)
78{
79  test_context *ctx = &test_instance;
80
81  if (ctx->delay_switch_for_executing == executing) {
82    ctx->delay_switch_for_executing = NULL;
83
84    /* (A) */
85    barrier(ctx, &ctx->worker_barrier_state);
86
87    rtems_counter_delay_nanoseconds(100000000);
88
89    /* Avoid bad profiling statisitics */
90    _Profiling_Thread_dispatch_disable( _Per_CPU_Get(), 0 );
91  }
92}
93
94static void worker_task(rtems_task_argument arg)
95{
96  test_context *ctx = &test_instance;
97
98  rtems_test_assert(arg == ctx->main_arg);
99
100  ctx->worker_arg = arg;
101
102  /* (B) */
103  barrier(ctx, &ctx->worker_barrier_state);
104
105  while (true) {
106    /* Do nothing */
107  }
108}
109
110static void test_restart(void)
111{
112  test_context *ctx = &test_instance;
113  rtems_status_code sc;
114  rtems_id id;
115  rtems_task_argument arg;
116  rtems_resource_snapshot snapshot;
117
118  rtems_resource_snapshot_take(&snapshot);
119
120  sc = rtems_task_create(
121    rtems_build_name('W', 'O', 'R', 'K'),
122    1,
123    RTEMS_MINIMUM_STACK_SIZE,
124    RTEMS_DEFAULT_MODES,
125    RTEMS_DEFAULT_ATTRIBUTES,
126    &id
127  );
128  rtems_test_assert(sc == RTEMS_SUCCESSFUL);
129
130  sc = rtems_task_start(id, worker_task, 0);
131  rtems_test_assert(sc == RTEMS_SUCCESSFUL);
132
133  /* (B) */
134  barrier(ctx, &ctx->main_barrier_state);
135
136  for (arg = 1; arg < 23; ++arg) {
137    ctx->main_arg = arg;
138    ctx->worker_arg = 0;
139
140    sc = rtems_task_restart(id, arg);
141    rtems_test_assert(sc == RTEMS_SUCCESSFUL);
142
143    /* (B) */
144    barrier(ctx, &ctx->main_barrier_state);
145
146    rtems_test_assert(ctx->worker_arg == arg);
147  }
148
149  sc = rtems_task_delete(id);
150  rtems_test_assert(sc == RTEMS_SUCCESSFUL);
151
152  rtems_test_assert(rtems_resource_snapshot_check(&snapshot));
153}
154
155static void test_delete(void)
156{
157  test_context *ctx = &test_instance;
158  rtems_status_code sc;
159  rtems_id id;
160  rtems_task_argument arg;
161  rtems_resource_snapshot snapshot;
162
163  rtems_resource_snapshot_take(&snapshot);
164
165  for (arg = 31; arg < 57; ++arg) {
166    ctx->main_arg = arg;
167    ctx->worker_arg = 0;
168    ctx->terminated = false;
169
170    sc = rtems_task_create(
171      rtems_build_name('W', 'O', 'R', 'K'),
172      1,
173      RTEMS_MINIMUM_STACK_SIZE,
174      RTEMS_DEFAULT_MODES,
175      RTEMS_DEFAULT_ATTRIBUTES,
176      &id
177    );
178    rtems_test_assert(sc == RTEMS_SUCCESSFUL);
179
180    sc = rtems_task_start(id, worker_task, arg);
181    rtems_test_assert(sc == RTEMS_SUCCESSFUL);
182
183    /* (B) */
184    barrier(ctx, &ctx->main_barrier_state);
185
186    rtems_test_assert(ctx->worker_arg == arg);
187    rtems_test_assert(!ctx->terminated);
188
189    sc = rtems_task_delete(id);
190    rtems_test_assert(sc == RTEMS_SUCCESSFUL);
191
192    rtems_test_assert(ctx->terminated);
193
194    rtems_test_assert(rtems_resource_snapshot_check(&snapshot));
195  }
196}
197
198static void delay_ipi_task(rtems_task_argument variant)
199{
200  test_context *ctx = &test_instance;
201  ISR_Level level;
202
203  _ISR_Disable_without_giant(level);
204
205  /* (C) */
206  barrier(ctx, &ctx->worker_barrier_state);
207
208  /*
209   * Interrupts are disabled, so the inter-processor interrupt deleting us will
210   * be delayed a bit.
211   */
212  rtems_counter_delay_nanoseconds(100000000);
213
214  if (variant != 0) {
215    _Thread_Disable_dispatch();
216  }
217
218  _ISR_Enable_without_giant(level);
219
220  /*
221   * We get deleted as a side effect of enabling the thread life protection or
222   * later if we enable the thread dispatching.
223   */
224  _Thread_Set_life_protection(true);
225
226  if (variant != 0) {
227    _Thread_Enable_dispatch();
228  }
229
230  rtems_test_assert(0);
231}
232
233static void test_set_life_protection(rtems_task_argument variant)
234{
235  test_context *ctx = &test_instance;
236  rtems_status_code sc;
237  rtems_id id;
238  rtems_resource_snapshot snapshot;
239
240  rtems_resource_snapshot_take(&snapshot);
241
242  sc = rtems_task_create(
243    rtems_build_name('D', 'E', 'L', 'Y'),
244    1,
245    RTEMS_MINIMUM_STACK_SIZE,
246    RTEMS_DEFAULT_MODES,
247    RTEMS_DEFAULT_ATTRIBUTES,
248    &id
249  );
250  rtems_test_assert(sc == RTEMS_SUCCESSFUL);
251
252  sc = rtems_task_start(id, delay_ipi_task, variant);
253  rtems_test_assert(sc == RTEMS_SUCCESSFUL);
254
255  /* (C) */
256  barrier(ctx, &ctx->main_barrier_state);
257
258  sc = rtems_task_delete(id);
259  rtems_test_assert(sc == RTEMS_SUCCESSFUL);
260
261  rtems_test_assert(rtems_resource_snapshot_check(&snapshot));
262}
263
264static void delay_switch_task(rtems_task_argument arg)
265{
266  test_context *ctx = &test_instance;
267  rtems_status_code sc;
268  ISR_Level level;
269
270  _ISR_Disable_without_giant(level);
271  (void) level;
272
273  ctx->delay_switch_for_executing = _Thread_Get_executing();
274
275  /* (D) */
276  barrier(ctx, &ctx->worker_barrier_state);
277
278  sc = rtems_task_delete(RTEMS_SELF);
279  rtems_test_assert(sc == RTEMS_SUCCESSFUL);
280}
281
282static void test_wait_for_execution_stop(void)
283{
284  test_context *ctx = &test_instance;
285  rtems_status_code sc;
286  rtems_id id;
287  rtems_resource_snapshot snapshot;
288
289  rtems_resource_snapshot_take(&snapshot);
290
291  sc = rtems_task_create(
292    rtems_build_name('S', 'W', 'I', 'T'),
293    1,
294    RTEMS_MINIMUM_STACK_SIZE,
295    RTEMS_DEFAULT_MODES,
296    RTEMS_DEFAULT_ATTRIBUTES,
297    &id
298  );
299  rtems_test_assert(sc == RTEMS_SUCCESSFUL);
300
301  sc = rtems_task_start(id, delay_switch_task, 0);
302  rtems_test_assert(sc == RTEMS_SUCCESSFUL);
303
304  /* (D) */
305  barrier(ctx, &ctx->main_barrier_state);
306
307  /* (A) */
308  barrier(ctx, &ctx->main_barrier_state);
309
310  sc = rtems_task_create(
311    rtems_build_name('W', 'A', 'I', 'T'),
312    1,
313    RTEMS_MINIMUM_STACK_SIZE,
314    RTEMS_DEFAULT_MODES,
315    RTEMS_DEFAULT_ATTRIBUTES,
316    &id
317  );
318  rtems_test_assert(sc == RTEMS_SUCCESSFUL);
319
320  sc = rtems_task_delete(id);
321  rtems_test_assert(sc == RTEMS_SUCCESSFUL);
322
323  rtems_test_assert(rtems_resource_snapshot_check(&snapshot));
324}
325
326typedef enum {
327  TEST_OP_SUSPEND,
328  TEST_OP_EVENT,
329  TEST_OP_EVENT_SYSTEM
330} test_op;
331
332static void op_begin_suspend(void)
333{
334  rtems_task_suspend(RTEMS_SELF);
335  rtems_test_assert(0);
336}
337
338static void op_begin_event(void)
339{
340  rtems_event_set events;
341
342  rtems_event_receive(
343    RTEMS_EVENT_0,
344    RTEMS_EVENT_ALL | RTEMS_WAIT,
345    RTEMS_NO_TIMEOUT,
346    &events
347  );
348  rtems_test_assert(0);
349}
350
351static void op_begin_event_system(void)
352{
353  rtems_event_set events;
354
355  rtems_event_system_receive(
356    RTEMS_EVENT_0,
357    RTEMS_EVENT_ALL | RTEMS_WAIT,
358    RTEMS_NO_TIMEOUT,
359    &events
360  );
361  rtems_test_assert(0);
362}
363
364static void (*const test_ops_begin[])(void) = {
365  op_begin_suspend,
366  op_begin_event,
367  op_begin_event_system
368};
369
370static void op_end_suspend(test_context *ctx)
371{
372  rtems_status_code sc;
373
374  sc = rtems_task_resume(ctx->worker_id);
375  rtems_test_assert(sc == RTEMS_SUCCESSFUL);
376}
377
378static void op_end_event(test_context *ctx)
379{
380  rtems_status_code sc;
381
382  sc = rtems_event_send(ctx->worker_id, RTEMS_EVENT_0);
383  rtems_test_assert(sc == RTEMS_SUCCESSFUL);
384}
385
386static void op_end_event_system(test_context *ctx)
387{
388  rtems_status_code sc;
389
390  sc = rtems_event_system_send(ctx->worker_id, RTEMS_EVENT_0);
391  rtems_test_assert(sc == RTEMS_SUCCESSFUL);
392}
393
394static void (*const test_ops_end[])(test_context *) = {
395  op_end_suspend,
396  op_end_event,
397  op_end_event_system
398};
399
400static void op_worker_task(rtems_task_argument arg)
401{
402  test_context *ctx = &test_instance;
403  test_op op = arg;
404  ISR_Level level;
405
406  _ISR_Disable_without_giant(level);
407  (void) level;
408
409  /* (E) */
410  barrier(ctx, &ctx->worker_barrier_state);
411
412  /* (F) */
413  barrier(ctx, &ctx->worker_barrier_state);
414
415  (*test_ops_begin[op])();
416}
417
418static void help_task(rtems_task_argument arg)
419{
420  test_context *ctx = &test_instance;
421  test_op op = arg;
422
423  /* (F) */
424  barrier(ctx, &ctx->main_barrier_state);
425
426  rtems_counter_delay_nanoseconds(100000000);
427
428  (*test_ops_end[op])(ctx);
429
430  rtems_task_suspend(RTEMS_SELF);
431  rtems_test_assert(0);
432}
433
434static void test_operation_with_delete_in_progress(test_op op)
435{
436  test_context *ctx = &test_instance;
437  rtems_status_code sc;
438  rtems_id help_id;
439  rtems_resource_snapshot snapshot;
440
441  rtems_resource_snapshot_take(&snapshot);
442
443  sc = rtems_task_create(
444    rtems_build_name('W', 'O', 'R', 'K'),
445    1,
446    RTEMS_MINIMUM_STACK_SIZE,
447    RTEMS_DEFAULT_MODES,
448    RTEMS_DEFAULT_ATTRIBUTES,
449    &ctx->worker_id
450  );
451  rtems_test_assert(sc == RTEMS_SUCCESSFUL);
452
453  sc = rtems_task_start(ctx->worker_id, op_worker_task, op);
454  rtems_test_assert(sc == RTEMS_SUCCESSFUL);
455
456  /* (E) */
457  barrier(ctx, &ctx->main_barrier_state);
458
459  sc = rtems_task_create(
460    rtems_build_name('H', 'E', 'L', 'P'),
461    2,
462    RTEMS_MINIMUM_STACK_SIZE,
463    RTEMS_DEFAULT_MODES,
464    RTEMS_DEFAULT_ATTRIBUTES,
465    &help_id
466  );
467  rtems_test_assert(sc == RTEMS_SUCCESSFUL);
468
469  sc = rtems_task_start(help_id, help_task, op);
470  rtems_test_assert(sc == RTEMS_SUCCESSFUL);
471
472  sc = rtems_task_delete(ctx->worker_id);
473  rtems_test_assert(sc == RTEMS_SUCCESSFUL);
474
475  sc = rtems_task_delete(help_id);
476  rtems_test_assert(sc == RTEMS_SUCCESSFUL);
477
478  rtems_test_assert(rtems_resource_snapshot_check(&snapshot));
479}
480
481static void Init(rtems_task_argument arg)
482{
483  TEST_BEGIN();
484
485  if (rtems_get_processor_count() >= CPU_COUNT) {
486    test_restart();
487    test_delete();
488    test_set_life_protection(0);
489    test_set_life_protection(1);
490    test_wait_for_execution_stop();
491    test_operation_with_delete_in_progress(TEST_OP_SUSPEND);
492    test_operation_with_delete_in_progress(TEST_OP_EVENT);
493    test_operation_with_delete_in_progress(TEST_OP_EVENT_SYSTEM);
494  }
495
496  TEST_END();
497  rtems_test_exit(0);
498}
499
500#define CONFIGURE_APPLICATION_DOES_NOT_NEED_CLOCK_DRIVER
501#define CONFIGURE_APPLICATION_NEEDS_CONSOLE_DRIVER
502
503#define CONFIGURE_SMP_APPLICATION
504
505#define CONFIGURE_SMP_MAXIMUM_PROCESSORS CPU_COUNT
506
507#define CONFIGURE_MAXIMUM_TASKS (CPU_COUNT + 1)
508
509#define CONFIGURE_INITIAL_EXTENSIONS \
510  { \
511    .thread_restart = restart_extension, \
512    .thread_delete = delete_extension, \
513    .thread_terminate = terminate_extension, \
514    .thread_switch = switch_extension \
515  }, \
516  RTEMS_TEST_INITIAL_EXTENSION
517
518#define CONFIGURE_RTEMS_INIT_TASKS_TABLE
519
520#define CONFIGURE_INIT
521
522#include <rtems/confdefs.h>
Note: See TracBrowser for help on using the repository browser.