source: rtems/testsuites/validation/tc-score-smp-thread.c @ 74a6b33f

Last change on this file since 74a6b33f was 74a6b33f, checked in by Sebastian Huber <sebastian.huber@…>, on 03/17/23 at 14:52:33

validation: Replace enum Event with defines

There is a Doxygen limitation that all compound names (enum, struct,
class, union, group) within a project must be unique.

Update #3716.

  • Property mode set to 100644
File size: 15.0 KB
Line 
1/* SPDX-License-Identifier: BSD-2-Clause */
2
3/**
4 * @file
5 *
6 * @ingroup ScoreThreadValSmp
7 */
8
9/*
10 * Copyright (C) 2021 embedded brains GmbH (http://www.embedded-brains.de)
11 *
12 * Redistribution and use in source and binary forms, with or without
13 * modification, are permitted provided that the following conditions
14 * are met:
15 * 1. Redistributions of source code must retain the above copyright
16 *    notice, this list of conditions and the following disclaimer.
17 * 2. Redistributions in binary form must reproduce the above copyright
18 *    notice, this list of conditions and the following disclaimer in the
19 *    documentation and/or other materials provided with the distribution.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
22 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
25 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
29 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31 * POSSIBILITY OF SUCH DAMAGE.
32 */
33
34/*
35 * This file is part of the RTEMS quality process and was automatically
36 * generated.  If you find something that needs to be fixed or
37 * worded better please post a report or patch to an RTEMS mailing list
38 * or raise a bug report:
39 *
40 * https://www.rtems.org/bugs.html
41 *
42 * For information on updating and regenerating please refer to the How-To
43 * section in the Software Requirements Engineering chapter of the
44 * RTEMS Software Engineering manual.  The manual is provided as a part of
45 * a release.  For development sources please refer to the online
46 * documentation at:
47 *
48 * https://docs.rtems.org
49 */
50
51#ifdef HAVE_CONFIG_H
52#include "config.h"
53#endif
54
55#include <rtems.h>
56#include <rtems/test-scheduler.h>
57#include <rtems/score/smpbarrier.h>
58#include <rtems/score/threadimpl.h>
59
60#include "ts-config.h"
61#include "tx-support.h"
62
63#include <rtems/test.h>
64
65/**
66 * @defgroup ScoreThreadValSmp spec:/score/thread/val/smp
67 *
68 * @ingroup TestsuitesValidationSmpOnly0
69 *
70 * @brief Tests SMP-specific thread behaviour.
71 *
72 * This test case performs the following actions:
73 *
74 * - Create three worker threads and a mutex.  Use the mutex and the worker to
75 *   move to a helping scheduler.
76 *
77 *   - Pin the runner thread while it executes on a processor owned by a
78 *     helping scheduler.
79 *
80 *   - Pin and unpin the runner thread.  This is a nested operation.
81 *
82 *   - Preempt the pinned runner thread.  Worker B and C execute at the same
83 *     time on processor 0 and 1 respectively for some point in time.  This
84 *     shows that the pinning of the runner thread is maintained.
85 *
86 *   - Unpin the runner thread.  The runner moves back to its home scheduler.
87 *
88 *   - Release the mutex.
89 *
90 *   - Pin the runner thread.  Unpin the runner thread while it is suspended.
91 *
92 *   - Make sure the worker released the mutex.
93 *
94 *   - Clean up all used resources.
95 *
96 * - Create three worker threads and a mutex.  Use the mutex and the worker to
97 *   check that a suspended thread does not reconsider help requests.
98 *
99 *   - Let worker B help worker A through the mutex.  Preempt worker A.  Delay
100 *     the thread switch to worker A.
101 *
102 *   - Suspend worker A and let it wait on its thread state lock.  Check that
103 *     worker A did not reconsider help requests.
104 *
105 *   - Resume worker A.  Check that worker A did reconsider help requests after
106 *     the thread dispatch.
107 *
108 *   - Clean up all used resources.
109 *
110 * @{
111 */
112
113/**
114 * @brief Test context for spec:/score/thread/val/smp test case.
115 */
116typedef struct {
117  /**
118   * @brief This member contains the worker A identifier.
119   */
120  rtems_id worker_a_id;
121
122  /**
123   * @brief This member contains the worker B identifier.
124   */
125  rtems_id worker_b_id;
126
127  /**
128   * @brief This member contains the worker C identifier.
129   */
130  rtems_id worker_c_id;
131
132  /**
133   * @brief This member contains the mutex identifier.
134   */
135  rtems_id mutex_id;
136
137  /**
138   * @brief If this member is true, then the worker shall busy wait.
139   */
140  volatile bool busy;
141
142  /**
143   * @brief This member contains a counter for EVENT_COUNT.
144   */
145  volatile uint32_t counter;
146
147  /**
148   * @brief This member contains the barrier to synchronize the runner and the
149   *   workers.
150   */
151  SMP_barrier_Control barrier;
152
153  /**
154   * @brief This member contains the barrier state for the runner processor.
155   */
156  SMP_barrier_State barrier_state;
157} ScoreThreadValSmp_Context;
158
159static ScoreThreadValSmp_Context
160  ScoreThreadValSmp_Instance;
161
162#define EVENT_OBTAIN RTEMS_EVENT_0
163
164#define EVENT_RELEASE RTEMS_EVENT_1
165
166#define EVENT_COUNT_EARLY RTEMS_EVENT_2
167
168#define EVENT_BUSY RTEMS_EVENT_3
169
170#define EVENT_COUNT RTEMS_EVENT_4
171
172#define EVENT_LET_WORKER_C_COUNT RTEMS_EVENT_5
173
174#define EVENT_SET_TASK_SWITCH_EXTENSION RTEMS_EVENT_6
175
176typedef ScoreThreadValSmp_Context Context;
177
178static void TaskSwitchExtension( rtems_tcb *executing, rtems_tcb *heir )
179{
180  Context        *ctx;
181  Thread_Control *thread;
182
183  (void) executing;
184  (void) heir;
185
186  ctx = T_fixture_context();
187  thread = GetThread( ctx->worker_a_id );
188
189  if ( thread == heir ) {
190    SMP_barrier_State state;
191
192    _SMP_barrier_State_initialize( &state );
193
194    /* B0 */
195    _SMP_barrier_Wait( &ctx->barrier, &state, 2 );
196
197    /* B1 */
198    _SMP_barrier_Wait( &ctx->barrier, &state, 2 );
199  }
200}
201
202static void WorkerTask( rtems_task_argument arg )
203{
204  Context *ctx;
205
206  ctx = (Context *) arg;
207
208  while ( true ) {
209    rtems_event_set events;
210
211    events = ReceiveAnyEvents();
212
213    if ( ( events & EVENT_OBTAIN ) != 0 ) {
214      ObtainMutex( ctx->mutex_id );
215    }
216
217    if ( ( events & EVENT_RELEASE ) != 0 ) {
218      ReleaseMutex( ctx->mutex_id );
219    }
220
221    if ( ( events & EVENT_COUNT_EARLY ) != 0 ) {
222      ++ctx->counter;
223    }
224
225    if ( ( events & EVENT_BUSY ) != 0 ) {
226      while ( ctx->busy ) {
227        /* Do nothing */
228      }
229    }
230
231    if ( ( events & EVENT_COUNT ) != 0 ) {
232      ++ctx->counter;
233    }
234
235    if ( ( events & EVENT_LET_WORKER_C_COUNT ) != 0 ) {
236      uint32_t counter;
237
238      counter = ctx->counter;
239      SendEvents( ctx->worker_c_id, EVENT_COUNT );
240
241      while ( ctx->counter == counter ) {
242        /* Wait */
243      }
244    }
245
246    if ( ( events & EVENT_SET_TASK_SWITCH_EXTENSION ) != 0 ) {
247      SetTaskSwitchExtension( TaskSwitchExtension );
248    }
249  }
250}
251
252static void SchedulerBlock(
253  void                    *arg,
254  const T_scheduler_event *event,
255  T_scheduler_when         when
256)
257{
258  Context *ctx;
259
260  ctx = arg;
261
262  if (
263    when == T_SCHEDULER_BEFORE &&
264    event->operation == T_SCHEDULER_BLOCK
265  ) {
266    Thread_Control *thread;
267
268    T_scheduler_set_event_handler( NULL, NULL );
269
270    /* B1 */
271    _SMP_barrier_Wait( &ctx->barrier, &ctx->barrier_state, 2 );
272
273    thread = GetThread( ctx->worker_a_id );
274    TicketLockWaitForOthers( &thread->Join_queue.Queue.Lock, 1 );
275  }
276}
277
278static void Suspend( void *arg )
279{
280  Thread_Control *thread;
281
282  thread = arg;
283  SuspendTask( thread->Object.id );
284}
285
286static void Resume( void *arg )
287{
288  Thread_Control *thread;
289
290  thread = arg;
291  ResumeTask( thread->Object.id );
292}
293
294static void WaitForCounter( const Context *ctx, uint32_t expected )
295{
296  while ( ctx->counter != expected ) {
297    /* Wait */
298  }
299}
300
301static void ScoreThreadValSmp_Setup( ScoreThreadValSmp_Context *ctx )
302{
303  SetSelfPriority( PRIO_NORMAL );
304}
305
306static void ScoreThreadValSmp_Setup_Wrap( void *arg )
307{
308  ScoreThreadValSmp_Context *ctx;
309
310  ctx = arg;
311  ScoreThreadValSmp_Setup( ctx );
312}
313
314static void ScoreThreadValSmp_Teardown( ScoreThreadValSmp_Context *ctx )
315{
316  RestoreRunnerPriority();
317}
318
319static void ScoreThreadValSmp_Teardown_Wrap( void *arg )
320{
321  ScoreThreadValSmp_Context *ctx;
322
323  ctx = arg;
324  ScoreThreadValSmp_Teardown( ctx );
325}
326
327static T_fixture ScoreThreadValSmp_Fixture = {
328  .setup = ScoreThreadValSmp_Setup_Wrap,
329  .stop = NULL,
330  .teardown = ScoreThreadValSmp_Teardown_Wrap,
331  .scope = NULL,
332  .initial_context = &ScoreThreadValSmp_Instance
333};
334
335/**
336 * @brief Create three worker threads and a mutex.  Use the mutex and the
337 *   worker to move to a helping scheduler.
338 */
339static void ScoreThreadValSmp_Action_0( ScoreThreadValSmp_Context *ctx )
340{
341  Per_CPU_Control*cpu_self;
342  Thread_Control *executing;
343
344  executing = _Thread_Get_executing();
345  ctx->counter = 0;
346
347  ctx->mutex_id = CreateMutex();
348
349  ctx->worker_a_id = CreateTask( "WRKA", PRIO_NORMAL );
350  SetScheduler( ctx->worker_a_id, SCHEDULER_B_ID, PRIO_NORMAL );
351  StartTask( ctx->worker_a_id, WorkerTask, ctx );
352
353  ctx->worker_b_id = CreateTask( "WRKB", PRIO_HIGH );
354  StartTask( ctx->worker_b_id, WorkerTask, ctx );
355
356  ctx->worker_c_id = CreateTask( "WRKC", PRIO_LOW );
357  StartTask( ctx->worker_c_id, WorkerTask, ctx );
358
359  ObtainMutex( ctx->mutex_id );
360  SendEvents( ctx->worker_a_id, EVENT_OBTAIN | EVENT_RELEASE );
361
362  ctx->busy = true;
363  SendEvents( ctx->worker_b_id, EVENT_BUSY );
364
365  /*
366   * Pin the runner thread while it executes on a processor owned by a helping
367   * scheduler.
368   */
369  T_eq_u32( rtems_scheduler_get_processor(), 1 );
370  _Thread_Pin( executing );
371
372  /*
373   * Pin and unpin the runner thread.  This is a nested operation.
374   */
375  T_eq_u32( rtems_scheduler_get_processor(), 1 );
376  _Thread_Pin( executing );
377  _Thread_Unpin( executing, _Per_CPU_Get_snapshot() );
378
379  /*
380   * Preempt the pinned runner thread.  Worker B and C execute at the same time
381   * on processor 0 and 1 respectively for some point in time.  This shows that
382   * the pinning of the runner thread is maintained.
383   */
384  ctx->busy = false;
385  SetScheduler( ctx->worker_b_id, SCHEDULER_B_ID, PRIO_HIGH );
386  SendEvents( ctx->worker_b_id, EVENT_LET_WORKER_C_COUNT );
387
388  T_eq_u32( rtems_scheduler_get_processor(), 1 );
389  T_eq_u32( ctx->counter, 1 );
390
391  /*
392   * Unpin the runner thread.  The runner moves back to its home scheduler.
393   */
394  cpu_self = _Thread_Dispatch_disable();
395  _Thread_Unpin( executing, cpu_self );
396  _Thread_Dispatch_direct( cpu_self );
397
398  T_eq_u32( rtems_scheduler_get_processor(), 0 );
399
400  /*
401   * Release the mutex.
402   */
403  ReleaseMutex( ctx->mutex_id);
404  T_eq_u32( rtems_scheduler_get_processor(), 0 );
405
406  /*
407   * Pin the runner thread.  Unpin the runner thread while it is suspended.
408   */
409  _Thread_Pin( executing );
410
411  /* We have to preempt the runner to end up in _Thread_Do_unpin() */
412  SetPriority( ctx->worker_c_id, PRIO_HIGH );
413  SendEvents( ctx->worker_c_id, EVENT_COUNT );
414  T_eq_u32( ctx->counter, 2 );
415
416  cpu_self = _Thread_Dispatch_disable();
417  CallWithinISR( Suspend, executing );
418  _Thread_Unpin( executing, cpu_self );
419  CallWithinISR( Resume, executing );
420  _Thread_Dispatch_direct( cpu_self );
421
422  /*
423   * Make sure the worker released the mutex.
424   */
425  SetSelfScheduler( SCHEDULER_B_ID, PRIO_LOW );
426  SetSelfScheduler( SCHEDULER_A_ID, PRIO_NORMAL );
427
428  /*
429   * Clean up all used resources.
430   */
431  DeleteTask( ctx->worker_a_id );
432  DeleteTask( ctx->worker_b_id );
433  DeleteTask( ctx->worker_c_id );
434  DeleteMutex( ctx->mutex_id );
435}
436
437/**
438 * @brief Create three worker threads and a mutex.  Use the mutex and the
439 *   worker to check that a suspended thread does not reconsider help requests.
440 */
441static void ScoreThreadValSmp_Action_1( ScoreThreadValSmp_Context *ctx )
442{
443  T_scheduler_log_10       scheduler_log;
444  size_t                   index;
445  const T_scheduler_event *event;
446
447  _SMP_barrier_Control_initialize( &ctx->barrier );
448  _SMP_barrier_State_initialize( &ctx->barrier_state );
449
450  ctx->counter = 0;
451  ctx->mutex_id = CreateMutex();
452
453  ctx->worker_a_id = CreateTask( "WRKA", PRIO_NORMAL );
454  SetScheduler( ctx->worker_a_id, SCHEDULER_B_ID, PRIO_NORMAL );
455  StartTask( ctx->worker_a_id, WorkerTask, ctx );
456
457  ctx->worker_b_id = CreateTask( "WRKB", PRIO_HIGH );
458  StartTask( ctx->worker_b_id, WorkerTask, ctx );
459
460  ctx->worker_c_id = CreateTask( "WRKC", PRIO_NORMAL );
461  SetScheduler( ctx->worker_c_id, SCHEDULER_B_ID, PRIO_HIGH );
462  StartTask( ctx->worker_c_id, WorkerTask, ctx );
463
464  /*
465   * Let worker B help worker A through the mutex.  Preempt worker A.  Delay
466   * the thread switch to worker A.
467   */
468  ctx->busy = true;
469  SendEvents(
470    ctx->worker_a_id,
471    EVENT_OBTAIN | EVENT_COUNT_EARLY | EVENT_BUSY | EVENT_COUNT
472  );
473  WaitForCounter( ctx, 1 );
474
475  SendEvents( ctx->worker_b_id, EVENT_OBTAIN );
476  SetPriority( ctx->worker_b_id, PRIO_LOW );
477  SendEvents( ctx->worker_c_id, EVENT_SET_TASK_SWITCH_EXTENSION );
478
479  /* B0 */
480  _SMP_barrier_Wait( &ctx->barrier, &ctx->barrier_state, 2 );
481
482  /*
483   * Suspend worker A and let it wait on its thread state lock.  Check that
484   * worker A did not reconsider help requests.
485   */
486  T_scheduler_record_10( &scheduler_log );
487  T_scheduler_set_event_handler( SchedulerBlock, ctx );
488  SuspendTask( ctx->worker_a_id );
489  WaitForExecutionStop( ctx->worker_a_id );
490  T_scheduler_record( NULL );
491  T_eq_sz( scheduler_log.header.recorded, 2 );
492  index = 0;
493  event = T_scheduler_next_any( &scheduler_log.header, &index );
494  T_eq_int( event->operation, T_SCHEDULER_BLOCK );
495  event = T_scheduler_next_any( &scheduler_log.header, &index );
496  T_eq_int( event->operation, T_SCHEDULER_WITHDRAW_NODE );
497  event = T_scheduler_next_any( &scheduler_log.header, &index );
498  T_eq_ptr( event, &T_scheduler_event_null );
499  SetTaskSwitchExtension( NULL );
500
501  /*
502   * Resume worker A.  Check that worker A did reconsider help requests after
503   * the thread dispatch.
504   */
505  T_scheduler_record_10( &scheduler_log );
506  ResumeTask( ctx->worker_a_id );
507  ctx->busy = false;
508  WaitForCounter( ctx, 2 );
509  WaitForExecutionStop( ctx->worker_a_id );
510  T_scheduler_record( NULL );
511  T_eq_sz( scheduler_log.header.recorded, 5 );
512  index = 0;
513  event = T_scheduler_next_any( &scheduler_log.header, &index );
514  T_eq_int( event->operation, T_SCHEDULER_UNBLOCK );
515  event = T_scheduler_next_any( &scheduler_log.header, &index );
516  T_eq_int( event->operation, T_SCHEDULER_RECONSIDER_HELP_REQUEST );
517  event = T_scheduler_next_any( &scheduler_log.header, &index );
518  T_eq_int( event->operation, T_SCHEDULER_RECONSIDER_HELP_REQUEST );
519  event = T_scheduler_next_any( &scheduler_log.header, &index );
520  T_eq_int( event->operation, T_SCHEDULER_BLOCK );
521  event = T_scheduler_next_any( &scheduler_log.header, &index );
522  T_eq_int( event->operation, T_SCHEDULER_WITHDRAW_NODE );
523  event = T_scheduler_next_any( &scheduler_log.header, &index );
524  T_eq_ptr( event, &T_scheduler_event_null );
525
526  /*
527   * Clean up all used resources.
528   */
529  SendEvents( ctx->worker_a_id, EVENT_RELEASE | EVENT_COUNT );
530  WaitForCounter( ctx, 3 );
531
532  SetPriority( ctx->worker_b_id, PRIO_HIGH );
533  SendEvents( ctx->worker_b_id, EVENT_RELEASE );
534
535  DeleteTask( ctx->worker_a_id );
536  DeleteTask( ctx->worker_b_id );
537  DeleteTask( ctx->worker_c_id );
538  DeleteMutex( ctx->mutex_id );
539}
540
541/**
542 * @fn void T_case_body_ScoreThreadValSmp( void )
543 */
544T_TEST_CASE_FIXTURE( ScoreThreadValSmp, &ScoreThreadValSmp_Fixture )
545{
546  ScoreThreadValSmp_Context *ctx;
547
548  ctx = T_fixture_context();
549
550  ScoreThreadValSmp_Action_0( ctx );
551  ScoreThreadValSmp_Action_1( ctx );
552}
553
554/** @} */
Note: See TracBrowser for help on using the repository browser.