[rtems-central commit] spec: Specify user extensions

Sebastian Huber sebh at rtems.org
Mon Aug 9 16:02:28 UTC 2021


Module:    rtems-central
Branch:    master
Commit:    6e24f98c582f0032acfa9accba3bd44834f35a0b
Changeset: http://git.rtems.org/rtems-central/commit/?id=6e24f98c582f0032acfa9accba3bd44834f35a0b

Author:    Sebastian Huber <sebastian.huber at embedded-brains.de>
Date:      Sun Aug  8 15:28:38 2021 +0200

spec: Specify user extensions

---

 config.yml                                         |   2 +-
 .../userext/req/thread-begin-iterate-remove.yml    |  17 +
 spec/rtems/userext/req/thread-begin-order.yml      |  15 +
 .../userext/req/thread-create-allocator-owner.yml  |  17 +
 .../userext/req/thread-create-iterate-remove.yml   |  17 +
 spec/rtems/userext/req/thread-create-order.yml     |  15 +
 .../userext/req/thread-delete-allocator-owner.yml  |  17 +
 .../userext/req/thread-delete-iterate-remove.yml   |  17 +
 spec/rtems/userext/req/thread-delete-order.yml     |  15 +
 .../userext/req/thread-exitted-iterate-remove.yml  |  17 +
 spec/rtems/userext/req/thread-exitted-order.yml    |  15 +
 .../userext/req/thread-restart-iterate-remove.yml  |  17 +
 spec/rtems/userext/req/thread-restart-order.yml    |  15 +
 .../userext/req/thread-start-iterate-remove.yml    |  17 +
 spec/rtems/userext/req/thread-start-order.yml      |  15 +
 spec/rtems/userext/req/thread-switch-order.yml     |  15 +
 .../req/thread-terminate-iterate-remove.yml        |  17 +
 spec/rtems/userext/req/thread-terminate-order.yml  |  15 +
 spec/rtems/userext/val/userext.yml                 | 716 +++++++++++++++++++++
 spec/score/interr/val/terminate.yml                | 110 +++-
 spec/testsuites/terminate.yml                      |   4 +-
 spec/testsuites/userext.yml                        |  44 ++
 22 files changed, 1120 insertions(+), 29 deletions(-)

diff --git a/config.yml b/config.yml
index b03c3be..c91cc29 100644
--- a/config.yml
+++ b/config.yml
@@ -36,7 +36,7 @@ build:
   - gccdeps.py
   - long_gcc.py
   - testsuites/validation/tc-basedefs-pendant.h
-  - testsuites/validation/tc-terminate.h
+  - testsuites/validation/tc-userext.h
   - testsuites/validation/tr-model-chains-api.h
   - testsuites/validation/tr-model-events-mgr.h
   - testsuites/validation/ts-config.h
