summaryrefslogtreecommitdiffstats
path: root/cpukit/libmisc/testsupport/test.h
blob: a32e2c42c1e453d214e98f826375cb0f02e25dc7 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
/*
 * Copyright (c) 2014, 2016 embedded brains GmbH.  All rights reserved.
 *
 *  embedded brains GmbH
 *  Dornierstr. 4
 *  82178 Puchheim
 *  Germany
 *  <rtems@embedded-brains.de>
 *
 * The license and distribution terms for this file may be
 * found in the file LICENSE in this distribution or at
 * http://www.rtems.org/license/LICENSE.
 */

#ifndef _RTEMS_TEST_H
#define _RTEMS_TEST_H

#include <rtems.h>
#include <rtems/printer.h>
#include <rtems/score/atomic.h>
#include <rtems/score/smpbarrier.h>

#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */

/**
 * @defgroup RTEMSTest Test Support
 *
 * @brief Test support functions.
 *
 * @{
 */

/**
 * @brief Each test must define a test name string.
 */
extern const char rtems_test_name[];

/**
 * @brief Each test must define a printer.
 */
extern rtems_printer rtems_test_printer;

/**
 * @brief Fatal extension for tests.
 */
void rtems_test_fatal_extension(
  rtems_fatal_source source,
  bool always_set_to_false,
  rtems_fatal_code code
);

/**
 * @brief Initial extension for tests.
 */
#define RTEMS_TEST_INITIAL_EXTENSION \
  { NULL, NULL, NULL, NULL, NULL, NULL, NULL, rtems_test_fatal_extension }

/**
 * @brief Begin of test message format string.
 */
#define TEST_BEGIN_STRING "\n\n*** BEGIN OF TEST %s ***\n", rtems_test_name

/**
 * @brief End of test message format string.
 */
#define TEST_END_STRING "*** END OF TEST %s ***\n", rtems_test_name

/**
 * @brief Prints a begin of test message using printf().
 *
 * @returns As specified by printf().
 */
int rtems_test_begin(void);

/**
 * @brief Prints an end of test message using printf().
 *
 * @returns As specified by printf().
 */
int rtems_test_end(void);

/**
 * @brief Prints via the RTEMS printer.
 *
 * @returns As specified by printf().
 */
int rtems_test_printf(const char* format, ...) RTEMS_PRINTFLIKE(1, 2);

#define RTEMS_TEST_PARALLEL_PROCESSOR_MAX 32

typedef struct rtems_test_parallel_job rtems_test_parallel_job;

/**
 * @brief Internal context for parallel job execution.
 */
typedef struct {
  Atomic_Ulong stop;
  SMP_barrier_Control barrier;
  size_t worker_count;
  rtems_id worker_ids[RTEMS_TEST_PARALLEL_PROCESSOR_MAX];
  rtems_id stop_worker_timer_id;
  const struct rtems_test_parallel_job *jobs;
  size_t job_count;
} rtems_test_parallel_context;

/**
 * @brief Worker task setup handler.
 *
 * Called during rtems_test_parallel() to optionally setup a worker task before
 * it is started.
 *
 * @param[in] ctx The parallel context.
 * @param[in] worker_index The worker index.
 * @param[in] worker_id The worker task identifier.
 */
typedef void (*rtems_test_parallel_worker_setup)(
  rtems_test_parallel_context *ctx,
  size_t worker_index,
  rtems_id worker_id
);

/**
 * @brief Basic parallel job description.
 */
struct rtems_test_parallel_job {
  /**
   * @brief Job initialization handler.
   *
   * This handler executes only in the context of the master worker before the
   * job body handler.
   *
   * @param[in] ctx The parallel context.
   * @param[in] arg The user specified argument.
   * @param[in] active_workers Count of active workers.  Depends on the cascade
   *   option.
   *
   * @return The desired job body execution time in clock ticks.  See
   *   rtems_test_parallel_stop_job().
   */
  rtems_interval (*init)(
    rtems_test_parallel_context *ctx,
    void *arg,
    size_t active_workers
  );

  /**
   * @brief Job body handler.
   *
   * @param[in] ctx The parallel context.
   * @param[in] arg The user specified argument.
   * @param[in] active_workers Count of active workers.  Depends on the cascade
   *   option.
   * @param[in] worker_index The worker index.  It ranges from 0 to the
   *   processor count minus one.
   */
  void (*body)(
    rtems_test_parallel_context *ctx,
    void *arg,
    size_t active_workers,
    size_t worker_index
  );

  /**
   * @brief Job finalization handler.
   *
   * This handler executes only in the context of the master worker after the
   * job body handler.
   *
   * @param[in] ctx The parallel context.
   * @param[in] arg The user specified argument.
   * @param[in] active_workers Count of active workers.  Depends on the cascade
   *   option.
   */
  void (*fini)(
    rtems_test_parallel_context *ctx,
    void *arg,
    size_t active_workers
  );

  /**
   * @brief Job specific argument.
   */
  void *arg;

  /**
   * @brief Job cascading flag.
   *
   * This flag indicates whether the job should be executed in a cascaded
   * manner (the job is executed on one processor first, two processors
   * afterwards and incremented step by step until all processors are used).
   */
  bool cascade;
};

/**
 * @brief Indicates if a job body should stop its work loop.
 *
 * @param[in] ctx The parallel context.
 *
 * @retval true The job body should stop its work loop and return to the caller.
 * @retval false Otherwise.
 */
static inline bool rtems_test_parallel_stop_job(
  const rtems_test_parallel_context *ctx
)
{
  return _Atomic_Load_ulong(&ctx->stop, ATOMIC_ORDER_RELAXED) != 0;
}

/**
 * @brief Indicates if a worker is the master worker.
 *
 * The master worker is the thread that called rtems_test_parallel().
 *
 * @param[in] worker_index The worker index.
 *
 * @retval true This is the master worker.
 * @retval false Otherwise.
 */
static inline bool rtems_test_parallel_is_master_worker(size_t worker_index)
{
  return worker_index == 0;
}

/**
 * @brief Returns the task identifier for a worker.
 *
 * @param[in] ctx The parallel context.
 * @param[in] worker_index The worker index.
 *
 * @return The task identifier of the worker.
 */
static inline rtems_id rtems_test_parallel_get_task_id(
  const rtems_test_parallel_context *ctx,
  size_t worker_index
)
{
  return ctx->worker_ids[worker_index];
}

/**
 * @brief Runs a bunch of jobs in parallel on all processors of the system.
 *
 * The worker tasks inherit the priority of the executing task.
 *
 * There are SMP barriers before and after the job body.
 *
 * @param[in] ctx The parallel context.
 * @param[in] worker_setup Optional handler to setup a worker task before it is
 *   started.
 * @param[in] jobs The table of jobs.
 * @param[in] job_count The count of jobs in the job table.
 */
void rtems_test_parallel(
  rtems_test_parallel_context *ctx,
  rtems_test_parallel_worker_setup worker_setup,
  const rtems_test_parallel_job *jobs,
  size_t job_count
);

/** @} */

#ifdef __cplusplus
}
#endif /* __cplusplus */

#endif /* _RTEMS_TEST_H */