Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@ docs(
"bundle": "//score/concurrency:docs",
"mount_at": "baselibs/components/concurrency",
},
{
"bundle": "//score/scope_exit:docs",
"mount_at": "baselibs/components/scope_exit",
},
{
"bundle": "//score/containers:docs",
"mount_at": "baselibs/components/containers",
Expand Down
3 changes: 2 additions & 1 deletion docs/baselibs/components/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ Components
.. toctree::
:maxdepth: 1

abi_compatible_data_types/docs/index
abi_compatible_data_types/docs/index


Overview
Expand Down Expand Up @@ -55,3 +55,4 @@ Overview
- *mw::log*: Logging frontend.
- :need:`doc__utils`: Provides a collection of small, reusable utilities that do not fit into the other
base libraries.
- :need:`doc__scope_exit`: Provides move-only ownership utilities for scope-based cleanup and flag transfer.
7 changes: 7 additions & 0 deletions score/scope_exit/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ load("@rules_cc//cc:defs.bzl", "cc_library")
load("@score_baselibs//:bazel/unit_tests.bzl", "cc_gtest_unit_test")
load("@score_baselibs//score/language/safecpp:toolchain_features.bzl", "COMPILER_WARNING_FEATURES")
load("@score_baselibs//score/quality/clang_tidy:extra_checks.bzl", "clang_tidy_extra_checks")
load("@score_docs_as_code//:docs.bzl", "docs_bundle")

cc_library(
name = "flag_owner",
Expand Down Expand Up @@ -64,3 +65,9 @@ clang_tidy_extra_checks(
],
tidy_config_file = ".clang-tidy-extra",
)

docs_bundle(
name = "docs",
source_dir = "docs",
visibility = ["//visibility:public"],
)
34 changes: 34 additions & 0 deletions score/scope_exit/docs/architecture/index.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
..
# *******************************************************************************
# Copyright (c) 2026 Contributors to the Eclipse Foundation
#
# See the NOTICE file(s) distributed with this work for additional
# information regarding copyright ownership.
#
# This program and the accompanying materials are made available under the
# terms of the Apache License Version 2.0 which is available at
# https://www.apache.org/licenses/LICENSE-2.0
#
# SPDX-License-Identifier: Apache-2.0
# *******************************************************************************

Scope Exit Architecture
########################

.. document:: Scope Exit Architecture
:id: doc__scope_exit_architecture
:status: draft
:version: 1
:safety: ASIL_B
:security: YES
:realizes: wp__component_arch[version==1]

.. comp:: Scope Exit
:id: comp__baselibs_scope_exit
:security: YES
:safety: ASIL_B
:status: valid
:version: 1
:belongs_to: feat__baselibs[version==1]

The Scope Exit component contains the scope guard and flag ownership utilities.
33 changes: 33 additions & 0 deletions score/scope_exit/docs/index.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
..
# *******************************************************************************
# Copyright (c) 2026 Contributors to the Eclipse Foundation
#
# See the NOTICE file(s) distributed with this work for additional
# information regarding copyright ownership.
#
# This program and the accompanying materials are made available under the
# terms of the Apache License Version 2.0 which is available at
# https://www.apache.org/licenses/LICENSE-2.0
#
# SPDX-License-Identifier: Apache-2.0
# *******************************************************************************

Scope Exit
##########

.. document:: Scope Exit
:id: doc__scope_exit
:status: draft
:version: 1
:safety: ASIL_B
:security: YES
:realizes: wp__cmpt_request[version==1]

.. toctree::
:hidden:

requirements/index.rst
architecture/index.rst

The Scope Exit component provides move-only ownership utilities for reliable
scope-based cleanup and flag transfer.
54 changes: 54 additions & 0 deletions score/scope_exit/docs/requirements/index.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
..
# *******************************************************************************
# Copyright (c) 2026 Contributors to the Eclipse Foundation
#
# See the NOTICE file(s) distributed with this work for additional
# information regarding copyright ownership.
#
# This program and the accompanying materials are made available under the
# terms of the Apache License Version 2.0 which is available at
# https://www.apache.org/licenses/LICENSE-2.0
#
# SPDX-License-Identifier: Apache-2.0
# *******************************************************************************

Requirements
############

.. document:: Scope Exit Requirements
:id: doc__scope_exit_requirements
:status: draft
:version: 1
:safety: ASIL_B
:security: YES
:realizes: wp__requirements_comp[version==1]
:tags: requirements, scope_exit

Functional Requirements
=======================

.. comp_req:: Scope-Based Cleanup Ownership
:id: comp_req__scope_exit__cleanup_ownership
:reqtype: Functional
:security: YES
:safety: ASIL_B
:derived_from: feat_req__baselibs__utils_library[version==2]
:status: valid
:version: 1
:tags: inspected
:satisfied_by: comp__baselibs_scope_exit[version==1]

The Scope Exit component shall invoke an owned cleanup callback at scope exit at most once, and shall transfer or release callback ownership according to move construction, move assignment, and explicit release operations.

.. comp_req:: Move-Only Flag Ownership
:id: comp_req__scope_exit__flag_ownership
:reqtype: Functional
:security: YES
:safety: ASIL_B
:derived_from: feat_req__baselibs__utils_library[version==2]
:status: valid
:version: 1
:tags: inspected
:satisfied_by: comp__baselibs_scope_exit[version==1]

The Scope Exit component shall provide a move-only flag owner that transfers its flag value to the destination and clears the source during move operations, while preserving the value during self-move assignment.
20 changes: 20 additions & 0 deletions score/scope_exit/flag_owner_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ namespace

TEST(FlagOwnerTest, CreatingFlagOwnerWithTrueInitialValueWillSetTheFlag)
{
RecordProperty("PartiallyVerifies", "comp_req__scope_exit__flag_ownership");
RecordProperty("Description", "Check that a flag owner initialized true reports an engaged flag.");
RecordProperty("TestType", "requirements-based");
RecordProperty("DerivationTechnique", "boundary-values");
// When creating the FlagOwner with the initial value set to true
FlagOwner flag_owner{true};

Expand All @@ -32,6 +36,10 @@ TEST(FlagOwnerTest, CreatingFlagOwnerWithTrueInitialValueWillSetTheFlag)

TEST(FlagOwnerTest, CreatingFlagOwnerWithFalseInitialValueWillClearTheFlag)
{
RecordProperty("PartiallyVerifies", "comp_req__scope_exit__flag_ownership");
RecordProperty("Description", "Check that a flag owner initialized false reports a cleared flag.");
RecordProperty("TestType", "requirements-based");
RecordProperty("DerivationTechnique", "boundary-values");
// When creating the FlagOwner with the initial value set to false
FlagOwner flag_owner{false};

Expand All @@ -41,6 +49,10 @@ TEST(FlagOwnerTest, CreatingFlagOwnerWithFalseInitialValueWillClearTheFlag)

TEST(FlagOwnerTest, MoveConstructingAFlagOwnerWillTransferTheFlagValue)
{
RecordProperty("PartiallyVerifies", "comp_req__scope_exit__flag_ownership");
RecordProperty("Description", "Check that move construction transfers the flag and clears the source owner.");
RecordProperty("TestType", "requirements-based");
RecordProperty("DerivationTechnique", "equivalence-classes");
// Given a FlagOwner with the flag set
FlagOwner flag_owner_1{true};

Expand All @@ -54,6 +66,10 @@ TEST(FlagOwnerTest, MoveConstructingAFlagOwnerWillTransferTheFlagValue)

TEST(FlagOwnerTest, MoveAssigningAFlagOwnerWillTransferTheFlagValue)
{
RecordProperty("PartiallyVerifies", "comp_req__scope_exit__flag_ownership");
RecordProperty("Description", "Check that move assignment transfers the flag and clears the source owner.");
RecordProperty("TestType", "requirements-based");
RecordProperty("DerivationTechnique", "equivalence-classes");
// Given a FlagOwner with the flag set and another the the flag cleraed
FlagOwner flag_owner_1{true};
FlagOwner flag_owner_2{false};
Expand All @@ -68,6 +84,10 @@ TEST(FlagOwnerTest, MoveAssigningAFlagOwnerWillTransferTheFlagValue)

TEST(FlagOwnerTest, SelfMoveAssigningAFlagOwnerDoesNotChangeFlagValue)
{
RecordProperty("PartiallyVerifies", "comp_req__scope_exit__flag_ownership");
RecordProperty("Description", "Check that self-move assignment leaves the flag value unchanged.");
RecordProperty("TestType", "requirements-based");
RecordProperty("DerivationTechnique", "boundary-values");
// Given a FlagOwner with the flag set
const bool initial_flag_value{true};
// Note. we use a std::optional to avoid a clang compiler warning -Wself-move
Expand Down
52 changes: 52 additions & 0 deletions score/scope_exit/scope_exit_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,11 @@ class ScopeExitFixture : public ::testing::Test
// use GivenAScopeExit.
TEST_F(ScopeExitFixture, CreatingDoesNotCallDestructionHandler)
{
RecordProperty("PartiallyVerifies", "comp_req__scope_exit__cleanup_ownership");
RecordProperty("Description",
"Check that constructing a scope guard does not invoke its cleanup callback prematurely.");
RecordProperty("TestType", "requirements-based");
RecordProperty("DerivationTechnique", "boundary-values");
// When creating a ScopeExit
bool destruction_handler_called{false};
ScopeExit<> scope_exit{[&destruction_handler_called]() noexcept {
Expand All @@ -75,6 +80,11 @@ TEST_F(ScopeExitFixture, CreatingDoesNotCallDestructionHandler)

TEST_F(ScopeExitFixture, DestroyingCallsDestructionHandler)
{
RecordProperty("PartiallyVerifies", "comp_req__scope_exit__cleanup_ownership");
RecordProperty("Description",
"Check that destroying an active scope guard invokes its cleanup callback exactly once.");
RecordProperty("TestType", "requirements-based");
RecordProperty("DerivationTechnique", "requirements-analysis");
bool destruction_handler_called{false};
{
// Given a ScopeExit
Expand All @@ -91,6 +101,11 @@ TEST_F(ScopeExitFixture, DestroyingCallsDestructionHandler)

TEST_F(ScopeExitFixture, DestroyingWithScopedFunctionCallsDestructionHandler)
{
RecordProperty("PartiallyVerifies", "comp_req__scope_exit__cleanup_ownership");
RecordProperty("Description",
"Check that a scope guard accepts a move-only scoped callback and invokes it at destruction.");
RecordProperty("TestType", "interface-test");
RecordProperty("DerivationTechnique", "equivalence-classes");
safecpp::Scope<> scope{};
bool destruction_handler_called{false};
{
Expand All @@ -108,6 +123,11 @@ TEST_F(ScopeExitFixture, DestroyingWithScopedFunctionCallsDestructionHandler)

TEST_F(ScopeExitFixture, DestroyingAfterCallingReleaseDoesNotCallDestructionHandler)
{
RecordProperty("PartiallyVerifies", "comp_req__scope_exit__cleanup_ownership");
RecordProperty("Description",
"Check that releasing an active scope guard suppresses its cleanup callback at destruction.");
RecordProperty("TestType", "requirements-based");
RecordProperty("DerivationTechnique", "boundary-values");
GivenAScopeExit();

// and given that Release has been called on the ScopeExit
Expand All @@ -122,6 +142,12 @@ TEST_F(ScopeExitFixture, DestroyingAfterCallingReleaseDoesNotCallDestructionHand

TEST_F(ScopeExitFixture, MoveConstructingGuardDoesNotCallDestructionHandler)
{
RecordProperty("PartiallyVerifies", "comp_req__scope_exit__cleanup_ownership");
RecordProperty(
"Description",
"Check that move construction transfers callback ownership without invoking the moved-from callback.");
RecordProperty("TestType", "requirements-based");
RecordProperty("DerivationTechnique", "equivalence-classes");
GivenAScopeExit();

// When move constructing a new ScopeExit
Expand All @@ -133,6 +159,11 @@ TEST_F(ScopeExitFixture, MoveConstructingGuardDoesNotCallDestructionHandler)

TEST_F(ScopeExitFixture, DestroyingMoveConstructedMovedFromGuardDoesNotCallDestructionHandler)
{
RecordProperty("PartiallyVerifies", "comp_req__scope_exit__cleanup_ownership");
RecordProperty("Description",
"Check that destroying a moved-from guard does not invoke the transferred cleanup callback.");
RecordProperty("TestType", "requirements-based");
RecordProperty("DerivationTechnique", "equivalence-classes");
GivenAScopeExit();

// and given a new ScopeExit move constructed from another
Expand All @@ -147,6 +178,11 @@ TEST_F(ScopeExitFixture, DestroyingMoveConstructedMovedFromGuardDoesNotCallDestr

TEST_F(ScopeExitFixture, DestroyingMoveConstructedMovedToGuardCallsDestructionHandler)
{
RecordProperty("PartiallyVerifies", "comp_req__scope_exit__cleanup_ownership");
RecordProperty("Description",
"Check that destroying the move destination invokes the transferred cleanup callback.");
RecordProperty("TestType", "requirements-based");
RecordProperty("DerivationTechnique", "equivalence-classes");
GivenAScopeExit();

// and given a new ScopeExit move constructed from another
Expand All @@ -161,6 +197,12 @@ TEST_F(ScopeExitFixture, DestroyingMoveConstructedMovedToGuardCallsDestructionHa

TEST_F(ScopeExitFixture, MoveAssigningGuardCallsDestructionHandlerOnMovedToGuard)
{
RecordProperty("PartiallyVerifies", "comp_req__scope_exit__cleanup_ownership");
RecordProperty(
"Description",
"Check that move assignment invokes the destination's former callback and transfers the source callback.");
RecordProperty("TestType", "requirements-based");
RecordProperty("DerivationTechnique", "equivalence-classes");
GivenTwoScopeExits();

// When move assigning one ScopeExit to another
Expand All @@ -173,6 +215,11 @@ TEST_F(ScopeExitFixture, MoveAssigningGuardCallsDestructionHandlerOnMovedToGuard

TEST_F(ScopeExitFixture, DestroyingMoveAssignedMovedFromGuardDoesNotCallDestructionHandler)
{
RecordProperty("PartiallyVerifies", "comp_req__scope_exit__cleanup_ownership");
RecordProperty("Description",
"Check that destroying a move-assigned source guard does not invoke its transferred callback.");
RecordProperty("TestType", "requirements-based");
RecordProperty("DerivationTechnique", "equivalence-classes");
GivenTwoScopeExits();

// and given that one ScopeExit was move assigned to another
Expand All @@ -187,6 +234,11 @@ TEST_F(ScopeExitFixture, DestroyingMoveAssignedMovedFromGuardDoesNotCallDestruct

TEST_F(ScopeExitFixture, DestroyingMoveAssignedMovedToGuardCallsDestructionHandler)
{
RecordProperty("PartiallyVerifies", "comp_req__scope_exit__cleanup_ownership");
RecordProperty("Description",
"Check that destroying a move-assigned destination invokes the transferred callback.");
RecordProperty("TestType", "requirements-based");
RecordProperty("DerivationTechnique", "equivalence-classes");
GivenTwoScopeExits();

// and given that one ScopeExit was move assigned to another
Expand Down
Loading