diff --git a/spec/rtems/userext/req/thread-begin-iterate-remove.yml b/spec/rtems/userext/req/thread-begin-iterate-remove.yml
new file mode 100644
index 0000000..bd96175
--- /dev/null
+++ b/spec/rtems/userext/req/thread-begin-iterate-remove.yml
@@ -0,0 +1,17 @@
+SPDX-License-Identifier: CC-BY-SA-4.0
+copyrights:
+- Copyright (C) 2021 embedded brains GmbH (http://www.embedded-brains.de)
+enabled-by: true
+links:
+- role: requirement-refinement
+  uid: ../if/group
+functional-type: function
+rationale: null
+references: []
+requirement-type: functional
+text: |
+  While the ${/glossary/userextensions:/term} are iterated to invoke the thread
+  begin extensions, when an extension set is removed, the removed extension set
+  shall not be a next extension set for any iteration in progress after the
+  removal.
+type: requirement
diff --git a/spec/rtems/userext/req/thread-begin-order.yml b/spec/rtems/userext/req/thread-begin-order.yml
new file mode 100644
index 0000000..5314def
--- /dev/null
+++ b/spec/rtems/userext/req/thread-begin-order.yml
@@ -0,0 +1,15 @@
+SPDX-License-Identifier: CC-BY-SA-4.0
+copyrights:
+- Copyright (C) 2021 embedded brains GmbH (http://www.embedded-brains.de)
+enabled-by: true
+links:
+- role: requirement-refinement
+  uid: ../if/group
+functional-type: function
+rationale: null
+references: []
+requirement-type: functional
+text: |
+  The thread begin ${/glossary/userextensions:/term} shall be invoked in
+  ${/glossary/extension-forward-order:/term}.
+type: requirement
diff --git a/spec/rtems/userext/req/thread-create-allocator-owner.yml b/spec/rtems/userext/req/thread-create-allocator-owner.yml
new file mode 100644
index 0000000..a852500
--- /dev/null
+++ b/spec/rtems/userext/req/thread-create-allocator-owner.yml
@@ -0,0 +1,17 @@
+SPDX-License-Identifier: CC-BY-SA-4.0
+copyrights:
+- Copyright (C) 2021 embedded brains GmbH (http://www.embedded-brains.de)
+enabled-by: true
+links:
+- role: requirement-refinement
+  uid: ../if/group
+- role: requirement-refinement
+  uid: /score/object/req/allocator-mutex
+functional-type: function
+rationale: null
+references: []
+requirement-type: functional
+text: |
+  The thread invoking the thread create ${/glossary/userextensions:/term} shall
+  be the owner of the allocator mutex.
+type: requirement
diff --git a/spec/rtems/userext/req/thread-create-iterate-remove.yml b/spec/rtems/userext/req/thread-create-iterate-remove.yml
new file mode 100644
index 0000000..4fa9e33
--- /dev/null
+++ b/spec/rtems/userext/req/thread-create-iterate-remove.yml
@@ -0,0 +1,17 @@
+SPDX-License-Identifier: CC-BY-SA-4.0
+copyrights:
+- Copyright (C) 2021 embedded brains GmbH (http://www.embedded-brains.de)
+enabled-by: true
+links:
+- role: requirement-refinement
+  uid: ../if/group
+functional-type: function
+rationale: null
+references: []
+requirement-type: functional
+text: |
+  While the ${/glossary/userextensions:/term} are iterated to invoke the thread
+  create extensions, when an extension set is removed, the removed extension
+  set shall not be a next extension set for any iteration in progress after the
+  removal.
+type: requirement
diff --git a/spec/rtems/userext/req/thread-create-order.yml b/spec/rtems/userext/req/thread-create-order.yml
new file mode 100644
index 0000000..418d3c1
--- /dev/null
+++ b/spec/rtems/userext/req/thread-create-order.yml
@@ -0,0 +1,15 @@
+SPDX-License-Identifier: CC-BY-SA-4.0
+copyrights:
+- Copyright (C) 2021 embedded brains GmbH (http://www.embedded-brains.de)
+enabled-by: true
+links:
+- role: requirement-refinement
+  uid: ../if/group
+functional-type: function
+rationale: null
+references: []
+requirement-type: functional
+text: |
+  The thread create ${/glossary/userextensions:/term} shall be invoked in
+  ${/glossary/extension-forward-order:/term}.
+type: requirement
diff --git a/spec/rtems/userext/req/thread-delete-allocator-owner.yml b/spec/rtems/userext/req/thread-delete-allocator-owner.yml
new file mode 100644
index 0000000..1321699
--- /dev/null
+++ b/spec/rtems/userext/req/thread-delete-allocator-owner.yml
@@ -0,0 +1,17 @@
+SPDX-License-Identifier: CC-BY-SA-4.0
+copyrights:
+- Copyright (C) 2021 embedded brains GmbH (http://www.embedded-brains.de)
+enabled-by: true
+links:
+- role: requirement-refinement
+  uid: ../if/group
+- role: requirement-refinement
+  uid: /score/object/req/allocator-mutex
+functional-type: function
+rationale: null
+references: []
+requirement-type: functional
+text: |
+  The thread invoking the thread delete ${/glossary/userextensions:/term} shall
+  be the owner of the allocator mutex.
+type: requirement
diff --git a/spec/rtems/userext/req/thread-delete-iterate-remove.yml b/spec/rtems/userext/req/thread-delete-iterate-remove.yml
new file mode 100644
index 0000000..59a17bd
--- /dev/null
+++ b/spec/rtems/userext/req/thread-delete-iterate-remove.yml
@@ -0,0 +1,17 @@
+SPDX-License-Identifier: CC-BY-SA-4.0
+copyrights:
+- Copyright (C) 2021 embedded brains GmbH (http://www.embedded-brains.de)
+enabled-by: true
+links:
+- role: requirement-refinement
+  uid: ../if/group
+functional-type: function
+rationale: null
+references: []
+requirement-type: functional
+text: |
+  While the ${/glossary/userextensions:/term} are iterated to invoke the thread
+  delete extensions, when an extension set is removed, the removed extension
+  set shall not be a next extension set for any iteration in progress after the
+  removal.
+type: requirement
diff --git a/spec/rtems/userext/req/thread-delete-order.yml b/spec/rtems/userext/req/thread-delete-order.yml
new file mode 100644
index 0000000..d3fc171
--- /dev/null
+++ b/spec/rtems/userext/req/thread-delete-order.yml
@@ -0,0 +1,15 @@
+SPDX-License-Identifier: CC-BY-SA-4.0
+copyrights:
+- Copyright (C) 2021 embedded brains GmbH (http://www.embedded-brains.de)
+enabled-by: true
+links:
+- role: requirement-refinement
+  uid: ../if/group
+functional-type: function
+rationale: null
+references: []
+requirement-type: functional
+text: |
+  The thread delete ${/glossary/userextensions:/term} shall be invoked in
+  ${/glossary/extension-reverse-order:/term}.
+type: requirement
diff --git a/spec/rtems/userext/req/thread-exitted-iterate-remove.yml b/spec/rtems/userext/req/thread-exitted-iterate-remove.yml
new file mode 100644
index 0000000..aa53e3b
--- /dev/null
+++ b/spec/rtems/userext/req/thread-exitted-iterate-remove.yml
@@ -0,0 +1,17 @@
+SPDX-License-Identifier: CC-BY-SA-4.0
+copyrights:
+- Copyright (C) 2021 embedded brains GmbH (http://www.embedded-brains.de)
+enabled-by: true
+links:
+- role: requirement-refinement
+  uid: ../if/group
+functional-type: function
+rationale: null
+references: []
+requirement-type: functional
+text: |
+  While the ${/glossary/userextensions:/term} are iterated to invoke the thread
+  exitted extensions, when an extension set is removed, the removed extension
+  set shall not be a next extension set for any iteration in progress after the
+  removal.
+type: requirement
diff --git a/spec/rtems/userext/req/thread-exitted-order.yml b/spec/rtems/userext/req/thread-exitted-order.yml
new file mode 100644
index 0000000..64c3f3f
--- /dev/null
+++ b/spec/rtems/userext/req/thread-exitted-order.yml
@@ -0,0 +1,15 @@
+SPDX-License-Identifier: CC-BY-SA-4.0
+copyrights:
+- Copyright (C) 2021 embedded brains GmbH (http://www.embedded-brains.de)
+enabled-by: true
+links:
+- role: requirement-refinement
+  uid: ../if/group
+functional-type: function
+rationale: null
+references: []
+requirement-type: functional
+text: |
+  The thread exitted ${/glossary/userextensions:/term} shall be invoked in
+  ${/glossary/extension-forward-order:/term}.
+type: requirement
diff --git a/spec/rtems/userext/req/thread-restart-iterate-remove.yml b/spec/rtems/userext/req/thread-restart-iterate-remove.yml
new file mode 100644
index 0000000..7b75e5d
--- /dev/null
+++ b/spec/rtems/userext/req/thread-restart-iterate-remove.yml
@@ -0,0 +1,17 @@
+SPDX-License-Identifier: CC-BY-SA-4.0
+copyrights:
+- Copyright (C) 2021 embedded brains GmbH (http://www.embedded-brains.de)
+enabled-by: true
+links:
+- role: requirement-refinement
+  uid: ../if/group
+functional-type: function
+rationale: null
+references: []
+requirement-type: functional
+text: |
+  While the ${/glossary/userextensions:/term} are iterated to invoke the thread
+  restart extensions, when an extension set is removed, the removed extension
+  set shall not be a next extension set for any iteration in progress after the
+  removal.
+type: requirement
diff --git a/spec/rtems/userext/req/thread-restart-order.yml b/spec/rtems/userext/req/thread-restart-order.yml
new file mode 100644
index 0000000..6d1546a
--- /dev/null
+++ b/spec/rtems/userext/req/thread-restart-order.yml
@@ -0,0 +1,15 @@
+SPDX-License-Identifier: CC-BY-SA-4.0
+copyrights:
+- Copyright (C) 2021 embedded brains GmbH (http://www.embedded-brains.de)
+enabled-by: true
+links:
+- role: requirement-refinement
+  uid: ../if/group
+functional-type: function
+rationale: null
+references: []
+requirement-type: functional
+text: |
+  The thread restart ${/glossary/userextensions:/term} shall be invoked in
+  ${/glossary/extension-forward-order:/term}.
+type: requirement
diff --git a/spec/rtems/userext/req/thread-start-iterate-remove.yml b/spec/rtems/userext/req/thread-start-iterate-remove.yml
new file mode 100644
index 0000000..6059efe
--- /dev/null
+++ b/spec/rtems/userext/req/thread-start-iterate-remove.yml
@@ -0,0 +1,17 @@
+SPDX-License-Identifier: CC-BY-SA-4.0
+copyrights:
+- Copyright (C) 2021 embedded brains GmbH (http://www.embedded-brains.de)
+enabled-by: true
+links:
+- role: requirement-refinement
+  uid: ../if/group
+functional-type: function
+rationale: null
+references: []
+requirement-type: functional
+text: |
+  While the ${/glossary/userextensions:/term} are iterated to invoke the thread
+  start extensions, when an extension set is removed, the removed extension set
+  shall not be a next extension set for any iteration in progress after the
+  removal.
+type: requirement
diff --git a/spec/rtems/userext/req/thread-start-order.yml b/spec/rtems/userext/req/thread-start-order.yml
new file mode 100644
index 0000000..98ff2bd
--- /dev/null
+++ b/spec/rtems/userext/req/thread-start-order.yml
@@ -0,0 +1,15 @@
+SPDX-License-Identifier: CC-BY-SA-4.0
+copyrights:
+- Copyright (C) 2021 embedded brains GmbH (http://www.embedded-brains.de)
+enabled-by: true
+links:
+- role: requirement-refinement
+  uid: ../if/group
+functional-type: function
+rationale: null
+references: []
+requirement-type: functional
+text: |
+  The thread start ${/glossary/userextensions:/term} shall be invoked in
+  ${/glossary/extension-forward-order:/term}.
+type: requirement
diff --git a/spec/rtems/userext/req/thread-switch-order.yml b/spec/rtems/userext/req/thread-switch-order.yml
new file mode 100644
index 0000000..754633f
--- /dev/null
+++ b/spec/rtems/userext/req/thread-switch-order.yml
@@ -0,0 +1,15 @@
+SPDX-License-Identifier: CC-BY-SA-4.0
+copyrights:
+- Copyright (C) 2021 embedded brains GmbH (http://www.embedded-brains.de)
+enabled-by: true
+links:
+- role: requirement-refinement
+  uid: ../if/group
+functional-type: function
+rationale: null
+references: []
+requirement-type: functional
+text: |
+  The thread switch ${/glossary/userextensions:/term} shall be invoked in
+  ${/glossary/extension-forward-order:/term}.
+type: requirement
diff --git a/spec/rtems/userext/req/thread-terminate-iterate-remove.yml b/spec/rtems/userext/req/thread-terminate-iterate-remove.yml
new file mode 100644
index 0000000..f552285
--- /dev/null
+++ b/spec/rtems/userext/req/thread-terminate-iterate-remove.yml
@@ -0,0 +1,17 @@
+SPDX-License-Identifier: CC-BY-SA-4.0
+copyrights:
+- Copyright (C) 2021 embedded brains GmbH (http://www.embedded-brains.de)
+enabled-by: true
+links:
+- role: requirement-refinement
+  uid: ../if/group
+functional-type: function
+rationale: null
+references: []
+requirement-type: functional
+text: |
+  While the ${/glossary/userextensions:/term} are iterated to invoke the thread
+  terminate extensions, when an extension set is removed, the removed extension
+  set shall not be a next extension set for any iteration in progress after the
+  removal.
+type: requirement
diff --git a/spec/rtems/userext/req/thread-terminate-order.yml b/spec/rtems/userext/req/thread-terminate-order.yml
new file mode 100644
index 0000000..6ceb4ad
--- /dev/null
+++ b/spec/rtems/userext/req/thread-terminate-order.yml
@@ -0,0 +1,15 @@
+SPDX-License-Identifier: CC-BY-SA-4.0
+copyrights:
+- Copyright (C) 2021 embedded brains GmbH (http://www.embedded-brains.de)
+enabled-by: true
+links:
+- role: requirement-refinement
+  uid: ../if/group
+functional-type: function
+rationale: null
+references: []
+requirement-type: functional
+text: |
+  The thread terminate ${/glossary/userextensions:/term} shall be invoked in
+  ${/glossary/extension-reverse-order:/term}.
+type: requirement
diff --git a/spec/rtems/userext/val/userext.yml b/spec/rtems/userext/val/userext.yml
new file mode 100644
index 0000000..e85f619
--- /dev/null
+++ b/spec/rtems/userext/val/userext.yml
@@ -0,0 +1,716 @@
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+copyrights:
+- Copyright (C) 2021 embedded brains GmbH (http://www.embedded-brains.de)
+enabled-by: true
+links: []
+test-actions:
+- action-brief: |
+    Create five dynamic extensions.  Switch to a started thread.  Delete three
+    dynamic extension during the thread begin invocation.  Clean up the used
+    resources.
+  action-code: |
+    rtems_tcb *executing;
+    rtems_tcb *thread;
+    rtems_id   id;
+
+    id = CreateTask( "WORK", PRIO_LOW );
+    thread = GetThread( id );
+    StartTask( id, BeginWorker, NULL );
+    executing = StartTestCase( THREAD_BEGIN );
+    SetPriority( id, PRIO_HIGH );
+    KillZombies();
+  checks:
+  - brief: |
+      Check that the thread switch extensions were invoked in the right order
+      before the thread begin extensions.
+    code: |
+      CheckForward( THREAD_SWITCH, 1, 1, executing, thread );
+    links:
+    - role: validation
+      uid: ../req/thread-switch-order
+  - brief: |
+      Check that the thread begin extensions were invoked in the right order.
+    code: |
+      CheckForward( THREAD_BEGIN, 8, 1, thread, NULL );
+    links:
+    - role: validation
+      uid: ../req/thread-begin-iterate-remove
+    - role: validation
+      uid: ../req/thread-begin-order
+  - brief: |
+      Check that the other extensions were not invoked.
+    code: |
+      CheckForward( THREAD_CREATE, 0, 0, NULL, NULL );
+      CheckReverse( THREAD_DELETE, 0, 0, NULL, NULL );
+      CheckForward( THREAD_EXITTED, 0, 0, NULL, NULL );
+      CheckForward( THREAD_RESTART, 0, 0, NULL, NULL );
+      CheckForward( THREAD_START, 0, 0, NULL, NULL );
+      CheckReverse( THREAD_TERMINATE, 0, 0, NULL, NULL );
+    links: []
+  - brief: |
+      Check that the thread begin extension of the extension set deleted
+      before its turn in the invocation was not invoked.
+    code: |
+      CheckDeletedNotInvoked( THREAD_BEGIN );
+    links:
+    - role: validation
+      uid: ../req/thread-begin-iterate-remove
+  links: []
+- action-brief: |
+    Create five dynamic extensions.  Create a thread.  Delete three dynamic
+    extension during the thread create invocation.  Clean up the used
+    resources.
+  action-code: |
+    rtems_tcb *executing;
+    rtems_tcb *thread;
+    rtems_id   id;
+
+    executing = StartTestCase( THREAD_CREATE );
+    id = CreateTask( "WORK", PRIO_NORMAL );
+    thread = GetThread( id );
+    StopTestCase();
+    DeleteTask( id );
+    KillZombies();
+  checks:
+  - brief: |
+      Check that the thread create extensions were invoked in the right order.
+    code: |
+      CheckForward( THREAD_CREATE, 1, 1, executing, thread );
+    links:
+    - role: validation
+      uid: ../req/thread-create-iterate-remove
+    - role: validation
+      uid: ../req/thread-create-order
+  - brief: |
+      Check that the thread create extensions were invoked under protection of
+      the allocator mutex.
+    code: |
+      T_eq_u32( thread_create_allocator_owner_count, 6 );
+    links:
+    - role: validation
+      uid: ../req/thread-create-allocator-owner
+  - brief: |
+      Check that the other extensions were not invoked.
+    code: |
+      CheckForward( THREAD_BEGIN, 0, 0, NULL, NULL );
+      CheckReverse( THREAD_DELETE, 0, 0, NULL, NULL );
+      CheckForward( THREAD_EXITTED, 0, 0, NULL, NULL );
+      CheckForward( THREAD_RESTART, 0, 0, NULL, NULL );
+      CheckForward( THREAD_START, 0, 0, NULL, NULL );
+      CheckForward( THREAD_SWITCH, 0, 0, NULL, NULL );
+      CheckReverse( THREAD_TERMINATE, 0, 0, NULL, NULL );
+    links: []
+  - brief: |
+      Check that the thread create extension of the extension set deleted
+      before its turn in the invocation was not invoked.
+    code: |
+      CheckDeletedNotInvoked( THREAD_CREATE );
+    links:
+    - role: validation
+      uid: ../req/thread-create-iterate-remove
+  links: []
+- action-brief: |
+    Create five dynamic extensions.  Delete a thread.  Delete three dynamic
+    extension during the thread delete invocation.  Clean up the used
+    resources.
+  action-code: |
+    rtems_tcb *executing;
+    rtems_tcb *thread;
+    rtems_id   id;
+
+    id = CreateTask( "WORK", PRIO_NORMAL );
+    thread = GetThread( id );
+    DeleteTask( id );
+    executing = StartTestCase( THREAD_DELETE );
+    KillZombies();
+    StopTestCase();
+  checks:
+  - brief: |
+      Check that the thread delete extensions were invoked in the right order.
+    code: |
+      CheckReverse( THREAD_DELETE, 1, 1, executing, thread );
+    links:
+    - role: validation
+      uid: ../req/thread-delete-iterate-remove
+    - role: validation
+      uid: ../req/thread-delete-order
+  - brief: |
+      Check that the thread delete extensions were invoked under protection of
+      the allocator mutex.
+    code: |
+      T_eq_u32( thread_delete_allocator_owner_count, 6 );
+    links:
+    - role: validation
+      uid: ../req/thread-delete-allocator-owner
+  - brief: |
+      Check that the other extensions were not invoked.
+    code: |
+      CheckForward( THREAD_BEGIN, 0, 0, NULL, NULL );
+      CheckForward( THREAD_CREATE, 0, 0, NULL, NULL );
+      CheckForward( THREAD_EXITTED, 0, 0, NULL, NULL );
+      CheckForward( THREAD_RESTART, 0, 0, NULL, NULL );
+      CheckForward( THREAD_START, 0, 0, NULL, NULL );
+      CheckForward( THREAD_SWITCH, 0, 0, NULL, NULL );
+      CheckReverse( THREAD_TERMINATE, 0, 0, NULL, NULL );
+    links: []
+  - brief: |
+      Check that the thread delete extension of the extension set deleted
+      before its turn in the invocation was not invoked.
+    code: |
+      CheckDeletedNotInvoked( THREAD_DELETE );
+    links:
+    - role: validation
+      uid: ../req/thread-delete-iterate-remove
+  links: []
+- action-brief: |
+    Create five dynamic extensions.  Return from a thread entry.  Delete three
+    dynamic extension during the thread exitted invocation.  Clean up the used
+    resources.
+  action-code: |
+    rtems_tcb *thread;
+    rtems_id   id;
+
+    id = CreateTask( "WORK", PRIO_HIGH );
+    thread = GetThread( id );
+    StartTask( id, ExittedWorker, NULL );
+    KillZombies();
+  checks:
+  - brief: |
+      Check that the thread exitted extensions were invoked in the right order.
+    code: |
+      CheckForward( THREAD_EXITTED, 1, 1, thread, NULL );
+    links:
+    - role: validation
+      uid: ../req/thread-exitted-iterate-remove
+    - role: validation
+      uid: ../req/thread-exitted-order
+  - brief: |
+      Check that the other extensions were not invoked.
+    code: |
+      CheckForward( THREAD_BEGIN, 0, 0, NULL, NULL );
+      CheckForward( THREAD_CREATE, 0, 0, NULL, NULL );
+      CheckReverse( THREAD_DELETE, 0, 0, NULL, NULL );
+      CheckForward( THREAD_RESTART, 0, 0, NULL, NULL );
+      CheckForward( THREAD_START, 0, 0, NULL, NULL );
+      CheckForward( THREAD_SWITCH, 0, 0, NULL, NULL );
+      CheckReverse( THREAD_TERMINATE, 0, 0, NULL, NULL );
+    links: []
+  - brief: |
+      Check that the thread exitted extension of the extension set deleted
+      before its turn in the invocation was not invoked.
+    code: |
+      CheckDeletedNotInvoked( THREAD_EXITTED );
+    links:
+    - role: validation
+      uid: ../req/thread-exitted-iterate-remove
+  links: []
+- action-brief: |
+    Create five dynamic extensions.  Restart a thread.  Delete three
+    dynamic extension during the thread restart invocation.  Clean up the used
+    resources.
+  action-code: |
+    rtems_tcb *thread;
+    rtems_id   id;
+
+    id = CreateTask( "WORK", PRIO_HIGH );
+    thread = GetThread( id );
+    StartTask( id, RestartWorker, NULL );
+    KillZombies();
+  checks:
+  - brief: |
+      Check that the thread restart extensions were invoked in the right order.
+    code: |
+      CheckForward( THREAD_RESTART, 1, 1, thread, thread );
+    links:
+    - role: validation
+      uid: ../req/thread-restart-iterate-remove
+    - role: validation
+      uid: ../req/thread-restart-order
+  - brief: |
+      Check that the other extensions were not invoked.
+    code: |
+      CheckForward( THREAD_BEGIN, 0, 0, NULL, NULL );
+      CheckForward( THREAD_EXITTED, 0, 0, NULL, NULL );
+      CheckForward( THREAD_CREATE, 0, 0, NULL, NULL );
+      CheckReverse( THREAD_DELETE, 0, 0, NULL, NULL );
+      CheckForward( THREAD_START, 0, 0, NULL, NULL );
+      CheckForward( THREAD_SWITCH, 0, 0, NULL, NULL );
+      CheckForward( THREAD_TERMINATE, 0, 0, NULL, NULL );
+    links: []
+  - brief: |
+      Check that the thread restart extension of the extension set deleted
+      before its turn in the invocation was not invoked.
+    code: |
+      CheckDeletedNotInvoked( THREAD_RESTART );
+    links:
+    - role: validation
+      uid: ../req/thread-restart-iterate-remove
+  links: []
+- action-brief: |
+    Create five dynamic extensions.  Start a thread.  Delete three dynamic
+    extension during the thread start invocation.  Clean up the used resources.
+  action-code: |
+    rtems_tcb *executing;
+    rtems_tcb *thread;
+    rtems_id   id;
+
+    id = CreateTask( "WORK", PRIO_LOW );
+    thread = GetThread( id );
+    executing = StartTestCase( THREAD_START );
+    StartTask( id, StartWorker, NULL );
+    StopTestCase();
+    DeleteTask( id );
+    KillZombies();
+  checks:
+  - brief: |
+      Check that the thread start extensions were invoked in the right order.
+    code: |
+      CheckForward( THREAD_START, 1, 1, executing, thread );
+    links:
+    - role: validation
+      uid: ../req/thread-start-iterate-remove
+    - role: validation
+      uid: ../req/thread-start-order
+  - brief: |
+      Check that the other extensions were not invoked.
+    code: |
+      CheckForward( THREAD_BEGIN, 0, 0, NULL, NULL );
+      CheckForward( THREAD_EXITTED, 0, 0, NULL, NULL );
+      CheckForward( THREAD_CREATE, 0, 0, NULL, NULL );
+      CheckReverse( THREAD_DELETE, 0, 0, NULL, NULL );
+      CheckForward( THREAD_RESTART, 0, 0, NULL, NULL );
+      CheckForward( THREAD_SWITCH, 0, 0, NULL, NULL );
+      CheckForward( THREAD_TERMINATE, 0, 0, NULL, NULL );
+    links: []
+  - brief: |
+      Check that the thread start extension of the extension set deleted
+      before its turn in the invocation was not invoked.
+    code: |
+      CheckDeletedNotInvoked( THREAD_START );
+    links:
+    - role: validation
+      uid: ../req/thread-start-iterate-remove
+  links: []
+- action-brief: |
+    Create five dynamic extensions.  Terminate a thread.  Delete three dynamic
+    extension during the thread terminate invocation.  Clean up the used
+    resources.
+  action-code: |
+    rtems_tcb *thread;
+    rtems_id   id;
+
+    id = CreateTask( "WORK", PRIO_HIGH );
+    thread = GetThread( id );
+    StartTask( id, TerminateWorker, NULL );
+    KillZombies();
+  checks:
+  - brief: |
+      Check that the thread terminate extensions were invoked in the right
+      order.
+    code: |
+      CheckReverse( THREAD_TERMINATE, 1, 1, thread, NULL );
+    links:
+    - role: validation
+      uid: ../req/thread-terminate-iterate-remove
+    - role: validation
+      uid: ../req/thread-terminate-order
+  - brief: |
+      Check that the other extensions were not invoked.
+    code: |
+      CheckForward( THREAD_BEGIN, 0, 0, NULL, NULL );
+      CheckForward( THREAD_EXITTED, 0, 0, NULL, NULL );
+      CheckForward( THREAD_CREATE, 0, 0, NULL, NULL );
+      CheckReverse( THREAD_DELETE, 0, 0, NULL, NULL );
+      CheckForward( THREAD_RESTART, 0, 0, NULL, NULL );
+      CheckForward( THREAD_START, 0, 0, NULL, NULL );
+      CheckForward( THREAD_SWITCH, 0, 0, NULL, NULL );
+    links: []
+  - brief: |
+      Check that the thread terminate extension of the extension set deleted
+      before its turn in the invocation was not invoked.
+    code: |
+      CheckDeletedNotInvoked( THREAD_TERMINATE );
+    links:
+    - role: validation
+      uid: ../req/thread-terminate-iterate-remove
+  links: []
+test-brief: |
+  Tests the thread user extensions.
+test-context: []
+test-context-support: null
+test-description: null
+test-header: null
+test-includes:
+- rtems/score/atomic.h
+- rtems/score/apimutex.h
+- string.h
+test-local-includes:
+- tc-userext.h
+- tx-support.h
+test-setup:
+  brief: null
+  code: |
+    SetSelfPriority( PRIO_NORMAL );
+  description: null
+test-stop: null
+test-support: |
+  typedef struct {
+    unsigned int counter;
+    rtems_tcb   *executing;
+    rtems_tcb   *thread;
+  } ExtensionEvent;
+
+  typedef enum {
+    THREAD_BEGIN,
+    THREAD_CREATE,
+    THREAD_DELETE,
+    THREAD_EXITTED,
+    THREAD_RESTART,
+    THREAD_START,
+    THREAD_SWITCH,
+    THREAD_TERMINATE,
+    EXTENSION_KIND_COUNT
+  } ExtensionKind;
+
+  static rtems_id extension_ids[ 7 ];
+
+  static Atomic_Uint extension_counter[ RTEMS_ARRAY_SIZE( extension_ids ) ]
+    [ EXTENSION_KIND_COUNT ];
+
+  static ExtensionEvent extension_events[ RTEMS_ARRAY_SIZE( extension_ids ) ]
+    [ EXTENSION_KIND_COUNT ][ 3 ];
+
+  static Atomic_Uint global_counter;
+
+  static ExtensionKind extension_under_test = EXTENSION_KIND_COUNT;
+
+  static uint32_t thread_create_allocator_owner_count;
+
+  static uint32_t thread_delete_allocator_owner_count;
+
+  static void StopTestCase( void )
+  {
+    ExtensionKind     kind;
+    rtems_status_code sc;
+
+    kind = extension_under_test;
+    extension_under_test = EXTENSION_KIND_COUNT;
+
+    sc = rtems_extension_delete( extension_ids[ 2 ] );
+    T_rsc_success( sc );
+
+    if ( kind == THREAD_SWITCH ) {
+      sc = rtems_extension_delete( extension_ids[ 3 ] );
+      T_rsc_success( sc );
+
+      sc = rtems_extension_delete( extension_ids[ 4 ] );
+      T_rsc_success( sc );
+
+      sc = rtems_extension_delete( extension_ids[ 5 ] );
+      T_rsc_success( sc );
+    }
+
+    sc = rtems_extension_delete( extension_ids[ 6 ] );
+    T_rsc_success( sc );
+  }
+
+  static void Extension(
+    size_t        index,
+    ExtensionKind kind,
+    rtems_tcb    *executing,
+    rtems_tcb    *thread
+  )
+  {
+    unsigned int      gc;
+    unsigned int      c;
+    rtems_status_code sc;
+
+    if ( extension_under_test == EXTENSION_KIND_COUNT ) {
+      return;
+    }
+
+    if ( kind == THREAD_CREATE && _RTEMS_Allocator_is_owner() ) {
+      ++thread_create_allocator_owner_count;
+    }
+
+    if ( kind == THREAD_DELETE && _RTEMS_Allocator_is_owner() ) {
+      ++thread_delete_allocator_owner_count;
+    }
+
+    gc = _Atomic_Fetch_add_uint( &global_counter, 1, ATOMIC_ORDER_RELAXED ) + 1;
+    c = _Atomic_Fetch_add_uint(
+      &extension_counter[ index ][ kind ],
+      1,
+      ATOMIC_ORDER_RELAXED
+    );
+
+    if ( c < RTEMS_ARRAY_SIZE( extension_events[ index ][ kind ] ) ) {
+      extension_events[ index ][ kind ][ c ].counter = gc;
+      extension_events[ index ][ kind ][ c ].executing = executing;
+      extension_events[ index ][ kind ][ c ].thread = thread;
+    }
+
+    if ( kind == THREAD_SWITCH ) {
+      /* Extension set deletion is not allowed in thread switch extensions */
+      return;
+    }
+
+    if ( kind != extension_under_test ) {
+      return;
+    }
+
+    if ( kind == THREAD_DELETE || kind == THREAD_TERMINATE ) {
+      if ( index == 6 ) {
+        sc = rtems_extension_delete( extension_ids[ 5 ] );
+        T_rsc_success( sc );
+      } else if ( index == 3 ) {
+        sc = rtems_extension_delete( extension_ids[ 3 ] );
+        T_rsc_success( sc );
+      } else if ( index == 2 ) {
+        sc = rtems_extension_delete( extension_ids[ 4 ] );
+        T_rsc_success( sc );
+      }
+    } else {
+      if ( index == 2 ) {
+        sc = rtems_extension_delete( extension_ids[ 3 ] );
+        T_rsc_success( sc );
+      } else if ( index == 5 ) {
+        sc = rtems_extension_delete( extension_ids[ 5 ] );
+        T_rsc_success( sc );
+      } else if ( index == 6 ) {
+        sc = rtems_extension_delete( extension_ids[ 4 ] );
+        T_rsc_success( sc );
+      }
+    }
+
+    if ( index == 6 && ( kind == THREAD_EXITTED || kind == THREAD_RESTART ) ) {
+      StopTestCase();
+      rtems_task_exit();
+    }
+
+    if ( index == 0 && kind == THREAD_TERMINATE ) {
+      StopTestCase();
+    }
+  }
+
+  #define DEFINE_EXTENSIONS( index, linkage ) \
+    linkage void ThreadBeginExtension##index( rtems_tcb *executing ) \
+    { \
+      Extension( index, THREAD_BEGIN, executing, NULL ); \
+    } \
+    linkage bool ThreadCreateExtension##index( \
+      rtems_tcb *executing, \
+      rtems_tcb *created \
+    ) \
+    { \
+      Extension( index, THREAD_CREATE, executing, created ); \
+      return true; \
+    } \
+    linkage void ThreadDeleteExtension##index( \
+      rtems_tcb *executing, \
+      rtems_tcb *deleted \
+    ) \
+    { \
+      Extension( index, THREAD_DELETE, executing, deleted ); \
+    } \
+    linkage void ThreadExittedExtension##index( rtems_tcb *executing ) \
+    { \
+      Extension( index, THREAD_EXITTED, executing, NULL ); \
+    } \
+    linkage void ThreadRestartExtension##index( \
+      rtems_tcb *executing, \
+      rtems_tcb *restarted \
+    ) \
+    { \
+      Extension( index, THREAD_RESTART, executing, restarted ); \
+    } \
+    linkage void ThreadStartExtension##index( \
+      rtems_tcb *executing, \
+      rtems_tcb *started \
+    ) \
+    { \
+      Extension( index, THREAD_START, executing, started ); \
+    } \
+    linkage void ThreadSwitchExtension##index( \
+      rtems_tcb *executing, \
+      rtems_tcb *heir \
+    ) \
+    { \
+      Extension( index, THREAD_SWITCH, executing, heir ); \
+    } \
+    linkage void ThreadTerminateExtension##index( rtems_tcb *executing ) \
+    { \
+      Extension( index, THREAD_TERMINATE, executing, NULL ); \
+    }
+
+  DEFINE_EXTENSIONS( 0, )
+  DEFINE_EXTENSIONS( 1, )
+
+  #define DEFINE_EXTENSIONS_AND_TABLE( index ) \
+    DEFINE_EXTENSIONS( index, static ) \
+    static const rtems_extensions_table table_##index = { \
+      .thread_begin = ThreadBeginExtension##index, \
+      .thread_create = ThreadCreateExtension##index, \
+      .thread_delete = ThreadDeleteExtension##index, \
+      .thread_exitted = ThreadExittedExtension##index, \
+      .thread_restart = ThreadRestartExtension##index, \
+      .thread_start = ThreadStartExtension##index, \
+      .thread_switch = ThreadSwitchExtension##index, \
+      .thread_terminate = ThreadTerminateExtension##index \
+    }
+
+  DEFINE_EXTENSIONS_AND_TABLE( 2 );
+  DEFINE_EXTENSIONS_AND_TABLE( 3 );
+  DEFINE_EXTENSIONS_AND_TABLE( 4 );
+  DEFINE_EXTENSIONS_AND_TABLE( 5 );
+  DEFINE_EXTENSIONS_AND_TABLE( 6 );
+
+  static const rtems_extensions_table * const tables[] = {
+    NULL,
+    NULL,
+    &table_2,
+    &table_3,
+    &table_4,
+    &table_5,
+    &table_6
+  };
+
+  static rtems_tcb *StartTestCase( ExtensionKind kind )
+  {
+    size_t i;
+
+    thread_create_allocator_owner_count = 0;
+    thread_delete_allocator_owner_count = 0;
+    _Atomic_Store_uint( &global_counter, 0, ATOMIC_ORDER_RELAXED );
+    memset( extension_counter, 0, sizeof( extension_counter ) );
+    memset( extension_events, 0, sizeof( extension_events ) );
+
+    extension_under_test = kind;
+
+    for ( i = 2; i < RTEMS_ARRAY_SIZE( extension_ids ); ++i ) {
+      rtems_status_code sc;
+
+      sc = rtems_extension_create(
+        rtems_build_name( ' ', ' ', ' ', '2' + i ),
+        tables[ i ],
+        &extension_ids[ i ]
+      );
+      T_rsc_success( sc );
+    }
+
+    return GetExecuting();
+  }
+
+  static void CheckForward(
+    ExtensionKind kind,
+    unsigned int  counter,
+    unsigned int  increment,
+    rtems_tcb    *executing,
+    rtems_tcb    *thread
+  )
+  {
+    size_t i;
+
+    for ( i = 0; i < RTEMS_ARRAY_SIZE( extension_ids ); ++i ) {
+      if ( i == 3 && kind != THREAD_SWITCH ) {
+        continue;
+      }
+
+      if ( counter == 0 ) {
+        T_eq_uint( extension_counter[ i ][ kind ], 0 );
+      } else {
+        T_eq_uint( extension_counter[ i ][ kind ], 1 );
+        T_eq_uint( extension_events[ i ][ kind ][ 0 ].counter, counter );
+        T_eq_ptr( extension_events[ i ][ kind ][ 0 ].executing, executing );
+        T_eq_ptr( extension_events[ i ][ kind ][ 0 ].thread, thread );
+
+        counter += increment;
+      }
+    }
+  }
+
+  static void CheckReverse(
+    ExtensionKind kind,
+    unsigned int  counter,
+    unsigned int  increment,
+    rtems_tcb    *executing,
+    rtems_tcb    *thread
+  )
+  {
+    size_t i;
+
+    for ( i = 0; i < RTEMS_ARRAY_SIZE( extension_ids ); ++i ) {
+      if ( i == 5 && kind != THREAD_SWITCH ) {
+        continue;
+      }
+
+      if ( counter == 0 ) {
+        T_eq_uint( extension_counter[ i ][ kind ], 0 );
+      } else {
+        T_eq_uint( extension_counter[ i ][ kind ], 1 );
+        T_eq_uint(
+          extension_events[ i ][ kind ][ 0 ].counter,
+          7 - counter
+        );
+        T_eq_ptr( extension_events[ i ][ kind ][ 0 ].executing, executing );
+        T_eq_ptr( extension_events[ i ][ kind ][ 0 ].thread, thread );
+
+        counter += increment;
+      }
+    }
+  }
+
+  static void CheckDeletedNotInvoked( ExtensionKind kind )
+  {
+    size_t index;
+
+    if ( kind == THREAD_DELETE || kind == THREAD_TERMINATE ) {
+      index = 5;
+    } else {
+      index = 3;
+    }
+
+    T_eq_uint( extension_events[ index ][ kind ][ 0 ].counter, 0 );
+    T_null( extension_events[ index ][ kind ][ 0 ].executing );
+    T_null( extension_events[ index ][ kind ][ 0 ].thread );
+  }
+
+  static void BeginWorker( rtems_task_argument arg )
+  {
+    T_eq_u32( arg, 0 );
+    StopTestCase();
+    rtems_task_exit();
+  }
+
+  static void ExittedWorker( rtems_task_argument arg )
+  {
+    T_eq_u32( arg, 0 );
+    (void) StartTestCase( THREAD_EXITTED );
+  }
+
+  static void RestartWorker( rtems_task_argument arg )
+  {
+    T_eq_u32( arg, 0 );
+    (void) StartTestCase( THREAD_RESTART );
+    (void) rtems_task_restart( RTEMS_SELF, 1 );
+  }
+
+  static void StartWorker( rtems_task_argument arg )
+  {
+    (void) arg;
+    T_unreachable();
+  }
+
+  static void TerminateWorker( rtems_task_argument arg )
+  {
+    T_eq_u32( arg, 0 );
+    (void) StartTestCase( THREAD_TERMINATE );
+    rtems_task_exit();
+  }
+test-target: testsuites/validation/tc-userext.c
+test-teardown:
+  brief: null
+  code: |
+    RestoreRunnerPriority();
+  description: null
+type: test-case
diff --git a/spec/score/interr/val/terminate.yml b/spec/score/interr/val/terminate.yml
index 4cb9465..2021fb1 100644
--- a/spec/score/interr/val/terminate.yml
+++ b/spec/score/interr/val/terminate.yml
@@ -5,14 +5,12 @@ enabled-by: true
 links: []
 test-actions:
 - action-brief: |
-    Create three dynamic extensions.  Call the system termination procedure.
-    Delete one dynamic extension during the fatal extension invocation.  Delete
-    the two remaining dynamic extensions.
+    Create five dynamic extensions.  Call the system termination procedure.
+    Delete three dynamic extension during the fatal extension invocation.
+    Delete the two remaining dynamic extensions.
   action-code: |
     rtems_status_code      sc;
     rtems_extensions_table table;
-    rtems_id               id_2;
-    rtems_id               id_3;
     bool                   shutdown_ok;
 
     #if defined(RTEMS_SMP)
@@ -27,7 +25,7 @@ test-actions:
     sc = rtems_extension_create(
       rtems_build_name( ' ', ' ', ' ', '2' ),
       &table,
-      &id_2
+      &extension_ids[ 2 ]
     );
     T_step_rsc_success( ${step}, sc );
 
@@ -35,7 +33,7 @@ test-actions:
     sc = rtems_extension_create(
       rtems_build_name( ' ', ' ', ' ', '3' ),
       &table,
-      &id_3
+      &extension_ids[ 3 ]
     );
     T_step_rsc_success( ${step}, sc );
 
@@ -43,7 +41,23 @@ test-actions:
     sc = rtems_extension_create(
       rtems_build_name( ' ', ' ', ' ', '4' ),
       &table,
-      &extension_id_4
+      &extension_ids[ 4 ]
+    );
+    T_step_rsc_success( ${step}, sc );
+
+    table.fatal = FatalExtension5;
+    sc = rtems_extension_create(
+      rtems_build_name( ' ', ' ', ' ', '5' ),
+      &table,
+      &extension_ids[ 5 ]
+    );
+    T_step_rsc_success( ${step}, sc );
+
+    table.fatal = FatalExtension6;
+    sc = rtems_extension_create(
+      rtems_build_name( ' ', ' ', ' ', '6' ),
+      &table,
+      &extension_ids[ 6 ]
     );
     T_step_rsc_success( ${step}, sc );
 
@@ -55,10 +69,10 @@ test-actions:
 
     test_case_active = false;
 
-    sc = rtems_extension_delete( id_2 );
+    sc = rtems_extension_delete( extension_ids[ 2 ] );
     T_step_rsc_success( ${step}, sc );
 
-    sc = rtems_extension_delete( id_3 );
+    sc = rtems_extension_delete( extension_ids[ 6 ] );
     T_step_rsc_success( ${step}, sc );
   checks:
   - brief: |
@@ -81,7 +95,17 @@ test-actions:
       );
       T_step_eq_int(
         ${step},
-        info[ 3 ].source,
+        info[ 4 ].source,
+        RTEMS_FATAL_SOURCE_APPLICATION
+      );
+      T_step_eq_int(
+        ${step},
+        info[ 5 ].source,
+        RTEMS_FATAL_SOURCE_APPLICATION
+      );
+      T_step_eq_int(
+        ${step},
+        info[ 6 ].source,
         RTEMS_FATAL_SOURCE_APPLICATION
       );
     links:
@@ -94,7 +118,9 @@ test-actions:
       T_step_false( ${step}, info[ 0 ].always_set_to_false );
       T_step_false( ${step}, info[ 1 ].always_set_to_false );
       T_step_false( ${step}, info[ 2 ].always_set_to_false );
-      T_step_false( ${step}, info[ 3 ].always_set_to_false );
+      T_step_false( ${step}, info[ 4 ].always_set_to_false );
+      T_step_false( ${step}, info[ 5 ].always_set_to_false );
+      T_step_false( ${step}, info[ 6 ].always_set_to_false );
     links:
     - role: validation
       uid: ../req/terminate-userext
@@ -104,7 +130,9 @@ test-actions:
       T_step_eq_ulong( ${step}, info[ 0 ].code, 123456 );
       T_step_eq_ulong( ${step}, info[ 1 ].code, 123456 );
       T_step_eq_ulong( ${step}, info[ 2 ].code, 123456 );
-      T_step_eq_ulong( ${step}, info[ 3 ].code, 123456 );
+      T_step_eq_ulong( ${step}, info[ 4 ].code, 123456 );
+      T_step_eq_ulong( ${step}, info[ 5 ].code, 123456 );
+      T_step_eq_ulong( ${step}, info[ 6 ].code, 123456 );
     links:
     - role: validation
       uid: ../req/terminate-userext
@@ -114,7 +142,9 @@ test-actions:
       T_step_eq_uint( ${step}, info[ 0 ].counter, 1 );
       T_step_eq_uint( ${step}, info[ 1 ].counter, 2 );
       T_step_eq_uint( ${step}, info[ 2 ].counter, 3 );
-      T_step_eq_uint( ${step}, info[ 3 ].counter, 4 );
+      T_step_eq_uint( ${step}, info[ 4 ].counter, 4 );
+      T_step_eq_uint( ${step}, info[ 5 ].counter, 5 );
+      T_step_eq_uint( ${step}, info[ 6 ].counter, 6 );
     links:
     - role: validation
       uid: /rtems/userext/req/fatal-order
@@ -122,10 +152,10 @@ test-actions:
       Check that the fatal extension in the deleted extension set was not
       invoked.
     code: |
-      T_step_eq_int( ${step}, info[ 4 ].source, 0 );
-      T_step_false( ${step}, info[ 4 ].always_set_to_false );
-      T_step_eq_ulong( ${step}, info[ 4 ].code, 0 );
-      T_step_eq_uint( ${step}, info[ 4 ].counter, 0 );
+      T_step_eq_int( ${step}, info[ 3 ].source, 0 );
+      T_step_false( ${step}, info[ 3 ].always_set_to_false );
+      T_step_eq_ulong( ${step}, info[ 3 ].code, 0 );
+      T_step_eq_uint( ${step}, info[ 3 ].counter, 0 );
     links:
     - role: validation
       uid: /rtems/userext/req/fatal-iterate-remove
@@ -170,7 +200,7 @@ test-actions:
   - brief: |
       Check that the system was finally halted.
     code: |
-      T_step_eq_uint( ${step}, counter, 5 );
+      T_step_eq_uint( ${step}, counter, 7 );
     links:
     - role: validation
       uid: ../req/terminate-halt
@@ -193,7 +223,7 @@ test-includes:
 - setjmp.h
 - string.h
 test-local-includes:
-- tc-terminate.h
+- tc-userext.h
 test-setup: null
 test-stop: null
 test-support: |
@@ -206,7 +236,7 @@ test-support: |
 
   static Atomic_Uint counter;
 
-  static FatalInfo info[ 5 ];
+  static FatalInfo info[ 7 ];
 
   static bool test_case_active;
 
@@ -220,7 +250,7 @@ test-support: |
 
   static rtems_fatal_code halt_code;
 
-  static rtems_id extension_id_4;
+  static rtems_id extension_ids[ 7 ];
 
   static unsigned int GetCounter( void )
   {
@@ -284,7 +314,12 @@ test-support: |
     rtems_fatal_code   code
   )
   {
+    rtems_status_code sc;
+
     FatalExtension( source, always_set_to_false, code, 2 );
+
+    sc = rtems_extension_delete( extension_ids[ 3 ] );
+    T_quiet_rsc_success( sc );
   }
 
   static void FatalExtension3(
@@ -293,21 +328,44 @@ test-support: |
     rtems_fatal_code   code
   )
   {
+    FatalExtension( source, always_set_to_false, code, 3 );
+  }
+
+  static void FatalExtension4(
+    rtems_fatal_source source,
+    bool               always_set_to_false,
+    rtems_fatal_code   code
+  )
+  {
+    FatalExtension( source, always_set_to_false, code, 4 );
+  }
+
+  static void FatalExtension5(
+    rtems_fatal_source source,
+    bool               always_set_to_false,
+    rtems_fatal_code   code
+  )
+  {
     rtems_status_code sc;
 
-    FatalExtension( source, always_set_to_false, code, 3 );
+    FatalExtension( source, always_set_to_false, code, 5 );
 
-    sc = rtems_extension_delete( extension_id_4 );
+    sc = rtems_extension_delete( extension_ids[ 5 ] );
     T_quiet_rsc_success( sc );
   }
 
-  static void FatalExtension4(
+  static void FatalExtension6(
     rtems_fatal_source source,
     bool               always_set_to_false,
     rtems_fatal_code   code
   )
   {
-    FatalExtension( source, always_set_to_false, code, 3 );
+    rtems_status_code sc;
+
+    FatalExtension( source, always_set_to_false, code, 6 );
+
+    sc = rtems_extension_delete( extension_ids[ 4 ] );
+    T_quiet_rsc_success( sc );
   }
 test-target: testsuites/validation/tc-terminate.c
 test-teardown: null
diff --git a/spec/testsuites/terminate.yml b/spec/testsuites/terminate.yml
index 555947c..8ac5466 100644
--- a/spec/testsuites/terminate.yml
+++ b/spec/testsuites/terminate.yml
@@ -25,7 +25,7 @@ test-code: |
 test-description: null
 test-includes: []
 test-local-includes:
-- tc-terminate.h
-test-suite-name: Fatal
+- tc-userext.h
+test-suite-name: Terminate
 test-target: testsuites/validation/ts-terminate.c
 type: test-suite
diff --git a/spec/testsuites/userext.yml b/spec/testsuites/userext.yml
new file mode 100644
index 0000000..4e1aa50
--- /dev/null
+++ b/spec/testsuites/userext.yml
@@ -0,0 +1,44 @@
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+copyrights:
+- Copyright (C) 2021 embedded brains GmbH (http://www.embedded-brains.de)
+enabled-by: true
+links:
+- role: requirement-refinement
+  uid: /req/test-suites
+test-brief: |
+  This validation test suite contains a test cases related to the invocation of
+  user extensions.
+test-code: |
+  const char rtems_test_name[] = "${.:/test-suite-name}";
+
+  #define CONFIGURE_MAXIMUM_PROCESSORS 2
+
+  #define CONFIGURE_INITIAL_EXTENSIONS \
+    { \
+      .thread_begin = ThreadBeginExtension0, \
+      .thread_create = ThreadCreateExtension0, \
+      .thread_delete = ThreadDeleteExtension0, \
+      .thread_exitted = ThreadExittedExtension0, \
+      .thread_restart = ThreadRestartExtension0, \
+      .thread_start = ThreadStartExtension0, \
+      .thread_switch = ThreadSwitchExtension0, \
+      .thread_terminate = ThreadTerminateExtension0 \
+    }, { \
+      .thread_begin = ThreadBeginExtension1, \
+      .thread_create = ThreadCreateExtension1, \
+      .thread_delete = ThreadDeleteExtension1, \
+      .thread_exitted = ThreadExittedExtension1, \
+      .thread_restart = ThreadRestartExtension1, \
+      .thread_start = ThreadStartExtension1, \
+      .thread_switch = ThreadSwitchExtension1, \
+      .thread_terminate = ThreadTerminateExtension1 \
+    }
+
+  #include "ts-default.h"
+test-description: null
+test-includes: []
+test-local-includes:
+- tc-userext.h
+test-suite-name: Userext
+test-target: testsuites/validation/ts-userext.c
+type: test-suite



More information about the vc mailing list