Skip to content

New control API is difficult to use #611

Description

@danth

What

Due to the asynchronous design, the new control API introduced in #478 is more difficult to use compared to the old one.

For example, a version of the smoke test:

Before
void main()
{
    score::mw::lifecycle::ControlClient client{};

    TEST_STEP("Control daemon report running")
    {
        // report running
        score::mw::lifecycle::report_running();
    }

    TEST_STEP("Activate RunTarget Running")
    {
        score::cpp::stop_token stop_token;
        auto result = client.ActivateRunTarget("Running").Get(stop_token);
        EXPECT_TRUE(result.has_value()) << "Activating target Running failed: " << result.error().Message();
    }

    TEST_STEP("Activate RunTarget Startup")
    {
        score::cpp::stop_token stop_token;
        auto result = client.ActivateRunTarget("Startup").Get(stop_token);
        EXPECT_TRUE(result.has_value());
    }

    TEST_STEP("Activate RunTarget Off")
    {
        client.ActivateRunTarget("Off");
    }
}
After
bool event_received = false;
RunTargetActivationSource event_source;
RunTargetName event_target;
std::mutex event_mutex;
std::condition_variable event_condition;

void push_event(RunTargetActivationSource source, RunTargetName target)
{
    {
        std::unique_lock event_lock(event_mutex);
        event_condition.wait(event_lock, [&] {
            return !event_received;
        });
        event_received = true;
        event_source = source;
        event_target = target;
    }
    event_condition.notify_one();
};

void pop_event(std::function<void(RunTargetActivationSource source, RunTargetName target)> callback)
{
    {
        std::unique_lock event_lock(event_mutex);
        event_condition.wait(event_lock, [&] {
            return event_received;
        });
        callback(event_source, event_target);
        event_received = false;
    }
    event_condition.notify_one();
};

void main()
{
    std::unique_ptr<ILmControl> client;

    TEST_STEP("Create client")
    {
        auto client_result = ILmControl::Create("StateManager/LaunchManager/Instance");
        ASSERT_TRUE(client_result.has_value()) << client_result.error().Message();
        client = std::move(client_result).value();
    }

    TEST_STEP("Register callback")
    {
        const auto result = client->register_run_target_activation_callback(push_event);
        ASSERT_TRUE(result.has_value());
    }

    TEST_STEP("Report running")
    {
        report_running();
    }

    pop_event([](RunTargetActivationSource source, RunTargetName target) {
        TEST_STEP("Callback for RunTarget Startup")
        {
            EXPECT_EQ(source, RunTargetActivationSource::kInitialActivation);
            EXPECT_EQ(target, "Startup");
        }
    });

    TEST_STEP("Activate RunTarget Running")
    {
        const auto result = client->activate_run_target("Running", true);
        EXPECT_TRUE(result.has_value()) << result.error().Message();
    }

    pop_event([](RunTargetActivationSource source, RunTargetName target) {
        TEST_STEP("Callback for RunTarget Running")
        {
            EXPECT_EQ(source, RunTargetActivationSource::kStateManagerRequest);
            EXPECT_EQ(target, "Running");
        }
    });

    TEST_STEP("Activate RunTarget Startup")
    {
        const auto result = client->activate_run_target("Startup", true);
        EXPECT_TRUE(result.has_value()) << result.error().Message();
    }

    pop_event([](RunTargetActivationSource source, RunTargetName target) {
        TEST_STEP("Callback for RunTarget Startup")
        {
            EXPECT_EQ(source, RunTargetActivationSource::kStateManagerRequest);
            EXPECT_EQ(target, "Startup");
        }
    });

    TEST_STEP("Activate RunTarget Off")
    {
        const auto result = client->activate_run_target("Off", true);
        EXPECT_TRUE(result.has_value()) << result.error().Message();
    }
}

In the new API, it is not possible to call activate_run_target while the activation_callback is still running. So, the user has to write multithreaded code.

Acceptance Criteria (DoD)

  • Control API provides methods that do not require external synchronisation

How

#489 (comment)

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    cleanupTasks to cleanup the codecomRelated to score::comenhancementNew feature or request

    Type

    Projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions