From d3e49b08e558d9b0b72e913a9263684827f38520 Mon Sep 17 00:00:00 2001 From: Bret Ambrose Date: Fri, 11 Apr 2025 13:55:28 -0700 Subject: [PATCH 01/43] Initial resolution attempt --- .builder/actions/sdk-ci-test.py | 21 +++++++++++++++++++ builder.json | 3 +++ .../aws/eventstreamrpc/EventStreamClient.h | 4 ++-- eventstream_rpc/source/EventStreamClient.cpp | 18 ++++++++-------- eventstream_rpc/tests/CMakeLists.txt | 6 +++--- 5 files changed, 38 insertions(+), 14 deletions(-) create mode 100644 .builder/actions/sdk-ci-test.py diff --git a/.builder/actions/sdk-ci-test.py b/.builder/actions/sdk-ci-test.py new file mode 100644 index 000000000..fc95230ff --- /dev/null +++ b/.builder/actions/sdk-ci-test.py @@ -0,0 +1,21 @@ +import Builder + +class SdkCiTest(Builder.Action): + + def run(self, env): + + actions = [] + java_sdk_dir = None + + try: + java_sdk_dir = Builder.SetupEventStreamEchoServer().run(env) + Builder.SetupCrossCICrtEnvironment().run(env) + env.shell.exec(["make", "test"], check=True) + except: + print(f'Failure while running tests') + actions.append("exit 1") + finally: + if java_sdk_dir: + env.shell.rm(java_sdk_dir) + + return Builder.Script(actions, name='sdk-ci-test') diff --git a/builder.json b/builder.json index f1b1b3787..9a3423938 100644 --- a/builder.json +++ b/builder.json @@ -23,6 +23,9 @@ "build", "build-samples" ], + "test_steps": [ + "sdk-ci-test" + ], "variants" : { "skip_sample": { "!build_steps": [ diff --git a/eventstream_rpc/include/aws/eventstreamrpc/EventStreamClient.h b/eventstream_rpc/include/aws/eventstreamrpc/EventStreamClient.h index 4b4904729..706b94fdf 100644 --- a/eventstream_rpc/include/aws/eventstreamrpc/EventStreamClient.h +++ b/eventstream_rpc/include/aws/eventstreamrpc/EventStreamClient.h @@ -717,8 +717,8 @@ namespace Aws std::mutex m_continuationMutex; bool m_resultReceived; std::promise m_initialResponsePromise; - std::atomic_int m_expectedCloses; - std::atomic_bool m_streamClosedCalled; + bool m_expectingClose; + bool m_streamClosedCalled; std::condition_variable m_closeReady; }; diff --git a/eventstream_rpc/source/EventStreamClient.cpp b/eventstream_rpc/source/EventStreamClient.cpp index d5e816702..ddf1d1c99 100644 --- a/eventstream_rpc/source/EventStreamClient.cpp +++ b/eventstream_rpc/source/EventStreamClient.cpp @@ -1134,7 +1134,7 @@ namespace Aws Crt::Allocator *allocator) noexcept : m_operationModelContext(operationModelContext), m_asyncLaunchMode(std::launch::deferred), m_messageCount(0), m_allocator(allocator), m_streamHandler(streamHandler), - m_clientContinuation(connection.NewStream(*this)), m_expectedCloses(0), m_streamClosedCalled(false) + m_clientContinuation(connection.NewStream(*this)), m_expectingClose(false), m_streamClosedCalled(false) { } @@ -1142,7 +1142,7 @@ namespace Aws { Close().wait(); std::unique_lock lock(m_continuationMutex); - m_closeReady.wait(lock, [this] { return m_expectedCloses.load() == 0; }); + m_closeReady.wait(lock, [this] { return m_expectingClose == false; }); } TaggedResult::TaggedResult(Crt::ScopedResource operationResponse) noexcept @@ -1390,7 +1390,7 @@ namespace Aws if (messageFlags & AWS_EVENT_STREAM_RPC_MESSAGE_FLAG_TERMINATE_STREAM) { const std::lock_guard lock(m_continuationMutex); - m_expectedCloses.fetch_add(1); + m_expectingClose = true; } m_messageCount += 1; @@ -1528,13 +1528,13 @@ namespace Aws m_resultReceived = true; } - if (m_expectedCloses.load() > 0) + if (m_expectingClose) { - m_expectedCloses.fetch_sub(1); - if (!m_streamClosedCalled.load() && m_streamHandler) + m_expectingClose = false; + if (!m_streamClosedCalled && m_streamHandler) { m_streamHandler->OnStreamClosed(); - m_streamClosedCalled.store(true); + m_streamClosedCalled = true; } m_closeReady.notify_one(); } @@ -1545,7 +1545,7 @@ namespace Aws std::future ClientOperation::Close(OnMessageFlushCallback onMessageFlushCallback) noexcept { const std::lock_guard lock(m_continuationMutex); - if (m_expectedCloses.load() > 0 || m_clientContinuation.IsClosed()) + if (m_expectingClose || m_clientContinuation.IsClosed()) { std::promise errorPromise; errorPromise.set_value({EVENT_STREAM_RPC_CONTINUATION_CLOSED, 0}); @@ -1592,7 +1592,7 @@ namespace Aws } else { - m_expectedCloses.fetch_add(1); + m_expectingClose = true; return callbackContainer->onFlushPromise.get_future(); } diff --git a/eventstream_rpc/tests/CMakeLists.txt b/eventstream_rpc/tests/CMakeLists.txt index 53ac62dc1..f048ac8e0 100644 --- a/eventstream_rpc/tests/CMakeLists.txt +++ b/eventstream_rpc/tests/CMakeLists.txt @@ -32,9 +32,9 @@ set(TEST_BINARY_NAME ${PROJECT_NAME}-tests) add_test_case(OperateWhileDisconnected) # The tests below can be commented out when an EchoRPC Server is running on 127.0.0.1:8033 -#add_test_case(EventStreamConnect) -#add_test_case(EchoOperation) -#add_test_case(StressTestClient) +add_test_case(EventStreamConnect) +add_test_case(EchoOperation) +add_test_case(StressTestClient) generate_cpp_test_driver(${TEST_BINARY_NAME}) aws_add_sanitizers(${TEST_BINARY_NAME}) target_include_directories(${TEST_BINARY_NAME} PUBLIC From a99cd1e5e13d227ae13ebbe039a9eec766376f70 Mon Sep 17 00:00:00 2001 From: Bret Ambrose Date: Fri, 11 Apr 2025 14:24:21 -0700 Subject: [PATCH 02/43] Update --- .builder/actions/{sdk-ci-test.py => sdk-ci-test-setup.py} | 7 +++---- builder.json | 3 ++- 2 files changed, 5 insertions(+), 5 deletions(-) rename .builder/actions/{sdk-ci-test.py => sdk-ci-test-setup.py} (65%) diff --git a/.builder/actions/sdk-ci-test.py b/.builder/actions/sdk-ci-test-setup.py similarity index 65% rename from .builder/actions/sdk-ci-test.py rename to .builder/actions/sdk-ci-test-setup.py index fc95230ff..a9aaf6dc1 100644 --- a/.builder/actions/sdk-ci-test.py +++ b/.builder/actions/sdk-ci-test-setup.py @@ -1,6 +1,6 @@ import Builder -class SdkCiTest(Builder.Action): +class SdkCiTestSetup(Builder.Action): def run(self, env): @@ -10,12 +10,11 @@ def run(self, env): try: java_sdk_dir = Builder.SetupEventStreamEchoServer().run(env) Builder.SetupCrossCICrtEnvironment().run(env) - env.shell.exec(["make", "test"], check=True) except: - print(f'Failure while running tests') + print(f'Failure while setting up tests') actions.append("exit 1") finally: if java_sdk_dir: env.shell.rm(java_sdk_dir) - return Builder.Script(actions, name='sdk-ci-test') + return Builder.Script(actions, name='sdk-ci-test-setup') diff --git a/builder.json b/builder.json index 9a3423938..4f1256ac0 100644 --- a/builder.json +++ b/builder.json @@ -24,7 +24,8 @@ "build-samples" ], "test_steps": [ - "sdk-ci-test" + "sdk-ci-test-setup", + "test" ], "variants" : { "skip_sample": { From 904651a180746df8e683b34c462ad7fba15c1263 Mon Sep 17 00:00:00 2001 From: Bret Ambrose Date: Mon, 14 Apr 2025 08:41:46 -0700 Subject: [PATCH 03/43] Testing --- .builder/actions/sdk-ci-test-setup.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.builder/actions/sdk-ci-test-setup.py b/.builder/actions/sdk-ci-test-setup.py index a9aaf6dc1..344815e13 100644 --- a/.builder/actions/sdk-ci-test-setup.py +++ b/.builder/actions/sdk-ci-test-setup.py @@ -13,8 +13,8 @@ def run(self, env): except: print(f'Failure while setting up tests') actions.append("exit 1") - finally: - if java_sdk_dir: - env.shell.rm(java_sdk_dir) + #finally: + # if java_sdk_dir: + # env.shell.rm(java_sdk_dir) return Builder.Script(actions, name='sdk-ci-test-setup') From bb948aacb169202e9efc9823991bf1d933f5bbb4 Mon Sep 17 00:00:00 2001 From: Bret Ambrose Date: Mon, 14 Apr 2025 10:52:54 -0700 Subject: [PATCH 04/43] Pre-format sync --- .builder/actions/sdk-ci-test-setup.py | 10 +-- .../tests/EventStreamClientTest.cpp | 75 ++++++++++++++++++- 2 files changed, 77 insertions(+), 8 deletions(-) diff --git a/.builder/actions/sdk-ci-test-setup.py b/.builder/actions/sdk-ci-test-setup.py index 344815e13..1ffbca9ef 100644 --- a/.builder/actions/sdk-ci-test-setup.py +++ b/.builder/actions/sdk-ci-test-setup.py @@ -10,11 +10,11 @@ def run(self, env): try: java_sdk_dir = Builder.SetupEventStreamEchoServer().run(env) Builder.SetupCrossCICrtEnvironment().run(env) - except: - print(f'Failure while setting up tests') + except Exception as ex: + print(f'Failure while setting up tests: {ex}') actions.append("exit 1") - #finally: - # if java_sdk_dir: - # env.shell.rm(java_sdk_dir) + finally: + if java_sdk_dir: + env.shell.rm(java_sdk_dir) return Builder.Script(actions, name='sdk-ci-test-setup') diff --git a/eventstream_rpc/tests/EventStreamClientTest.cpp b/eventstream_rpc/tests/EventStreamClientTest.cpp index 232afc202..8ecbc0239 100644 --- a/eventstream_rpc/tests/EventStreamClientTest.cpp +++ b/eventstream_rpc/tests/EventStreamClientTest.cpp @@ -14,6 +14,7 @@ # undef GetMessage #endif +#include #include #include #include @@ -24,14 +25,45 @@ using namespace Awstest; struct EventStreamClientTestContext { + EventStreamClientTestContext(); + ~EventStreamClientTestContext(); + std::unique_ptr apiHandle; std::unique_ptr elGroup; std::unique_ptr resolver; std::unique_ptr clientBootstrap; + + Aws::Crt::String echoServerHostName; + uint16_t echoServerPort; + + struct aws_string *echoServerHostNameValue; + struct aws_string *echoServerPortValue; }; +EventStreamClientTestContext::EventStreamClientTestContext() : + echoServerPort(0), + echoServerHostNameValue(nullptr), + echoServerPortValue(nullptr) +{ +} + +EventStreamClientTestContext::~EventStreamClientTestContext() +{ + aws_string_destroy(echoServerHostNameValue); + aws_string_destroy(echoServerPortValue); +} + static EventStreamClientTestContext s_testContext; +static bool s_isEchoserverSetup(const EventStreamClientTestContext &context) +{ + return !context.echoServerHostName.empty() && context.echoServerPort > 0; +} + +AWS_STATIC_STRING_FROM_LITERAL(s_env_name_echo_server_host, "AWS_TEST_EVENT_STREAM_ECHO_SERVER_HOST"); +AWS_STATIC_STRING_FROM_LITERAL(s_env_name_echo_server_port, "AWS_TEST_EVENT_STREAM_ECHO_SERVER_PORT"); + + static int s_testSetup(struct aws_allocator *allocator, void *ctx) { auto *testContext = static_cast(ctx); @@ -43,6 +75,19 @@ static int s_testSetup(struct aws_allocator *allocator, void *ctx) testContext->clientBootstrap = std::unique_ptr( new Io::ClientBootstrap(*testContext->elGroup, *testContext->resolver, allocator)); + testContext->echoServerPort = 0; + testContext->echoServerHostName = ""; + + if (!aws_get_environment_value(allocator, s_env_name_echo_server_host, &testContext->echoServerHostNameValue) && testContext->echoServerHostNameValue != nullptr) + { + testContext->echoServerHostName = aws_string_c_str(testContext->echoServerHostNameValue); + } + + if (!aws_get_environment_value(allocator, s_env_name_echo_server_port, &testContext->echoServerPortValue) && testContext->echoServerPortValue != nullptr) + { + testContext->echoServerPort = static_cast(atoi(aws_string_c_str(testContext->echoServerPortValue))); + } + return AWS_ERROR_SUCCESS; } @@ -110,15 +155,21 @@ static void s_onMessageFlush(int errorCode) AWS_TEST_CASE_FIXTURE(EventStreamConnect, s_testSetup, s_TestEventStreamConnect, s_testTeardown, &s_testContext); static int s_TestEventStreamConnect(struct aws_allocator *allocator, void *ctx) { - auto *testContext = static_cast(ctx); + auto *testContext = static_cast(ctx); { + if (!s_isEchoserverSetup(*testContext)) + { + printf("Environment Variables are not set for the test, skip the test"); + return AWS_OP_SKIP; + } + MessageAmendment connectionAmendment; auto messageAmender = [&](void) -> const MessageAmendment & { return connectionAmendment; }; ConnectionConfig accessDeniedConfig; - accessDeniedConfig.SetHostName(Aws::Crt::String("127.0.0.1")); - accessDeniedConfig.SetPort(8033U); + accessDeniedConfig.SetHostName(testContext->echoServerHostName); + accessDeniedConfig.SetPort(testContext->echoServerPort); /* Happy path case. */ { @@ -173,6 +224,12 @@ static int s_TestOperationWhileDisconnected(struct aws_allocator *allocator, voi /* Don't connect at all and try running operations as normal. */ { + if (!s_isEchoserverSetup(*testContext)) + { + printf("Environment Variables are not set for the test, skip the test"); + return AWS_OP_SKIP; + } + ConnectionLifecycleHandler lifecycleHandler; Awstest::EchoTestRpcClient client(*testContext->clientBootstrap, allocator); auto echoMessage = client.NewEchoMessage(); @@ -204,6 +261,12 @@ AWS_TEST_CASE_FIXTURE(EchoOperation, s_testSetup, s_TestEchoOperation, s_testTea static int s_TestEchoOperation(struct aws_allocator *allocator, void *ctx) { auto *testContext = static_cast(ctx); + if (!s_isEchoserverSetup(*testContext)) + { + printf("Environment Variables are not set for the test, skip the test"); + return AWS_OP_SKIP; + } + ConnectionLifecycleHandler lifecycleHandler; Aws::Crt::String expectedMessage("Async I0 FTW"); EchoMessageRequest echoMessageRequest; @@ -495,6 +558,12 @@ AWS_TEST_CASE_FIXTURE(StressTestClient, s_testSetup, s_TestStressClient, s_testT static int s_TestStressClient(struct aws_allocator *allocator, void *ctx) { auto *testContext = static_cast(ctx); + if (!s_isEchoserverSetup(*testContext)) + { + printf("Environment Variables are not set for the test, skip the test"); + return AWS_OP_SKIP; + } + ThreadPool threadPool; ConnectionLifecycleHandler lifecycleHandler; Aws::Crt::String expectedMessage("Async I0 FTW"); From 8403ed4bce32cb37969d8c336431b0b64c91d0d4 Mon Sep 17 00:00:00 2001 From: Bret Ambrose Date: Mon, 14 Apr 2025 10:53:28 -0700 Subject: [PATCH 05/43] Updated format script --- format-check.py | 46 ++++++++++++++++++++++++++++++++++++++++++++++ format-check.sh | 24 ------------------------ 2 files changed, 46 insertions(+), 24 deletions(-) create mode 100755 format-check.py delete mode 100755 format-check.sh diff --git a/format-check.py b/format-check.py new file mode 100755 index 000000000..e3c69fcd5 --- /dev/null +++ b/format-check.py @@ -0,0 +1,46 @@ +#!/usr/bin/env python3 +import argparse +import os +from pathlib import Path +import re +from subprocess import list2cmdline, run +from tempfile import NamedTemporaryFile + +CLANG_FORMAT_VERSION = '18.1.6' + +INCLUDE_REGEX = re.compile(r'^(include|source|tests)/.*\.(cpp|h)$') +EXCLUDE_REGEX = re.compile(r'^$') + +arg_parser = argparse.ArgumentParser(description="Check with clang-format") +arg_parser.add_argument('-i', '--inplace-edit', action='store_true', + help="Edit files inplace") +args = arg_parser.parse_args() + +os.chdir(Path(__file__).parent) + +# create file containing list of all files to format +filepaths_file = NamedTemporaryFile(delete=False) +for dirpath, dirnames, filenames in os.walk('.'): + for filename in filenames: + # our regexes expect filepath to use forward slash + filepath = Path(dirpath, filename).as_posix() + if not INCLUDE_REGEX.match(filepath): + continue + if EXCLUDE_REGEX.match(filepath): + continue + + filepaths_file.write(f"{filepath}\n".encode()) +filepaths_file.close() + +# use pipx to run clang-format from PyPI +# this is a simple way to run the same clang-format version regardless of OS +cmd = ['pipx', 'run', f'clang-format=={CLANG_FORMAT_VERSION}', + f'--files={filepaths_file.name}'] +if args.inplace_edit: + cmd += ['-i'] +else: + cmd += ['--Werror', '--dry-run'] + +print(f"{Path.cwd()}$ {list2cmdline(cmd)}") +if run(cmd).returncode: + exit(1) diff --git a/format-check.sh b/format-check.sh deleted file mode 100755 index 0899f8975..000000000 --- a/format-check.sh +++ /dev/null @@ -1,24 +0,0 @@ -#!/usr/bin/env bash - -if [[ -z $CLANG_FORMAT ]] ; then - CLANG_FORMAT=clang-format -fi - -if NOT type $CLANG_FORMAT 2> /dev/null ; then - echo "No appropriate clang-format found." - exit 1 -fi - -FAIL=0 -SOURCE_FILES=`find deviceadvisor devicedefender discovery eventstream_rpc greengrass_ipc identity iotdevicecommon jobs shadow samples secure_tunneling -type f \( -name '*.h' -o -name '*.cpp' \)` -for i in $SOURCE_FILES -do - $CLANG_FORMAT -output-replacements-xml $i | grep -c " /dev/null - if [ $? -ne 1 ] - then - echo "$i failed clang-format check." - FAIL=1 - fi -done - -exit $FAIL From 1160d10e6cd93f93a8ccec210bdee6b8a1922159 Mon Sep 17 00:00:00 2001 From: Bret Ambrose Date: Tue, 15 Apr 2025 09:00:34 -0700 Subject: [PATCH 06/43] Update formatting to python-based with clang version control --- format-check.py | 33 ++-- .../source/IotSecureTunnelingClient.cpp | 27 ++-- secure_tunneling/source/SecureTunnel.cpp | 145 ++++++++++++++---- .../SubscribeToTunnelsNotifyRequest.cpp | 5 +- secure_tunneling/tests/main.cpp | 138 +++++++++-------- 5 files changed, 232 insertions(+), 116 deletions(-) diff --git a/format-check.py b/format-check.py index e3c69fcd5..b2678b8a4 100755 --- a/format-check.py +++ b/format-check.py @@ -8,7 +8,7 @@ CLANG_FORMAT_VERSION = '18.1.6' -INCLUDE_REGEX = re.compile(r'^(include|source|tests)/.*\.(cpp|h)$') +INCLUDE_REGEX = re.compile(r'.*(include|source|tests)/.*\.(cpp|h)$') EXCLUDE_REGEX = re.compile(r'^$') arg_parser = argparse.ArgumentParser(description="Check with clang-format") @@ -18,19 +18,24 @@ os.chdir(Path(__file__).parent) -# create file containing list of all files to format -filepaths_file = NamedTemporaryFile(delete=False) -for dirpath, dirnames, filenames in os.walk('.'): - for filename in filenames: - # our regexes expect filepath to use forward slash - filepath = Path(dirpath, filename).as_posix() - if not INCLUDE_REGEX.match(filepath): - continue - if EXCLUDE_REGEX.match(filepath): - continue - - filepaths_file.write(f"{filepath}\n".encode()) -filepaths_file.close() +check_dirs = ['deviceadvisor', 'devicedefender', 'discovery', 'eventstream_rpc', 'greengrass_ipc', 'identity', 'iotdevicecommon', 'jobs', 'shadow', 'samples', 'secure_tunneling'] + +for check_dir in check_dirs: + # create file containing list of all files to format + filepaths_file = NamedTemporaryFile(delete=False) + for dirpath, dirnames, filenames in os.walk(check_dir): + for filename in filenames: + + # our regexes expect filepath to use forward slash + filepath = Path(dirpath, filename).as_posix() + + if not INCLUDE_REGEX.match(filepath): + continue + if EXCLUDE_REGEX.match(filepath): + continue + + filepaths_file.write(f"{filepath}\n".encode()) + filepaths_file.close() # use pipx to run clang-format from PyPI # this is a simple way to run the same clang-format version regardless of OS diff --git a/secure_tunneling/source/IotSecureTunnelingClient.cpp b/secure_tunneling/source/IotSecureTunnelingClient.cpp index ec569de27..303294511 100644 --- a/secure_tunneling/source/IotSecureTunnelingClient.cpp +++ b/secure_tunneling/source/IotSecureTunnelingClient.cpp @@ -29,9 +29,15 @@ namespace Aws { } - IotSecureTunnelingClient::operator bool() const noexcept { return m_connection && *m_connection; } + IotSecureTunnelingClient::operator bool() const noexcept + { + return m_connection && *m_connection; + } - int IotSecureTunnelingClient::GetLastError() const noexcept { return aws_last_error(); } + int IotSecureTunnelingClient::GetLastError() const noexcept + { + return aws_last_error(); + } bool IotSecureTunnelingClient::SubscribeToTunnelsNotify( const Aws::Iotsecuretunneling::SubscribeToTunnelsNotifyRequest &request, @@ -45,7 +51,8 @@ namespace Aws uint16_t, const Aws::Crt::String &topic, Aws::Crt::Mqtt::QOS, - int errorCode) { + int errorCode) + { (void)topic; if (errorCode) { @@ -59,13 +66,13 @@ namespace Aws }; auto onSubscribePublish = - [handler]( - Aws::Crt::Mqtt::MqttConnection &, const Aws::Crt::String &, const Aws::Crt::ByteBuf &payload) { - Aws::Crt::String objectStr(reinterpret_cast(payload.buffer), payload.len); - Aws::Crt::JsonObject jsonObject(objectStr); - Aws::Iotsecuretunneling::SecureTunnelingNotifyResponse response(jsonObject); - handler(&response, AWS_ERROR_SUCCESS); - }; + [handler](Aws::Crt::Mqtt::MqttConnection &, const Aws::Crt::String &, const Aws::Crt::ByteBuf &payload) + { + Aws::Crt::String objectStr(reinterpret_cast(payload.buffer), payload.len); + Aws::Crt::JsonObject jsonObject(objectStr); + Aws::Iotsecuretunneling::SecureTunnelingNotifyResponse response(jsonObject); + handler(&response, AWS_ERROR_SUCCESS); + }; Aws::Crt::StringStream subscribeTopicSStr; subscribeTopicSStr << "$aws" diff --git a/secure_tunneling/source/SecureTunnel.cpp b/secure_tunneling/source/SecureTunnel.cpp index d2101bc94..c5f02267d 100644 --- a/secure_tunneling/source/SecureTunnel.cpp +++ b/secure_tunneling/source/SecureTunnel.cpp @@ -164,11 +164,20 @@ namespace Aws return true; } - const Crt::Optional &Message::getPayload() const noexcept { return m_payload; } + const Crt::Optional &Message::getPayload() const noexcept + { + return m_payload; + } - const Crt::Optional &Message::getServiceId() const noexcept { return m_serviceId; } + const Crt::Optional &Message::getServiceId() const noexcept + { + return m_serviceId; + } - const uint32_t &Message::getConnectionId() const noexcept { return m_connectionId; } + const uint32_t &Message::getConnectionId() const noexcept + { + return m_connectionId; + } Message::~Message() { @@ -193,9 +202,15 @@ namespace Aws m_messageType = aws_byte_cursor_from_buf(&m_messageTypeStorage); } - const Crt::ByteCursor &SendMessageCompleteData::getMessageType() const noexcept { return m_messageType; } + const Crt::ByteCursor &SendMessageCompleteData::getMessageType() const noexcept + { + return m_messageType; + } - SendMessageCompleteData::~SendMessageCompleteData() { aws_byte_buf_clean_up(&m_messageTypeStorage); } + SendMessageCompleteData::~SendMessageCompleteData() + { + aws_byte_buf_clean_up(&m_messageTypeStorage); + } //*********************************************************************************************************************** /* ConnectionData */ @@ -215,9 +230,18 @@ namespace Aws setPacketByteBufOptional(m_serviceId3, m_serviceId3Storage, m_allocator, connection.service_id_3); } - const Crt::Optional &ConnectionData::getServiceId1() const noexcept { return m_serviceId1; } - const Crt::Optional &ConnectionData::getServiceId2() const noexcept { return m_serviceId2; } - const Crt::Optional &ConnectionData::getServiceId3() const noexcept { return m_serviceId3; } + const Crt::Optional &ConnectionData::getServiceId1() const noexcept + { + return m_serviceId1; + } + const Crt::Optional &ConnectionData::getServiceId2() const noexcept + { + return m_serviceId2; + } + const Crt::Optional &ConnectionData::getServiceId3() const noexcept + { + return m_serviceId3; + } ConnectionData::~ConnectionData() { @@ -241,11 +265,20 @@ namespace Aws m_connectionId = message.connection_id; } - const Crt::Optional &StreamStartedData::getServiceId() const noexcept { return m_serviceId; } + const Crt::Optional &StreamStartedData::getServiceId() const noexcept + { + return m_serviceId; + } - const uint32_t &StreamStartedData::getConnectionId() const noexcept { return m_connectionId; } + const uint32_t &StreamStartedData::getConnectionId() const noexcept + { + return m_connectionId; + } - StreamStartedData::~StreamStartedData() { aws_byte_buf_clean_up(&m_serviceIdStorage); } + StreamStartedData::~StreamStartedData() + { + aws_byte_buf_clean_up(&m_serviceIdStorage); + } //*********************************************************************************************************************** /* StreamStoppedData */ @@ -261,9 +294,15 @@ namespace Aws setPacketByteBufOptional(m_serviceId, m_serviceIdStorage, m_allocator, message.service_id); } - const Crt::Optional &StreamStoppedData::getServiceId() const noexcept { return m_serviceId; } + const Crt::Optional &StreamStoppedData::getServiceId() const noexcept + { + return m_serviceId; + } - StreamStoppedData::~StreamStoppedData() { aws_byte_buf_clean_up(&m_serviceIdStorage); } + StreamStoppedData::~StreamStoppedData() + { + aws_byte_buf_clean_up(&m_serviceIdStorage); + } //*********************************************************************************************************************** /* ConnectionStartedData */ @@ -285,9 +324,15 @@ namespace Aws return m_serviceId; } - const uint32_t &ConnectionStartedData::getConnectionId() const noexcept { return m_connectionId; } + const uint32_t &ConnectionStartedData::getConnectionId() const noexcept + { + return m_connectionId; + } - ConnectionStartedData::~ConnectionStartedData() { aws_byte_buf_clean_up(&m_serviceIdStorage); } + ConnectionStartedData::~ConnectionStartedData() + { + aws_byte_buf_clean_up(&m_serviceIdStorage); + } //*********************************************************************************************************************** /* ConnectionResetData */ @@ -304,11 +349,20 @@ namespace Aws m_connectionId = message.connection_id; } - const Crt::Optional &ConnectionResetData::getServiceId() const noexcept { return m_serviceId; } + const Crt::Optional &ConnectionResetData::getServiceId() const noexcept + { + return m_serviceId; + } - const uint32_t &ConnectionResetData::getConnectionId() const noexcept { return m_connectionId; } + const uint32_t &ConnectionResetData::getConnectionId() const noexcept + { + return m_connectionId; + } - ConnectionResetData::~ConnectionResetData() { aws_byte_buf_clean_up(&m_serviceIdStorage); } + ConnectionResetData::~ConnectionResetData() + { + aws_byte_buf_clean_up(&m_serviceIdStorage); + } //*********************************************************************************************************************** /* SecureTunnelBuilder */ @@ -786,7 +840,10 @@ namespace Aws return *this; } - bool SecureTunnel::IsValid() { return m_secure_tunnel ? true : false; } + bool SecureTunnel::IsValid() + { + return m_secure_tunnel ? true : false; + } int SecureTunnel::Start() { @@ -797,13 +854,22 @@ namespace Aws return aws_secure_tunnel_start(m_secure_tunnel); } - int SecureTunnel::Stop() { return aws_secure_tunnel_stop(m_secure_tunnel); } + int SecureTunnel::Stop() + { + return aws_secure_tunnel_stop(m_secure_tunnel); + } /* Deprecated - Use Start() */ - int SecureTunnel::Connect() { return Start(); } + int SecureTunnel::Connect() + { + return Start(); + } /* Deprecated - Use Stop() */ - int SecureTunnel::Close() { return Stop(); } + int SecureTunnel::Close() + { + return Stop(); + } /* Deprecated - Use SendMessage() */ int SecureTunnel::SendData(const Crt::ByteCursor &data) @@ -825,9 +891,18 @@ namespace Aws return aws_secure_tunnel_send_message(m_secure_tunnel, &message); } - int SecureTunnel::SendStreamStart() { return SendStreamStart(""); } - int SecureTunnel::SendStreamStart(std::string serviceId) { return SendStreamStart(serviceId, 0); } - int SecureTunnel::SendStreamStart(Crt::ByteCursor serviceId) { return SendStreamStart(serviceId, 0); } + int SecureTunnel::SendStreamStart() + { + return SendStreamStart(""); + } + int SecureTunnel::SendStreamStart(std::string serviceId) + { + return SendStreamStart(serviceId, 0); + } + int SecureTunnel::SendStreamStart(Crt::ByteCursor serviceId) + { + return SendStreamStart(serviceId, 0); + } int SecureTunnel::SendStreamStart(std::string serviceId, uint32_t connectionId) { struct aws_byte_cursor service_id_cur; @@ -851,7 +926,10 @@ namespace Aws return aws_secure_tunnel_stream_start(m_secure_tunnel, &messageView); } - int SecureTunnel::SendConnectionStart(uint32_t connectionId) { return SendConnectionStart("", connectionId); } + int SecureTunnel::SendConnectionStart(uint32_t connectionId) + { + return SendConnectionStart("", connectionId); + } int SecureTunnel::SendConnectionStart(std::string serviceId, uint32_t connectionId) { @@ -873,9 +951,15 @@ namespace Aws return aws_secure_tunnel_connection_start(m_secure_tunnel, &messageView); } - int SecureTunnel::SendStreamReset() { return aws_secure_tunnel_stream_reset(m_secure_tunnel, NULL); } + int SecureTunnel::SendStreamReset() + { + return aws_secure_tunnel_stream_reset(m_secure_tunnel, NULL); + } - aws_secure_tunnel *SecureTunnel::GetUnderlyingHandle() { return m_secure_tunnel; } + aws_secure_tunnel *SecureTunnel::GetUnderlyingHandle() + { + return m_secure_tunnel; + } void SecureTunnel::s_OnConnectionComplete( const struct aws_secure_tunnel_connection_view *connection, @@ -1097,6 +1181,9 @@ namespace Aws } } - void SecureTunnel::Shutdown() { Stop(); } + void SecureTunnel::Shutdown() + { + Stop(); + } } // namespace Iotsecuretunneling } // namespace Aws diff --git a/secure_tunneling/source/SubscribeToTunnelsNotifyRequest.cpp b/secure_tunneling/source/SubscribeToTunnelsNotifyRequest.cpp index 62c9608c9..011a83de1 100644 --- a/secure_tunneling/source/SubscribeToTunnelsNotifyRequest.cpp +++ b/secure_tunneling/source/SubscribeToTunnelsNotifyRequest.cpp @@ -28,7 +28,10 @@ namespace Aws (void)doc; } - void SubscribeToTunnelsNotifyRequest::SerializeToObject(Aws::Crt::JsonObject &object) const { (void)object; } + void SubscribeToTunnelsNotifyRequest::SerializeToObject(Aws::Crt::JsonObject &object) const + { + (void)object; + } SubscribeToTunnelsNotifyRequest::SubscribeToTunnelsNotifyRequest(const Crt::JsonView &doc) { diff --git a/secure_tunneling/tests/main.cpp b/secure_tunneling/tests/main.cpp index bd6606e19..93427786d 100644 --- a/secure_tunneling/tests/main.cpp +++ b/secure_tunneling/tests/main.cpp @@ -99,7 +99,8 @@ int main(int argc, char *argv[]) } builderDestination.WithOnMessageReceived( - [&](SecureTunnel *secureTunnel, const MessageReceivedEventData &eventData) { + [&](SecureTunnel *secureTunnel, const MessageReceivedEventData &eventData) + { { (void)secureTunnel; (void)eventData; @@ -108,17 +109,20 @@ int main(int argc, char *argv[]) } }); - builderSource.WithOnMessageReceived([&](SecureTunnel *secureTunnel, const MessageReceivedEventData &eventData) { + builderSource.WithOnMessageReceived( + [&](SecureTunnel *secureTunnel, const MessageReceivedEventData &eventData) { - (void)secureTunnel; - (void)eventData; - fprintf(stdout, "Source Client Received Message\n"); - promiseSourceReceivedMessage.set_value(); - } - }); + { + (void)secureTunnel; + (void)eventData; + fprintf(stdout, "Source Client Received Message\n"); + promiseSourceReceivedMessage.set_value(); + } + }); builderDestination.WithOnSendMessageComplete( - [&](SecureTunnel *secureTunnel, int errorCode, const SendMessageCompleteEventData &eventData) { + [&](SecureTunnel *secureTunnel, int errorCode, const SendMessageCompleteEventData &eventData) + { (void)secureTunnel; (void)eventData; @@ -138,7 +142,8 @@ int main(int argc, char *argv[]) }); builderSource.WithOnSendMessageComplete( - [&](SecureTunnel *secureTunnel, int errorCode, const SendMessageCompleteEventData &eventData) { + [&](SecureTunnel *secureTunnel, int errorCode, const SendMessageCompleteEventData &eventData) + { (void)secureTunnel; (void)eventData; @@ -158,42 +163,46 @@ int main(int argc, char *argv[]) }); builderDestination.WithOnConnectionSuccess( - [&](SecureTunnel *secureTunnel, const ConnectionSuccessEventData &eventData) { + [&](SecureTunnel *secureTunnel, const ConnectionSuccessEventData &eventData) + { (void)secureTunnel; (void)eventData; fprintf(stdout, "Destination Client Connection Success\n"); promiseDestinationConnected.set_value(); }); - builderSource.WithOnConnectionSuccess([&](SecureTunnel *secureTunnel, const ConnectionSuccessEventData &eventData) { - (void)secureTunnel; - (void)eventData; + builderSource.WithOnConnectionSuccess( + [&](SecureTunnel *secureTunnel, const ConnectionSuccessEventData &eventData) + { + (void)secureTunnel; + (void)eventData; - fprintf(stdout, "Source Client Connection Success\n"); + fprintf(stdout, "Source Client Connection Success\n"); - /* Use a Multiplexing (Service Id) if available on this Secure Tunnel */ - if (eventData.connectionData->getServiceId1().has_value()) - { - /* Store the service id for future use */ - aws_byte_buf_clean_up(&m_serviceIdStorage); - AWS_ZERO_STRUCT(m_serviceIdStorage); - aws_byte_buf_init_copy_from_cursor( - &m_serviceIdStorage, allocator, eventData.connectionData->getServiceId1().value()); - m_serviceId = aws_byte_cursor_from_buf(&m_serviceIdStorage); - secureTunnel->SendStreamStart(m_serviceId.value(), connectionId); - fprintf(stdout, "Stream Start sent from Source Client.\n"); - } - else - { - fprintf(stdout, "Secure Tunnel should have service ids set for proper testing\n"); - exit(-1); - } + /* Use a Multiplexing (Service Id) if available on this Secure Tunnel */ + if (eventData.connectionData->getServiceId1().has_value()) + { + /* Store the service id for future use */ + aws_byte_buf_clean_up(&m_serviceIdStorage); + AWS_ZERO_STRUCT(m_serviceIdStorage); + aws_byte_buf_init_copy_from_cursor( + &m_serviceIdStorage, allocator, eventData.connectionData->getServiceId1().value()); + m_serviceId = aws_byte_cursor_from_buf(&m_serviceIdStorage); + secureTunnel->SendStreamStart(m_serviceId.value(), connectionId); + fprintf(stdout, "Stream Start sent from Source Client.\n"); + } + else + { + fprintf(stdout, "Secure Tunnel should have service ids set for proper testing\n"); + exit(-1); + } - promiseSourceConnected.set_value(); - }); + promiseSourceConnected.set_value(); + }); builderDestination.WithOnStreamStarted( - [&](SecureTunnel *secureTunnel, int errorCode, const StreamStartedEventData &eventData) { + [&](SecureTunnel *secureTunnel, int errorCode, const StreamStartedEventData &eventData) + { (void)secureTunnel; (void)eventData; if (!errorCode) @@ -209,39 +218,44 @@ int main(int argc, char *argv[]) } }); - builderDestination.WithOnConnectionStarted([&](SecureTunnel *secureTunnel, - int errorCode, - const ConnectionStartedEventData &eventData) { - (void)secureTunnel; - (void)eventData; - if (!errorCode) - { - fprintf(stdout, "Connection Started on Destination Client.\n"); - promiseDestinationConnectionStarted.set_value(); - } - else + builderDestination.WithOnConnectionStarted( + [&](SecureTunnel *secureTunnel, int errorCode, const ConnectionStartedEventData &eventData) { - fprintf(stdout, "Connection Start failed with error code %d(%s)\n", errorCode, ErrorDebugString(errorCode)); - aws_byte_buf_clean_up(&m_serviceIdStorage); - exit(-1); - } - }); + (void)secureTunnel; + (void)eventData; + if (!errorCode) + { + fprintf(stdout, "Connection Started on Destination Client.\n"); + promiseDestinationConnectionStarted.set_value(); + } + else + { + fprintf( + stdout, "Connection Start failed with error code %d(%s)\n", errorCode, ErrorDebugString(errorCode)); + aws_byte_buf_clean_up(&m_serviceIdStorage); + exit(-1); + } + }); builderDestination.WithOnConnectionShutdown([&]() { fprintf(stdout, "Destination Connection Shutdown\n"); }); builderSource.WithOnConnectionShutdown([&]() { fprintf(stdout, "Source Connection Shutdown\n"); }); - builderDestination.WithOnStopped([&](SecureTunnel *secureTunnel) { - (void)secureTunnel; - fprintf(stdout, "Destination entered Stopped State\n"); - promiseDestinationStopped.set_value(); - }); - - builderSource.WithOnStopped([&](SecureTunnel *secureTunnel) { - (void)secureTunnel; - fprintf(stdout, "Source has entered Stopped State\n"); - promiseSourceStopped.set_value(); - }); + builderDestination.WithOnStopped( + [&](SecureTunnel *secureTunnel) + { + (void)secureTunnel; + fprintf(stdout, "Destination entered Stopped State\n"); + promiseDestinationStopped.set_value(); + }); + + builderSource.WithOnStopped( + [&](SecureTunnel *secureTunnel) + { + (void)secureTunnel; + fprintf(stdout, "Source has entered Stopped State\n"); + promiseSourceStopped.set_value(); + }); /* Create Secure Tunnel using the options set with the builder */ std::shared_ptr secureTunnelDestination = builderDestination.Build(); From b4d3bcdf6613f1a4c380eb57e9d3dc1022644d81 Mon Sep 17 00:00:00 2001 From: Bret Ambrose Date: Tue, 15 Apr 2025 09:04:40 -0700 Subject: [PATCH 07/43] Update to builder version with eventstream server support --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3079dabea..571f33583 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -7,7 +7,7 @@ on: - 'docs' env: - BUILDER_VERSION: v0.9.56 + BUILDER_VERSION: v0.9.78 BUILDER_SOURCE: releases BUILDER_HOST: https://d19elf31gohf1l.cloudfront.net PACKAGE_NAME: aws-iot-device-sdk-cpp-v2 From 21abfcd25cc5fe91977b212c937a3c3518de22ef Mon Sep 17 00:00:00 2001 From: Bret Ambrose Date: Tue, 15 Apr 2025 12:36:57 -0700 Subject: [PATCH 08/43] Credentials setup --- .github/workflows/ci.yml | 85 ++++++++++++++++++++++++++++++---------- 1 file changed, 65 insertions(+), 20 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 571f33583..d61afd245 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -7,7 +7,7 @@ on: - 'docs' env: - BUILDER_VERSION: v0.9.78 + BUILDER_VERSION: v0.9.79 BUILDER_SOURCE: releases BUILDER_HOST: https://d19elf31gohf1l.cloudfront.net PACKAGE_NAME: aws-iot-device-sdk-cpp-v2 @@ -153,6 +153,11 @@ jobs: permissions: id-token: write # This is required for requesting the JWT steps: + - name: configure AWS credentials (containers) + uses: aws-actions/configure-aws-credentials@v2 + with: + role-to-assume: ${{ env.CI_IOT_CONTAINERS }} + aws-region: ${{ env.AWS_DEFAULT_REGION }} - name: Build ${{ env.PACKAGE_NAME }} + consumers run: | md ${{ env.CI_FOLDER }} @@ -202,6 +207,11 @@ jobs: permissions: id-token: write # This is required for requesting the JWT steps: + - name: configure AWS credentials (containers) + uses: aws-actions/configure-aws-credentials@v2 + with: + role-to-assume: ${{ env.CI_IOT_CONTAINERS }} + aws-region: ${{ env.AWS_DEFAULT_REGION }} - name: Build ${{ env.PACKAGE_NAME }} + consumers run: | md ${{ env.CI_FOLDER }} @@ -247,6 +257,11 @@ jobs: permissions: id-token: write # This is required for requesting the JWT steps: + - name: configure AWS credentials (containers) + uses: aws-actions/configure-aws-credentials@v2 + with: + role-to-assume: ${{ env.CI_IOT_CONTAINERS }} + aws-region: ${{ env.AWS_DEFAULT_REGION }} - name: Build ${{ env.PACKAGE_NAME }} + consumers run: | md ${{ env.CI_FOLDER }} @@ -292,30 +307,40 @@ jobs: permissions: id-token: write # This is required for requesting the JWT steps: - - name: Build ${{ env.PACKAGE_NAME }} + consumers - run: | - md ${{ env.CI_FOLDER }} - cd ${{ env.CI_FOLDER }} - python -c "from urllib.request import urlretrieve; urlretrieve('${{ env.BUILDER_HOST }}/${{ env.BUILDER_SOURCE }}/${{ env.BUILDER_VERSION }}/builder.pyz', 'builder.pyz')" - python builder.pyz build -p ${{ env.PACKAGE_NAME }} --cmake-extra=-DUSE_CPU_EXTENSIONS=OFF - - name: Running samples in CI setup - run: | - python -m pip install boto3 - - name: configure AWS credentials (CyclePubSub) - uses: aws-actions/configure-aws-credentials@v2 - with: - role-to-assume: ${{ env.CI_CYCLEPUBSUB_ROLE }} - aws-region: ${{ env.AWS_DEFAULT_REGION }} - - name: Run and check AppVerifier - run: | - cd ${{ env.CI_FOLDER }} - echo "Starting to run AppVerifier with cycle pub-sub sample" - python ${{ env.CI_UTILS_FOLDER }}/appverifier_launch_sample.py --sample_file ".\aws-iot-device-sdk-cpp-v2\build\samples\pub_sub\cycle_pub_sub\RelWithDebInfo\cycle-pub-sub.exe" --sample_secret_endpoint 'ci/endpoint' --sample_secret_certificate 'ci/CyclePubSub/cert' --sample_secret_private_key 'ci/CyclePubSub/key' + - name: configure AWS credentials (containers) + uses: aws-actions/configure-aws-credentials@v2 + with: + role-to-assume: ${{ env.CI_IOT_CONTAINERS }} + aws-region: ${{ env.AWS_DEFAULT_REGION }} + - name: Build ${{ env.PACKAGE_NAME }} + consumers + run: | + md ${{ env.CI_FOLDER }} + cd ${{ env.CI_FOLDER }} + python -c "from urllib.request import urlretrieve; urlretrieve('${{ env.BUILDER_HOST }}/${{ env.BUILDER_SOURCE }}/${{ env.BUILDER_VERSION }}/builder.pyz', 'builder.pyz')" + python builder.pyz build -p ${{ env.PACKAGE_NAME }} --cmake-extra=-DUSE_CPU_EXTENSIONS=OFF + - name: Running samples in CI setup + run: | + python -m pip install boto3 + - name: configure AWS credentials (CyclePubSub) + uses: aws-actions/configure-aws-credentials@v2 + with: + role-to-assume: ${{ env.CI_CYCLEPUBSUB_ROLE }} + aws-region: ${{ env.AWS_DEFAULT_REGION }} + - name: Run and check AppVerifier + run: | + cd ${{ env.CI_FOLDER }} + echo "Starting to run AppVerifier with cycle pub-sub sample" + python ${{ env.CI_UTILS_FOLDER }}/appverifier_launch_sample.py --sample_file ".\aws-iot-device-sdk-cpp-v2\build\samples\pub_sub\cycle_pub_sub\RelWithDebInfo\cycle-pub-sub.exe" --sample_secret_endpoint 'ci/endpoint' --sample_secret_certificate 'ci/CyclePubSub/cert' --sample_secret_private_key 'ci/CyclePubSub/key' windows-shared-lib: runs-on: windows-latest permissions: id-token: write # This is required for requesting the JWT steps: + - name: configure AWS credentials (containers) + uses: aws-actions/configure-aws-credentials@v2 + with: + role-to-assume: ${{ env.CI_IOT_CONTAINERS }} + aws-region: ${{ env.AWS_DEFAULT_REGION }} - name: Build ${{ env.PACKAGE_NAME }} + consumers run: | md ${{ env.CI_FOLDER }} @@ -334,6 +359,11 @@ jobs: id-token: write # This is required for requesting the JWT security-events: write # This is required for pkcs12 sample to sign the key steps: + - name: configure AWS credentials (containers) + uses: aws-actions/configure-aws-credentials@v2 + with: + role-to-assume: ${{ env.CI_IOT_CONTAINERS }} + aws-region: ${{ env.AWS_DEFAULT_REGION }} - name: Build ${{ env.PACKAGE_NAME }} + consumers run: | python3 -c "from urllib.request import urlretrieve; urlretrieve('${{ env.BUILDER_HOST }}/${{ env.BUILDER_SOURCE }}/${{ env.BUILDER_VERSION }}/builder.pyz', 'builder')" @@ -398,6 +428,11 @@ jobs: permissions: id-token: write # This is required for requesting the JWT steps: + - name: configure AWS credentials (containers) + uses: aws-actions/configure-aws-credentials@v2 + with: + role-to-assume: ${{ env.CI_IOT_CONTAINERS }} + aws-region: ${{ env.AWS_DEFAULT_REGION }} - name: Build ${{ env.PACKAGE_NAME }} + consumers run: | python -c "from urllib.request import urlretrieve; urlretrieve('${{ env.BUILDER_HOST }}/${{ env.BUILDER_SOURCE }}/${{ env.BUILDER_VERSION }}/builder.pyz?run=${{ env.RUN }}', 'builder.pyz')" @@ -458,6 +493,11 @@ jobs: sudo apt install cmake gcc --version cmake --version + - name: configure AWS credentials (containers) + uses: aws-actions/configure-aws-credentials@v2 + with: + role-to-assume: ${{ env.CI_IOT_CONTAINERS }} + aws-region: ${{ env.AWS_DEFAULT_REGION }} - name: Build ${{ env.PACKAGE_NAME }} + consumers run: | echo "Downloading source" @@ -694,6 +734,11 @@ jobs: permissions: id-token: write # This is required for requesting the JWT steps: + - name: configure AWS credentials (containers) + uses: aws-actions/configure-aws-credentials@v2 + with: + role-to-assume: ${{ env.CI_IOT_CONTAINERS }} + aws-region: ${{ env.AWS_DEFAULT_REGION }} - name: Build ${{ env.PACKAGE_NAME }} + consumers run: | echo "Downloading source" From 4df20ffa02e970e079f17a011436189a09c32b3e Mon Sep 17 00:00:00 2001 From: Bret Ambrose Date: Tue, 15 Apr 2025 12:58:36 -0700 Subject: [PATCH 09/43] Cleanup strings per fixture tear down --- eventstream_rpc/tests/EventStreamClientTest.cpp | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/eventstream_rpc/tests/EventStreamClientTest.cpp b/eventstream_rpc/tests/EventStreamClientTest.cpp index 8ecbc0239..5ff119f76 100644 --- a/eventstream_rpc/tests/EventStreamClientTest.cpp +++ b/eventstream_rpc/tests/EventStreamClientTest.cpp @@ -26,7 +26,6 @@ using namespace Awstest; struct EventStreamClientTestContext { EventStreamClientTestContext(); - ~EventStreamClientTestContext(); std::unique_ptr apiHandle; std::unique_ptr elGroup; @@ -47,12 +46,6 @@ EventStreamClientTestContext::EventStreamClientTestContext() : { } -EventStreamClientTestContext::~EventStreamClientTestContext() -{ - aws_string_destroy(echoServerHostNameValue); - aws_string_destroy(echoServerPortValue); -} - static EventStreamClientTestContext s_testContext; static bool s_isEchoserverSetup(const EventStreamClientTestContext &context) @@ -101,6 +94,12 @@ static int s_testTeardown(struct aws_allocator *allocator, int setup_result, voi /* The ApiHandle must be deallocated last or a deadlock occurs. */ testContext->apiHandle.reset(); + aws_string_destroy(testContext->echoServerHostNameValue); + testContext->echoServerHostNameValue = nullptr; + + aws_string_destroy(testContext->echoServerPortValue); + testContext->echoServerPortValue = nullptr; + return AWS_ERROR_SUCCESS; } From 6c0184c385bedf917793748193eb4a64ebfb2d18 Mon Sep 17 00:00:00 2001 From: Bret Ambrose Date: Tue, 15 Apr 2025 13:19:26 -0700 Subject: [PATCH 10/43] Try ubuntu 22 to see if maven is pre-installed --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d61afd245..faee4db88 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -90,7 +90,7 @@ jobs: aws s3 cp s3://aws-crt-test-stuff/ci/${{ env.BUILDER_VERSION }}/linux-container-ci.sh ./linux-container-ci.sh && chmod a+x ./linux-container-ci.sh ./linux-container-ci.sh ${{ env.BUILDER_VERSION }} aws-crt-${{ matrix.image }} build -p ${{ env.PACKAGE_NAME }} linux-compiler-compat: - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 strategy: matrix: compiler: From e6fbe72602280c42b0246fb15a970079ff432d8c Mon Sep 17 00:00:00 2001 From: Bret Ambrose Date: Mon, 21 Apr 2025 14:28:54 -0700 Subject: [PATCH 11/43] No allocator exists during static initialization --- eventstream_rpc/tests/EventStreamClientTest.cpp | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/eventstream_rpc/tests/EventStreamClientTest.cpp b/eventstream_rpc/tests/EventStreamClientTest.cpp index 5ff119f76..15f52489c 100644 --- a/eventstream_rpc/tests/EventStreamClientTest.cpp +++ b/eventstream_rpc/tests/EventStreamClientTest.cpp @@ -32,7 +32,6 @@ struct EventStreamClientTestContext std::unique_ptr resolver; std::unique_ptr clientBootstrap; - Aws::Crt::String echoServerHostName; uint16_t echoServerPort; struct aws_string *echoServerHostNameValue; @@ -50,7 +49,7 @@ static EventStreamClientTestContext s_testContext; static bool s_isEchoserverSetup(const EventStreamClientTestContext &context) { - return !context.echoServerHostName.empty() && context.echoServerPort > 0; + return context.echoServerHostNameValue != nullptr && context.echoServerHostNameValue->len > 0 && context.echoServerPort > 0; } AWS_STATIC_STRING_FROM_LITERAL(s_env_name_echo_server_host, "AWS_TEST_EVENT_STREAM_ECHO_SERVER_HOST"); @@ -69,12 +68,8 @@ static int s_testSetup(struct aws_allocator *allocator, void *ctx) new Io::ClientBootstrap(*testContext->elGroup, *testContext->resolver, allocator)); testContext->echoServerPort = 0; - testContext->echoServerHostName = ""; - if (!aws_get_environment_value(allocator, s_env_name_echo_server_host, &testContext->echoServerHostNameValue) && testContext->echoServerHostNameValue != nullptr) - { - testContext->echoServerHostName = aws_string_c_str(testContext->echoServerHostNameValue); - } + aws_get_environment_value(allocator, s_env_name_echo_server_host, &testContext->echoServerHostNameValue); if (!aws_get_environment_value(allocator, s_env_name_echo_server_port, &testContext->echoServerPortValue) && testContext->echoServerPortValue != nullptr) { @@ -166,8 +161,9 @@ static int s_TestEventStreamConnect(struct aws_allocator *allocator, void *ctx) MessageAmendment connectionAmendment; auto messageAmender = [&](void) -> const MessageAmendment & { return connectionAmendment; }; + Aws::Crt::String hostname(aws_string_c_str(testContext->echoServerHostNameValue)); ConnectionConfig accessDeniedConfig; - accessDeniedConfig.SetHostName(testContext->echoServerHostName); + accessDeniedConfig.SetHostName(hostname); accessDeniedConfig.SetPort(testContext->echoServerPort); /* Happy path case. */ From 8f2417e42749c515479d4b9b950e5fa580640647 Mon Sep 17 00:00:00 2001 From: Bret Ambrose Date: Tue, 22 Apr 2025 13:54:29 -0700 Subject: [PATCH 12/43] Begin test rewrite; failures on other platforms should reveal other terminal status codes --- eventstream_rpc/tests/CMakeLists.txt | 13 +- .../tests/EventStreamClientTest.cpp | 346 ++++++++++++------ 2 files changed, 237 insertions(+), 122 deletions(-) diff --git a/eventstream_rpc/tests/CMakeLists.txt b/eventstream_rpc/tests/CMakeLists.txt index f048ac8e0..ddb0b2027 100644 --- a/eventstream_rpc/tests/CMakeLists.txt +++ b/eventstream_rpc/tests/CMakeLists.txt @@ -30,11 +30,16 @@ file(GLOB TESTS ${TEST_HDRS} ${TEST_SRC}) set(TEST_BINARY_NAME ${PROJECT_NAME}-tests) -add_test_case(OperateWhileDisconnected) +add_test_case(EventStreamConnectSuccess) +add_test_case(EventStreamConnectFailureNoAuthHeader) +add_test_case(EventStreamConnectFailureBadAuthHeader) +add_test_case(EchoClientConnectSuccess) + +#add_test_case(OperateWhileDisconnected) # The tests below can be commented out when an EchoRPC Server is running on 127.0.0.1:8033 -add_test_case(EventStreamConnect) -add_test_case(EchoOperation) -add_test_case(StressTestClient) +#add_test_case(EventStreamConnect) +#add_test_case(EchoOperation) +#add_test_case(StressTestClient) generate_cpp_test_driver(${TEST_BINARY_NAME}) aws_add_sanitizers(${TEST_BINARY_NAME}) target_include_directories(${TEST_BINARY_NAME} PUBLIC diff --git a/eventstream_rpc/tests/EventStreamClientTest.cpp b/eventstream_rpc/tests/EventStreamClientTest.cpp index 15f52489c..6f2bf37f3 100644 --- a/eventstream_rpc/tests/EventStreamClientTest.cpp +++ b/eventstream_rpc/tests/EventStreamClientTest.cpp @@ -18,195 +18,304 @@ #include #include #include +#include using namespace Aws::Crt; using namespace Aws::Eventstreamrpc; using namespace Awstest; +AWS_STATIC_STRING_FROM_LITERAL(s_env_name_echo_server_host, "AWS_TEST_EVENT_STREAM_ECHO_SERVER_HOST"); +AWS_STATIC_STRING_FROM_LITERAL(s_env_name_echo_server_port, "AWS_TEST_EVENT_STREAM_ECHO_SERVER_PORT"); + struct EventStreamClientTestContext { - EventStreamClientTestContext(); + explicit EventStreamClientTestContext(struct aws_allocator *allocator); + ~EventStreamClientTestContext() = default; - std::unique_ptr apiHandle; - std::unique_ptr elGroup; - std::unique_ptr resolver; - std::unique_ptr clientBootstrap; + bool isValidEnvironment() const; - uint16_t echoServerPort; + std::shared_ptr elGroup; + std::shared_ptr resolver; + std::shared_ptr clientBootstrap; - struct aws_string *echoServerHostNameValue; - struct aws_string *echoServerPortValue; + uint16_t echoServerPort; + Aws::Crt::String echoServerHost; }; -EventStreamClientTestContext::EventStreamClientTestContext() : - echoServerPort(0), - echoServerHostNameValue(nullptr), - echoServerPortValue(nullptr) +EventStreamClientTestContext::EventStreamClientTestContext(struct aws_allocator *allocator) : + echoServerPort(0) { -} + elGroup = Aws::Crt::MakeShared(allocator, 0, allocator); + resolver = + Aws::Crt::MakeShared(allocator, *elGroup, 8, 30, allocator); + clientBootstrap = Aws::Crt::MakeShared( + allocator, *elGroup, *resolver, allocator); + + aws_string *host_env_value = nullptr; + aws_get_environment_value(allocator, s_env_name_echo_server_host, &host_env_value); + if (host_env_value) + { + echoServerHost = aws_string_c_str(host_env_value); + } -static EventStreamClientTestContext s_testContext; + struct aws_string *port_env_value = nullptr; + if (!aws_get_environment_value(allocator, s_env_name_echo_server_port, &port_env_value) && port_env_value != nullptr) + { + echoServerPort = static_cast(atoi(aws_string_c_str(port_env_value))); + } + + aws_string_destroy(host_env_value); + aws_string_destroy(port_env_value); +} -static bool s_isEchoserverSetup(const EventStreamClientTestContext &context) +bool EventStreamClientTestContext::isValidEnvironment() const { - return context.echoServerHostNameValue != nullptr && context.echoServerHostNameValue->len > 0 && context.echoServerPort > 0; + return !echoServerHost.empty() && echoServerPort > 0; } -AWS_STATIC_STRING_FROM_LITERAL(s_env_name_echo_server_host, "AWS_TEST_EVENT_STREAM_ECHO_SERVER_HOST"); -AWS_STATIC_STRING_FROM_LITERAL(s_env_name_echo_server_port, "AWS_TEST_EVENT_STREAM_ECHO_SERVER_PORT"); +class TestLifecycleHandler : public ConnectionLifecycleHandler +{ + public: + TestLifecycleHandler() : + isConnected(false), + disconnectCrtErrorCode(AWS_ERROR_SUCCESS), + disconnectRpcStatusCode(EVENT_STREAM_RPC_SUCCESS) + {} + void OnConnectCallback() override + { + std::lock_guard lockGuard(semaphoreLock); -static int s_testSetup(struct aws_allocator *allocator, void *ctx) -{ - auto *testContext = static_cast(ctx); + isConnected = true; + + semaphore.notify_one(); + } - testContext->apiHandle = std::unique_ptr(new ApiHandle(allocator)); - testContext->elGroup = std::unique_ptr(new Io::EventLoopGroup(0, allocator)); - testContext->resolver = - std::unique_ptr(new Io::DefaultHostResolver(*testContext->elGroup, 8, 30, allocator)); - testContext->clientBootstrap = std::unique_ptr( - new Io::ClientBootstrap(*testContext->elGroup, *testContext->resolver, allocator)); + void OnDisconnectCallback(RpcError error) override + { + std::lock_guard lockGuard(semaphoreLock); - testContext->echoServerPort = 0; + disconnectCrtErrorCode = error.crtError; + disconnectRpcStatusCode = error.baseStatus; - aws_get_environment_value(allocator, s_env_name_echo_server_host, &testContext->echoServerHostNameValue); + semaphore.notify_one(); + } - if (!aws_get_environment_value(allocator, s_env_name_echo_server_port, &testContext->echoServerPortValue) && testContext->echoServerPortValue != nullptr) + void WaitOnCondition(std::function condition) { - testContext->echoServerPort = static_cast(atoi(aws_string_c_str(testContext->echoServerPortValue))); + std::unique_lock semaphoreULock(semaphoreLock); + semaphore.wait(semaphoreULock, condition); } - return AWS_ERROR_SUCCESS; -} + private: -static int s_testTeardown(struct aws_allocator *allocator, int setup_result, void *ctx) + std::condition_variable semaphore; + std::mutex semaphoreLock; + + bool isConnected; + int disconnectCrtErrorCode; + EventStreamRpcStatusCode disconnectRpcStatusCode; +}; + +static int s_TestEventStreamConnectSuccess(struct aws_allocator *allocator, void *ctx) { - auto *testContext = static_cast(ctx); + ApiHandle apiHandle(allocator); + EventStreamClientTestContext testContext(allocator); + if (!testContext.isValidEnvironment()) + { + printf("Environment Variables are not set for the test, skipping..."); + return AWS_OP_SKIP; + } + + { + MessageAmendment connectionAmendment; + connectionAmendment.AddHeader(EventStreamHeader( + Aws::Crt::String("client-name"), Aws::Crt::String("accepted.testy_mc_testerson"), allocator)); - testContext->elGroup.reset(); - testContext->resolver.reset(); - testContext->clientBootstrap.reset(); - /* The ApiHandle must be deallocated last or a deadlock occurs. */ - testContext->apiHandle.reset(); + ConnectionConfig connectionConfig; + connectionConfig.SetHostName(testContext.echoServerHost); + connectionConfig.SetPort(testContext.echoServerPort); + connectionConfig.SetConnectAmendment(connectionAmendment); - aws_string_destroy(testContext->echoServerHostNameValue); - testContext->echoServerHostNameValue = nullptr; + TestLifecycleHandler lifecycleHandler; + ClientConnection connection(allocator); + auto future = connection.Connect(connectionConfig, &lifecycleHandler, *testContext.clientBootstrap); + EventStreamRpcStatusCode clientStatus = future.get().baseStatus; - aws_string_destroy(testContext->echoServerPortValue); - testContext->echoServerPortValue = nullptr; + ASSERT_INT_EQUALS(EVENT_STREAM_RPC_SUCCESS, clientStatus); + } - return AWS_ERROR_SUCCESS; + return AWS_OP_SUCCESS; } -static int s_TestEventStreamConnect(struct aws_allocator *allocator, void *ctx); -static int s_TestEchoOperation(struct aws_allocator *allocator, void *ctx); -static int s_TestOperationWhileDisconnected(struct aws_allocator *allocator, void *ctx); +AWS_TEST_CASE(EventStreamConnectSuccess, s_TestEventStreamConnectSuccess); -class TestLifecycleHandler : public ConnectionLifecycleHandler +static int s_TestEventStreamConnectFailureNoAuthHeader(struct aws_allocator *allocator, void *ctx) { - public: - TestLifecycleHandler() + ApiHandle apiHandle(allocator); + EventStreamClientTestContext testContext(allocator); + if (!testContext.isValidEnvironment()) { - semaphoreULock = std::unique_lock(semaphoreLock); - isConnected = false; - lastErrorCode = AWS_OP_ERR; + printf("Environment Variables are not set for the test, skipping..."); + return AWS_OP_SKIP; } - void OnConnectCallback() override { - std::lock_guard lockGuard(semaphoreLock); + ConnectionConfig connectionConfig; + connectionConfig.SetHostName(testContext.echoServerHost); + connectionConfig.SetPort(testContext.echoServerPort); - isConnected = true; + TestLifecycleHandler lifecycleHandler; + ClientConnection connection(allocator); + auto future = connection.Connect(connectionConfig, &lifecycleHandler, *testContext.clientBootstrap); + EventStreamRpcStatusCode clientStatus = future.get().baseStatus; - semaphore.notify_one(); + // TOFIX: this isn't reliably true on Windows over TCP due to RSTs blocking final data reads + ASSERT_INT_EQUALS(EVENT_STREAM_RPC_CONNECTION_ACCESS_DENIED, clientStatus); } - void OnDisconnectCallback(RpcError error) override + return AWS_OP_SUCCESS; +} + +AWS_TEST_CASE(EventStreamConnectFailureNoAuthHeader, s_TestEventStreamConnectFailureNoAuthHeader); + +static int s_TestEventStreamConnectFailureBadAuthHeader(struct aws_allocator *allocator, void *ctx) +{ + ApiHandle apiHandle(allocator); + EventStreamClientTestContext testContext(allocator); + if (!testContext.isValidEnvironment()) { - std::lock_guard lockGuard(semaphoreLock); + printf("Environment Variables are not set for the test, skipping..."); + return AWS_OP_SKIP; + } - lastErrorCode = error.baseStatus; + { + MessageAmendment connectionAmendment; + connectionAmendment.AddHeader(EventStreamHeader( + Aws::Crt::String("client-name"), Aws::Crt::String("rejected.testy_mc_testerson"), allocator)); - semaphore.notify_one(); + ConnectionConfig connectionConfig; + connectionConfig.SetHostName(testContext.echoServerHost); + connectionConfig.SetPort(testContext.echoServerPort); + connectionConfig.SetConnectAmendment(connectionAmendment); + + TestLifecycleHandler lifecycleHandler; + ClientConnection connection(allocator); + auto future = connection.Connect(connectionConfig, &lifecycleHandler, *testContext.clientBootstrap); + EventStreamRpcStatusCode clientStatus = future.get().baseStatus; + + // TOFIX: this isn't reliably true on Windows over TCP due to RSTs blocking final data reads + ASSERT_INT_EQUALS(EVENT_STREAM_RPC_CONNECTION_ACCESS_DENIED, clientStatus); } - void WaitOnCondition(std::function condition) { semaphore.wait(semaphoreULock, condition); } + return AWS_OP_SUCCESS; +} - private: - friend int s_TestEventStreamConnect(struct aws_allocator *allocator, void *ctx); - std::condition_variable semaphore; - std::mutex semaphoreLock; - std::unique_lock semaphoreULock; - int isConnected; - int lastErrorCode; -}; +AWS_TEST_CASE(EventStreamConnectFailureBadAuthHeader, s_TestEventStreamConnectFailureBadAuthHeader); -static void s_onMessageFlush(int errorCode) +static int s_TestEchoClientConnectSuccess(struct aws_allocator *allocator, void *ctx) { - (void)errorCode; + ApiHandle apiHandle(allocator); + EventStreamClientTestContext testContext(allocator); + if (!testContext.isValidEnvironment()) + { + printf("Environment Variables are not set for the test, skipping..."); + return AWS_OP_SKIP; + } + + { + ConnectionLifecycleHandler lifecycleHandler; + Awstest::EchoTestRpcClient client(*testContext.clientBootstrap, allocator); + auto connectedStatus = client.Connect(lifecycleHandler); + EventStreamRpcStatusCode clientStatus = connectedStatus.get().baseStatus; + + ASSERT_INT_EQUALS(EVENT_STREAM_RPC_SUCCESS, clientStatus); + client.Close(); + } + + return AWS_OP_SUCCESS; } -AWS_TEST_CASE_FIXTURE(EventStreamConnect, s_testSetup, s_TestEventStreamConnect, s_testTeardown, &s_testContext); +AWS_TEST_CASE(EchoClientConnectSuccess, s_TestEchoClientConnectSuccess); + +#ifdef NEVER + static int s_TestEventStreamConnect(struct aws_allocator *allocator, void *ctx) { - auto *testContext = static_cast(ctx); + ApiHandle apiHandle(allocator); + EventStreamClientTestContext testContext; + s_testSetup(allocator, testContext); + if (!testContext.hasTestEnvironment()) { - if (!s_isEchoserverSetup(*testContext)) - { - printf("Environment Variables are not set for the test, skip the test"); - return AWS_OP_SKIP; - } + printf("Environment Variables are not set for the test, skipping..."); + return AWS_OP_SKIP; + } - MessageAmendment connectionAmendment; - auto messageAmender = [&](void) -> const MessageAmendment & { return connectionAmendment; }; + /* Happy path case. */ + { + ConnectionLifecycleHandler lifecycleHandler; + Awstest::EchoTestRpcClient client(*testContext.clientBootstrap, allocator); + auto connectedStatus = client.Connect(lifecycleHandler); + EventStreamRpcStatusCode clientStatus = connectedStatus.get().baseStatus; + + ASSERT_INT_EQUALS(EVENT_STREAM_RPC_SUCCESS, clientStatus); + client.Close(); + } - Aws::Crt::String hostname(aws_string_c_str(testContext->echoServerHostNameValue)); + /* Empty amendment headers. */ + { ConnectionConfig accessDeniedConfig; - accessDeniedConfig.SetHostName(hostname); - accessDeniedConfig.SetPort(testContext->echoServerPort); + accessDeniedConfig.SetHostName(testContext.echoServerHost); + accessDeniedConfig.SetPort(testContext.echoServerPort); - /* Happy path case. */ - { - ConnectionLifecycleHandler lifecycleHandler; - Awstest::EchoTestRpcClient client(*testContext->clientBootstrap, allocator); - auto connectedStatus = client.Connect(lifecycleHandler); - ASSERT_TRUE(connectedStatus.get().baseStatus == EVENT_STREAM_RPC_SUCCESS); - client.Close(); - } + TestLifecycleHandler lifecycleHandler; + ClientConnection connection(allocator); + auto future = connection.Connect(accessDeniedConfig, &lifecycleHandler, *testContext.clientBootstrap); + EventStreamRpcStatusCode clientStatus = future.get().baseStatus; - /* Empty amendment headers. */ - { - TestLifecycleHandler lifecycleHandler; - ClientConnection connection(allocator); - auto future = connection.Connect(accessDeniedConfig, &lifecycleHandler, *testContext->clientBootstrap); - ASSERT_TRUE(future.get().baseStatus == EVENT_STREAM_RPC_CONNECTION_ACCESS_DENIED); - } + // TOFIX: this isn't reliably true on Windows over TCP due to RSTs blocking final data reads + ASSERT_INT_EQUALS(EVENT_STREAM_RPC_CONNECTION_ACCESS_DENIED, clientStatus); + } - /* Rejected client-name header. */ - { - TestLifecycleHandler lifecycleHandler; - ClientConnection connection(allocator); - connectionAmendment.AddHeader(EventStreamHeader( - Aws::Crt::String("client-name"), Aws::Crt::String("rejected.testy_mc_testerson"), allocator)); - auto future = connection.Connect(accessDeniedConfig, &lifecycleHandler, *testContext->clientBootstrap); - ASSERT_TRUE(future.get().baseStatus == EVENT_STREAM_RPC_CONNECTION_ACCESS_DENIED); - } + /* Rejected client-name header. */ + { + ConnectionConfig accessDeniedConfig; + accessDeniedConfig.SetHostName(testContext.echoServerHost); + accessDeniedConfig.SetPort(testContext.echoServerPort); - /* Connect without taking its future then immediately close. */ - { - ConnectionLifecycleHandler lifecycleHandler; - Awstest::EchoTestRpcClient client(*testContext->clientBootstrap, allocator); - auto connectedStatus = client.Connect(lifecycleHandler); - client.Close(); - client.Close(); - ASSERT_FALSE(client.IsConnected()); - } + MessageAmendment connectionAmendment; + connectionAmendment.AddHeader(EventStreamHeader( + Aws::Crt::String("client-name"), Aws::Crt::String("rejected.testy_mc_testerson"), allocator)); + + accessDeniedConfig.SetConnectAmendment(connectionAmendment); + + TestLifecycleHandler lifecycleHandler; + ClientConnection connection(allocator); + + auto future = connection.Connect(accessDeniedConfig, &lifecycleHandler, *testContext.clientBootstrap); + EventStreamRpcStatusCode clientStatus = future.get().baseStatus; + + // TOFIX: this isn't reliably true on Windows over TCP due to RSTs blocking final data reads + ASSERT_INT_EQUALS(EVENT_STREAM_RPC_CONNECTION_ACCESS_DENIED, clientStatus); + } + + /* Connect without taking its future then immediately close. */ + { + ConnectionLifecycleHandler lifecycleHandler; + Awstest::EchoTestRpcClient client(*testContext.clientBootstrap, allocator); + auto connectedStatus = client.Connect(lifecycleHandler); + client.Close(); + client.Close(); + ASSERT_FALSE(client.IsConnected()); } return AWS_OP_SUCCESS; } +AWS_TEST_CASE(EventStreamConnect, s_TestEventStreamConnect); + + AWS_TEST_CASE_FIXTURE( OperateWhileDisconnected, s_testSetup, @@ -603,3 +712,4 @@ static int s_TestStressClient(struct aws_allocator *allocator, void *ctx) return AWS_OP_SUCCESS; } +#endif From 5c3ccfced2ac3cb51c96bd8d62e98e4bda025967 Mon Sep 17 00:00:00 2001 From: Bret Ambrose Date: Tue, 22 Apr 2025 14:09:12 -0700 Subject: [PATCH 13/43] Includes --- .../tests/EventStreamClientTest.cpp | 79 ------------------- 1 file changed, 79 deletions(-) diff --git a/eventstream_rpc/tests/EventStreamClientTest.cpp b/eventstream_rpc/tests/EventStreamClientTest.cpp index 6f2bf37f3..6a101b212 100644 --- a/eventstream_rpc/tests/EventStreamClientTest.cpp +++ b/eventstream_rpc/tests/EventStreamClientTest.cpp @@ -15,10 +15,6 @@ #endif #include -#include -#include -#include -#include using namespace Aws::Crt; using namespace Aws::Eventstreamrpc; @@ -240,81 +236,6 @@ AWS_TEST_CASE(EchoClientConnectSuccess, s_TestEchoClientConnectSuccess); #ifdef NEVER -static int s_TestEventStreamConnect(struct aws_allocator *allocator, void *ctx) -{ - ApiHandle apiHandle(allocator); - EventStreamClientTestContext testContext; - s_testSetup(allocator, testContext); - - if (!testContext.hasTestEnvironment()) - { - printf("Environment Variables are not set for the test, skipping..."); - return AWS_OP_SKIP; - } - - /* Happy path case. */ - { - ConnectionLifecycleHandler lifecycleHandler; - Awstest::EchoTestRpcClient client(*testContext.clientBootstrap, allocator); - auto connectedStatus = client.Connect(lifecycleHandler); - EventStreamRpcStatusCode clientStatus = connectedStatus.get().baseStatus; - - ASSERT_INT_EQUALS(EVENT_STREAM_RPC_SUCCESS, clientStatus); - client.Close(); - } - - /* Empty amendment headers. */ - { - ConnectionConfig accessDeniedConfig; - accessDeniedConfig.SetHostName(testContext.echoServerHost); - accessDeniedConfig.SetPort(testContext.echoServerPort); - - TestLifecycleHandler lifecycleHandler; - ClientConnection connection(allocator); - auto future = connection.Connect(accessDeniedConfig, &lifecycleHandler, *testContext.clientBootstrap); - EventStreamRpcStatusCode clientStatus = future.get().baseStatus; - - // TOFIX: this isn't reliably true on Windows over TCP due to RSTs blocking final data reads - ASSERT_INT_EQUALS(EVENT_STREAM_RPC_CONNECTION_ACCESS_DENIED, clientStatus); - } - - /* Rejected client-name header. */ - { - ConnectionConfig accessDeniedConfig; - accessDeniedConfig.SetHostName(testContext.echoServerHost); - accessDeniedConfig.SetPort(testContext.echoServerPort); - - MessageAmendment connectionAmendment; - connectionAmendment.AddHeader(EventStreamHeader( - Aws::Crt::String("client-name"), Aws::Crt::String("rejected.testy_mc_testerson"), allocator)); - - accessDeniedConfig.SetConnectAmendment(connectionAmendment); - - TestLifecycleHandler lifecycleHandler; - ClientConnection connection(allocator); - - auto future = connection.Connect(accessDeniedConfig, &lifecycleHandler, *testContext.clientBootstrap); - EventStreamRpcStatusCode clientStatus = future.get().baseStatus; - - // TOFIX: this isn't reliably true on Windows over TCP due to RSTs blocking final data reads - ASSERT_INT_EQUALS(EVENT_STREAM_RPC_CONNECTION_ACCESS_DENIED, clientStatus); - } - - /* Connect without taking its future then immediately close. */ - { - ConnectionLifecycleHandler lifecycleHandler; - Awstest::EchoTestRpcClient client(*testContext.clientBootstrap, allocator); - auto connectedStatus = client.Connect(lifecycleHandler); - client.Close(); - client.Close(); - ASSERT_FALSE(client.IsConnected()); - } - - return AWS_OP_SUCCESS; -} - -AWS_TEST_CASE(EventStreamConnect, s_TestEventStreamConnect); - AWS_TEST_CASE_FIXTURE( OperateWhileDisconnected, From a1b918aafd02f5acff6cae14e6ec345c4dd3462a Mon Sep 17 00:00:00 2001 From: Bret Ambrose Date: Tue, 22 Apr 2025 14:18:09 -0700 Subject: [PATCH 14/43] Use latest configure credentials to see if retries avoid baffling error --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index faee4db88..976dfa63b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -57,7 +57,7 @@ jobs: id-token: write # This is required for requesting the JWT steps: - name: configure AWS credentials (containers) - uses: aws-actions/configure-aws-credentials@v2 + uses: aws-actions/configure-aws-credentials@v4 with: role-to-assume: ${{ env.CI_IOT_CONTAINERS }} aws-region: ${{ env.AWS_DEFAULT_REGION }} From a571695902b4adedf7dede33abfcbcd8efa8bac2 Mon Sep 17 00:00:00 2001 From: Bret Ambrose Date: Tue, 22 Apr 2025 14:37:57 -0700 Subject: [PATCH 15/43] Widen failure possibility based on CI --- eventstream_rpc/tests/EventStreamClientTest.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/eventstream_rpc/tests/EventStreamClientTest.cpp b/eventstream_rpc/tests/EventStreamClientTest.cpp index 6a101b212..1f85fd83b 100644 --- a/eventstream_rpc/tests/EventStreamClientTest.cpp +++ b/eventstream_rpc/tests/EventStreamClientTest.cpp @@ -166,8 +166,7 @@ static int s_TestEventStreamConnectFailureNoAuthHeader(struct aws_allocator *all auto future = connection.Connect(connectionConfig, &lifecycleHandler, *testContext.clientBootstrap); EventStreamRpcStatusCode clientStatus = future.get().baseStatus; - // TOFIX: this isn't reliably true on Windows over TCP due to RSTs blocking final data reads - ASSERT_INT_EQUALS(EVENT_STREAM_RPC_CONNECTION_ACCESS_DENIED, clientStatus); + ASSERT_TRUE(clientStatus == EVENT_STREAM_RPC_CRT_ERROR || clientStatus == EVENT_STREAM_RPC_CONNECTION_ACCESS_DENIED); } return AWS_OP_SUCCESS; @@ -200,8 +199,7 @@ static int s_TestEventStreamConnectFailureBadAuthHeader(struct aws_allocator *al auto future = connection.Connect(connectionConfig, &lifecycleHandler, *testContext.clientBootstrap); EventStreamRpcStatusCode clientStatus = future.get().baseStatus; - // TOFIX: this isn't reliably true on Windows over TCP due to RSTs blocking final data reads - ASSERT_INT_EQUALS(EVENT_STREAM_RPC_CONNECTION_ACCESS_DENIED, clientStatus); + ASSERT_TRUE(clientStatus == EVENT_STREAM_RPC_CRT_ERROR || clientStatus == EVENT_STREAM_RPC_CONNECTION_ACCESS_DENIED); } return AWS_OP_SUCCESS; From 9c5dd023af1a728a85df7f6065fcc4ef4c5f5dba Mon Sep 17 00:00:00 2001 From: Bret Ambrose Date: Wed, 23 Apr 2025 09:31:37 -0700 Subject: [PATCH 16/43] Custom eventstream server step so that we can dump server-side logs for debugging on posix systems --- .builder/actions/setup-eventstream-server.py | 86 ++++++++++++++++++++ builder.json | 2 +- 2 files changed, 87 insertions(+), 1 deletion(-) create mode 100644 .builder/actions/setup-eventstream-server.py diff --git a/.builder/actions/setup-eventstream-server.py b/.builder/actions/setup-eventstream-server.py new file mode 100644 index 000000000..5f6c80342 --- /dev/null +++ b/.builder/actions/setup-eventstream-server.py @@ -0,0 +1,86 @@ +import atexit +import Builder +import os +import subprocess + +class SetupEventstreamServer(Builder.Action): + + def _build_and_run_eventstream_echo_server(self, env): + java_sdk_dir = None + + try: + env.shell.exec(["mvn", "--version"], check=True) + + # maven is installed, so this is a configuration we can start an event stream echo server + java_sdk_dir = env.shell.mktemp() + + env.shell.exec(["git", "clone", "https://github.com/aws/aws-iot-device-sdk-java-v2"], working_dir=java_sdk_dir, check=True) + + sdk_dir = os.path.join(java_sdk_dir, "aws-iot-device-sdk-java-v2", "sdk") + env.shell.pushd(sdk_dir) + + try: + # The EchoTest server is in test-only code + env.shell.exec(["mvn", "test-compile"], check=True) + + env.shell.exec(["mvn", "dependency:build-classpath", "-Dmdep.outputFile=classpath.txt"], check=True) + + with open('classpath.txt', 'r') as file: + classpath = file.read() + + test_class_path = os.path.join(sdk_dir, "target", "test-classes") + target_class_path = os.path.join(sdk_dir, "target", "classes") + directory_separator = os.pathsep + + echo_server_command = [ + "java", + "-Daws.crt.log.level=Trace", + "-Daws.crt.log.destination=File", + "-Daws.crt.log.filename=/tmp/crt.txt", + "-classpath", + f"{test_class_path}{directory_separator}{target_class_path}{directory_separator}{classpath}", + "software.amazon.awssdk.eventstreamrpc.echotest.EchoTestServiceRunner", + "127.0.0.1", + "8033"] + + print(f'Echo server command: {echo_server_command}') + + # bypass builder's exec wrapper since it doesn't allow for background execution + proc = subprocess.Popen(echo_server_command) + + @atexit.register + def _terminate_echo_server(): + proc.terminate() + proc.wait() + with open('/tmp/crt.txt', 'r') as logfile: + serverlog = logfile.read() + print("**************************************************") + print("Eventstream Server Log:\n\n") + print(serverlog) + print("\n\nEnd Log") + print("**************************************************") + os.remove("/tmp/crt.txt") + + env.shell.setenv("AWS_TEST_EVENT_STREAM_ECHO_SERVER_HOST", "127.0.0.1", quiet=False) + env.shell.setenv("AWS_TEST_EVENT_STREAM_ECHO_SERVER_PORT", "8033", quiet=False) + finally: + env.shell.popd() + + except: + print('Failed to set up event stream server. Eventstream CI tests will not be run.') + + return java_sdk_dir + + def run(self, env): + + actions = [] + java_sdk_dir = None + + try: + java_sdk_dir = self._build_and_run_eventstream_echo_server(env) + Builder.SetupCrossCICrtEnvironment().run(env) + except: + if java_sdk_dir: + env.shell.rm(java_sdk_dir) + + return Builder.Script(actions, name='setup-eventstream-server') diff --git a/builder.json b/builder.json index 4f1256ac0..c4cdf6b7c 100644 --- a/builder.json +++ b/builder.json @@ -24,7 +24,7 @@ "build-samples" ], "test_steps": [ - "sdk-ci-test-setup", + "setup-eventstream-server", "test" ], "variants" : { From 8639e715e2388cb0b429edfdb561aaf51d3f5935 Mon Sep 17 00:00:00 2001 From: Bret Ambrose Date: Wed, 23 Apr 2025 09:46:51 -0700 Subject: [PATCH 17/43] Install maven during setup --- builder.json | 3 +++ 1 file changed, 3 insertions(+) diff --git a/builder.json b/builder.json index c4cdf6b7c..8bd999ebb 100644 --- a/builder.json +++ b/builder.json @@ -1,5 +1,8 @@ { "name": "aws-iot-device-sdk-cpp-v2", + "packages": [ + "maven" + ], "!cmake_args": [ "-DPERFORM_HEADER_CHECK=OFF", "-DS2N_NO_PQ_ASM=ON" From 8085d1ae662058a29707aa078236a659da8e546d Mon Sep 17 00:00:00 2001 From: Bret Ambrose Date: Wed, 23 Apr 2025 10:28:06 -0700 Subject: [PATCH 18/43] Try to gather output that tells us whether the Java CRT is supported on this platform --- .builder/actions/setup-eventstream-server.py | 22 ++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/.builder/actions/setup-eventstream-server.py b/.builder/actions/setup-eventstream-server.py index 5f6c80342..6188b8d86 100644 --- a/.builder/actions/setup-eventstream-server.py +++ b/.builder/actions/setup-eventstream-server.py @@ -21,9 +21,9 @@ def _build_and_run_eventstream_echo_server(self, env): try: # The EchoTest server is in test-only code - env.shell.exec(["mvn", "test-compile"], check=True) + env.shell.exec(["mvn", "-q", "test-compile"], check=True) - env.shell.exec(["mvn", "dependency:build-classpath", "-Dmdep.outputFile=classpath.txt"], check=True) + env.shell.exec(["mvn", "-q", "dependency:build-classpath", "-Dmdep.outputFile=classpath.txt"], check=True) with open('classpath.txt', 'r') as file: classpath = file.read() @@ -32,6 +32,24 @@ def _build_and_run_eventstream_echo_server(self, env): target_class_path = os.path.join(sdk_dir, "target", "classes") directory_separator = os.pathsep + echo_server_probe_command = [ + "java", + "-Daws.crt.log.level=Trace", + "-Daws.crt.log.destination=File", + "-Daws.crt.log.filename=/tmp/crt.txt", + "-classpath", + f"{test_class_path}{directory_separator}{target_class_path}{directory_separator}{classpath}", + "software.amazon.awssdk.eventstreamrpc.echotest.EchoTestServiceRunner"] + + """ + Try to run the echo server in the foreground without required arguments. This always fails, but + the exception text can tell us whether or not the Java CRT is available on the platform (we have SDK CI + that runs on platforms that the Java CRT does not support). + """ + probe_output = env.shell.exec(echo_server_probe_command) + print("Probe result:\n\n") + print(probe_output) + echo_server_command = [ "java", "-Daws.crt.log.level=Trace", From cf79be3c8066ef278e7eb6d6a24673093a1a4da4 Mon Sep 17 00:00:00 2001 From: Bret Ambrose Date: Wed, 23 Apr 2025 11:23:40 -0700 Subject: [PATCH 19/43] Check stderr of eventstream run to verify platform Java CRT support --- .builder/actions/setup-eventstream-server.py | 39 ++++++++++++++++---- 1 file changed, 32 insertions(+), 7 deletions(-) diff --git a/.builder/actions/setup-eventstream-server.py b/.builder/actions/setup-eventstream-server.py index 6188b8d86..f32785f49 100644 --- a/.builder/actions/setup-eventstream-server.py +++ b/.builder/actions/setup-eventstream-server.py @@ -34,21 +34,46 @@ def _build_and_run_eventstream_echo_server(self, env): echo_server_probe_command = [ "java", - "-Daws.crt.log.level=Trace", - "-Daws.crt.log.destination=File", - "-Daws.crt.log.filename=/tmp/crt.txt", "-classpath", f"{test_class_path}{directory_separator}{target_class_path}{directory_separator}{classpath}", "software.amazon.awssdk.eventstreamrpc.echotest.EchoTestServiceRunner"] """ Try to run the echo server in the foreground without required arguments. This always fails, but - the exception text can tell us whether or not the Java CRT is available on the platform (we have SDK CI + the output can tell us whether or not the Java CRT is available on the platform (we have SDK CI that runs on platforms that the Java CRT does not support). + + If the CRT supports the platform, we fail with an out-of-index exception (referencing non-existent + command line arguments) + + If the CRT does not support the platform, we fail with + 'java.io.IOException: Unable to open library in jar for AWS CRT' """ - probe_output = env.shell.exec(echo_server_probe_command) - print("Probe result:\n\n") - print(probe_output) + prone_output = "" + probe = subprocess.Popen( + echo_server_probe_command, + stdout=subprocess.STDOUT, + stderr=subprocess.PIPE, + shell=True, + bufsize=0) # do not buffer output + with probe: + + # Convert all output to strings, which makes it much easier to both print + # and process, since all known uses of parsing output want strings anyway + line = probe.stderr.readline() + while (line): + # ignore weird characters coming back from the shell (colors, etc) + if not isinstance(line, str): + line = line.decode('ascii', 'ignore') + # We're reading in binary mode, so no automatic newline translation + if sys.platform == 'win32': + line = line.replace('\r\n', '\n') + prone_output += line + line = probe.stderr.readline() + probe.wait() + + if "java.io.IOException" in probe_output: + raise Exception("Java CRT not supported by this platform") echo_server_command = [ "java", From 440c493994bbaa5a0ce2696874673cde9d2469e2 Mon Sep 17 00:00:00 2001 From: Bret Ambrose Date: Wed, 23 Apr 2025 11:39:07 -0700 Subject: [PATCH 20/43] print stderr --- .builder/actions/setup-eventstream-server.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.builder/actions/setup-eventstream-server.py b/.builder/actions/setup-eventstream-server.py index f32785f49..3bd8548c1 100644 --- a/.builder/actions/setup-eventstream-server.py +++ b/.builder/actions/setup-eventstream-server.py @@ -72,7 +72,11 @@ def _build_and_run_eventstream_echo_server(self, env): line = probe.stderr.readline() probe.wait() + print("Probe stderr:\n\n") + print(probe_output) + if "java.io.IOException" in probe_output: + print("Skipping eventstream server unsupported platform") raise Exception("Java CRT not supported by this platform") echo_server_command = [ From e8f00fdff1ace5e94bfed9d5e95a6be17a4afc30 Mon Sep 17 00:00:00 2001 From: Bret Ambrose Date: Wed, 23 Apr 2025 11:59:56 -0700 Subject: [PATCH 21/43] Why is everything failing now? --- .builder/actions/setup-eventstream-server.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.builder/actions/setup-eventstream-server.py b/.builder/actions/setup-eventstream-server.py index 3bd8548c1..af4824137 100644 --- a/.builder/actions/setup-eventstream-server.py +++ b/.builder/actions/setup-eventstream-server.py @@ -49,7 +49,7 @@ def _build_and_run_eventstream_echo_server(self, env): If the CRT does not support the platform, we fail with 'java.io.IOException: Unable to open library in jar for AWS CRT' """ - prone_output = "" + probe_output = "" probe = subprocess.Popen( echo_server_probe_command, stdout=subprocess.STDOUT, @@ -68,7 +68,7 @@ def _build_and_run_eventstream_echo_server(self, env): # We're reading in binary mode, so no automatic newline translation if sys.platform == 'win32': line = line.replace('\r\n', '\n') - prone_output += line + probe_output += line line = probe.stderr.readline() probe.wait() @@ -113,8 +113,9 @@ def _terminate_echo_server(): finally: env.shell.popd() - except: + except Exception as ex: print('Failed to set up event stream server. Eventstream CI tests will not be run.') + print(ex) return java_sdk_dir From d80780f1f5b832aa082c72e15bc57b1c6ad42c4d Mon Sep 17 00:00:00 2001 From: Bret Ambrose Date: Wed, 23 Apr 2025 12:18:13 -0700 Subject: [PATCH 22/43] Don't route stdout to stdout --- .builder/actions/setup-eventstream-server.py | 1 - 1 file changed, 1 deletion(-) diff --git a/.builder/actions/setup-eventstream-server.py b/.builder/actions/setup-eventstream-server.py index af4824137..46a921442 100644 --- a/.builder/actions/setup-eventstream-server.py +++ b/.builder/actions/setup-eventstream-server.py @@ -52,7 +52,6 @@ def _build_and_run_eventstream_echo_server(self, env): probe_output = "" probe = subprocess.Popen( echo_server_probe_command, - stdout=subprocess.STDOUT, stderr=subprocess.PIPE, shell=True, bufsize=0) # do not buffer output From 121dc44508dc5465037ed8258ec3974c0c735919 Mon Sep 17 00:00:00 2001 From: Bret Ambrose Date: Wed, 23 Apr 2025 12:30:35 -0700 Subject: [PATCH 23/43] Missing import --- .builder/actions/setup-eventstream-server.py | 1 + 1 file changed, 1 insertion(+) diff --git a/.builder/actions/setup-eventstream-server.py b/.builder/actions/setup-eventstream-server.py index 46a921442..a16da933d 100644 --- a/.builder/actions/setup-eventstream-server.py +++ b/.builder/actions/setup-eventstream-server.py @@ -2,6 +2,7 @@ import Builder import os import subprocess +import sys class SetupEventstreamServer(Builder.Action): From 7c7f7cdcf21db1bf6899b420de5d073b9a908be6 Mon Sep 17 00:00:00 2001 From: Bret Ambrose Date: Wed, 23 Apr 2025 12:45:17 -0700 Subject: [PATCH 24/43] Probe command log --- .builder/actions/setup-eventstream-server.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.builder/actions/setup-eventstream-server.py b/.builder/actions/setup-eventstream-server.py index a16da933d..6c2e8cd14 100644 --- a/.builder/actions/setup-eventstream-server.py +++ b/.builder/actions/setup-eventstream-server.py @@ -3,6 +3,7 @@ import os import subprocess import sys +import time class SetupEventstreamServer(Builder.Action): @@ -24,8 +25,9 @@ def _build_and_run_eventstream_echo_server(self, env): # The EchoTest server is in test-only code env.shell.exec(["mvn", "-q", "test-compile"], check=True) - env.shell.exec(["mvn", "-q", "dependency:build-classpath", "-Dmdep.outputFile=classpath.txt"], check=True) + env.shell.exec(["mvn", "dependency:build-classpath", "-Dmdep.outputFile=classpath.txt"], check=True) + time.sleep(1) with open('classpath.txt', 'r') as file: classpath = file.read() @@ -39,6 +41,9 @@ def _build_and_run_eventstream_echo_server(self, env): f"{test_class_path}{directory_separator}{target_class_path}{directory_separator}{classpath}", "software.amazon.awssdk.eventstreamrpc.echotest.EchoTestServiceRunner"] + probe_command_flat = " ".join(echo_server_probe_command) + print(f'Echo probe command: {probe_command_flat}') + """ Try to run the echo server in the foreground without required arguments. This always fails, but the output can tell us whether or not the Java CRT is available on the platform (we have SDK CI From 2028eacca5bd3a5f5efdca879b68156a8d13844c Mon Sep 17 00:00:00 2001 From: Bret Ambrose Date: Wed, 23 Apr 2025 13:52:45 -0700 Subject: [PATCH 25/43] Updates --- .builder/actions/setup-eventstream-server.py | 28 +++++--------------- 1 file changed, 7 insertions(+), 21 deletions(-) diff --git a/.builder/actions/setup-eventstream-server.py b/.builder/actions/setup-eventstream-server.py index 6c2e8cd14..349826c3c 100644 --- a/.builder/actions/setup-eventstream-server.py +++ b/.builder/actions/setup-eventstream-server.py @@ -1,6 +1,7 @@ import atexit import Builder import os +import shutil import subprocess import sys import time @@ -23,8 +24,7 @@ def _build_and_run_eventstream_echo_server(self, env): try: # The EchoTest server is in test-only code - env.shell.exec(["mvn", "-q", "test-compile"], check=True) - + env.shell.exec(["mvn", "test-compile"], check=True) env.shell.exec(["mvn", "dependency:build-classpath", "-Dmdep.outputFile=classpath.txt"], check=True) time.sleep(1) @@ -58,9 +58,7 @@ def _build_and_run_eventstream_echo_server(self, env): probe_output = "" probe = subprocess.Popen( echo_server_probe_command, - stderr=subprocess.PIPE, - shell=True, - bufsize=0) # do not buffer output + stderr=subprocess.PIPE) with probe: # Convert all output to strings, which makes it much easier to both print @@ -86,9 +84,6 @@ def _build_and_run_eventstream_echo_server(self, env): echo_server_command = [ "java", - "-Daws.crt.log.level=Trace", - "-Daws.crt.log.destination=File", - "-Daws.crt.log.filename=/tmp/crt.txt", "-classpath", f"{test_class_path}{directory_separator}{target_class_path}{directory_separator}{classpath}", "software.amazon.awssdk.eventstreamrpc.echotest.EchoTestServiceRunner", @@ -104,14 +99,7 @@ def _build_and_run_eventstream_echo_server(self, env): def _terminate_echo_server(): proc.terminate() proc.wait() - with open('/tmp/crt.txt', 'r') as logfile: - serverlog = logfile.read() - print("**************************************************") - print("Eventstream Server Log:\n\n") - print(serverlog) - print("\n\nEnd Log") - print("**************************************************") - os.remove("/tmp/crt.txt") + shutil.rmtree(java_sdk_dir) env.shell.setenv("AWS_TEST_EVENT_STREAM_ECHO_SERVER_HOST", "127.0.0.1", quiet=False) env.shell.setenv("AWS_TEST_EVENT_STREAM_ECHO_SERVER_PORT", "8033", quiet=False) @@ -122,18 +110,16 @@ def _terminate_echo_server(): print('Failed to set up event stream server. Eventstream CI tests will not be run.') print(ex) - return java_sdk_dir + return def run(self, env): actions = [] - java_sdk_dir = None try: - java_sdk_dir = self._build_and_run_eventstream_echo_server(env) + self._build_and_run_eventstream_echo_server(env) Builder.SetupCrossCICrtEnvironment().run(env) except: - if java_sdk_dir: - env.shell.rm(java_sdk_dir) + pass return Builder.Script(actions, name='setup-eventstream-server') From 231e4f4eb7e7aaef2c7a6dc539d655ffe4122b96 Mon Sep 17 00:00:00 2001 From: Bret Ambrose Date: Wed, 23 Apr 2025 14:10:12 -0700 Subject: [PATCH 26/43] Umm let's not cause tsan warnings by blindly unlocking stuff --- eventstream_rpc/source/EventStreamClient.cpp | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/eventstream_rpc/source/EventStreamClient.cpp b/eventstream_rpc/source/EventStreamClient.cpp index ddf1d1c99..3a52b259b 100644 --- a/eventstream_rpc/source/EventStreamClient.cpp +++ b/eventstream_rpc/source/EventStreamClient.cpp @@ -221,22 +221,28 @@ namespace Aws ClientConnection::~ClientConnection() noexcept { m_stateMutex.lock(); - if (m_connectionWillSetup) + bool waitForSetup = m_connectionWillSetup; + m_stateMutex.unlock(); + + if (waitForSetup) { - m_stateMutex.unlock(); m_connectionSetupPromise.get_future().wait(); } + + bool waitForClosed = false; m_stateMutex.lock(); if (m_clientState != DISCONNECTED) { Close(); - m_stateMutex.unlock(); - m_closedPromise.get_future().wait(); + waitForClosed = true; } - /* Cover the case in which the if statements are not hit. */ - m_stateMutex.unlock(); m_stateMutex.unlock(); + if (waitForClosed) + { + m_closedPromise.get_future().wait(); + } + m_underlyingConnection = nullptr; } From cc7afc785e30d4f22c3578ebf1651c2cdb4351ce Mon Sep 17 00:00:00 2001 From: Bret Ambrose Date: Thu, 24 Apr 2025 13:03:14 -0700 Subject: [PATCH 27/43] Formatting fix (but don't check in format update yet) + more test work --- .github/workflows/lint.yml | 15 +- eventstream_rpc/tests/EchoTestRpcModel.cpp | 57 +++- .../tests/EventStreamClientTest.cpp | 279 +++++++++++++++--- .../tests/include/awstest/EchoTestRpcModel.h | 48 +-- format-check.py | 6 +- 5 files changed, 321 insertions(+), 84 deletions(-) diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index a0da81330..a09ac6dec 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -9,15 +9,12 @@ on: jobs: clang-format: - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 # latest steps: - - name: Checkout Sources - uses: actions/checkout@v1 + - name: Checkout Sources + uses: actions/checkout@v4 - - name: clang-format lint - uses: DoozyX/clang-format-lint-action@v0.13 - with: - # List of extensions to check - extensions: cpp,h - clangFormatVersion: 11.1.0 + - name: clang-format lint + run: | + ./format-check.py diff --git a/eventstream_rpc/tests/EchoTestRpcModel.cpp b/eventstream_rpc/tests/EchoTestRpcModel.cpp index c0737ce68..3220f85a0 100644 --- a/eventstream_rpc/tests/EchoTestRpcModel.cpp +++ b/eventstream_rpc/tests/EchoTestRpcModel.cpp @@ -36,7 +36,10 @@ namespace Awstest const char *Product::MODEL_NAME = "awstest#Product"; - Aws::Crt::String Product::GetModelName() const noexcept { return Product::MODEL_NAME; } + Aws::Crt::String Product::GetModelName() const noexcept + { + return Product::MODEL_NAME; + } Aws::Crt::ScopedResource Product::s_allocateFromPayload( Aws::Crt::StringView stringView, @@ -84,7 +87,10 @@ namespace Awstest const char *Pair::MODEL_NAME = "awstest#Pair"; - Aws::Crt::String Pair::GetModelName() const noexcept { return Pair::MODEL_NAME; } + Aws::Crt::String Pair::GetModelName() const noexcept + { + return Pair::MODEL_NAME; + } Aws::Crt::ScopedResource Pair::s_allocateFromPayload( Aws::Crt::StringView stringView, @@ -140,7 +146,10 @@ namespace Awstest const char *Customer::MODEL_NAME = "awstest#Customer"; - Aws::Crt::String Customer::GetModelName() const noexcept { return Customer::MODEL_NAME; } + Aws::Crt::String Customer::GetModelName() const noexcept + { + return Customer::MODEL_NAME; + } Aws::Crt::ScopedResource Customer::s_allocateFromPayload( Aws::Crt::StringView stringView, @@ -317,7 +326,7 @@ namespace Awstest } } - Aws::Crt::Optional MessageData::GetEnumMessage() noexcept + Aws::Crt::Optional MessageData::GetEnumMessage() const noexcept { if (!m_enumMessage.has_value()) return Aws::Crt::Optional(); @@ -343,7 +352,10 @@ namespace Awstest const char *MessageData::MODEL_NAME = "awstest#MessageData"; - Aws::Crt::String MessageData::GetModelName() const noexcept { return MessageData::MODEL_NAME; } + Aws::Crt::String MessageData::GetModelName() const noexcept + { + return MessageData::MODEL_NAME; + } Aws::Crt::ScopedResource MessageData::s_allocateFromPayload( Aws::Crt::StringView stringView, @@ -419,7 +431,10 @@ namespace Awstest const char *EchoStreamingMessage::MODEL_NAME = "awstest#EchoStreamingMessage"; - Aws::Crt::String EchoStreamingMessage::GetModelName() const noexcept { return EchoStreamingMessage::MODEL_NAME; } + Aws::Crt::String EchoStreamingMessage::GetModelName() const noexcept + { + return EchoStreamingMessage::MODEL_NAME; + } Aws::Crt::ScopedResource EchoStreamingMessage::s_allocateFromPayload( Aws::Crt::StringView stringView, @@ -468,7 +483,10 @@ namespace Awstest const char *ServiceError::MODEL_NAME = "awstest#ServiceError"; - Aws::Crt::String ServiceError::GetModelName() const noexcept { return ServiceError::MODEL_NAME; } + Aws::Crt::String ServiceError::GetModelName() const noexcept + { + return ServiceError::MODEL_NAME; + } Aws::Crt::ScopedResource ServiceError::s_allocateFromPayload( Aws::Crt::StringView stringView, @@ -566,7 +584,10 @@ namespace Awstest const char *GetAllProductsRequest::MODEL_NAME = "awstest#GetAllProductsRequest"; - Aws::Crt::String GetAllProductsRequest::GetModelName() const noexcept { return GetAllProductsRequest::MODEL_NAME; } + Aws::Crt::String GetAllProductsRequest::GetModelName() const noexcept + { + return GetAllProductsRequest::MODEL_NAME; + } Aws::Crt::ScopedResource GetAllProductsRequest::s_allocateFromPayload( Aws::Crt::StringView stringView, @@ -707,7 +728,10 @@ namespace Awstest const char *EchoStreamingResponse::MODEL_NAME = "awstest#EchoStreamingResponse"; - Aws::Crt::String EchoStreamingResponse::GetModelName() const noexcept { return EchoStreamingResponse::MODEL_NAME; } + Aws::Crt::String EchoStreamingResponse::GetModelName() const noexcept + { + return EchoStreamingResponse::MODEL_NAME; + } Aws::Crt::ScopedResource EchoStreamingResponse::s_allocateFromPayload( Aws::Crt::StringView stringView, @@ -745,7 +769,10 @@ namespace Awstest const char *EchoStreamingRequest::MODEL_NAME = "awstest#EchoStreamingRequest"; - Aws::Crt::String EchoStreamingRequest::GetModelName() const noexcept { return EchoStreamingRequest::MODEL_NAME; } + Aws::Crt::String EchoStreamingRequest::GetModelName() const noexcept + { + return EchoStreamingRequest::MODEL_NAME; + } Aws::Crt::ScopedResource EchoStreamingRequest::s_allocateFromPayload( Aws::Crt::StringView stringView, @@ -791,7 +818,10 @@ namespace Awstest const char *EchoMessageResponse::MODEL_NAME = "awstest#EchoMessageResponse"; - Aws::Crt::String EchoMessageResponse::GetModelName() const noexcept { return EchoMessageResponse::MODEL_NAME; } + Aws::Crt::String EchoMessageResponse::GetModelName() const noexcept + { + return EchoMessageResponse::MODEL_NAME; + } Aws::Crt::ScopedResource EchoMessageResponse::s_allocateFromPayload( Aws::Crt::StringView stringView, @@ -837,7 +867,10 @@ namespace Awstest const char *EchoMessageRequest::MODEL_NAME = "awstest#EchoMessageRequest"; - Aws::Crt::String EchoMessageRequest::GetModelName() const noexcept { return EchoMessageRequest::MODEL_NAME; } + Aws::Crt::String EchoMessageRequest::GetModelName() const noexcept + { + return EchoMessageRequest::MODEL_NAME; + } Aws::Crt::ScopedResource EchoMessageRequest::s_allocateFromPayload( Aws::Crt::StringView stringView, diff --git a/eventstream_rpc/tests/EventStreamClientTest.cpp b/eventstream_rpc/tests/EventStreamClientTest.cpp index 1f85fd83b..5f50258dd 100644 --- a/eventstream_rpc/tests/EventStreamClientTest.cpp +++ b/eventstream_rpc/tests/EventStreamClientTest.cpp @@ -38,14 +38,11 @@ struct EventStreamClientTestContext Aws::Crt::String echoServerHost; }; -EventStreamClientTestContext::EventStreamClientTestContext(struct aws_allocator *allocator) : - echoServerPort(0) +EventStreamClientTestContext::EventStreamClientTestContext(struct aws_allocator *allocator) : echoServerPort(0) { elGroup = Aws::Crt::MakeShared(allocator, 0, allocator); - resolver = - Aws::Crt::MakeShared(allocator, *elGroup, 8, 30, allocator); - clientBootstrap = Aws::Crt::MakeShared( - allocator, *elGroup, *resolver, allocator); + resolver = Aws::Crt::MakeShared(allocator, *elGroup, 8, 30, allocator); + clientBootstrap = Aws::Crt::MakeShared(allocator, *elGroup, *resolver, allocator); aws_string *host_env_value = nullptr; aws_get_environment_value(allocator, s_env_name_echo_server_host, &host_env_value); @@ -55,7 +52,8 @@ EventStreamClientTestContext::EventStreamClientTestContext(struct aws_allocator } struct aws_string *port_env_value = nullptr; - if (!aws_get_environment_value(allocator, s_env_name_echo_server_port, &port_env_value) && port_env_value != nullptr) + if (!aws_get_environment_value(allocator, s_env_name_echo_server_port, &port_env_value) && + port_env_value != nullptr) { echoServerPort = static_cast(atoi(aws_string_c_str(port_env_value))); } @@ -72,11 +70,11 @@ bool EventStreamClientTestContext::isValidEnvironment() const class TestLifecycleHandler : public ConnectionLifecycleHandler { public: - TestLifecycleHandler() : - isConnected(false), - disconnectCrtErrorCode(AWS_ERROR_SUCCESS), - disconnectRpcStatusCode(EVENT_STREAM_RPC_SUCCESS) - {} + TestLifecycleHandler() + : isConnected(false), disconnectCrtErrorCode(AWS_ERROR_SUCCESS), + disconnectRpcStatusCode(EVENT_STREAM_RPC_SUCCESS) + { + } void OnConnectCallback() override { @@ -104,7 +102,6 @@ class TestLifecycleHandler : public ConnectionLifecycleHandler } private: - std::condition_variable semaphore; std::mutex semaphoreLock; @@ -126,7 +123,7 @@ static int s_TestEventStreamConnectSuccess(struct aws_allocator *allocator, void { MessageAmendment connectionAmendment; connectionAmendment.AddHeader(EventStreamHeader( - Aws::Crt::String("client-name"), Aws::Crt::String("accepted.testy_mc_testerson"), allocator)); + Aws::Crt::String("client-name"), Aws::Crt::String("accepted.testy_mc_testerson"), allocator)); ConnectionConfig connectionConfig; connectionConfig.SetHostName(testContext.echoServerHost); @@ -166,7 +163,8 @@ static int s_TestEventStreamConnectFailureNoAuthHeader(struct aws_allocator *all auto future = connection.Connect(connectionConfig, &lifecycleHandler, *testContext.clientBootstrap); EventStreamRpcStatusCode clientStatus = future.get().baseStatus; - ASSERT_TRUE(clientStatus == EVENT_STREAM_RPC_CRT_ERROR || clientStatus == EVENT_STREAM_RPC_CONNECTION_ACCESS_DENIED); + ASSERT_TRUE( + clientStatus == EVENT_STREAM_RPC_CRT_ERROR || clientStatus == EVENT_STREAM_RPC_CONNECTION_ACCESS_DENIED); } return AWS_OP_SUCCESS; @@ -187,7 +185,7 @@ static int s_TestEventStreamConnectFailureBadAuthHeader(struct aws_allocator *al { MessageAmendment connectionAmendment; connectionAmendment.AddHeader(EventStreamHeader( - Aws::Crt::String("client-name"), Aws::Crt::String("rejected.testy_mc_testerson"), allocator)); + Aws::Crt::String("client-name"), Aws::Crt::String("rejected.testy_mc_testerson"), allocator)); ConnectionConfig connectionConfig; connectionConfig.SetHostName(testContext.echoServerHost); @@ -199,7 +197,8 @@ static int s_TestEventStreamConnectFailureBadAuthHeader(struct aws_allocator *al auto future = connection.Connect(connectionConfig, &lifecycleHandler, *testContext.clientBootstrap); EventStreamRpcStatusCode clientStatus = future.get().baseStatus; - ASSERT_TRUE(clientStatus == EVENT_STREAM_RPC_CRT_ERROR || clientStatus == EVENT_STREAM_RPC_CONNECTION_ACCESS_DENIED); + ASSERT_TRUE( + clientStatus == EVENT_STREAM_RPC_CRT_ERROR || clientStatus == EVENT_STREAM_RPC_CONNECTION_ACCESS_DENIED); } return AWS_OP_SUCCESS; @@ -232,54 +231,259 @@ static int s_TestEchoClientConnectSuccess(struct aws_allocator *allocator, void AWS_TEST_CASE(EchoClientConnectSuccess, s_TestEchoClientConnectSuccess); -#ifdef NEVER +static int s_TestEchoClientDoubleClose(struct aws_allocator *allocator, void *ctx) +{ + ApiHandle apiHandle(allocator); + EventStreamClientTestContext testContext(allocator); + if (!testContext.isValidEnvironment()) + { + printf("Environment Variables are not set for the test, skipping..."); + return AWS_OP_SKIP; + } + { + ConnectionLifecycleHandler lifecycleHandler; + Awstest::EchoTestRpcClient client(*testContext.clientBootstrap, allocator); + client.Close(); + client.Close(); + } -AWS_TEST_CASE_FIXTURE( - OperateWhileDisconnected, - s_testSetup, - s_TestOperationWhileDisconnected, - s_testTeardown, - &s_testContext); -static int s_TestOperationWhileDisconnected(struct aws_allocator *allocator, void *ctx) + return AWS_OP_SUCCESS; +} + +AWS_TEST_CASE(EchoClientDoubleClose, s_TestEchoClientDoubleClose); + +static int s_TestEchoClientMultiConnectSuccessFail(struct aws_allocator *allocator, void *ctx) { - auto *testContext = static_cast(ctx); + ApiHandle apiHandle(allocator); + EventStreamClientTestContext testContext(allocator); + if (!testContext.isValidEnvironment()) + { + printf("Environment Variables are not set for the test, skipping..."); + return AWS_OP_SKIP; + } + + { + ConnectionLifecycleHandler lifecycleHandler; + Awstest::EchoTestRpcClient client(*testContext.clientBootstrap, allocator); + auto connectedStatus = client.Connect(lifecycleHandler); + + auto failedStatus1 = client.Connect(lifecycleHandler); + ASSERT_INT_EQUALS(EVENT_STREAM_RPC_CONNECTION_ALREADY_ESTABLISHED, failedStatus1.get().baseStatus); + + auto failedStatus2 = client.Connect(lifecycleHandler); + ASSERT_INT_EQUALS(EVENT_STREAM_RPC_CONNECTION_ALREADY_ESTABLISHED, failedStatus2.get().baseStatus); + + EventStreamRpcStatusCode clientStatus = connectedStatus.get().baseStatus; + ASSERT_INT_EQUALS(EVENT_STREAM_RPC_SUCCESS, clientStatus); + + client.Close(); + } + + return AWS_OP_SUCCESS; +} + +AWS_TEST_CASE(EchoClientMultiConnectSuccessFail, s_TestEchoClientMultiConnectSuccessFail); + +static void s_onMessageFlush(int errorCode) +{ + (void)errorCode; +} + +#define DEFINE_CHECK_MESSAGE_DATA_MEMBER_EQUALITY_FN(TYPENAME) \ + static int s_checkMessageDataMemberEquality( \ + Aws::Crt::Optional expectedValue, Aws::Crt::Optional actualValue) \ + { \ + ASSERT_TRUE(expectedValue.has_value() == actualValue.has_value()); \ + if (expectedValue.has_value()) \ + { \ + ASSERT_TRUE(expectedValue.value() == actualValue.value()); \ + } \ + \ + return AWS_OP_SUCCESS; \ + } + +DEFINE_CHECK_MESSAGE_DATA_MEMBER_EQUALITY_FN(Aws::Crt::String) +DEFINE_CHECK_MESSAGE_DATA_MEMBER_EQUALITY_FN(bool) + +static int s_checkMessageDataEquality(const MessageData &expectedData, const MessageData &actualData) +{ + ASSERT_SUCCESS(s_checkMessageDataMemberEquality(expectedData.GetStringMessage(), actualData.GetStringMessage())); + ASSERT_SUCCESS(s_checkMessageDataMemberEquality(expectedData.GetBooleanMessage(), actualData.GetBooleanMessage())); + + return AWS_OP_SUCCESS; +} + +static int s_DoTestEchoClientOperationEchoSuccess( + struct aws_allocator *allocator, + std::function messageDataBuilder) +{ + ApiHandle apiHandle(allocator); + EventStreamClientTestContext testContext(allocator); + if (!testContext.isValidEnvironment()) + { + printf("Environment Variables are not set for the test, skipping..."); + return AWS_OP_SKIP; + } + + { + ConnectionLifecycleHandler lifecycleHandler; + Awstest::EchoTestRpcClient client(*testContext.clientBootstrap, allocator); + auto connectedStatus = client.Connect(lifecycleHandler); + ASSERT_TRUE(connectedStatus.get().baseStatus == EVENT_STREAM_RPC_SUCCESS); + + auto echoMessage = client.NewEchoMessage(); + EchoMessageRequest echoMessageRequest; + MessageData messageData; + messageDataBuilder(messageData); + echoMessageRequest.SetMessage(messageData); + + auto requestFuture = echoMessage->Activate(echoMessageRequest, s_onMessageFlush); + requestFuture.wait(); + auto result = echoMessage->GetResult().get(); + ASSERT_TRUE(result); + auto response = result.GetOperationResponse(); + ASSERT_NOT_NULL(response); + + ASSERT_SUCCESS(s_checkMessageDataEquality(messageData, response->GetMessage().value())); + } + + return AWS_OP_SUCCESS; +} + +static int s_TestEchoClientOperationEchoSuccessString(struct aws_allocator *allocator, void *ctx) +{ + return s_DoTestEchoClientOperationEchoSuccess( + allocator, + [](MessageData &messageData) + { + Aws::Crt::String value = "Hello World"; + messageData.SetStringMessage(value); + }); +} + +AWS_TEST_CASE(EchoClientOperationEchoSuccessString, s_TestEchoClientOperationEchoSuccessString); - /* Don't connect at all and try running operations as normal. */ +static int s_TestEchoClientOperationEchoSuccessMultiple(struct aws_allocator *allocator, void *ctx) +{ + ApiHandle apiHandle(allocator); + EventStreamClientTestContext testContext(allocator); + if (!testContext.isValidEnvironment()) { - if (!s_isEchoserverSetup(*testContext)) + printf("Environment Variables are not set for the test, skipping..."); + return AWS_OP_SKIP; + } + + { + ConnectionLifecycleHandler lifecycleHandler; + Awstest::EchoTestRpcClient client(*testContext.clientBootstrap, allocator); + auto connectedStatus = client.Connect(lifecycleHandler); + ASSERT_TRUE(connectedStatus.get().baseStatus == EVENT_STREAM_RPC_SUCCESS); + + for (size_t i = 0; i < 5; i++) { - printf("Environment Variables are not set for the test, skip the test"); - return AWS_OP_SKIP; + auto echoMessage = client.NewEchoMessage(); + EchoMessageRequest echoMessageRequest; + MessageData messageData; + Aws::Crt::StringStream ss; + ss << "Hello Echo #" << i + 1; + Aws::Crt::String expectedMessage(ss.str().c_str()); + messageData.SetStringMessage(expectedMessage); + echoMessageRequest.SetMessage(messageData); + + auto requestFuture = echoMessage->Activate(echoMessageRequest, s_onMessageFlush); + requestFuture.wait(); + auto result = echoMessage->GetResult().get(); + ASSERT_TRUE(result); + auto response = result.GetOperationResponse(); + ASSERT_NOT_NULL(response); + ASSERT_TRUE(response->GetMessage().value().GetStringMessage().value() == expectedMessage); } + } + + return AWS_OP_SUCCESS; +} + +AWS_TEST_CASE(EchoClientOperationEchoSuccessMultiple, s_TestEchoClientOperationEchoSuccessMultiple); +static int s_TestEchoClientOperationEchoFailureNeverConnected(struct aws_allocator *allocator, void *ctx) +{ + ApiHandle apiHandle(allocator); + EventStreamClientTestContext testContext(allocator); + if (!testContext.isValidEnvironment()) + { + printf("Environment Variables are not set for the test, skipping..."); + return AWS_OP_SKIP; + } + + { ConnectionLifecycleHandler lifecycleHandler; - Awstest::EchoTestRpcClient client(*testContext->clientBootstrap, allocator); + Awstest::EchoTestRpcClient client(*testContext.clientBootstrap, allocator); + auto echoMessage = client.NewEchoMessage(); EchoMessageRequest echoMessageRequest; MessageData messageData; Aws::Crt::String expectedMessage("l33t"); messageData.SetStringMessage(expectedMessage); echoMessageRequest.SetMessage(messageData); + auto requestFuture = echoMessage->Activate(echoMessageRequest, s_onMessageFlush); - ASSERT_TRUE(requestFuture.get().baseStatus == EVENT_STREAM_RPC_CONNECTION_CLOSED); + ASSERT_INT_EQUALS(EVENT_STREAM_RPC_CONNECTION_CLOSED, requestFuture.get().baseStatus); + auto result = echoMessage->GetOperationResult().get(); ASSERT_FALSE(result); + auto error = result.GetRpcError(); - ASSERT_TRUE(error.baseStatus == EVENT_STREAM_RPC_CONNECTION_CLOSED); + ASSERT_INT_EQUALS(EVENT_STREAM_RPC_CONNECTION_CLOSED, error.baseStatus); + } + + return AWS_OP_SUCCESS; +} + +AWS_TEST_CASE(EchoClientOperationEchoFailureNeverConnected, s_TestEchoClientOperationEchoFailureNeverConnected); + +static int s_TestEchoClientOperationEchoFailureDisconnected(struct aws_allocator *allocator, void *ctx) +{ + ApiHandle apiHandle(allocator); + EventStreamClientTestContext testContext(allocator); + if (!testContext.isValidEnvironment()) + { + printf("Environment Variables are not set for the test, skipping..."); + return AWS_OP_SKIP; } - /* Idempotent close and its safety. */ { ConnectionLifecycleHandler lifecycleHandler; - Awstest::EchoTestRpcClient client(*testContext->clientBootstrap, allocator); - client.Close(); + Awstest::EchoTestRpcClient client(*testContext.clientBootstrap, allocator); + auto connectedStatus = client.Connect(lifecycleHandler); + ASSERT_TRUE(connectedStatus.get().baseStatus == EVENT_STREAM_RPC_SUCCESS); + client.Close(); + + auto echoMessage = client.NewEchoMessage(); + EchoMessageRequest echoMessageRequest; + MessageData messageData; + Aws::Crt::String expectedMessage("l33t"); + messageData.SetStringMessage(expectedMessage); + echoMessageRequest.SetMessage(messageData); + + auto requestFuture = echoMessage->Activate(echoMessageRequest, s_onMessageFlush); + ASSERT_INT_EQUALS(EVENT_STREAM_RPC_CONNECTION_CLOSED, requestFuture.get().baseStatus); + + auto result = echoMessage->GetOperationResult().get(); + ASSERT_FALSE(result); + + auto error = result.GetRpcError(); + ASSERT_INT_EQUALS(EVENT_STREAM_RPC_CONNECTION_CLOSED, error.baseStatus); } return AWS_OP_SUCCESS; } +AWS_TEST_CASE(EchoClientOperationEchoFailureDisconnected, s_TestEchoClientOperationEchoFailureDisconnected); + +#ifdef NEVER + AWS_TEST_CASE_FIXTURE(EchoOperation, s_testSetup, s_TestEchoOperation, s_testTeardown, &s_testContext); static int s_TestEchoOperation(struct aws_allocator *allocator, void *ctx) { @@ -598,7 +802,8 @@ static int s_TestStressClient(struct aws_allocator *allocator, void *ctx) Awstest::EchoTestRpcClient client(*testContext->clientBootstrap, allocator); auto connectedStatus = client.Connect(lifecycleHandler); ASSERT_TRUE(connectedStatus.get().baseStatus == EVENT_STREAM_RPC_SUCCESS); - auto invokeOperation = [&](void) -> int { + auto invokeOperation = [&](void) -> int + { auto echoMessage = client.NewEchoMessage(); messageData.SetStringMessage(expectedMessage); echoMessageRequest.SetMessage(messageData); diff --git a/eventstream_rpc/tests/include/awstest/EchoTestRpcModel.h b/eventstream_rpc/tests/include/awstest/EchoTestRpcModel.h index b3eec4ce2..ea644a271 100644 --- a/eventstream_rpc/tests/include/awstest/EchoTestRpcModel.h +++ b/eventstream_rpc/tests/include/awstest/EchoTestRpcModel.h @@ -34,7 +34,7 @@ namespace Awstest /** * The product's name */ - Aws::Crt::Optional GetName() noexcept { return m_name; } + Aws::Crt::Optional GetName() const noexcept { return m_name; } /** * How much the product costs */ @@ -42,7 +42,7 @@ namespace Awstest /** * How much the product costs */ - Aws::Crt::Optional GetPrice() noexcept { return m_price; } + Aws::Crt::Optional GetPrice() const noexcept { return m_price; } void SerializeToJsonObject(Aws::Crt::JsonObject &payloadObject) const noexcept override; static void s_loadFromJsonView(Product &, const Aws::Crt::JsonView &) noexcept; static Aws::Crt::ScopedResource s_allocateFromPayload( @@ -76,7 +76,7 @@ namespace Awstest /** * Pair.key as a string */ - Aws::Crt::Optional GetKey() noexcept { return m_key; } + Aws::Crt::Optional GetKey() const noexcept { return m_key; } /** * Pair.value also a string! */ @@ -84,7 +84,7 @@ namespace Awstest /** * Pair.value also a string! */ - Aws::Crt::Optional GetValue() noexcept { return m_value; } + Aws::Crt::Optional GetValue() const noexcept { return m_value; } void SerializeToJsonObject(Aws::Crt::JsonObject &payloadObject) const noexcept override; static void s_loadFromJsonView(Pair &, const Aws::Crt::JsonView &) noexcept; static Aws::Crt::ScopedResource s_allocateFromPayload( @@ -126,7 +126,7 @@ namespace Awstest /** * Opaque customer identifier */ - Aws::Crt::Optional GetId() noexcept { return m_id; } + Aws::Crt::Optional GetId() const noexcept { return m_id; } /** * First name of the customer */ @@ -134,7 +134,7 @@ namespace Awstest /** * First name of the customer */ - Aws::Crt::Optional GetFirstName() noexcept { return m_firstName; } + Aws::Crt::Optional GetFirstName() const noexcept { return m_firstName; } /** * Last name of the customer */ @@ -142,7 +142,7 @@ namespace Awstest /** * Last name of the customer */ - Aws::Crt::Optional GetLastName() noexcept { return m_lastName; } + Aws::Crt::Optional GetLastName() const noexcept { return m_lastName; } void SerializeToJsonObject(Aws::Crt::JsonObject &payloadObject) const noexcept override; static void s_loadFromJsonView(Customer &, const Aws::Crt::JsonView &) noexcept; static Aws::Crt::ScopedResource s_allocateFromPayload( @@ -177,7 +177,7 @@ namespace Awstest /** * Some string data */ - Aws::Crt::Optional GetStringMessage() noexcept { return m_stringMessage; } + Aws::Crt::Optional GetStringMessage() const noexcept { return m_stringMessage; } /** * Some boolean data */ @@ -185,7 +185,7 @@ namespace Awstest /** * Some boolean data */ - Aws::Crt::Optional GetBooleanMessage() noexcept { return m_booleanMessage; } + Aws::Crt::Optional GetBooleanMessage() const noexcept { return m_booleanMessage; } /** * Some timestamp data */ @@ -193,7 +193,7 @@ namespace Awstest /** * Some timestamp data */ - Aws::Crt::Optional GetTimeMessage() noexcept { return m_timeMessage; } + Aws::Crt::Optional GetTimeMessage() const noexcept { return m_timeMessage; } /** * Some document data */ @@ -204,7 +204,7 @@ namespace Awstest /** * Some document data */ - Aws::Crt::Optional GetDocumentMessage() noexcept { return m_documentMessage; } + Aws::Crt::Optional GetDocumentMessage() const noexcept { return m_documentMessage; } /** * Some FruitEnum data */ @@ -212,7 +212,7 @@ namespace Awstest /** * Some FruitEnum data */ - Aws::Crt::Optional GetEnumMessage() noexcept; + Aws::Crt::Optional GetEnumMessage() const noexcept; /** * Some blob data */ @@ -220,7 +220,7 @@ namespace Awstest /** * Some blob data */ - Aws::Crt::Optional> GetBlobMessage() noexcept { return m_blobMessage; } + Aws::Crt::Optional> GetBlobMessage() const noexcept { return m_blobMessage; } /** * Some list of strings data */ @@ -231,7 +231,7 @@ namespace Awstest /** * Some list of strings data */ - Aws::Crt::Optional> GetStringListMessage() noexcept + Aws::Crt::Optional> GetStringListMessage() const noexcept { return m_stringListMessage; } @@ -245,7 +245,7 @@ namespace Awstest /** * A list of key-value pairs */ - Aws::Crt::Optional> GetKeyValuePairList() noexcept { return m_keyValuePairList; } + Aws::Crt::Optional> GetKeyValuePairList() const noexcept { return m_keyValuePairList; } /** * A map from strings to Product shapes */ @@ -256,7 +256,7 @@ namespace Awstest /** * A map from strings to Product shapes */ - Aws::Crt::Optional> GetStringToValue() noexcept + Aws::Crt::Optional> GetStringToValue() const noexcept { return m_stringToValue; } @@ -305,7 +305,7 @@ namespace Awstest /** * A message data record */ - Aws::Crt::Optional GetStreamMessage() noexcept + Aws::Crt::Optional GetStreamMessage() const noexcept { if (m_chosenMember == TAG_STREAM_MESSAGE) { @@ -327,7 +327,7 @@ namespace Awstest /** * A key value pair */ - Aws::Crt::Optional GetKeyValuePair() noexcept + Aws::Crt::Optional GetKeyValuePair() const noexcept { if (m_chosenMember == TAG_KEY_VALUE_PAIR) { @@ -376,7 +376,7 @@ namespace Awstest /** * An error message */ - Aws::Crt::Optional GetMessage() noexcept override { return m_message; } + Aws::Crt::Optional GetMessage() const noexcept override { return m_message; } /** * Some auxiliary value */ @@ -384,7 +384,7 @@ namespace Awstest /** * Some auxiliary value */ - Aws::Crt::Optional GetValue() noexcept { return m_value; } + Aws::Crt::Optional GetValue() const noexcept { return m_value; } void SerializeToJsonObject(Aws::Crt::JsonObject &payloadObject) const noexcept override; static void s_loadFromJsonView(ServiceError &, const Aws::Crt::JsonView &) noexcept; static Aws::Crt::ScopedResource s_allocateFromPayload( @@ -418,7 +418,7 @@ namespace Awstest /** * A map from strings to products */ - Aws::Crt::Optional> GetProducts() noexcept { return m_products; } + Aws::Crt::Optional> GetProducts() const noexcept { return m_products; } void SerializeToJsonObject(Aws::Crt::JsonObject &payloadObject) const noexcept override; static void s_loadFromJsonView(GetAllProductsResponse &, const Aws::Crt::JsonView &) noexcept; static Aws::Crt::ScopedResource s_allocateFromPayload( @@ -475,7 +475,7 @@ namespace Awstest /** * A list of all known customers */ - Aws::Crt::Optional> GetCustomers() noexcept { return m_customers; } + Aws::Crt::Optional> GetCustomers() const noexcept { return m_customers; } void SerializeToJsonObject(Aws::Crt::JsonObject &payloadObject) const noexcept override; static void s_loadFromJsonView(GetAllCustomersResponse &, const Aws::Crt::JsonView &) noexcept; static Aws::Crt::ScopedResource s_allocateFromPayload( @@ -580,7 +580,7 @@ namespace Awstest /** * Some message data */ - Aws::Crt::Optional GetMessage() noexcept { return m_message; } + Aws::Crt::Optional GetMessage() const noexcept { return m_message; } void SerializeToJsonObject(Aws::Crt::JsonObject &payloadObject) const noexcept override; static void s_loadFromJsonView(EchoMessageResponse &, const Aws::Crt::JsonView &) noexcept; static Aws::Crt::ScopedResource s_allocateFromPayload( @@ -613,7 +613,7 @@ namespace Awstest /** * Some message data */ - Aws::Crt::Optional GetMessage() noexcept { return m_message; } + Aws::Crt::Optional GetMessage() const noexcept { return m_message; } void SerializeToJsonObject(Aws::Crt::JsonObject &payloadObject) const noexcept override; static void s_loadFromJsonView(EchoMessageRequest &, const Aws::Crt::JsonView &) noexcept; static Aws::Crt::ScopedResource s_allocateFromPayload( diff --git a/format-check.py b/format-check.py index b2678b8a4..8dcc21958 100755 --- a/format-check.py +++ b/format-check.py @@ -20,9 +20,10 @@ check_dirs = ['deviceadvisor', 'devicedefender', 'discovery', 'eventstream_rpc', 'greengrass_ipc', 'identity', 'iotdevicecommon', 'jobs', 'shadow', 'samples', 'secure_tunneling'] +filepaths_file = NamedTemporaryFile(delete=False) for check_dir in check_dirs: # create file containing list of all files to format - filepaths_file = NamedTemporaryFile(delete=False) + for dirpath, dirnames, filenames in os.walk(check_dir): for filename in filenames: @@ -35,7 +36,8 @@ continue filepaths_file.write(f"{filepath}\n".encode()) - filepaths_file.close() + +filepaths_file.close() # use pipx to run clang-format from PyPI # this is a simple way to run the same clang-format version regardless of OS From cd9289ccca937b9c54d166e4dd80d9af3d1c3664 Mon Sep 17 00:00:00 2001 From: Bret Ambrose Date: Thu, 24 Apr 2025 13:29:13 -0700 Subject: [PATCH 28/43] Formatting and codegen updates --- .../aws/eventstreamrpc/EventStreamClient.h | 6 +- eventstream_rpc/source/EventStreamClient.cpp | 45 +- eventstream_rpc/tests/CMakeLists.txt | 14 +- eventstream_rpc/tests/EchoTestRpcClient.cpp | 15 +- .../aws/greengrass/GreengrassCoreIpcModel.h | 405 ++++++++++-------- .../source/GreengrassCoreIpcClient.cpp | 15 +- .../source/GreengrassCoreIpcModel.cpp | 166 ++++--- 7 files changed, 412 insertions(+), 254 deletions(-) diff --git a/eventstream_rpc/include/aws/eventstreamrpc/EventStreamClient.h b/eventstream_rpc/include/aws/eventstreamrpc/EventStreamClient.h index 706b94fdf..ccd464eb9 100644 --- a/eventstream_rpc/include/aws/eventstreamrpc/EventStreamClient.h +++ b/eventstream_rpc/include/aws/eventstreamrpc/EventStreamClient.h @@ -127,9 +127,9 @@ namespace Aws void PrependHeaders(Crt::List &&headers); void SetPayload(const Crt::Optional &payload) noexcept; void SetPayload(Crt::Optional &&payload); - const Crt::List &GetHeaders() const &noexcept; + const Crt::List &GetHeaders() const & noexcept; Crt::List &&GetHeaders() &&; - const Crt::Optional &GetPayload() const &noexcept; + const Crt::Optional &GetPayload() const & noexcept; Crt::Optional &&GetPayload() &&; private: @@ -408,7 +408,7 @@ namespace Aws explicit OperationError() noexcept = default; static void s_customDeleter(OperationError *shape) noexcept; virtual void SerializeToJsonObject(Crt::JsonObject &payloadObject) const override; - virtual Crt::Optional GetMessage() noexcept = 0; + virtual Crt::Optional GetMessage() const noexcept = 0; }; /** diff --git a/eventstream_rpc/source/EventStreamClient.cpp b/eventstream_rpc/source/EventStreamClient.cpp index 3a52b259b..65983178e 100644 --- a/eventstream_rpc/source/EventStreamClient.cpp +++ b/eventstream_rpc/source/EventStreamClient.cpp @@ -113,13 +113,25 @@ namespace Aws return *this; } - const Crt::List &MessageAmendment::GetHeaders() const &noexcept { return m_headers; } + const Crt::List &MessageAmendment::GetHeaders() const & noexcept + { + return m_headers; + } - Crt::List &&MessageAmendment::GetHeaders() && { return std::move(m_headers); } + Crt::List &&MessageAmendment::GetHeaders() && + { + return std::move(m_headers); + } - const Crt::Optional &MessageAmendment::GetPayload() const &noexcept { return m_payload; } + const Crt::Optional &MessageAmendment::GetPayload() const & noexcept + { + return m_payload; + } - Crt::Optional &&MessageAmendment::GetPayload() && { return std::move(m_payload); } + Crt::Optional &&MessageAmendment::GetPayload() && + { + return std::move(m_payload); + } void MessageAmendment::AddHeader(EventStreamHeader &&header) noexcept { @@ -139,7 +151,10 @@ namespace Aws } } - void MessageAmendment::SetPayload(Crt::Optional &&payload) { m_payload = std::move(payload); } + void MessageAmendment::SetPayload(Crt::Optional &&payload) + { + m_payload = std::move(payload); + } MessageAmendment::~MessageAmendment() noexcept { @@ -263,7 +278,10 @@ namespace Aws void ConnectionLifecycleHandler::OnConnectCallback() {} - void ConnectionLifecycleHandler::OnDisconnectCallback(RpcError error) { (void)error; } + void ConnectionLifecycleHandler::OnDisconnectCallback(RpcError error) + { + (void)error; + } Crt::String RpcError::StatusToString() { @@ -1129,7 +1147,10 @@ namespace Aws { } - void OperationError::SerializeToJsonObject(Crt::JsonObject &payloadObject) const { (void)payloadObject; } + void OperationError::SerializeToJsonObject(Crt::JsonObject &payloadObject) const + { + (void)payloadObject; + } AbstractShapeBase::AbstractShapeBase() noexcept : m_allocator(nullptr) {} @@ -1217,7 +1238,10 @@ namespace Aws rhs.m_rpcError = {EVENT_STREAM_RPC_UNINITIALIZED, 0}; } - TaggedResult::operator bool() const noexcept { return m_responseType == OPERATION_RESPONSE; } + TaggedResult::operator bool() const noexcept + { + return m_responseType == OPERATION_RESPONSE; + } AbstractShapeBase *TaggedResult::GetOperationResponse() const noexcept { @@ -1546,7 +1570,10 @@ namespace Aws } } - void ClientOperation::WithLaunchMode(std::launch mode) noexcept { m_asyncLaunchMode = mode; } + void ClientOperation::WithLaunchMode(std::launch mode) noexcept + { + m_asyncLaunchMode = mode; + } std::future ClientOperation::Close(OnMessageFlushCallback onMessageFlushCallback) noexcept { diff --git a/eventstream_rpc/tests/CMakeLists.txt b/eventstream_rpc/tests/CMakeLists.txt index ddb0b2027..456a3368d 100644 --- a/eventstream_rpc/tests/CMakeLists.txt +++ b/eventstream_rpc/tests/CMakeLists.txt @@ -33,12 +33,18 @@ set(TEST_BINARY_NAME ${PROJECT_NAME}-tests) add_test_case(EventStreamConnectSuccess) add_test_case(EventStreamConnectFailureNoAuthHeader) add_test_case(EventStreamConnectFailureBadAuthHeader) + add_test_case(EchoClientConnectSuccess) +add_test_case(EchoClientDoubleClose) +add_test_case(EchoClientMultiConnectSuccessFail) + +add_test_case(EchoClientOperationEchoSuccessString) +add_test_case(EchoClientOperationEchoSuccessMultiple) +add_test_case(EchoClientOperationEchoFailureNeverConnected) +add_test_case(EchoClientOperationEchoFailureDisconnected) + + -#add_test_case(OperateWhileDisconnected) -# The tests below can be commented out when an EchoRPC Server is running on 127.0.0.1:8033 -#add_test_case(EventStreamConnect) -#add_test_case(EchoOperation) #add_test_case(StressTestClient) generate_cpp_test_driver(${TEST_BINARY_NAME}) aws_add_sanitizers(${TEST_BINARY_NAME}) diff --git a/eventstream_rpc/tests/EchoTestRpcClient.cpp b/eventstream_rpc/tests/EchoTestRpcClient.cpp index 508e13fcb..05fe07477 100644 --- a/eventstream_rpc/tests/EchoTestRpcClient.cpp +++ b/eventstream_rpc/tests/EchoTestRpcClient.cpp @@ -28,11 +28,20 @@ namespace Awstest return m_connection.Connect(connectionConfig, &lifecycleHandler, m_clientBootstrap); } - void EchoTestRpcClient::Close() noexcept { m_connection.Close(); } + void EchoTestRpcClient::Close() noexcept + { + m_connection.Close(); + } - void EchoTestRpcClient::WithLaunchMode(std::launch mode) noexcept { m_asyncLaunchMode = mode; } + void EchoTestRpcClient::WithLaunchMode(std::launch mode) noexcept + { + m_asyncLaunchMode = mode; + } - EchoTestRpcClient::~EchoTestRpcClient() noexcept { Close(); } + EchoTestRpcClient::~EchoTestRpcClient() noexcept + { + Close(); + } std::shared_ptr EchoTestRpcClient::NewGetAllProducts() noexcept { diff --git a/greengrass_ipc/include/aws/greengrass/GreengrassCoreIpcModel.h b/greengrass_ipc/include/aws/greengrass/GreengrassCoreIpcModel.h index 652da3f4a..5884b7172 100644 --- a/greengrass_ipc/include/aws/greengrass/GreengrassCoreIpcModel.h +++ b/greengrass_ipc/include/aws/greengrass/GreengrassCoreIpcModel.h @@ -29,11 +29,11 @@ namespace Aws void SetKey(const Aws::Crt::String &key) noexcept { m_key = key; } - Aws::Crt::Optional GetKey() noexcept { return m_key; } + Aws::Crt::Optional GetKey() const noexcept { return m_key; } void SetValue(const Aws::Crt::String &value) noexcept { m_value = value; } - Aws::Crt::Optional GetValue() noexcept { return m_value; } + Aws::Crt::Optional GetValue() const noexcept { return m_value; } void SerializeToJsonObject(Aws::Crt::JsonObject &payloadObject) const noexcept override; static void s_loadFromJsonView(UserProperty &, const Aws::Crt::JsonView &) noexcept; static Aws::Crt::ScopedResource s_allocateFromPayload( @@ -77,7 +77,7 @@ namespace Aws /** * The topic where the message was published. */ - Aws::Crt::Optional GetTopic() noexcept { return m_topic; } + Aws::Crt::Optional GetTopic() const noexcept { return m_topic; } void SerializeToJsonObject(Aws::Crt::JsonObject &payloadObject) const noexcept override; static void s_loadFromJsonView(MessageContext &, const Aws::Crt::JsonView &) noexcept; static Aws::Crt::ScopedResource s_allocateFromPayload( @@ -123,7 +123,7 @@ namespace Aws /** * The detailed deployment status of the local deployment. */ - Aws::Crt::Optional GetDetailedDeploymentStatus() noexcept; + Aws::Crt::Optional GetDetailedDeploymentStatus() const noexcept; /** * (Optional) The list of local deployment errors */ @@ -134,7 +134,7 @@ namespace Aws /** * (Optional) The list of local deployment errors */ - Aws::Crt::Optional> GetDeploymentErrorStack() noexcept + Aws::Crt::Optional> GetDeploymentErrorStack() const noexcept { return m_deploymentErrorStack; } @@ -148,7 +148,7 @@ namespace Aws /** * (Optional) The list of local deployment error types */ - Aws::Crt::Optional> GetDeploymentErrorTypes() noexcept + Aws::Crt::Optional> GetDeploymentErrorTypes() const noexcept { return m_deploymentErrorTypes; } @@ -162,7 +162,7 @@ namespace Aws /** * (Optional) The cause of local deployment failure */ - Aws::Crt::Optional GetDeploymentFailureCause() noexcept + Aws::Crt::Optional GetDeploymentFailureCause() const noexcept { return m_deploymentFailureCause; } @@ -221,7 +221,7 @@ namespace Aws * (Optional) The maximum amount of RAM (in kilobytes) that this component's processes can use on the core * device. */ - Aws::Crt::Optional GetMemory() noexcept { return m_memory; } + Aws::Crt::Optional GetMemory() const noexcept { return m_memory; } /** * (Optional) The maximum amount of CPU time that this component's processes can use on the core device. */ @@ -229,7 +229,7 @@ namespace Aws /** * (Optional) The maximum amount of CPU time that this component's processes can use on the core device. */ - Aws::Crt::Optional GetCpus() noexcept { return m_cpus; } + Aws::Crt::Optional GetCpus() const noexcept { return m_cpus; } void SerializeToJsonObject(Aws::Crt::JsonObject &payloadObject) const noexcept override; static void s_loadFromJsonView(SystemResourceLimits &, const Aws::Crt::JsonView &) noexcept; static Aws::Crt::ScopedResource s_allocateFromPayload( @@ -263,7 +263,7 @@ namespace Aws /** * The object that contains the new configuration. */ - Aws::Crt::Optional GetConfiguration() noexcept { return m_configuration; } + Aws::Crt::Optional GetConfiguration() const noexcept { return m_configuration; } /** * The ID of the AWS IoT Greengrass deployment that updates the component. */ @@ -271,7 +271,7 @@ namespace Aws /** * The ID of the AWS IoT Greengrass deployment that updates the component. */ - Aws::Crt::Optional GetDeploymentId() noexcept { return m_deploymentId; } + Aws::Crt::Optional GetDeploymentId() const noexcept { return m_deploymentId; } void SerializeToJsonObject(Aws::Crt::JsonObject &payloadObject) const noexcept override; static void s_loadFromJsonView(ValidateConfigurationUpdateEvent &, const Aws::Crt::JsonView &) noexcept; static Aws::Crt::ScopedResource s_allocateFromPayload( @@ -302,7 +302,7 @@ namespace Aws /** * The binary message as a blob. */ - Aws::Crt::Optional> GetMessage() noexcept { return m_message; } + Aws::Crt::Optional> GetMessage() const noexcept { return m_message; } /** * The context of the message, such as the topic where the message was published. */ @@ -310,7 +310,7 @@ namespace Aws /** * The context of the message, such as the topic where the message was published. */ - Aws::Crt::Optional GetContext() noexcept { return m_context; } + Aws::Crt::Optional GetContext() const noexcept { return m_context; } void SerializeToJsonObject(Aws::Crt::JsonObject &payloadObject) const noexcept override; static void s_loadFromJsonView(BinaryMessage &, const Aws::Crt::JsonView &) noexcept; static Aws::Crt::ScopedResource s_allocateFromPayload( @@ -341,7 +341,7 @@ namespace Aws /** * The JSON message as an object. */ - Aws::Crt::Optional GetMessage() noexcept { return m_message; } + Aws::Crt::Optional GetMessage() const noexcept { return m_message; } /** * The context of the message, such as the topic where the message was published. */ @@ -349,7 +349,7 @@ namespace Aws /** * The context of the message, such as the topic where the message was published. */ - Aws::Crt::Optional GetContext() noexcept { return m_context; } + Aws::Crt::Optional GetContext() const noexcept { return m_context; } void SerializeToJsonObject(Aws::Crt::JsonObject &payloadObject) const noexcept override; static void s_loadFromJsonView(JsonMessage &, const Aws::Crt::JsonView &) noexcept; static Aws::Crt::ScopedResource s_allocateFromPayload( @@ -380,7 +380,7 @@ namespace Aws /** * The topic to which the message was published. */ - Aws::Crt::Optional GetTopicName() noexcept { return m_topicName; } + Aws::Crt::Optional GetTopicName() const noexcept { return m_topicName; } /** * (Optional) The message payload as a blob. */ @@ -388,7 +388,7 @@ namespace Aws /** * (Optional) The message payload as a blob. */ - Aws::Crt::Optional> GetPayload() noexcept { return m_payload; } + Aws::Crt::Optional> GetPayload() const noexcept { return m_payload; } /** * (Optional) The value of the retain flag. */ @@ -396,7 +396,7 @@ namespace Aws /** * (Optional) The value of the retain flag. */ - Aws::Crt::Optional GetRetain() noexcept { return m_retain; } + Aws::Crt::Optional GetRetain() const noexcept { return m_retain; } /** * (Optional) MQTT user properties associated with the message. */ @@ -407,7 +407,10 @@ namespace Aws /** * (Optional) MQTT user properties associated with the message. */ - Aws::Crt::Optional> GetUserProperties() noexcept { return m_userProperties; } + Aws::Crt::Optional> GetUserProperties() const noexcept + { + return m_userProperties; + } /** * (Optional) Message expiry interval in seconds. */ @@ -418,7 +421,7 @@ namespace Aws /** * (Optional) Message expiry interval in seconds. */ - Aws::Crt::Optional GetMessageExpiryIntervalSeconds() noexcept + Aws::Crt::Optional GetMessageExpiryIntervalSeconds() const noexcept { return m_messageExpiryIntervalSeconds; } @@ -432,7 +435,10 @@ namespace Aws /** * (Optional) Correlation data blob for request/response. */ - Aws::Crt::Optional> GetCorrelationData() noexcept { return m_correlationData; } + Aws::Crt::Optional> GetCorrelationData() const noexcept + { + return m_correlationData; + } /** * (Optional) Response topic for request/response. */ @@ -440,7 +446,7 @@ namespace Aws /** * (Optional) Response topic for request/response. */ - Aws::Crt::Optional GetResponseTopic() noexcept { return m_responseTopic; } + Aws::Crt::Optional GetResponseTopic() const noexcept { return m_responseTopic; } /** * (Optional) Message payload format. */ @@ -448,7 +454,7 @@ namespace Aws /** * (Optional) Message payload format. */ - Aws::Crt::Optional GetPayloadFormat() noexcept; + Aws::Crt::Optional GetPayloadFormat() const noexcept; /** * (Optional) Message content type. */ @@ -456,7 +462,7 @@ namespace Aws /** * (Optional) Message content type. */ - Aws::Crt::Optional GetContentType() noexcept { return m_contentType; } + Aws::Crt::Optional GetContentType() const noexcept { return m_contentType; } void SerializeToJsonObject(Aws::Crt::JsonObject &payloadObject) const noexcept override; static void s_loadFromJsonView(MQTTMessage &, const Aws::Crt::JsonView &) noexcept; static Aws::Crt::ScopedResource s_allocateFromPayload( @@ -494,7 +500,7 @@ namespace Aws /** * The name of the component. */ - Aws::Crt::Optional GetComponentName() noexcept { return m_componentName; } + Aws::Crt::Optional GetComponentName() const noexcept { return m_componentName; } /** * The key path to the configuration value that updated. */ @@ -502,7 +508,7 @@ namespace Aws /** * The key path to the configuration value that updated. */ - Aws::Crt::Optional> GetKeyPath() noexcept { return m_keyPath; } + Aws::Crt::Optional> GetKeyPath() const noexcept { return m_keyPath; } void SerializeToJsonObject(Aws::Crt::JsonObject &payloadObject) const noexcept override; static void s_loadFromJsonView(ConfigurationUpdateEvent &, const Aws::Crt::JsonView &) noexcept; static Aws::Crt::ScopedResource s_allocateFromPayload( @@ -533,7 +539,7 @@ namespace Aws /** * The ID of the AWS IoT Greengrass deployment that updated the component. */ - Aws::Crt::Optional GetDeploymentId() noexcept { return m_deploymentId; } + Aws::Crt::Optional GetDeploymentId() const noexcept { return m_deploymentId; } void SerializeToJsonObject(Aws::Crt::JsonObject &payloadObject) const noexcept override; static void s_loadFromJsonView(PostComponentUpdateEvent &, const Aws::Crt::JsonView &) noexcept; static Aws::Crt::ScopedResource s_allocateFromPayload( @@ -563,7 +569,7 @@ namespace Aws /** * The ID of the AWS IoT Greengrass deployment that updates the component. */ - Aws::Crt::Optional GetDeploymentId() noexcept { return m_deploymentId; } + Aws::Crt::Optional GetDeploymentId() const noexcept { return m_deploymentId; } /** * Whether or not Greengrass needs to restart to apply the update. */ @@ -571,7 +577,7 @@ namespace Aws /** * Whether or not Greengrass needs to restart to apply the update. */ - Aws::Crt::Optional GetIsGgcRestarting() noexcept { return m_isGgcRestarting; } + Aws::Crt::Optional GetIsGgcRestarting() const noexcept { return m_isGgcRestarting; } void SerializeToJsonObject(Aws::Crt::JsonObject &payloadObject) const noexcept override; static void s_loadFromJsonView(PreComponentUpdateEvent &, const Aws::Crt::JsonView &) noexcept; static Aws::Crt::ScopedResource s_allocateFromPayload( @@ -602,7 +608,7 @@ namespace Aws /** * The private key in pem format. */ - Aws::Crt::Optional GetPrivateKey() noexcept { return m_privateKey; } + Aws::Crt::Optional GetPrivateKey() const noexcept { return m_privateKey; } /** * The public key in pem format. */ @@ -610,7 +616,7 @@ namespace Aws /** * The public key in pem format. */ - Aws::Crt::Optional GetPublicKey() noexcept { return m_publicKey; } + Aws::Crt::Optional GetPublicKey() const noexcept { return m_publicKey; } /** * The certificate in pem format. */ @@ -618,7 +624,7 @@ namespace Aws /** * The certificate in pem format. */ - Aws::Crt::Optional GetCertificate() noexcept { return m_certificate; } + Aws::Crt::Optional GetCertificate() const noexcept { return m_certificate; } /** * List of CA certificates in pem format. */ @@ -629,7 +635,7 @@ namespace Aws /** * List of CA certificates in pem format. */ - Aws::Crt::Optional> GetCaCertificates() noexcept + Aws::Crt::Optional> GetCaCertificates() const noexcept { return m_caCertificates; } @@ -672,15 +678,15 @@ namespace Aws void SetName(const Aws::Crt::String &name) noexcept { m_name = name; } - Aws::Crt::Optional GetName() noexcept { return m_name; } + Aws::Crt::Optional GetName() const noexcept { return m_name; } void SetUnit(MetricUnitType unit) noexcept; - Aws::Crt::Optional GetUnit() noexcept; + Aws::Crt::Optional GetUnit() const noexcept; void SetValue(const double &value) noexcept { m_value = value; } - Aws::Crt::Optional GetValue() noexcept { return m_value; } + Aws::Crt::Optional GetValue() const noexcept { return m_value; } void SerializeToJsonObject(Aws::Crt::JsonObject &payloadObject) const noexcept override; static void s_loadFromJsonView(Metric &, const Aws::Crt::JsonView &) noexcept; static Aws::Crt::ScopedResource s_allocateFromPayload( @@ -712,7 +718,7 @@ namespace Aws /** * The ID of the local deployment. */ - Aws::Crt::Optional GetDeploymentId() noexcept { return m_deploymentId; } + Aws::Crt::Optional GetDeploymentId() const noexcept { return m_deploymentId; } /** * The status of the local deployment. */ @@ -720,7 +726,7 @@ namespace Aws /** * The status of the local deployment. */ - Aws::Crt::Optional GetStatus() noexcept; + Aws::Crt::Optional GetStatus() const noexcept; /** * (Optional) The timestamp at which the local deployment was created in MM/dd/yyyy hh:mm:ss format */ @@ -728,7 +734,7 @@ namespace Aws /** * (Optional) The timestamp at which the local deployment was created in MM/dd/yyyy hh:mm:ss format */ - Aws::Crt::Optional GetCreatedOn() noexcept { return m_createdOn; } + Aws::Crt::Optional GetCreatedOn() const noexcept { return m_createdOn; } /** * (Optional) The status details of the local deployment. */ @@ -739,7 +745,7 @@ namespace Aws /** * (Optional) The status details of the local deployment. */ - Aws::Crt::Optional GetDeploymentStatusDetails() noexcept + Aws::Crt::Optional GetDeploymentStatusDetails() const noexcept { return m_deploymentStatusDetails; } @@ -775,7 +781,7 @@ namespace Aws /** * The name of the component. */ - Aws::Crt::Optional GetComponentName() noexcept { return m_componentName; } + Aws::Crt::Optional GetComponentName() const noexcept { return m_componentName; } /** * The version of the component. */ @@ -783,7 +789,7 @@ namespace Aws /** * The version of the component. */ - Aws::Crt::Optional GetVersion() noexcept { return m_version; } + Aws::Crt::Optional GetVersion() const noexcept { return m_version; } /** * The state of the component. */ @@ -791,7 +797,7 @@ namespace Aws /** * The state of the component. */ - Aws::Crt::Optional GetState() noexcept; + Aws::Crt::Optional GetState() const noexcept; /** * The component's configuration as a JSON object. */ @@ -802,7 +808,7 @@ namespace Aws /** * The component's configuration as a JSON object. */ - Aws::Crt::Optional GetConfiguration() noexcept { return m_configuration; } + Aws::Crt::Optional GetConfiguration() const noexcept { return m_configuration; } void SerializeToJsonObject(Aws::Crt::JsonObject &payloadObject) const noexcept override; static void s_loadFromJsonView(ComponentDetails &, const Aws::Crt::JsonView &) noexcept; static Aws::Crt::ScopedResource s_allocateFromPayload( @@ -835,7 +841,7 @@ namespace Aws /** * The client ID to used to connect. */ - Aws::Crt::Optional GetClientId() noexcept { return m_clientId; } + Aws::Crt::Optional GetClientId() const noexcept { return m_clientId; } /** * The client certificate in pem format. */ @@ -846,7 +852,7 @@ namespace Aws /** * The client certificate in pem format. */ - Aws::Crt::Optional GetCertificatePem() noexcept { return m_certificatePem; } + Aws::Crt::Optional GetCertificatePem() const noexcept { return m_certificatePem; } /** * The username. (unused). */ @@ -854,7 +860,7 @@ namespace Aws /** * The username. (unused). */ - Aws::Crt::Optional GetUsername() noexcept { return m_username; } + Aws::Crt::Optional GetUsername() const noexcept { return m_username; } /** * The password. (unused). */ @@ -862,7 +868,7 @@ namespace Aws /** * The password. (unused). */ - Aws::Crt::Optional GetPassword() noexcept { return m_password; } + Aws::Crt::Optional GetPassword() const noexcept { return m_password; } void SerializeToJsonObject(Aws::Crt::JsonObject &payloadObject) const noexcept override; static void s_loadFromJsonView(MQTTCredential &, const Aws::Crt::JsonView &) noexcept; static Aws::Crt::ScopedResource s_allocateFromPayload( @@ -897,7 +903,7 @@ namespace Aws * (Optional) The POSIX system user and, optionally, group to use to run this component on Linux core * devices. */ - Aws::Crt::Optional GetPosixUser() noexcept { return m_posixUser; } + Aws::Crt::Optional GetPosixUser() const noexcept { return m_posixUser; } /** * (Optional) The Windows user to use to run this component on Windows core devices. */ @@ -905,7 +911,7 @@ namespace Aws /** * (Optional) The Windows user to use to run this component on Windows core devices. */ - Aws::Crt::Optional GetWindowsUser() noexcept { return m_windowsUser; } + Aws::Crt::Optional GetWindowsUser() const noexcept { return m_windowsUser; } /** * (Optional) The system resource limits to apply to this component's processes. */ @@ -916,7 +922,7 @@ namespace Aws /** * (Optional) The system resource limits to apply to this component's processes. */ - Aws::Crt::Optional GetSystemResourceLimits() noexcept + Aws::Crt::Optional GetSystemResourceLimits() const noexcept { return m_systemResourceLimits; } @@ -956,7 +962,7 @@ namespace Aws /** * The client device's X.509 device certificate. */ - Aws::Crt::Optional GetClientDeviceCertificate() noexcept + Aws::Crt::Optional GetClientDeviceCertificate() const noexcept { if (m_chosenMember == TAG_CLIENT_DEVICE_CERTIFICATE) { @@ -1015,7 +1021,7 @@ namespace Aws /** * The configuration update event. */ - Aws::Crt::Optional GetValidateConfigurationUpdateEvent() noexcept + Aws::Crt::Optional GetValidateConfigurationUpdateEvent() const noexcept { if (m_chosenMember == TAG_VALIDATE_CONFIGURATION_UPDATE_EVENT) { @@ -1064,7 +1070,7 @@ namespace Aws /** * (Optional) A JSON message. */ - Aws::Crt::Optional GetJsonMessage() noexcept + Aws::Crt::Optional GetJsonMessage() const noexcept { if (m_chosenMember == TAG_JSON_MESSAGE) { @@ -1086,7 +1092,7 @@ namespace Aws /** * (Optional) A binary message. */ - Aws::Crt::Optional GetBinaryMessage() noexcept + Aws::Crt::Optional GetBinaryMessage() const noexcept { if (m_chosenMember == TAG_BINARY_MESSAGE) { @@ -1143,7 +1149,7 @@ namespace Aws /** * The MQTT message. */ - Aws::Crt::Optional GetMessage() noexcept + Aws::Crt::Optional GetMessage() const noexcept { if (m_chosenMember == TAG_MESSAGE) { @@ -1198,7 +1204,7 @@ namespace Aws /** * The configuration update event. */ - Aws::Crt::Optional GetConfigurationUpdateEvent() noexcept + Aws::Crt::Optional GetConfigurationUpdateEvent() const noexcept { if (m_chosenMember == TAG_CONFIGURATION_UPDATE_EVENT) { @@ -1247,7 +1253,7 @@ namespace Aws /** * An event that indicates that the Greengrass wants to update a component. */ - Aws::Crt::Optional GetPreUpdateEvent() noexcept + Aws::Crt::Optional GetPreUpdateEvent() const noexcept { if (m_chosenMember == TAG_PRE_UPDATE_EVENT) { @@ -1269,7 +1275,7 @@ namespace Aws /** * An event that indicates that the nucleus updated a component. */ - Aws::Crt::Optional GetPostUpdateEvent() noexcept + Aws::Crt::Optional GetPostUpdateEvent() const noexcept { if (m_chosenMember == TAG_POST_UPDATE_EVENT) { @@ -1320,7 +1326,7 @@ namespace Aws /** * The information about the new certificate. */ - Aws::Crt::Optional GetCertificateUpdate() noexcept + Aws::Crt::Optional GetCertificateUpdate() const noexcept { if (m_chosenMember == TAG_CERTIFICATE_UPDATE) { @@ -1364,7 +1370,7 @@ namespace Aws /** * The types of certificate updates to subscribe to. */ - Aws::Crt::Optional GetCertificateType() noexcept; + Aws::Crt::Optional GetCertificateType() const noexcept; void SerializeToJsonObject(Aws::Crt::JsonObject &payloadObject) const noexcept override; static void s_loadFromJsonView(CertificateOptions &, const Aws::Crt::JsonView &) noexcept; static Aws::Crt::ScopedResource s_allocateFromPayload( @@ -1400,7 +1406,7 @@ namespace Aws /** * The validity status. */ - Aws::Crt::Optional GetStatus() noexcept; + Aws::Crt::Optional GetStatus() const noexcept; /** * The ID of the AWS IoT Greengrass deployment that requested the configuration update. */ @@ -1408,7 +1414,7 @@ namespace Aws /** * The ID of the AWS IoT Greengrass deployment that requested the configuration update. */ - Aws::Crt::Optional GetDeploymentId() noexcept { return m_deploymentId; } + Aws::Crt::Optional GetDeploymentId() const noexcept { return m_deploymentId; } /** * (Optional) A message that reports why the configuration isn't valid. */ @@ -1416,7 +1422,7 @@ namespace Aws /** * (Optional) A message that reports why the configuration isn't valid. */ - Aws::Crt::Optional GetMessage() noexcept { return m_message; } + Aws::Crt::Optional GetMessage() const noexcept { return m_message; } void SerializeToJsonObject(Aws::Crt::JsonObject &payloadObject) const noexcept override; static void s_loadFromJsonView(ConfigurationValidityReport &, const Aws::Crt::JsonView &) noexcept; static Aws::Crt::ScopedResource s_allocateFromPayload( @@ -1453,7 +1459,7 @@ namespace Aws /** * (Optional) A JSON message. */ - Aws::Crt::Optional GetJsonMessage() noexcept + Aws::Crt::Optional GetJsonMessage() const noexcept { if (m_chosenMember == TAG_JSON_MESSAGE) { @@ -1475,7 +1481,7 @@ namespace Aws /** * (Optional) A binary message. */ - Aws::Crt::Optional GetBinaryMessage() noexcept + Aws::Crt::Optional GetBinaryMessage() const noexcept { if (m_chosenMember == TAG_BINARY_MESSAGE) { @@ -1526,7 +1532,7 @@ namespace Aws /** * The decrypted part of the protected secret information that you provided to Secrets Manager as a string. */ - Aws::Crt::Optional GetSecretString() noexcept + Aws::Crt::Optional GetSecretString() const noexcept { if (m_chosenMember == TAG_SECRET_STRING) { @@ -1550,7 +1556,7 @@ namespace Aws * (Optional) The decrypted part of the protected secret information that you provided to Secrets Manager as * binary data in the form of a byte array. */ - Aws::Crt::Optional> GetSecretBinary() noexcept + Aws::Crt::Optional> GetSecretBinary() const noexcept { if (m_chosenMember == TAG_SECRET_BINARY) { @@ -1603,7 +1609,7 @@ namespace Aws * The client device's MQTT credentials. Specify the client ID and certificate that the client device uses * to connect. */ - Aws::Crt::Optional GetMqttCredential() noexcept + Aws::Crt::Optional GetMqttCredential() const noexcept { if (m_chosenMember == TAG_MQTT_CREDENTIAL) { @@ -1649,7 +1655,7 @@ namespace Aws void SetMessage(const Aws::Crt::String &message) noexcept { m_message = message; } - Aws::Crt::Optional GetMessage() noexcept override { return m_message; } + Aws::Crt::Optional GetMessage() const noexcept override { return m_message; } void SerializeToJsonObject(Aws::Crt::JsonObject &payloadObject) const noexcept override; static void s_loadFromJsonView(InvalidArgumentsError &, const Aws::Crt::JsonView &) noexcept; static Aws::Crt::ScopedResource s_allocateFromPayload( @@ -1675,11 +1681,11 @@ namespace Aws void SetMessage(const Aws::Crt::String &message) noexcept { m_message = message; } - Aws::Crt::Optional GetMessage() noexcept override { return m_message; } + Aws::Crt::Optional GetMessage() const noexcept override { return m_message; } void SetContext(const Aws::Crt::JsonObject &context) noexcept { m_context = context; } - Aws::Crt::Optional GetContext() noexcept { return m_context; } + Aws::Crt::Optional GetContext() const noexcept { return m_context; } void SerializeToJsonObject(Aws::Crt::JsonObject &payloadObject) const noexcept override; static void s_loadFromJsonView(ServiceError &, const Aws::Crt::JsonView &) noexcept; static Aws::Crt::ScopedResource s_allocateFromPayload( @@ -1706,7 +1712,7 @@ namespace Aws void SetMessage(const Aws::Crt::String &message) noexcept { m_message = message; } - Aws::Crt::Optional GetMessage() noexcept override { return m_message; } + Aws::Crt::Optional GetMessage() const noexcept override { return m_message; } void SerializeToJsonObject(Aws::Crt::JsonObject &payloadObject) const noexcept override; static void s_loadFromJsonView(UnauthorizedError &, const Aws::Crt::JsonView &) noexcept; static Aws::Crt::ScopedResource s_allocateFromPayload( @@ -1739,7 +1745,7 @@ namespace Aws /** * Whether the client device's identity is valid. */ - Aws::Crt::Optional GetIsValidClientDevice() noexcept { return m_isValidClientDevice; } + Aws::Crt::Optional GetIsValidClientDevice() const noexcept { return m_isValidClientDevice; } void SerializeToJsonObject(Aws::Crt::JsonObject &payloadObject) const noexcept override; static void s_loadFromJsonView(VerifyClientDeviceIdentityResponse &, const Aws::Crt::JsonView &) noexcept; static Aws::Crt::ScopedResource s_allocateFromPayload( @@ -1769,7 +1775,7 @@ namespace Aws /** * The client device's credentials. */ - Aws::Crt::Optional GetCredential() noexcept { return m_credential; } + Aws::Crt::Optional GetCredential() const noexcept { return m_credential; } void SerializeToJsonObject(Aws::Crt::JsonObject &payloadObject) const noexcept override; static void s_loadFromJsonView(VerifyClientDeviceIdentityRequest &, const Aws::Crt::JsonView &) noexcept; static Aws::Crt::ScopedResource s_allocateFromPayload( @@ -1795,7 +1801,7 @@ namespace Aws void SetMessage(const Aws::Crt::String &message) noexcept { m_message = message; } - Aws::Crt::Optional GetMessage() noexcept override { return m_message; } + Aws::Crt::Optional GetMessage() const noexcept override { return m_message; } void SerializeToJsonObject(Aws::Crt::JsonObject &payloadObject) const noexcept override; static void s_loadFromJsonView(InvalidTokenError &, const Aws::Crt::JsonView &) noexcept; static Aws::Crt::ScopedResource s_allocateFromPayload( @@ -1821,7 +1827,7 @@ namespace Aws void SetIsValid(const bool &isValid) noexcept { m_isValid = isValid; } - Aws::Crt::Optional GetIsValid() noexcept { return m_isValid; } + Aws::Crt::Optional GetIsValid() const noexcept { return m_isValid; } void SerializeToJsonObject(Aws::Crt::JsonObject &payloadObject) const noexcept override; static void s_loadFromJsonView(ValidateAuthorizationTokenResponse &, const Aws::Crt::JsonView &) noexcept; static Aws::Crt::ScopedResource s_allocateFromPayload( @@ -1847,7 +1853,7 @@ namespace Aws void SetToken(const Aws::Crt::String &token) noexcept { m_token = token; } - Aws::Crt::Optional GetToken() noexcept { return m_token; } + Aws::Crt::Optional GetToken() const noexcept { return m_token; } void SerializeToJsonObject(Aws::Crt::JsonObject &payloadObject) const noexcept override; static void s_loadFromJsonView(ValidateAuthorizationTokenRequest &, const Aws::Crt::JsonView &) noexcept; static Aws::Crt::ScopedResource s_allocateFromPayload( @@ -1873,7 +1879,7 @@ namespace Aws void SetMessage(const Aws::Crt::String &message) noexcept { m_message = message; } - Aws::Crt::Optional GetMessage() noexcept override { return m_message; } + Aws::Crt::Optional GetMessage() const noexcept override { return m_message; } void SerializeToJsonObject(Aws::Crt::JsonObject &payloadObject) const noexcept override; static void s_loadFromJsonView(ConflictError &, const Aws::Crt::JsonView &) noexcept; static Aws::Crt::ScopedResource s_allocateFromPayload( @@ -1903,7 +1909,7 @@ namespace Aws /** * The response state document as a JSON encoded blob. */ - Aws::Crt::Optional> GetPayload() noexcept { return m_payload; } + Aws::Crt::Optional> GetPayload() const noexcept { return m_payload; } void SerializeToJsonObject(Aws::Crt::JsonObject &payloadObject) const noexcept override; static void s_loadFromJsonView(UpdateThingShadowResponse &, const Aws::Crt::JsonView &) noexcept; static Aws::Crt::ScopedResource s_allocateFromPayload( @@ -1933,7 +1939,7 @@ namespace Aws /** * The name of the thing. */ - Aws::Crt::Optional GetThingName() noexcept { return m_thingName; } + Aws::Crt::Optional GetThingName() const noexcept { return m_thingName; } /** * The name of the shadow. To specify the thing's classic shadow, set this parameter to an empty string * (""). @@ -1943,7 +1949,7 @@ namespace Aws * The name of the shadow. To specify the thing's classic shadow, set this parameter to an empty string * (""). */ - Aws::Crt::Optional GetShadowName() noexcept { return m_shadowName; } + Aws::Crt::Optional GetShadowName() const noexcept { return m_shadowName; } /** * The request state document as a JSON encoded blob. */ @@ -1951,7 +1957,7 @@ namespace Aws /** * The request state document as a JSON encoded blob. */ - Aws::Crt::Optional> GetPayload() noexcept { return m_payload; } + Aws::Crt::Optional> GetPayload() const noexcept { return m_payload; } void SerializeToJsonObject(Aws::Crt::JsonObject &payloadObject) const noexcept override; static void s_loadFromJsonView(UpdateThingShadowRequest &, const Aws::Crt::JsonView &) noexcept; static Aws::Crt::ScopedResource s_allocateFromPayload( @@ -1979,15 +1985,15 @@ namespace Aws void SetMessage(const Aws::Crt::String &message) noexcept { m_message = message; } - Aws::Crt::Optional GetMessage() noexcept override { return m_message; } + Aws::Crt::Optional GetMessage() const noexcept override { return m_message; } void SetResourceType(const Aws::Crt::String &resourceType) noexcept { m_resourceType = resourceType; } - Aws::Crt::Optional GetResourceType() noexcept { return m_resourceType; } + Aws::Crt::Optional GetResourceType() const noexcept { return m_resourceType; } void SetResourceName(const Aws::Crt::String &resourceName) noexcept { m_resourceName = resourceName; } - Aws::Crt::Optional GetResourceName() noexcept { return m_resourceName; } + Aws::Crt::Optional GetResourceName() const noexcept { return m_resourceName; } void SerializeToJsonObject(Aws::Crt::JsonObject &payloadObject) const noexcept override; static void s_loadFromJsonView(ResourceNotFoundError &, const Aws::Crt::JsonView &) noexcept; static Aws::Crt::ScopedResource s_allocateFromPayload( @@ -2040,7 +2046,7 @@ namespace Aws /** * The state to set this component to. */ - Aws::Crt::Optional GetState() noexcept; + Aws::Crt::Optional GetState() const noexcept; void SerializeToJsonObject(Aws::Crt::JsonObject &payloadObject) const noexcept override; static void s_loadFromJsonView(UpdateStateRequest &, const Aws::Crt::JsonView &) noexcept; static Aws::Crt::ScopedResource s_allocateFromPayload( @@ -2066,7 +2072,7 @@ namespace Aws void SetMessage(const Aws::Crt::String &message) noexcept { m_message = message; } - Aws::Crt::Optional GetMessage() noexcept override { return m_message; } + Aws::Crt::Optional GetMessage() const noexcept override { return m_message; } void SerializeToJsonObject(Aws::Crt::JsonObject &payloadObject) const noexcept override; static void s_loadFromJsonView(FailedUpdateConditionCheckError &, const Aws::Crt::JsonView &) noexcept; static Aws::Crt::ScopedResource s_allocateFromPayload( @@ -2119,7 +2125,7 @@ namespace Aws * (Optional) The key path to the container node (the object) to update. Specify a list where each entry is * the key for a single level in the configuration object. Defaults to the root of the configuration object. */ - Aws::Crt::Optional> GetKeyPath() noexcept { return m_keyPath; } + Aws::Crt::Optional> GetKeyPath() const noexcept { return m_keyPath; } /** * The current Unix epoch time in milliseconds. This operation uses this timestamp to resolve concurrent * updates to the key. If the key in the component configuration has a greater timestamp than the timestamp @@ -2131,7 +2137,7 @@ namespace Aws * updates to the key. If the key in the component configuration has a greater timestamp than the timestamp * in the request, then the request fails. */ - Aws::Crt::Optional GetTimestamp() noexcept { return m_timestamp; } + Aws::Crt::Optional GetTimestamp() const noexcept { return m_timestamp; } /** * The configuration object to merge at the location that you specify in keyPath. */ @@ -2139,7 +2145,7 @@ namespace Aws /** * The configuration object to merge at the location that you specify in keyPath. */ - Aws::Crt::Optional GetValueToMerge() noexcept { return m_valueToMerge; } + Aws::Crt::Optional GetValueToMerge() const noexcept { return m_valueToMerge; } void SerializeToJsonObject(Aws::Crt::JsonObject &payloadObject) const noexcept override; static void s_loadFromJsonView(UpdateConfigurationRequest &, const Aws::Crt::JsonView &) noexcept; static Aws::Crt::ScopedResource s_allocateFromPayload( @@ -2221,7 +2227,7 @@ namespace Aws /** * @deprecated No longer used */ - Aws::Crt::Optional GetTopicName() noexcept { return m_topicName; } + Aws::Crt::Optional GetTopicName() const noexcept { return m_topicName; } void SerializeToJsonObject(Aws::Crt::JsonObject &payloadObject) const noexcept override; static void s_loadFromJsonView(SubscribeToTopicResponse &, const Aws::Crt::JsonView &) noexcept; static Aws::Crt::ScopedResource s_allocateFromPayload( @@ -2251,7 +2257,7 @@ namespace Aws /** * The topic to subscribe to. Supports MQTT-style wildcards. */ - Aws::Crt::Optional GetTopic() noexcept { return m_topic; } + Aws::Crt::Optional GetTopic() const noexcept { return m_topic; } /** * (Optional) The behavior that specifies whether the component receives messages from itself. */ @@ -2259,7 +2265,7 @@ namespace Aws /** * (Optional) The behavior that specifies whether the component receives messages from itself. */ - Aws::Crt::Optional GetReceiveMode() noexcept; + Aws::Crt::Optional GetReceiveMode() const noexcept; void SerializeToJsonObject(Aws::Crt::JsonObject &payloadObject) const noexcept override; static void s_loadFromJsonView(SubscribeToTopicRequest &, const Aws::Crt::JsonView &) noexcept; static Aws::Crt::ScopedResource s_allocateFromPayload( @@ -2311,7 +2317,7 @@ namespace Aws /** * The topic to which to subscribe. Supports MQTT wildcards. */ - Aws::Crt::Optional GetTopicName() noexcept { return m_topicName; } + Aws::Crt::Optional GetTopicName() const noexcept { return m_topicName; } /** * The MQTT QoS to use. */ @@ -2319,7 +2325,7 @@ namespace Aws /** * The MQTT QoS to use. */ - Aws::Crt::Optional GetQos() noexcept; + Aws::Crt::Optional GetQos() const noexcept; void SerializeToJsonObject(Aws::Crt::JsonObject &payloadObject) const noexcept override; static void s_loadFromJsonView(SubscribeToIoTCoreRequest &, const Aws::Crt::JsonView &) noexcept; static Aws::Crt::ScopedResource s_allocateFromPayload( @@ -2374,7 +2380,7 @@ namespace Aws /** * (Optional) The name of the component. Defaults to the name of the component that makes the request. */ - Aws::Crt::Optional GetComponentName() noexcept { return m_componentName; } + Aws::Crt::Optional GetComponentName() const noexcept { return m_componentName; } /** * The key path to the configuration value for which to subscribe. Specify a list where each entry is the * key for a single level in the configuration object. @@ -2384,7 +2390,7 @@ namespace Aws * The key path to the configuration value for which to subscribe. Specify a list where each entry is the * key for a single level in the configuration object. */ - Aws::Crt::Optional> GetKeyPath() noexcept { return m_keyPath; } + Aws::Crt::Optional> GetKeyPath() const noexcept { return m_keyPath; } void SerializeToJsonObject(Aws::Crt::JsonObject &payloadObject) const noexcept override; static void s_loadFromJsonView( SubscribeToConfigurationUpdateRequest &, @@ -2481,7 +2487,10 @@ namespace Aws m_certificateOptions = certificateOptions; } - Aws::Crt::Optional GetCertificateOptions() noexcept { return m_certificateOptions; } + Aws::Crt::Optional GetCertificateOptions() const noexcept + { + return m_certificateOptions; + } void SerializeToJsonObject(Aws::Crt::JsonObject &payloadObject) const noexcept override; static void s_loadFromJsonView(SubscribeToCertificateUpdatesRequest &, const Aws::Crt::JsonView &) noexcept; static Aws::Crt::ScopedResource s_allocateFromPayload( @@ -2507,7 +2516,7 @@ namespace Aws void SetMessage(const Aws::Crt::String &message) noexcept { m_message = message; } - Aws::Crt::Optional GetMessage() noexcept override { return m_message; } + Aws::Crt::Optional GetMessage() const noexcept override { return m_message; } void SerializeToJsonObject(Aws::Crt::JsonObject &payloadObject) const noexcept override; static void s_loadFromJsonView(ComponentNotFoundError &, const Aws::Crt::JsonView &) noexcept; static Aws::Crt::ScopedResource s_allocateFromPayload( @@ -2537,7 +2546,7 @@ namespace Aws /** * The status of the stop request. */ - Aws::Crt::Optional GetStopStatus() noexcept; + Aws::Crt::Optional GetStopStatus() const noexcept; /** * A message about why the component failed to stop, if the request failed. */ @@ -2545,7 +2554,7 @@ namespace Aws /** * A message about why the component failed to stop, if the request failed. */ - Aws::Crt::Optional GetMessage() noexcept { return m_message; } + Aws::Crt::Optional GetMessage() const noexcept { return m_message; } void SerializeToJsonObject(Aws::Crt::JsonObject &payloadObject) const noexcept override; static void s_loadFromJsonView(StopComponentResponse &, const Aws::Crt::JsonView &) noexcept; static Aws::Crt::ScopedResource s_allocateFromPayload( @@ -2576,7 +2585,7 @@ namespace Aws /** * The name of the component. */ - Aws::Crt::Optional GetComponentName() noexcept { return m_componentName; } + Aws::Crt::Optional GetComponentName() const noexcept { return m_componentName; } void SerializeToJsonObject(Aws::Crt::JsonObject &payloadObject) const noexcept override; static void s_loadFromJsonView(StopComponentRequest &, const Aws::Crt::JsonView &) noexcept; static Aws::Crt::ScopedResource s_allocateFromPayload( @@ -2633,7 +2642,7 @@ namespace Aws /** * The report that tells Greengrass whether or not the configuration update is valid. */ - Aws::Crt::Optional GetConfigurationValidityReport() noexcept + Aws::Crt::Optional GetConfigurationValidityReport() const noexcept { return m_configurationValidityReport; } @@ -2690,7 +2699,7 @@ namespace Aws /** * The name of the component to resume. */ - Aws::Crt::Optional GetComponentName() noexcept { return m_componentName; } + Aws::Crt::Optional GetComponentName() const noexcept { return m_componentName; } void SerializeToJsonObject(Aws::Crt::JsonObject &payloadObject) const noexcept override; static void s_loadFromJsonView(ResumeComponentRequest &, const Aws::Crt::JsonView &) noexcept; static Aws::Crt::ScopedResource s_allocateFromPayload( @@ -2720,7 +2729,7 @@ namespace Aws /** * The status of the restart request. */ - Aws::Crt::Optional GetRestartStatus() noexcept; + Aws::Crt::Optional GetRestartStatus() const noexcept; /** * A message about why the component failed to restart, if the request failed. */ @@ -2728,7 +2737,7 @@ namespace Aws /** * A message about why the component failed to restart, if the request failed. */ - Aws::Crt::Optional GetMessage() noexcept { return m_message; } + Aws::Crt::Optional GetMessage() const noexcept { return m_message; } void SerializeToJsonObject(Aws::Crt::JsonObject &payloadObject) const noexcept override; static void s_loadFromJsonView(RestartComponentResponse &, const Aws::Crt::JsonView &) noexcept; static Aws::Crt::ScopedResource s_allocateFromPayload( @@ -2759,7 +2768,7 @@ namespace Aws /** * The name of the component. */ - Aws::Crt::Optional GetComponentName() noexcept { return m_componentName; } + Aws::Crt::Optional GetComponentName() const noexcept { return m_componentName; } void SerializeToJsonObject(Aws::Crt::JsonObject &payloadObject) const noexcept override; static void s_loadFromJsonView(RestartComponentRequest &, const Aws::Crt::JsonView &) noexcept; static Aws::Crt::ScopedResource s_allocateFromPayload( @@ -2806,7 +2815,7 @@ namespace Aws void SetMetrics(const Aws::Crt::Vector &metrics) noexcept { m_metrics = metrics; } - Aws::Crt::Optional> GetMetrics() noexcept { return m_metrics; } + Aws::Crt::Optional> GetMetrics() const noexcept { return m_metrics; } void SerializeToJsonObject(Aws::Crt::JsonObject &payloadObject) const noexcept override; static void s_loadFromJsonView(PutComponentMetricRequest &, const Aws::Crt::JsonView &) noexcept; static Aws::Crt::ScopedResource s_allocateFromPayload( @@ -2857,7 +2866,7 @@ namespace Aws /** * The topic to publish the message. */ - Aws::Crt::Optional GetTopic() noexcept { return m_topic; } + Aws::Crt::Optional GetTopic() const noexcept { return m_topic; } /** * The message to publish. */ @@ -2865,7 +2874,7 @@ namespace Aws /** * The message to publish. */ - Aws::Crt::Optional GetPublishMessage() noexcept { return m_publishMessage; } + Aws::Crt::Optional GetPublishMessage() const noexcept { return m_publishMessage; } void SerializeToJsonObject(Aws::Crt::JsonObject &payloadObject) const noexcept override; static void s_loadFromJsonView(PublishToTopicRequest &, const Aws::Crt::JsonView &) noexcept; static Aws::Crt::ScopedResource s_allocateFromPayload( @@ -2917,7 +2926,7 @@ namespace Aws /** * The topic to which to publish the message. */ - Aws::Crt::Optional GetTopicName() noexcept { return m_topicName; } + Aws::Crt::Optional GetTopicName() const noexcept { return m_topicName; } /** * The MQTT QoS to use. */ @@ -2925,7 +2934,7 @@ namespace Aws /** * The MQTT QoS to use. */ - Aws::Crt::Optional GetQos() noexcept; + Aws::Crt::Optional GetQos() const noexcept; /** * (Optional) The message payload as a blob. */ @@ -2933,7 +2942,7 @@ namespace Aws /** * (Optional) The message payload as a blob. */ - Aws::Crt::Optional> GetPayload() noexcept { return m_payload; } + Aws::Crt::Optional> GetPayload() const noexcept { return m_payload; } /** * (Optional) Whether to set MQTT retain option to true when publishing. */ @@ -2941,7 +2950,7 @@ namespace Aws /** * (Optional) Whether to set MQTT retain option to true when publishing. */ - Aws::Crt::Optional GetRetain() noexcept { return m_retain; } + Aws::Crt::Optional GetRetain() const noexcept { return m_retain; } /** * (Optional) MQTT user properties associated with the message. */ @@ -2952,7 +2961,10 @@ namespace Aws /** * (Optional) MQTT user properties associated with the message. */ - Aws::Crt::Optional> GetUserProperties() noexcept { return m_userProperties; } + Aws::Crt::Optional> GetUserProperties() const noexcept + { + return m_userProperties; + } /** * (Optional) Message expiry interval in seconds. */ @@ -2963,7 +2975,7 @@ namespace Aws /** * (Optional) Message expiry interval in seconds. */ - Aws::Crt::Optional GetMessageExpiryIntervalSeconds() noexcept + Aws::Crt::Optional GetMessageExpiryIntervalSeconds() const noexcept { return m_messageExpiryIntervalSeconds; } @@ -2977,7 +2989,10 @@ namespace Aws /** * (Optional) Correlation data blob for request/response. */ - Aws::Crt::Optional> GetCorrelationData() noexcept { return m_correlationData; } + Aws::Crt::Optional> GetCorrelationData() const noexcept + { + return m_correlationData; + } /** * (Optional) Response topic for request/response. */ @@ -2985,7 +3000,7 @@ namespace Aws /** * (Optional) Response topic for request/response. */ - Aws::Crt::Optional GetResponseTopic() noexcept { return m_responseTopic; } + Aws::Crt::Optional GetResponseTopic() const noexcept { return m_responseTopic; } /** * (Optional) Message payload format. */ @@ -2993,7 +3008,7 @@ namespace Aws /** * (Optional) Message payload format. */ - Aws::Crt::Optional GetPayloadFormat() noexcept; + Aws::Crt::Optional GetPayloadFormat() const noexcept; /** * (Optional) Message content type. */ @@ -3001,7 +3016,7 @@ namespace Aws /** * (Optional) Message content type. */ - Aws::Crt::Optional GetContentType() noexcept { return m_contentType; } + Aws::Crt::Optional GetContentType() const noexcept { return m_contentType; } void SerializeToJsonObject(Aws::Crt::JsonObject &payloadObject) const noexcept override; static void s_loadFromJsonView(PublishToIoTCoreRequest &, const Aws::Crt::JsonView &) noexcept; static Aws::Crt::ScopedResource s_allocateFromPayload( @@ -3061,7 +3076,7 @@ namespace Aws /** * The name of the component to pause, which must be a generic component. */ - Aws::Crt::Optional GetComponentName() noexcept { return m_componentName; } + Aws::Crt::Optional GetComponentName() const noexcept { return m_componentName; } void SerializeToJsonObject(Aws::Crt::JsonObject &payloadObject) const noexcept override; static void s_loadFromJsonView(PauseComponentRequest &, const Aws::Crt::JsonView &) noexcept; static Aws::Crt::ScopedResource s_allocateFromPayload( @@ -3091,7 +3106,7 @@ namespace Aws /** * The list of shadow names. */ - Aws::Crt::Optional> GetResults() noexcept { return m_results; } + Aws::Crt::Optional> GetResults() const noexcept { return m_results; } /** * (Optional) The date and time that the response was generated. */ @@ -3099,7 +3114,7 @@ namespace Aws /** * (Optional) The date and time that the response was generated. */ - Aws::Crt::Optional GetTimestamp() noexcept { return m_timestamp; } + Aws::Crt::Optional GetTimestamp() const noexcept { return m_timestamp; } /** * (Optional) The token value to use in paged requests to retrieve the next page in the sequence. This token * isn't present when there are no more shadow names to return. @@ -3109,7 +3124,7 @@ namespace Aws * (Optional) The token value to use in paged requests to retrieve the next page in the sequence. This token * isn't present when there are no more shadow names to return. */ - Aws::Crt::Optional GetNextToken() noexcept { return m_nextToken; } + Aws::Crt::Optional GetNextToken() const noexcept { return m_nextToken; } void SerializeToJsonObject(Aws::Crt::JsonObject &payloadObject) const noexcept override; static void s_loadFromJsonView(ListNamedShadowsForThingResponse &, const Aws::Crt::JsonView &) noexcept; static Aws::Crt::ScopedResource s_allocateFromPayload( @@ -3141,7 +3156,7 @@ namespace Aws /** * The name of the thing. */ - Aws::Crt::Optional GetThingName() noexcept { return m_thingName; } + Aws::Crt::Optional GetThingName() const noexcept { return m_thingName; } /** * (Optional) The token to retrieve the next set of results. This value is returned on paged results and is * used in the call that returns the next page. @@ -3151,7 +3166,7 @@ namespace Aws * (Optional) The token to retrieve the next set of results. This value is returned on paged results and is * used in the call that returns the next page. */ - Aws::Crt::Optional GetNextToken() noexcept { return m_nextToken; } + Aws::Crt::Optional GetNextToken() const noexcept { return m_nextToken; } /** * (Optional) The number of shadow names to return in each call. Value must be between 1 and 100. Default * is 25. @@ -3161,7 +3176,7 @@ namespace Aws * (Optional) The number of shadow names to return in each call. Value must be between 1 and 100. Default * is 25. */ - Aws::Crt::Optional GetPageSize() noexcept { return m_pageSize; } + Aws::Crt::Optional GetPageSize() const noexcept { return m_pageSize; } void SerializeToJsonObject(Aws::Crt::JsonObject &payloadObject) const noexcept override; static void s_loadFromJsonView(ListNamedShadowsForThingRequest &, const Aws::Crt::JsonView &) noexcept; static Aws::Crt::ScopedResource s_allocateFromPayload( @@ -3196,7 +3211,7 @@ namespace Aws /** * The list of local deployments. */ - Aws::Crt::Optional> GetLocalDeployments() noexcept + Aws::Crt::Optional> GetLocalDeployments() const noexcept { return m_localDeployments; } @@ -3253,7 +3268,10 @@ namespace Aws /** * The list of components. */ - Aws::Crt::Optional> GetComponents() noexcept { return m_components; } + Aws::Crt::Optional> GetComponents() const noexcept + { + return m_components; + } void SerializeToJsonObject(Aws::Crt::JsonObject &payloadObject) const noexcept override; static void s_loadFromJsonView(ListComponentsResponse &, const Aws::Crt::JsonView &) noexcept; static Aws::Crt::ScopedResource s_allocateFromPayload( @@ -3304,7 +3322,7 @@ namespace Aws /** * The response state document as a JSON encoded blob. */ - Aws::Crt::Optional> GetPayload() noexcept { return m_payload; } + Aws::Crt::Optional> GetPayload() const noexcept { return m_payload; } void SerializeToJsonObject(Aws::Crt::JsonObject &payloadObject) const noexcept override; static void s_loadFromJsonView(GetThingShadowResponse &, const Aws::Crt::JsonView &) noexcept; static Aws::Crt::ScopedResource s_allocateFromPayload( @@ -3334,7 +3352,7 @@ namespace Aws /** * The name of the thing. */ - Aws::Crt::Optional GetThingName() noexcept { return m_thingName; } + Aws::Crt::Optional GetThingName() const noexcept { return m_thingName; } /** * The name of the shadow. To specify the thing's classic shadow, set this parameter to an empty string * (""). @@ -3344,7 +3362,7 @@ namespace Aws * The name of the shadow. To specify the thing's classic shadow, set this parameter to an empty string * (""). */ - Aws::Crt::Optional GetShadowName() noexcept { return m_shadowName; } + Aws::Crt::Optional GetShadowName() const noexcept { return m_shadowName; } void SerializeToJsonObject(Aws::Crt::JsonObject &payloadObject) const noexcept override; static void s_loadFromJsonView(GetThingShadowRequest &, const Aws::Crt::JsonView &) noexcept; static Aws::Crt::ScopedResource s_allocateFromPayload( @@ -3375,7 +3393,7 @@ namespace Aws /** * The ID of the secret. */ - Aws::Crt::Optional GetSecretId() noexcept { return m_secretId; } + Aws::Crt::Optional GetSecretId() const noexcept { return m_secretId; } /** * The ID of this version of the secret. */ @@ -3383,7 +3401,7 @@ namespace Aws /** * The ID of this version of the secret. */ - Aws::Crt::Optional GetVersionId() noexcept { return m_versionId; } + Aws::Crt::Optional GetVersionId() const noexcept { return m_versionId; } /** * The list of staging labels attached to this version of the secret. */ @@ -3394,7 +3412,10 @@ namespace Aws /** * The list of staging labels attached to this version of the secret. */ - Aws::Crt::Optional> GetVersionStage() noexcept { return m_versionStage; } + Aws::Crt::Optional> GetVersionStage() const noexcept + { + return m_versionStage; + } /** * The value of this version of the secret. */ @@ -3402,7 +3423,7 @@ namespace Aws /** * The value of this version of the secret. */ - Aws::Crt::Optional GetSecretValue() noexcept { return m_secretValue; } + Aws::Crt::Optional GetSecretValue() const noexcept { return m_secretValue; } void SerializeToJsonObject(Aws::Crt::JsonObject &payloadObject) const noexcept override; static void s_loadFromJsonView(GetSecretValueResponse &, const Aws::Crt::JsonView &) noexcept; static Aws::Crt::ScopedResource s_allocateFromPayload( @@ -3437,7 +3458,7 @@ namespace Aws * The name of the secret to get. You can specify either the Amazon Resource Name (ARN) or the friendly name * of the secret. */ - Aws::Crt::Optional GetSecretId() noexcept { return m_secretId; } + Aws::Crt::Optional GetSecretId() const noexcept { return m_secretId; } /** * (Optional) The ID of the version to get. If you don't specify versionId or versionStage, this operation * defaults to the version with the AWSCURRENT label. @@ -3447,7 +3468,7 @@ namespace Aws * (Optional) The ID of the version to get. If you don't specify versionId or versionStage, this operation * defaults to the version with the AWSCURRENT label. */ - Aws::Crt::Optional GetVersionId() noexcept { return m_versionId; } + Aws::Crt::Optional GetVersionId() const noexcept { return m_versionId; } /** * (Optional) The staging label of the version to get. If you don't specify versionId or versionStage, this * operation defaults to the version with the AWSCURRENT label. @@ -3457,7 +3478,7 @@ namespace Aws * (Optional) The staging label of the version to get. If you don't specify versionId or versionStage, this * operation defaults to the version with the AWSCURRENT label. */ - Aws::Crt::Optional GetVersionStage() noexcept { return m_versionStage; } + Aws::Crt::Optional GetVersionStage() const noexcept { return m_versionStage; } /** * (Optional) Whether to fetch the latest secret from cloud when the request is handled. Defaults to false. */ @@ -3465,7 +3486,7 @@ namespace Aws /** * (Optional) Whether to fetch the latest secret from cloud when the request is handled. Defaults to false. */ - Aws::Crt::Optional GetRefresh() noexcept { return m_refresh; } + Aws::Crt::Optional GetRefresh() const noexcept { return m_refresh; } void SerializeToJsonObject(Aws::Crt::JsonObject &payloadObject) const noexcept override; static void s_loadFromJsonView(GetSecretValueRequest &, const Aws::Crt::JsonView &) noexcept; static Aws::Crt::ScopedResource s_allocateFromPayload( @@ -3498,7 +3519,7 @@ namespace Aws /** * The local deployment. */ - Aws::Crt::Optional GetDeployment() noexcept { return m_deployment; } + Aws::Crt::Optional GetDeployment() const noexcept { return m_deployment; } void SerializeToJsonObject(Aws::Crt::JsonObject &payloadObject) const noexcept override; static void s_loadFromJsonView(GetLocalDeploymentStatusResponse &, const Aws::Crt::JsonView &) noexcept; static Aws::Crt::ScopedResource s_allocateFromPayload( @@ -3528,7 +3549,7 @@ namespace Aws /** * The ID of the local deployment to get. */ - Aws::Crt::Optional GetDeploymentId() noexcept { return m_deploymentId; } + Aws::Crt::Optional GetDeploymentId() const noexcept { return m_deploymentId; } void SerializeToJsonObject(Aws::Crt::JsonObject &payloadObject) const noexcept override; static void s_loadFromJsonView(GetLocalDeploymentStatusRequest &, const Aws::Crt::JsonView &) noexcept; static Aws::Crt::ScopedResource s_allocateFromPayload( @@ -3558,7 +3579,7 @@ namespace Aws /** * The name of the component. */ - Aws::Crt::Optional GetComponentName() noexcept { return m_componentName; } + Aws::Crt::Optional GetComponentName() const noexcept { return m_componentName; } /** * The requested configuration as an object. */ @@ -3566,7 +3587,7 @@ namespace Aws /** * The requested configuration as an object. */ - Aws::Crt::Optional GetValue() noexcept { return m_value; } + Aws::Crt::Optional GetValue() const noexcept { return m_value; } void SerializeToJsonObject(Aws::Crt::JsonObject &payloadObject) const noexcept override; static void s_loadFromJsonView(GetConfigurationResponse &, const Aws::Crt::JsonView &) noexcept; static Aws::Crt::ScopedResource s_allocateFromPayload( @@ -3597,7 +3618,7 @@ namespace Aws /** * (Optional) The name of the component. Defaults to the name of the component that makes the request. */ - Aws::Crt::Optional GetComponentName() noexcept { return m_componentName; } + Aws::Crt::Optional GetComponentName() const noexcept { return m_componentName; } /** * The key path to the configuration value. Specify a list where each entry is the key for a single level in * the configuration object. @@ -3607,7 +3628,7 @@ namespace Aws * The key path to the configuration value. Specify a list where each entry is the key for a single level in * the configuration object. */ - Aws::Crt::Optional> GetKeyPath() noexcept { return m_keyPath; } + Aws::Crt::Optional> GetKeyPath() const noexcept { return m_keyPath; } void SerializeToJsonObject(Aws::Crt::JsonObject &payloadObject) const noexcept override; static void s_loadFromJsonView(GetConfigurationRequest &, const Aws::Crt::JsonView &) noexcept; static Aws::Crt::ScopedResource s_allocateFromPayload( @@ -3641,7 +3662,7 @@ namespace Aws /** * The component's details. */ - Aws::Crt::Optional GetComponentDetails() noexcept { return m_componentDetails; } + Aws::Crt::Optional GetComponentDetails() const noexcept { return m_componentDetails; } void SerializeToJsonObject(Aws::Crt::JsonObject &payloadObject) const noexcept override; static void s_loadFromJsonView(GetComponentDetailsResponse &, const Aws::Crt::JsonView &) noexcept; static Aws::Crt::ScopedResource s_allocateFromPayload( @@ -3671,7 +3692,7 @@ namespace Aws /** * The name of the component to get. */ - Aws::Crt::Optional GetComponentName() noexcept { return m_componentName; } + Aws::Crt::Optional GetComponentName() const noexcept { return m_componentName; } void SerializeToJsonObject(Aws::Crt::JsonObject &payloadObject) const noexcept override; static void s_loadFromJsonView(GetComponentDetailsRequest &, const Aws::Crt::JsonView &) noexcept; static Aws::Crt::ScopedResource s_allocateFromPayload( @@ -3697,7 +3718,7 @@ namespace Aws void SetMessage(const Aws::Crt::String &message) noexcept { m_message = message; } - Aws::Crt::Optional GetMessage() noexcept override { return m_message; } + Aws::Crt::Optional GetMessage() const noexcept override { return m_message; } void SerializeToJsonObject(Aws::Crt::JsonObject &payloadObject) const noexcept override; static void s_loadFromJsonView(InvalidCredentialError &, const Aws::Crt::JsonView &) noexcept; static Aws::Crt::ScopedResource s_allocateFromPayload( @@ -3732,7 +3753,10 @@ namespace Aws * The session token for the client device. You can use this session token in subsequent requests to * authorize this client device's actions. */ - Aws::Crt::Optional GetClientDeviceAuthToken() noexcept { return m_clientDeviceAuthToken; } + Aws::Crt::Optional GetClientDeviceAuthToken() const noexcept + { + return m_clientDeviceAuthToken; + } void SerializeToJsonObject(Aws::Crt::JsonObject &payloadObject) const noexcept override; static void s_loadFromJsonView(GetClientDeviceAuthTokenResponse &, const Aws::Crt::JsonView &) noexcept; static Aws::Crt::ScopedResource s_allocateFromPayload( @@ -3762,7 +3786,7 @@ namespace Aws /** * The client device's credentials. */ - Aws::Crt::Optional GetCredential() noexcept { return m_credential; } + Aws::Crt::Optional GetCredential() const noexcept { return m_credential; } void SerializeToJsonObject(Aws::Crt::JsonObject &payloadObject) const noexcept override; static void s_loadFromJsonView(GetClientDeviceAuthTokenRequest &, const Aws::Crt::JsonView &) noexcept; static Aws::Crt::ScopedResource s_allocateFromPayload( @@ -3792,7 +3816,7 @@ namespace Aws /** * An empty response state document. */ - Aws::Crt::Optional> GetPayload() noexcept { return m_payload; } + Aws::Crt::Optional> GetPayload() const noexcept { return m_payload; } void SerializeToJsonObject(Aws::Crt::JsonObject &payloadObject) const noexcept override; static void s_loadFromJsonView(DeleteThingShadowResponse &, const Aws::Crt::JsonView &) noexcept; static Aws::Crt::ScopedResource s_allocateFromPayload( @@ -3822,7 +3846,7 @@ namespace Aws /** * The name of the thing. */ - Aws::Crt::Optional GetThingName() noexcept { return m_thingName; } + Aws::Crt::Optional GetThingName() const noexcept { return m_thingName; } /** * The name of the shadow. To specify the thing's classic shadow, set this parameter to an empty string * (""). @@ -3832,7 +3856,7 @@ namespace Aws * The name of the shadow. To specify the thing's classic shadow, set this parameter to an empty string * (""). */ - Aws::Crt::Optional GetShadowName() noexcept { return m_shadowName; } + Aws::Crt::Optional GetShadowName() const noexcept { return m_shadowName; } void SerializeToJsonObject(Aws::Crt::JsonObject &payloadObject) const noexcept override; static void s_loadFromJsonView(DeleteThingShadowRequest &, const Aws::Crt::JsonView &) noexcept; static Aws::Crt::ScopedResource s_allocateFromPayload( @@ -3884,7 +3908,7 @@ namespace Aws /** * The ID of the AWS IoT Greengrass deployment to defer. */ - Aws::Crt::Optional GetDeploymentId() noexcept { return m_deploymentId; } + Aws::Crt::Optional GetDeploymentId() const noexcept { return m_deploymentId; } /** * (Optional) The name of the component for which to defer updates. Defaults to the name of the component * that makes the request. @@ -3894,7 +3918,7 @@ namespace Aws * (Optional) The name of the component for which to defer updates. Defaults to the name of the component * that makes the request. */ - Aws::Crt::Optional GetMessage() noexcept { return m_message; } + Aws::Crt::Optional GetMessage() const noexcept { return m_message; } /** * The amount of time in milliseconds for which to defer the update. Greengrass waits for this amount of * time and then sends another PreComponentUpdateEvent @@ -3904,7 +3928,7 @@ namespace Aws * The amount of time in milliseconds for which to defer the update. Greengrass waits for this amount of * time and then sends another PreComponentUpdateEvent */ - Aws::Crt::Optional GetRecheckAfterMs() noexcept { return m_recheckAfterMs; } + Aws::Crt::Optional GetRecheckAfterMs() const noexcept { return m_recheckAfterMs; } void SerializeToJsonObject(Aws::Crt::JsonObject &payloadObject) const noexcept override; static void s_loadFromJsonView(DeferComponentUpdateRequest &, const Aws::Crt::JsonView &) noexcept; static Aws::Crt::ScopedResource s_allocateFromPayload( @@ -3932,7 +3956,7 @@ namespace Aws void SetMessage(const Aws::Crt::String &message) noexcept { m_message = message; } - Aws::Crt::Optional GetMessage() noexcept override { return m_message; } + Aws::Crt::Optional GetMessage() const noexcept override { return m_message; } void SerializeToJsonObject(Aws::Crt::JsonObject &payloadObject) const noexcept override; static void s_loadFromJsonView(InvalidArtifactsDirectoryPathError &, const Aws::Crt::JsonView &) noexcept; static Aws::Crt::ScopedResource s_allocateFromPayload( @@ -3958,7 +3982,7 @@ namespace Aws void SetMessage(const Aws::Crt::String &message) noexcept { m_message = message; } - Aws::Crt::Optional GetMessage() noexcept override { return m_message; } + Aws::Crt::Optional GetMessage() const noexcept override { return m_message; } void SerializeToJsonObject(Aws::Crt::JsonObject &payloadObject) const noexcept override; static void s_loadFromJsonView(InvalidRecipeDirectoryPathError &, const Aws::Crt::JsonView &) noexcept; static Aws::Crt::ScopedResource s_allocateFromPayload( @@ -3988,7 +4012,7 @@ namespace Aws /** * The ID of the local deployment that the request created. */ - Aws::Crt::Optional GetDeploymentId() noexcept { return m_deploymentId; } + Aws::Crt::Optional GetDeploymentId() const noexcept { return m_deploymentId; } void SerializeToJsonObject(Aws::Crt::JsonObject &payloadObject) const noexcept override; static void s_loadFromJsonView(CreateLocalDeploymentResponse &, const Aws::Crt::JsonView &) noexcept; static Aws::Crt::ScopedResource s_allocateFromPayload( @@ -4020,7 +4044,7 @@ namespace Aws * The thing group name the deployment is targeting. If the group name is not specified, "LOCAL_DEPLOYMENT" * will be used. */ - Aws::Crt::Optional GetGroupName() noexcept { return m_groupName; } + Aws::Crt::Optional GetGroupName() const noexcept { return m_groupName; } /** * Map of component name to version. Components will be added to the group's existing root components. */ @@ -4032,8 +4056,8 @@ namespace Aws /** * Map of component name to version. Components will be added to the group's existing root components. */ - Aws::Crt::Optional> - GetRootComponentVersionsToAdd() noexcept + Aws::Crt::Optional> GetRootComponentVersionsToAdd() + const noexcept { return m_rootComponentVersionsToAdd; } @@ -4049,7 +4073,7 @@ namespace Aws * List of components that need to be removed from the group, for example if new artifacts were loaded in * this request but recipe version did not change. */ - Aws::Crt::Optional> GetRootComponentsToRemove() noexcept + Aws::Crt::Optional> GetRootComponentsToRemove() const noexcept { return m_rootComponentsToRemove; } @@ -4064,8 +4088,8 @@ namespace Aws /** * Map of component names to configuration. */ - Aws::Crt::Optional> - GetComponentToConfiguration() noexcept + Aws::Crt::Optional> GetComponentToConfiguration() + const noexcept { return m_componentToConfiguration; } @@ -4080,7 +4104,7 @@ namespace Aws /** * Map of component names to component run as info. */ - Aws::Crt::Optional> GetComponentToRunWithInfo() noexcept + Aws::Crt::Optional> GetComponentToRunWithInfo() const noexcept { return m_componentToRunWithInfo; } @@ -4094,7 +4118,10 @@ namespace Aws /** * All recipes files in this directory will be copied over to the Greengrass package store. */ - Aws::Crt::Optional GetRecipeDirectoryPath() noexcept { return m_recipeDirectoryPath; } + Aws::Crt::Optional GetRecipeDirectoryPath() const noexcept + { + return m_recipeDirectoryPath; + } /** * All artifact files in this directory will be copied over to the Greengrass package store. */ @@ -4105,7 +4132,7 @@ namespace Aws /** * All artifact files in this directory will be copied over to the Greengrass package store. */ - Aws::Crt::Optional GetArtifactsDirectoryPath() noexcept + Aws::Crt::Optional GetArtifactsDirectoryPath() const noexcept { return m_artifactsDirectoryPath; } @@ -4116,7 +4143,7 @@ namespace Aws /** * Deployment failure handling policy. */ - Aws::Crt::Optional GetFailureHandlingPolicy() noexcept; + Aws::Crt::Optional GetFailureHandlingPolicy() const noexcept; void SerializeToJsonObject(Aws::Crt::JsonObject &payloadObject) const noexcept override; static void s_loadFromJsonView(CreateLocalDeploymentRequest &, const Aws::Crt::JsonView &) noexcept; static Aws::Crt::ScopedResource s_allocateFromPayload( @@ -4149,32 +4176,41 @@ namespace Aws void SetPassword(const Aws::Crt::String &password) noexcept { m_password = password; } - Aws::Crt::Optional GetPassword() noexcept { return m_password; } + Aws::Crt::Optional GetPassword() const noexcept { return m_password; } void SetUsername(const Aws::Crt::String &username) noexcept { m_username = username; } - Aws::Crt::Optional GetUsername() noexcept { return m_username; } + Aws::Crt::Optional GetUsername() const noexcept { return m_username; } void SetPasswordExpiration(const Aws::Crt::DateTime &passwordExpiration) noexcept { m_passwordExpiration = passwordExpiration; } - Aws::Crt::Optional GetPasswordExpiration() noexcept { return m_passwordExpiration; } + Aws::Crt::Optional GetPasswordExpiration() const noexcept + { + return m_passwordExpiration; + } void SetCertificateSHA256Hash(const Aws::Crt::String &certificateSHA256Hash) noexcept { m_certificateSHA256Hash = certificateSHA256Hash; } - Aws::Crt::Optional GetCertificateSHA256Hash() noexcept { return m_certificateSHA256Hash; } + Aws::Crt::Optional GetCertificateSHA256Hash() const noexcept + { + return m_certificateSHA256Hash; + } void SetCertificateSHA1Hash(const Aws::Crt::String &certificateSHA1Hash) noexcept { m_certificateSHA1Hash = certificateSHA1Hash; } - Aws::Crt::Optional GetCertificateSHA1Hash() noexcept { return m_certificateSHA1Hash; } + Aws::Crt::Optional GetCertificateSHA1Hash() const noexcept + { + return m_certificateSHA1Hash; + } void SerializeToJsonObject(Aws::Crt::JsonObject &payloadObject) const noexcept override; static void s_loadFromJsonView(CreateDebugPasswordResponse &, const Aws::Crt::JsonView &) noexcept; static Aws::Crt::ScopedResource s_allocateFromPayload( @@ -4225,7 +4261,7 @@ namespace Aws void SetMessage(const Aws::Crt::String &message) noexcept { m_message = message; } - Aws::Crt::Optional GetMessage() noexcept { return m_message; } + Aws::Crt::Optional GetMessage() const noexcept { return m_message; } void SerializeToJsonObject(Aws::Crt::JsonObject &payloadObject) const noexcept override; static void s_loadFromJsonView(CancelLocalDeploymentResponse &, const Aws::Crt::JsonView &) noexcept; static Aws::Crt::ScopedResource s_allocateFromPayload( @@ -4255,7 +4291,7 @@ namespace Aws /** * (Optional) The ID of the local deployment to cancel. */ - Aws::Crt::Optional GetDeploymentId() noexcept { return m_deploymentId; } + Aws::Crt::Optional GetDeploymentId() const noexcept { return m_deploymentId; } void SerializeToJsonObject(Aws::Crt::JsonObject &payloadObject) const noexcept override; static void s_loadFromJsonView(CancelLocalDeploymentRequest &, const Aws::Crt::JsonView &) noexcept; static Aws::Crt::ScopedResource s_allocateFromPayload( @@ -4281,7 +4317,7 @@ namespace Aws void SetMessage(const Aws::Crt::String &message) noexcept { m_message = message; } - Aws::Crt::Optional GetMessage() noexcept override { return m_message; } + Aws::Crt::Optional GetMessage() const noexcept override { return m_message; } void SerializeToJsonObject(Aws::Crt::JsonObject &payloadObject) const noexcept override; static void s_loadFromJsonView(InvalidClientDeviceAuthTokenError &, const Aws::Crt::JsonView &) noexcept; static Aws::Crt::ScopedResource s_allocateFromPayload( @@ -4311,7 +4347,7 @@ namespace Aws /** * Whether the client device is authorized to perform the operation on the resource. */ - Aws::Crt::Optional GetIsAuthorized() noexcept { return m_isAuthorized; } + Aws::Crt::Optional GetIsAuthorized() const noexcept { return m_isAuthorized; } void SerializeToJsonObject(Aws::Crt::JsonObject &payloadObject) const noexcept override; static void s_loadFromJsonView(AuthorizeClientDeviceActionResponse &, const Aws::Crt::JsonView &) noexcept; static Aws::Crt::ScopedResource s_allocateFromPayload( @@ -4344,7 +4380,10 @@ namespace Aws /** * The session token for the client device from GetClientDeviceAuthToken. */ - Aws::Crt::Optional GetClientDeviceAuthToken() noexcept { return m_clientDeviceAuthToken; } + Aws::Crt::Optional GetClientDeviceAuthToken() const noexcept + { + return m_clientDeviceAuthToken; + } /** * The operation to authorize. */ @@ -4352,7 +4391,7 @@ namespace Aws /** * The operation to authorize. */ - Aws::Crt::Optional GetOperation() noexcept { return m_operation; } + Aws::Crt::Optional GetOperation() const noexcept { return m_operation; } /** * The resource the client device performs the operation on. */ @@ -4360,7 +4399,7 @@ namespace Aws /** * The resource the client device performs the operation on. */ - Aws::Crt::Optional GetResource() noexcept { return m_resource; } + Aws::Crt::Optional GetResource() const noexcept { return m_resource; } void SerializeToJsonObject(Aws::Crt::JsonObject &payloadObject) const noexcept override; static void s_loadFromJsonView(AuthorizeClientDeviceActionRequest &, const Aws::Crt::JsonView &) noexcept; static Aws::Crt::ScopedResource s_allocateFromPayload( diff --git a/greengrass_ipc/source/GreengrassCoreIpcClient.cpp b/greengrass_ipc/source/GreengrassCoreIpcClient.cpp index 5d75d1240..a1db24d5a 100644 --- a/greengrass_ipc/source/GreengrassCoreIpcClient.cpp +++ b/greengrass_ipc/source/GreengrassCoreIpcClient.cpp @@ -58,11 +58,20 @@ namespace Aws return m_connection.Connect(connectionConfig, &lifecycleHandler, m_clientBootstrap); } - void GreengrassCoreIpcClient::Close() noexcept { m_connection.Close(); } + void GreengrassCoreIpcClient::Close() noexcept + { + m_connection.Close(); + } - void GreengrassCoreIpcClient::WithLaunchMode(std::launch mode) noexcept { m_asyncLaunchMode = mode; } + void GreengrassCoreIpcClient::WithLaunchMode(std::launch mode) noexcept + { + m_asyncLaunchMode = mode; + } - GreengrassCoreIpcClient::~GreengrassCoreIpcClient() noexcept { Close(); } + GreengrassCoreIpcClient::~GreengrassCoreIpcClient() noexcept + { + Close(); + } std::shared_ptr GreengrassCoreIpcClient::NewSubscribeToIoTCore( std::shared_ptr streamHandler) noexcept diff --git a/greengrass_ipc/source/GreengrassCoreIpcModel.cpp b/greengrass_ipc/source/GreengrassCoreIpcModel.cpp index a3f9331eb..272d0d146 100644 --- a/greengrass_ipc/source/GreengrassCoreIpcModel.cpp +++ b/greengrass_ipc/source/GreengrassCoreIpcModel.cpp @@ -38,7 +38,10 @@ namespace Aws const char *UserProperty::MODEL_NAME = "aws.greengrass#UserProperty"; - Aws::Crt::String UserProperty::GetModelName() const noexcept { return UserProperty::MODEL_NAME; } + Aws::Crt::String UserProperty::GetModelName() const noexcept + { + return UserProperty::MODEL_NAME; + } Aws::Crt::ScopedResource UserProperty::s_allocateFromPayload( Aws::Crt::StringView stringView, @@ -81,7 +84,10 @@ namespace Aws const char *MessageContext::MODEL_NAME = "aws.greengrass#MessageContext"; - Aws::Crt::String MessageContext::GetModelName() const noexcept { return MessageContext::MODEL_NAME; } + Aws::Crt::String MessageContext::GetModelName() const noexcept + { + return MessageContext::MODEL_NAME; + } Aws::Crt::ScopedResource MessageContext::s_allocateFromPayload( Aws::Crt::StringView stringView, @@ -205,7 +211,8 @@ namespace Aws } } - Aws::Crt::Optional DeploymentStatusDetails::GetDetailedDeploymentStatus() noexcept + Aws::Crt::Optional DeploymentStatusDetails::GetDetailedDeploymentStatus() + const noexcept { if (!m_detailedDeploymentStatus.has_value()) return Aws::Crt::Optional(); @@ -412,7 +419,10 @@ namespace Aws const char *BinaryMessage::MODEL_NAME = "aws.greengrass#BinaryMessage"; - Aws::Crt::String BinaryMessage::GetModelName() const noexcept { return BinaryMessage::MODEL_NAME; } + Aws::Crt::String BinaryMessage::GetModelName() const noexcept + { + return BinaryMessage::MODEL_NAME; + } Aws::Crt::ScopedResource BinaryMessage::s_allocateFromPayload( Aws::Crt::StringView stringView, @@ -465,7 +475,10 @@ namespace Aws const char *JsonMessage::MODEL_NAME = "aws.greengrass#JsonMessage"; - Aws::Crt::String JsonMessage::GetModelName() const noexcept { return JsonMessage::MODEL_NAME; } + Aws::Crt::String JsonMessage::GetModelName() const noexcept + { + return JsonMessage::MODEL_NAME; + } Aws::Crt::ScopedResource JsonMessage::s_allocateFromPayload( Aws::Crt::StringView stringView, @@ -614,7 +627,7 @@ namespace Aws } } - Aws::Crt::Optional MQTTMessage::GetPayloadFormat() noexcept + Aws::Crt::Optional MQTTMessage::GetPayloadFormat() const noexcept { if (!m_payloadFormat.has_value()) return Aws::Crt::Optional(); @@ -632,7 +645,10 @@ namespace Aws const char *MQTTMessage::MODEL_NAME = "aws.greengrass#MQTTMessage"; - Aws::Crt::String MQTTMessage::GetModelName() const noexcept { return MQTTMessage::MODEL_NAME; } + Aws::Crt::String MQTTMessage::GetModelName() const noexcept + { + return MQTTMessage::MODEL_NAME; + } Aws::Crt::ScopedResource MQTTMessage::s_allocateFromPayload( Aws::Crt::StringView stringView, @@ -888,7 +904,10 @@ namespace Aws const char *CertificateUpdate::MODEL_NAME = "aws.greengrass#CertificateUpdate"; - Aws::Crt::String CertificateUpdate::GetModelName() const noexcept { return CertificateUpdate::MODEL_NAME; } + Aws::Crt::String CertificateUpdate::GetModelName() const noexcept + { + return CertificateUpdate::MODEL_NAME; + } Aws::Crt::ScopedResource CertificateUpdate::s_allocateFromPayload( Aws::Crt::StringView stringView, @@ -970,7 +989,7 @@ namespace Aws } } - Aws::Crt::Optional Metric::GetUnit() noexcept + Aws::Crt::Optional Metric::GetUnit() const noexcept { if (!m_unit.has_value()) return Aws::Crt::Optional(); @@ -1004,7 +1023,10 @@ namespace Aws const char *Metric::MODEL_NAME = "aws.greengrass#Metric"; - Aws::Crt::String Metric::GetModelName() const noexcept { return Metric::MODEL_NAME; } + Aws::Crt::String Metric::GetModelName() const noexcept + { + return Metric::MODEL_NAME; + } Aws::Crt::ScopedResource Metric::s_allocateFromPayload( Aws::Crt::StringView stringView, @@ -1098,7 +1120,7 @@ namespace Aws } } - Aws::Crt::Optional LocalDeployment::GetStatus() noexcept + Aws::Crt::Optional LocalDeployment::GetStatus() const noexcept { if (!m_status.has_value()) return Aws::Crt::Optional(); @@ -1128,7 +1150,10 @@ namespace Aws const char *LocalDeployment::MODEL_NAME = "aws.greengrass#LocalDeployment"; - Aws::Crt::String LocalDeployment::GetModelName() const noexcept { return LocalDeployment::MODEL_NAME; } + Aws::Crt::String LocalDeployment::GetModelName() const noexcept + { + return LocalDeployment::MODEL_NAME; + } Aws::Crt::ScopedResource LocalDeployment::s_allocateFromPayload( Aws::Crt::StringView stringView, @@ -1228,7 +1253,7 @@ namespace Aws } } - Aws::Crt::Optional ComponentDetails::GetState() noexcept + Aws::Crt::Optional ComponentDetails::GetState() const noexcept { if (!m_state.has_value()) return Aws::Crt::Optional(); @@ -1270,7 +1295,10 @@ namespace Aws const char *ComponentDetails::MODEL_NAME = "aws.greengrass#ComponentDetails"; - Aws::Crt::String ComponentDetails::GetModelName() const noexcept { return ComponentDetails::MODEL_NAME; } + Aws::Crt::String ComponentDetails::GetModelName() const noexcept + { + return ComponentDetails::MODEL_NAME; + } Aws::Crt::ScopedResource ComponentDetails::s_allocateFromPayload( Aws::Crt::StringView stringView, @@ -1338,7 +1366,10 @@ namespace Aws const char *MQTTCredential::MODEL_NAME = "aws.greengrass#MQTTCredential"; - Aws::Crt::String MQTTCredential::GetModelName() const noexcept { return MQTTCredential::MODEL_NAME; } + Aws::Crt::String MQTTCredential::GetModelName() const noexcept + { + return MQTTCredential::MODEL_NAME; + } Aws::Crt::ScopedResource MQTTCredential::s_allocateFromPayload( Aws::Crt::StringView stringView, @@ -1399,7 +1430,10 @@ namespace Aws const char *RunWithInfo::MODEL_NAME = "aws.greengrass#RunWithInfo"; - Aws::Crt::String RunWithInfo::GetModelName() const noexcept { return RunWithInfo::MODEL_NAME; } + Aws::Crt::String RunWithInfo::GetModelName() const noexcept + { + return RunWithInfo::MODEL_NAME; + } Aws::Crt::ScopedResource RunWithInfo::s_allocateFromPayload( Aws::Crt::StringView stringView, @@ -1662,7 +1696,10 @@ namespace Aws const char *IoTCoreMessage::MODEL_NAME = "aws.greengrass#IoTCoreMessage"; - Aws::Crt::String IoTCoreMessage::GetModelName() const noexcept { return IoTCoreMessage::MODEL_NAME; } + Aws::Crt::String IoTCoreMessage::GetModelName() const noexcept + { + return IoTCoreMessage::MODEL_NAME; + } Aws::Crt::ScopedResource IoTCoreMessage::s_allocateFromPayload( Aws::Crt::StringView stringView, @@ -1920,7 +1957,7 @@ namespace Aws } } - Aws::Crt::Optional CertificateOptions::GetCertificateType() noexcept + Aws::Crt::Optional CertificateOptions::GetCertificateType() const noexcept { if (!m_certificateType.has_value()) return Aws::Crt::Optional(); @@ -1934,7 +1971,10 @@ namespace Aws const char *CertificateOptions::MODEL_NAME = "aws.greengrass#CertificateOptions"; - Aws::Crt::String CertificateOptions::GetModelName() const noexcept { return CertificateOptions::MODEL_NAME; } + Aws::Crt::String CertificateOptions::GetModelName() const noexcept + { + return CertificateOptions::MODEL_NAME; + } Aws::Crt::ScopedResource CertificateOptions::s_allocateFromPayload( Aws::Crt::StringView stringView, @@ -2009,7 +2049,7 @@ namespace Aws } } - Aws::Crt::Optional ConfigurationValidityReport::GetStatus() noexcept + Aws::Crt::Optional ConfigurationValidityReport::GetStatus() const noexcept { if (!m_status.has_value()) return Aws::Crt::Optional(); @@ -2106,7 +2146,10 @@ namespace Aws const char *PublishMessage::MODEL_NAME = "aws.greengrass#PublishMessage"; - Aws::Crt::String PublishMessage::GetModelName() const noexcept { return PublishMessage::MODEL_NAME; } + Aws::Crt::String PublishMessage::GetModelName() const noexcept + { + return PublishMessage::MODEL_NAME; + } Aws::Crt::ScopedResource PublishMessage::s_allocateFromPayload( Aws::Crt::StringView stringView, @@ -2179,7 +2222,10 @@ namespace Aws const char *SecretValue::MODEL_NAME = "aws.greengrass#SecretValue"; - Aws::Crt::String SecretValue::GetModelName() const noexcept { return SecretValue::MODEL_NAME; } + Aws::Crt::String SecretValue::GetModelName() const noexcept + { + return SecretValue::MODEL_NAME; + } Aws::Crt::ScopedResource SecretValue::s_allocateFromPayload( Aws::Crt::StringView stringView, @@ -2237,7 +2283,10 @@ namespace Aws const char *CredentialDocument::MODEL_NAME = "aws.greengrass#CredentialDocument"; - Aws::Crt::String CredentialDocument::GetModelName() const noexcept { return CredentialDocument::MODEL_NAME; } + Aws::Crt::String CredentialDocument::GetModelName() const noexcept + { + return CredentialDocument::MODEL_NAME; + } Aws::Crt::ScopedResource CredentialDocument::s_allocateFromPayload( Aws::Crt::StringView stringView, @@ -2333,7 +2382,10 @@ namespace Aws const char *ServiceError::MODEL_NAME = "aws.greengrass#ServiceError"; - Aws::Crt::String ServiceError::GetModelName() const noexcept { return ServiceError::MODEL_NAME; } + Aws::Crt::String ServiceError::GetModelName() const noexcept + { + return ServiceError::MODEL_NAME; + } Aws::Crt::ScopedResource ServiceError::s_allocateFromPayload( Aws::Crt::StringView stringView, @@ -2376,7 +2428,10 @@ namespace Aws const char *UnauthorizedError::MODEL_NAME = "aws.greengrass#UnauthorizedError"; - Aws::Crt::String UnauthorizedError::GetModelName() const noexcept { return UnauthorizedError::MODEL_NAME; } + Aws::Crt::String UnauthorizedError::GetModelName() const noexcept + { + return UnauthorizedError::MODEL_NAME; + } Aws::Crt::ScopedResource UnauthorizedError::s_allocateFromPayload( Aws::Crt::StringView stringView, @@ -2521,7 +2576,10 @@ namespace Aws const char *InvalidTokenError::MODEL_NAME = "aws.greengrass#InvalidTokenError"; - Aws::Crt::String InvalidTokenError::GetModelName() const noexcept { return InvalidTokenError::MODEL_NAME; } + Aws::Crt::String InvalidTokenError::GetModelName() const noexcept + { + return InvalidTokenError::MODEL_NAME; + } Aws::Crt::ScopedResource InvalidTokenError::s_allocateFromPayload( Aws::Crt::StringView stringView, @@ -2662,7 +2720,10 @@ namespace Aws const char *ConflictError::MODEL_NAME = "aws.greengrass#ConflictError"; - Aws::Crt::String ConflictError::GetModelName() const noexcept { return ConflictError::MODEL_NAME; } + Aws::Crt::String ConflictError::GetModelName() const noexcept + { + return ConflictError::MODEL_NAME; + } Aws::Crt::ScopedResource ConflictError::s_allocateFromPayload( Aws::Crt::StringView stringView, @@ -2888,7 +2949,10 @@ namespace Aws const char *UpdateStateResponse::MODEL_NAME = "aws.greengrass#UpdateStateResponse"; - Aws::Crt::String UpdateStateResponse::GetModelName() const noexcept { return UpdateStateResponse::MODEL_NAME; } + Aws::Crt::String UpdateStateResponse::GetModelName() const noexcept + { + return UpdateStateResponse::MODEL_NAME; + } Aws::Crt::ScopedResource UpdateStateResponse::s_allocateFromPayload( Aws::Crt::StringView stringView, @@ -2944,7 +3008,7 @@ namespace Aws } } - Aws::Crt::Optional UpdateStateRequest::GetState() noexcept + Aws::Crt::Optional UpdateStateRequest::GetState() const noexcept { if (!m_state.has_value()) return Aws::Crt::Optional(); @@ -2962,7 +3026,10 @@ namespace Aws const char *UpdateStateRequest::MODEL_NAME = "aws.greengrass#UpdateStateRequest"; - Aws::Crt::String UpdateStateRequest::GetModelName() const noexcept { return UpdateStateRequest::MODEL_NAME; } + Aws::Crt::String UpdateStateRequest::GetModelName() const noexcept + { + return UpdateStateRequest::MODEL_NAME; + } Aws::Crt::ScopedResource UpdateStateRequest::s_allocateFromPayload( Aws::Crt::StringView stringView, @@ -3330,7 +3397,7 @@ namespace Aws } } - Aws::Crt::Optional SubscribeToTopicRequest::GetReceiveMode() noexcept + Aws::Crt::Optional SubscribeToTopicRequest::GetReceiveMode() const noexcept { if (!m_receiveMode.has_value()) return Aws::Crt::Optional(); @@ -3457,7 +3524,7 @@ namespace Aws } } - Aws::Crt::Optional SubscribeToIoTCoreRequest::GetQos() noexcept + Aws::Crt::Optional SubscribeToIoTCoreRequest::GetQos() const noexcept { if (!m_qos.has_value()) return Aws::Crt::Optional(); @@ -3895,7 +3962,7 @@ namespace Aws } } - Aws::Crt::Optional StopComponentResponse::GetStopStatus() noexcept + Aws::Crt::Optional StopComponentResponse::GetStopStatus() const noexcept { if (!m_stopStatus.has_value()) return Aws::Crt::Optional(); @@ -4217,7 +4284,7 @@ namespace Aws } } - Aws::Crt::Optional RestartComponentResponse::GetRestartStatus() noexcept + Aws::Crt::Optional RestartComponentResponse::GetRestartStatus() const noexcept { if (!m_restartStatus.has_value()) return Aws::Crt::Optional(); @@ -4691,7 +4758,7 @@ namespace Aws } } - Aws::Crt::Optional PublishToIoTCoreRequest::GetQos() noexcept + Aws::Crt::Optional PublishToIoTCoreRequest::GetQos() const noexcept { if (!m_qos.has_value()) return Aws::Crt::Optional(); @@ -4721,7 +4788,7 @@ namespace Aws } } - Aws::Crt::Optional PublishToIoTCoreRequest::GetPayloadFormat() noexcept + Aws::Crt::Optional PublishToIoTCoreRequest::GetPayloadFormat() const noexcept { if (!m_payloadFormat.has_value()) return Aws::Crt::Optional(); @@ -6469,7 +6536,8 @@ namespace Aws } } - Aws::Crt::Optional CreateLocalDeploymentRequest::GetFailureHandlingPolicy() noexcept + Aws::Crt::Optional CreateLocalDeploymentRequest::GetFailureHandlingPolicy() + const noexcept { if (!m_failureHandlingPolicy.has_value()) return Aws::Crt::Optional(); @@ -7211,9 +7279,9 @@ namespace Aws std::future SubscribeToConfigurationUpdateOperation::GetResult() noexcept { - return std::async(m_asyncLaunchMode, [this]() { - return SubscribeToConfigurationUpdateResult(GetOperationResult().get()); - }); + return std::async( + m_asyncLaunchMode, + [this]() { return SubscribeToConfigurationUpdateResult(GetOperationResult().get()); }); } SubscribeToConfigurationUpdateOperation::SubscribeToConfigurationUpdateOperation( @@ -7516,9 +7584,9 @@ namespace Aws std::future SubscribeToValidateConfigurationUpdatesOperation:: GetResult() noexcept { - return std::async(m_asyncLaunchMode, [this]() { - return SubscribeToValidateConfigurationUpdatesResult(GetOperationResult().get()); - }); + return std::async( + m_asyncLaunchMode, + [this]() { return SubscribeToValidateConfigurationUpdatesResult(GetOperationResult().get()); }); } SubscribeToValidateConfigurationUpdatesOperation::SubscribeToValidateConfigurationUpdatesOperation( @@ -8003,9 +8071,9 @@ namespace Aws std::future SubscribeToCertificateUpdatesOperation::GetResult() noexcept { - return std::async(m_asyncLaunchMode, [this]() { - return SubscribeToCertificateUpdatesResult(GetOperationResult().get()); - }); + return std::async( + m_asyncLaunchMode, + [this]() { return SubscribeToCertificateUpdatesResult(GetOperationResult().get()); }); } SubscribeToCertificateUpdatesOperation::SubscribeToCertificateUpdatesOperation( @@ -8418,9 +8486,9 @@ namespace Aws std::future SendConfigurationValidityReportOperation:: GetResult() noexcept { - return std::async(m_asyncLaunchMode, [this]() { - return SendConfigurationValidityReportResult(GetOperationResult().get()); - }); + return std::async( + m_asyncLaunchMode, + [this]() { return SendConfigurationValidityReportResult(GetOperationResult().get()); }); } SendConfigurationValidityReportOperation::SendConfigurationValidityReportOperation( From 34b1e291716a0e3782993e080ac8ef65e98c0133 Mon Sep 17 00:00:00 2001 From: Bret Ambrose Date: Fri, 25 Apr 2025 12:18:30 -0700 Subject: [PATCH 29/43] More tests --- eventstream_rpc/tests/CMakeLists.txt | 9 + .../tests/EventStreamClientTest.cpp | 250 ++++++++++++++++-- 2 files changed, 242 insertions(+), 17 deletions(-) diff --git a/eventstream_rpc/tests/CMakeLists.txt b/eventstream_rpc/tests/CMakeLists.txt index 456a3368d..3d5838d63 100644 --- a/eventstream_rpc/tests/CMakeLists.txt +++ b/eventstream_rpc/tests/CMakeLists.txt @@ -39,6 +39,15 @@ add_test_case(EchoClientDoubleClose) add_test_case(EchoClientMultiConnectSuccessFail) add_test_case(EchoClientOperationEchoSuccessString) +add_test_case(EchoClientOperationEchoSuccessBoolean) +add_test_case(EchoClientOperationEchoSuccessTime) +add_test_case(EchoClientOperationEchoSuccessDocument) +add_test_case(EchoClientOperationEchoSuccessEnum) +add_test_case(EchoClientOperationEchoSuccessBlob) +add_test_case(EchoClientOperationEchoSuccessStringList) +add_test_case(EchoClientOperationEchoSuccessPairList) +add_test_case(EchoClientOperationEchoSuccessProductMap) + add_test_case(EchoClientOperationEchoSuccessMultiple) add_test_case(EchoClientOperationEchoFailureNeverConnected) add_test_case(EchoClientOperationEchoFailureDisconnected) diff --git a/eventstream_rpc/tests/EventStreamClientTest.cpp b/eventstream_rpc/tests/EventStreamClientTest.cpp index 5f50258dd..e867a24a9 100644 --- a/eventstream_rpc/tests/EventStreamClientTest.cpp +++ b/eventstream_rpc/tests/EventStreamClientTest.cpp @@ -290,26 +290,129 @@ static void s_onMessageFlush(int errorCode) (void)errorCode; } -#define DEFINE_CHECK_MESSAGE_DATA_MEMBER_EQUALITY_FN(TYPENAME) \ - static int s_checkMessageDataMemberEquality( \ - Aws::Crt::Optional expectedValue, Aws::Crt::Optional actualValue) \ - { \ - ASSERT_TRUE(expectedValue.has_value() == actualValue.has_value()); \ - if (expectedValue.has_value()) \ - { \ - ASSERT_TRUE(expectedValue.value() == actualValue.value()); \ - } \ - \ - return AWS_OP_SUCCESS; \ - } - -DEFINE_CHECK_MESSAGE_DATA_MEMBER_EQUALITY_FN(Aws::Crt::String) -DEFINE_CHECK_MESSAGE_DATA_MEMBER_EQUALITY_FN(bool) +template +static bool s_messageDataMembersAreEqual(Aws::Crt::Optional expectedValue, Aws::Crt::Optional actualValue) +{ + if (expectedValue.has_value() != actualValue.has_value()) + { + return false; + } + + if (expectedValue.has_value()) + { + return expectedValue.value() == actualValue.value(); + } + + return true; +} + +// Specialization for Vector since we don't codegen == for Shapes +static bool s_messageDataMembersAreEqual( + const Aws::Crt::Optional> &lhs, + const Aws::Crt::Optional> &rhs) +{ + if (lhs.has_value() != rhs.has_value()) + { + return false; + } + + if (!lhs.has_value()) + { + return true; + } + + if (lhs.value().size() != rhs.value().size()) + { + return false; + } + + for (size_t i = 0; i < lhs.value().size(); ++i) + { + const auto &lhs_pair = (lhs.value())[i]; + const auto &rhs_pair = (rhs.value())[i]; + + if (lhs_pair.GetKey().has_value() != rhs_pair.GetKey().has_value()) + { + return false; + } + + if (lhs_pair.GetValue().has_value() != rhs_pair.GetValue().has_value()) + { + return false; + } + + if (lhs_pair.GetKey().value() != rhs_pair.GetKey().value() || + lhs_pair.GetValue().value() != rhs_pair.GetValue().value()) + { + return false; + } + } + + return true; +} + +// Specialization for Map since we don't codegen == for Shapes +static bool s_messageDataMembersAreEqual( + const Aws::Crt::Optional> &lhs, + const Aws::Crt::Optional> &rhs) +{ + if (lhs.has_value() != rhs.has_value()) + { + return false; + } + + if (!lhs.has_value()) + { + return true; + } + + if (lhs.value().size() != rhs.value().size()) + { + return false; + } + + for (const auto &lhs_entry : lhs.value()) + { + const auto &rhs_entry = rhs.value().find(lhs_entry.first); + if (rhs_entry == rhs.value().end()) + { + return false; + } + + const auto &lhs_product = lhs_entry.second; + const auto &rhs_product = rhs_entry->second; + + if (lhs_product.GetName().has_value() != rhs_product.GetName().has_value()) + { + return false; + } + + if (lhs_product.GetPrice().has_value() != rhs_product.GetPrice().has_value()) + { + return false; + } + + if (lhs_product.GetName().value() != rhs_product.GetName().value() || + lhs_product.GetPrice().value() != rhs_product.GetPrice().value()) + { + return false; + } + } + + return true; +} static int s_checkMessageDataEquality(const MessageData &expectedData, const MessageData &actualData) { - ASSERT_SUCCESS(s_checkMessageDataMemberEquality(expectedData.GetStringMessage(), actualData.GetStringMessage())); - ASSERT_SUCCESS(s_checkMessageDataMemberEquality(expectedData.GetBooleanMessage(), actualData.GetBooleanMessage())); + ASSERT_TRUE(s_messageDataMembersAreEqual(expectedData.GetStringMessage(), actualData.GetStringMessage())); + ASSERT_TRUE(s_messageDataMembersAreEqual(expectedData.GetBooleanMessage(), actualData.GetBooleanMessage())); + ASSERT_TRUE(s_messageDataMembersAreEqual(expectedData.GetTimeMessage(), actualData.GetTimeMessage())); + ASSERT_TRUE(s_messageDataMembersAreEqual(expectedData.GetDocumentMessage(), actualData.GetDocumentMessage())); + ASSERT_TRUE(s_messageDataMembersAreEqual(expectedData.GetEnumMessage(), actualData.GetEnumMessage())); + ASSERT_TRUE(s_messageDataMembersAreEqual(expectedData.GetBlobMessage(), actualData.GetBlobMessage())); + ASSERT_TRUE(s_messageDataMembersAreEqual(expectedData.GetStringListMessage(), actualData.GetStringListMessage())); + ASSERT_TRUE(s_messageDataMembersAreEqual(expectedData.GetKeyValuePairList(), actualData.GetKeyValuePairList())); + ASSERT_TRUE(s_messageDataMembersAreEqual(expectedData.GetStringToValue(), actualData.GetStringToValue())); return AWS_OP_SUCCESS; } @@ -364,6 +467,119 @@ static int s_TestEchoClientOperationEchoSuccessString(struct aws_allocator *allo AWS_TEST_CASE(EchoClientOperationEchoSuccessString, s_TestEchoClientOperationEchoSuccessString); +static int s_TestEchoClientOperationEchoSuccessBoolean(struct aws_allocator *allocator, void *ctx) +{ + return s_DoTestEchoClientOperationEchoSuccess( + allocator, [](MessageData &messageData) { messageData.SetBooleanMessage(true); }); +} + +AWS_TEST_CASE(EchoClientOperationEchoSuccessBoolean, s_TestEchoClientOperationEchoSuccessBoolean); + +static int s_TestEchoClientOperationEchoSuccessTime(struct aws_allocator *allocator, void *ctx) +{ + return s_DoTestEchoClientOperationEchoSuccess( + allocator, [](MessageData &messageData) { messageData.SetTimeMessage(Aws::Crt::DateTime::Now()); }); +} + +AWS_TEST_CASE(EchoClientOperationEchoSuccessTime, s_TestEchoClientOperationEchoSuccessTime); + +static int s_TestEchoClientOperationEchoSuccessDocument(struct aws_allocator *allocator, void *ctx) +{ + return s_DoTestEchoClientOperationEchoSuccess( + allocator, + [](MessageData &messageData) + { + Aws::Crt::JsonObject subobject; + subobject.WithString("Hello", "There"); + Aws::Crt::JsonObject document; + document.WithInt64("Derp", 21); + document.WithObject("DailyAffirmations", subobject); + + messageData.SetDocumentMessage(document); + }); +} + +AWS_TEST_CASE(EchoClientOperationEchoSuccessDocument, s_TestEchoClientOperationEchoSuccessDocument); + +static int s_TestEchoClientOperationEchoSuccessEnum(struct aws_allocator *allocator, void *ctx) +{ + return s_DoTestEchoClientOperationEchoSuccess( + allocator, [](MessageData &messageData) { messageData.SetEnumMessage(FruitEnum::FRUIT_ENUM_PINEAPPLE); }); +} + +AWS_TEST_CASE(EchoClientOperationEchoSuccessEnum, s_TestEchoClientOperationEchoSuccessEnum); + +static int s_TestEchoClientOperationEchoSuccessBlob(struct aws_allocator *allocator, void *ctx) +{ + return s_DoTestEchoClientOperationEchoSuccess( + allocator, + [](MessageData &messageData) + { + Aws::Crt::Vector blob = {1, 2, 3, 4, 5}; + messageData.SetBlobMessage(blob); + }); +} + +AWS_TEST_CASE(EchoClientOperationEchoSuccessBlob, s_TestEchoClientOperationEchoSuccessBlob); + +static int s_TestEchoClientOperationEchoSuccessStringList(struct aws_allocator *allocator, void *ctx) +{ + return s_DoTestEchoClientOperationEchoSuccess( + allocator, + [](MessageData &messageData) + { + Aws::Crt::Vector stringList = {"1", "2", "Toasty", "Mctoaster"}; + messageData.SetStringListMessage(stringList); + }); +} + +AWS_TEST_CASE(EchoClientOperationEchoSuccessStringList, s_TestEchoClientOperationEchoSuccessStringList); + +static int s_TestEchoClientOperationEchoSuccessPairList(struct aws_allocator *allocator, void *ctx) +{ + return s_DoTestEchoClientOperationEchoSuccess( + allocator, + [](MessageData &messageData) + { + Pair pair1; + pair1.SetKey("Uff"); + pair1.SetValue("Dah"); + + Pair pair2; + pair2.SetKey("Hello"); + pair2.SetValue("World"); + + Aws::Crt::Vector pairList = {pair1, pair2}; + messageData.SetKeyValuePairList(pairList); + }); +} + +AWS_TEST_CASE(EchoClientOperationEchoSuccessPairList, s_TestEchoClientOperationEchoSuccessPairList); + +static int s_TestEchoClientOperationEchoSuccessProductMap(struct aws_allocator *allocator, void *ctx) +{ + return s_DoTestEchoClientOperationEchoSuccess( + allocator, + [](MessageData &messageData) + { + Aws::Crt::Map productMap = {}; + Product product1; + product1.SetName("Derp"); + product1.SetPrice(4.0); + + Product product2; + product2.SetName("Can Of Derp"); + product2.SetPrice(7.5); + + productMap[product1.GetName().value()] = product1; + productMap[product2.GetName().value()] = product2; + + messageData.SetStringToValue(productMap); + }); +} + +AWS_TEST_CASE(EchoClientOperationEchoSuccessProductMap, s_TestEchoClientOperationEchoSuccessProductMap); + static int s_TestEchoClientOperationEchoSuccessMultiple(struct aws_allocator *allocator, void *ctx) { ApiHandle apiHandle(allocator); From 1d0236206204ca8dd5e5b7053eca7c7d00681ee0 Mon Sep 17 00:00:00 2001 From: Bret Ambrose Date: Fri, 25 Apr 2025 12:55:52 -0700 Subject: [PATCH 30/43] Weird x86 compile error --- eventstream_rpc/tests/EventStreamClientTest.cpp | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/eventstream_rpc/tests/EventStreamClientTest.cpp b/eventstream_rpc/tests/EventStreamClientTest.cpp index e867a24a9..f54aa31df 100644 --- a/eventstream_rpc/tests/EventStreamClientTest.cpp +++ b/eventstream_rpc/tests/EventStreamClientTest.cpp @@ -306,6 +306,21 @@ static bool s_messageDataMembersAreEqual(Aws::Crt::Optional expectedValue, Aw return true; } +static bool s_messageDataMembersAreEqual(Aws::Crt::Optional expectedValue, Aws::Crt::Optional actualValue) +{ + if (expectedValue.has_value() != actualValue.has_value()) + { + return false; + } + + if (expectedValue.has_value()) + { + return expectedValue.value() == actualValue.value(); + } + + return true; +} + // Specialization for Vector since we don't codegen == for Shapes static bool s_messageDataMembersAreEqual( const Aws::Crt::Optional> &lhs, From 7cdbd3924caa4e3c46840b95ff046fc58fd66c94 Mon Sep 17 00:00:00 2001 From: Bret Ambrose Date: Thu, 8 May 2025 14:28:40 -0700 Subject: [PATCH 31/43] Checkpoint before refactoring impl --- .../aws/eventstreamrpc/EventStreamClient.h | 218 ++---- eventstream_rpc/source/EventStreamClient.cpp | 620 ++++++++++-------- eventstream_rpc/tests/CMakeLists.txt | 26 +- .../tests/EventStreamClientTest.cpp | 17 +- 4 files changed, 429 insertions(+), 452 deletions(-) diff --git a/eventstream_rpc/include/aws/eventstreamrpc/EventStreamClient.h b/eventstream_rpc/include/aws/eventstreamrpc/EventStreamClient.h index ccd464eb9..1414409dd 100644 --- a/eventstream_rpc/include/aws/eventstreamrpc/EventStreamClient.h +++ b/eventstream_rpc/include/aws/eventstreamrpc/EventStreamClient.h @@ -41,6 +41,7 @@ namespace Aws class ClientOperation; class ClientConnection; class ClientContinuation; + class ClientContinuationHandler; using HeaderValueType = aws_event_stream_header_value_type; using MessageType = aws_event_stream_rpc_message_type; @@ -212,10 +213,11 @@ namespace Aws struct AWS_EVENTSTREAMRPC_API RpcError { + explicit operator bool() const noexcept { return baseStatus == EVENT_STREAM_RPC_SUCCESS; } + Crt::String StatusToString(); + EventStreamRpcStatusCode baseStatus; int crtError; - operator bool() const noexcept { return baseStatus == EVENT_STREAM_RPC_SUCCESS; } - Crt::String StatusToString(); }; /** @@ -232,20 +234,21 @@ namespace Aws * is invoked, the `ClientConnection` is ready to be used for sending messages. */ virtual void OnConnectCallback(); + /** * Invoked upon connection shutdown. * @param status The status upon disconnection. It can be treated as a bool * with true implying a successful disconnection. */ virtual void OnDisconnectCallback(RpcError status); + /** * Invoked upon receiving an error. Use the return value to determine - * whether or not to force the connection to close. Keep in mind that once - * closed, the `ClientConnection` can no longer send messages. - * @param status The status upon disconnection. It can be treated as a bool - * with true implying a successful disconnection. + * whether or not to force the connection to close. + * @param status Details about the error encountered. */ virtual bool OnErrorCallback(RpcError status); + /** * Invoked upon receiving a ping from the server. The `headers` and `payload` * refer to what is contained in the ping message. @@ -255,6 +258,61 @@ namespace Aws const Crt::Optional &payload); }; + + class ClientConnectionImpl; + + /** + * Class representing a connection to an RPC server. + */ + class AWS_EVENTSTREAMRPC_API ClientConnection final + { + public: + + explicit ClientConnection(Crt::Allocator *allocator = Crt::g_allocator) noexcept; + ~ClientConnection() noexcept; + + /** + * Initiates a new outgoing event-stream-rpc connection. + * @param connectionOptions Connection options. + * @param connectionLifecycleHandler Handler to process connection lifecycle events. + * @param clientBootstrap ClientBootstrap object to run the connection on. + * @return Future that will be resolved when connection either succeeds or fails. + */ + std::future Connect( + const ConnectionConfig &connectionOptions, + ConnectionLifecycleHandler *connectionLifecycleHandler, + Crt::Io::ClientBootstrap &clientBootstrap) noexcept; + + /** + * Create a new stream. + * @note Activate() must be called on the stream for it to actually initiate the new stream. + * @param clientContinuationHandler Handler to process continuation events. + * @return A newly created continuation. + */ + ClientContinuation NewStream(ClientContinuationHandler &clientContinuationHandler) noexcept; + + /** + * Close the connection. + */ + void Close() noexcept; + + /** + * Check if the connection is open. + * @return True if the connection is open, false otherwise. + */ + bool IsOpen() const noexcept; + + /** + * Returns the C connection object, if it exists. + * @return the C connection object, if it exists. + */ + struct aws_event_stream_rpc_client_connection *GetUnderlyingHandle() const noexcept; + + private: + + std::shared_ptr m_impl; + }; + /** * User data passed to callbacks for a new stream. */ @@ -301,7 +359,7 @@ namespace Aws private: friend class ClientContinuation; - ContinuationCallbackData *m_callbackData; + std::shared_ptr m_callbackData; }; /** @@ -320,7 +378,7 @@ namespace Aws * @param allocator Allocator to use. */ ClientContinuation( - ClientConnection *connection, + struct aws_event_stream_rpc_client_connection *connection, ClientContinuationHandler &continuationHandler, Crt::Allocator *allocator) noexcept; ~ClientContinuation() noexcept; @@ -372,7 +430,9 @@ namespace Aws Crt::Allocator *m_allocator; ClientContinuationHandler &m_continuationHandler; struct aws_event_stream_rpc_client_continuation_token *m_continuationToken; - ContinuationCallbackData *m_callbackData; + std::shared_ptr m_callbackData; + + void Release(); static void s_onContinuationMessage( struct aws_event_stream_rpc_client_continuation_token *continuationToken, @@ -722,146 +782,6 @@ namespace Aws std::condition_variable m_closeReady; }; - /** - * Class representing a connection to an RPC server. - */ - class AWS_EVENTSTREAMRPC_API ClientConnection final - { - public: - ClientConnection(Crt::Allocator *allocator = Crt::g_allocator) noexcept; - ~ClientConnection() noexcept; - ClientConnection(const ClientConnection &) noexcept = delete; - ClientConnection &operator=(const ClientConnection &) noexcept = delete; - ClientConnection(ClientConnection &&) noexcept; - ClientConnection &operator=(ClientConnection &&) noexcept; - /** - * Initiates a new outgoing event-stream-rpc connection. - * @param connectionOptions Connection options. - * @param connectionLifecycleHandler Handler to process connection lifecycle events. - * @param clientBootstrap ClientBootstrap object to run the connection on. - * @return Future that will be resolved when connection either succeeds or fails. - */ - std::future Connect( - const ConnectionConfig &connectionOptions, - ConnectionLifecycleHandler *connectionLifecycleHandler, - Crt::Io::ClientBootstrap &clientBootstrap) noexcept; - - std::future SendPing( - const Crt::List &headers, - const Crt::Optional &payload, - OnMessageFlushCallback onMessageFlushCallback) noexcept; - - std::future SendPingResponse( - const Crt::List &headers, - const Crt::Optional &payload, - OnMessageFlushCallback onMessageFlushCallback) noexcept; - - /** - * Create a new stream. - * @note Activate() must be called on the stream for it to actually initiate the new stream. - * @param clientContinuationHandler Handler to process continuation events. - * @return A newly created continuation. - */ - ClientContinuation NewStream(ClientContinuationHandler &clientContinuationHandler) noexcept; - - /** - * Close the connection. - */ - void Close() noexcept; - - /** - * Check if the connection is open. - * @return True if the connection is open, false otherwise. - */ - bool IsOpen() const noexcept - { - if (this->m_underlyingConnection == nullptr) - { - return false; - } - else - { - return aws_event_stream_rpc_client_connection_is_open(this->m_underlyingConnection); - } - } - - /** - * @return true if the connection is open, false otherwise. - */ - operator bool() const noexcept { return IsOpen(); } - - private: - friend class ClientContinuation; - friend std::future ClientOperation::Close(OnMessageFlushCallback onMessageFlushCallback) noexcept; - enum ClientState - { - DISCONNECTED = 1, - CONNECTING_SOCKET, - WAITING_FOR_CONNECT_ACK, - CONNECTED, - DISCONNECTING, - }; - /* This recursive mutex protects m_clientState & m_connectionWillSetup */ - std::recursive_mutex m_stateMutex; - Crt::Allocator *m_allocator; - struct aws_event_stream_rpc_client_connection *m_underlyingConnection; - ClientState m_clientState; - ConnectionLifecycleHandler *m_lifecycleHandler; - ConnectMessageAmender m_connectMessageAmender; - std::promise m_connectionSetupPromise; - bool m_connectionWillSetup; - std::promise m_connectAckedPromise; - std::promise m_closedPromise; - bool m_onConnectCalled; - RpcError m_closeReason; - OnMessageFlushCallback m_onConnectRequestCallback; - Crt::Io::SocketOptions m_socketOptions; - ConnectionConfig m_connectionConfig; - std::future SendProtocolMessage( - const Crt::List &headers, - const Crt::Optional &payload, - MessageType messageType, - uint32_t messageFlags, - OnMessageFlushCallback onMessageFlushCallback) noexcept; - - static void s_onConnectionShutdown( - struct aws_event_stream_rpc_client_connection *connection, - int errorCode, - void *userData) noexcept; - static void s_onConnectionSetup( - struct aws_event_stream_rpc_client_connection *connection, - int errorCode, - void *userData) noexcept; - static void s_onProtocolMessage( - struct aws_event_stream_rpc_client_connection *connection, - const struct aws_event_stream_rpc_message_args *messageArgs, - void *userData) noexcept; - - static void s_protocolMessageCallback(int errorCode, void *userData) noexcept; - - /** - * Sends a message on the connection. These must be connection level messages (not application messages). - */ - static std::future s_sendProtocolMessage( - ClientConnection *connection, - const Crt::List &headers, - const Crt::Optional &payload, - MessageType messageType, - uint32_t messageFlags, - OnMessageFlushCallback onMessageFlushCallback) noexcept; - - static std::future s_sendPing( - ClientConnection *connection, - const Crt::List &headers, - const Crt::Optional &payload, - OnMessageFlushCallback onMessageFlushCallback) noexcept; - - static std::future s_sendPingResponse( - ClientConnection *connection, - const Crt::List &headers, - const Crt::Optional &payload, - OnMessageFlushCallback onMessageFlushCallback) noexcept; - }; } // namespace Eventstreamrpc } // namespace Aws diff --git a/eventstream_rpc/source/EventStreamClient.cpp b/eventstream_rpc/source/EventStreamClient.cpp index 65983178e..e14bbdeb1 100644 --- a/eventstream_rpc/source/EventStreamClient.cpp +++ b/eventstream_rpc/source/EventStreamClient.cpp @@ -164,76 +164,260 @@ namespace Aws } } - class EventStreamCppToNativeCrtBuilder + static int s_fillNativeHeadersArray( + const Crt::List &headers, + struct aws_array_list *headersArray, + Crt::Allocator *m_allocator = Crt::g_allocator) { - private: - friend class ClientConnection; - friend class ClientContinuation; - static int s_fillNativeHeadersArray( - const Crt::List &headers, - struct aws_array_list *headersArray, - Crt::Allocator *m_allocator = Crt::g_allocator) - { - AWS_ZERO_STRUCT(*headersArray); - /* Check if the connection has expired before attempting to send. */ - int errorCode = aws_event_stream_headers_list_init(headersArray, m_allocator); + AWS_ZERO_STRUCT(*headersArray); + /* Check if the connection has expired before attempting to send. */ + int errorCode = aws_event_stream_headers_list_init(headersArray, m_allocator); - if (!errorCode) + if (!errorCode) + { + /* Populate the array with the underlying handle of each EventStreamHeader. */ + for (auto &i : headers) { - /* Populate the array with the underlying handle of each EventStreamHeader. */ - for (auto &i : headers) - { - errorCode = aws_array_list_push_back(headersArray, i.GetUnderlyingHandle()); + errorCode = aws_array_list_push_back(headersArray, i.GetUnderlyingHandle()); - if (errorCode) - { - break; - } + if (errorCode) + { + break; } } + } + + return errorCode; + } - return errorCode; + Crt::String RpcError::StatusToString() + { + switch (baseStatus) + { + case EVENT_STREAM_RPC_SUCCESS: + return "EVENT_STREAM_RPC_SUCCESS"; + case EVENT_STREAM_RPC_NULL_PARAMETER: + return "EVENT_STREAM_RPC_NULL_PARAMETER"; + case EVENT_STREAM_RPC_UNINITIALIZED: + return "EVENT_STREAM_RPC_UNINITIALIZED"; + case EVENT_STREAM_RPC_ALLOCATION_ERROR: + return "EVENT_STREAM_RPC_ALLOCATION_ERROR"; + case EVENT_STREAM_RPC_CONNECTION_SETUP_FAILED: + return "EVENT_STREAM_RPC_CONNECTION_SETUP_FAILED"; + case EVENT_STREAM_RPC_CONNECTION_ACCESS_DENIED: + return "EVENT_STREAM_RPC_CONNECTION_ACCESS_DENIED"; + case EVENT_STREAM_RPC_CONNECTION_ALREADY_ESTABLISHED: + return "EVENT_STREAM_RPC_CONNECTION_ALREADY_ESTABLISHED"; + case EVENT_STREAM_RPC_CONNECTION_CLOSED: + return "EVENT_STREAM_RPC_CONNECTION_CLOSED"; + case EVENT_STREAM_RPC_CONTINUATION_CLOSED: + return "EVENT_STREAM_RPC_CONTINUATION_CLOSED"; + case EVENT_STREAM_RPC_UNKNOWN_PROTOCOL_MESSAGE: + return "EVENT_STREAM_RPC_UNKNOWN_PROTOCOL_MESSAGE"; + case EVENT_STREAM_RPC_UNMAPPED_DATA: + return "EVENT_STREAM_RPC_UNMAPPED_DATA"; + case EVENT_STREAM_RPC_UNSUPPORTED_CONTENT_TYPE: + return "EVENT_STREAM_RPC_UNSUPPORTED_CONTENT_TYPE"; + case EVENT_STREAM_RPC_CRT_ERROR: + Crt::String ret = "Failed with EVENT_STREAM_RPC_CRT_ERROR, the CRT error was "; + ret += Crt::ErrorDebugString(crtError); + return ret; } - }; + return "Unknown status code"; + } + + bool ConnectionLifecycleHandler::OnErrorCallback(RpcError error) + { + (void)error; + /* Returning true implies that the connection should close as a result of encountering this error. */ + return true; + } - ClientConnection &ClientConnection::operator=(ClientConnection &&rhs) noexcept + void ConnectionLifecycleHandler::OnPingCallback( + const Crt::List &headers, + const Crt::Optional &payload) { - m_allocator = std::move(rhs.m_allocator); - m_underlyingConnection = rhs.m_underlyingConnection; - rhs.m_stateMutex.lock(); - m_clientState = rhs.m_clientState; - rhs.m_stateMutex.unlock(); - m_lifecycleHandler = rhs.m_lifecycleHandler; - m_connectMessageAmender = rhs.m_connectMessageAmender; - m_connectAckedPromise = std::move(rhs.m_connectAckedPromise); - m_closedPromise = std::move(rhs.m_closedPromise); - m_onConnectRequestCallback = rhs.m_onConnectRequestCallback; + (void)headers; + (void)payload; + } - /* Reset rhs. */ - rhs.m_allocator = nullptr; - rhs.m_underlyingConnection = nullptr; - rhs.m_clientState = DISCONNECTED; - rhs.m_lifecycleHandler = nullptr; - rhs.m_connectMessageAmender = nullptr; - rhs.m_closedPromise = {}; - rhs.m_onConnectRequestCallback = nullptr; + void ConnectionLifecycleHandler::OnConnectCallback() {} + + void ConnectionLifecycleHandler::OnDisconnectCallback(RpcError error) + { + (void)error; + } + + EventStreamHeader::EventStreamHeader( + const struct aws_event_stream_header_value_pair &header, + Crt::Allocator *allocator) + : m_allocator(allocator), m_valueByteBuf({}), m_underlyingHandle(header) + { + } + EventStreamHeader::EventStreamHeader( + const Crt::String &name, + const Crt::String &value, + Crt::Allocator *allocator) noexcept + : m_allocator(allocator), + m_valueByteBuf(Crt::ByteBufNewCopy(allocator, (uint8_t *)value.c_str(), value.length())) + { + m_underlyingHandle.header_name_len = static_cast(name.length()); + size_t length; + if (name.length() > INT8_MAX) + { + length = INT8_MAX; + } + else + { + length = static_cast(name.length()); + } + (void)memcpy(m_underlyingHandle.header_name, name.c_str(), length); + m_underlyingHandle.header_value_type = AWS_EVENT_STREAM_HEADER_STRING; + m_underlyingHandle.header_value.variable_len_val = m_valueByteBuf.buffer; + m_underlyingHandle.header_value_len = (uint16_t)m_valueByteBuf.len; + } + + EventStreamHeader::~EventStreamHeader() noexcept + { + if (aws_byte_buf_is_valid(&m_valueByteBuf)) + Crt::ByteBufDelete(m_valueByteBuf); + } + + EventStreamHeader::EventStreamHeader(const EventStreamHeader &lhs) noexcept + : m_allocator(lhs.m_allocator), + m_valueByteBuf(Crt::ByteBufNewCopy(lhs.m_allocator, lhs.m_valueByteBuf.buffer, lhs.m_valueByteBuf.len)), + m_underlyingHandle(lhs.m_underlyingHandle) + { + m_underlyingHandle.header_value.variable_len_val = m_valueByteBuf.buffer; + m_underlyingHandle.header_value_len = static_cast(m_valueByteBuf.len); + } + + EventStreamHeader &EventStreamHeader::operator=(const EventStreamHeader &lhs) noexcept + { + m_allocator = lhs.m_allocator; + m_valueByteBuf = Crt::ByteBufNewCopy(lhs.m_allocator, lhs.m_valueByteBuf.buffer, lhs.m_valueByteBuf.len); + m_underlyingHandle = lhs.m_underlyingHandle; + m_underlyingHandle.header_value.variable_len_val = m_valueByteBuf.buffer; + m_underlyingHandle.header_value_len = static_cast(m_valueByteBuf.len); return *this; } - ClientConnection::ClientConnection(ClientConnection &&rhs) noexcept : m_lifecycleHandler(rhs.m_lifecycleHandler) + EventStreamHeader::EventStreamHeader(EventStreamHeader &&rhs) noexcept + : m_allocator(rhs.m_allocator), m_valueByteBuf(rhs.m_valueByteBuf), + m_underlyingHandle(rhs.m_underlyingHandle) + { + rhs.m_valueByteBuf.allocator = nullptr; + rhs.m_valueByteBuf.buffer = nullptr; + } + + const struct aws_event_stream_header_value_pair *EventStreamHeader::GetUnderlyingHandle() const { - *this = std::move(rhs); + return &m_underlyingHandle; } - ClientConnection::ClientConnection(Crt::Allocator *allocator) noexcept + Crt::String EventStreamHeader::GetHeaderName() const noexcept + { + return Crt::String(m_underlyingHandle.header_name, m_underlyingHandle.header_name_len, m_allocator); + } + + bool EventStreamHeader::GetValueAsString(Crt::String &value) const noexcept + { + if (m_underlyingHandle.header_value_type != AWS_EVENT_STREAM_HEADER_STRING) + { + return false; + } + value = Crt::String( + reinterpret_cast(m_underlyingHandle.header_value.variable_len_val), + m_underlyingHandle.header_value_len, + m_allocator); + + return true; + } + + class ClientConnectionImpl final + { + public: + + explicit ClientConnectionImpl(Crt::Allocator *allocator = Crt::g_allocator) noexcept; + ~ClientConnectionImpl() noexcept; + + std::future Connect( + const ConnectionConfig &connectionOptions, + ConnectionLifecycleHandler *connectionLifecycleHandler, + Crt::Io::ClientBootstrap &clientBootstrap) noexcept; + + ClientContinuation NewStream(ClientContinuationHandler &clientContinuationHandler) noexcept; + + void Close() noexcept; + + bool IsOpen() const noexcept; + + struct aws_event_stream_rpc_client_connection *GetUnderlyingHandle() const noexcept; + + void Shutdown() noexcept; + + private: + + enum ClientState + { + DISCONNECTED = 1, + CONNECTING_SOCKET, + WAITING_FOR_CONNECT_ACK, + CONNECTED, + DISCONNECTING, + }; + /* This recursive mutex protects m_clientState & m_connectionWillSetup */ + std::recursive_mutex m_stateMutex; + Crt::Allocator *m_allocator; + struct aws_event_stream_rpc_client_connection *m_underlyingConnection; + ClientState m_clientState; + ConnectionLifecycleHandler *m_lifecycleHandler; + ConnectMessageAmender m_connectMessageAmender; + std::promise m_connectionSetupPromise; + bool m_connectionWillSetup; + std::promise m_connectAckedPromise; + std::promise m_closedPromise; + bool m_onConnectCalled; + RpcError m_closeReason; + OnMessageFlushCallback m_onConnectRequestCallback; + Crt::Io::SocketOptions m_socketOptions; + ConnectionConfig m_connectionConfig; + + static std::future s_sendProtocolMessage( + ClientConnectionImpl *connectionImpl, + const Crt::List &headers, + const Crt::Optional &payload, + MessageType messageType, + uint32_t messageFlags, + OnMessageFlushCallback onMessageFlushCallback) noexcept; + + static void s_onConnectionShutdown( + struct aws_event_stream_rpc_client_connection *connection, + int errorCode, + void *userData) noexcept; + + static void s_onConnectionSetup( + struct aws_event_stream_rpc_client_connection *connection, + int errorCode, + void *userData) noexcept; + + static void s_onProtocolMessage( + struct aws_event_stream_rpc_client_connection *connection, + const struct aws_event_stream_rpc_message_args *messageArgs, + void *userData) noexcept; + + }; + + ClientConnectionImpl::ClientConnectionImpl(Crt::Allocator *allocator) noexcept : m_allocator(allocator), m_underlyingConnection(nullptr), m_clientState(DISCONNECTED), m_lifecycleHandler(nullptr), m_connectMessageAmender(nullptr), m_connectionWillSetup(false), + m_onConnectCalled(false), m_closeReason{EVENT_STREAM_RPC_SUCCESS, AWS_ERROR_SUCCESS}, m_onConnectRequestCallback(nullptr) { } - ClientConnection::~ClientConnection() noexcept + ClientConnectionImpl::~ClientConnectionImpl() noexcept { m_stateMutex.lock(); bool waitForSetup = m_connectionWillSetup; @@ -261,65 +445,12 @@ namespace Aws m_underlyingConnection = nullptr; } - bool ConnectionLifecycleHandler::OnErrorCallback(RpcError error) + void ClientConnectionImpl::Shutdown() noexcept { - (void)error; - /* Returning true implies that the connection should close as a result of encountering this error. */ - return true; - } - void ConnectionLifecycleHandler::OnPingCallback( - const Crt::List &headers, - const Crt::Optional &payload) - { - (void)headers; - (void)payload; - } - - void ConnectionLifecycleHandler::OnConnectCallback() {} - - void ConnectionLifecycleHandler::OnDisconnectCallback(RpcError error) - { - (void)error; } - Crt::String RpcError::StatusToString() - { - switch (baseStatus) - { - case EVENT_STREAM_RPC_SUCCESS: - return "EVENT_STREAM_RPC_SUCCESS"; - case EVENT_STREAM_RPC_NULL_PARAMETER: - return "EVENT_STREAM_RPC_NULL_PARAMETER"; - case EVENT_STREAM_RPC_UNINITIALIZED: - return "EVENT_STREAM_RPC_UNINITIALIZED"; - case EVENT_STREAM_RPC_ALLOCATION_ERROR: - return "EVENT_STREAM_RPC_ALLOCATION_ERROR"; - case EVENT_STREAM_RPC_CONNECTION_SETUP_FAILED: - return "EVENT_STREAM_RPC_CONNECTION_SETUP_FAILED"; - case EVENT_STREAM_RPC_CONNECTION_ACCESS_DENIED: - return "EVENT_STREAM_RPC_CONNECTION_ACCESS_DENIED"; - case EVENT_STREAM_RPC_CONNECTION_ALREADY_ESTABLISHED: - return "EVENT_STREAM_RPC_CONNECTION_ALREADY_ESTABLISHED"; - case EVENT_STREAM_RPC_CONNECTION_CLOSED: - return "EVENT_STREAM_RPC_CONNECTION_CLOSED"; - case EVENT_STREAM_RPC_CONTINUATION_CLOSED: - return "EVENT_STREAM_RPC_CONTINUATION_CLOSED"; - case EVENT_STREAM_RPC_UNKNOWN_PROTOCOL_MESSAGE: - return "EVENT_STREAM_RPC_UNKNOWN_PROTOCOL_MESSAGE"; - case EVENT_STREAM_RPC_UNMAPPED_DATA: - return "EVENT_STREAM_RPC_UNMAPPED_DATA"; - case EVENT_STREAM_RPC_UNSUPPORTED_CONTENT_TYPE: - return "EVENT_STREAM_RPC_UNSUPPORTED_CONTENT_TYPE"; - case EVENT_STREAM_RPC_CRT_ERROR: - Crt::String ret = "Failed with EVENT_STREAM_RPC_CRT_ERROR, the CRT error was "; - ret += Crt::ErrorDebugString(crtError); - return ret; - } - return "Unknown status code"; - } - - std::future ClientConnection::Connect( + std::future ClientConnectionImpl::Connect( const ConnectionConfig &connectionConfig, ConnectionLifecycleHandler *connectionLifecycleHandler, Crt::Io::ClientBootstrap &clientBootstrap) noexcept @@ -391,9 +522,9 @@ namespace Aws } connOptions.socket_options = &m_socketOptions.GetImpl(); - connOptions.on_connection_setup = ClientConnection::s_onConnectionSetup; - connOptions.on_connection_protocol_message = ClientConnection::s_onProtocolMessage; - connOptions.on_connection_shutdown = ClientConnection::s_onConnectionShutdown; + connOptions.on_connection_setup = ClientConnectionImpl::s_onConnectionSetup; + connOptions.on_connection_protocol_message = ClientConnectionImpl::s_onProtocolMessage; + connOptions.on_connection_shutdown = ClientConnectionImpl::s_onConnectionShutdown; connOptions.user_data = reinterpret_cast(this); m_lifecycleHandler = connectionLifecycleHandler; @@ -427,58 +558,7 @@ namespace Aws return m_connectAckedPromise.get_future(); } - std::future ClientConnection::SendPing( - const Crt::List &headers, - const Crt::Optional &payload, - OnMessageFlushCallback onMessageFlushCallback) noexcept - { - return s_sendPing(this, headers, payload, onMessageFlushCallback); - } - - std::future ClientConnection::SendPingResponse( - const Crt::List &headers, - const Crt::Optional &payload, - OnMessageFlushCallback onMessageFlushCallback) noexcept - { - return s_sendPingResponse(this, headers, payload, onMessageFlushCallback); - } - - std::future ClientConnection::s_sendPing( - ClientConnection *connection, - const Crt::List &headers, - const Crt::Optional &payload, - OnMessageFlushCallback onMessageFlushCallback) noexcept - { - return s_sendProtocolMessage( - connection, headers, payload, AWS_EVENT_STREAM_RPC_MESSAGE_TYPE_PING, 0, onMessageFlushCallback); - } - - std::future ClientConnection::s_sendPingResponse( - ClientConnection *connection, - const Crt::List &headers, - const Crt::Optional &payload, - OnMessageFlushCallback onMessageFlushCallback) noexcept - { - return s_sendProtocolMessage( - connection, - headers, - payload, - AWS_EVENT_STREAM_RPC_MESSAGE_TYPE_PING_RESPONSE, - 0, - onMessageFlushCallback); - } - - std::future ClientConnection::SendProtocolMessage( - const Crt::List &headers, - const Crt::Optional &payload, - MessageType messageType, - uint32_t messageFlags, - OnMessageFlushCallback onMessageFlushCallback) noexcept - { - return s_sendProtocolMessage(this, headers, payload, messageType, messageFlags, onMessageFlushCallback); - } - - void ClientConnection::s_protocolMessageCallback(int errorCode, void *userData) noexcept + static void s_protocolMessageCallback(int errorCode, void *userData) noexcept { auto *callbackData = static_cast(userData); @@ -504,8 +584,8 @@ namespace Aws Crt::Delete(callbackData, callbackData->allocator); } - std::future ClientConnection::s_sendProtocolMessage( - ClientConnection *connection, + std::future ClientConnectionImpl::s_sendProtocolMessage( + ClientConnectionImpl *connection, const Crt::List &headers, const Crt::Optional &payload, MessageType messageType, @@ -519,9 +599,7 @@ namespace Aws /* The caller should never pass a NULL connection. */ AWS_PRECONDITION(connection != nullptr); - int errorCode = EventStreamCppToNativeCrtBuilder::s_fillNativeHeadersArray( - headers, &headersArray, connection->m_allocator); - + int errorCode = s_fillNativeHeadersArray(headers, &headersArray, connection->m_allocator); if (!errorCode) { struct aws_event_stream_rpc_message_args msg_args; @@ -541,7 +619,7 @@ namespace Aws errorCode = aws_event_stream_rpc_client_connection_send_protocol_message( connection->m_underlyingConnection, &msg_args, - ClientConnection::s_protocolMessageCallback, + s_protocolMessageCallback, reinterpret_cast(callbackContainer)); } @@ -569,7 +647,7 @@ namespace Aws return onFlushPromise.get_future(); } - void ClientConnection::Close() noexcept + void ClientConnectionImpl::Close() noexcept { const std::lock_guard lock(m_stateMutex); @@ -593,105 +671,28 @@ namespace Aws } } - EventStreamHeader::EventStreamHeader( - const struct aws_event_stream_header_value_pair &header, - Crt::Allocator *allocator) - : m_allocator(allocator), m_valueByteBuf({}), m_underlyingHandle(header) - { - } - - EventStreamHeader::EventStreamHeader( - const Crt::String &name, - const Crt::String &value, - Crt::Allocator *allocator) noexcept - : m_allocator(allocator), - m_valueByteBuf(Crt::ByteBufNewCopy(allocator, (uint8_t *)value.c_str(), value.length())) - { - m_underlyingHandle.header_name_len = static_cast(name.length()); - size_t length; - if (name.length() > INT8_MAX) - { - length = INT8_MAX; - } - else - { - length = static_cast(name.length()); - } - (void)memcpy(m_underlyingHandle.header_name, name.c_str(), length); - m_underlyingHandle.header_value_type = AWS_EVENT_STREAM_HEADER_STRING; - m_underlyingHandle.header_value.variable_len_val = m_valueByteBuf.buffer; - m_underlyingHandle.header_value_len = (uint16_t)m_valueByteBuf.len; - } - - EventStreamHeader::~EventStreamHeader() noexcept - { - if (aws_byte_buf_is_valid(&m_valueByteBuf)) - Crt::ByteBufDelete(m_valueByteBuf); - } - - EventStreamHeader::EventStreamHeader(const EventStreamHeader &lhs) noexcept - : m_allocator(lhs.m_allocator), - m_valueByteBuf(Crt::ByteBufNewCopy(lhs.m_allocator, lhs.m_valueByteBuf.buffer, lhs.m_valueByteBuf.len)), - m_underlyingHandle(lhs.m_underlyingHandle) - { - m_underlyingHandle.header_value.variable_len_val = m_valueByteBuf.buffer; - m_underlyingHandle.header_value_len = static_cast(m_valueByteBuf.len); - } - - EventStreamHeader &EventStreamHeader::operator=(const EventStreamHeader &lhs) noexcept - { - m_allocator = lhs.m_allocator; - m_valueByteBuf = Crt::ByteBufNewCopy(lhs.m_allocator, lhs.m_valueByteBuf.buffer, lhs.m_valueByteBuf.len); - m_underlyingHandle = lhs.m_underlyingHandle; - m_underlyingHandle.header_value.variable_len_val = m_valueByteBuf.buffer; - m_underlyingHandle.header_value_len = static_cast(m_valueByteBuf.len); - return *this; - } - - EventStreamHeader::EventStreamHeader(EventStreamHeader &&rhs) noexcept - : m_allocator(rhs.m_allocator), m_valueByteBuf(rhs.m_valueByteBuf), - m_underlyingHandle(rhs.m_underlyingHandle) - { - rhs.m_valueByteBuf.allocator = nullptr; - rhs.m_valueByteBuf.buffer = nullptr; - } - - const struct aws_event_stream_header_value_pair *EventStreamHeader::GetUnderlyingHandle() const - { - return &m_underlyingHandle; - } - - Crt::String EventStreamHeader::GetHeaderName() const noexcept + bool ClientConnectionImpl::IsOpen() const noexcept { - return Crt::String(m_underlyingHandle.header_name, m_underlyingHandle.header_name_len, m_allocator); - } - - bool EventStreamHeader::GetValueAsString(Crt::String &value) const noexcept - { - if (m_underlyingHandle.header_value_type != AWS_EVENT_STREAM_HEADER_STRING) + if (this->m_underlyingConnection == nullptr) { return false; } - value = Crt::String( - reinterpret_cast(m_underlyingHandle.header_value.variable_len_val), - m_underlyingHandle.header_value_len, - m_allocator); - return true; + return aws_event_stream_rpc_client_connection_is_open(this->m_underlyingConnection); } - ClientContinuation ClientConnection::NewStream(ClientContinuationHandler &clientContinuationHandler) noexcept + struct aws_event_stream_rpc_client_connection *ClientConnectionImpl::GetUnderlyingHandle() const noexcept { - return ClientContinuation(this, clientContinuationHandler, m_allocator); + return m_underlyingConnection; } - void ClientConnection::s_onConnectionSetup( + void ClientConnectionImpl::s_onConnectionSetup( struct aws_event_stream_rpc_client_connection *connection, int errorCode, void *userData) noexcept { /* The `userData` pointer is used to pass `this` of a `ClientConnection` object. */ - auto *thisConnection = static_cast(userData); + auto *thisConnection = static_cast(userData); const std::lock_guard lock(thisConnection->m_stateMutex); @@ -745,14 +746,14 @@ namespace Aws thisConnection->m_connectionSetupPromise.set_value(); } - void ClientConnection::s_onConnectionShutdown( + void ClientConnectionImpl::s_onConnectionShutdown( struct aws_event_stream_rpc_client_connection *connection, int errorCode, void *userData) noexcept { (void)connection; /* The `userData` pointer is used to pass `this` of a `ClientConnection` object. */ - auto *thisConnection = static_cast(userData); + auto *thisConnection = static_cast(userData); const std::lock_guard lock(thisConnection->m_stateMutex); @@ -798,7 +799,7 @@ namespace Aws } } - void ClientConnection::s_onProtocolMessage( + void ClientConnectionImpl::s_onProtocolMessage( struct aws_event_stream_rpc_client_connection *connection, const struct aws_event_stream_rpc_message_args *messageArgs, void *userData) noexcept @@ -807,7 +808,7 @@ namespace Aws (void)connection; /* The `userData` pointer is used to pass `this` of a `ClientConnection` object. */ - auto *thisConnection = static_cast(userData); + auto *thisConnection = static_cast(userData); Crt::List pingHeaders; switch (messageArgs->message_type) @@ -883,14 +884,80 @@ namespace Aws } } + ClientContinuation ClientConnectionImpl::NewStream(ClientContinuationHandler &clientContinuationHandler) noexcept + { + return ClientContinuation(m_underlyingConnection, clientContinuationHandler, m_allocator); + } + + ClientConnection::ClientConnection(Crt::Allocator *allocator) noexcept + :m_impl(Aws::Crt::MakeShared(allocator, allocator)) + { + } + + ClientConnection::~ClientConnection() noexcept + { + m_impl->Shutdown(); + m_impl = nullptr; + } + + std::future ClientConnection::Connect( + const ConnectionConfig &connectionOptions, + ConnectionLifecycleHandler *connectionLifecycleHandler, + Crt::Io::ClientBootstrap &clientBootstrap) noexcept + { + return m_impl->Connect(connectionOptions, connectionLifecycleHandler, clientBootstrap); + } + + ClientContinuation ClientConnection::NewStream(ClientContinuationHandler &clientContinuationHandler) noexcept + { + return m_impl->NewStream(clientContinuationHandler); + } + + void ClientConnection::Close() noexcept + { + m_impl->Close(); + } + + bool ClientConnection::IsOpen() const noexcept + { + return m_impl->IsOpen(); + } + + struct aws_event_stream_rpc_client_connection *ClientConnection::GetUnderlyingHandle() const noexcept + { + return m_impl->GetUnderlyingHandle(); + } + void AbstractShapeBase::s_customDeleter(AbstractShapeBase *shape) noexcept { if (shape->m_allocator != nullptr) Crt::Delete(shape, shape->m_allocator); } + struct RawContinuationCallbackDataWrapper + { + RawContinuationCallbackDataWrapper(Aws::Crt::Allocator *allocator, const std::shared_ptr &callbackData) : + m_allocator(allocator), + m_callbackData(callbackData) + {} + + Aws::Crt::Allocator *m_allocator; + std::shared_ptr m_callbackData; + }; + + static void s_onContinuationTerminated(void *user_data) + { + if (user_data == nullptr) + { + return; + } + + struct RawContinuationCallbackDataWrapper *wrapper = static_cast(user_data); + Aws::Crt::Delete(wrapper, wrapper->m_allocator); + } + ClientContinuation::ClientContinuation( - ClientConnection *connection, + struct aws_event_stream_rpc_client_connection *connection, ClientContinuationHandler &continuationHandler, Crt::Allocator *allocator) noexcept : m_allocator(allocator), m_continuationHandler(continuationHandler), m_continuationToken(nullptr) @@ -898,19 +965,19 @@ namespace Aws struct aws_event_stream_rpc_client_stream_continuation_options options; options.on_continuation = ClientContinuation::s_onContinuationMessage; options.on_continuation_closed = ClientContinuation::s_onContinuationClosed; + options.on_continuation_terminated = s_onContinuationTerminated; - m_callbackData = Crt::New(m_allocator, this, m_allocator); + m_callbackData = Crt::MakeShared(m_allocator, this, m_allocator); m_continuationHandler.m_callbackData = m_callbackData; - options.user_data = reinterpret_cast(m_callbackData); + options.user_data = reinterpret_cast(Aws::Crt::New(allocator, allocator, m_callbackData)); - if (connection->IsOpen()) + if (connection) { m_continuationToken = - aws_event_stream_rpc_client_connection_new_stream(connection->m_underlyingConnection, &options); + aws_event_stream_rpc_client_connection_new_stream(connection, &options); if (m_continuationToken == nullptr) { - Crt::Delete(m_callbackData, m_allocator); m_continuationHandler.m_callbackData = nullptr; m_callbackData = nullptr; } @@ -919,18 +986,23 @@ namespace Aws ClientContinuation::~ClientContinuation() noexcept { - if (m_continuationToken) - { - aws_event_stream_rpc_client_continuation_release(m_continuationToken); - m_continuationToken = nullptr; - } + Release(); + } + + void ClientContinuation::Release() + { if (m_callbackData != nullptr) { { const std::lock_guard lock(m_callbackData->callbackMutex); m_callbackData->continuationDestroyed = true; } - Crt::Delete(m_callbackData, m_allocator); + } + + if (m_continuationToken) + { + aws_event_stream_rpc_client_continuation_release(m_continuationToken); + m_continuationToken = nullptr; } } @@ -941,7 +1013,7 @@ namespace Aws { (void)continuationToken; /* The `userData` pointer is used to pass a `ContinuationCallbackData` object. */ - auto *callbackData = static_cast(userData); + auto *callbackData = static_cast(userData)->m_callbackData.get(); auto *thisContinuation = callbackData->clientContinuation; Crt::List continuationMessageHeaders; @@ -976,7 +1048,7 @@ namespace Aws (void)continuationToken; /* The `userData` pointer is used to pass a `ContinuationCallbackData` object. */ - auto *callbackData = static_cast(userData); + auto *callbackData = static_cast(userData)->m_callbackData.get(); const std::lock_guard lock(callbackData->callbackMutex); if (callbackData->continuationDestroyed) @@ -1010,8 +1082,7 @@ namespace Aws return onFlushPromise.get_future(); } - int errorCode = - EventStreamCppToNativeCrtBuilder::s_fillNativeHeadersArray(headers, &headersArray, m_allocator); + int errorCode = s_fillNativeHeadersArray(headers, &headersArray, m_allocator); /* * Regardless of how the promise gets moved around (or not), this future should stay valid as a return @@ -1041,7 +1112,7 @@ namespace Aws m_continuationToken, Crt::ByteCursorFromCString(operationName.c_str()), &msg_args, - ClientConnection::s_protocolMessageCallback, + s_protocolMessageCallback, reinterpret_cast(callbackContainer)); } @@ -1078,8 +1149,7 @@ namespace Aws return onFlushPromise.get_future(); } - int errorCode = - EventStreamCppToNativeCrtBuilder::s_fillNativeHeadersArray(headers, &headersArray, m_allocator); + int errorCode = s_fillNativeHeadersArray(headers, &headersArray, m_allocator); if (!errorCode) { @@ -1101,7 +1171,7 @@ namespace Aws errorCode = aws_event_stream_rpc_client_continuation_send_message( m_continuationToken, &msg_args, - ClientConnection::s_protocolMessageCallback, + s_protocolMessageCallback, reinterpret_cast(callbackContainer)); } } @@ -1168,8 +1238,10 @@ namespace Aws ClientOperation::~ClientOperation() noexcept { Close().wait(); - std::unique_lock lock(m_continuationMutex); - m_closeReady.wait(lock, [this] { return m_expectingClose == false; }); + m_clientContinuation.Release(); + //std::unique_lock lock(m_continuationMutex); + //m_closeReady.wait(lock, [this] { return m_expectingClose == false; }); + } TaggedResult::TaggedResult(Crt::ScopedResource operationResponse) noexcept @@ -1608,7 +1680,7 @@ namespace Aws errorCode = aws_event_stream_rpc_client_continuation_send_message( m_clientContinuation.m_continuationToken, &msg_args, - ClientConnection::s_protocolMessageCallback, + s_protocolMessageCallback, reinterpret_cast(callbackContainer)); } diff --git a/eventstream_rpc/tests/CMakeLists.txt b/eventstream_rpc/tests/CMakeLists.txt index 3d5838d63..abf50b5a4 100644 --- a/eventstream_rpc/tests/CMakeLists.txt +++ b/eventstream_rpc/tests/CMakeLists.txt @@ -38,19 +38,19 @@ add_test_case(EchoClientConnectSuccess) add_test_case(EchoClientDoubleClose) add_test_case(EchoClientMultiConnectSuccessFail) -add_test_case(EchoClientOperationEchoSuccessString) -add_test_case(EchoClientOperationEchoSuccessBoolean) -add_test_case(EchoClientOperationEchoSuccessTime) -add_test_case(EchoClientOperationEchoSuccessDocument) -add_test_case(EchoClientOperationEchoSuccessEnum) -add_test_case(EchoClientOperationEchoSuccessBlob) -add_test_case(EchoClientOperationEchoSuccessStringList) -add_test_case(EchoClientOperationEchoSuccessPairList) -add_test_case(EchoClientOperationEchoSuccessProductMap) - -add_test_case(EchoClientOperationEchoSuccessMultiple) -add_test_case(EchoClientOperationEchoFailureNeverConnected) -add_test_case(EchoClientOperationEchoFailureDisconnected) +#add_test_case(EchoClientOperationEchoSuccessString) +#add_test_case(EchoClientOperationEchoSuccessBoolean) +#add_test_case(EchoClientOperationEchoSuccessTime) +#add_test_case(EchoClientOperationEchoSuccessDocument) +#add_test_case(EchoClientOperationEchoSuccessEnum) +#add_test_case(EchoClientOperationEchoSuccessBlob) +#add_test_case(EchoClientOperationEchoSuccessStringList) +#add_test_case(EchoClientOperationEchoSuccessPairList) +#add_test_case(EchoClientOperationEchoSuccessProductMap) + +#add_test_case(EchoClientOperationEchoSuccessMultiple) +#add_test_case(EchoClientOperationEchoFailureNeverConnected) +#add_test_case(EchoClientOperationEchoFailureDisconnected) diff --git a/eventstream_rpc/tests/EventStreamClientTest.cpp b/eventstream_rpc/tests/EventStreamClientTest.cpp index f54aa31df..05dcf2a52 100644 --- a/eventstream_rpc/tests/EventStreamClientTest.cpp +++ b/eventstream_rpc/tests/EventStreamClientTest.cpp @@ -291,22 +291,7 @@ static void s_onMessageFlush(int errorCode) } template -static bool s_messageDataMembersAreEqual(Aws::Crt::Optional expectedValue, Aws::Crt::Optional actualValue) -{ - if (expectedValue.has_value() != actualValue.has_value()) - { - return false; - } - - if (expectedValue.has_value()) - { - return expectedValue.value() == actualValue.value(); - } - - return true; -} - -static bool s_messageDataMembersAreEqual(Aws::Crt::Optional expectedValue, Aws::Crt::Optional actualValue) +static bool s_messageDataMembersAreEqual(const Aws::Crt::Optional &expectedValue, const Aws::Crt::Optional &actualValue) { if (expectedValue.has_value() != actualValue.has_value()) { From 8079f33c46546a08a2149c45e33248a2dfd40452 Mon Sep 17 00:00:00 2001 From: Bret Ambrose Date: Wed, 14 May 2025 14:43:32 -0700 Subject: [PATCH 32/43] Checkpoint --- .../aws/eventstreamrpc/EventStreamClient.h | 48 +- eventstream_rpc/source/EventStreamClient.cpp | 885 ++++++++++-------- eventstream_rpc/tests/EchoTestRpcClient.cpp | 4 +- .../tests/EventStreamClientTest.cpp | 12 +- .../tests/include/awstest/EchoTestRpcClient.h | 1 - .../aws/greengrass/GreengrassCoreIpcClient.h | 1 - .../source/GreengrassCoreIpcClient.cpp | 4 +- 7 files changed, 522 insertions(+), 433 deletions(-) diff --git a/eventstream_rpc/include/aws/eventstreamrpc/EventStreamClient.h b/eventstream_rpc/include/aws/eventstreamrpc/EventStreamClient.h index 1414409dd..7f87df0f1 100644 --- a/eventstream_rpc/include/aws/eventstreamrpc/EventStreamClient.h +++ b/eventstream_rpc/include/aws/eventstreamrpc/EventStreamClient.h @@ -139,6 +139,23 @@ namespace Aws Crt::Allocator *m_allocator; }; + enum EventStreamRpcStatusCode + { + EVENT_STREAM_RPC_SUCCESS = 0, + EVENT_STREAM_RPC_NULL_PARAMETER, + EVENT_STREAM_RPC_UNINITIALIZED, + EVENT_STREAM_RPC_ALLOCATION_ERROR, + EVENT_STREAM_RPC_CONNECTION_SETUP_FAILED, + EVENT_STREAM_RPC_CONNECTION_ACCESS_DENIED, + EVENT_STREAM_RPC_CONNECTION_ALREADY_ESTABLISHED, + EVENT_STREAM_RPC_CONNECTION_CLOSED, + EVENT_STREAM_RPC_CONTINUATION_CLOSED, + EVENT_STREAM_RPC_UNKNOWN_PROTOCOL_MESSAGE, + EVENT_STREAM_RPC_UNMAPPED_DATA, + EVENT_STREAM_RPC_UNSUPPORTED_CONTENT_TYPE, + EVENT_STREAM_RPC_CRT_ERROR + }; + /** * Configuration structure holding all configurations relating to eventstream RPC connection establishment */ @@ -184,6 +201,8 @@ namespace Aws m_connectRequestCallback = connectRequestCallback; } + EventStreamRpcStatusCode Validate() const noexcept; + protected: Crt::Optional m_hostName; Crt::Optional m_port; @@ -194,23 +213,6 @@ namespace Aws OnMessageFlushCallback m_connectRequestCallback; }; - enum EventStreamRpcStatusCode - { - EVENT_STREAM_RPC_SUCCESS = 0, - EVENT_STREAM_RPC_NULL_PARAMETER, - EVENT_STREAM_RPC_UNINITIALIZED, - EVENT_STREAM_RPC_ALLOCATION_ERROR, - EVENT_STREAM_RPC_CONNECTION_SETUP_FAILED, - EVENT_STREAM_RPC_CONNECTION_ACCESS_DENIED, - EVENT_STREAM_RPC_CONNECTION_ALREADY_ESTABLISHED, - EVENT_STREAM_RPC_CONNECTION_CLOSED, - EVENT_STREAM_RPC_CONTINUATION_CLOSED, - EVENT_STREAM_RPC_UNKNOWN_PROTOCOL_MESSAGE, - EVENT_STREAM_RPC_UNMAPPED_DATA, - EVENT_STREAM_RPC_UNSUPPORTED_CONTENT_TYPE, - EVENT_STREAM_RPC_CRT_ERROR - }; - struct AWS_EVENTSTREAMRPC_API RpcError { explicit operator bool() const noexcept { return baseStatus == EVENT_STREAM_RPC_SUCCESS; } @@ -268,20 +270,18 @@ namespace Aws { public: - explicit ClientConnection(Crt::Allocator *allocator = Crt::g_allocator) noexcept; + explicit ClientConnection(Crt::Allocator *allocator, aws_client_bootstrap *bootstrap) noexcept; ~ClientConnection() noexcept; /** * Initiates a new outgoing event-stream-rpc connection. * @param connectionOptions Connection options. * @param connectionLifecycleHandler Handler to process connection lifecycle events. - * @param clientBootstrap ClientBootstrap object to run the connection on. * @return Future that will be resolved when connection either succeeds or fails. */ std::future Connect( const ConnectionConfig &connectionOptions, - ConnectionLifecycleHandler *connectionLifecycleHandler, - Crt::Io::ClientBootstrap &clientBootstrap) noexcept; + ConnectionLifecycleHandler *connectionLifecycleHandler) noexcept; /** * Create a new stream. @@ -302,12 +302,6 @@ namespace Aws */ bool IsOpen() const noexcept; - /** - * Returns the C connection object, if it exists. - * @return the C connection object, if it exists. - */ - struct aws_event_stream_rpc_client_connection *GetUnderlyingHandle() const noexcept; - private: std::shared_ptr m_impl; diff --git a/eventstream_rpc/source/EventStreamClient.cpp b/eventstream_rpc/source/EventStreamClient.cpp index e14bbdeb1..1ab76bf00 100644 --- a/eventstream_rpc/source/EventStreamClient.cpp +++ b/eventstream_rpc/source/EventStreamClient.cpp @@ -164,30 +164,20 @@ namespace Aws } } - static int s_fillNativeHeadersArray( + static void s_fillNativeHeadersArray( const Crt::List &headers, struct aws_array_list *headersArray, Crt::Allocator *m_allocator = Crt::g_allocator) { AWS_ZERO_STRUCT(*headersArray); /* Check if the connection has expired before attempting to send. */ - int errorCode = aws_event_stream_headers_list_init(headersArray, m_allocator); + aws_event_stream_headers_list_init(headersArray, m_allocator); - if (!errorCode) + /* Populate the array with the underlying handle of each EventStreamHeader. */ + for (auto &i : headers) { - /* Populate the array with the underlying handle of each EventStreamHeader. */ - for (auto &i : headers) - { - errorCode = aws_array_list_push_back(headersArray, i.GetUnderlyingHandle()); - - if (errorCode) - { - break; - } - } + aws_array_list_push_back(headersArray, i.GetUnderlyingHandle()); } - - return errorCode; } Crt::String RpcError::StatusToString() @@ -226,6 +216,16 @@ namespace Aws return "Unknown status code"; } + EventStreamRpcStatusCode ConnectionConfig::Validate() const noexcept + { + if (!m_hostName.has_value() || !m_port.has_value()) + { + return EVENT_STREAM_RPC_NULL_PARAMETER; + } + + return EVENT_STREAM_RPC_SUCCESS; + } + bool ConnectionLifecycleHandler::OnErrorCallback(RpcError error) { (void)error; @@ -335,17 +335,162 @@ namespace Aws return true; } - class ClientConnectionImpl final +/* + * Eventstream Connection Refactor + * + * Part 1 of an effort to refactor the eventstream bindings to conform to the latest CRT binding guidelines. + * + * For connections, we now enforce the following invariants for correctness: + * + * 1. No callback is made while a lock is held. We perform callbacks by having a transactional callback context that + * is moved out of shared state (under the lock), but the callback context does not perform its work until the lock + * is released. + * 2. No destructor blocking or synchronization. In order to provide the best behavioral backwards compatibility, we + * "synthesize" the callbacks that would occur at destruction when we kick off the async cleanup process. When the + * asynchronous events occur that would normally trigger a callback occur, we ignore them via the has_shut_down flag. + * 3. A self-reference (via shared_ptr member) guarantees the binding impl stays alive longer than the C objects. The + * public binding object also keeps a shared_ptr to the impl, so final destruction only occurs once both the public + * binding object's destructor has run and no C connection object is still alive. + */ + + /* What kind of callback should this context trigger? */ + enum class ConnectionCallbackActionType + { + None, + CompleteConnectPromise, + DisconnectionCallback + }; + + /* + * The purpose of this type is to hide much of the conditional complexity around callbacks that the + * connection implementation faces. Issues include: + * (1) When to complete a promise vs. calling a function + * (2) Tracking what promise to complete + * (3) Helping ensure callbacks always occur once and only once + * (4) Passing delayed state (like an error code) forward until the callback can safely be triggered + * + * We rely heavily on careful move semantics to ensure (2) and (3) + */ + class ConnectionCallbackContext { public: + ConnectionCallbackContext() : m_action(ConnectionCallbackActionType::None), m_error{EVENT_STREAM_RPC_SUCCESS, AWS_ERROR_SUCCESS} {} + + ConnectionCallbackContext(const std::function &disconnectionCallback, const std::function &errorCallback, const std::function &connectionSuccessCallback) : + m_action(ConnectionCallbackActionType::CompleteConnectPromise), + m_error{EVENT_STREAM_RPC_SUCCESS, AWS_ERROR_SUCCESS}, + m_connectPromise(), + m_disconnectionCallback(disconnectionCallback), + m_errorCallback(errorCallback), + m_connectionSuccessCallback(connectionSuccessCallback) + {} + + ConnectionCallbackContext(ConnectionCallbackContext &&rhs) noexcept : + m_action(rhs.m_action), + m_error(rhs.m_error), + m_connectPromise(std::move(rhs.m_connectPromise)), + m_disconnectionCallback(std::move(rhs.m_disconnectionCallback)), + m_errorCallback(std::move(rhs.m_errorCallback)), + m_connectionSuccessCallback(std::move(rhs.m_connectionSuccessCallback)) + { + rhs.ClearContext(); + } + + ConnectionCallbackContext &operator=(ConnectionCallbackContext &&rhs) noexcept + { + m_action = rhs.m_action; + m_error = rhs.m_error; + m_connectPromise = std::move(rhs.m_connectPromise); + m_disconnectionCallback = std::move(rhs.m_disconnectionCallback); + m_errorCallback = std::move(rhs.m_errorCallback); + m_connectionSuccessCallback = std::move(rhs.m_connectionSuccessCallback); + + rhs.ClearContext(); - explicit ClientConnectionImpl(Crt::Allocator *allocator = Crt::g_allocator) noexcept; + return *this; + } + + void InvokeCallbacks() + { + switch (m_action) + { + case ConnectionCallbackActionType::CompleteConnectPromise: + { + if (m_error.crtError != AWS_ERROR_SUCCESS) + { + (void)m_errorCallback(m_error); + } + else + { + m_connectionSuccessCallback(); + } + m_connectPromise.set_value(m_error); + break; + } + case ConnectionCallbackActionType::DisconnectionCallback: + { + m_disconnectionCallback(m_error); + break; + } + default: + break; + } + } + + /* + * Returns a callback context that should be invoked, and moves this context into a state that should be + * invoked when the connection is closed + */ + ConnectionCallbackContext TransitionToConnected() + { + ConnectionCallbackContext context = {}; + context.m_action = ConnectionCallbackActionType::CompleteConnectPromise; + context.m_connectPromise = std::move(m_connectPromise); + context.m_connectionSuccessCallback = m_connectionSuccessCallback; + + m_action = ConnectionCallbackActionType::DisconnectionCallback; + m_error = {EVENT_STREAM_RPC_SUCCESS, AWS_ERROR_SUCCESS}; + m_connectPromise = {}; + + return context; + } + + void SetAction(ConnectionCallbackActionType action) { m_action = action; } + void SetError(RpcError error) { m_error = error; } + + std::future GetConnectPromiseFuture() { return m_connectPromise.get_future(); } + + private: + + /* Wipes out state in a context to guarantee it does nothing if someone tries to InvokeCallbacks on it */ + void ClearContext() + { + m_action = ConnectionCallbackActionType::None; + m_error = {EVENT_STREAM_RPC_SUCCESS, AWS_ERROR_SUCCESS}; + m_connectPromise = {}; + m_disconnectionCallback = {}; + m_errorCallback = {}; + m_connectionSuccessCallback = {}; + } + + ConnectionCallbackActionType m_action; + RpcError m_error; + std::promise m_connectPromise; + std::function m_disconnectionCallback; + std::function m_errorCallback; + std::function m_connectionSuccessCallback; + }; + + class ClientConnectionImpl final : public std::enable_shared_from_this + { + public: + + explicit ClientConnectionImpl(Crt::Allocator *allocator, aws_client_bootstrap *bootstrap) noexcept; ~ClientConnectionImpl() noexcept; std::future Connect( const ConnectionConfig &connectionOptions, - ConnectionLifecycleHandler *connectionLifecycleHandler, - Crt::Io::ClientBootstrap &clientBootstrap) noexcept; + ConnectionLifecycleHandler *connectionLifecycleHandler) noexcept; ClientContinuation NewStream(ClientContinuationHandler &clientContinuationHandler) noexcept; @@ -353,44 +498,56 @@ namespace Aws bool IsOpen() const noexcept; - struct aws_event_stream_rpc_client_connection *GetUnderlyingHandle() const noexcept; - void Shutdown() noexcept; + std::future SendProtocolMessage( + const Crt::List &headers, + const Crt::Optional &payload, + MessageType messageType, + uint32_t messageFlags, + OnMessageFlushCallback onMessageFlushCallback) noexcept; + private: - enum ClientState + void CloseInternal(bool isShutdown) noexcept; + void MoveToDisconnected(RpcError error) noexcept; + + int SendProtocolMessageAux( + const Crt::List &headers, + const Crt::Optional &payload, + MessageType messageType, + uint32_t messageFlags, + OnMessageFlushCallback &&onMessageFlushCallback, + std::promise *sendOutcomePromise, + bool takeLock) noexcept; + + enum class ClientState { - DISCONNECTED = 1, - CONNECTING_SOCKET, - WAITING_FOR_CONNECT_ACK, - CONNECTED, - DISCONNECTING, + Disconnected, + PendingConnect, + PendingConnack, + Connected, + Disconnecting, }; - /* This recursive mutex protects m_clientState & m_connectionWillSetup */ - std::recursive_mutex m_stateMutex; + Crt::Allocator *m_allocator; - struct aws_event_stream_rpc_client_connection *m_underlyingConnection; - ClientState m_clientState; - ConnectionLifecycleHandler *m_lifecycleHandler; + + ConnectionLifecycleHandler *m_lifecycleHandler; // cannot be made a shared_ptr sadly ConnectMessageAmender m_connectMessageAmender; - std::promise m_connectionSetupPromise; - bool m_connectionWillSetup; - std::promise m_connectAckedPromise; - std::promise m_closedPromise; - bool m_onConnectCalled; - RpcError m_closeReason; - OnMessageFlushCallback m_onConnectRequestCallback; - Crt::Io::SocketOptions m_socketOptions; ConnectionConfig m_connectionConfig; + std::shared_ptr m_selfReference; + aws_client_bootstrap *m_bootstrap; + aws_event_loop *m_eventLoop; - static std::future s_sendProtocolMessage( - ClientConnectionImpl *connectionImpl, - const Crt::List &headers, - const Crt::Optional &payload, - MessageType messageType, - uint32_t messageFlags, - OnMessageFlushCallback onMessageFlushCallback) noexcept; + std::mutex m_sharedStateLock; + struct + { + aws_event_stream_rpc_client_connection *m_underlyingConnection; + ClientState m_currentState; + ClientState m_desiredState; + ConnectionCallbackContext m_callbackContext; + bool m_hasShutDown; + } m_sharedState; static void s_onConnectionShutdown( struct aws_event_stream_rpc_client_connection *connection, @@ -409,153 +566,176 @@ namespace Aws }; - ClientConnectionImpl::ClientConnectionImpl(Crt::Allocator *allocator) noexcept - : m_allocator(allocator), m_underlyingConnection(nullptr), m_clientState(DISCONNECTED), - m_lifecycleHandler(nullptr), m_connectMessageAmender(nullptr), m_connectionWillSetup(false), - m_onConnectCalled(false), m_closeReason{EVENT_STREAM_RPC_SUCCESS, AWS_ERROR_SUCCESS}, - m_onConnectRequestCallback(nullptr) + ClientConnectionImpl::ClientConnectionImpl(Crt::Allocator *allocator, aws_client_bootstrap *bootstrap) noexcept + : m_allocator(allocator), m_lifecycleHandler(nullptr), m_connectMessageAmender(nullptr), + m_bootstrap(aws_client_bootstrap_acquire(bootstrap)), m_eventLoop(nullptr), + m_sharedState { nullptr, ClientState::Disconnected, ClientState::Disconnected, {}, false} { + m_eventLoop = aws_event_loop_group_get_next_loop(bootstrap->event_loop_group); } ClientConnectionImpl::~ClientConnectionImpl() noexcept { - m_stateMutex.lock(); - bool waitForSetup = m_connectionWillSetup; - m_stateMutex.unlock(); + aws_client_bootstrap_release(m_bootstrap); + } - if (waitForSetup) - { - m_connectionSetupPromise.get_future().wait(); - } + // We use a task to zero out the self reference to make sure we are not in a call stack that includes + // a member function of the connection impl itself when potentially releasing the final reference. + struct AwsEventstreamConnectionImplClearSharedTask { + AwsEventstreamConnectionImplClearSharedTask(Aws::Crt::Allocator *allocator, ClientConnectionImpl *clientConnectionImpl) noexcept; - bool waitForClosed = false; - m_stateMutex.lock(); - if (m_clientState != DISCONNECTED) - { - Close(); - waitForClosed = true; - } - m_stateMutex.unlock(); + struct aws_task m_task; + struct aws_allocator *m_allocator; + std::shared_ptr m_impl; + }; - if (waitForClosed) - { - m_closedPromise.get_future().wait(); - } + static void s_zeroSharedReference(struct aws_task *task, void *arg, enum aws_task_status status) + { + auto clearSharedTask = static_cast(arg); - m_underlyingConnection = nullptr; + // implicit destructor does all the work + Aws::Crt::Delete(clearSharedTask, clearSharedTask->m_allocator); } - void ClientConnectionImpl::Shutdown() noexcept + AwsEventstreamConnectionImplClearSharedTask::AwsEventstreamConnectionImplClearSharedTask(Aws::Crt::Allocator *allocator, ClientConnectionImpl *clientConnectionImpl) noexcept : + m_task{}, + m_allocator(allocator), + m_impl(clientConnectionImpl->shared_from_this()) { - + aws_task_init(&m_task, s_zeroSharedReference, this, "AwsEventstreamConnectionImplClearSharedTask"); } - std::future ClientConnectionImpl::Connect( - const ConnectionConfig &connectionConfig, - ConnectionLifecycleHandler *connectionLifecycleHandler, - Crt::Io::ClientBootstrap &clientBootstrap) noexcept + void ClientConnectionImpl::MoveToDisconnected(RpcError error) noexcept { - EventStreamRpcStatusCode baseError = EVENT_STREAM_RPC_SUCCESS; - struct aws_event_stream_rpc_client_connection_options connOptions; - + auto *clearSharedTask = Aws::Crt::New(m_allocator, m_allocator, this); + ConnectionCallbackContext localContext = {}; { - const std::lock_guard lock(m_stateMutex); - if (m_clientState == DISCONNECTED) - { - m_clientState = CONNECTING_SOCKET; - m_onConnectCalled = false; - m_connectionSetupPromise = {}; - m_connectAckedPromise = {}; - m_closedPromise = {}; - m_closeReason = {EVENT_STREAM_RPC_UNINITIALIZED, 0}; - m_connectionConfig = connectionConfig; - m_lifecycleHandler = connectionLifecycleHandler; - } - else + std::lock_guard lock(m_sharedStateLock); + + aws_event_stream_rpc_client_connection_release(m_sharedState.m_underlyingConnection); + m_sharedState.m_underlyingConnection = nullptr; + m_sharedState.m_currentState = ClientState::Disconnected; + m_sharedState.m_desiredState = ClientState::Disconnected; + m_sharedState.m_callbackContext.SetError(error); + m_selfReference = nullptr; + if (!m_sharedState.m_hasShutDown) { - baseError = EVENT_STREAM_RPC_CONNECTION_ALREADY_ESTABLISHED; + localContext = std::move(m_sharedState.m_callbackContext); } + m_sharedState.m_callbackContext = {}; } - m_onConnectRequestCallback = m_connectionConfig.GetConnectRequestCallback(); - Crt::String hostName; + localContext.InvokeCallbacks(); + aws_event_loop_schedule_task_now(m_eventLoop, &clearSharedTask->m_task); + } - if (baseError == EVENT_STREAM_RPC_SUCCESS) + void ClientConnectionImpl::CloseInternal(bool isShutdown) noexcept + { + ConnectionCallbackContext localContext = {}; { - AWS_ZERO_STRUCT(connOptions); - if (m_connectionConfig.GetHostName().has_value()) + std::lock_guard lock(m_sharedStateLock); + m_sharedState.m_desiredState = ClientState::Disconnected; + m_sharedState.m_callbackContext.SetError({EVENT_STREAM_RPC_CONNECTION_CLOSED, AWS_ERROR_EVENT_STREAM_RPC_CONNECTION_CLOSED}); + if (isShutdown) { - hostName = m_connectionConfig.GetHostName().value(); - connOptions.host_name = hostName.c_str(); + m_sharedState.m_hasShutDown = true; } - else - { - baseError = EVENT_STREAM_RPC_NULL_PARAMETER; - } - if (m_connectionConfig.GetPort().has_value()) + + if (m_sharedState.m_currentState == ClientState::Connected || m_sharedState.m_currentState == ClientState::PendingConnack) { - connOptions.port = m_connectionConfig.GetPort().value(); + m_sharedState.m_currentState = ClientState::Disconnecting; + aws_event_stream_rpc_client_connection_close(m_sharedState.m_underlyingConnection, AWS_ERROR_EVENT_STREAM_RPC_CONNECTION_CLOSED); } - else + + if (isShutdown) { - baseError = EVENT_STREAM_RPC_NULL_PARAMETER; + localContext = std::move(m_sharedState.m_callbackContext); } + } + + localContext.InvokeCallbacks(); + } + + void ClientConnectionImpl::Shutdown() noexcept + { + CloseInternal(true); + } + + void ClientConnectionImpl::Close() noexcept + { + CloseInternal(false); + } - connOptions.bootstrap = clientBootstrap.GetUnderlyingHandle(); + std::future ClientConnectionImpl::Connect( + const ConnectionConfig &connectionConfig, + ConnectionLifecycleHandler *connectionLifecycleHandler) noexcept + { + EventStreamRpcStatusCode status = connectionConfig.Validate(); + if (status != EVENT_STREAM_RPC_SUCCESS) + { + std::promise localPromise; + localPromise.set_value({status, AWS_ERROR_INVALID_ARGUMENT}); + + return localPromise.get_future(); } - if (baseError) + std::future localFuture = {}; { - std::promise errorPromise; - errorPromise.set_value({baseError, 0}); - if (baseError == EVENT_STREAM_RPC_NULL_PARAMETER) + std::lock_guard lock(m_sharedStateLock); + if (m_sharedState.m_currentState != ClientState::Disconnected) { - const std::lock_guard lock(m_stateMutex); - m_clientState = DISCONNECTED; + std::promise localPromise; + localPromise.set_value({EVENT_STREAM_RPC_CONNECTION_ALREADY_ESTABLISHED, AWS_ERROR_SUCCESS}); + return localPromise.get_future(); } - return errorPromise.get_future(); + + m_lifecycleHandler = connectionLifecycleHandler; + std::function disconnectCallback = [this](RpcError error) { this->m_lifecycleHandler->OnDisconnectCallback(error); }; + std::function errorCallback = [this](RpcError error){ return this->m_lifecycleHandler->OnErrorCallback(error); }; + std::function connectionSuccessCallback = [this](){ this->m_lifecycleHandler->OnConnectCallback(); }; + + m_connectionConfig = connectionConfig; + m_selfReference = shared_from_this(); + m_sharedState.m_desiredState = ClientState::Connected; + m_sharedState.m_currentState = ClientState::PendingConnect; + m_sharedState.m_callbackContext = {std::move(disconnectCallback), std::move(errorCallback), std::move(connectionSuccessCallback)}; + localFuture = m_sharedState.m_callbackContext.GetConnectPromiseFuture(); } + Crt::Io::SocketOptions socketOptions; if (m_connectionConfig.GetSocketOptions().has_value()) { - m_socketOptions = m_connectionConfig.GetSocketOptions().value(); + socketOptions = m_connectionConfig.GetSocketOptions().value(); } - connOptions.socket_options = &m_socketOptions.GetImpl(); - - connOptions.on_connection_setup = ClientConnectionImpl::s_onConnectionSetup; - connOptions.on_connection_protocol_message = ClientConnectionImpl::s_onProtocolMessage; - connOptions.on_connection_shutdown = ClientConnectionImpl::s_onConnectionShutdown; - connOptions.user_data = reinterpret_cast(this); - m_lifecycleHandler = connectionLifecycleHandler; - m_connectMessageAmender = m_connectionConfig.GetConnectMessageAmender(); + struct aws_event_stream_rpc_client_connection_options connectOptions = { + .host_name = connectionConfig.GetHostName().value().c_str(), + .port = connectionConfig.GetPort().value(), + .bootstrap = m_bootstrap, + .socket_options = &socketOptions.GetImpl(), + .on_connection_setup = ClientConnectionImpl::s_onConnectionSetup, + .on_connection_protocol_message = ClientConnectionImpl::s_onProtocolMessage, + .on_connection_shutdown = ClientConnectionImpl::s_onConnectionShutdown, + .user_data = reinterpret_cast(this), + }; if (m_connectionConfig.GetTlsConnectionOptions().has_value()) { - connOptions.tls_options = m_connectionConfig.GetTlsConnectionOptions()->GetUnderlyingHandle(); + connectOptions.tls_options = m_connectionConfig.GetTlsConnectionOptions()->GetUnderlyingHandle(); } - int crtError = aws_event_stream_rpc_client_connection_connect(m_allocator, &connOptions); - - if (crtError) + if (aws_event_stream_rpc_client_connection_connect(m_allocator, &connectOptions)) { - std::promise errorPromise; - AWS_LOGF_ERROR( - AWS_LS_EVENT_STREAM_RPC_CLIENT, - "A CRT error occurred while attempting to establish the connection: %s", - Crt::ErrorDebugString(crtError)); - errorPromise.set_value({EVENT_STREAM_RPC_CRT_ERROR, crtError}); - const std::lock_guard lock(m_stateMutex); - m_clientState = DISCONNECTED; - return errorPromise.get_future(); - } - else - { - const std::lock_guard lock(m_stateMutex); - m_connectionWillSetup = true; + MoveToDisconnected({EVENT_STREAM_RPC_CONNECTION_SETUP_FAILED, aws_last_error()}); } - return m_connectAckedPromise.get_future(); + return localFuture; + } + + bool ClientConnectionImpl::IsOpen() const noexcept + { + std::lock_guard lock(const_cast(this)->m_sharedStateLock); + return m_sharedState.m_currentState == ClientState::Connected; } static void s_protocolMessageCallback(int errorCode, void *userData) noexcept @@ -584,43 +764,59 @@ namespace Aws Crt::Delete(callbackData, callbackData->allocator); } - std::future ClientConnectionImpl::s_sendProtocolMessage( - ClientConnectionImpl *connection, + ClientContinuation ClientConnectionImpl::NewStream(ClientContinuationHandler &clientContinuationHandler) noexcept + { + std::lock_guard lock(m_sharedStateLock); + return {m_sharedState.m_underlyingConnection, clientContinuationHandler, m_allocator}; + } + + int ClientConnectionImpl::SendProtocolMessageAux( const Crt::List &headers, const Crt::Optional &payload, MessageType messageType, uint32_t messageFlags, - OnMessageFlushCallback onMessageFlushCallback) noexcept + OnMessageFlushCallback &&onMessageFlushCallback, + std::promise *sendOutcomePromise, + bool takeLock) noexcept { - std::promise onFlushPromise; OnMessageFlushCallbackContainer *callbackContainer = nullptr; struct aws_array_list headersArray; - /* The caller should never pass a NULL connection. */ - AWS_PRECONDITION(connection != nullptr); + s_fillNativeHeadersArray(headers, &headersArray, m_allocator); + struct aws_event_stream_rpc_message_args msg_args; + msg_args.headers = (struct aws_event_stream_header_value_pair *)headersArray.data; + msg_args.headers_count = headers.size(); + msg_args.payload = payload.has_value() ? (aws_byte_buf *)(&(payload.value())) : nullptr; + msg_args.message_type = messageType; + msg_args.message_flags = messageFlags; - int errorCode = s_fillNativeHeadersArray(headers, &headersArray, connection->m_allocator); - if (!errorCode) + callbackContainer = Crt::New(m_allocator, m_allocator); + callbackContainer->onMessageFlushCallback = std::move(onMessageFlushCallback); + if (sendOutcomePromise) { - struct aws_event_stream_rpc_message_args msg_args; - msg_args.headers = (struct aws_event_stream_header_value_pair *)headersArray.data; - msg_args.headers_count = headers.size(); - msg_args.payload = payload.has_value() ? (aws_byte_buf *)(&(payload.value())) : nullptr; - msg_args.message_type = messageType; - msg_args.message_flags = messageFlags; + callbackContainer->onFlushPromise = std::move(*sendOutcomePromise); + } - /* This heap allocation is necessary so that the flush callback can still be invoked when this function - * returns. */ - callbackContainer = - Crt::New(connection->m_allocator, connection->m_allocator); - callbackContainer->onMessageFlushCallback = onMessageFlushCallback; - callbackContainer->onFlushPromise = std::move(onFlushPromise); + int errorCode = AWS_ERROR_SUCCESS; + { + if (takeLock) + { + m_sharedStateLock.lock(); + } - errorCode = aws_event_stream_rpc_client_connection_send_protocol_message( - connection->m_underlyingConnection, + if (aws_event_stream_rpc_client_connection_send_protocol_message( + m_sharedState.m_underlyingConnection, &msg_args, s_protocolMessageCallback, - reinterpret_cast(callbackContainer)); + reinterpret_cast(callbackContainer))) + { + errorCode = aws_last_error(); + } + + if (takeLock) + { + m_sharedStateLock.unlock(); + } } /* Cleanup. */ @@ -631,59 +827,33 @@ namespace Aws if (errorCode) { - onFlushPromise = std::move(callbackContainer->onFlushPromise); AWS_LOGF_ERROR( AWS_LS_EVENT_STREAM_RPC_CLIENT, "A CRT error occurred while queueing a message to be sent on the connection: %s", Crt::ErrorDebugString(errorCode)); - onFlushPromise.set_value({EVENT_STREAM_RPC_CRT_ERROR, errorCode}); - Crt::Delete(callbackContainer, connection->m_allocator); - } - else - { - return callbackContainer->onFlushPromise.get_future(); - } - - return onFlushPromise.get_future(); - } - - void ClientConnectionImpl::Close() noexcept - { - const std::lock_guard lock(m_stateMutex); - - if (IsOpen()) - { - aws_event_stream_rpc_client_connection_close(this->m_underlyingConnection, AWS_OP_SUCCESS); - } - else if (m_clientState == CONNECTING_SOCKET && !m_connectionWillSetup) - { - m_connectAckedPromise.set_value({EVENT_STREAM_RPC_CONNECTION_CLOSED, 0}); - } + if (sendOutcomePromise) + { + sendOutcomePromise->set_value({EVENT_STREAM_RPC_CRT_ERROR, errorCode}); + } - if (m_clientState != DISCONNECTING && m_clientState != DISCONNECTED) - { - m_clientState = DISCONNECTING; + Crt::Delete(callbackContainer, m_allocator); } - if (m_closeReason.baseStatus == EVENT_STREAM_RPC_UNINITIALIZED) - { - m_closeReason = {EVENT_STREAM_RPC_CONNECTION_CLOSED, 0}; - } + return (errorCode == AWS_ERROR_SUCCESS) ? AWS_OP_SUCCESS : AWS_OP_ERR; } - bool ClientConnectionImpl::IsOpen() const noexcept + std::future ClientConnectionImpl::SendProtocolMessage( + const Crt::List &headers, + const Crt::Optional &payload, + MessageType messageType, + uint32_t messageFlags, + OnMessageFlushCallback onMessageFlushCallback) noexcept { - if (this->m_underlyingConnection == nullptr) - { - return false; - } + std::promise sendOutcomePromise; + auto sendOutcomeFuture = sendOutcomePromise.get_future(); + SendProtocolMessageAux(headers, payload, messageType, messageFlags, std::move(onMessageFlushCallback), &sendOutcomePromise, true); - return aws_event_stream_rpc_client_connection_is_open(this->m_underlyingConnection); - } - - struct aws_event_stream_rpc_client_connection *ClientConnectionImpl::GetUnderlyingHandle() const noexcept - { - return m_underlyingConnection; + return sendOutcomeFuture; } void ClientConnectionImpl::s_onConnectionSetup( @@ -691,59 +861,53 @@ namespace Aws int errorCode, void *userData) noexcept { - /* The `userData` pointer is used to pass `this` of a `ClientConnection` object. */ - auto *thisConnection = static_cast(userData); - - const std::lock_guard lock(thisConnection->m_stateMutex); - - if (errorCode) + auto *impl = static_cast(userData); + if (errorCode != AWS_ERROR_SUCCESS) { - thisConnection->m_clientState = DISCONNECTED; - AWS_LOGF_ERROR( - AWS_LS_EVENT_STREAM_RPC_CLIENT, - "A CRT error occurred while setting up the connection: %s", - Crt::ErrorDebugString(errorCode)); - thisConnection->m_connectAckedPromise.set_value({EVENT_STREAM_RPC_CRT_ERROR, errorCode}); - aws_event_stream_rpc_client_connection_release(connection); - thisConnection->m_underlyingConnection = nullptr; - /* No connection to close on error, so no need to check return value of the callback. */ - (void)thisConnection->m_lifecycleHandler->OnErrorCallback({EVENT_STREAM_RPC_CRT_ERROR, errorCode}); - } - else if (thisConnection->m_clientState == DISCONNECTING || thisConnection->m_clientState == DISCONNECTED) - { - thisConnection->m_underlyingConnection = connection; - thisConnection->m_closeReason = {EVENT_STREAM_RPC_CONNECTION_CLOSED, 0}; - thisConnection->Close(); + AWS_FATAL_ASSERT(connection == nullptr); + impl->MoveToDisconnected({EVENT_STREAM_RPC_CRT_ERROR, errorCode}); + return; } - else + + AWS_FATAL_ASSERT(connection != nullptr); { - thisConnection->m_clientState = WAITING_FOR_CONNECT_ACK; - thisConnection->m_underlyingConnection = connection; + std::lock_guard lock(impl->m_sharedStateLock); + AWS_FATAL_ASSERT(impl->m_sharedState.m_currentState == ClientState::PendingConnect); + impl->m_sharedState.m_underlyingConnection = connection; + if (impl->m_sharedState.m_desiredState != ClientState::Connected) + { + AWS_FATAL_ASSERT(impl->m_sharedState.m_desiredState == ClientState::Disconnected); + impl->m_sharedState.m_currentState = ClientState::Disconnecting; + aws_event_stream_rpc_client_connection_close(connection, AWS_ERROR_EVENT_STREAM_RPC_CONNECTION_CLOSED); + return; + } + + impl->m_sharedState.m_currentState = ClientState::PendingConnack; MessageAmendment messageAmendment; - if (thisConnection->m_connectMessageAmender) + if (impl->m_connectionConfig.GetConnectAmendment().has_value()) { - MessageAmendment connectAmendment(thisConnection->m_connectMessageAmender()); - /* The version header is necessary for establishing the connection. */ + MessageAmendment connectAmendment(impl->m_connectionConfig.GetConnectAmendment().value()); + // The version header is necessary for establishing the connection. messageAmendment.AddHeader(EventStreamHeader( Crt::String(EVENTSTREAM_VERSION_HEADER), Crt::String(EVENTSTREAM_VERSION_STRING), - thisConnection->m_allocator)); + impl->m_allocator)); messageAmendment.PrependHeaders(std::move(connectAmendment).GetHeaders()); messageAmendment.SetPayload(std::move(connectAmendment).GetPayload()); } - /* Send a CONNECT packet to the server. */ - s_sendProtocolMessage( - thisConnection, - messageAmendment.GetHeaders(), + if (impl->SendProtocolMessageAux( messageAmendment.GetHeaders(), messageAmendment.GetPayload(), AWS_EVENT_STREAM_RPC_MESSAGE_TYPE_CONNECT, 0U, - thisConnection->m_onConnectRequestCallback); + impl->m_connectionConfig.GetConnectRequestCallback(), nullptr, false)) + { + impl->m_sharedState.m_callbackContext.SetError({EVENT_STREAM_RPC_CRT_ERROR, aws_last_error()}); + impl->m_sharedState.m_currentState = ClientState::Disconnecting; + aws_event_stream_rpc_client_connection_close(connection, AWS_ERROR_EVENT_STREAM_RPC_CONNECTION_CLOSED); + } } - - thisConnection->m_connectionSetupPromise.set_value(); } void ClientConnectionImpl::s_onConnectionShutdown( @@ -752,51 +916,9 @@ namespace Aws void *userData) noexcept { (void)connection; - /* The `userData` pointer is used to pass `this` of a `ClientConnection` object. */ - auto *thisConnection = static_cast(userData); - - const std::lock_guard lock(thisConnection->m_stateMutex); - - if (thisConnection->m_closeReason.baseStatus == EVENT_STREAM_RPC_UNINITIALIZED && errorCode) - { - thisConnection->m_closeReason = {EVENT_STREAM_RPC_CRT_ERROR, errorCode}; - } - - thisConnection->m_underlyingConnection = nullptr; - - if (thisConnection->m_closeReason.baseStatus != EVENT_STREAM_RPC_UNINITIALIZED && - !thisConnection->m_onConnectCalled) - { - thisConnection->m_connectAckedPromise.set_value(thisConnection->m_closeReason); - } - - thisConnection->m_clientState = DISCONNECTED; - - if (thisConnection->m_onConnectCalled) - { - if (errorCode) - { - thisConnection->m_lifecycleHandler->OnDisconnectCallback({EVENT_STREAM_RPC_CRT_ERROR, errorCode}); - } - else - { - thisConnection->m_lifecycleHandler->OnDisconnectCallback({EVENT_STREAM_RPC_SUCCESS, 0}); - } - thisConnection->m_onConnectCalled = false; - } + auto *impl = static_cast(userData); - if (errorCode) - { - AWS_LOGF_ERROR( - AWS_LS_EVENT_STREAM_RPC_CLIENT, - "A CRT error occurred while shutting down the connection: %s", - Crt::ErrorDebugString(errorCode)); - thisConnection->m_closedPromise.set_value({EVENT_STREAM_RPC_CRT_ERROR, errorCode}); - } - else - { - thisConnection->m_closedPromise.set_value({EVENT_STREAM_RPC_SUCCESS, errorCode}); - } + impl->MoveToDisconnected({EVENT_STREAM_RPC_CONNECTION_CLOSED, errorCode}); } void ClientConnectionImpl::s_onProtocolMessage( @@ -807,90 +929,73 @@ namespace Aws AWS_PRECONDITION(messageArgs != nullptr); (void)connection; - /* The `userData` pointer is used to pass `this` of a `ClientConnection` object. */ - auto *thisConnection = static_cast(userData); - Crt::List pingHeaders; - + auto *impl = static_cast(userData); switch (messageArgs->message_type) { case AWS_EVENT_STREAM_RPC_MESSAGE_TYPE_CONNECT_ACK: - thisConnection->m_stateMutex.lock(); - if (thisConnection->m_clientState == WAITING_FOR_CONNECT_ACK) + { + ConnectionCallbackContext localCallbackContext = {}; + bool successfulAck = (messageArgs->message_flags & AWS_EVENT_STREAM_RPC_MESSAGE_FLAG_CONNECTION_ACCEPTED) != 0; { - if (messageArgs->message_flags & AWS_EVENT_STREAM_RPC_MESSAGE_FLAG_CONNECTION_ACCEPTED) + std::lock_guard lock(impl->m_sharedStateLock); + if (impl->m_sharedState.m_currentState != ClientState::PendingConnack || !successfulAck) { - thisConnection->m_clientState = CONNECTED; - thisConnection->m_onConnectCalled = true; - thisConnection->m_connectAckedPromise.set_value({EVENT_STREAM_RPC_SUCCESS, 0}); - thisConnection->m_lifecycleHandler->OnConnectCallback(); + if (!impl->m_sharedState.m_hasShutDown) + { + impl->m_sharedState.m_callbackContext.SetError( + (successfulAck) ? RpcError{EVENT_STREAM_RPC_CONNECTION_CLOSED, 0} : RpcError{EVENT_STREAM_RPC_CONNECTION_ACCESS_DENIED, 0} + ); + } + impl->m_sharedState.m_desiredState = ClientState::Disconnected; + if (impl->m_sharedState.m_currentState != ClientState::Disconnecting) + { + impl->m_sharedState.m_currentState = ClientState::Disconnecting; + aws_event_stream_rpc_client_connection_close(impl->m_sharedState.m_underlyingConnection, AWS_ERROR_EVENT_STREAM_RPC_CONNECTION_CLOSED); + } + return; } - else - { - thisConnection->m_closeReason = {EVENT_STREAM_RPC_CONNECTION_ACCESS_DENIED, 0}; - thisConnection->Close(); - } - } - else - { - /* Unexpected CONNECT_ACK received. */ - } - thisConnection->m_stateMutex.unlock(); + impl->m_sharedState.m_currentState = ClientState::Connected; + localCallbackContext = impl->m_sharedState.m_callbackContext.TransitionToConnected(); + } + localCallbackContext.InvokeCallbacks(); break; + } case AWS_EVENT_STREAM_RPC_MESSAGE_TYPE_PING: - + { + Crt::List pingHeaders; for (size_t i = 0; i < messageArgs->headers_count; ++i) { pingHeaders.emplace_back( - EventStreamHeader(messageArgs->headers[i], thisConnection->m_allocator)); + EventStreamHeader(messageArgs->headers[i], impl->m_allocator)); } if (messageArgs->payload) { - thisConnection->m_lifecycleHandler->OnPingCallback(pingHeaders, *messageArgs->payload); + impl->m_lifecycleHandler->OnPingCallback(pingHeaders, *messageArgs->payload); } else { - thisConnection->m_lifecycleHandler->OnPingCallback(pingHeaders, Crt::Optional()); + impl->m_lifecycleHandler->OnPingCallback(pingHeaders, Crt::Optional()); } break; + } case AWS_EVENT_STREAM_RPC_MESSAGE_TYPE_PING_RESPONSE: - return; - break; - - case AWS_EVENT_STREAM_RPC_MESSAGE_TYPE_PROTOCOL_ERROR: - case AWS_EVENT_STREAM_RPC_MESSAGE_TYPE_INTERNAL_ERROR: - - if (thisConnection->m_lifecycleHandler->OnErrorCallback( - {EVENT_STREAM_RPC_CRT_ERROR, AWS_ERROR_EVENT_STREAM_RPC_PROTOCOL_ERROR})) - { - thisConnection->Close(); - } - break; default: - - if (thisConnection->m_lifecycleHandler->OnErrorCallback( - {EVENT_STREAM_RPC_UNKNOWN_PROTOCOL_MESSAGE, 0})) - { - thisConnection->Close(); - } - + impl->m_lifecycleHandler->OnErrorCallback( + {EVENT_STREAM_RPC_CRT_ERROR, AWS_ERROR_EVENT_STREAM_RPC_PROTOCOL_ERROR}); + impl->Close(); break; } } - ClientContinuation ClientConnectionImpl::NewStream(ClientContinuationHandler &clientContinuationHandler) noexcept - { - return ClientContinuation(m_underlyingConnection, clientContinuationHandler, m_allocator); - } - - ClientConnection::ClientConnection(Crt::Allocator *allocator) noexcept - :m_impl(Aws::Crt::MakeShared(allocator, allocator)) + ClientConnection::ClientConnection(Crt::Allocator *allocator, aws_client_bootstrap *bootstrap) noexcept + :m_impl(Aws::Crt::MakeShared(allocator, allocator, bootstrap)) { } @@ -902,10 +1007,9 @@ namespace Aws std::future ClientConnection::Connect( const ConnectionConfig &connectionOptions, - ConnectionLifecycleHandler *connectionLifecycleHandler, - Crt::Io::ClientBootstrap &clientBootstrap) noexcept + ConnectionLifecycleHandler *connectionLifecycleHandler) noexcept { - return m_impl->Connect(connectionOptions, connectionLifecycleHandler, clientBootstrap); + return m_impl->Connect(connectionOptions, connectionLifecycleHandler); } ClientContinuation ClientConnection::NewStream(ClientContinuationHandler &clientContinuationHandler) noexcept @@ -923,11 +1027,6 @@ namespace Aws return m_impl->IsOpen(); } - struct aws_event_stream_rpc_client_connection *ClientConnection::GetUnderlyingHandle() const noexcept - { - return m_impl->GetUnderlyingHandle(); - } - void AbstractShapeBase::s_customDeleter(AbstractShapeBase *shape) noexcept { if (shape->m_allocator != nullptr) @@ -1082,7 +1181,7 @@ namespace Aws return onFlushPromise.get_future(); } - int errorCode = s_fillNativeHeadersArray(headers, &headersArray, m_allocator); + s_fillNativeHeadersArray(headers, &headersArray, m_allocator); /* * Regardless of how the promise gets moved around (or not), this future should stay valid as a return @@ -1093,28 +1192,25 @@ namespace Aws */ std::future retValue = onFlushPromise.get_future(); - if (!errorCode) - { - struct aws_event_stream_rpc_message_args msg_args; - msg_args.headers = (struct aws_event_stream_header_value_pair *)headersArray.data; - msg_args.headers_count = headers.size(); - msg_args.payload = payload.has_value() ? (aws_byte_buf *)(&(payload.value())) : nullptr; - msg_args.message_type = messageType; - msg_args.message_flags = messageFlags; - - /* This heap allocation is necessary so that the flush callback can still be invoked when this function - * returns. */ - callbackContainer = Crt::New(m_allocator, m_allocator); - callbackContainer->onMessageFlushCallback = onMessageFlushCallback; - callbackContainer->onFlushPromise = std::move(onFlushPromise); - - errorCode = aws_event_stream_rpc_client_continuation_activate( - m_continuationToken, - Crt::ByteCursorFromCString(operationName.c_str()), - &msg_args, - s_protocolMessageCallback, - reinterpret_cast(callbackContainer)); - } + struct aws_event_stream_rpc_message_args msg_args; + msg_args.headers = (struct aws_event_stream_header_value_pair *)headersArray.data; + msg_args.headers_count = headers.size(); + msg_args.payload = payload.has_value() ? (aws_byte_buf *)(&(payload.value())) : nullptr; + msg_args.message_type = messageType; + msg_args.message_flags = messageFlags; + + /* This heap allocation is necessary so that the flush callback can still be invoked when this function + * returns. */ + callbackContainer = Crt::New(m_allocator, m_allocator); + callbackContainer->onMessageFlushCallback = onMessageFlushCallback; + callbackContainer->onFlushPromise = std::move(onFlushPromise); + + int errorCode = aws_event_stream_rpc_client_continuation_activate( + m_continuationToken, + Crt::ByteCursorFromCString(operationName.c_str()), + &msg_args, + s_protocolMessageCallback, + reinterpret_cast(callbackContainer)); /* Cleanup. */ if (aws_array_list_is_valid(&headersArray)) @@ -1149,30 +1245,31 @@ namespace Aws return onFlushPromise.get_future(); } - int errorCode = s_fillNativeHeadersArray(headers, &headersArray, m_allocator); + s_fillNativeHeadersArray(headers, &headersArray, m_allocator); - if (!errorCode) - { - struct aws_event_stream_rpc_message_args msg_args; - msg_args.headers = (struct aws_event_stream_header_value_pair *)headersArray.data; - msg_args.headers_count = headers.size(); - msg_args.payload = payload.has_value() ? (aws_byte_buf *)(&(payload.value())) : nullptr; - msg_args.message_type = messageType; - msg_args.message_flags = messageFlags; + struct aws_event_stream_rpc_message_args msg_args; + msg_args.headers = (struct aws_event_stream_header_value_pair *)headersArray.data; + msg_args.headers_count = headers.size(); + msg_args.payload = payload.has_value() ? (aws_byte_buf *)(&(payload.value())) : nullptr; + msg_args.message_type = messageType; + msg_args.message_flags = messageFlags; - /* This heap allocation is necessary so that the flush callback can still be invoked when this function - * returns. */ - callbackContainer = Crt::New(m_allocator, m_allocator); - callbackContainer->onMessageFlushCallback = onMessageFlushCallback; - callbackContainer->onFlushPromise = std::move(onFlushPromise); + /* This heap allocation is necessary so that the flush callback can still be invoked when this function + * returns. */ + callbackContainer = Crt::New(m_allocator, m_allocator); + callbackContainer->onMessageFlushCallback = onMessageFlushCallback; + callbackContainer->onFlushPromise = std::move(onFlushPromise); - if (m_continuationToken) + int errorCode = AWS_OP_SUCCESS; + if (m_continuationToken) + { + if (aws_event_stream_rpc_client_continuation_send_message( + m_continuationToken, + &msg_args, + s_protocolMessageCallback, + reinterpret_cast(callbackContainer))) { - errorCode = aws_event_stream_rpc_client_continuation_send_message( - m_continuationToken, - &msg_args, - s_protocolMessageCallback, - reinterpret_cast(callbackContainer)); + errorCode = aws_last_error(); } } diff --git a/eventstream_rpc/tests/EchoTestRpcClient.cpp b/eventstream_rpc/tests/EchoTestRpcClient.cpp index 05fe07477..ee911b0c8 100644 --- a/eventstream_rpc/tests/EchoTestRpcClient.cpp +++ b/eventstream_rpc/tests/EchoTestRpcClient.cpp @@ -14,7 +14,7 @@ namespace Awstest EchoTestRpcClient::EchoTestRpcClient( Aws::Crt::Io::ClientBootstrap &clientBootstrap, Aws::Crt::Allocator *allocator) noexcept - : m_connection(allocator), m_clientBootstrap(clientBootstrap), m_allocator(allocator), + : m_connection(allocator, clientBootstrap.GetUnderlyingHandle()), m_allocator(allocator), m_asyncLaunchMode(std::launch::deferred) { m_echoTestRpcServiceModel.AssignModelNameToErrorResponse( @@ -25,7 +25,7 @@ namespace Awstest ConnectionLifecycleHandler &lifecycleHandler, const ConnectionConfig &connectionConfig) noexcept { - return m_connection.Connect(connectionConfig, &lifecycleHandler, m_clientBootstrap); + return m_connection.Connect(connectionConfig, &lifecycleHandler); } void EchoTestRpcClient::Close() noexcept diff --git a/eventstream_rpc/tests/EventStreamClientTest.cpp b/eventstream_rpc/tests/EventStreamClientTest.cpp index 05dcf2a52..9082678f2 100644 --- a/eventstream_rpc/tests/EventStreamClientTest.cpp +++ b/eventstream_rpc/tests/EventStreamClientTest.cpp @@ -131,8 +131,8 @@ static int s_TestEventStreamConnectSuccess(struct aws_allocator *allocator, void connectionConfig.SetConnectAmendment(connectionAmendment); TestLifecycleHandler lifecycleHandler; - ClientConnection connection(allocator); - auto future = connection.Connect(connectionConfig, &lifecycleHandler, *testContext.clientBootstrap); + ClientConnection connection(allocator, testContext.clientBootstrap->GetUnderlyingHandle()); + auto future = connection.Connect(connectionConfig, &lifecycleHandler); EventStreamRpcStatusCode clientStatus = future.get().baseStatus; ASSERT_INT_EQUALS(EVENT_STREAM_RPC_SUCCESS, clientStatus); @@ -159,8 +159,8 @@ static int s_TestEventStreamConnectFailureNoAuthHeader(struct aws_allocator *all connectionConfig.SetPort(testContext.echoServerPort); TestLifecycleHandler lifecycleHandler; - ClientConnection connection(allocator); - auto future = connection.Connect(connectionConfig, &lifecycleHandler, *testContext.clientBootstrap); + ClientConnection connection(allocator, testContext.clientBootstrap->GetUnderlyingHandle()); + auto future = connection.Connect(connectionConfig, &lifecycleHandler); EventStreamRpcStatusCode clientStatus = future.get().baseStatus; ASSERT_TRUE( @@ -193,8 +193,8 @@ static int s_TestEventStreamConnectFailureBadAuthHeader(struct aws_allocator *al connectionConfig.SetConnectAmendment(connectionAmendment); TestLifecycleHandler lifecycleHandler; - ClientConnection connection(allocator); - auto future = connection.Connect(connectionConfig, &lifecycleHandler, *testContext.clientBootstrap); + ClientConnection connection(allocator, testContext.clientBootstrap->GetUnderlyingHandle()); + auto future = connection.Connect(connectionConfig, &lifecycleHandler); EventStreamRpcStatusCode clientStatus = future.get().baseStatus; ASSERT_TRUE( diff --git a/eventstream_rpc/tests/include/awstest/EchoTestRpcClient.h b/eventstream_rpc/tests/include/awstest/EchoTestRpcClient.h index 0afe066f1..0f1f8b50b 100644 --- a/eventstream_rpc/tests/include/awstest/EchoTestRpcClient.h +++ b/eventstream_rpc/tests/include/awstest/EchoTestRpcClient.h @@ -77,7 +77,6 @@ namespace Awstest private: EchoTestRpcServiceModel m_echoTestRpcServiceModel; ClientConnection m_connection; - Aws::Crt::Io::ClientBootstrap &m_clientBootstrap; Aws::Crt::Allocator *m_allocator; MessageAmendment m_connectAmendment; std::launch m_asyncLaunchMode; diff --git a/greengrass_ipc/include/aws/greengrass/GreengrassCoreIpcClient.h b/greengrass_ipc/include/aws/greengrass/GreengrassCoreIpcClient.h index 502031d4f..393da1747 100644 --- a/greengrass_ipc/include/aws/greengrass/GreengrassCoreIpcClient.h +++ b/greengrass_ipc/include/aws/greengrass/GreengrassCoreIpcClient.h @@ -236,7 +236,6 @@ namespace Aws private: GreengrassCoreIpcServiceModel m_greengrassCoreIpcServiceModel; ClientConnection m_connection; - Aws::Crt::Io::ClientBootstrap &m_clientBootstrap; Aws::Crt::Allocator *m_allocator; MessageAmendment m_connectAmendment; std::launch m_asyncLaunchMode; diff --git a/greengrass_ipc/source/GreengrassCoreIpcClient.cpp b/greengrass_ipc/source/GreengrassCoreIpcClient.cpp index a1db24d5a..9afa6b197 100644 --- a/greengrass_ipc/source/GreengrassCoreIpcClient.cpp +++ b/greengrass_ipc/source/GreengrassCoreIpcClient.cpp @@ -16,7 +16,7 @@ namespace Aws GreengrassCoreIpcClient::GreengrassCoreIpcClient( Aws::Crt::Io::ClientBootstrap &clientBootstrap, Aws::Crt::Allocator *allocator) noexcept - : m_connection(allocator), m_clientBootstrap(clientBootstrap), m_allocator(allocator), + : m_connection(allocator, clientBootstrap.GetUnderlyingHandle()), m_allocator(allocator), m_asyncLaunchMode(std::launch::deferred) { m_greengrassCoreIpcServiceModel.AssignModelNameToErrorResponse( @@ -55,7 +55,7 @@ namespace Aws ConnectionLifecycleHandler &lifecycleHandler, const ConnectionConfig &connectionConfig) noexcept { - return m_connection.Connect(connectionConfig, &lifecycleHandler, m_clientBootstrap); + return m_connection.Connect(connectionConfig, &lifecycleHandler); } void GreengrassCoreIpcClient::Close() noexcept From 180f1500e4b36fa34244ee1dfcb4782633a908fc Mon Sep 17 00:00:00 2001 From: Bret Ambrose Date: Wed, 14 May 2025 20:14:24 -0700 Subject: [PATCH 33/43] Fixes --- eventstream_rpc/source/EventStreamClient.cpp | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/eventstream_rpc/source/EventStreamClient.cpp b/eventstream_rpc/source/EventStreamClient.cpp index 1ab76bf00..f533b1dfb 100644 --- a/eventstream_rpc/source/EventStreamClient.cpp +++ b/eventstream_rpc/source/EventStreamClient.cpp @@ -456,7 +456,13 @@ namespace Aws } void SetAction(ConnectionCallbackActionType action) { m_action = action; } - void SetError(RpcError error) { m_error = error; } + void SetError(RpcError error) + { + if (m_error.baseStatus == EVENT_STREAM_RPC_SUCCESS) + { + m_error = error; + } + } std::future GetConnectPromiseFuture() { return m_connectPromise.get_future(); } @@ -873,6 +879,9 @@ namespace Aws { std::lock_guard lock(impl->m_sharedStateLock); AWS_FATAL_ASSERT(impl->m_sharedState.m_currentState == ClientState::PendingConnect); + + // the channel owns the initial ref; we have to take our own + aws_event_stream_rpc_client_connection_acquire(connection); impl->m_sharedState.m_underlyingConnection = connection; if (impl->m_sharedState.m_desiredState != ClientState::Connected) { From 83b950e9090cfdc9a496b9d4b67c7968834bd2b6 Mon Sep 17 00:00:00 2001 From: Bret Ambrose Date: Tue, 20 May 2025 13:21:28 -0700 Subject: [PATCH 34/43] Checkpoint --- .../aws/eventstreamrpc/EventStreamClient.h | 30 +-- eventstream_rpc/source/EventStreamClient.cpp | 235 +++++++++++------- eventstream_rpc/tests/CMakeLists.txt | 7 +- .../tests/EventStreamClientTest.cpp | 202 ++++++++++++++- 4 files changed, 359 insertions(+), 115 deletions(-) diff --git a/eventstream_rpc/include/aws/eventstreamrpc/EventStreamClient.h b/eventstream_rpc/include/aws/eventstreamrpc/EventStreamClient.h index 7f87df0f1..4577e1762 100644 --- a/eventstream_rpc/include/aws/eventstreamrpc/EventStreamClient.h +++ b/eventstream_rpc/include/aws/eventstreamrpc/EventStreamClient.h @@ -141,19 +141,19 @@ namespace Aws enum EventStreamRpcStatusCode { - EVENT_STREAM_RPC_SUCCESS = 0, - EVENT_STREAM_RPC_NULL_PARAMETER, - EVENT_STREAM_RPC_UNINITIALIZED, - EVENT_STREAM_RPC_ALLOCATION_ERROR, - EVENT_STREAM_RPC_CONNECTION_SETUP_FAILED, - EVENT_STREAM_RPC_CONNECTION_ACCESS_DENIED, - EVENT_STREAM_RPC_CONNECTION_ALREADY_ESTABLISHED, - EVENT_STREAM_RPC_CONNECTION_CLOSED, - EVENT_STREAM_RPC_CONTINUATION_CLOSED, - EVENT_STREAM_RPC_UNKNOWN_PROTOCOL_MESSAGE, - EVENT_STREAM_RPC_UNMAPPED_DATA, - EVENT_STREAM_RPC_UNSUPPORTED_CONTENT_TYPE, - EVENT_STREAM_RPC_CRT_ERROR + EVENT_STREAM_RPC_SUCCESS = 0, + EVENT_STREAM_RPC_NULL_PARAMETER, + EVENT_STREAM_RPC_UNINITIALIZED, + EVENT_STREAM_RPC_ALLOCATION_ERROR, + EVENT_STREAM_RPC_CONNECTION_SETUP_FAILED, + EVENT_STREAM_RPC_CONNECTION_ACCESS_DENIED, + EVENT_STREAM_RPC_CONNECTION_ALREADY_ESTABLISHED, + EVENT_STREAM_RPC_CONNECTION_CLOSED, + EVENT_STREAM_RPC_CONTINUATION_CLOSED, + EVENT_STREAM_RPC_UNKNOWN_PROTOCOL_MESSAGE, + EVENT_STREAM_RPC_UNMAPPED_DATA, + EVENT_STREAM_RPC_UNSUPPORTED_CONTENT_TYPE, + EVENT_STREAM_RPC_CRT_ERROR }; /** @@ -260,7 +260,6 @@ namespace Aws const Crt::Optional &payload); }; - class ClientConnectionImpl; /** @@ -269,7 +268,6 @@ namespace Aws class AWS_EVENTSTREAMRPC_API ClientConnection final { public: - explicit ClientConnection(Crt::Allocator *allocator, aws_client_bootstrap *bootstrap) noexcept; ~ClientConnection() noexcept; @@ -303,7 +301,6 @@ namespace Aws bool IsOpen() const noexcept; private: - std::shared_ptr m_impl; }; @@ -776,6 +773,5 @@ namespace Aws std::condition_variable m_closeReady; }; - } // namespace Eventstreamrpc } // namespace Aws diff --git a/eventstream_rpc/source/EventStreamClient.cpp b/eventstream_rpc/source/EventStreamClient.cpp index f533b1dfb..0de055d97 100644 --- a/eventstream_rpc/source/EventStreamClient.cpp +++ b/eventstream_rpc/source/EventStreamClient.cpp @@ -253,6 +253,21 @@ namespace Aws Crt::Allocator *allocator) : m_allocator(allocator), m_valueByteBuf({}), m_underlyingHandle(header) { + switch (header.header_value_type) + { + case AWS_EVENT_STREAM_HEADER_STRING: + case AWS_EVENT_STREAM_HEADER_BYTE_BUF: + // Unsafe to copy C struct by value. Copy the referenced buffer and fix up pointers. + m_valueByteBuf = + Crt::ByteBufNewCopy(allocator, header.header_value.variable_len_val, header.header_value_len); + m_underlyingHandle.header_value.variable_len_val = m_valueByteBuf.buffer; + m_underlyingHandle.header_value_len = m_valueByteBuf.len; + break; + + default: + // C struct can be copied by value safely + break; + } } EventStreamHeader::EventStreamHeader( @@ -296,6 +311,10 @@ namespace Aws EventStreamHeader &EventStreamHeader::operator=(const EventStreamHeader &lhs) noexcept { m_allocator = lhs.m_allocator; + if (aws_byte_buf_is_valid(&m_valueByteBuf)) + { + Crt::ByteBufDelete(m_valueByteBuf); + } m_valueByteBuf = Crt::ByteBufNewCopy(lhs.m_allocator, lhs.m_valueByteBuf.buffer, lhs.m_valueByteBuf.len); m_underlyingHandle = lhs.m_underlyingHandle; m_underlyingHandle.header_value.variable_len_val = m_valueByteBuf.buffer; @@ -335,23 +354,24 @@ namespace Aws return true; } -/* - * Eventstream Connection Refactor - * - * Part 1 of an effort to refactor the eventstream bindings to conform to the latest CRT binding guidelines. - * - * For connections, we now enforce the following invariants for correctness: - * - * 1. No callback is made while a lock is held. We perform callbacks by having a transactional callback context that - * is moved out of shared state (under the lock), but the callback context does not perform its work until the lock - * is released. - * 2. No destructor blocking or synchronization. In order to provide the best behavioral backwards compatibility, we - * "synthesize" the callbacks that would occur at destruction when we kick off the async cleanup process. When the - * asynchronous events occur that would normally trigger a callback occur, we ignore them via the has_shut_down flag. - * 3. A self-reference (via shared_ptr member) guarantees the binding impl stays alive longer than the C objects. The - * public binding object also keeps a shared_ptr to the impl, so final destruction only occurs once both the public - * binding object's destructor has run and no C connection object is still alive. - */ + /* + * Eventstream Connection Refactor + * + * Part 1 of an effort to refactor the eventstream bindings to conform to the latest CRT binding guidelines. + * + * For connections, we now enforce the following invariants for correctness: + * + * 1. No callback is made while a lock is held. We perform callbacks by having a transactional callback context + * that is moved out of shared state (under the lock), but the callback context does not perform its work until + * the lock is released. + * 2. No destructor blocking or synchronization. In order to provide the best behavioral backwards + * compatibility, we "synthesize" the callbacks that would occur at destruction when we kick off the async + * cleanup process. When the asynchronous events occur that would normally trigger a callback occur, we ignore + * them via the has_shut_down flag. + * 3. A self-reference (via shared_ptr member) guarantees the binding impl stays alive longer than the C + * objects. The public binding object also keeps a shared_ptr to the impl, so final destruction only occurs + * once both the public binding object's destructor has run and no C connection object is still alive. + */ /* What kind of callback should this context trigger? */ enum class ConnectionCallbackActionType @@ -374,24 +394,27 @@ namespace Aws class ConnectionCallbackContext { public: - ConnectionCallbackContext() : m_action(ConnectionCallbackActionType::None), m_error{EVENT_STREAM_RPC_SUCCESS, AWS_ERROR_SUCCESS} {} - - ConnectionCallbackContext(const std::function &disconnectionCallback, const std::function &errorCallback, const std::function &connectionSuccessCallback) : - m_action(ConnectionCallbackActionType::CompleteConnectPromise), - m_error{EVENT_STREAM_RPC_SUCCESS, AWS_ERROR_SUCCESS}, - m_connectPromise(), - m_disconnectionCallback(disconnectionCallback), - m_errorCallback(errorCallback), - m_connectionSuccessCallback(connectionSuccessCallback) - {} - - ConnectionCallbackContext(ConnectionCallbackContext &&rhs) noexcept : - m_action(rhs.m_action), - m_error(rhs.m_error), - m_connectPromise(std::move(rhs.m_connectPromise)), - m_disconnectionCallback(std::move(rhs.m_disconnectionCallback)), - m_errorCallback(std::move(rhs.m_errorCallback)), - m_connectionSuccessCallback(std::move(rhs.m_connectionSuccessCallback)) + ConnectionCallbackContext() + : m_action(ConnectionCallbackActionType::None), m_error{EVENT_STREAM_RPC_SUCCESS, AWS_ERROR_SUCCESS} + { + } + + ConnectionCallbackContext( + const std::function &disconnectionCallback, + const std::function &errorCallback, + const std::function &connectionSuccessCallback) + : m_action(ConnectionCallbackActionType::CompleteConnectPromise), + m_error{EVENT_STREAM_RPC_SUCCESS, AWS_ERROR_SUCCESS}, m_connectPromise(), + m_disconnectionCallback(disconnectionCallback), m_errorCallback(errorCallback), + m_connectionSuccessCallback(connectionSuccessCallback) + { + } + + ConnectionCallbackContext(ConnectionCallbackContext &&rhs) noexcept + : m_action(rhs.m_action), m_error(rhs.m_error), m_connectPromise(std::move(rhs.m_connectPromise)), + m_disconnectionCallback(std::move(rhs.m_disconnectionCallback)), + m_errorCallback(std::move(rhs.m_errorCallback)), + m_connectionSuccessCallback(std::move(rhs.m_connectionSuccessCallback)) { rhs.ClearContext(); } @@ -466,8 +489,7 @@ namespace Aws std::future GetConnectPromiseFuture() { return m_connectPromise.get_future(); } - private: - + private: /* Wipes out state in a context to guarantee it does nothing if someone tries to InvokeCallbacks on it */ void ClearContext() { @@ -490,7 +512,6 @@ namespace Aws class ClientConnectionImpl final : public std::enable_shared_from_this { public: - explicit ClientConnectionImpl(Crt::Allocator *allocator, aws_client_bootstrap *bootstrap) noexcept; ~ClientConnectionImpl() noexcept; @@ -514,7 +535,6 @@ namespace Aws OnMessageFlushCallback onMessageFlushCallback) noexcept; private: - void CloseInternal(bool isShutdown) noexcept; void MoveToDisconnected(RpcError error) noexcept; @@ -569,13 +589,12 @@ namespace Aws struct aws_event_stream_rpc_client_connection *connection, const struct aws_event_stream_rpc_message_args *messageArgs, void *userData) noexcept; - }; ClientConnectionImpl::ClientConnectionImpl(Crt::Allocator *allocator, aws_client_bootstrap *bootstrap) noexcept - : m_allocator(allocator), m_lifecycleHandler(nullptr), m_connectMessageAmender(nullptr), - m_bootstrap(aws_client_bootstrap_acquire(bootstrap)), m_eventLoop(nullptr), - m_sharedState { nullptr, ClientState::Disconnected, ClientState::Disconnected, {}, false} + : m_allocator(allocator), m_lifecycleHandler(nullptr), m_connectMessageAmender(nullptr), + m_bootstrap(aws_client_bootstrap_acquire(bootstrap)), m_eventLoop(nullptr), + m_sharedState{nullptr, ClientState::Disconnected, ClientState::Disconnected, {}, false} { m_eventLoop = aws_event_loop_group_get_next_loop(bootstrap->event_loop_group); } @@ -587,8 +606,11 @@ namespace Aws // We use a task to zero out the self reference to make sure we are not in a call stack that includes // a member function of the connection impl itself when potentially releasing the final reference. - struct AwsEventstreamConnectionImplClearSharedTask { - AwsEventstreamConnectionImplClearSharedTask(Aws::Crt::Allocator *allocator, ClientConnectionImpl *clientConnectionImpl) noexcept; + struct AwsEventstreamConnectionImplClearSharedTask + { + AwsEventstreamConnectionImplClearSharedTask( + Aws::Crt::Allocator *allocator, + ClientConnectionImpl *clientConnectionImpl) noexcept; struct aws_task m_task; struct aws_allocator *m_allocator; @@ -603,17 +625,18 @@ namespace Aws Aws::Crt::Delete(clearSharedTask, clearSharedTask->m_allocator); } - AwsEventstreamConnectionImplClearSharedTask::AwsEventstreamConnectionImplClearSharedTask(Aws::Crt::Allocator *allocator, ClientConnectionImpl *clientConnectionImpl) noexcept : - m_task{}, - m_allocator(allocator), - m_impl(clientConnectionImpl->shared_from_this()) + AwsEventstreamConnectionImplClearSharedTask::AwsEventstreamConnectionImplClearSharedTask( + Aws::Crt::Allocator *allocator, + ClientConnectionImpl *clientConnectionImpl) noexcept + : m_task{}, m_allocator(allocator), m_impl(clientConnectionImpl->shared_from_this()) { aws_task_init(&m_task, s_zeroSharedReference, this, "AwsEventstreamConnectionImplClearSharedTask"); } void ClientConnectionImpl::MoveToDisconnected(RpcError error) noexcept { - auto *clearSharedTask = Aws::Crt::New(m_allocator, m_allocator, this); + auto *clearSharedTask = + Aws::Crt::New(m_allocator, m_allocator, this); ConnectionCallbackContext localContext = {}; { std::lock_guard lock(m_sharedStateLock); @@ -641,16 +664,19 @@ namespace Aws { std::lock_guard lock(m_sharedStateLock); m_sharedState.m_desiredState = ClientState::Disconnected; - m_sharedState.m_callbackContext.SetError({EVENT_STREAM_RPC_CONNECTION_CLOSED, AWS_ERROR_EVENT_STREAM_RPC_CONNECTION_CLOSED}); + m_sharedState.m_callbackContext.SetError( + {EVENT_STREAM_RPC_CONNECTION_CLOSED, AWS_ERROR_EVENT_STREAM_RPC_CONNECTION_CLOSED}); if (isShutdown) { m_sharedState.m_hasShutDown = true; } - if (m_sharedState.m_currentState == ClientState::Connected || m_sharedState.m_currentState == ClientState::PendingConnack) + if (m_sharedState.m_currentState == ClientState::Connected || + m_sharedState.m_currentState == ClientState::PendingConnack) { m_sharedState.m_currentState = ClientState::Disconnecting; - aws_event_stream_rpc_client_connection_close(m_sharedState.m_underlyingConnection, AWS_ERROR_EVENT_STREAM_RPC_CONNECTION_CLOSED); + aws_event_stream_rpc_client_connection_close( + m_sharedState.m_underlyingConnection, AWS_ERROR_EVENT_STREAM_RPC_CONNECTION_CLOSED); } if (isShutdown) @@ -696,16 +722,20 @@ namespace Aws } m_lifecycleHandler = connectionLifecycleHandler; - std::function disconnectCallback = [this](RpcError error) { this->m_lifecycleHandler->OnDisconnectCallback(error); }; - std::function errorCallback = [this](RpcError error){ return this->m_lifecycleHandler->OnErrorCallback(error); }; - std::function connectionSuccessCallback = [this](){ this->m_lifecycleHandler->OnConnectCallback(); }; + std::function disconnectCallback = [this](RpcError error) + { this->m_lifecycleHandler->OnDisconnectCallback(error); }; + std::function errorCallback = [this](RpcError error) + { return this->m_lifecycleHandler->OnErrorCallback(error); }; + std::function connectionSuccessCallback = [this]() + { this->m_lifecycleHandler->OnConnectCallback(); }; m_connectionConfig = connectionConfig; m_selfReference = shared_from_this(); m_sharedState.m_desiredState = ClientState::Connected; m_sharedState.m_currentState = ClientState::PendingConnect; - m_sharedState.m_callbackContext = {std::move(disconnectCallback), std::move(errorCallback), std::move(connectionSuccessCallback)}; - localFuture = m_sharedState.m_callbackContext.GetConnectPromiseFuture(); + m_sharedState.m_callbackContext = { + std::move(disconnectCallback), std::move(errorCallback), std::move(connectionSuccessCallback)}; + localFuture = m_sharedState.m_callbackContext.GetConnectPromiseFuture(); } Crt::Io::SocketOptions socketOptions; @@ -770,7 +800,8 @@ namespace Aws Crt::Delete(callbackData, callbackData->allocator); } - ClientContinuation ClientConnectionImpl::NewStream(ClientContinuationHandler &clientContinuationHandler) noexcept + ClientContinuation ClientConnectionImpl::NewStream( + ClientContinuationHandler &clientContinuationHandler) noexcept { std::lock_guard lock(m_sharedStateLock); return {m_sharedState.m_underlyingConnection, clientContinuationHandler, m_allocator}; @@ -811,10 +842,10 @@ namespace Aws } if (aws_event_stream_rpc_client_connection_send_protocol_message( - m_sharedState.m_underlyingConnection, - &msg_args, - s_protocolMessageCallback, - reinterpret_cast(callbackContainer))) + m_sharedState.m_underlyingConnection, + &msg_args, + s_protocolMessageCallback, + reinterpret_cast(callbackContainer))) { errorCode = aws_last_error(); } @@ -857,7 +888,14 @@ namespace Aws { std::promise sendOutcomePromise; auto sendOutcomeFuture = sendOutcomePromise.get_future(); - SendProtocolMessageAux(headers, payload, messageType, messageFlags, std::move(onMessageFlushCallback), &sendOutcomePromise, true); + SendProtocolMessageAux( + headers, + payload, + messageType, + messageFlags, + std::move(onMessageFlushCallback), + &sendOutcomePromise, + true); return sendOutcomeFuture; } @@ -887,7 +925,8 @@ namespace Aws { AWS_FATAL_ASSERT(impl->m_sharedState.m_desiredState == ClientState::Disconnected); impl->m_sharedState.m_currentState = ClientState::Disconnecting; - aws_event_stream_rpc_client_connection_close(connection, AWS_ERROR_EVENT_STREAM_RPC_CONNECTION_CLOSED); + aws_event_stream_rpc_client_connection_close( + connection, AWS_ERROR_EVENT_STREAM_RPC_CONNECTION_CLOSED); return; } @@ -906,15 +945,19 @@ namespace Aws messageAmendment.SetPayload(std::move(connectAmendment).GetPayload()); } - if (impl->SendProtocolMessageAux( messageAmendment.GetHeaders(), - messageAmendment.GetPayload(), - AWS_EVENT_STREAM_RPC_MESSAGE_TYPE_CONNECT, - 0U, - impl->m_connectionConfig.GetConnectRequestCallback(), nullptr, false)) + if (impl->SendProtocolMessageAux( + messageAmendment.GetHeaders(), + messageAmendment.GetPayload(), + AWS_EVENT_STREAM_RPC_MESSAGE_TYPE_CONNECT, + 0U, + impl->m_connectionConfig.GetConnectRequestCallback(), + nullptr, + false)) { impl->m_sharedState.m_callbackContext.SetError({EVENT_STREAM_RPC_CRT_ERROR, aws_last_error()}); impl->m_sharedState.m_currentState = ClientState::Disconnecting; - aws_event_stream_rpc_client_connection_close(connection, AWS_ERROR_EVENT_STREAM_RPC_CONNECTION_CLOSED); + aws_event_stream_rpc_client_connection_close( + connection, AWS_ERROR_EVENT_STREAM_RPC_CONNECTION_CLOSED); } } } @@ -944,7 +987,8 @@ namespace Aws case AWS_EVENT_STREAM_RPC_MESSAGE_TYPE_CONNECT_ACK: { ConnectionCallbackContext localCallbackContext = {}; - bool successfulAck = (messageArgs->message_flags & AWS_EVENT_STREAM_RPC_MESSAGE_FLAG_CONNECTION_ACCEPTED) != 0; + bool successfulAck = + (messageArgs->message_flags & AWS_EVENT_STREAM_RPC_MESSAGE_FLAG_CONNECTION_ACCEPTED) != 0; { std::lock_guard lock(impl->m_sharedStateLock); if (impl->m_sharedState.m_currentState != ClientState::PendingConnack || !successfulAck) @@ -952,14 +996,16 @@ namespace Aws if (!impl->m_sharedState.m_hasShutDown) { impl->m_sharedState.m_callbackContext.SetError( - (successfulAck) ? RpcError{EVENT_STREAM_RPC_CONNECTION_CLOSED, 0} : RpcError{EVENT_STREAM_RPC_CONNECTION_ACCESS_DENIED, 0} - ); + (successfulAck) ? RpcError{EVENT_STREAM_RPC_CONNECTION_CLOSED, 0} + : RpcError{EVENT_STREAM_RPC_CONNECTION_ACCESS_DENIED, 0}); } impl->m_sharedState.m_desiredState = ClientState::Disconnected; if (impl->m_sharedState.m_currentState != ClientState::Disconnecting) { impl->m_sharedState.m_currentState = ClientState::Disconnecting; - aws_event_stream_rpc_client_connection_close(impl->m_sharedState.m_underlyingConnection, AWS_ERROR_EVENT_STREAM_RPC_CONNECTION_CLOSED); + aws_event_stream_rpc_client_connection_close( + impl->m_sharedState.m_underlyingConnection, + AWS_ERROR_EVENT_STREAM_RPC_CONNECTION_CLOSED); } return; } @@ -976,8 +1022,7 @@ namespace Aws Crt::List pingHeaders; for (size_t i = 0; i < messageArgs->headers_count; ++i) { - pingHeaders.emplace_back( - EventStreamHeader(messageArgs->headers[i], impl->m_allocator)); + pingHeaders.emplace_back(EventStreamHeader(messageArgs->headers[i], impl->m_allocator)); } if (messageArgs->payload) @@ -997,14 +1042,14 @@ namespace Aws default: impl->m_lifecycleHandler->OnErrorCallback( - {EVENT_STREAM_RPC_CRT_ERROR, AWS_ERROR_EVENT_STREAM_RPC_PROTOCOL_ERROR}); + {EVENT_STREAM_RPC_CRT_ERROR, AWS_ERROR_EVENT_STREAM_RPC_PROTOCOL_ERROR}); impl->Close(); break; } } ClientConnection::ClientConnection(Crt::Allocator *allocator, aws_client_bootstrap *bootstrap) noexcept - :m_impl(Aws::Crt::MakeShared(allocator, allocator, bootstrap)) + : m_impl(Aws::Crt::MakeShared(allocator, allocator, bootstrap)) { } @@ -1044,10 +1089,12 @@ namespace Aws struct RawContinuationCallbackDataWrapper { - RawContinuationCallbackDataWrapper(Aws::Crt::Allocator *allocator, const std::shared_ptr &callbackData) : - m_allocator(allocator), - m_callbackData(callbackData) - {} + RawContinuationCallbackDataWrapper( + Aws::Crt::Allocator *allocator, + const std::shared_ptr &callbackData) + : m_allocator(allocator), m_callbackData(callbackData) + { + } Aws::Crt::Allocator *m_allocator; std::shared_ptr m_callbackData; @@ -1060,7 +1107,8 @@ namespace Aws return; } - struct RawContinuationCallbackDataWrapper *wrapper = static_cast(user_data); + struct RawContinuationCallbackDataWrapper *wrapper = + static_cast(user_data); Aws::Crt::Delete(wrapper, wrapper->m_allocator); } @@ -1078,12 +1126,12 @@ namespace Aws m_callbackData = Crt::MakeShared(m_allocator, this, m_allocator); m_continuationHandler.m_callbackData = m_callbackData; - options.user_data = reinterpret_cast(Aws::Crt::New(allocator, allocator, m_callbackData)); + options.user_data = reinterpret_cast( + Aws::Crt::New(allocator, allocator, m_callbackData)); if (connection) { - m_continuationToken = - aws_event_stream_rpc_client_connection_new_stream(connection, &options); + m_continuationToken = aws_event_stream_rpc_client_connection_new_stream(connection, &options); if (m_continuationToken == nullptr) { m_continuationHandler.m_callbackData = nullptr; @@ -1273,10 +1321,10 @@ namespace Aws if (m_continuationToken) { if (aws_event_stream_rpc_client_continuation_send_message( - m_continuationToken, - &msg_args, - s_protocolMessageCallback, - reinterpret_cast(callbackContainer))) + m_continuationToken, + &msg_args, + s_protocolMessageCallback, + reinterpret_cast(callbackContainer))) { errorCode = aws_last_error(); } @@ -1345,9 +1393,8 @@ namespace Aws { Close().wait(); m_clientContinuation.Release(); - //std::unique_lock lock(m_continuationMutex); - //m_closeReady.wait(lock, [this] { return m_expectingClose == false; }); - + // std::unique_lock lock(m_continuationMutex); + // m_closeReady.wait(lock, [this] { return m_expectingClose == false; }); } TaggedResult::TaggedResult(Crt::ScopedResource operationResponse) noexcept diff --git a/eventstream_rpc/tests/CMakeLists.txt b/eventstream_rpc/tests/CMakeLists.txt index abf50b5a4..84a993f0a 100644 --- a/eventstream_rpc/tests/CMakeLists.txt +++ b/eventstream_rpc/tests/CMakeLists.txt @@ -30,13 +30,18 @@ file(GLOB TESTS ${TEST_HDRS} ${TEST_SRC}) set(TEST_BINARY_NAME ${PROJECT_NAME}-tests) +add_test_case(EventStreamClientCreateFailureInvalidHost) +add_test_case(EventStreamClientCreateFailureInvalidPort) add_test_case(EventStreamConnectSuccess) add_test_case(EventStreamConnectFailureNoAuthHeader) add_test_case(EventStreamConnectFailureBadAuthHeader) - add_test_case(EchoClientConnectSuccess) add_test_case(EchoClientDoubleClose) add_test_case(EchoClientMultiConnectSuccessFail) +add_test_case(EchoClientReconnect) +add_test_case(EchoClientCloseWhileConnecting) +add_test_case(EchoClientConnectWhileClosing) +add_test_case(EchoClientOpenCloseStress) #add_test_case(EchoClientOperationEchoSuccessString) #add_test_case(EchoClientOperationEchoSuccessBoolean) diff --git a/eventstream_rpc/tests/EventStreamClientTest.cpp b/eventstream_rpc/tests/EventStreamClientTest.cpp index 9082678f2..ff057a56b 100644 --- a/eventstream_rpc/tests/EventStreamClientTest.cpp +++ b/eventstream_rpc/tests/EventStreamClientTest.cpp @@ -91,6 +91,7 @@ class TestLifecycleHandler : public ConnectionLifecycleHandler disconnectCrtErrorCode = error.crtError; disconnectRpcStatusCode = error.baseStatus; + isConnected = false; semaphore.notify_one(); } @@ -101,6 +102,11 @@ class TestLifecycleHandler : public ConnectionLifecycleHandler semaphore.wait(semaphoreULock, condition); } + void WaitOnDisconnected() + { + WaitOnCondition([this]() { return !isConnected; }); + } + private: std::condition_variable semaphore; std::mutex semaphoreLock; @@ -285,13 +291,205 @@ static int s_TestEchoClientMultiConnectSuccessFail(struct aws_allocator *allocat AWS_TEST_CASE(EchoClientMultiConnectSuccessFail, s_TestEchoClientMultiConnectSuccessFail); +static int s_TestEventStreamClientCreateFailureInvalidHost(struct aws_allocator *allocator, void *ctx) +{ + ApiHandle apiHandle(allocator); + + { + auto elGroup = Aws::Crt::MakeShared(allocator, 0, allocator); + auto resolver = Aws::Crt::MakeShared(allocator, *elGroup, 8, 30, allocator); + auto clientBootstrap = Aws::Crt::MakeShared(allocator, *elGroup, *resolver, allocator); + + ConnectionLifecycleHandler lifecycleHandler; + ClientConnection client(allocator, clientBootstrap->GetUnderlyingHandle()); + + ConnectionConfig config; + config.SetPort(8033); + + auto connectFuture = client.Connect(config, &lifecycleHandler); + ASSERT_INT_EQUALS(EVENT_STREAM_RPC_NULL_PARAMETER, connectFuture.get().baseStatus); + } + + return AWS_OP_SUCCESS; +} + +AWS_TEST_CASE(EventStreamClientCreateFailureInvalidHost, s_TestEventStreamClientCreateFailureInvalidHost); + +static int s_TestEventStreamClientCreateFailureInvalidPort(struct aws_allocator *allocator, void *ctx) +{ + ApiHandle apiHandle(allocator); + + { + auto elGroup = Aws::Crt::MakeShared(allocator, 0, allocator); + auto resolver = Aws::Crt::MakeShared(allocator, *elGroup, 8, 30, allocator); + auto clientBootstrap = Aws::Crt::MakeShared(allocator, *elGroup, *resolver, allocator); + + ConnectionLifecycleHandler lifecycleHandler; + ClientConnection client(allocator, clientBootstrap->GetUnderlyingHandle()); + + ConnectionConfig config; + config.SetHostName("localhost"); + + auto connectFuture = client.Connect(config, &lifecycleHandler); + ASSERT_INT_EQUALS(EVENT_STREAM_RPC_NULL_PARAMETER, connectFuture.get().baseStatus); + } + + return AWS_OP_SUCCESS; +} + +AWS_TEST_CASE(EventStreamClientCreateFailureInvalidPort, s_TestEventStreamClientCreateFailureInvalidPort); + +static int s_TestEchoClientReconnect(struct aws_allocator *allocator, void *ctx) +{ + ApiHandle apiHandle(allocator); + EventStreamClientTestContext testContext(allocator); + if (!testContext.isValidEnvironment()) + { + printf("Environment Variables are not set for the test, skipping..."); + return AWS_OP_SKIP; + } + + { + TestLifecycleHandler lifecycleHandler; + Awstest::EchoTestRpcClient client(*testContext.clientBootstrap, allocator); + auto connectedStatus1 = client.Connect(lifecycleHandler).get().baseStatus; + ASSERT_INT_EQUALS(EVENT_STREAM_RPC_SUCCESS, connectedStatus1); + + client.Close(); + lifecycleHandler.WaitOnDisconnected(); + + auto connectedStatus2 = client.Connect(lifecycleHandler).get().baseStatus; + ASSERT_INT_EQUALS(EVENT_STREAM_RPC_SUCCESS, connectedStatus2); + + client.Close(); + } + + return AWS_OP_SUCCESS; +} + +AWS_TEST_CASE(EchoClientReconnect, s_TestEchoClientReconnect); + +static int s_TestEchoClientCloseWhileConnecting(struct aws_allocator *allocator, void *ctx) +{ + ApiHandle apiHandle(allocator); + EventStreamClientTestContext testContext(allocator); + if (!testContext.isValidEnvironment()) + { + printf("Environment Variables are not set for the test, skipping..."); + return AWS_OP_SKIP; + } + + { + TestLifecycleHandler lifecycleHandler; + Awstest::EchoTestRpcClient client(*testContext.clientBootstrap, allocator); + auto connectedFuture = client.Connect(lifecycleHandler); + client.Close(); + + // Our primary concern is that + // we end up in a closed state, the connect future resolves, and nothing explodes in-between. + auto status = connectedFuture.get().baseStatus; + + // depending on race conditions, we may end up with a connection success (before the close gets triggered) + // or a connection closed + ASSERT_TRUE(status == EVENT_STREAM_RPC_CONNECTION_CLOSED || status == EVENT_STREAM_RPC_SUCCESS); + lifecycleHandler.WaitOnDisconnected(); + } + + return AWS_OP_SUCCESS; +} + +AWS_TEST_CASE(EchoClientCloseWhileConnecting, s_TestEchoClientCloseWhileConnecting); + +static int s_TestEchoClientConnectWhileClosing(struct aws_allocator *allocator, void *ctx) +{ + ApiHandle apiHandle(allocator); + EventStreamClientTestContext testContext(allocator); + if (!testContext.isValidEnvironment()) + { + printf("Environment Variables are not set for the test, skipping..."); + return AWS_OP_SKIP; + } + + { + TestLifecycleHandler lifecycleHandler; + Awstest::EchoTestRpcClient client(*testContext.clientBootstrap, allocator); + auto connectedFuture = client.Connect(lifecycleHandler); + auto status = connectedFuture.get().baseStatus; + ASSERT_INT_EQUALS(EVENT_STREAM_RPC_SUCCESS, status); + + client.Close(); + + auto reconnectFuture = client.Connect(lifecycleHandler); + auto reconnectStatus = reconnectFuture.get().baseStatus; + + // depending on race conditions, we may end up with a success (the close completed quickly) + // or an already established code (the connect arrived in the middle of close) + ASSERT_TRUE( + reconnectStatus == EVENT_STREAM_RPC_CONNECTION_ALREADY_ESTABLISHED || + reconnectStatus == EVENT_STREAM_RPC_SUCCESS); + client.Close(); + + lifecycleHandler.WaitOnDisconnected(); + } + + return AWS_OP_SUCCESS; +} + +AWS_TEST_CASE(EchoClientConnectWhileClosing, s_TestEchoClientConnectWhileClosing); + +static int s_TestEchoClientOpenCloseStress(struct aws_allocator *allocator, void *ctx) +{ + ApiHandle apiHandle(allocator); + EventStreamClientTestContext testContext(allocator); + if (!testContext.isValidEnvironment()) + { + printf("Environment Variables are not set for the test, skipping..."); + return AWS_OP_SKIP; + } + + { + TestLifecycleHandler lifecycleHandler; + Awstest::EchoTestRpcClient client(*testContext.clientBootstrap, allocator); + + for (size_t i = 0; i < 1000; ++i) + { + auto connectedFuture = client.Connect(lifecycleHandler); + + if (rand() % 2 == 0) + { + std::this_thread::sleep_for(std::chrono::milliseconds(rand() % 10)); + } + + client.Close(); + + auto connectedStatus = connectedFuture.get().baseStatus; + ASSERT_TRUE( + connectedStatus == EVENT_STREAM_RPC_CONNECTION_CLOSED || connectedStatus == EVENT_STREAM_RPC_SUCCESS || + connectedStatus == EVENT_STREAM_RPC_CONNECTION_ALREADY_ESTABLISHED); + + if (rand() % 2 == 0) + { + std::this_thread::sleep_for(std::chrono::milliseconds(rand() % 10)); + } + } + } + + return AWS_OP_SUCCESS; +} + +AWS_TEST_CASE(EchoClientOpenCloseStress, s_TestEchoClientOpenCloseStress); + +#ifdef NEVER + static void s_onMessageFlush(int errorCode) { (void)errorCode; } template -static bool s_messageDataMembersAreEqual(const Aws::Crt::Optional &expectedValue, const Aws::Crt::Optional &actualValue) +static bool s_messageDataMembersAreEqual( + const Aws::Crt::Optional &expectedValue, + const Aws::Crt::Optional &actualValue) { if (expectedValue.has_value() != actualValue.has_value()) { @@ -698,8 +896,6 @@ static int s_TestEchoClientOperationEchoFailureDisconnected(struct aws_allocator AWS_TEST_CASE(EchoClientOperationEchoFailureDisconnected, s_TestEchoClientOperationEchoFailureDisconnected); -#ifdef NEVER - AWS_TEST_CASE_FIXTURE(EchoOperation, s_testSetup, s_TestEchoOperation, s_testTeardown, &s_testContext); static int s_TestEchoOperation(struct aws_allocator *allocator, void *ctx) { From 6f6b0e1ef711d125d2e0cd9cfb1d2321a109ef6f Mon Sep 17 00:00:00 2001 From: Bret Ambrose Date: Tue, 20 May 2025 13:41:11 -0700 Subject: [PATCH 35/43] Misc rewrite --- eventstream_rpc/source/EventStreamClient.cpp | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/eventstream_rpc/source/EventStreamClient.cpp b/eventstream_rpc/source/EventStreamClient.cpp index 0de055d97..2326133fe 100644 --- a/eventstream_rpc/source/EventStreamClient.cpp +++ b/eventstream_rpc/source/EventStreamClient.cpp @@ -744,16 +744,15 @@ namespace Aws socketOptions = m_connectionConfig.GetSocketOptions().value(); } - struct aws_event_stream_rpc_client_connection_options connectOptions = { - .host_name = connectionConfig.GetHostName().value().c_str(), - .port = connectionConfig.GetPort().value(), - .bootstrap = m_bootstrap, - .socket_options = &socketOptions.GetImpl(), - .on_connection_setup = ClientConnectionImpl::s_onConnectionSetup, - .on_connection_protocol_message = ClientConnectionImpl::s_onProtocolMessage, - .on_connection_shutdown = ClientConnectionImpl::s_onConnectionShutdown, - .user_data = reinterpret_cast(this), - }; + struct aws_event_stream_rpc_client_connection_options connectOptions = {}; + connectOptions.host_name = connectionConfig.GetHostName().value().c_str(); + connectOptions.port = connectionConfig.GetPort().value(); + connectOptions.bootstrap = m_bootstrap; + connectOptions.socket_options = &socketOptions.GetImpl(); + connectOptions.on_connection_setup = ClientConnectionImpl::s_onConnectionSetup; + connectOptions.on_connection_protocol_message = ClientConnectionImpl::s_onProtocolMessage; + connectOptions.on_connection_shutdown = ClientConnectionImpl::s_onConnectionShutdown; + connectOptions.user_data = reinterpret_cast(this); if (m_connectionConfig.GetTlsConnectionOptions().has_value()) { From 64032b58b6e54e2e20d46cbe29f239d452490d03 Mon Sep 17 00:00:00 2001 From: Bret Ambrose Date: Wed, 21 May 2025 10:10:00 -0700 Subject: [PATCH 36/43] Clients now track connection by shared pointer --- .../aws/eventstreamrpc/EventStreamClient.h | 2 +- eventstream_rpc/tests/EchoTestRpcClient.cpp | 21 ++--- .../tests/include/awstest/EchoTestRpcClient.h | 4 +- .../aws/greengrass/GreengrassCoreIpcClient.h | 4 +- .../source/GreengrassCoreIpcClient.cpp | 77 ++++++++++--------- 5 files changed, 55 insertions(+), 53 deletions(-) diff --git a/eventstream_rpc/include/aws/eventstreamrpc/EventStreamClient.h b/eventstream_rpc/include/aws/eventstreamrpc/EventStreamClient.h index 4577e1762..f22136113 100644 --- a/eventstream_rpc/include/aws/eventstreamrpc/EventStreamClient.h +++ b/eventstream_rpc/include/aws/eventstreamrpc/EventStreamClient.h @@ -268,7 +268,7 @@ namespace Aws class AWS_EVENTSTREAMRPC_API ClientConnection final { public: - explicit ClientConnection(Crt::Allocator *allocator, aws_client_bootstrap *bootstrap) noexcept; + ClientConnection(Crt::Allocator *allocator, aws_client_bootstrap *bootstrap) noexcept; ~ClientConnection() noexcept; /** diff --git a/eventstream_rpc/tests/EchoTestRpcClient.cpp b/eventstream_rpc/tests/EchoTestRpcClient.cpp index ee911b0c8..871869f68 100644 --- a/eventstream_rpc/tests/EchoTestRpcClient.cpp +++ b/eventstream_rpc/tests/EchoTestRpcClient.cpp @@ -14,8 +14,9 @@ namespace Awstest EchoTestRpcClient::EchoTestRpcClient( Aws::Crt::Io::ClientBootstrap &clientBootstrap, Aws::Crt::Allocator *allocator) noexcept - : m_connection(allocator, clientBootstrap.GetUnderlyingHandle()), m_allocator(allocator), - m_asyncLaunchMode(std::launch::deferred) + : m_connection( + Aws::Crt::MakeShared(allocator, allocator, clientBootstrap.GetUnderlyingHandle())), + m_allocator(allocator), m_asyncLaunchMode(std::launch::deferred) { m_echoTestRpcServiceModel.AssignModelNameToErrorResponse( Aws::Crt::String("awstest#ServiceError"), ServiceError::s_allocateFromPayload); @@ -25,12 +26,12 @@ namespace Awstest ConnectionLifecycleHandler &lifecycleHandler, const ConnectionConfig &connectionConfig) noexcept { - return m_connection.Connect(connectionConfig, &lifecycleHandler); + return m_connection->Connect(connectionConfig, &lifecycleHandler); } void EchoTestRpcClient::Close() noexcept { - m_connection.Close(); + m_connection->Close(); } void EchoTestRpcClient::WithLaunchMode(std::launch mode) noexcept @@ -46,7 +47,7 @@ namespace Awstest std::shared_ptr EchoTestRpcClient::NewGetAllProducts() noexcept { auto operation = Aws::Crt::MakeShared( - m_allocator, m_connection, m_echoTestRpcServiceModel.m_getAllProductsOperationContext, m_allocator); + m_allocator, *m_connection, m_echoTestRpcServiceModel.m_getAllProductsOperationContext, m_allocator); operation->WithLaunchMode(m_asyncLaunchMode); return operation; } @@ -54,7 +55,7 @@ namespace Awstest std::shared_ptr EchoTestRpcClient::NewCauseServiceError() noexcept { auto operation = Aws::Crt::MakeShared( - m_allocator, m_connection, m_echoTestRpcServiceModel.m_causeServiceErrorOperationContext, m_allocator); + m_allocator, *m_connection, m_echoTestRpcServiceModel.m_causeServiceErrorOperationContext, m_allocator); operation->WithLaunchMode(m_asyncLaunchMode); return operation; } @@ -64,7 +65,7 @@ namespace Awstest { return Aws::Crt::MakeShared( m_allocator, - m_connection, + *m_connection, std::move(streamHandler), m_echoTestRpcServiceModel.m_causeStreamServiceToErrorOperationContext, m_allocator); @@ -75,7 +76,7 @@ namespace Awstest { return Aws::Crt::MakeShared( m_allocator, - m_connection, + *m_connection, std::move(streamHandler), m_echoTestRpcServiceModel.m_echoStreamMessagesOperationContext, m_allocator); @@ -84,7 +85,7 @@ namespace Awstest std::shared_ptr EchoTestRpcClient::NewEchoMessage() noexcept { auto operation = Aws::Crt::MakeShared( - m_allocator, m_connection, m_echoTestRpcServiceModel.m_echoMessageOperationContext, m_allocator); + m_allocator, *m_connection, m_echoTestRpcServiceModel.m_echoMessageOperationContext, m_allocator); operation->WithLaunchMode(m_asyncLaunchMode); return operation; } @@ -92,7 +93,7 @@ namespace Awstest std::shared_ptr EchoTestRpcClient::NewGetAllCustomers() noexcept { auto operation = Aws::Crt::MakeShared( - m_allocator, m_connection, m_echoTestRpcServiceModel.m_getAllCustomersOperationContext, m_allocator); + m_allocator, *m_connection, m_echoTestRpcServiceModel.m_getAllCustomersOperationContext, m_allocator); operation->WithLaunchMode(m_asyncLaunchMode); return operation; } diff --git a/eventstream_rpc/tests/include/awstest/EchoTestRpcClient.h b/eventstream_rpc/tests/include/awstest/EchoTestRpcClient.h index 0f1f8b50b..231ff78e7 100644 --- a/eventstream_rpc/tests/include/awstest/EchoTestRpcClient.h +++ b/eventstream_rpc/tests/include/awstest/EchoTestRpcClient.h @@ -36,7 +36,7 @@ namespace Awstest std::future Connect( ConnectionLifecycleHandler &lifecycleHandler, const ConnectionConfig &connectionConfig = DefaultConnectionConfig()) noexcept; - bool IsConnected() const noexcept { return m_connection.IsOpen(); } + bool IsConnected() const noexcept { return m_connection->IsOpen(); } void Close() noexcept; void WithLaunchMode(std::launch mode) noexcept; @@ -76,7 +76,7 @@ namespace Awstest private: EchoTestRpcServiceModel m_echoTestRpcServiceModel; - ClientConnection m_connection; + std::shared_ptr m_connection; Aws::Crt::Allocator *m_allocator; MessageAmendment m_connectAmendment; std::launch m_asyncLaunchMode; diff --git a/greengrass_ipc/include/aws/greengrass/GreengrassCoreIpcClient.h b/greengrass_ipc/include/aws/greengrass/GreengrassCoreIpcClient.h index 393da1747..4724e1bdc 100644 --- a/greengrass_ipc/include/aws/greengrass/GreengrassCoreIpcClient.h +++ b/greengrass_ipc/include/aws/greengrass/GreengrassCoreIpcClient.h @@ -38,7 +38,7 @@ namespace Aws std::future Connect( ConnectionLifecycleHandler &lifecycleHandler, const ConnectionConfig &connectionConfig = DefaultConnectionConfig()) noexcept; - bool IsConnected() const noexcept { return m_connection.IsOpen(); } + bool IsConnected() const noexcept { return m_connection->IsOpen(); } void Close() noexcept; void WithLaunchMode(std::launch mode) noexcept; @@ -235,7 +235,7 @@ namespace Aws private: GreengrassCoreIpcServiceModel m_greengrassCoreIpcServiceModel; - ClientConnection m_connection; + std::shared_ptr m_connection; Aws::Crt::Allocator *m_allocator; MessageAmendment m_connectAmendment; std::launch m_asyncLaunchMode; diff --git a/greengrass_ipc/source/GreengrassCoreIpcClient.cpp b/greengrass_ipc/source/GreengrassCoreIpcClient.cpp index 9afa6b197..1a00a3416 100644 --- a/greengrass_ipc/source/GreengrassCoreIpcClient.cpp +++ b/greengrass_ipc/source/GreengrassCoreIpcClient.cpp @@ -16,8 +16,9 @@ namespace Aws GreengrassCoreIpcClient::GreengrassCoreIpcClient( Aws::Crt::Io::ClientBootstrap &clientBootstrap, Aws::Crt::Allocator *allocator) noexcept - : m_connection(allocator, clientBootstrap.GetUnderlyingHandle()), m_allocator(allocator), - m_asyncLaunchMode(std::launch::deferred) + : m_connection( + Aws::Crt::MakeShared(allocator, allocator, clientBootstrap.GetUnderlyingHandle())), + m_allocator(allocator), m_asyncLaunchMode(std::launch::deferred) { m_greengrassCoreIpcServiceModel.AssignModelNameToErrorResponse( Aws::Crt::String("aws.greengrass#InvalidArgumentsError"), InvalidArgumentsError::s_allocateFromPayload); @@ -55,12 +56,12 @@ namespace Aws ConnectionLifecycleHandler &lifecycleHandler, const ConnectionConfig &connectionConfig) noexcept { - return m_connection.Connect(connectionConfig, &lifecycleHandler); + return m_connection->Connect(connectionConfig, &lifecycleHandler); } void GreengrassCoreIpcClient::Close() noexcept { - m_connection.Close(); + m_connection->Close(); } void GreengrassCoreIpcClient::WithLaunchMode(std::launch mode) noexcept @@ -78,7 +79,7 @@ namespace Aws { return Aws::Crt::MakeShared( m_allocator, - m_connection, + *m_connection, std::move(streamHandler), m_greengrassCoreIpcServiceModel.m_subscribeToIoTCoreOperationContext, m_allocator); @@ -88,7 +89,7 @@ namespace Aws { auto operation = Aws::Crt::MakeShared( m_allocator, - m_connection, + *m_connection, m_greengrassCoreIpcServiceModel.m_resumeComponentOperationContext, m_allocator); operation->WithLaunchMode(m_asyncLaunchMode); @@ -99,7 +100,7 @@ namespace Aws { auto operation = Aws::Crt::MakeShared( m_allocator, - m_connection, + *m_connection, m_greengrassCoreIpcServiceModel.m_publishToIoTCoreOperationContext, m_allocator); operation->WithLaunchMode(m_asyncLaunchMode); @@ -112,7 +113,7 @@ namespace Aws { return Aws::Crt::MakeShared( m_allocator, - m_connection, + *m_connection, std::move(streamHandler), m_greengrassCoreIpcServiceModel.m_subscribeToConfigurationUpdateOperationContext, m_allocator); @@ -122,7 +123,7 @@ namespace Aws { auto operation = Aws::Crt::MakeShared( m_allocator, - m_connection, + *m_connection, m_greengrassCoreIpcServiceModel.m_deleteThingShadowOperationContext, m_allocator); operation->WithLaunchMode(m_asyncLaunchMode); @@ -133,7 +134,7 @@ namespace Aws { auto operation = Aws::Crt::MakeShared( m_allocator, - m_connection, + *m_connection, m_greengrassCoreIpcServiceModel.m_putComponentMetricOperationContext, m_allocator); operation->WithLaunchMode(m_asyncLaunchMode); @@ -144,7 +145,7 @@ namespace Aws { auto operation = Aws::Crt::MakeShared( m_allocator, - m_connection, + *m_connection, m_greengrassCoreIpcServiceModel.m_deferComponentUpdateOperationContext, m_allocator); operation->WithLaunchMode(m_asyncLaunchMode); @@ -157,7 +158,7 @@ namespace Aws { return Aws::Crt::MakeShared( m_allocator, - m_connection, + *m_connection, std::move(streamHandler), m_greengrassCoreIpcServiceModel.m_subscribeToValidateConfigurationUpdatesOperationContext, m_allocator); @@ -167,7 +168,7 @@ namespace Aws { auto operation = Aws::Crt::MakeShared( m_allocator, - m_connection, + *m_connection, m_greengrassCoreIpcServiceModel.m_getConfigurationOperationContext, m_allocator); operation->WithLaunchMode(m_asyncLaunchMode); @@ -179,7 +180,7 @@ namespace Aws { return Aws::Crt::MakeShared( m_allocator, - m_connection, + *m_connection, std::move(streamHandler), m_greengrassCoreIpcServiceModel.m_subscribeToTopicOperationContext, m_allocator); @@ -189,7 +190,7 @@ namespace Aws { auto operation = Aws::Crt::MakeShared( m_allocator, - m_connection, + *m_connection, m_greengrassCoreIpcServiceModel.m_getComponentDetailsOperationContext, m_allocator); operation->WithLaunchMode(m_asyncLaunchMode); @@ -201,7 +202,7 @@ namespace Aws { auto operation = Aws::Crt::MakeShared( m_allocator, - m_connection, + *m_connection, m_greengrassCoreIpcServiceModel.m_getClientDeviceAuthTokenOperationContext, m_allocator); operation->WithLaunchMode(m_asyncLaunchMode); @@ -212,7 +213,7 @@ namespace Aws { auto operation = Aws::Crt::MakeShared( m_allocator, - m_connection, + *m_connection, m_greengrassCoreIpcServiceModel.m_publishToTopicOperationContext, m_allocator); operation->WithLaunchMode(m_asyncLaunchMode); @@ -225,7 +226,7 @@ namespace Aws { return Aws::Crt::MakeShared( m_allocator, - m_connection, + *m_connection, std::move(streamHandler), m_greengrassCoreIpcServiceModel.m_subscribeToCertificateUpdatesOperationContext, m_allocator); @@ -236,7 +237,7 @@ namespace Aws { auto operation = Aws::Crt::MakeShared( m_allocator, - m_connection, + *m_connection, m_greengrassCoreIpcServiceModel.m_verifyClientDeviceIdentityOperationContext, m_allocator); operation->WithLaunchMode(m_asyncLaunchMode); @@ -248,7 +249,7 @@ namespace Aws { auto operation = Aws::Crt::MakeShared( m_allocator, - m_connection, + *m_connection, m_greengrassCoreIpcServiceModel.m_authorizeClientDeviceActionOperationContext, m_allocator); operation->WithLaunchMode(m_asyncLaunchMode); @@ -259,7 +260,7 @@ namespace Aws { auto operation = Aws::Crt::MakeShared( m_allocator, - m_connection, + *m_connection, m_greengrassCoreIpcServiceModel.m_listComponentsOperationContext, m_allocator); operation->WithLaunchMode(m_asyncLaunchMode); @@ -270,7 +271,7 @@ namespace Aws { auto operation = Aws::Crt::MakeShared( m_allocator, - m_connection, + *m_connection, m_greengrassCoreIpcServiceModel.m_createDebugPasswordOperationContext, m_allocator); operation->WithLaunchMode(m_asyncLaunchMode); @@ -281,7 +282,7 @@ namespace Aws { auto operation = Aws::Crt::MakeShared( m_allocator, - m_connection, + *m_connection, m_greengrassCoreIpcServiceModel.m_getThingShadowOperationContext, m_allocator); operation->WithLaunchMode(m_asyncLaunchMode); @@ -293,7 +294,7 @@ namespace Aws { auto operation = Aws::Crt::MakeShared( m_allocator, - m_connection, + *m_connection, m_greengrassCoreIpcServiceModel.m_sendConfigurationValidityReportOperationContext, m_allocator); operation->WithLaunchMode(m_asyncLaunchMode); @@ -304,7 +305,7 @@ namespace Aws { auto operation = Aws::Crt::MakeShared( m_allocator, - m_connection, + *m_connection, m_greengrassCoreIpcServiceModel.m_updateThingShadowOperationContext, m_allocator); operation->WithLaunchMode(m_asyncLaunchMode); @@ -315,7 +316,7 @@ namespace Aws { auto operation = Aws::Crt::MakeShared( m_allocator, - m_connection, + *m_connection, m_greengrassCoreIpcServiceModel.m_updateConfigurationOperationContext, m_allocator); operation->WithLaunchMode(m_asyncLaunchMode); @@ -327,7 +328,7 @@ namespace Aws { auto operation = Aws::Crt::MakeShared( m_allocator, - m_connection, + *m_connection, m_greengrassCoreIpcServiceModel.m_validateAuthorizationTokenOperationContext, m_allocator); operation->WithLaunchMode(m_asyncLaunchMode); @@ -338,7 +339,7 @@ namespace Aws { auto operation = Aws::Crt::MakeShared( m_allocator, - m_connection, + *m_connection, m_greengrassCoreIpcServiceModel.m_restartComponentOperationContext, m_allocator); operation->WithLaunchMode(m_asyncLaunchMode); @@ -350,7 +351,7 @@ namespace Aws { auto operation = Aws::Crt::MakeShared( m_allocator, - m_connection, + *m_connection, m_greengrassCoreIpcServiceModel.m_getLocalDeploymentStatusOperationContext, m_allocator); operation->WithLaunchMode(m_asyncLaunchMode); @@ -361,7 +362,7 @@ namespace Aws { auto operation = Aws::Crt::MakeShared( m_allocator, - m_connection, + *m_connection, m_greengrassCoreIpcServiceModel.m_getSecretValueOperationContext, m_allocator); operation->WithLaunchMode(m_asyncLaunchMode); @@ -371,7 +372,7 @@ namespace Aws std::shared_ptr GreengrassCoreIpcClient::NewUpdateState() noexcept { auto operation = Aws::Crt::MakeShared( - m_allocator, m_connection, m_greengrassCoreIpcServiceModel.m_updateStateOperationContext, m_allocator); + m_allocator, *m_connection, m_greengrassCoreIpcServiceModel.m_updateStateOperationContext, m_allocator); operation->WithLaunchMode(m_asyncLaunchMode); return operation; } @@ -380,7 +381,7 @@ namespace Aws { auto operation = Aws::Crt::MakeShared( m_allocator, - m_connection, + *m_connection, m_greengrassCoreIpcServiceModel.m_cancelLocalDeploymentOperationContext, m_allocator); operation->WithLaunchMode(m_asyncLaunchMode); @@ -392,7 +393,7 @@ namespace Aws { auto operation = Aws::Crt::MakeShared( m_allocator, - m_connection, + *m_connection, m_greengrassCoreIpcServiceModel.m_listNamedShadowsForThingOperationContext, m_allocator); operation->WithLaunchMode(m_asyncLaunchMode); @@ -404,7 +405,7 @@ namespace Aws { return Aws::Crt::MakeShared( m_allocator, - m_connection, + *m_connection, std::move(streamHandler), m_greengrassCoreIpcServiceModel.m_subscribeToComponentUpdatesOperationContext, m_allocator); @@ -414,7 +415,7 @@ namespace Aws { auto operation = Aws::Crt::MakeShared( m_allocator, - m_connection, + *m_connection, m_greengrassCoreIpcServiceModel.m_listLocalDeploymentsOperationContext, m_allocator); operation->WithLaunchMode(m_asyncLaunchMode); @@ -425,7 +426,7 @@ namespace Aws { auto operation = Aws::Crt::MakeShared( m_allocator, - m_connection, + *m_connection, m_greengrassCoreIpcServiceModel.m_stopComponentOperationContext, m_allocator); operation->WithLaunchMode(m_asyncLaunchMode); @@ -436,7 +437,7 @@ namespace Aws { auto operation = Aws::Crt::MakeShared( m_allocator, - m_connection, + *m_connection, m_greengrassCoreIpcServiceModel.m_pauseComponentOperationContext, m_allocator); operation->WithLaunchMode(m_asyncLaunchMode); @@ -447,7 +448,7 @@ namespace Aws { auto operation = Aws::Crt::MakeShared( m_allocator, - m_connection, + *m_connection, m_greengrassCoreIpcServiceModel.m_createLocalDeploymentOperationContext, m_allocator); operation->WithLaunchMode(m_asyncLaunchMode); From 37291b98bf4041355fda6dd2de61efdcfc0e8b81 Mon Sep 17 00:00:00 2001 From: Bret Ambrose Date: Fri, 30 May 2025 14:46:12 -0700 Subject: [PATCH 37/43] Checkpoint --- .../aws/eventstreamrpc/EventStreamClient.h | 94 +- eventstream_rpc/source/EventStreamClient.cpp | 1668 +++++++++++++---- eventstream_rpc/tests/CMakeLists.txt | 18 +- eventstream_rpc/tests/EchoTestRpcModel.cpp | 145 +- .../tests/EventStreamClientTest.cpp | 4 +- .../tests/include/awstest/EchoTestRpcModel.h | 97 +- .../aws/greengrass/GreengrassCoreIpcModel.h | 555 +++++- .../source/GreengrassCoreIpcModel.cpp | 848 ++++++--- 8 files changed, 2513 insertions(+), 916 deletions(-) diff --git a/eventstream_rpc/include/aws/eventstreamrpc/EventStreamClient.h b/eventstream_rpc/include/aws/eventstreamrpc/EventStreamClient.h index f22136113..1cb58c76f 100644 --- a/eventstream_rpc/include/aws/eventstreamrpc/EventStreamClient.h +++ b/eventstream_rpc/include/aws/eventstreamrpc/EventStreamClient.h @@ -153,7 +153,9 @@ namespace Aws EVENT_STREAM_RPC_UNKNOWN_PROTOCOL_MESSAGE, EVENT_STREAM_RPC_UNMAPPED_DATA, EVENT_STREAM_RPC_UNSUPPORTED_CONTENT_TYPE, - EVENT_STREAM_RPC_CRT_ERROR + EVENT_STREAM_RPC_CRT_ERROR, + EVENT_STREAM_RPC_CONTINUATION_ALREADY_OPENED, + EVENT_STREAM_RPC_CONTINUATION_CLOSE_IN_PROGRESS, }; /** @@ -261,6 +263,7 @@ namespace Aws }; class ClientConnectionImpl; + class ClientContinuationImpl; /** * Class representing a connection to an RPC server. @@ -282,12 +285,10 @@ namespace Aws ConnectionLifecycleHandler *connectionLifecycleHandler) noexcept; /** - * Create a new stream. - * @note Activate() must be called on the stream for it to actually initiate the new stream. - * @param clientContinuationHandler Handler to process continuation events. + * Create a new stream (continuation). * @return A newly created continuation. */ - ClientContinuation NewStream(ClientContinuationHandler &clientContinuationHandler) noexcept; + std::shared_ptr NewStream() noexcept; /** * Close the connection. @@ -304,6 +305,7 @@ namespace Aws std::shared_ptr m_impl; }; +#ifdef NEVER /** * User data passed to callbacks for a new stream. */ @@ -323,13 +325,16 @@ namespace Aws ClientContinuation *clientContinuation; Crt::Allocator *allocator; }; +#endif /** - * Handler interface for continuation events. + * Vestigial, do-nothing class that remains for backwards compatibility with the + * original, publicly-visible class hierarchy. */ class AWS_EVENTSTREAMRPC_API ClientContinuationHandler { public: +#ifdef NEVER /** * Invoked when a message is received on this continuation. */ @@ -346,13 +351,16 @@ namespace Aws * the TERMINATE_STREAM flag, or when the connection shuts down. */ virtual void OnContinuationClosed() = 0; +#endif virtual ~ClientContinuationHandler() noexcept = default; - +#ifdef NEVER private: friend class ClientContinuation; std::shared_ptr m_callbackData; +#endif }; +#ifdef NEVER /** * A wrapper for event-stream-rpc client continuation. */ @@ -433,7 +441,7 @@ namespace Aws struct aws_event_stream_rpc_client_continuation_token *continuationToken, void *userData) noexcept; }; - +#endif /** * Base class for types used by operations. */ @@ -478,7 +486,7 @@ namespace Aws virtual void OnStreamClosed(); protected: - friend class ClientOperation; + friend class ClientContinuationImpl; /** * Invoked when a message is received on this continuation. */ @@ -597,7 +605,7 @@ namespace Aws class AWS_EVENTSTREAMRPC_API OperationModelContext { public: - OperationModelContext(const ServiceModel &serviceModel) noexcept; + explicit OperationModelContext(const ServiceModel &serviceModel) noexcept; virtual ~OperationModelContext() noexcept = default; @@ -677,7 +685,7 @@ namespace Aws std::shared_ptr streamHandler, const OperationModelContext &operationModelContext, Crt::Allocator *allocator) noexcept; - ~ClientOperation() noexcept; + ~ClientOperation() noexcept override; ClientOperation(const ClientOperation &clientOperation) noexcept = delete; ClientOperation(ClientOperation &&clientOperation) noexcept = delete; @@ -694,12 +702,9 @@ namespace Aws std::future Close(OnMessageFlushCallback onMessageFlushCallback = nullptr) noexcept; /** - * Get an operation result. - * @return Future which will be resolved when the corresponding RPC request completes. - */ - std::future GetOperationResult() noexcept; - - /** + * @deprecated This no longer does anything useful. Launch policy was added because of a + * mistake in the implementation that was attempting to chain two promises together. + * * Set the launch mode for executing operations. The mode is set to std::launch::deferred by default. * @param mode The launch mode to use. */ @@ -710,67 +715,26 @@ namespace Aws * Initiate a new client stream. Send the shape for the new stream. * @param shape A parameter for RPC operation. * @param onMessageFlushCallback Callback to invoke when the shape is flushed to the underlying transport. + * @param onResultCallback Callback to invoke with the untransformed activation result * @return Future which will be resolved once the message is sent. */ std::future Activate( const AbstractShapeBase *shape, - OnMessageFlushCallback onMessageFlushCallback) noexcept; + OnMessageFlushCallback &&onMessageFlushCallback, + std::function &&onResultCallback, + bool &synchronousSuccess) noexcept; /** * Returns the canonical model name associated with this operation across any client language. * Namespace included. * @return The model name. */ - virtual Crt::String GetModelName() const noexcept = 0; - - const OperationModelContext &m_operationModelContext; - std::launch m_asyncLaunchMode; + virtual Crt::String GetModelName() const noexcept; private: - EventStreamRpcStatusCode HandleData(const Crt::Optional &payload); - EventStreamRpcStatusCode HandleError( - const Crt::String &modelName, - const Crt::Optional &payload, - uint32_t messageFlags); - /** - * Invoked when a message is received on this continuation. - */ - void OnContinuationMessage( - const Crt::List &headers, - const Crt::Optional &payload, - MessageType messageType, - uint32_t messageFlags) override; - /** - * Invoked when the continuation is closed. - * - * Once the continuation is closed, no more messages may be sent or received. - * The continuation is closed when a message is sent or received with - * the TERMINATE_STREAM flag, or when the connection shuts down. - */ - void OnContinuationClosed() override; - - const EventStreamHeader *GetHeaderByName( - const Crt::List &headers, - const Crt::String &name) noexcept; + Aws::Crt::Allocator *m_allocator; - enum CloseState - { - WONT_CLOSE = 0, - WILL_CLOSE, - ALREADY_CLOSED - }; - - uint32_t m_messageCount; - Crt::Allocator *m_allocator; - std::shared_ptr m_streamHandler; - ClientContinuation m_clientContinuation; - /* This mutex protects m_resultReceived & m_closeState. */ - std::mutex m_continuationMutex; - bool m_resultReceived; - std::promise m_initialResponsePromise; - bool m_expectingClose; - bool m_streamClosedCalled; - std::condition_variable m_closeReady; + std::shared_ptr m_impl; }; } // namespace Eventstreamrpc diff --git a/eventstream_rpc/source/EventStreamClient.cpp b/eventstream_rpc/source/EventStreamClient.cpp index 2326133fe..488ec7f2d 100644 --- a/eventstream_rpc/source/EventStreamClient.cpp +++ b/eventstream_rpc/source/EventStreamClient.cpp @@ -23,15 +23,146 @@ namespace Aws { namespace Eventstreamrpc { - /* Because `std::function` cannot be typecasted to void *, we must contain it in a struct. */ - struct OnMessageFlushCallbackContainer + static void s_deleteMessageCallbackContainer(void *); + + /* + * This structure has evolved into a more complex form in order to support some additional requirements + * in the eventstream refactor. + * + * In order to maintain behavioral compatibility while removing blocking destruction, we need to be able to + * synchronously "cancel" pending callbacks/completions at the C++ level. The easiest way to accomplish + * this is by ref-counting the structure (so both the C++ and C continuations can reference it) and using + * a lock + completion state to control whether or not the completion actually happens (first one wins). + * + * Because C callbacks are just function pointers, using shared_ptrs is difficult/awkward. So instead we + * just use the C pattern of an internal ref-count that deletes on dropping to zero. + * + * We also want to support tracking the whole set of pending callbacks so that we can cancel them all + * at once while still efficiently completing then one-at-a-time, hence the intrusive linked list node. The + * linked list node is not always used (for example, the connect message callback is not added to any list). + */ + class OnMessageFlushCallbackContainer { - explicit OnMessageFlushCallbackContainer(Crt::Allocator *allocator) : allocator(allocator) {} - Crt::Allocator *allocator; - OnMessageFlushCallback onMessageFlushCallback; - std::promise onFlushPromise; + public: + OnMessageFlushCallbackContainer(Crt::Allocator *allocator, OnMessageFlushCallback &&flushCallback) + : m_allocator(allocator), m_refCount({}), m_sharedState({}) + { + aws_ref_count_init(&m_refCount, this, s_deleteMessageCallbackContainer); + aws_ref_count_acquire(&m_refCount); // always start at 2, one for C++, one for C + AWS_ZERO_STRUCT(m_sharedState.m_node); + m_sharedState.m_state = CallbackState::Incomplete; + m_sharedState.m_onMessageFlushCallback = std::move(flushCallback); + } + + OnMessageFlushCallbackContainer( + Crt::Allocator *allocator, + OnMessageFlushCallback &&flushCallback, + std::promise &&flushPromise) + : OnMessageFlushCallbackContainer(allocator, std::move(flushCallback)) + { + m_sharedState.m_onFlushPromise = std::move(flushPromise); + } + + ~OnMessageFlushCallbackContainer() = default; + + static void Complete(OnMessageFlushCallbackContainer *callback, RpcError error) + { + if (callback == nullptr) + { + return; + } + + bool performCallback = false; + OnMessageFlushCallback onMessageFlushCallback; + std::promise onFlushPromise; + { + std::lock_guard lock(callback->m_sharedStateLock); + + if (callback->m_sharedState.m_state == CallbackState::Incomplete) + { + performCallback = true; + callback->m_sharedState.m_state = CallbackState::Finished; + onMessageFlushCallback = std::move(callback->m_sharedState.m_onMessageFlushCallback); + onFlushPromise = std::move(callback->m_sharedState.m_onFlushPromise); + if (aws_linked_list_node_is_in_list(&callback->m_sharedState.m_node)) + { + aws_linked_list_remove(&callback->m_sharedState.m_node); + } + } + } + + if (performCallback) + { + if (error.crtError != AWS_ERROR_SUCCESS) + { + AWS_LOGF_ERROR( + AWS_LS_EVENT_STREAM_RPC_CLIENT, + "A CRT error occurred while attempting to send a message: %s", + Crt::ErrorDebugString(error.crtError)); + } + onFlushPromise.set_value(error); + if (onMessageFlushCallback) + { + onMessageFlushCallback(error.crtError); + } + } + } + + static void Release(OnMessageFlushCallbackContainer *callback) + { + if (callback == nullptr) + { + return; + } + + aws_ref_count_release(&callback->m_refCount); + } + + Aws::Crt::Allocator *GetAllocator() const { return m_allocator; } + + void AddToList(struct aws_linked_list *list) + { + std::lock_guard lock(m_sharedStateLock); + + aws_linked_list_push_back(list, &m_sharedState.m_node); + } + + private: + enum class CallbackState + { + Incomplete, + Finished + }; + + Crt::Allocator *m_allocator; + struct aws_ref_count m_refCount; + + std::mutex m_sharedStateLock; + struct + { + CallbackState m_state; + struct aws_linked_list_node m_node; + OnMessageFlushCallback m_onMessageFlushCallback; + std::promise m_onFlushPromise; + } m_sharedState; }; + static void s_deleteMessageCallbackContainer(void *object) + { + auto container = reinterpret_cast(object); + + Aws::Crt::Delete(container, container->GetAllocator()); + } + + static void s_protocolMessageCallback(int errorCode, void *userData) noexcept + { + auto *callbackData = static_cast(userData); + + auto rpcStatus = (errorCode == AWS_ERROR_SUCCESS) ? EVENT_STREAM_RPC_SUCCESS : EVENT_STREAM_RPC_CRT_ERROR; + OnMessageFlushCallbackContainer::Complete(callbackData, {rpcStatus, errorCode}); + OnMessageFlushCallbackContainer::Release(callbackData); + } + MessageAmendment::MessageAmendment(Crt::Allocator *allocator) noexcept : m_headers(), m_payload(), m_allocator(allocator) { @@ -209,9 +340,15 @@ namespace Aws case EVENT_STREAM_RPC_UNSUPPORTED_CONTENT_TYPE: return "EVENT_STREAM_RPC_UNSUPPORTED_CONTENT_TYPE"; case EVENT_STREAM_RPC_CRT_ERROR: + { Crt::String ret = "Failed with EVENT_STREAM_RPC_CRT_ERROR, the CRT error was "; ret += Crt::ErrorDebugString(crtError); return ret; + } + case EVENT_STREAM_RPC_CONTINUATION_ALREADY_OPENED: + return "EVENT_STREAM_RPC_CONTINUATION_ALREADY_OPENED"; + case EVENT_STREAM_RPC_CONTINUATION_CLOSE_IN_PROGRESS: + return "EVENT_STREAM_RPC_CONTINUATION_CLOSE_IN_PROGRESS"; } return "Unknown status code"; } @@ -519,7 +656,7 @@ namespace Aws const ConnectionConfig &connectionOptions, ConnectionLifecycleHandler *connectionLifecycleHandler) noexcept; - ClientContinuation NewStream(ClientContinuationHandler &clientContinuationHandler) noexcept; + std::shared_ptr NewStream() noexcept; void Close() noexcept; @@ -527,25 +664,11 @@ namespace Aws void Shutdown() noexcept; - std::future SendProtocolMessage( - const Crt::List &headers, - const Crt::Optional &payload, - MessageType messageType, - uint32_t messageFlags, - OnMessageFlushCallback onMessageFlushCallback) noexcept; - private: void CloseInternal(bool isShutdown) noexcept; void MoveToDisconnected(RpcError error) noexcept; - int SendProtocolMessageAux( - const Crt::List &headers, - const Crt::Optional &payload, - MessageType messageType, - uint32_t messageFlags, - OnMessageFlushCallback &&onMessageFlushCallback, - std::promise *sendOutcomePromise, - bool takeLock) noexcept; + int SendConnectMessage(OnMessageFlushCallback &&onMessageFlushCallback) noexcept; enum class ClientState { @@ -572,6 +695,7 @@ namespace Aws ClientState m_currentState; ClientState m_desiredState; ConnectionCallbackContext m_callbackContext; + OnMessageFlushCallbackContainer *m_connectFlushContainer; bool m_hasShutDown; } m_sharedState; @@ -594,7 +718,7 @@ namespace Aws ClientConnectionImpl::ClientConnectionImpl(Crt::Allocator *allocator, aws_client_bootstrap *bootstrap) noexcept : m_allocator(allocator), m_lifecycleHandler(nullptr), m_connectMessageAmender(nullptr), m_bootstrap(aws_client_bootstrap_acquire(bootstrap)), m_eventLoop(nullptr), - m_sharedState{nullptr, ClientState::Disconnected, ClientState::Disconnected, {}, false} + m_sharedState{nullptr, ClientState::Disconnected, ClientState::Disconnected, {}, nullptr, false} { m_eventLoop = aws_event_loop_group_get_next_loop(bootstrap->event_loop_group); } @@ -661,6 +785,7 @@ namespace Aws void ClientConnectionImpl::CloseInternal(bool isShutdown) noexcept { ConnectionCallbackContext localContext = {}; + OnMessageFlushCallbackContainer *connectFlushContainer = nullptr; { std::lock_guard lock(m_sharedStateLock); m_sharedState.m_desiredState = ClientState::Disconnected; @@ -682,10 +807,17 @@ namespace Aws if (isShutdown) { localContext = std::move(m_sharedState.m_callbackContext); + connectFlushContainer = m_sharedState.m_connectFlushContainer; + m_sharedState.m_connectFlushContainer = nullptr; } } localContext.InvokeCallbacks(); + + OnMessageFlushCallbackContainer::Complete( + connectFlushContainer, + {EVENT_STREAM_RPC_CONNECTION_CLOSED, AWS_ERROR_EVENT_STREAM_RPC_CONNECTION_CLOSED}); + OnMessageFlushCallbackContainer::Release(connectFlushContainer); } void ClientConnectionImpl::Shutdown() noexcept @@ -722,12 +854,12 @@ namespace Aws } m_lifecycleHandler = connectionLifecycleHandler; - std::function disconnectCallback = [this](RpcError error) - { this->m_lifecycleHandler->OnDisconnectCallback(error); }; - std::function errorCallback = [this](RpcError error) - { return this->m_lifecycleHandler->OnErrorCallback(error); }; - std::function connectionSuccessCallback = [this]() - { this->m_lifecycleHandler->OnConnectCallback(); }; + std::function disconnectCallback = [connectionLifecycleHandler](RpcError error) + { connectionLifecycleHandler->OnDisconnectCallback(error); }; + std::function errorCallback = [connectionLifecycleHandler](RpcError error) + { return connectionLifecycleHandler->OnErrorCallback(error); }; + std::function connectionSuccessCallback = [connectionLifecycleHandler]() + { connectionLifecycleHandler->OnConnectCallback(); }; m_connectionConfig = connectionConfig; m_selfReference = shared_from_this(); @@ -773,86 +905,47 @@ namespace Aws return m_sharedState.m_currentState == ClientState::Connected; } - static void s_protocolMessageCallback(int errorCode, void *userData) noexcept + int ClientConnectionImpl::SendConnectMessage(OnMessageFlushCallback &&onMessageFlushCallback) noexcept { - auto *callbackData = static_cast(userData); - - if (errorCode) - { - AWS_LOGF_ERROR( - AWS_LS_EVENT_STREAM_RPC_CLIENT, - "A CRT error occurred while attempting to send a message: %s", - Crt::ErrorDebugString(errorCode)); - callbackData->onFlushPromise.set_value({EVENT_STREAM_RPC_CRT_ERROR, errorCode}); - } - else - { - callbackData->onFlushPromise.set_value({EVENT_STREAM_RPC_SUCCESS, 0}); - } - - /* Call the user-provided callback. */ - if (callbackData->onMessageFlushCallback) + MessageAmendment messageAmendment; + if (m_connectionConfig.GetConnectAmendment().has_value()) { - callbackData->onMessageFlushCallback(errorCode); + MessageAmendment connectAmendment(m_connectionConfig.GetConnectAmendment().value()); + // The version header is necessary for establishing the connection. + messageAmendment.AddHeader(EventStreamHeader( + Crt::String(EVENTSTREAM_VERSION_HEADER), Crt::String(EVENTSTREAM_VERSION_STRING), m_allocator)); + messageAmendment.PrependHeaders(std::move(connectAmendment).GetHeaders()); + messageAmendment.SetPayload(std::move(connectAmendment).GetPayload()); } - Crt::Delete(callbackData, callbackData->allocator); - } - - ClientContinuation ClientConnectionImpl::NewStream( - ClientContinuationHandler &clientContinuationHandler) noexcept - { - std::lock_guard lock(m_sharedStateLock); - return {m_sharedState.m_underlyingConnection, clientContinuationHandler, m_allocator}; - } - - int ClientConnectionImpl::SendProtocolMessageAux( - const Crt::List &headers, - const Crt::Optional &payload, - MessageType messageType, - uint32_t messageFlags, - OnMessageFlushCallback &&onMessageFlushCallback, - std::promise *sendOutcomePromise, - bool takeLock) noexcept - { - OnMessageFlushCallbackContainer *callbackContainer = nullptr; struct aws_array_list headersArray; - s_fillNativeHeadersArray(headers, &headersArray, m_allocator); + s_fillNativeHeadersArray(messageAmendment.GetHeaders(), &headersArray, m_allocator); struct aws_event_stream_rpc_message_args msg_args; msg_args.headers = (struct aws_event_stream_header_value_pair *)headersArray.data; - msg_args.headers_count = headers.size(); - msg_args.payload = payload.has_value() ? (aws_byte_buf *)(&(payload.value())) : nullptr; - msg_args.message_type = messageType; - msg_args.message_flags = messageFlags; + msg_args.headers_count = messageAmendment.GetHeaders().size(); + msg_args.payload = messageAmendment.GetPayload().has_value() + ? (aws_byte_buf *)(&(messageAmendment.GetPayload().value())) + : nullptr; + msg_args.message_type = AWS_EVENT_STREAM_RPC_MESSAGE_TYPE_CONNECT; + msg_args.message_flags = 0U; - callbackContainer = Crt::New(m_allocator, m_allocator); - callbackContainer->onMessageFlushCallback = std::move(onMessageFlushCallback); - if (sendOutcomePromise) - { - callbackContainer->onFlushPromise = std::move(*sendOutcomePromise); - } + // It shouldn't be possible to interrupt a connect with another connect, but let's handle that case anyways + OnMessageFlushCallbackContainer::Complete( + m_sharedState.m_connectFlushContainer, {EVENT_STREAM_RPC_CONNECTION_SETUP_FAILED, AWS_ERROR_SUCCESS}); + OnMessageFlushCallbackContainer::Release(m_sharedState.m_connectFlushContainer); + + m_sharedState.m_connectFlushContainer = + Crt::New(m_allocator, m_allocator, std::move(onMessageFlushCallback)); int errorCode = AWS_ERROR_SUCCESS; + if (aws_event_stream_rpc_client_connection_send_protocol_message( + m_sharedState.m_underlyingConnection, + &msg_args, + s_protocolMessageCallback, + reinterpret_cast(m_sharedState.m_connectFlushContainer))) { - if (takeLock) - { - m_sharedStateLock.lock(); - } - - if (aws_event_stream_rpc_client_connection_send_protocol_message( - m_sharedState.m_underlyingConnection, - &msg_args, - s_protocolMessageCallback, - reinterpret_cast(callbackContainer))) - { - errorCode = aws_last_error(); - } - - if (takeLock) - { - m_sharedStateLock.unlock(); - } + errorCode = aws_last_error(); } /* Cleanup. */ @@ -867,38 +960,16 @@ namespace Aws AWS_LS_EVENT_STREAM_RPC_CLIENT, "A CRT error occurred while queueing a message to be sent on the connection: %s", Crt::ErrorDebugString(errorCode)); - if (sendOutcomePromise) - { - sendOutcomePromise->set_value({EVENT_STREAM_RPC_CRT_ERROR, errorCode}); - } - Crt::Delete(callbackContainer, m_allocator); + OnMessageFlushCallbackContainer::Complete( + m_sharedState.m_connectFlushContainer, {EVENT_STREAM_RPC_CRT_ERROR, errorCode}); + OnMessageFlushCallbackContainer::Release(m_sharedState.m_connectFlushContainer); + m_sharedState.m_connectFlushContainer = nullptr; } return (errorCode == AWS_ERROR_SUCCESS) ? AWS_OP_SUCCESS : AWS_OP_ERR; } - std::future ClientConnectionImpl::SendProtocolMessage( - const Crt::List &headers, - const Crt::Optional &payload, - MessageType messageType, - uint32_t messageFlags, - OnMessageFlushCallback onMessageFlushCallback) noexcept - { - std::promise sendOutcomePromise; - auto sendOutcomeFuture = sendOutcomePromise.get_future(); - SendProtocolMessageAux( - headers, - payload, - messageType, - messageFlags, - std::move(onMessageFlushCallback), - &sendOutcomePromise, - true); - - return sendOutcomeFuture; - } - void ClientConnectionImpl::s_onConnectionSetup( struct aws_event_stream_rpc_client_connection *connection, int errorCode, @@ -930,28 +1001,8 @@ namespace Aws } impl->m_sharedState.m_currentState = ClientState::PendingConnack; - MessageAmendment messageAmendment; - - if (impl->m_connectionConfig.GetConnectAmendment().has_value()) - { - MessageAmendment connectAmendment(impl->m_connectionConfig.GetConnectAmendment().value()); - // The version header is necessary for establishing the connection. - messageAmendment.AddHeader(EventStreamHeader( - Crt::String(EVENTSTREAM_VERSION_HEADER), - Crt::String(EVENTSTREAM_VERSION_STRING), - impl->m_allocator)); - messageAmendment.PrependHeaders(std::move(connectAmendment).GetHeaders()); - messageAmendment.SetPayload(std::move(connectAmendment).GetPayload()); - } - - if (impl->SendProtocolMessageAux( - messageAmendment.GetHeaders(), - messageAmendment.GetPayload(), - AWS_EVENT_STREAM_RPC_MESSAGE_TYPE_CONNECT, - 0U, - impl->m_connectionConfig.GetConnectRequestCallback(), - nullptr, - false)) + auto callback = impl->m_connectionConfig.GetConnectRequestCallback(); + if (impl->SendConnectMessage(std::move(callback))) { impl->m_sharedState.m_callbackContext.SetError({EVENT_STREAM_RPC_CRT_ERROR, aws_last_error()}); impl->m_sharedState.m_currentState = ClientState::Disconnecting; @@ -1065,9 +1116,9 @@ namespace Aws return m_impl->Connect(connectionOptions, connectionLifecycleHandler); } - ClientContinuation ClientConnection::NewStream(ClientContinuationHandler &clientContinuationHandler) noexcept + std::shared_ptr ClientConnection::NewStream() noexcept { - return m_impl->NewStream(clientContinuationHandler); + return m_impl->NewStream(); } void ClientConnection::Close() noexcept @@ -1080,112 +1131,239 @@ namespace Aws return m_impl->IsOpen(); } - void AbstractShapeBase::s_customDeleter(AbstractShapeBase *shape) noexcept + OperationModelContext::OperationModelContext(const ServiceModel &serviceModel) noexcept + : m_serviceModel(serviceModel) { - if (shape->m_allocator != nullptr) - Crt::Delete(shape, shape->m_allocator); } - struct RawContinuationCallbackDataWrapper - { - RawContinuationCallbackDataWrapper( - Aws::Crt::Allocator *allocator, - const std::shared_ptr &callbackData) - : m_allocator(allocator), m_callbackData(callbackData) - { - } - - Aws::Crt::Allocator *m_allocator; - std::shared_ptr m_callbackData; - }; - - static void s_onContinuationTerminated(void *user_data) + void OperationError::SerializeToJsonObject(Crt::JsonObject &payloadObject) const { - if (user_data == nullptr) - { - return; - } - - struct RawContinuationCallbackDataWrapper *wrapper = - static_cast(user_data); - Aws::Crt::Delete(wrapper, wrapper->m_allocator); + (void)payloadObject; } - ClientContinuation::ClientContinuation( - struct aws_event_stream_rpc_client_connection *connection, - ClientContinuationHandler &continuationHandler, - Crt::Allocator *allocator) noexcept - : m_allocator(allocator), m_continuationHandler(continuationHandler), m_continuationToken(nullptr) + void OperationError::s_customDeleter(OperationError *shape) noexcept { - struct aws_event_stream_rpc_client_stream_continuation_options options; - options.on_continuation = ClientContinuation::s_onContinuationMessage; - options.on_continuation_closed = ClientContinuation::s_onContinuationClosed; - options.on_continuation_terminated = s_onContinuationTerminated; - - m_callbackData = Crt::MakeShared(m_allocator, this, m_allocator); + AbstractShapeBase::s_customDeleter(shape); + } - m_continuationHandler.m_callbackData = m_callbackData; - options.user_data = reinterpret_cast( - Aws::Crt::New(allocator, allocator, m_callbackData)); + AbstractShapeBase::AbstractShapeBase() noexcept : m_allocator(nullptr) {} - if (connection) + void AbstractShapeBase::s_customDeleter(AbstractShapeBase *shape) noexcept + { + if (shape->m_allocator != nullptr) { - m_continuationToken = aws_event_stream_rpc_client_connection_new_stream(connection, &options); - if (m_continuationToken == nullptr) - { - m_continuationHandler.m_callbackData = nullptr; - m_callbackData = nullptr; - } + Crt::Delete(shape, shape->m_allocator); } } - ClientContinuation::~ClientContinuation() noexcept + TaggedResult::TaggedResult(Crt::ScopedResource operationResponse) noexcept + : m_responseType(OPERATION_RESPONSE), m_rpcError({}) { - Release(); + m_operationResult.m_response = std::move(operationResponse); } - void ClientContinuation::Release() + TaggedResult::~TaggedResult() noexcept { - if (m_callbackData != nullptr) + if (m_responseType == OPERATION_RESPONSE) { - { - const std::lock_guard lock(m_callbackData->callbackMutex); - m_callbackData->continuationDestroyed = true; - } + m_operationResult.m_response.~unique_ptr(); } - - if (m_continuationToken) + else if (m_responseType == OPERATION_ERROR) { - aws_event_stream_rpc_client_continuation_release(m_continuationToken); - m_continuationToken = nullptr; + m_operationResult.m_error.~unique_ptr(); } } - void ClientContinuation::s_onContinuationMessage( - struct aws_event_stream_rpc_client_continuation_token *continuationToken, - const struct aws_event_stream_rpc_message_args *messageArgs, - void *userData) noexcept + TaggedResult::TaggedResult(Crt::ScopedResource operationError) noexcept + : m_responseType(OPERATION_ERROR), m_rpcError({EVENT_STREAM_RPC_UNINITIALIZED, 0}) { - (void)continuationToken; - /* The `userData` pointer is used to pass a `ContinuationCallbackData` object. */ - auto *callbackData = static_cast(userData)->m_callbackData.get(); - auto *thisContinuation = callbackData->clientContinuation; + m_operationResult.m_error = std::move(operationError); + } - Crt::List continuationMessageHeaders; - for (size_t i = 0; i < messageArgs->headers_count; ++i) + TaggedResult &TaggedResult::operator=(TaggedResult &&rhs) noexcept + { + m_responseType = rhs.m_responseType; + if (m_responseType == OPERATION_RESPONSE) { - continuationMessageHeaders.emplace_back( - EventStreamHeader(messageArgs->headers[i], thisContinuation->m_allocator)); + m_operationResult.m_response = std::move(rhs.m_operationResult.m_response); } - - Crt::Optional payload; - - if (messageArgs->payload) + else if (m_responseType == OPERATION_ERROR) { - payload = Crt::Optional(*messageArgs->payload); + m_operationResult.m_error = std::move(rhs.m_operationResult.m_error); } - else - { + m_rpcError = rhs.m_rpcError; + rhs.m_rpcError = {EVENT_STREAM_RPC_UNINITIALIZED, 0}; + + return *this; + } + + TaggedResult::TaggedResult(RpcError rpcError) noexcept + : m_responseType(RPC_ERROR), m_operationResult(), m_rpcError(rpcError) + { + } + + TaggedResult::TaggedResult() noexcept + : m_responseType(RPC_ERROR), m_operationResult(), m_rpcError({EVENT_STREAM_RPC_UNINITIALIZED, 0}) + { + } + + TaggedResult::TaggedResult(TaggedResult &&rhs) noexcept + { + m_responseType = rhs.m_responseType; + if (m_responseType == OPERATION_RESPONSE) + { + m_operationResult.m_response = std::move(rhs.m_operationResult.m_response); + } + else if (m_responseType == OPERATION_ERROR) + { + m_operationResult.m_error = std::move(rhs.m_operationResult.m_error); + } + m_rpcError = rhs.m_rpcError; + rhs.m_rpcError = {EVENT_STREAM_RPC_UNINITIALIZED, 0}; + } + + TaggedResult::operator bool() const noexcept + { + return m_responseType == OPERATION_RESPONSE; + } + + AbstractShapeBase *TaggedResult::GetOperationResponse() const noexcept + { + return (m_responseType == OPERATION_RESPONSE) ? m_operationResult.m_response.get() : nullptr; + } + + OperationError *TaggedResult::GetOperationError() const noexcept + { + return (m_responseType == OPERATION_ERROR) ? m_operationResult.m_error.get() : nullptr; + } + + RpcError TaggedResult::GetRpcError() const noexcept + { + if (m_responseType == RPC_ERROR) + { + return m_rpcError; + } + else + { + /* Assume success since an application response or error was received. */ + return {EVENT_STREAM_RPC_SUCCESS, 0}; + } + } + + bool StreamResponseHandler::OnStreamError(Crt::ScopedResource operationError, RpcError rpcError) + { + (void)operationError; + (void)rpcError; + /* Note: Always returning true implies that the stream should close + * as a result of encountering this error. */ + return true; + } + + void StreamResponseHandler::OnStreamEvent(Crt::ScopedResource response) {} + + void StreamResponseHandler::OnStreamClosed() {} + +#ifdef NEVER + struct RawContinuationCallbackDataWrapper + { + RawContinuationCallbackDataWrapper( + Aws::Crt::Allocator *allocator, + const std::shared_ptr &callbackData) + : m_allocator(allocator), m_callbackData(callbackData) + { + } + + Aws::Crt::Allocator *m_allocator; + std::shared_ptr m_callbackData; + }; + + static void s_onContinuationTerminated(void *user_data) + { + if (user_data == nullptr) + { + return; + } + + struct RawContinuationCallbackDataWrapper *wrapper = + static_cast(user_data); + Aws::Crt::Delete(wrapper, wrapper->m_allocator); + } + + ClientContinuation::ClientContinuation( + struct aws_event_stream_rpc_client_connection *connection, + ClientContinuationHandler &continuationHandler, + Crt::Allocator *allocator) noexcept + : m_allocator(allocator), m_continuationHandler(continuationHandler), m_continuationToken(nullptr) + { + struct aws_event_stream_rpc_client_stream_continuation_options options; + options.on_continuation = ClientContinuation::s_onContinuationMessage; + options.on_continuation_closed = ClientContinuation::s_onContinuationClosed; + options.on_continuation_terminated = s_onContinuationTerminated; + + m_callbackData = Crt::MakeShared(m_allocator, this, m_allocator); + + m_continuationHandler.m_callbackData = m_callbackData; + options.user_data = reinterpret_cast( + Aws::Crt::New(allocator, allocator, m_callbackData)); + + if (connection) + { + m_continuationToken = aws_event_stream_rpc_client_connection_new_stream(connection, &options); + if (m_continuationToken == nullptr) + { + m_continuationHandler.m_callbackData = nullptr; + m_callbackData = nullptr; + } + } + } + + ClientContinuation::~ClientContinuation() noexcept + { + Release(); + } + + void ClientContinuation::Release() + { + if (m_callbackData != nullptr) + { + { + const std::lock_guard lock(m_callbackData->callbackMutex); + m_callbackData->continuationDestroyed = true; + } + } + + if (m_continuationToken) + { + aws_event_stream_rpc_client_continuation_release(m_continuationToken); + m_continuationToken = nullptr; + } + } + + void ClientContinuation::s_onContinuationMessage( + struct aws_event_stream_rpc_client_continuation_token *continuationToken, + const struct aws_event_stream_rpc_message_args *messageArgs, + void *userData) noexcept + { + (void)continuationToken; + /* The `userData` pointer is used to pass a `ContinuationCallbackData` object. */ + auto *callbackData = static_cast(userData)->m_callbackData.get(); + auto *thisContinuation = callbackData->clientContinuation; + + Crt::List continuationMessageHeaders; + for (size_t i = 0; i < messageArgs->headers_count; ++i) + { + continuationMessageHeaders.emplace_back( + EventStreamHeader(messageArgs->headers[i], thisContinuation->m_allocator)); + } + + Crt::Optional payload; + + if (messageArgs->payload) + { + payload = Crt::Optional(*messageArgs->payload); + } + else + { payload = Crt::Optional(); } @@ -1365,193 +1543,68 @@ namespace Aws } } - OperationModelContext::OperationModelContext(const ServiceModel &serviceModel) noexcept - : m_serviceModel(serviceModel) - { - } - - void OperationError::SerializeToJsonObject(Crt::JsonObject &payloadObject) const + std::future ClientOperation::GetOperationResult() noexcept { - (void)payloadObject; - } + { + const std::lock_guard lock(m_continuationMutex); - AbstractShapeBase::AbstractShapeBase() noexcept : m_allocator(nullptr) {} + if (m_clientContinuation.IsClosed() && !m_resultReceived) + { + AWS_LOGF_ERROR(AWS_LS_EVENT_STREAM_RPC_CLIENT, "The underlying stream is already closed."); + m_initialResponsePromise.set_value(TaggedResult({EVENT_STREAM_RPC_CONNECTION_CLOSED, 0})); + m_resultReceived = true; + } + } - ClientOperation::ClientOperation( - ClientConnection &connection, - std::shared_ptr streamHandler, - const OperationModelContext &operationModelContext, - Crt::Allocator *allocator) noexcept - : m_operationModelContext(operationModelContext), m_asyncLaunchMode(std::launch::deferred), - m_messageCount(0), m_allocator(allocator), m_streamHandler(streamHandler), - m_clientContinuation(connection.NewStream(*this)), m_expectingClose(false), m_streamClosedCalled(false) - { + return m_initialResponsePromise.get_future(); } - ClientOperation::~ClientOperation() noexcept + const EventStreamHeader *ClientOperation::GetHeaderByName( + const Crt::List &headers, + const Crt::String &name) noexcept { - Close().wait(); - m_clientContinuation.Release(); - // std::unique_lock lock(m_continuationMutex); - // m_closeReady.wait(lock, [this] { return m_expectingClose == false; }); + for (auto it = headers.begin(); it != headers.end(); ++it) + { + if (name == it->GetHeaderName()) + { + return &(*it); + } + } + return nullptr; } - TaggedResult::TaggedResult(Crt::ScopedResource operationResponse) noexcept - : m_responseType(OPERATION_RESPONSE) + EventStreamRpcStatusCode ClientOperation::HandleData(const Crt::Optional &payload) { - m_operationResult.m_response = std::move(operationResponse); - } + Crt::StringView payloadStringView; + if (payload.has_value()) + { + payloadStringView = Crt::ByteCursorToStringView(Crt::ByteCursorFromByteBuf(payload.value())); + } - TaggedResult::~TaggedResult() noexcept - { - if (m_responseType == OPERATION_RESPONSE) + /* The value of this hashmap contains the function that allocates the response object from the + * payload. */ + /* Responses after the first message don't necessarily have the same shape as the first. */ + Crt::ScopedResource response; + if (m_messageCount == 1) { - m_operationResult.m_response.~unique_ptr(); + response = m_operationModelContext.AllocateInitialResponseFromPayload(payloadStringView, m_allocator); } - else if (m_responseType == OPERATION_ERROR) + else { - m_operationResult.m_error.~unique_ptr(); + response = m_operationModelContext.AllocateStreamingResponseFromPayload(payloadStringView, m_allocator); } - } - - TaggedResult::TaggedResult(Crt::ScopedResource operationError) noexcept - : m_responseType(OPERATION_ERROR), m_rpcError({EVENT_STREAM_RPC_UNINITIALIZED, 0}) - { - m_operationResult.m_error = std::move(operationError); - } - TaggedResult &TaggedResult::operator=(TaggedResult &&rhs) noexcept - { - m_responseType = rhs.m_responseType; - if (m_responseType == OPERATION_RESPONSE) + if (response.get() == nullptr) { - m_operationResult.m_response = std::move(rhs.m_operationResult.m_response); + AWS_LOGF_ERROR(AWS_LS_EVENT_STREAM_RPC_CLIENT, "Failed to allocate a response from the payload."); + return EVENT_STREAM_RPC_ALLOCATION_ERROR; } - else if (m_responseType == OPERATION_ERROR) + + if (m_messageCount == 1) { - m_operationResult.m_error = std::move(rhs.m_operationResult.m_error); - } - m_rpcError = rhs.m_rpcError; - rhs.m_rpcError = {EVENT_STREAM_RPC_UNINITIALIZED, 0}; - - return *this; - } - - TaggedResult::TaggedResult(RpcError rpcError) noexcept - : m_responseType(RPC_ERROR), m_operationResult(), m_rpcError(rpcError) - { - } - - TaggedResult::TaggedResult() noexcept - : m_responseType(RPC_ERROR), m_operationResult(), m_rpcError({EVENT_STREAM_RPC_UNINITIALIZED, 0}) - { - } - - TaggedResult::TaggedResult(TaggedResult &&rhs) noexcept - { - m_responseType = rhs.m_responseType; - if (m_responseType == OPERATION_RESPONSE) - { - m_operationResult.m_response = std::move(rhs.m_operationResult.m_response); - } - else if (m_responseType == OPERATION_ERROR) - { - m_operationResult.m_error = std::move(rhs.m_operationResult.m_error); - } - m_rpcError = rhs.m_rpcError; - rhs.m_rpcError = {EVENT_STREAM_RPC_UNINITIALIZED, 0}; - } - - TaggedResult::operator bool() const noexcept - { - return m_responseType == OPERATION_RESPONSE; - } - - AbstractShapeBase *TaggedResult::GetOperationResponse() const noexcept - { - return (m_responseType == OPERATION_RESPONSE) ? m_operationResult.m_response.get() : nullptr; - } - - OperationError *TaggedResult::GetOperationError() const noexcept - { - return (m_responseType == OPERATION_ERROR) ? m_operationResult.m_error.get() : nullptr; - } - - RpcError TaggedResult::GetRpcError() const noexcept - { - if (m_responseType == RPC_ERROR) - { - return m_rpcError; - } - else - { - /* Assume success since an application response or error was received. */ - return {EVENT_STREAM_RPC_SUCCESS, 0}; - } - } - - std::future ClientOperation::GetOperationResult() noexcept - { - { - const std::lock_guard lock(m_continuationMutex); - - if (m_clientContinuation.IsClosed() && !m_resultReceived) - { - AWS_LOGF_ERROR(AWS_LS_EVENT_STREAM_RPC_CLIENT, "The underlying stream is already closed."); - m_initialResponsePromise.set_value(TaggedResult({EVENT_STREAM_RPC_CONNECTION_CLOSED, 0})); - m_resultReceived = true; - } - } - - return m_initialResponsePromise.get_future(); - } - - const EventStreamHeader *ClientOperation::GetHeaderByName( - const Crt::List &headers, - const Crt::String &name) noexcept - { - for (auto it = headers.begin(); it != headers.end(); ++it) - { - if (name == it->GetHeaderName()) - { - return &(*it); - } - } - return nullptr; - } - - EventStreamRpcStatusCode ClientOperation::HandleData(const Crt::Optional &payload) - { - Crt::StringView payloadStringView; - if (payload.has_value()) - { - payloadStringView = Crt::ByteCursorToStringView(Crt::ByteCursorFromByteBuf(payload.value())); - } - - /* The value of this hashmap contains the function that allocates the response object from the - * payload. */ - /* Responses after the first message don't necessarily have the same shape as the first. */ - Crt::ScopedResource response; - if (m_messageCount == 1) - { - response = m_operationModelContext.AllocateInitialResponseFromPayload(payloadStringView, m_allocator); - } - else - { - response = m_operationModelContext.AllocateStreamingResponseFromPayload(payloadStringView, m_allocator); - } - - if (response.get() == nullptr) - { - AWS_LOGF_ERROR(AWS_LS_EVENT_STREAM_RPC_CLIENT, "Failed to allocate a response from the payload."); - return EVENT_STREAM_RPC_ALLOCATION_ERROR; - } - - if (m_messageCount == 1) - { - const std::lock_guard lock(m_continuationMutex); - m_resultReceived = true; - m_initialResponsePromise.set_value(TaggedResult(std::move(response))); + const std::lock_guard lock(m_continuationMutex); + m_resultReceived = true; + m_initialResponsePromise.set_value(TaggedResult(std::move(response))); } else { @@ -1617,19 +1670,6 @@ namespace Aws return EVENT_STREAM_RPC_SUCCESS; } - bool StreamResponseHandler::OnStreamError(Crt::ScopedResource operationError, RpcError rpcError) - { - (void)operationError; - (void)rpcError; - /* Note: Always returning true implies that the stream should close - * as a result of encountering this error. */ - return true; - } - - void StreamResponseHandler::OnStreamEvent(Crt::ScopedResource response) {} - - void StreamResponseHandler::OnStreamClosed() {} - void ClientOperation::OnContinuationMessage( const Crt::List &headers, const Crt::Optional &payload, @@ -1799,6 +1839,26 @@ namespace Aws m_asyncLaunchMode = mode; } + class AWS_EVENTSTREAMRPC_API ClientOperation : public ClientContinuationHandler + { + public: + ClientOperation( + ClientConnection &connection, + std::shared_ptr streamHandler, + const OperationModelContext &operationModelContext, + Crt::Allocator *allocator) noexcept; + ~ClientOperation() noexcept override; + + std::future Close(OnMessageFlushCallback onMessageFlushCallback = nullptr) noexcept; + + void WithLaunchMode(std::launch mode) noexcept; + + std::future Activate( + const AbstractShapeBase *shape, + OnMessageFlushCallback onMessageFlushCallback, + std::function &&onResultCallback) noexcept; + }; + std::future ClientOperation::Close(OnMessageFlushCallback onMessageFlushCallback) noexcept { const std::lock_guard lock(m_continuationMutex); @@ -1856,10 +1916,802 @@ namespace Aws return onTerminatePromise.get_future(); } } +#endif // NEVER - void OperationError::s_customDeleter(OperationError *shape) noexcept + static const EventStreamHeader *s_GetHeaderByName( + const Crt::List &headers, + const Crt::String &name) noexcept { - AbstractShapeBase::s_customDeleter(shape); + for (const auto &header : headers) + { + if (name == header.GetHeaderName()) + { + return &header; + } + } + return nullptr; + } + + enum class EventStreamMessageRoutingType + { + Response, + Stream, + Error + }; + + struct MessageDeserialization + { + MessageDeserialization() : m_route(EventStreamMessageRoutingType::Error) {} + + MessageDeserialization(EventStreamMessageRoutingType route, Crt::ScopedResource shape) + : m_route(route), m_shape(std::move(shape)) + { + } + + MessageDeserialization(MessageDeserialization &&) = default; + + MessageDeserialization &operator=(MessageDeserialization &&) = default; + + EventStreamMessageRoutingType m_route; + Crt::ScopedResource m_shape; + }; + + struct MessageResult + { + MessageResult() : m_statusCode(EVENT_STREAM_RPC_SUCCESS) {} + + MessageResult(MessageResult &&) = default; + + MessageResult &operator=(MessageResult &&) = default; + + EventStreamRpcStatusCode m_statusCode; + Crt::Optional m_message; + }; + + enum class ContinuationStateType + { + None, + PendingActivate, + Activated, + PendingClose, + Closed, + }; + + struct ContinuationSharedState + { + ContinuationSharedState(); + + ContinuationStateType m_currentState; + ContinuationStateType m_desiredState; + struct aws_event_stream_rpc_client_continuation_token *m_continuation; + OnMessageFlushCallbackContainer *m_activationCallbackContainer; + std::function m_activationResponseCallback; + OnMessageFlushCallbackContainer *m_closeCallbackContainer; + }; + + ContinuationSharedState::ContinuationSharedState() + : m_currentState(ContinuationStateType::None), m_desiredState(ContinuationStateType::None), + m_continuation(nullptr), m_activationCallbackContainer(nullptr), m_activationResponseCallback(), + m_closeCallbackContainer(nullptr) + { + } + + class ClientContinuationImpl : public std::enable_shared_from_this + { + public: + ClientContinuationImpl( + Aws::Crt::Allocator *allocator, + struct aws_event_stream_rpc_client_connection *connection) noexcept; + virtual ~ClientContinuationImpl(); + + // Called by ClientOperation::~ClientOperation(); the returned future is completed after the + // termination callback of the underlying C continuation + std::future ShutDown() noexcept; + + std::future Activate( + const Crt::String &operation, + const Crt::List &headers, + const Crt::Optional &payload, + MessageType messageType, + uint32_t messageFlags, + std::function &&onResultCallback, + OnMessageFlushCallback &&onMessageFlushCallback, + bool &synchronousSuccess) noexcept; + + std::future Close(OnMessageFlushCallback &&onMessageFlushCallback = nullptr) noexcept; + + Crt::String GetModelName() const noexcept; + + void Initialize( + const OperationModelContext *operationModelContext, + std::shared_ptr streamHandler) noexcept + { + m_operationModelContext = operationModelContext; + m_streamHandler = std::move(streamHandler); + if (m_sharedState.m_continuation != nullptr) + { + m_selfReference = shared_from_this(); + } + } + + private: + MessageResult DeserializeRawMessage( + const struct aws_event_stream_rpc_message_args *rawMessage, + bool shouldBeActivationResponse) noexcept; + + void OnClosed() noexcept; + + void OnMessage(const struct aws_event_stream_rpc_message_args *messageArgs) noexcept; + + int SendCloseMessage( + OnMessageFlushCallback &&closeFlushCallback, + std::promise &&closeFlushPromise) noexcept; + + static void s_OnContinuationClosed( + struct aws_event_stream_rpc_client_continuation_token *token, + void *user_data) noexcept; + + static void s_OnContinuationMessage( + struct aws_event_stream_rpc_client_continuation_token *token, + const struct aws_event_stream_rpc_message_args *message_args, + void *user_data) noexcept; + + static void s_OnContinuationTerminated(void *user_data) noexcept; + + static void ReleaseContinuation( + Aws::Crt::Allocator *allocator, + const struct aws_client_bootstrap *bootstrap, + struct aws_event_stream_rpc_client_continuation_token *continuation) noexcept; + + Aws::Crt::Allocator *m_allocator; + + struct aws_client_bootstrap *m_clientBootstrap; + + std::shared_ptr m_selfReference; + + std::mutex m_sharedStateLock; + ContinuationSharedState m_sharedState; + + const OperationModelContext *m_operationModelContext; + std::shared_ptr m_streamHandler; + + std::promise m_terminationPromise; + }; + + ClientOperation::ClientOperation( + ClientConnection &connection, + std::shared_ptr streamHandler, + const OperationModelContext &operationModelContext, + Crt::Allocator *allocator) noexcept + : m_allocator(allocator), m_impl(connection.NewStream()) + { + m_impl->Initialize(&operationModelContext, std::move(streamHandler)); + } + + ClientOperation::~ClientOperation() noexcept + { + auto terminationFuture = m_impl->ShutDown(); + m_impl = nullptr; + terminationFuture.get(); + } + + std::future ClientOperation::Close(OnMessageFlushCallback onMessageFlushCallback) noexcept + { + return m_impl->Close(std::move(onMessageFlushCallback)); + } + + void ClientOperation::WithLaunchMode(std::launch mode) noexcept + { + (void)mode; + } + + Crt::String ClientOperation::GetModelName() const noexcept + { + return m_impl->GetModelName(); + } + + std::future ClientOperation::Activate( + const AbstractShapeBase *shape, + OnMessageFlushCallback &&onMessageFlushCallback, + std::function &&onResultCallback, + bool &synchronousSuccess) noexcept + { + Crt::List headers; + headers.emplace_back(EventStreamHeader( + Crt::String(CONTENT_TYPE_HEADER), Crt::String(CONTENT_TYPE_APPLICATION_JSON), m_allocator)); + headers.emplace_back( + EventStreamHeader(Crt::String(SERVICE_MODEL_TYPE_HEADER), GetModelName(), m_allocator)); + Crt::JsonObject payloadObject; + shape->SerializeToJsonObject(payloadObject); + Crt::String payloadString = payloadObject.View().WriteCompact(); + + return m_impl->Activate( + GetModelName(), + headers, + Crt::ByteBufFromCString(payloadString.c_str()), + AWS_EVENT_STREAM_RPC_MESSAGE_TYPE_APPLICATION_MESSAGE, + 0, + std::move(onResultCallback), + std::move(onMessageFlushCallback), + synchronousSuccess); + } + + ClientContinuationImpl::ClientContinuationImpl( + Aws::Crt::Allocator *allocator, + struct aws_event_stream_rpc_client_connection *connection) noexcept + : m_allocator(allocator), m_clientBootstrap(aws_client_bootstrap_acquire( + aws_event_stream_rpc_client_connection_get_client_bootstrap(connection))), + m_sharedState(), m_operationModelContext(nullptr) + { + struct aws_event_stream_rpc_client_stream_continuation_options continuation_options = { + .on_continuation = s_OnContinuationMessage, + .on_continuation_closed = s_OnContinuationClosed, + .on_continuation_terminated = s_OnContinuationTerminated, + .user_data = this, + }; + m_sharedState.m_continuation = + aws_event_stream_rpc_client_connection_new_stream(connection, &continuation_options); + + if (!m_sharedState.m_continuation) + { + m_sharedState.m_currentState = ContinuationStateType::Closed; + m_sharedState.m_desiredState = ContinuationStateType::Closed; + } + } + + ClientContinuationImpl::~ClientContinuationImpl() + { + m_terminationPromise.set_value(); + aws_client_bootstrap_release(m_clientBootstrap); + } + + // We use a task to release the C continuation to make it impossible to trigger the termination callback + // in a callstack that includes ClientContinationImpl methods or state. + // + // If we were the last ref holder then release would trigger the termination callback further down the + // callstack. This isn't necessarily unsafe, but for peace-of-mind, it's best for destruction to never + // get invoked on an object that has methods within the callstack. + struct AwsEventstreamContinuationReleaseTask + { + AwsEventstreamContinuationReleaseTask( + Aws::Crt::Allocator *allocator, + struct aws_event_stream_rpc_client_continuation_token *continuation) noexcept; + + struct aws_task m_task; + struct aws_allocator *m_allocator; + struct aws_event_stream_rpc_client_continuation_token *m_continuation; + }; + + static void s_releaseContinuation(struct aws_task *task, void *arg, enum aws_task_status status) + { + auto releaseTask = static_cast(arg); + + aws_event_stream_rpc_client_continuation_release(releaseTask->m_continuation); + + Aws::Crt::Delete(releaseTask, releaseTask->m_allocator); + } + + AwsEventstreamContinuationReleaseTask::AwsEventstreamContinuationReleaseTask( + Aws::Crt::Allocator *allocator, + struct aws_event_stream_rpc_client_continuation_token *continuation) noexcept + : m_task{}, m_allocator(allocator), m_continuation(continuation) + { + aws_task_init(&m_task, s_releaseContinuation, this, "AwsEventstreamContinuationReleaseTask"); + } + + void ClientContinuationImpl::ReleaseContinuation( + Aws::Crt::Allocator *allocator, + const struct aws_client_bootstrap *bootstrap, + struct aws_event_stream_rpc_client_continuation_token *continuation) noexcept + { + if (continuation == nullptr) + { + return; + } + + AWS_FATAL_ASSERT(bootstrap != NULL); + + struct aws_event_loop *event_loop = aws_event_loop_group_get_next_loop(bootstrap->event_loop_group); + AWS_FATAL_ASSERT(event_loop != NULL); + + auto releaseTask = Aws::Crt::New(allocator, allocator, continuation); + aws_event_loop_schedule_task_now(event_loop, &releaseTask->m_task); + } + + std::future ClientContinuationImpl::ShutDown() noexcept + { + struct aws_event_stream_rpc_client_continuation_token *releaseContinuation = nullptr; + OnMessageFlushCallbackContainer *closeCallbackContainer = nullptr; + OnMessageFlushCallbackContainer *activationCallbackContainer = nullptr; + std::function activationResponseCallback; + + { + std::lock_guard lock(m_sharedStateLock); + if (m_sharedState.m_currentState == ContinuationStateType::None) + { + releaseContinuation = m_sharedState.m_continuation; + m_sharedState.m_continuation = nullptr; + m_sharedState.m_currentState = ContinuationStateType::Closed; + m_sharedState.m_desiredState = ContinuationStateType::Closed; + } + + activationCallbackContainer = m_sharedState.m_activationCallbackContainer; + m_sharedState.m_activationCallbackContainer = nullptr; + + closeCallbackContainer = m_sharedState.m_closeCallbackContainer; + m_sharedState.m_closeCallbackContainer = nullptr; + + activationResponseCallback = std::move(m_sharedState.m_activationResponseCallback); + m_sharedState.m_activationResponseCallback = nullptr; // unsure if necessary, let's be sure + } + + if (activationResponseCallback) + { + activationResponseCallback( + TaggedResult(RpcError{EVENT_STREAM_RPC_CONTINUATION_CLOSED, AWS_ERROR_SUCCESS})); + } + + /* + * Short-circuit and simulate both activate and close callbacks as necessary. Part of our contract is that + * when Shutdown returns, no further user-facing callbacks/promise completions will be performed. + */ + OnMessageFlushCallbackContainer::Complete( + activationCallbackContainer, {EVENT_STREAM_RPC_CONTINUATION_CLOSED, AWS_ERROR_SUCCESS}); + OnMessageFlushCallbackContainer::Release(activationCallbackContainer); + + OnMessageFlushCallbackContainer::Complete( + closeCallbackContainer, {EVENT_STREAM_RPC_CONTINUATION_CLOSED, AWS_ERROR_SUCCESS}); + OnMessageFlushCallbackContainer::Release(closeCallbackContainer); + + if (releaseContinuation != nullptr) + { + ReleaseContinuation(m_allocator, m_clientBootstrap, releaseContinuation); + } + else + { + Close(); + } + + return m_terminationPromise.get_future(); + } + + std::future ClientContinuationImpl::Activate( + const Crt::String &operation, + const Crt::List &headers, + const Crt::Optional &payload, + MessageType messageType, + uint32_t messageFlags, + std::function &&onResultCallback, + OnMessageFlushCallback &&onMessageFlushCallback, + bool &synchronousSuccess) noexcept + { + int result = AWS_OP_SUCCESS; + synchronousSuccess = false; + struct aws_array_list headersArray; // guaranteed to be zeroed or valid if we reach the end of the function + OnMessageFlushCallbackContainer *activationFailureContainer = nullptr; + std::promise activationPromise; + std::future activationFuture = activationPromise.get_future(); + { + std::lock_guard lock(m_sharedStateLock); + if (m_sharedState.m_continuation == nullptr) + { + activationPromise.set_value({EVENT_STREAM_RPC_CONNECTION_CLOSED, 0}); + return activationFuture; + } + + switch (m_sharedState.m_currentState) + { + case ContinuationStateType::PendingActivate: + case ContinuationStateType::Activated: + activationPromise.set_value({EVENT_STREAM_RPC_CONTINUATION_ALREADY_OPENED, 0}); + return activationFuture; + + case ContinuationStateType::PendingClose: + case ContinuationStateType::Closed: + activationPromise.set_value({EVENT_STREAM_RPC_CONTINUATION_CLOSED, 0}); + return activationFuture; + + default: + break; + } + + AWS_FATAL_ASSERT(m_sharedState.m_currentState == ContinuationStateType::None); + + // cleanup requirements mean we can't early out from here on + s_fillNativeHeadersArray(headers, &headersArray, m_allocator); + + struct aws_event_stream_rpc_message_args msg_args; + msg_args.headers = static_cast(headersArray.data); + msg_args.headers_count = headers.size(); + msg_args.payload = payload.has_value() ? (aws_byte_buf *)(&(payload.value())) : nullptr; + msg_args.message_type = messageType; + msg_args.message_flags = messageFlags; + + m_sharedState.m_activationCallbackContainer = Crt::New( + m_allocator, m_allocator, std::move(onMessageFlushCallback), std::move(activationPromise)); + m_sharedState.m_activationResponseCallback = std::move(onResultCallback); + + result = aws_event_stream_rpc_client_continuation_activate( + m_sharedState.m_continuation, + Crt::ByteCursorFromCString(operation.c_str()), + &msg_args, + s_protocolMessageCallback, + reinterpret_cast(m_sharedState.m_activationCallbackContainer)); + + if (result != AWS_OP_SUCCESS) + { + activationFailureContainer = m_sharedState.m_activationCallbackContainer; + m_sharedState.m_activationCallbackContainer = nullptr; + m_sharedState.m_activationResponseCallback = std::function(); + } + else + { + m_sharedState.m_currentState = ContinuationStateType::PendingActivate; + m_sharedState.m_desiredState = ContinuationStateType::Activated; + synchronousSuccess = true; + } + } + + if (activationFailureContainer) + { + OnMessageFlushCallbackContainer::Complete( + activationFailureContainer, {EVENT_STREAM_RPC_CRT_ERROR, aws_last_error()}); + OnMessageFlushCallbackContainer::Release(activationFailureContainer); + OnMessageFlushCallbackContainer::Release(activationFailureContainer); + } + + if (aws_array_list_is_valid(&headersArray)) + { + aws_array_list_clean_up(&headersArray); + } + + return activationFuture; + } + + // Intentionally returns an error code and not AWS_OP_ERR/AWS_OP_SUCCESS + // shared state lock must be held before calling + int ClientContinuationImpl::SendCloseMessage( + OnMessageFlushCallback &&closeFlushCallback, + std::promise &&closeFlushPromise) noexcept + { + int errorCode = AWS_ERROR_SUCCESS; + + struct aws_event_stream_rpc_message_args msg_args; + msg_args.headers = nullptr; + msg_args.headers_count = 0; + msg_args.payload = nullptr; + msg_args.message_type = AWS_EVENT_STREAM_RPC_MESSAGE_TYPE_APPLICATION_MESSAGE; + msg_args.message_flags = AWS_EVENT_STREAM_RPC_MESSAGE_FLAG_TERMINATE_STREAM; + + m_sharedState.m_closeCallbackContainer = Crt::New( + m_allocator, m_allocator, std::move(closeFlushCallback), std::move(closeFlushPromise)); + + int result = aws_event_stream_rpc_client_continuation_send_message( + m_sharedState.m_continuation, + &msg_args, + s_protocolMessageCallback, + reinterpret_cast(m_sharedState.m_closeCallbackContainer)); + + if (result) + { + errorCode = aws_last_error(); + AWS_LOGF_ERROR( + AWS_LS_EVENT_STREAM_RPC_CLIENT, + "A CRT error occurred while closing the stream: %s", + Crt::ErrorDebugString(errorCode)); + } + + return errorCode; + } + + std::future ClientContinuationImpl::Close(OnMessageFlushCallback &&onMessageFlushCallback) noexcept + { + int closeErrorCode = AWS_ERROR_SUCCESS; + OnMessageFlushCallbackContainer *closeFailureCallbackContainer = nullptr; + std::promise closePromise; + std::future closeFuture = closePromise.get_future(); + { + std::lock_guard lock(m_sharedStateLock); + if (m_sharedState.m_continuation == nullptr) + { + closePromise.set_value({EVENT_STREAM_RPC_CONTINUATION_CLOSED, 0}); + return closeFuture; + } + + switch (m_sharedState.m_currentState) + { + case ContinuationStateType::PendingActivate: + case ContinuationStateType::Activated: + { + closeErrorCode = SendCloseMessage(std::move(onMessageFlushCallback), std::move(closePromise)); + if (closeErrorCode != AWS_ERROR_SUCCESS) + { + closeFailureCallbackContainer = m_sharedState.m_closeCallbackContainer; + m_sharedState.m_closeCallbackContainer = nullptr; + } + else + { + m_sharedState.m_currentState = ContinuationStateType::PendingClose; + } + break; + } + + case ContinuationStateType::PendingClose: + closePromise.set_value({EVENT_STREAM_RPC_CONTINUATION_CLOSE_IN_PROGRESS, 0}); + return closeFuture; + + default: + closePromise.set_value({EVENT_STREAM_RPC_CONTINUATION_CLOSED, 0}); + return closeFuture; + } + } + + if (closeFailureCallbackContainer != nullptr) + { + OnMessageFlushCallbackContainer::Complete( + closeFailureCallbackContainer, {EVENT_STREAM_RPC_CRT_ERROR, closeErrorCode}); + OnMessageFlushCallbackContainer::Release(closeFailureCallbackContainer); + OnMessageFlushCallbackContainer::Release(closeFailureCallbackContainer); + } + + return closeFuture; + } + + void ClientContinuationImpl::OnClosed() noexcept + { + struct aws_event_stream_rpc_client_continuation_token *releaseContinuation = nullptr; + OnMessageFlushCallbackContainer *closeCallbackContainer = nullptr; + OnMessageFlushCallbackContainer *activationCallbackContainer = nullptr; + std::function activationResponseCallback; + + { + std::lock_guard lock(m_sharedStateLock); + + m_sharedState.m_currentState = ContinuationStateType::Closed; + m_sharedState.m_desiredState = ContinuationStateType::Closed; + releaseContinuation = m_sharedState.m_continuation; + m_sharedState.m_continuation = nullptr; + + activationCallbackContainer = m_sharedState.m_activationCallbackContainer; + m_sharedState.m_activationCallbackContainer = nullptr; + + closeCallbackContainer = m_sharedState.m_closeCallbackContainer; + m_sharedState.m_closeCallbackContainer = nullptr; + + activationResponseCallback = std::move(m_sharedState.m_activationResponseCallback); + m_sharedState.m_activationResponseCallback = nullptr; + } + + if (activationResponseCallback) + { + activationResponseCallback( + TaggedResult(RpcError{EVENT_STREAM_RPC_CONTINUATION_CLOSED, AWS_ERROR_SUCCESS})); + } + + OnMessageFlushCallbackContainer::Complete( + activationCallbackContainer, {EVENT_STREAM_RPC_CONTINUATION_CLOSED, AWS_ERROR_SUCCESS}); + OnMessageFlushCallbackContainer::Release(activationCallbackContainer); + + OnMessageFlushCallbackContainer::Complete( + closeCallbackContainer, {EVENT_STREAM_RPC_CONTINUATION_CLOSED, AWS_ERROR_SUCCESS}); + OnMessageFlushCallbackContainer::Release(closeCallbackContainer); + + ReleaseContinuation(m_allocator, m_clientBootstrap, releaseContinuation); + } + + MessageResult ClientContinuationImpl::DeserializeRawMessage( + const struct aws_event_stream_rpc_message_args *rawMessage, + bool shouldBeActivationResponse) noexcept + { + MessageResult result; + + Crt::List continuationMessageHeaders; + for (size_t i = 0; i < rawMessage->headers_count; ++i) + { + continuationMessageHeaders.emplace_back(EventStreamHeader(rawMessage->headers[i], m_allocator)); + } + + Crt::Optional payload; + if (rawMessage->payload) + { + payload = Crt::Optional(*rawMessage->payload); + } + else + { + payload = Crt::Optional(); + } + + const EventStreamHeader *modelHeader = + s_GetHeaderByName(continuationMessageHeaders, Crt::String(SERVICE_MODEL_TYPE_HEADER)); + if (modelHeader == nullptr) + { + AWS_LOGF_ERROR( + AWS_LS_EVENT_STREAM_RPC_CLIENT, + "A required header (%s) could not be found in the message.", + SERVICE_MODEL_TYPE_HEADER); + result.m_statusCode = EVENT_STREAM_RPC_UNMAPPED_DATA; + return result; + } + + Crt::String modelName; + modelHeader->GetValueAsString(modelName); + if (rawMessage->message_type == AWS_EVENT_STREAM_RPC_MESSAGE_TYPE_APPLICATION_MESSAGE) + { + if (shouldBeActivationResponse) + { + if (m_operationModelContext->GetInitialResponseModelName() != modelName) + { + AWS_LOGF_ERROR( + AWS_LS_EVENT_STREAM_RPC_CLIENT, + "The model name of the initial response did not match its expected model name."); + result.m_statusCode = EVENT_STREAM_RPC_UNMAPPED_DATA; + return result; + } + } + else + { + if (m_operationModelContext->GetStreamingResponseModelName().has_value() && + m_operationModelContext->GetStreamingResponseModelName().value() != modelName) + { + AWS_LOGF_ERROR( + AWS_LS_EVENT_STREAM_RPC_CLIENT, + "The model name of a subsequent response did not match its expected model name."); + result.m_statusCode = EVENT_STREAM_RPC_UNMAPPED_DATA; + return result; + } + } + } + + const EventStreamHeader *contentHeader = + s_GetHeaderByName(continuationMessageHeaders, Crt::String(CONTENT_TYPE_HEADER)); + if (contentHeader == nullptr) + { + AWS_LOGF_ERROR( + AWS_LS_EVENT_STREAM_RPC_CLIENT, + "A required header (%s) could not be found in the message.", + CONTENT_TYPE_HEADER); + result.m_statusCode = EVENT_STREAM_RPC_UNSUPPORTED_CONTENT_TYPE; + return result; + } + + Crt::String contentType; + if (contentHeader->GetValueAsString(contentType) && contentType != CONTENT_TYPE_APPLICATION_JSON) + { + /* Missing required content type header. */ + AWS_LOGF_ERROR( + AWS_LS_EVENT_STREAM_RPC_CLIENT, + "The content type (%s) header was specified with an unsupported value (%s).", + CONTENT_TYPE_HEADER, + contentType.c_str()); + result.m_statusCode = EVENT_STREAM_RPC_UNSUPPORTED_CONTENT_TYPE; + return result; + } + + Crt::StringView payloadStringView; + if (payload.has_value()) + { + payloadStringView = Crt::ByteCursorToStringView(Crt::ByteCursorFromByteBuf(payload.value())); + } + + if (rawMessage->message_type == AWS_EVENT_STREAM_RPC_MESSAGE_TYPE_APPLICATION_MESSAGE) + { + if (shouldBeActivationResponse) + { + result.m_message = MessageDeserialization{ + EventStreamMessageRoutingType::Response, + m_operationModelContext->AllocateInitialResponseFromPayload(payloadStringView, m_allocator)}; + } + else + { + result.m_message = MessageDeserialization{ + EventStreamMessageRoutingType::Stream, + m_operationModelContext->AllocateStreamingResponseFromPayload(payloadStringView, m_allocator)}; + } + } + else + { + auto errorShape = m_operationModelContext + ->AllocateOperationErrorFromPayload(modelName, payloadStringView, m_allocator) + .release(); + Crt::Allocator *allocator = m_allocator; + result.m_message = MessageDeserialization{ + EventStreamMessageRoutingType::Error, + Crt::ScopedResource( + errorShape, [allocator](AbstractShapeBase *shape) { Crt::Delete(shape, allocator); })}; + } + + if (result.m_message.value().m_shape.get() == nullptr) + { + result.m_statusCode = EVENT_STREAM_RPC_UNMAPPED_DATA; + } + + return result; + } + + void ClientContinuationImpl::OnMessage(const struct aws_event_stream_rpc_message_args *messageArgs) noexcept + { + std::function activationResultCallback; + + MessageResult result; + bool isResponse = false; + { + std::lock_guard lock(m_sharedStateLock); + isResponse = static_cast(m_sharedState.m_currentState == ContinuationStateType::PendingActivate); + if (isResponse) + { + activationResultCallback = std::move(m_sharedState.m_activationResponseCallback); + m_sharedState.m_activationResponseCallback = nullptr; + m_sharedState.m_currentState = ContinuationStateType::Activated; + } + } + + result = DeserializeRawMessage(messageArgs, isResponse); + + if (activationResultCallback) + { + if (result.m_statusCode == EVENT_STREAM_RPC_SUCCESS) + { + activationResultCallback(TaggedResult(std::move(result.m_message.value().m_shape))); + } + else + { + activationResultCallback(TaggedResult(RpcError{result.m_statusCode, 0})); + } + } + else if (!isResponse) + { + if (result.m_message.has_value()) + { + AWS_FATAL_ASSERT(result.m_message.value().m_route == EventStreamMessageRoutingType::Stream); + auto shape = std::move(result.m_message.value().m_shape); + m_streamHandler->OnStreamEvent(std::move(shape)); + } + else + { + bool shouldClose = m_streamHandler->OnStreamError(nullptr, {result.m_statusCode, 0}); + if (shouldClose) + { + Close(); + } + } + } + } + + void ClientContinuationImpl::s_OnContinuationClosed( + struct aws_event_stream_rpc_client_continuation_token *token, + void *user_data) noexcept + { + auto impl = reinterpret_cast(user_data); + impl->OnClosed(); + } + + void ClientContinuationImpl::s_OnContinuationMessage( + struct aws_event_stream_rpc_client_continuation_token *token, + const struct aws_event_stream_rpc_message_args *message_args, + void *user_data) noexcept + { + auto impl = reinterpret_cast(user_data); + impl->OnMessage(message_args); + } + + void ClientContinuationImpl::s_OnContinuationTerminated(void *user_data) noexcept + { + auto impl = reinterpret_cast(user_data); + + // We don't need to put this on an event loop task since we release the continuation on an event loop task + impl->m_selfReference = nullptr; + } + + Crt::String ClientContinuationImpl::GetModelName() const noexcept + { + return m_operationModelContext->GetOperationName(); + } + + std::shared_ptr ClientConnectionImpl::NewStream() noexcept + { + std::lock_guard lock(m_sharedStateLock); + return Aws::Crt::MakeShared( + m_allocator, m_allocator, m_sharedState.m_underlyingConnection); } } /* namespace Eventstreamrpc */ diff --git a/eventstream_rpc/tests/CMakeLists.txt b/eventstream_rpc/tests/CMakeLists.txt index 84a993f0a..08dedfecb 100644 --- a/eventstream_rpc/tests/CMakeLists.txt +++ b/eventstream_rpc/tests/CMakeLists.txt @@ -43,15 +43,15 @@ add_test_case(EchoClientCloseWhileConnecting) add_test_case(EchoClientConnectWhileClosing) add_test_case(EchoClientOpenCloseStress) -#add_test_case(EchoClientOperationEchoSuccessString) -#add_test_case(EchoClientOperationEchoSuccessBoolean) -#add_test_case(EchoClientOperationEchoSuccessTime) -#add_test_case(EchoClientOperationEchoSuccessDocument) -#add_test_case(EchoClientOperationEchoSuccessEnum) -#add_test_case(EchoClientOperationEchoSuccessBlob) -#add_test_case(EchoClientOperationEchoSuccessStringList) -#add_test_case(EchoClientOperationEchoSuccessPairList) -#add_test_case(EchoClientOperationEchoSuccessProductMap) +add_test_case(EchoClientOperationEchoSuccessString) +add_test_case(EchoClientOperationEchoSuccessBoolean) +add_test_case(EchoClientOperationEchoSuccessTime) +add_test_case(EchoClientOperationEchoSuccessDocument) +add_test_case(EchoClientOperationEchoSuccessEnum) +add_test_case(EchoClientOperationEchoSuccessBlob) +add_test_case(EchoClientOperationEchoSuccessStringList) +add_test_case(EchoClientOperationEchoSuccessPairList) +add_test_case(EchoClientOperationEchoSuccessProductMap) #add_test_case(EchoClientOperationEchoSuccessMultiple) #add_test_case(EchoClientOperationEchoFailureNeverConnected) diff --git a/eventstream_rpc/tests/EchoTestRpcModel.cpp b/eventstream_rpc/tests/EchoTestRpcModel.cpp index 3220f85a0..059554ed2 100644 --- a/eventstream_rpc/tests/EchoTestRpcModel.cpp +++ b/eventstream_rpc/tests/EchoTestRpcModel.cpp @@ -1018,7 +1018,7 @@ namespace Awstest std::future GetAllProductsOperation::GetResult() noexcept { - return std::async(m_asyncLaunchMode, [this]() { return GetAllProductsResult(GetOperationResult().get()); }); + return m_resultPromise.get_future(); } GetAllProductsOperation::GetAllProductsOperation( @@ -1033,12 +1033,22 @@ namespace Awstest const GetAllProductsRequest &request, OnMessageFlushCallback onMessageFlushCallback) noexcept { - return ClientOperation::Activate(static_cast(&request), onMessageFlushCallback); - } - - Aws::Crt::String GetAllProductsOperation::GetModelName() const noexcept - { - return m_operationModelContext.GetOperationName(); + bool synchronousSuccess = false; + m_selfReference = shared_from_this(); + auto activateFuture = ClientOperation::Activate( + static_cast(&request), + std::move(onMessageFlushCallback), + [this](TaggedResult &&unmodeledResult) + { + m_resultPromise.set_value(GetAllProductsResult(std::move(unmodeledResult))); + m_selfReference = nullptr; + }, + synchronousSuccess); + if (!synchronousSuccess) + { + m_selfReference = nullptr; + } + return activateFuture; } CauseServiceErrorOperationContext::CauseServiceErrorOperationContext( @@ -1086,7 +1096,7 @@ namespace Awstest std::future CauseServiceErrorOperation::GetResult() noexcept { - return std::async(m_asyncLaunchMode, [this]() { return CauseServiceErrorResult(GetOperationResult().get()); }); + return m_resultPromise.get_future(); } CauseServiceErrorOperation::CauseServiceErrorOperation( @@ -1101,12 +1111,22 @@ namespace Awstest const CauseServiceErrorRequest &request, OnMessageFlushCallback onMessageFlushCallback) noexcept { - return ClientOperation::Activate(static_cast(&request), onMessageFlushCallback); - } - - Aws::Crt::String CauseServiceErrorOperation::GetModelName() const noexcept - { - return m_operationModelContext.GetOperationName(); + bool synchronousSuccess = false; + m_selfReference = shared_from_this(); + auto activateFuture = ClientOperation::Activate( + static_cast(&request), + std::move(onMessageFlushCallback), + [this](TaggedResult &&unmodeledResult) + { + m_resultPromise.set_value(CauseServiceErrorResult(std::move(unmodeledResult))); + m_selfReference = nullptr; + }, + synchronousSuccess); + if (!synchronousSuccess) + { + m_selfReference = nullptr; + } + return activateFuture; } void CauseStreamServiceToErrorStreamHandler::OnStreamEvent(Aws::Crt::ScopedResource response) @@ -1176,8 +1196,7 @@ namespace Awstest std::future CauseStreamServiceToErrorOperation::GetResult() noexcept { - return std::async( - m_asyncLaunchMode, [this]() { return CauseStreamServiceToErrorResult(GetOperationResult().get()); }); + return m_resultPromise.get_future(); } CauseStreamServiceToErrorOperation::CauseStreamServiceToErrorOperation( @@ -1193,12 +1212,22 @@ namespace Awstest const EchoStreamingRequest &request, OnMessageFlushCallback onMessageFlushCallback) noexcept { - return ClientOperation::Activate(static_cast(&request), onMessageFlushCallback); - } - - Aws::Crt::String CauseStreamServiceToErrorOperation::GetModelName() const noexcept - { - return m_operationModelContext.GetOperationName(); + bool synchronousSuccess = false; + m_selfReference = shared_from_this(); + auto activateFuture = ClientOperation::Activate( + static_cast(&request), + std::move(onMessageFlushCallback), + [this](TaggedResult &&unmodeledResult) + { + m_resultPromise.set_value(CauseStreamServiceToErrorResult(std::move(unmodeledResult))); + m_selfReference = nullptr; + }, + synchronousSuccess); + if (!synchronousSuccess) + { + m_selfReference = nullptr; + } + return activateFuture; } void EchoStreamMessagesStreamHandler::OnStreamEvent(Aws::Crt::ScopedResource response) @@ -1263,7 +1292,7 @@ namespace Awstest std::future EchoStreamMessagesOperation::GetResult() noexcept { - return std::async(m_asyncLaunchMode, [this]() { return EchoStreamMessagesResult(GetOperationResult().get()); }); + return m_resultPromise.get_future(); } EchoStreamMessagesOperation::EchoStreamMessagesOperation( @@ -1279,12 +1308,22 @@ namespace Awstest const EchoStreamingRequest &request, OnMessageFlushCallback onMessageFlushCallback) noexcept { - return ClientOperation::Activate(static_cast(&request), onMessageFlushCallback); - } - - Aws::Crt::String EchoStreamMessagesOperation::GetModelName() const noexcept - { - return m_operationModelContext.GetOperationName(); + bool synchronousSuccess = false; + m_selfReference = shared_from_this(); + auto activateFuture = ClientOperation::Activate( + static_cast(&request), + std::move(onMessageFlushCallback), + [this](TaggedResult &&unmodeledResult) + { + m_resultPromise.set_value(EchoStreamMessagesResult(std::move(unmodeledResult))); + m_selfReference = nullptr; + }, + synchronousSuccess); + if (!synchronousSuccess) + { + m_selfReference = nullptr; + } + return activateFuture; } EchoMessageOperationContext::EchoMessageOperationContext(const EchoTestRpcServiceModel &serviceModel) noexcept @@ -1330,7 +1369,7 @@ namespace Awstest std::future EchoMessageOperation::GetResult() noexcept { - return std::async(m_asyncLaunchMode, [this]() { return EchoMessageResult(GetOperationResult().get()); }); + return m_resultPromise.get_future(); } EchoMessageOperation::EchoMessageOperation( @@ -1345,12 +1384,22 @@ namespace Awstest const EchoMessageRequest &request, OnMessageFlushCallback onMessageFlushCallback) noexcept { - return ClientOperation::Activate(static_cast(&request), onMessageFlushCallback); - } - - Aws::Crt::String EchoMessageOperation::GetModelName() const noexcept - { - return m_operationModelContext.GetOperationName(); + bool synchronousSuccess = false; + m_selfReference = shared_from_this(); + auto activateFuture = ClientOperation::Activate( + static_cast(&request), + std::move(onMessageFlushCallback), + [this](TaggedResult &&unmodeledResult) + { + m_resultPromise.set_value(EchoMessageResult(std::move(unmodeledResult))); + m_selfReference = nullptr; + }, + synchronousSuccess); + if (!synchronousSuccess) + { + m_selfReference = nullptr; + } + return activateFuture; } GetAllCustomersOperationContext::GetAllCustomersOperationContext( @@ -1397,7 +1446,7 @@ namespace Awstest std::future GetAllCustomersOperation::GetResult() noexcept { - return std::async(m_asyncLaunchMode, [this]() { return GetAllCustomersResult(GetOperationResult().get()); }); + return m_resultPromise.get_future(); } GetAllCustomersOperation::GetAllCustomersOperation( @@ -1412,12 +1461,22 @@ namespace Awstest const GetAllCustomersRequest &request, OnMessageFlushCallback onMessageFlushCallback) noexcept { - return ClientOperation::Activate(static_cast(&request), onMessageFlushCallback); - } - - Aws::Crt::String GetAllCustomersOperation::GetModelName() const noexcept - { - return m_operationModelContext.GetOperationName(); + bool synchronousSuccess = false; + m_selfReference = shared_from_this(); + auto activateFuture = ClientOperation::Activate( + static_cast(&request), + std::move(onMessageFlushCallback), + [this](TaggedResult &&unmodeledResult) + { + m_resultPromise.set_value(GetAllCustomersResult(std::move(unmodeledResult))); + m_selfReference = nullptr; + }, + synchronousSuccess); + if (!synchronousSuccess) + { + m_selfReference = nullptr; + } + return activateFuture; } EchoTestRpcServiceModel::EchoTestRpcServiceModel() noexcept diff --git a/eventstream_rpc/tests/EventStreamClientTest.cpp b/eventstream_rpc/tests/EventStreamClientTest.cpp index ff057a56b..a49fd1332 100644 --- a/eventstream_rpc/tests/EventStreamClientTest.cpp +++ b/eventstream_rpc/tests/EventStreamClientTest.cpp @@ -479,8 +479,6 @@ static int s_TestEchoClientOpenCloseStress(struct aws_allocator *allocator, void AWS_TEST_CASE(EchoClientOpenCloseStress, s_TestEchoClientOpenCloseStress); -#ifdef NEVER - static void s_onMessageFlush(int errorCode) { (void)errorCode; @@ -778,6 +776,8 @@ static int s_TestEchoClientOperationEchoSuccessProductMap(struct aws_allocator * AWS_TEST_CASE(EchoClientOperationEchoSuccessProductMap, s_TestEchoClientOperationEchoSuccessProductMap); +#ifdef NEVER + static int s_TestEchoClientOperationEchoSuccessMultiple(struct aws_allocator *allocator, void *ctx) { ApiHandle apiHandle(allocator); diff --git a/eventstream_rpc/tests/include/awstest/EchoTestRpcModel.h b/eventstream_rpc/tests/include/awstest/EchoTestRpcModel.h index ea644a271..a9cf8de9f 100644 --- a/eventstream_rpc/tests/include/awstest/EchoTestRpcModel.h +++ b/eventstream_rpc/tests/include/awstest/EchoTestRpcModel.h @@ -704,6 +704,7 @@ namespace Awstest { return static_cast(m_taggedResult.GetOperationResponse()); } + /** * @return true if the response is associated with an expected response; * false if the response is associated with an error. @@ -717,13 +718,15 @@ namespace Awstest TaggedResult m_taggedResult; }; - class AWS_ECHOTESTRPC_API GetAllProductsOperation : public ClientOperation + class AWS_ECHOTESTRPC_API GetAllProductsOperation : public ClientOperation, + public std::enable_shared_from_this { public: GetAllProductsOperation( ClientConnection &connection, const GetAllProductsOperationContext &operationContext, Aws::Crt::Allocator *allocator = Aws::Crt::g_allocator) noexcept; + /** * Used to activate a stream for the `GetAllProductsOperation` * @param request The request used for the `GetAllProductsOperation` @@ -733,13 +736,19 @@ namespace Awstest std::future Activate( const GetAllProductsRequest &request, OnMessageFlushCallback onMessageFlushCallback = nullptr) noexcept; + /** * Retrieve the result from activating the stream. */ std::future GetResult() noexcept; - protected: - Aws::Crt::String GetModelName() const noexcept override; + private: + std::promise m_resultPromise; + + /* Keeps the operation alive while activation is in-progress. Internally, we capture `this` in the function + * object that handles the result. If we did not do this, we risk a crash if the user drops their reference + * before the future gets completed. */ + std::shared_ptr m_selfReference; }; class AWS_ECHOTESTRPC_API CauseServiceErrorOperationContext : public OperationModelContext @@ -767,6 +776,7 @@ namespace Awstest { return static_cast(m_taggedResult.GetOperationResponse()); } + /** * @return true if the response is associated with an expected response; * false if the response is associated with an error. @@ -780,13 +790,16 @@ namespace Awstest TaggedResult m_taggedResult; }; - class AWS_ECHOTESTRPC_API CauseServiceErrorOperation : public ClientOperation + class AWS_ECHOTESTRPC_API CauseServiceErrorOperation + : public ClientOperation, + public std::enable_shared_from_this { public: CauseServiceErrorOperation( ClientConnection &connection, const CauseServiceErrorOperationContext &operationContext, Aws::Crt::Allocator *allocator = Aws::Crt::g_allocator) noexcept; + /** * Used to activate a stream for the `CauseServiceErrorOperation` * @param request The request used for the `CauseServiceErrorOperation` @@ -796,13 +809,19 @@ namespace Awstest std::future Activate( const CauseServiceErrorRequest &request, OnMessageFlushCallback onMessageFlushCallback = nullptr) noexcept; + /** * Retrieve the result from activating the stream. */ std::future GetResult() noexcept; - protected: - Aws::Crt::String GetModelName() const noexcept override; + private: + std::promise m_resultPromise; + + /* Keeps the operation alive while activation is in-progress. Internally, we capture `this` in the function + * object that handles the result. If we did not do this, we risk a crash if the user drops their reference + * before the future gets completed. */ + std::shared_ptr m_selfReference; }; class AWS_ECHOTESTRPC_API CauseStreamServiceToErrorStreamHandler : public StreamResponseHandler @@ -845,6 +864,7 @@ namespace Awstest * Invoked when a message is received on this continuation. */ void OnStreamEvent(Aws::Crt::ScopedResource response) override; + /** * Invoked when a message is received on this continuation but results in an error. * @@ -852,6 +872,7 @@ namespace Awstest */ bool OnStreamError(Aws::Crt::ScopedResource error, RpcError rpcError) override; }; + class AWS_ECHOTESTRPC_API CauseStreamServiceToErrorOperationContext : public OperationModelContext { public: @@ -879,6 +900,7 @@ namespace Awstest { return static_cast(m_taggedResult.GetOperationResponse()); } + /** * @return true if the response is associated with an expected response; * false if the response is associated with an error. @@ -892,7 +914,9 @@ namespace Awstest TaggedResult m_taggedResult; }; - class AWS_ECHOTESTRPC_API CauseStreamServiceToErrorOperation : public ClientOperation + class AWS_ECHOTESTRPC_API CauseStreamServiceToErrorOperation + : public ClientOperation, + public std::enable_shared_from_this { public: CauseStreamServiceToErrorOperation( @@ -900,6 +924,7 @@ namespace Awstest std::shared_ptr streamHandler, const CauseStreamServiceToErrorOperationContext &operationContext, Aws::Crt::Allocator *allocator = Aws::Crt::g_allocator) noexcept; + /** * Used to activate a stream for the `CauseStreamServiceToErrorOperation` * @param request The request used for the `CauseStreamServiceToErrorOperation` @@ -909,13 +934,19 @@ namespace Awstest std::future Activate( const EchoStreamingRequest &request, OnMessageFlushCallback onMessageFlushCallback = nullptr) noexcept; + /** * Retrieve the result from activating the stream. */ std::future GetResult() noexcept; - protected: - Aws::Crt::String GetModelName() const noexcept override; + private: + std::promise m_resultPromise; + + /* Keeps the operation alive while activation is in-progress. Internally, we capture `this` in the function + * object that handles the result. If we did not do this, we risk a crash if the user drops their reference + * before the future gets completed. */ + std::shared_ptr m_selfReference; }; class AWS_ECHOTESTRPC_API EchoStreamMessagesStreamHandler : public StreamResponseHandler @@ -948,6 +979,7 @@ namespace Awstest * Invoked when a message is received on this continuation. */ void OnStreamEvent(Aws::Crt::ScopedResource response) override; + /** * Invoked when a message is received on this continuation but results in an error. * @@ -955,6 +987,7 @@ namespace Awstest */ bool OnStreamError(Aws::Crt::ScopedResource error, RpcError rpcError) override; }; + class AWS_ECHOTESTRPC_API EchoStreamMessagesOperationContext : public OperationModelContext { public: @@ -980,6 +1013,7 @@ namespace Awstest { return static_cast(m_taggedResult.GetOperationResponse()); } + /** * @return true if the response is associated with an expected response; * false if the response is associated with an error. @@ -993,7 +1027,9 @@ namespace Awstest TaggedResult m_taggedResult; }; - class AWS_ECHOTESTRPC_API EchoStreamMessagesOperation : public ClientOperation + class AWS_ECHOTESTRPC_API EchoStreamMessagesOperation + : public ClientOperation, + public std::enable_shared_from_this { public: EchoStreamMessagesOperation( @@ -1001,6 +1037,7 @@ namespace Awstest std::shared_ptr streamHandler, const EchoStreamMessagesOperationContext &operationContext, Aws::Crt::Allocator *allocator = Aws::Crt::g_allocator) noexcept; + /** * Used to activate a stream for the `EchoStreamMessagesOperation` * @param request The request used for the `EchoStreamMessagesOperation` @@ -1010,13 +1047,19 @@ namespace Awstest std::future Activate( const EchoStreamingRequest &request, OnMessageFlushCallback onMessageFlushCallback = nullptr) noexcept; + /** * Retrieve the result from activating the stream. */ std::future GetResult() noexcept; - protected: - Aws::Crt::String GetModelName() const noexcept override; + private: + std::promise m_resultPromise; + + /* Keeps the operation alive while activation is in-progress. Internally, we capture `this` in the function + * object that handles the result. If we did not do this, we risk a crash if the user drops their reference + * before the future gets completed. */ + std::shared_ptr m_selfReference; }; class AWS_ECHOTESTRPC_API EchoMessageOperationContext : public OperationModelContext @@ -1044,6 +1087,7 @@ namespace Awstest { return static_cast(m_taggedResult.GetOperationResponse()); } + /** * @return true if the response is associated with an expected response; * false if the response is associated with an error. @@ -1057,13 +1101,15 @@ namespace Awstest TaggedResult m_taggedResult; }; - class AWS_ECHOTESTRPC_API EchoMessageOperation : public ClientOperation + class AWS_ECHOTESTRPC_API EchoMessageOperation : public ClientOperation, + public std::enable_shared_from_this { public: EchoMessageOperation( ClientConnection &connection, const EchoMessageOperationContext &operationContext, Aws::Crt::Allocator *allocator = Aws::Crt::g_allocator) noexcept; + /** * Used to activate a stream for the `EchoMessageOperation` * @param request The request used for the `EchoMessageOperation` @@ -1073,13 +1119,19 @@ namespace Awstest std::future Activate( const EchoMessageRequest &request, OnMessageFlushCallback onMessageFlushCallback = nullptr) noexcept; + /** * Retrieve the result from activating the stream. */ std::future GetResult() noexcept; - protected: - Aws::Crt::String GetModelName() const noexcept override; + private: + std::promise m_resultPromise; + + /* Keeps the operation alive while activation is in-progress. Internally, we capture `this` in the function + * object that handles the result. If we did not do this, we risk a crash if the user drops their reference + * before the future gets completed. */ + std::shared_ptr m_selfReference; }; class AWS_ECHOTESTRPC_API GetAllCustomersOperationContext : public OperationModelContext @@ -1107,6 +1159,7 @@ namespace Awstest { return static_cast(m_taggedResult.GetOperationResponse()); } + /** * @return true if the response is associated with an expected response; * false if the response is associated with an error. @@ -1120,13 +1173,15 @@ namespace Awstest TaggedResult m_taggedResult; }; - class AWS_ECHOTESTRPC_API GetAllCustomersOperation : public ClientOperation + class AWS_ECHOTESTRPC_API GetAllCustomersOperation : public ClientOperation, + public std::enable_shared_from_this { public: GetAllCustomersOperation( ClientConnection &connection, const GetAllCustomersOperationContext &operationContext, Aws::Crt::Allocator *allocator = Aws::Crt::g_allocator) noexcept; + /** * Used to activate a stream for the `GetAllCustomersOperation` * @param request The request used for the `GetAllCustomersOperation` @@ -1136,13 +1191,19 @@ namespace Awstest std::future Activate( const GetAllCustomersRequest &request, OnMessageFlushCallback onMessageFlushCallback = nullptr) noexcept; + /** * Retrieve the result from activating the stream. */ std::future GetResult() noexcept; - protected: - Aws::Crt::String GetModelName() const noexcept override; + private: + std::promise m_resultPromise; + + /* Keeps the operation alive while activation is in-progress. Internally, we capture `this` in the function + * object that handles the result. If we did not do this, we risk a crash if the user drops their reference + * before the future gets completed. */ + std::shared_ptr m_selfReference; }; class AWS_ECHOTESTRPC_API EchoTestRpcServiceModel : public ServiceModel diff --git a/greengrass_ipc/include/aws/greengrass/GreengrassCoreIpcModel.h b/greengrass_ipc/include/aws/greengrass/GreengrassCoreIpcModel.h index 5884b7172..c569872ce 100644 --- a/greengrass_ipc/include/aws/greengrass/GreengrassCoreIpcModel.h +++ b/greengrass_ipc/include/aws/greengrass/GreengrassCoreIpcModel.h @@ -4469,6 +4469,7 @@ namespace Aws * Invoked when a message is received on this continuation. */ void OnStreamEvent(Aws::Crt::ScopedResource response) override; + /** * Invoked when a message is received on this continuation but results in an error. * @@ -4476,6 +4477,7 @@ namespace Aws */ bool OnStreamError(Aws::Crt::ScopedResource error, RpcError rpcError) override; }; + class AWS_GREENGRASSCOREIPC_API SubscribeToIoTCoreOperationContext : public OperationModelContext { public: @@ -4501,6 +4503,7 @@ namespace Aws { return static_cast(m_taggedResult.GetOperationResponse()); } + /** * @return true if the response is associated with an expected response; * false if the response is associated with an error. @@ -4514,7 +4517,9 @@ namespace Aws TaggedResult m_taggedResult; }; - class AWS_GREENGRASSCOREIPC_API SubscribeToIoTCoreOperation : public ClientOperation + class AWS_GREENGRASSCOREIPC_API SubscribeToIoTCoreOperation + : public ClientOperation, + public std::enable_shared_from_this { public: SubscribeToIoTCoreOperation( @@ -4522,6 +4527,7 @@ namespace Aws std::shared_ptr streamHandler, const SubscribeToIoTCoreOperationContext &operationContext, Aws::Crt::Allocator *allocator = Aws::Crt::g_allocator) noexcept; + /** * Used to activate a stream for the `SubscribeToIoTCoreOperation` * @param request The request used for the `SubscribeToIoTCoreOperation` @@ -4531,13 +4537,19 @@ namespace Aws std::future Activate( const SubscribeToIoTCoreRequest &request, OnMessageFlushCallback onMessageFlushCallback = nullptr) noexcept; + /** * Retrieve the result from activating the stream. */ std::future GetResult() noexcept; - protected: - Aws::Crt::String GetModelName() const noexcept override; + private: + std::promise m_resultPromise; + + /* Keeps the operation alive while activation is in-progress. Internally, we capture `this` in the function + * object that handles the result. If we did not do this, we risk a crash if the user drops their reference + * before the future gets completed. */ + std::shared_ptr m_selfReference; }; class AWS_GREENGRASSCOREIPC_API ResumeComponentOperationContext : public OperationModelContext @@ -4565,6 +4577,7 @@ namespace Aws { return static_cast(m_taggedResult.GetOperationResponse()); } + /** * @return true if the response is associated with an expected response; * false if the response is associated with an error. @@ -4578,13 +4591,16 @@ namespace Aws TaggedResult m_taggedResult; }; - class AWS_GREENGRASSCOREIPC_API ResumeComponentOperation : public ClientOperation + class AWS_GREENGRASSCOREIPC_API ResumeComponentOperation + : public ClientOperation, + public std::enable_shared_from_this { public: ResumeComponentOperation( ClientConnection &connection, const ResumeComponentOperationContext &operationContext, Aws::Crt::Allocator *allocator = Aws::Crt::g_allocator) noexcept; + /** * Used to activate a stream for the `ResumeComponentOperation` * @param request The request used for the `ResumeComponentOperation` @@ -4594,13 +4610,19 @@ namespace Aws std::future Activate( const ResumeComponentRequest &request, OnMessageFlushCallback onMessageFlushCallback = nullptr) noexcept; + /** * Retrieve the result from activating the stream. */ std::future GetResult() noexcept; - protected: - Aws::Crt::String GetModelName() const noexcept override; + private: + std::promise m_resultPromise; + + /* Keeps the operation alive while activation is in-progress. Internally, we capture `this` in the function + * object that handles the result. If we did not do this, we risk a crash if the user drops their reference + * before the future gets completed. */ + std::shared_ptr m_selfReference; }; class AWS_GREENGRASSCOREIPC_API PublishToIoTCoreOperationContext : public OperationModelContext @@ -4628,6 +4650,7 @@ namespace Aws { return static_cast(m_taggedResult.GetOperationResponse()); } + /** * @return true if the response is associated with an expected response; * false if the response is associated with an error. @@ -4641,13 +4664,16 @@ namespace Aws TaggedResult m_taggedResult; }; - class AWS_GREENGRASSCOREIPC_API PublishToIoTCoreOperation : public ClientOperation + class AWS_GREENGRASSCOREIPC_API PublishToIoTCoreOperation + : public ClientOperation, + public std::enable_shared_from_this { public: PublishToIoTCoreOperation( ClientConnection &connection, const PublishToIoTCoreOperationContext &operationContext, Aws::Crt::Allocator *allocator = Aws::Crt::g_allocator) noexcept; + /** * Used to activate a stream for the `PublishToIoTCoreOperation` * @param request The request used for the `PublishToIoTCoreOperation` @@ -4657,13 +4683,19 @@ namespace Aws std::future Activate( const PublishToIoTCoreRequest &request, OnMessageFlushCallback onMessageFlushCallback = nullptr) noexcept; + /** * Retrieve the result from activating the stream. */ std::future GetResult() noexcept; - protected: - Aws::Crt::String GetModelName() const noexcept override; + private: + std::promise m_resultPromise; + + /* Keeps the operation alive while activation is in-progress. Internally, we capture `this` in the function + * object that handles the result. If we did not do this, we risk a crash if the user drops their reference + * before the future gets completed. */ + std::shared_ptr m_selfReference; }; class AWS_GREENGRASSCOREIPC_API SubscribeToConfigurationUpdateStreamHandler : public StreamResponseHandler @@ -4716,6 +4748,7 @@ namespace Aws * Invoked when a message is received on this continuation. */ void OnStreamEvent(Aws::Crt::ScopedResource response) override; + /** * Invoked when a message is received on this continuation but results in an error. * @@ -4723,6 +4756,7 @@ namespace Aws */ bool OnStreamError(Aws::Crt::ScopedResource error, RpcError rpcError) override; }; + class AWS_GREENGRASSCOREIPC_API SubscribeToConfigurationUpdateOperationContext : public OperationModelContext { public: @@ -4751,6 +4785,7 @@ namespace Aws { return static_cast(m_taggedResult.GetOperationResponse()); } + /** * @return true if the response is associated with an expected response; * false if the response is associated with an error. @@ -4764,7 +4799,9 @@ namespace Aws TaggedResult m_taggedResult; }; - class AWS_GREENGRASSCOREIPC_API SubscribeToConfigurationUpdateOperation : public ClientOperation + class AWS_GREENGRASSCOREIPC_API SubscribeToConfigurationUpdateOperation + : public ClientOperation, + public std::enable_shared_from_this { public: SubscribeToConfigurationUpdateOperation( @@ -4772,6 +4809,7 @@ namespace Aws std::shared_ptr streamHandler, const SubscribeToConfigurationUpdateOperationContext &operationContext, Aws::Crt::Allocator *allocator = Aws::Crt::g_allocator) noexcept; + /** * Used to activate a stream for the `SubscribeToConfigurationUpdateOperation` * @param request The request used for the `SubscribeToConfigurationUpdateOperation` @@ -4781,13 +4819,19 @@ namespace Aws std::future Activate( const SubscribeToConfigurationUpdateRequest &request, OnMessageFlushCallback onMessageFlushCallback = nullptr) noexcept; + /** * Retrieve the result from activating the stream. */ std::future GetResult() noexcept; - protected: - Aws::Crt::String GetModelName() const noexcept override; + private: + std::promise m_resultPromise; + + /* Keeps the operation alive while activation is in-progress. Internally, we capture `this` in the function + * object that handles the result. If we did not do this, we risk a crash if the user drops their reference + * before the future gets completed. */ + std::shared_ptr m_selfReference; }; class AWS_GREENGRASSCOREIPC_API DeleteThingShadowOperationContext : public OperationModelContext @@ -4815,6 +4859,7 @@ namespace Aws { return static_cast(m_taggedResult.GetOperationResponse()); } + /** * @return true if the response is associated with an expected response; * false if the response is associated with an error. @@ -4828,13 +4873,16 @@ namespace Aws TaggedResult m_taggedResult; }; - class AWS_GREENGRASSCOREIPC_API DeleteThingShadowOperation : public ClientOperation + class AWS_GREENGRASSCOREIPC_API DeleteThingShadowOperation + : public ClientOperation, + public std::enable_shared_from_this { public: DeleteThingShadowOperation( ClientConnection &connection, const DeleteThingShadowOperationContext &operationContext, Aws::Crt::Allocator *allocator = Aws::Crt::g_allocator) noexcept; + /** * Used to activate a stream for the `DeleteThingShadowOperation` * @param request The request used for the `DeleteThingShadowOperation` @@ -4844,13 +4892,19 @@ namespace Aws std::future Activate( const DeleteThingShadowRequest &request, OnMessageFlushCallback onMessageFlushCallback = nullptr) noexcept; + /** * Retrieve the result from activating the stream. */ std::future GetResult() noexcept; - protected: - Aws::Crt::String GetModelName() const noexcept override; + private: + std::promise m_resultPromise; + + /* Keeps the operation alive while activation is in-progress. Internally, we capture `this` in the function + * object that handles the result. If we did not do this, we risk a crash if the user drops their reference + * before the future gets completed. */ + std::shared_ptr m_selfReference; }; class AWS_GREENGRASSCOREIPC_API PutComponentMetricOperationContext : public OperationModelContext @@ -4878,6 +4932,7 @@ namespace Aws { return static_cast(m_taggedResult.GetOperationResponse()); } + /** * @return true if the response is associated with an expected response; * false if the response is associated with an error. @@ -4891,13 +4946,16 @@ namespace Aws TaggedResult m_taggedResult; }; - class AWS_GREENGRASSCOREIPC_API PutComponentMetricOperation : public ClientOperation + class AWS_GREENGRASSCOREIPC_API PutComponentMetricOperation + : public ClientOperation, + public std::enable_shared_from_this { public: PutComponentMetricOperation( ClientConnection &connection, const PutComponentMetricOperationContext &operationContext, Aws::Crt::Allocator *allocator = Aws::Crt::g_allocator) noexcept; + /** * Used to activate a stream for the `PutComponentMetricOperation` * @param request The request used for the `PutComponentMetricOperation` @@ -4907,13 +4965,19 @@ namespace Aws std::future Activate( const PutComponentMetricRequest &request, OnMessageFlushCallback onMessageFlushCallback = nullptr) noexcept; + /** * Retrieve the result from activating the stream. */ std::future GetResult() noexcept; - protected: - Aws::Crt::String GetModelName() const noexcept override; + private: + std::promise m_resultPromise; + + /* Keeps the operation alive while activation is in-progress. Internally, we capture `this` in the function + * object that handles the result. If we did not do this, we risk a crash if the user drops their reference + * before the future gets completed. */ + std::shared_ptr m_selfReference; }; class AWS_GREENGRASSCOREIPC_API DeferComponentUpdateOperationContext : public OperationModelContext @@ -4943,6 +5007,7 @@ namespace Aws { return static_cast(m_taggedResult.GetOperationResponse()); } + /** * @return true if the response is associated with an expected response; * false if the response is associated with an error. @@ -4956,13 +5021,16 @@ namespace Aws TaggedResult m_taggedResult; }; - class AWS_GREENGRASSCOREIPC_API DeferComponentUpdateOperation : public ClientOperation + class AWS_GREENGRASSCOREIPC_API DeferComponentUpdateOperation + : public ClientOperation, + public std::enable_shared_from_this { public: DeferComponentUpdateOperation( ClientConnection &connection, const DeferComponentUpdateOperationContext &operationContext, Aws::Crt::Allocator *allocator = Aws::Crt::g_allocator) noexcept; + /** * Used to activate a stream for the `DeferComponentUpdateOperation` * @param request The request used for the `DeferComponentUpdateOperation` @@ -4972,13 +5040,19 @@ namespace Aws std::future Activate( const DeferComponentUpdateRequest &request, OnMessageFlushCallback onMessageFlushCallback = nullptr) noexcept; + /** * Retrieve the result from activating the stream. */ std::future GetResult() noexcept; - protected: - Aws::Crt::String GetModelName() const noexcept override; + private: + std::promise m_resultPromise; + + /* Keeps the operation alive while activation is in-progress. Internally, we capture `this` in the function + * object that handles the result. If we did not do this, we risk a crash if the user drops their reference + * before the future gets completed. */ + std::shared_ptr m_selfReference; }; class AWS_GREENGRASSCOREIPC_API SubscribeToValidateConfigurationUpdatesStreamHandler @@ -5022,6 +5096,7 @@ namespace Aws * Invoked when a message is received on this continuation. */ void OnStreamEvent(Aws::Crt::ScopedResource response) override; + /** * Invoked when a message is received on this continuation but results in an error. * @@ -5029,6 +5104,7 @@ namespace Aws */ bool OnStreamError(Aws::Crt::ScopedResource error, RpcError rpcError) override; }; + class AWS_GREENGRASSCOREIPC_API SubscribeToValidateConfigurationUpdatesOperationContext : public OperationModelContext { @@ -5060,6 +5136,7 @@ namespace Aws return static_cast( m_taggedResult.GetOperationResponse()); } + /** * @return true if the response is associated with an expected response; * false if the response is associated with an error. @@ -5073,7 +5150,9 @@ namespace Aws TaggedResult m_taggedResult; }; - class AWS_GREENGRASSCOREIPC_API SubscribeToValidateConfigurationUpdatesOperation : public ClientOperation + class AWS_GREENGRASSCOREIPC_API SubscribeToValidateConfigurationUpdatesOperation + : public ClientOperation, + public std::enable_shared_from_this { public: SubscribeToValidateConfigurationUpdatesOperation( @@ -5081,6 +5160,7 @@ namespace Aws std::shared_ptr streamHandler, const SubscribeToValidateConfigurationUpdatesOperationContext &operationContext, Aws::Crt::Allocator *allocator = Aws::Crt::g_allocator) noexcept; + /** * Used to activate a stream for the `SubscribeToValidateConfigurationUpdatesOperation` * @param request The request used for the `SubscribeToValidateConfigurationUpdatesOperation` @@ -5090,13 +5170,19 @@ namespace Aws std::future Activate( const SubscribeToValidateConfigurationUpdatesRequest &request, OnMessageFlushCallback onMessageFlushCallback = nullptr) noexcept; + /** * Retrieve the result from activating the stream. */ std::future GetResult() noexcept; - protected: - Aws::Crt::String GetModelName() const noexcept override; + private: + std::promise m_resultPromise; + + /* Keeps the operation alive while activation is in-progress. Internally, we capture `this` in the function + * object that handles the result. If we did not do this, we risk a crash if the user drops their reference + * before the future gets completed. */ + std::shared_ptr m_selfReference; }; class AWS_GREENGRASSCOREIPC_API GetConfigurationOperationContext : public OperationModelContext @@ -5124,6 +5210,7 @@ namespace Aws { return static_cast(m_taggedResult.GetOperationResponse()); } + /** * @return true if the response is associated with an expected response; * false if the response is associated with an error. @@ -5137,13 +5224,16 @@ namespace Aws TaggedResult m_taggedResult; }; - class AWS_GREENGRASSCOREIPC_API GetConfigurationOperation : public ClientOperation + class AWS_GREENGRASSCOREIPC_API GetConfigurationOperation + : public ClientOperation, + public std::enable_shared_from_this { public: GetConfigurationOperation( ClientConnection &connection, const GetConfigurationOperationContext &operationContext, Aws::Crt::Allocator *allocator = Aws::Crt::g_allocator) noexcept; + /** * Used to activate a stream for the `GetConfigurationOperation` * @param request The request used for the `GetConfigurationOperation` @@ -5153,13 +5243,19 @@ namespace Aws std::future Activate( const GetConfigurationRequest &request, OnMessageFlushCallback onMessageFlushCallback = nullptr) noexcept; + /** * Retrieve the result from activating the stream. */ std::future GetResult() noexcept; - protected: - Aws::Crt::String GetModelName() const noexcept override; + private: + std::promise m_resultPromise; + + /* Keeps the operation alive while activation is in-progress. Internally, we capture `this` in the function + * object that handles the result. If we did not do this, we risk a crash if the user drops their reference + * before the future gets completed. */ + std::shared_ptr m_selfReference; }; class AWS_GREENGRASSCOREIPC_API SubscribeToTopicStreamHandler : public StreamResponseHandler @@ -5222,6 +5318,7 @@ namespace Aws * Invoked when a message is received on this continuation. */ void OnStreamEvent(Aws::Crt::ScopedResource response) override; + /** * Invoked when a message is received on this continuation but results in an error. * @@ -5229,6 +5326,7 @@ namespace Aws */ bool OnStreamError(Aws::Crt::ScopedResource error, RpcError rpcError) override; }; + class AWS_GREENGRASSCOREIPC_API SubscribeToTopicOperationContext : public OperationModelContext { public: @@ -5254,6 +5352,7 @@ namespace Aws { return static_cast(m_taggedResult.GetOperationResponse()); } + /** * @return true if the response is associated with an expected response; * false if the response is associated with an error. @@ -5267,7 +5366,9 @@ namespace Aws TaggedResult m_taggedResult; }; - class AWS_GREENGRASSCOREIPC_API SubscribeToTopicOperation : public ClientOperation + class AWS_GREENGRASSCOREIPC_API SubscribeToTopicOperation + : public ClientOperation, + public std::enable_shared_from_this { public: SubscribeToTopicOperation( @@ -5275,6 +5376,7 @@ namespace Aws std::shared_ptr streamHandler, const SubscribeToTopicOperationContext &operationContext, Aws::Crt::Allocator *allocator = Aws::Crt::g_allocator) noexcept; + /** * Used to activate a stream for the `SubscribeToTopicOperation` * @param request The request used for the `SubscribeToTopicOperation` @@ -5284,13 +5386,19 @@ namespace Aws std::future Activate( const SubscribeToTopicRequest &request, OnMessageFlushCallback onMessageFlushCallback = nullptr) noexcept; + /** * Retrieve the result from activating the stream. */ std::future GetResult() noexcept; - protected: - Aws::Crt::String GetModelName() const noexcept override; + private: + std::promise m_resultPromise; + + /* Keeps the operation alive while activation is in-progress. Internally, we capture `this` in the function + * object that handles the result. If we did not do this, we risk a crash if the user drops their reference + * before the future gets completed. */ + std::shared_ptr m_selfReference; }; class AWS_GREENGRASSCOREIPC_API GetComponentDetailsOperationContext : public OperationModelContext @@ -5318,6 +5426,7 @@ namespace Aws { return static_cast(m_taggedResult.GetOperationResponse()); } + /** * @return true if the response is associated with an expected response; * false if the response is associated with an error. @@ -5331,13 +5440,16 @@ namespace Aws TaggedResult m_taggedResult; }; - class AWS_GREENGRASSCOREIPC_API GetComponentDetailsOperation : public ClientOperation + class AWS_GREENGRASSCOREIPC_API GetComponentDetailsOperation + : public ClientOperation, + public std::enable_shared_from_this { public: GetComponentDetailsOperation( ClientConnection &connection, const GetComponentDetailsOperationContext &operationContext, Aws::Crt::Allocator *allocator = Aws::Crt::g_allocator) noexcept; + /** * Used to activate a stream for the `GetComponentDetailsOperation` * @param request The request used for the `GetComponentDetailsOperation` @@ -5347,13 +5459,19 @@ namespace Aws std::future Activate( const GetComponentDetailsRequest &request, OnMessageFlushCallback onMessageFlushCallback = nullptr) noexcept; + /** * Retrieve the result from activating the stream. */ std::future GetResult() noexcept; - protected: - Aws::Crt::String GetModelName() const noexcept override; + private: + std::promise m_resultPromise; + + /* Keeps the operation alive while activation is in-progress. Internally, we capture `this` in the function + * object that handles the result. If we did not do this, we risk a crash if the user drops their reference + * before the future gets completed. */ + std::shared_ptr m_selfReference; }; class AWS_GREENGRASSCOREIPC_API GetClientDeviceAuthTokenOperationContext : public OperationModelContext @@ -5384,6 +5502,7 @@ namespace Aws { return static_cast(m_taggedResult.GetOperationResponse()); } + /** * @return true if the response is associated with an expected response; * false if the response is associated with an error. @@ -5397,13 +5516,16 @@ namespace Aws TaggedResult m_taggedResult; }; - class AWS_GREENGRASSCOREIPC_API GetClientDeviceAuthTokenOperation : public ClientOperation + class AWS_GREENGRASSCOREIPC_API GetClientDeviceAuthTokenOperation + : public ClientOperation, + public std::enable_shared_from_this { public: GetClientDeviceAuthTokenOperation( ClientConnection &connection, const GetClientDeviceAuthTokenOperationContext &operationContext, Aws::Crt::Allocator *allocator = Aws::Crt::g_allocator) noexcept; + /** * Used to activate a stream for the `GetClientDeviceAuthTokenOperation` * @param request The request used for the `GetClientDeviceAuthTokenOperation` @@ -5413,13 +5535,19 @@ namespace Aws std::future Activate( const GetClientDeviceAuthTokenRequest &request, OnMessageFlushCallback onMessageFlushCallback = nullptr) noexcept; + /** * Retrieve the result from activating the stream. */ std::future GetResult() noexcept; - protected: - Aws::Crt::String GetModelName() const noexcept override; + private: + std::promise m_resultPromise; + + /* Keeps the operation alive while activation is in-progress. Internally, we capture `this` in the function + * object that handles the result. If we did not do this, we risk a crash if the user drops their reference + * before the future gets completed. */ + std::shared_ptr m_selfReference; }; class AWS_GREENGRASSCOREIPC_API PublishToTopicOperationContext : public OperationModelContext @@ -5447,6 +5575,7 @@ namespace Aws { return static_cast(m_taggedResult.GetOperationResponse()); } + /** * @return true if the response is associated with an expected response; * false if the response is associated with an error. @@ -5460,13 +5589,16 @@ namespace Aws TaggedResult m_taggedResult; }; - class AWS_GREENGRASSCOREIPC_API PublishToTopicOperation : public ClientOperation + class AWS_GREENGRASSCOREIPC_API PublishToTopicOperation + : public ClientOperation, + public std::enable_shared_from_this { public: PublishToTopicOperation( ClientConnection &connection, const PublishToTopicOperationContext &operationContext, Aws::Crt::Allocator *allocator = Aws::Crt::g_allocator) noexcept; + /** * Used to activate a stream for the `PublishToTopicOperation` * @param request The request used for the `PublishToTopicOperation` @@ -5476,13 +5608,19 @@ namespace Aws std::future Activate( const PublishToTopicRequest &request, OnMessageFlushCallback onMessageFlushCallback = nullptr) noexcept; + /** * Retrieve the result from activating the stream. */ std::future GetResult() noexcept; - protected: - Aws::Crt::String GetModelName() const noexcept override; + private: + std::promise m_resultPromise; + + /* Keeps the operation alive while activation is in-progress. Internally, we capture `this` in the function + * object that handles the result. If we did not do this, we risk a crash if the user drops their reference + * before the future gets completed. */ + std::shared_ptr m_selfReference; }; class AWS_GREENGRASSCOREIPC_API SubscribeToCertificateUpdatesStreamHandler : public StreamResponseHandler @@ -5545,6 +5683,7 @@ namespace Aws * Invoked when a message is received on this continuation. */ void OnStreamEvent(Aws::Crt::ScopedResource response) override; + /** * Invoked when a message is received on this continuation but results in an error. * @@ -5552,6 +5691,7 @@ namespace Aws */ bool OnStreamError(Aws::Crt::ScopedResource error, RpcError rpcError) override; }; + class AWS_GREENGRASSCOREIPC_API SubscribeToCertificateUpdatesOperationContext : public OperationModelContext { public: @@ -5580,6 +5720,7 @@ namespace Aws { return static_cast(m_taggedResult.GetOperationResponse()); } + /** * @return true if the response is associated with an expected response; * false if the response is associated with an error. @@ -5593,7 +5734,9 @@ namespace Aws TaggedResult m_taggedResult; }; - class AWS_GREENGRASSCOREIPC_API SubscribeToCertificateUpdatesOperation : public ClientOperation + class AWS_GREENGRASSCOREIPC_API SubscribeToCertificateUpdatesOperation + : public ClientOperation, + public std::enable_shared_from_this { public: SubscribeToCertificateUpdatesOperation( @@ -5601,6 +5744,7 @@ namespace Aws std::shared_ptr streamHandler, const SubscribeToCertificateUpdatesOperationContext &operationContext, Aws::Crt::Allocator *allocator = Aws::Crt::g_allocator) noexcept; + /** * Used to activate a stream for the `SubscribeToCertificateUpdatesOperation` * @param request The request used for the `SubscribeToCertificateUpdatesOperation` @@ -5610,13 +5754,19 @@ namespace Aws std::future Activate( const SubscribeToCertificateUpdatesRequest &request, OnMessageFlushCallback onMessageFlushCallback = nullptr) noexcept; + /** * Retrieve the result from activating the stream. */ std::future GetResult() noexcept; - protected: - Aws::Crt::String GetModelName() const noexcept override; + private: + std::promise m_resultPromise; + + /* Keeps the operation alive while activation is in-progress. Internally, we capture `this` in the function + * object that handles the result. If we did not do this, we risk a crash if the user drops their reference + * before the future gets completed. */ + std::shared_ptr m_selfReference; }; class AWS_GREENGRASSCOREIPC_API VerifyClientDeviceIdentityOperationContext : public OperationModelContext @@ -5647,6 +5797,7 @@ namespace Aws { return static_cast(m_taggedResult.GetOperationResponse()); } + /** * @return true if the response is associated with an expected response; * false if the response is associated with an error. @@ -5660,13 +5811,16 @@ namespace Aws TaggedResult m_taggedResult; }; - class AWS_GREENGRASSCOREIPC_API VerifyClientDeviceIdentityOperation : public ClientOperation + class AWS_GREENGRASSCOREIPC_API VerifyClientDeviceIdentityOperation + : public ClientOperation, + public std::enable_shared_from_this { public: VerifyClientDeviceIdentityOperation( ClientConnection &connection, const VerifyClientDeviceIdentityOperationContext &operationContext, Aws::Crt::Allocator *allocator = Aws::Crt::g_allocator) noexcept; + /** * Used to activate a stream for the `VerifyClientDeviceIdentityOperation` * @param request The request used for the `VerifyClientDeviceIdentityOperation` @@ -5676,13 +5830,19 @@ namespace Aws std::future Activate( const VerifyClientDeviceIdentityRequest &request, OnMessageFlushCallback onMessageFlushCallback = nullptr) noexcept; + /** * Retrieve the result from activating the stream. */ std::future GetResult() noexcept; - protected: - Aws::Crt::String GetModelName() const noexcept override; + private: + std::promise m_resultPromise; + + /* Keeps the operation alive while activation is in-progress. Internally, we capture `this` in the function + * object that handles the result. If we did not do this, we risk a crash if the user drops their reference + * before the future gets completed. */ + std::shared_ptr m_selfReference; }; class AWS_GREENGRASSCOREIPC_API AuthorizeClientDeviceActionOperationContext : public OperationModelContext @@ -5713,6 +5873,7 @@ namespace Aws { return static_cast(m_taggedResult.GetOperationResponse()); } + /** * @return true if the response is associated with an expected response; * false if the response is associated with an error. @@ -5726,13 +5887,16 @@ namespace Aws TaggedResult m_taggedResult; }; - class AWS_GREENGRASSCOREIPC_API AuthorizeClientDeviceActionOperation : public ClientOperation + class AWS_GREENGRASSCOREIPC_API AuthorizeClientDeviceActionOperation + : public ClientOperation, + public std::enable_shared_from_this { public: AuthorizeClientDeviceActionOperation( ClientConnection &connection, const AuthorizeClientDeviceActionOperationContext &operationContext, Aws::Crt::Allocator *allocator = Aws::Crt::g_allocator) noexcept; + /** * Used to activate a stream for the `AuthorizeClientDeviceActionOperation` * @param request The request used for the `AuthorizeClientDeviceActionOperation` @@ -5742,13 +5906,19 @@ namespace Aws std::future Activate( const AuthorizeClientDeviceActionRequest &request, OnMessageFlushCallback onMessageFlushCallback = nullptr) noexcept; + /** * Retrieve the result from activating the stream. */ std::future GetResult() noexcept; - protected: - Aws::Crt::String GetModelName() const noexcept override; + private: + std::promise m_resultPromise; + + /* Keeps the operation alive while activation is in-progress. Internally, we capture `this` in the function + * object that handles the result. If we did not do this, we risk a crash if the user drops their reference + * before the future gets completed. */ + std::shared_ptr m_selfReference; }; class AWS_GREENGRASSCOREIPC_API ListComponentsOperationContext : public OperationModelContext @@ -5776,6 +5946,7 @@ namespace Aws { return static_cast(m_taggedResult.GetOperationResponse()); } + /** * @return true if the response is associated with an expected response; * false if the response is associated with an error. @@ -5789,13 +5960,16 @@ namespace Aws TaggedResult m_taggedResult; }; - class AWS_GREENGRASSCOREIPC_API ListComponentsOperation : public ClientOperation + class AWS_GREENGRASSCOREIPC_API ListComponentsOperation + : public ClientOperation, + public std::enable_shared_from_this { public: ListComponentsOperation( ClientConnection &connection, const ListComponentsOperationContext &operationContext, Aws::Crt::Allocator *allocator = Aws::Crt::g_allocator) noexcept; + /** * Used to activate a stream for the `ListComponentsOperation` * @param request The request used for the `ListComponentsOperation` @@ -5805,13 +5979,19 @@ namespace Aws std::future Activate( const ListComponentsRequest &request, OnMessageFlushCallback onMessageFlushCallback = nullptr) noexcept; + /** * Retrieve the result from activating the stream. */ std::future GetResult() noexcept; - protected: - Aws::Crt::String GetModelName() const noexcept override; + private: + std::promise m_resultPromise; + + /* Keeps the operation alive while activation is in-progress. Internally, we capture `this` in the function + * object that handles the result. If we did not do this, we risk a crash if the user drops their reference + * before the future gets completed. */ + std::shared_ptr m_selfReference; }; class AWS_GREENGRASSCOREIPC_API CreateDebugPasswordOperationContext : public OperationModelContext @@ -5839,6 +6019,7 @@ namespace Aws { return static_cast(m_taggedResult.GetOperationResponse()); } + /** * @return true if the response is associated with an expected response; * false if the response is associated with an error. @@ -5852,13 +6033,16 @@ namespace Aws TaggedResult m_taggedResult; }; - class AWS_GREENGRASSCOREIPC_API CreateDebugPasswordOperation : public ClientOperation + class AWS_GREENGRASSCOREIPC_API CreateDebugPasswordOperation + : public ClientOperation, + public std::enable_shared_from_this { public: CreateDebugPasswordOperation( ClientConnection &connection, const CreateDebugPasswordOperationContext &operationContext, Aws::Crt::Allocator *allocator = Aws::Crt::g_allocator) noexcept; + /** * Used to activate a stream for the `CreateDebugPasswordOperation` * @param request The request used for the `CreateDebugPasswordOperation` @@ -5868,13 +6052,19 @@ namespace Aws std::future Activate( const CreateDebugPasswordRequest &request, OnMessageFlushCallback onMessageFlushCallback = nullptr) noexcept; + /** * Retrieve the result from activating the stream. */ std::future GetResult() noexcept; - protected: - Aws::Crt::String GetModelName() const noexcept override; + private: + std::promise m_resultPromise; + + /* Keeps the operation alive while activation is in-progress. Internally, we capture `this` in the function + * object that handles the result. If we did not do this, we risk a crash if the user drops their reference + * before the future gets completed. */ + std::shared_ptr m_selfReference; }; class AWS_GREENGRASSCOREIPC_API GetThingShadowOperationContext : public OperationModelContext @@ -5902,6 +6092,7 @@ namespace Aws { return static_cast(m_taggedResult.GetOperationResponse()); } + /** * @return true if the response is associated with an expected response; * false if the response is associated with an error. @@ -5915,13 +6106,16 @@ namespace Aws TaggedResult m_taggedResult; }; - class AWS_GREENGRASSCOREIPC_API GetThingShadowOperation : public ClientOperation + class AWS_GREENGRASSCOREIPC_API GetThingShadowOperation + : public ClientOperation, + public std::enable_shared_from_this { public: GetThingShadowOperation( ClientConnection &connection, const GetThingShadowOperationContext &operationContext, Aws::Crt::Allocator *allocator = Aws::Crt::g_allocator) noexcept; + /** * Used to activate a stream for the `GetThingShadowOperation` * @param request The request used for the `GetThingShadowOperation` @@ -5931,13 +6125,19 @@ namespace Aws std::future Activate( const GetThingShadowRequest &request, OnMessageFlushCallback onMessageFlushCallback = nullptr) noexcept; + /** * Retrieve the result from activating the stream. */ std::future GetResult() noexcept; - protected: - Aws::Crt::String GetModelName() const noexcept override; + private: + std::promise m_resultPromise; + + /* Keeps the operation alive while activation is in-progress. Internally, we capture `this` in the function + * object that handles the result. If we did not do this, we risk a crash if the user drops their reference + * before the future gets completed. */ + std::shared_ptr m_selfReference; }; class AWS_GREENGRASSCOREIPC_API SendConfigurationValidityReportOperationContext : public OperationModelContext @@ -5968,6 +6168,7 @@ namespace Aws { return static_cast(m_taggedResult.GetOperationResponse()); } + /** * @return true if the response is associated with an expected response; * false if the response is associated with an error. @@ -5981,13 +6182,16 @@ namespace Aws TaggedResult m_taggedResult; }; - class AWS_GREENGRASSCOREIPC_API SendConfigurationValidityReportOperation : public ClientOperation + class AWS_GREENGRASSCOREIPC_API SendConfigurationValidityReportOperation + : public ClientOperation, + public std::enable_shared_from_this { public: SendConfigurationValidityReportOperation( ClientConnection &connection, const SendConfigurationValidityReportOperationContext &operationContext, Aws::Crt::Allocator *allocator = Aws::Crt::g_allocator) noexcept; + /** * Used to activate a stream for the `SendConfigurationValidityReportOperation` * @param request The request used for the `SendConfigurationValidityReportOperation` @@ -5997,13 +6201,19 @@ namespace Aws std::future Activate( const SendConfigurationValidityReportRequest &request, OnMessageFlushCallback onMessageFlushCallback = nullptr) noexcept; + /** * Retrieve the result from activating the stream. */ std::future GetResult() noexcept; - protected: - Aws::Crt::String GetModelName() const noexcept override; + private: + std::promise m_resultPromise; + + /* Keeps the operation alive while activation is in-progress. Internally, we capture `this` in the function + * object that handles the result. If we did not do this, we risk a crash if the user drops their reference + * before the future gets completed. */ + std::shared_ptr m_selfReference; }; class AWS_GREENGRASSCOREIPC_API UpdateThingShadowOperationContext : public OperationModelContext @@ -6031,6 +6241,7 @@ namespace Aws { return static_cast(m_taggedResult.GetOperationResponse()); } + /** * @return true if the response is associated with an expected response; * false if the response is associated with an error. @@ -6044,13 +6255,16 @@ namespace Aws TaggedResult m_taggedResult; }; - class AWS_GREENGRASSCOREIPC_API UpdateThingShadowOperation : public ClientOperation + class AWS_GREENGRASSCOREIPC_API UpdateThingShadowOperation + : public ClientOperation, + public std::enable_shared_from_this { public: UpdateThingShadowOperation( ClientConnection &connection, const UpdateThingShadowOperationContext &operationContext, Aws::Crt::Allocator *allocator = Aws::Crt::g_allocator) noexcept; + /** * Used to activate a stream for the `UpdateThingShadowOperation` * @param request The request used for the `UpdateThingShadowOperation` @@ -6060,13 +6274,19 @@ namespace Aws std::future Activate( const UpdateThingShadowRequest &request, OnMessageFlushCallback onMessageFlushCallback = nullptr) noexcept; + /** * Retrieve the result from activating the stream. */ std::future GetResult() noexcept; - protected: - Aws::Crt::String GetModelName() const noexcept override; + private: + std::promise m_resultPromise; + + /* Keeps the operation alive while activation is in-progress. Internally, we capture `this` in the function + * object that handles the result. If we did not do this, we risk a crash if the user drops their reference + * before the future gets completed. */ + std::shared_ptr m_selfReference; }; class AWS_GREENGRASSCOREIPC_API UpdateConfigurationOperationContext : public OperationModelContext @@ -6094,6 +6314,7 @@ namespace Aws { return static_cast(m_taggedResult.GetOperationResponse()); } + /** * @return true if the response is associated with an expected response; * false if the response is associated with an error. @@ -6107,13 +6328,16 @@ namespace Aws TaggedResult m_taggedResult; }; - class AWS_GREENGRASSCOREIPC_API UpdateConfigurationOperation : public ClientOperation + class AWS_GREENGRASSCOREIPC_API UpdateConfigurationOperation + : public ClientOperation, + public std::enable_shared_from_this { public: UpdateConfigurationOperation( ClientConnection &connection, const UpdateConfigurationOperationContext &operationContext, Aws::Crt::Allocator *allocator = Aws::Crt::g_allocator) noexcept; + /** * Used to activate a stream for the `UpdateConfigurationOperation` * @param request The request used for the `UpdateConfigurationOperation` @@ -6123,13 +6347,19 @@ namespace Aws std::future Activate( const UpdateConfigurationRequest &request, OnMessageFlushCallback onMessageFlushCallback = nullptr) noexcept; + /** * Retrieve the result from activating the stream. */ std::future GetResult() noexcept; - protected: - Aws::Crt::String GetModelName() const noexcept override; + private: + std::promise m_resultPromise; + + /* Keeps the operation alive while activation is in-progress. Internally, we capture `this` in the function + * object that handles the result. If we did not do this, we risk a crash if the user drops their reference + * before the future gets completed. */ + std::shared_ptr m_selfReference; }; class AWS_GREENGRASSCOREIPC_API ValidateAuthorizationTokenOperationContext : public OperationModelContext @@ -6160,6 +6390,7 @@ namespace Aws { return static_cast(m_taggedResult.GetOperationResponse()); } + /** * @return true if the response is associated with an expected response; * false if the response is associated with an error. @@ -6173,13 +6404,16 @@ namespace Aws TaggedResult m_taggedResult; }; - class AWS_GREENGRASSCOREIPC_API ValidateAuthorizationTokenOperation : public ClientOperation + class AWS_GREENGRASSCOREIPC_API ValidateAuthorizationTokenOperation + : public ClientOperation, + public std::enable_shared_from_this { public: ValidateAuthorizationTokenOperation( ClientConnection &connection, const ValidateAuthorizationTokenOperationContext &operationContext, Aws::Crt::Allocator *allocator = Aws::Crt::g_allocator) noexcept; + /** * Used to activate a stream for the `ValidateAuthorizationTokenOperation` * @param request The request used for the `ValidateAuthorizationTokenOperation` @@ -6189,13 +6423,19 @@ namespace Aws std::future Activate( const ValidateAuthorizationTokenRequest &request, OnMessageFlushCallback onMessageFlushCallback = nullptr) noexcept; + /** * Retrieve the result from activating the stream. */ std::future GetResult() noexcept; - protected: - Aws::Crt::String GetModelName() const noexcept override; + private: + std::promise m_resultPromise; + + /* Keeps the operation alive while activation is in-progress. Internally, we capture `this` in the function + * object that handles the result. If we did not do this, we risk a crash if the user drops their reference + * before the future gets completed. */ + std::shared_ptr m_selfReference; }; class AWS_GREENGRASSCOREIPC_API RestartComponentOperationContext : public OperationModelContext @@ -6223,6 +6463,7 @@ namespace Aws { return static_cast(m_taggedResult.GetOperationResponse()); } + /** * @return true if the response is associated with an expected response; * false if the response is associated with an error. @@ -6236,13 +6477,16 @@ namespace Aws TaggedResult m_taggedResult; }; - class AWS_GREENGRASSCOREIPC_API RestartComponentOperation : public ClientOperation + class AWS_GREENGRASSCOREIPC_API RestartComponentOperation + : public ClientOperation, + public std::enable_shared_from_this { public: RestartComponentOperation( ClientConnection &connection, const RestartComponentOperationContext &operationContext, Aws::Crt::Allocator *allocator = Aws::Crt::g_allocator) noexcept; + /** * Used to activate a stream for the `RestartComponentOperation` * @param request The request used for the `RestartComponentOperation` @@ -6252,13 +6496,19 @@ namespace Aws std::future Activate( const RestartComponentRequest &request, OnMessageFlushCallback onMessageFlushCallback = nullptr) noexcept; + /** * Retrieve the result from activating the stream. */ std::future GetResult() noexcept; - protected: - Aws::Crt::String GetModelName() const noexcept override; + private: + std::promise m_resultPromise; + + /* Keeps the operation alive while activation is in-progress. Internally, we capture `this` in the function + * object that handles the result. If we did not do this, we risk a crash if the user drops their reference + * before the future gets completed. */ + std::shared_ptr m_selfReference; }; class AWS_GREENGRASSCOREIPC_API GetLocalDeploymentStatusOperationContext : public OperationModelContext @@ -6289,6 +6539,7 @@ namespace Aws { return static_cast(m_taggedResult.GetOperationResponse()); } + /** * @return true if the response is associated with an expected response; * false if the response is associated with an error. @@ -6302,13 +6553,16 @@ namespace Aws TaggedResult m_taggedResult; }; - class AWS_GREENGRASSCOREIPC_API GetLocalDeploymentStatusOperation : public ClientOperation + class AWS_GREENGRASSCOREIPC_API GetLocalDeploymentStatusOperation + : public ClientOperation, + public std::enable_shared_from_this { public: GetLocalDeploymentStatusOperation( ClientConnection &connection, const GetLocalDeploymentStatusOperationContext &operationContext, Aws::Crt::Allocator *allocator = Aws::Crt::g_allocator) noexcept; + /** * Used to activate a stream for the `GetLocalDeploymentStatusOperation` * @param request The request used for the `GetLocalDeploymentStatusOperation` @@ -6318,13 +6572,19 @@ namespace Aws std::future Activate( const GetLocalDeploymentStatusRequest &request, OnMessageFlushCallback onMessageFlushCallback = nullptr) noexcept; + /** * Retrieve the result from activating the stream. */ std::future GetResult() noexcept; - protected: - Aws::Crt::String GetModelName() const noexcept override; + private: + std::promise m_resultPromise; + + /* Keeps the operation alive while activation is in-progress. Internally, we capture `this` in the function + * object that handles the result. If we did not do this, we risk a crash if the user drops their reference + * before the future gets completed. */ + std::shared_ptr m_selfReference; }; class AWS_GREENGRASSCOREIPC_API GetSecretValueOperationContext : public OperationModelContext @@ -6352,6 +6612,7 @@ namespace Aws { return static_cast(m_taggedResult.GetOperationResponse()); } + /** * @return true if the response is associated with an expected response; * false if the response is associated with an error. @@ -6365,13 +6626,16 @@ namespace Aws TaggedResult m_taggedResult; }; - class AWS_GREENGRASSCOREIPC_API GetSecretValueOperation : public ClientOperation + class AWS_GREENGRASSCOREIPC_API GetSecretValueOperation + : public ClientOperation, + public std::enable_shared_from_this { public: GetSecretValueOperation( ClientConnection &connection, const GetSecretValueOperationContext &operationContext, Aws::Crt::Allocator *allocator = Aws::Crt::g_allocator) noexcept; + /** * Used to activate a stream for the `GetSecretValueOperation` * @param request The request used for the `GetSecretValueOperation` @@ -6381,13 +6645,19 @@ namespace Aws std::future Activate( const GetSecretValueRequest &request, OnMessageFlushCallback onMessageFlushCallback = nullptr) noexcept; + /** * Retrieve the result from activating the stream. */ std::future GetResult() noexcept; - protected: - Aws::Crt::String GetModelName() const noexcept override; + private: + std::promise m_resultPromise; + + /* Keeps the operation alive while activation is in-progress. Internally, we capture `this` in the function + * object that handles the result. If we did not do this, we risk a crash if the user drops their reference + * before the future gets completed. */ + std::shared_ptr m_selfReference; }; class AWS_GREENGRASSCOREIPC_API UpdateStateOperationContext : public OperationModelContext @@ -6415,6 +6685,7 @@ namespace Aws { return static_cast(m_taggedResult.GetOperationResponse()); } + /** * @return true if the response is associated with an expected response; * false if the response is associated with an error. @@ -6428,13 +6699,15 @@ namespace Aws TaggedResult m_taggedResult; }; - class AWS_GREENGRASSCOREIPC_API UpdateStateOperation : public ClientOperation + class AWS_GREENGRASSCOREIPC_API UpdateStateOperation : public ClientOperation, + public std::enable_shared_from_this { public: UpdateStateOperation( ClientConnection &connection, const UpdateStateOperationContext &operationContext, Aws::Crt::Allocator *allocator = Aws::Crt::g_allocator) noexcept; + /** * Used to activate a stream for the `UpdateStateOperation` * @param request The request used for the `UpdateStateOperation` @@ -6444,13 +6717,19 @@ namespace Aws std::future Activate( const UpdateStateRequest &request, OnMessageFlushCallback onMessageFlushCallback = nullptr) noexcept; + /** * Retrieve the result from activating the stream. */ std::future GetResult() noexcept; - protected: - Aws::Crt::String GetModelName() const noexcept override; + private: + std::promise m_resultPromise; + + /* Keeps the operation alive while activation is in-progress. Internally, we capture `this` in the function + * object that handles the result. If we did not do this, we risk a crash if the user drops their reference + * before the future gets completed. */ + std::shared_ptr m_selfReference; }; class AWS_GREENGRASSCOREIPC_API CancelLocalDeploymentOperationContext : public OperationModelContext @@ -6480,6 +6759,7 @@ namespace Aws { return static_cast(m_taggedResult.GetOperationResponse()); } + /** * @return true if the response is associated with an expected response; * false if the response is associated with an error. @@ -6493,13 +6773,16 @@ namespace Aws TaggedResult m_taggedResult; }; - class AWS_GREENGRASSCOREIPC_API CancelLocalDeploymentOperation : public ClientOperation + class AWS_GREENGRASSCOREIPC_API CancelLocalDeploymentOperation + : public ClientOperation, + public std::enable_shared_from_this { public: CancelLocalDeploymentOperation( ClientConnection &connection, const CancelLocalDeploymentOperationContext &operationContext, Aws::Crt::Allocator *allocator = Aws::Crt::g_allocator) noexcept; + /** * Used to activate a stream for the `CancelLocalDeploymentOperation` * @param request The request used for the `CancelLocalDeploymentOperation` @@ -6509,13 +6792,19 @@ namespace Aws std::future Activate( const CancelLocalDeploymentRequest &request, OnMessageFlushCallback onMessageFlushCallback = nullptr) noexcept; + /** * Retrieve the result from activating the stream. */ std::future GetResult() noexcept; - protected: - Aws::Crt::String GetModelName() const noexcept override; + private: + std::promise m_resultPromise; + + /* Keeps the operation alive while activation is in-progress. Internally, we capture `this` in the function + * object that handles the result. If we did not do this, we risk a crash if the user drops their reference + * before the future gets completed. */ + std::shared_ptr m_selfReference; }; class AWS_GREENGRASSCOREIPC_API ListNamedShadowsForThingOperationContext : public OperationModelContext @@ -6546,6 +6835,7 @@ namespace Aws { return static_cast(m_taggedResult.GetOperationResponse()); } + /** * @return true if the response is associated with an expected response; * false if the response is associated with an error. @@ -6559,13 +6849,16 @@ namespace Aws TaggedResult m_taggedResult; }; - class AWS_GREENGRASSCOREIPC_API ListNamedShadowsForThingOperation : public ClientOperation + class AWS_GREENGRASSCOREIPC_API ListNamedShadowsForThingOperation + : public ClientOperation, + public std::enable_shared_from_this { public: ListNamedShadowsForThingOperation( ClientConnection &connection, const ListNamedShadowsForThingOperationContext &operationContext, Aws::Crt::Allocator *allocator = Aws::Crt::g_allocator) noexcept; + /** * Used to activate a stream for the `ListNamedShadowsForThingOperation` * @param request The request used for the `ListNamedShadowsForThingOperation` @@ -6575,13 +6868,19 @@ namespace Aws std::future Activate( const ListNamedShadowsForThingRequest &request, OnMessageFlushCallback onMessageFlushCallback = nullptr) noexcept; + /** * Retrieve the result from activating the stream. */ std::future GetResult() noexcept; - protected: - Aws::Crt::String GetModelName() const noexcept override; + private: + std::promise m_resultPromise; + + /* Keeps the operation alive while activation is in-progress. Internally, we capture `this` in the function + * object that handles the result. If we did not do this, we risk a crash if the user drops their reference + * before the future gets completed. */ + std::shared_ptr m_selfReference; }; class AWS_GREENGRASSCOREIPC_API SubscribeToComponentUpdatesStreamHandler : public StreamResponseHandler @@ -6634,6 +6933,7 @@ namespace Aws * Invoked when a message is received on this continuation. */ void OnStreamEvent(Aws::Crt::ScopedResource response) override; + /** * Invoked when a message is received on this continuation but results in an error. * @@ -6641,6 +6941,7 @@ namespace Aws */ bool OnStreamError(Aws::Crt::ScopedResource error, RpcError rpcError) override; }; + class AWS_GREENGRASSCOREIPC_API SubscribeToComponentUpdatesOperationContext : public OperationModelContext { public: @@ -6669,6 +6970,7 @@ namespace Aws { return static_cast(m_taggedResult.GetOperationResponse()); } + /** * @return true if the response is associated with an expected response; * false if the response is associated with an error. @@ -6682,7 +6984,9 @@ namespace Aws TaggedResult m_taggedResult; }; - class AWS_GREENGRASSCOREIPC_API SubscribeToComponentUpdatesOperation : public ClientOperation + class AWS_GREENGRASSCOREIPC_API SubscribeToComponentUpdatesOperation + : public ClientOperation, + public std::enable_shared_from_this { public: SubscribeToComponentUpdatesOperation( @@ -6690,6 +6994,7 @@ namespace Aws std::shared_ptr streamHandler, const SubscribeToComponentUpdatesOperationContext &operationContext, Aws::Crt::Allocator *allocator = Aws::Crt::g_allocator) noexcept; + /** * Used to activate a stream for the `SubscribeToComponentUpdatesOperation` * @param request The request used for the `SubscribeToComponentUpdatesOperation` @@ -6699,13 +7004,19 @@ namespace Aws std::future Activate( const SubscribeToComponentUpdatesRequest &request, OnMessageFlushCallback onMessageFlushCallback = nullptr) noexcept; + /** * Retrieve the result from activating the stream. */ std::future GetResult() noexcept; - protected: - Aws::Crt::String GetModelName() const noexcept override; + private: + std::promise m_resultPromise; + + /* Keeps the operation alive while activation is in-progress. Internally, we capture `this` in the function + * object that handles the result. If we did not do this, we risk a crash if the user drops their reference + * before the future gets completed. */ + std::shared_ptr m_selfReference; }; class AWS_GREENGRASSCOREIPC_API ListLocalDeploymentsOperationContext : public OperationModelContext @@ -6735,6 +7046,7 @@ namespace Aws { return static_cast(m_taggedResult.GetOperationResponse()); } + /** * @return true if the response is associated with an expected response; * false if the response is associated with an error. @@ -6748,13 +7060,16 @@ namespace Aws TaggedResult m_taggedResult; }; - class AWS_GREENGRASSCOREIPC_API ListLocalDeploymentsOperation : public ClientOperation + class AWS_GREENGRASSCOREIPC_API ListLocalDeploymentsOperation + : public ClientOperation, + public std::enable_shared_from_this { public: ListLocalDeploymentsOperation( ClientConnection &connection, const ListLocalDeploymentsOperationContext &operationContext, Aws::Crt::Allocator *allocator = Aws::Crt::g_allocator) noexcept; + /** * Used to activate a stream for the `ListLocalDeploymentsOperation` * @param request The request used for the `ListLocalDeploymentsOperation` @@ -6764,13 +7079,19 @@ namespace Aws std::future Activate( const ListLocalDeploymentsRequest &request, OnMessageFlushCallback onMessageFlushCallback = nullptr) noexcept; + /** * Retrieve the result from activating the stream. */ std::future GetResult() noexcept; - protected: - Aws::Crt::String GetModelName() const noexcept override; + private: + std::promise m_resultPromise; + + /* Keeps the operation alive while activation is in-progress. Internally, we capture `this` in the function + * object that handles the result. If we did not do this, we risk a crash if the user drops their reference + * before the future gets completed. */ + std::shared_ptr m_selfReference; }; class AWS_GREENGRASSCOREIPC_API StopComponentOperationContext : public OperationModelContext @@ -6798,6 +7119,7 @@ namespace Aws { return static_cast(m_taggedResult.GetOperationResponse()); } + /** * @return true if the response is associated with an expected response; * false if the response is associated with an error. @@ -6811,13 +7133,16 @@ namespace Aws TaggedResult m_taggedResult; }; - class AWS_GREENGRASSCOREIPC_API StopComponentOperation : public ClientOperation + class AWS_GREENGRASSCOREIPC_API StopComponentOperation + : public ClientOperation, + public std::enable_shared_from_this { public: StopComponentOperation( ClientConnection &connection, const StopComponentOperationContext &operationContext, Aws::Crt::Allocator *allocator = Aws::Crt::g_allocator) noexcept; + /** * Used to activate a stream for the `StopComponentOperation` * @param request The request used for the `StopComponentOperation` @@ -6827,13 +7152,19 @@ namespace Aws std::future Activate( const StopComponentRequest &request, OnMessageFlushCallback onMessageFlushCallback = nullptr) noexcept; + /** * Retrieve the result from activating the stream. */ std::future GetResult() noexcept; - protected: - Aws::Crt::String GetModelName() const noexcept override; + private: + std::promise m_resultPromise; + + /* Keeps the operation alive while activation is in-progress. Internally, we capture `this` in the function + * object that handles the result. If we did not do this, we risk a crash if the user drops their reference + * before the future gets completed. */ + std::shared_ptr m_selfReference; }; class AWS_GREENGRASSCOREIPC_API PauseComponentOperationContext : public OperationModelContext @@ -6861,6 +7192,7 @@ namespace Aws { return static_cast(m_taggedResult.GetOperationResponse()); } + /** * @return true if the response is associated with an expected response; * false if the response is associated with an error. @@ -6874,13 +7206,16 @@ namespace Aws TaggedResult m_taggedResult; }; - class AWS_GREENGRASSCOREIPC_API PauseComponentOperation : public ClientOperation + class AWS_GREENGRASSCOREIPC_API PauseComponentOperation + : public ClientOperation, + public std::enable_shared_from_this { public: PauseComponentOperation( ClientConnection &connection, const PauseComponentOperationContext &operationContext, Aws::Crt::Allocator *allocator = Aws::Crt::g_allocator) noexcept; + /** * Used to activate a stream for the `PauseComponentOperation` * @param request The request used for the `PauseComponentOperation` @@ -6890,13 +7225,19 @@ namespace Aws std::future Activate( const PauseComponentRequest &request, OnMessageFlushCallback onMessageFlushCallback = nullptr) noexcept; + /** * Retrieve the result from activating the stream. */ std::future GetResult() noexcept; - protected: - Aws::Crt::String GetModelName() const noexcept override; + private: + std::promise m_resultPromise; + + /* Keeps the operation alive while activation is in-progress. Internally, we capture `this` in the function + * object that handles the result. If we did not do this, we risk a crash if the user drops their reference + * before the future gets completed. */ + std::shared_ptr m_selfReference; }; class AWS_GREENGRASSCOREIPC_API CreateLocalDeploymentOperationContext : public OperationModelContext @@ -6926,6 +7267,7 @@ namespace Aws { return static_cast(m_taggedResult.GetOperationResponse()); } + /** * @return true if the response is associated with an expected response; * false if the response is associated with an error. @@ -6939,13 +7281,16 @@ namespace Aws TaggedResult m_taggedResult; }; - class AWS_GREENGRASSCOREIPC_API CreateLocalDeploymentOperation : public ClientOperation + class AWS_GREENGRASSCOREIPC_API CreateLocalDeploymentOperation + : public ClientOperation, + public std::enable_shared_from_this { public: CreateLocalDeploymentOperation( ClientConnection &connection, const CreateLocalDeploymentOperationContext &operationContext, Aws::Crt::Allocator *allocator = Aws::Crt::g_allocator) noexcept; + /** * Used to activate a stream for the `CreateLocalDeploymentOperation` * @param request The request used for the `CreateLocalDeploymentOperation` @@ -6955,13 +7300,19 @@ namespace Aws std::future Activate( const CreateLocalDeploymentRequest &request, OnMessageFlushCallback onMessageFlushCallback = nullptr) noexcept; + /** * Retrieve the result from activating the stream. */ std::future GetResult() noexcept; - protected: - Aws::Crt::String GetModelName() const noexcept override; + private: + std::promise m_resultPromise; + + /* Keeps the operation alive while activation is in-progress. Internally, we capture `this` in the function + * object that handles the result. If we did not do this, we risk a crash if the user drops their reference + * before the future gets completed. */ + std::shared_ptr m_selfReference; }; class AWS_GREENGRASSCOREIPC_API GreengrassCoreIpcServiceModel : public ServiceModel diff --git a/greengrass_ipc/source/GreengrassCoreIpcModel.cpp b/greengrass_ipc/source/GreengrassCoreIpcModel.cpp index 272d0d146..1bff67566 100644 --- a/greengrass_ipc/source/GreengrassCoreIpcModel.cpp +++ b/greengrass_ipc/source/GreengrassCoreIpcModel.cpp @@ -7041,8 +7041,7 @@ namespace Aws std::future SubscribeToIoTCoreOperation::GetResult() noexcept { - return std::async( - m_asyncLaunchMode, [this]() { return SubscribeToIoTCoreResult(GetOperationResult().get()); }); + return m_resultPromise.get_future(); } SubscribeToIoTCoreOperation::SubscribeToIoTCoreOperation( @@ -7058,12 +7057,22 @@ namespace Aws const SubscribeToIoTCoreRequest &request, OnMessageFlushCallback onMessageFlushCallback) noexcept { - return ClientOperation::Activate(static_cast(&request), onMessageFlushCallback); - } - - Aws::Crt::String SubscribeToIoTCoreOperation::GetModelName() const noexcept - { - return m_operationModelContext.GetOperationName(); + bool synchronousSuccess = false; + m_selfReference = shared_from_this(); + auto activateFuture = ClientOperation::Activate( + static_cast(&request), + std::move(onMessageFlushCallback), + [this](TaggedResult &&unmodeledResult) + { + m_resultPromise.set_value(SubscribeToIoTCoreResult(std::move(unmodeledResult))); + m_selfReference = nullptr; + }, + synchronousSuccess); + if (!synchronousSuccess) + { + m_selfReference = nullptr; + } + return activateFuture; } ResumeComponentOperationContext::ResumeComponentOperationContext( @@ -7111,8 +7120,7 @@ namespace Aws std::future ResumeComponentOperation::GetResult() noexcept { - return std::async( - m_asyncLaunchMode, [this]() { return ResumeComponentResult(GetOperationResult().get()); }); + return m_resultPromise.get_future(); } ResumeComponentOperation::ResumeComponentOperation( @@ -7127,12 +7135,22 @@ namespace Aws const ResumeComponentRequest &request, OnMessageFlushCallback onMessageFlushCallback) noexcept { - return ClientOperation::Activate(static_cast(&request), onMessageFlushCallback); - } - - Aws::Crt::String ResumeComponentOperation::GetModelName() const noexcept - { - return m_operationModelContext.GetOperationName(); + bool synchronousSuccess = false; + m_selfReference = shared_from_this(); + auto activateFuture = ClientOperation::Activate( + static_cast(&request), + std::move(onMessageFlushCallback), + [this](TaggedResult &&unmodeledResult) + { + m_resultPromise.set_value(ResumeComponentResult(std::move(unmodeledResult))); + m_selfReference = nullptr; + }, + synchronousSuccess); + if (!synchronousSuccess) + { + m_selfReference = nullptr; + } + return activateFuture; } PublishToIoTCoreOperationContext::PublishToIoTCoreOperationContext( @@ -7180,8 +7198,7 @@ namespace Aws std::future PublishToIoTCoreOperation::GetResult() noexcept { - return std::async( - m_asyncLaunchMode, [this]() { return PublishToIoTCoreResult(GetOperationResult().get()); }); + return m_resultPromise.get_future(); } PublishToIoTCoreOperation::PublishToIoTCoreOperation( @@ -7196,12 +7213,22 @@ namespace Aws const PublishToIoTCoreRequest &request, OnMessageFlushCallback onMessageFlushCallback) noexcept { - return ClientOperation::Activate(static_cast(&request), onMessageFlushCallback); - } - - Aws::Crt::String PublishToIoTCoreOperation::GetModelName() const noexcept - { - return m_operationModelContext.GetOperationName(); + bool synchronousSuccess = false; + m_selfReference = shared_from_this(); + auto activateFuture = ClientOperation::Activate( + static_cast(&request), + std::move(onMessageFlushCallback), + [this](TaggedResult &&unmodeledResult) + { + m_resultPromise.set_value(PublishToIoTCoreResult(std::move(unmodeledResult))); + m_selfReference = nullptr; + }, + synchronousSuccess); + if (!synchronousSuccess) + { + m_selfReference = nullptr; + } + return activateFuture; } void SubscribeToConfigurationUpdateStreamHandler::OnStreamEvent( @@ -7279,9 +7306,7 @@ namespace Aws std::future SubscribeToConfigurationUpdateOperation::GetResult() noexcept { - return std::async( - m_asyncLaunchMode, - [this]() { return SubscribeToConfigurationUpdateResult(GetOperationResult().get()); }); + return m_resultPromise.get_future(); } SubscribeToConfigurationUpdateOperation::SubscribeToConfigurationUpdateOperation( @@ -7297,12 +7322,22 @@ namespace Aws const SubscribeToConfigurationUpdateRequest &request, OnMessageFlushCallback onMessageFlushCallback) noexcept { - return ClientOperation::Activate(static_cast(&request), onMessageFlushCallback); - } - - Aws::Crt::String SubscribeToConfigurationUpdateOperation::GetModelName() const noexcept - { - return m_operationModelContext.GetOperationName(); + bool synchronousSuccess = false; + m_selfReference = shared_from_this(); + auto activateFuture = ClientOperation::Activate( + static_cast(&request), + std::move(onMessageFlushCallback), + [this](TaggedResult &&unmodeledResult) + { + m_resultPromise.set_value(SubscribeToConfigurationUpdateResult(std::move(unmodeledResult))); + m_selfReference = nullptr; + }, + synchronousSuccess); + if (!synchronousSuccess) + { + m_selfReference = nullptr; + } + return activateFuture; } DeleteThingShadowOperationContext::DeleteThingShadowOperationContext( @@ -7350,8 +7385,7 @@ namespace Aws std::future DeleteThingShadowOperation::GetResult() noexcept { - return std::async( - m_asyncLaunchMode, [this]() { return DeleteThingShadowResult(GetOperationResult().get()); }); + return m_resultPromise.get_future(); } DeleteThingShadowOperation::DeleteThingShadowOperation( @@ -7366,12 +7400,22 @@ namespace Aws const DeleteThingShadowRequest &request, OnMessageFlushCallback onMessageFlushCallback) noexcept { - return ClientOperation::Activate(static_cast(&request), onMessageFlushCallback); - } - - Aws::Crt::String DeleteThingShadowOperation::GetModelName() const noexcept - { - return m_operationModelContext.GetOperationName(); + bool synchronousSuccess = false; + m_selfReference = shared_from_this(); + auto activateFuture = ClientOperation::Activate( + static_cast(&request), + std::move(onMessageFlushCallback), + [this](TaggedResult &&unmodeledResult) + { + m_resultPromise.set_value(DeleteThingShadowResult(std::move(unmodeledResult))); + m_selfReference = nullptr; + }, + synchronousSuccess); + if (!synchronousSuccess) + { + m_selfReference = nullptr; + } + return activateFuture; } PutComponentMetricOperationContext::PutComponentMetricOperationContext( @@ -7419,8 +7463,7 @@ namespace Aws std::future PutComponentMetricOperation::GetResult() noexcept { - return std::async( - m_asyncLaunchMode, [this]() { return PutComponentMetricResult(GetOperationResult().get()); }); + return m_resultPromise.get_future(); } PutComponentMetricOperation::PutComponentMetricOperation( @@ -7435,12 +7478,22 @@ namespace Aws const PutComponentMetricRequest &request, OnMessageFlushCallback onMessageFlushCallback) noexcept { - return ClientOperation::Activate(static_cast(&request), onMessageFlushCallback); - } - - Aws::Crt::String PutComponentMetricOperation::GetModelName() const noexcept - { - return m_operationModelContext.GetOperationName(); + bool synchronousSuccess = false; + m_selfReference = shared_from_this(); + auto activateFuture = ClientOperation::Activate( + static_cast(&request), + std::move(onMessageFlushCallback), + [this](TaggedResult &&unmodeledResult) + { + m_resultPromise.set_value(PutComponentMetricResult(std::move(unmodeledResult))); + m_selfReference = nullptr; + }, + synchronousSuccess); + if (!synchronousSuccess) + { + m_selfReference = nullptr; + } + return activateFuture; } DeferComponentUpdateOperationContext::DeferComponentUpdateOperationContext( @@ -7488,8 +7541,7 @@ namespace Aws std::future DeferComponentUpdateOperation::GetResult() noexcept { - return std::async( - m_asyncLaunchMode, [this]() { return DeferComponentUpdateResult(GetOperationResult().get()); }); + return m_resultPromise.get_future(); } DeferComponentUpdateOperation::DeferComponentUpdateOperation( @@ -7504,12 +7556,22 @@ namespace Aws const DeferComponentUpdateRequest &request, OnMessageFlushCallback onMessageFlushCallback) noexcept { - return ClientOperation::Activate(static_cast(&request), onMessageFlushCallback); - } - - Aws::Crt::String DeferComponentUpdateOperation::GetModelName() const noexcept - { - return m_operationModelContext.GetOperationName(); + bool synchronousSuccess = false; + m_selfReference = shared_from_this(); + auto activateFuture = ClientOperation::Activate( + static_cast(&request), + std::move(onMessageFlushCallback), + [this](TaggedResult &&unmodeledResult) + { + m_resultPromise.set_value(DeferComponentUpdateResult(std::move(unmodeledResult))); + m_selfReference = nullptr; + }, + synchronousSuccess); + if (!synchronousSuccess) + { + m_selfReference = nullptr; + } + return activateFuture; } void SubscribeToValidateConfigurationUpdatesStreamHandler::OnStreamEvent( @@ -7584,9 +7646,7 @@ namespace Aws std::future SubscribeToValidateConfigurationUpdatesOperation:: GetResult() noexcept { - return std::async( - m_asyncLaunchMode, - [this]() { return SubscribeToValidateConfigurationUpdatesResult(GetOperationResult().get()); }); + return m_resultPromise.get_future(); } SubscribeToValidateConfigurationUpdatesOperation::SubscribeToValidateConfigurationUpdatesOperation( @@ -7602,12 +7662,23 @@ namespace Aws const SubscribeToValidateConfigurationUpdatesRequest &request, OnMessageFlushCallback onMessageFlushCallback) noexcept { - return ClientOperation::Activate(static_cast(&request), onMessageFlushCallback); - } - - Aws::Crt::String SubscribeToValidateConfigurationUpdatesOperation::GetModelName() const noexcept - { - return m_operationModelContext.GetOperationName(); + bool synchronousSuccess = false; + m_selfReference = shared_from_this(); + auto activateFuture = ClientOperation::Activate( + static_cast(&request), + std::move(onMessageFlushCallback), + [this](TaggedResult &&unmodeledResult) + { + m_resultPromise.set_value( + SubscribeToValidateConfigurationUpdatesResult(std::move(unmodeledResult))); + m_selfReference = nullptr; + }, + synchronousSuccess); + if (!synchronousSuccess) + { + m_selfReference = nullptr; + } + return activateFuture; } GetConfigurationOperationContext::GetConfigurationOperationContext( @@ -7655,8 +7726,7 @@ namespace Aws std::future GetConfigurationOperation::GetResult() noexcept { - return std::async( - m_asyncLaunchMode, [this]() { return GetConfigurationResult(GetOperationResult().get()); }); + return m_resultPromise.get_future(); } GetConfigurationOperation::GetConfigurationOperation( @@ -7671,12 +7741,22 @@ namespace Aws const GetConfigurationRequest &request, OnMessageFlushCallback onMessageFlushCallback) noexcept { - return ClientOperation::Activate(static_cast(&request), onMessageFlushCallback); - } - - Aws::Crt::String GetConfigurationOperation::GetModelName() const noexcept - { - return m_operationModelContext.GetOperationName(); + bool synchronousSuccess = false; + m_selfReference = shared_from_this(); + auto activateFuture = ClientOperation::Activate( + static_cast(&request), + std::move(onMessageFlushCallback), + [this](TaggedResult &&unmodeledResult) + { + m_resultPromise.set_value(GetConfigurationResult(std::move(unmodeledResult))); + m_selfReference = nullptr; + }, + synchronousSuccess); + if (!synchronousSuccess) + { + m_selfReference = nullptr; + } + return activateFuture; } void SubscribeToTopicStreamHandler::OnStreamEvent(Aws::Crt::ScopedResource response) @@ -7759,8 +7839,7 @@ namespace Aws std::future SubscribeToTopicOperation::GetResult() noexcept { - return std::async( - m_asyncLaunchMode, [this]() { return SubscribeToTopicResult(GetOperationResult().get()); }); + return m_resultPromise.get_future(); } SubscribeToTopicOperation::SubscribeToTopicOperation( @@ -7776,12 +7855,22 @@ namespace Aws const SubscribeToTopicRequest &request, OnMessageFlushCallback onMessageFlushCallback) noexcept { - return ClientOperation::Activate(static_cast(&request), onMessageFlushCallback); - } - - Aws::Crt::String SubscribeToTopicOperation::GetModelName() const noexcept - { - return m_operationModelContext.GetOperationName(); + bool synchronousSuccess = false; + m_selfReference = shared_from_this(); + auto activateFuture = ClientOperation::Activate( + static_cast(&request), + std::move(onMessageFlushCallback), + [this](TaggedResult &&unmodeledResult) + { + m_resultPromise.set_value(SubscribeToTopicResult(std::move(unmodeledResult))); + m_selfReference = nullptr; + }, + synchronousSuccess); + if (!synchronousSuccess) + { + m_selfReference = nullptr; + } + return activateFuture; } GetComponentDetailsOperationContext::GetComponentDetailsOperationContext( @@ -7829,8 +7918,7 @@ namespace Aws std::future GetComponentDetailsOperation::GetResult() noexcept { - return std::async( - m_asyncLaunchMode, [this]() { return GetComponentDetailsResult(GetOperationResult().get()); }); + return m_resultPromise.get_future(); } GetComponentDetailsOperation::GetComponentDetailsOperation( @@ -7845,12 +7933,22 @@ namespace Aws const GetComponentDetailsRequest &request, OnMessageFlushCallback onMessageFlushCallback) noexcept { - return ClientOperation::Activate(static_cast(&request), onMessageFlushCallback); - } - - Aws::Crt::String GetComponentDetailsOperation::GetModelName() const noexcept - { - return m_operationModelContext.GetOperationName(); + bool synchronousSuccess = false; + m_selfReference = shared_from_this(); + auto activateFuture = ClientOperation::Activate( + static_cast(&request), + std::move(onMessageFlushCallback), + [this](TaggedResult &&unmodeledResult) + { + m_resultPromise.set_value(GetComponentDetailsResult(std::move(unmodeledResult))); + m_selfReference = nullptr; + }, + synchronousSuccess); + if (!synchronousSuccess) + { + m_selfReference = nullptr; + } + return activateFuture; } GetClientDeviceAuthTokenOperationContext::GetClientDeviceAuthTokenOperationContext( @@ -7898,8 +7996,7 @@ namespace Aws std::future GetClientDeviceAuthTokenOperation::GetResult() noexcept { - return std::async( - m_asyncLaunchMode, [this]() { return GetClientDeviceAuthTokenResult(GetOperationResult().get()); }); + return m_resultPromise.get_future(); } GetClientDeviceAuthTokenOperation::GetClientDeviceAuthTokenOperation( @@ -7914,12 +8011,22 @@ namespace Aws const GetClientDeviceAuthTokenRequest &request, OnMessageFlushCallback onMessageFlushCallback) noexcept { - return ClientOperation::Activate(static_cast(&request), onMessageFlushCallback); - } - - Aws::Crt::String GetClientDeviceAuthTokenOperation::GetModelName() const noexcept - { - return m_operationModelContext.GetOperationName(); + bool synchronousSuccess = false; + m_selfReference = shared_from_this(); + auto activateFuture = ClientOperation::Activate( + static_cast(&request), + std::move(onMessageFlushCallback), + [this](TaggedResult &&unmodeledResult) + { + m_resultPromise.set_value(GetClientDeviceAuthTokenResult(std::move(unmodeledResult))); + m_selfReference = nullptr; + }, + synchronousSuccess); + if (!synchronousSuccess) + { + m_selfReference = nullptr; + } + return activateFuture; } PublishToTopicOperationContext::PublishToTopicOperationContext( @@ -7967,7 +8074,7 @@ namespace Aws std::future PublishToTopicOperation::GetResult() noexcept { - return std::async(m_asyncLaunchMode, [this]() { return PublishToTopicResult(GetOperationResult().get()); }); + return m_resultPromise.get_future(); } PublishToTopicOperation::PublishToTopicOperation( @@ -7982,12 +8089,22 @@ namespace Aws const PublishToTopicRequest &request, OnMessageFlushCallback onMessageFlushCallback) noexcept { - return ClientOperation::Activate(static_cast(&request), onMessageFlushCallback); - } - - Aws::Crt::String PublishToTopicOperation::GetModelName() const noexcept - { - return m_operationModelContext.GetOperationName(); + bool synchronousSuccess = false; + m_selfReference = shared_from_this(); + auto activateFuture = ClientOperation::Activate( + static_cast(&request), + std::move(onMessageFlushCallback), + [this](TaggedResult &&unmodeledResult) + { + m_resultPromise.set_value(PublishToTopicResult(std::move(unmodeledResult))); + m_selfReference = nullptr; + }, + synchronousSuccess); + if (!synchronousSuccess) + { + m_selfReference = nullptr; + } + return activateFuture; } void SubscribeToCertificateUpdatesStreamHandler::OnStreamEvent( @@ -8071,9 +8188,7 @@ namespace Aws std::future SubscribeToCertificateUpdatesOperation::GetResult() noexcept { - return std::async( - m_asyncLaunchMode, - [this]() { return SubscribeToCertificateUpdatesResult(GetOperationResult().get()); }); + return m_resultPromise.get_future(); } SubscribeToCertificateUpdatesOperation::SubscribeToCertificateUpdatesOperation( @@ -8089,12 +8204,22 @@ namespace Aws const SubscribeToCertificateUpdatesRequest &request, OnMessageFlushCallback onMessageFlushCallback) noexcept { - return ClientOperation::Activate(static_cast(&request), onMessageFlushCallback); - } - - Aws::Crt::String SubscribeToCertificateUpdatesOperation::GetModelName() const noexcept - { - return m_operationModelContext.GetOperationName(); + bool synchronousSuccess = false; + m_selfReference = shared_from_this(); + auto activateFuture = ClientOperation::Activate( + static_cast(&request), + std::move(onMessageFlushCallback), + [this](TaggedResult &&unmodeledResult) + { + m_resultPromise.set_value(SubscribeToCertificateUpdatesResult(std::move(unmodeledResult))); + m_selfReference = nullptr; + }, + synchronousSuccess); + if (!synchronousSuccess) + { + m_selfReference = nullptr; + } + return activateFuture; } VerifyClientDeviceIdentityOperationContext::VerifyClientDeviceIdentityOperationContext( @@ -8142,8 +8267,7 @@ namespace Aws std::future VerifyClientDeviceIdentityOperation::GetResult() noexcept { - return std::async( - m_asyncLaunchMode, [this]() { return VerifyClientDeviceIdentityResult(GetOperationResult().get()); }); + return m_resultPromise.get_future(); } VerifyClientDeviceIdentityOperation::VerifyClientDeviceIdentityOperation( @@ -8158,12 +8282,22 @@ namespace Aws const VerifyClientDeviceIdentityRequest &request, OnMessageFlushCallback onMessageFlushCallback) noexcept { - return ClientOperation::Activate(static_cast(&request), onMessageFlushCallback); - } - - Aws::Crt::String VerifyClientDeviceIdentityOperation::GetModelName() const noexcept - { - return m_operationModelContext.GetOperationName(); + bool synchronousSuccess = false; + m_selfReference = shared_from_this(); + auto activateFuture = ClientOperation::Activate( + static_cast(&request), + std::move(onMessageFlushCallback), + [this](TaggedResult &&unmodeledResult) + { + m_resultPromise.set_value(VerifyClientDeviceIdentityResult(std::move(unmodeledResult))); + m_selfReference = nullptr; + }, + synchronousSuccess); + if (!synchronousSuccess) + { + m_selfReference = nullptr; + } + return activateFuture; } AuthorizeClientDeviceActionOperationContext::AuthorizeClientDeviceActionOperationContext( @@ -8211,8 +8345,7 @@ namespace Aws std::future AuthorizeClientDeviceActionOperation::GetResult() noexcept { - return std::async( - m_asyncLaunchMode, [this]() { return AuthorizeClientDeviceActionResult(GetOperationResult().get()); }); + return m_resultPromise.get_future(); } AuthorizeClientDeviceActionOperation::AuthorizeClientDeviceActionOperation( @@ -8227,12 +8360,22 @@ namespace Aws const AuthorizeClientDeviceActionRequest &request, OnMessageFlushCallback onMessageFlushCallback) noexcept { - return ClientOperation::Activate(static_cast(&request), onMessageFlushCallback); - } - - Aws::Crt::String AuthorizeClientDeviceActionOperation::GetModelName() const noexcept - { - return m_operationModelContext.GetOperationName(); + bool synchronousSuccess = false; + m_selfReference = shared_from_this(); + auto activateFuture = ClientOperation::Activate( + static_cast(&request), + std::move(onMessageFlushCallback), + [this](TaggedResult &&unmodeledResult) + { + m_resultPromise.set_value(AuthorizeClientDeviceActionResult(std::move(unmodeledResult))); + m_selfReference = nullptr; + }, + synchronousSuccess); + if (!synchronousSuccess) + { + m_selfReference = nullptr; + } + return activateFuture; } ListComponentsOperationContext::ListComponentsOperationContext( @@ -8280,7 +8423,7 @@ namespace Aws std::future ListComponentsOperation::GetResult() noexcept { - return std::async(m_asyncLaunchMode, [this]() { return ListComponentsResult(GetOperationResult().get()); }); + return m_resultPromise.get_future(); } ListComponentsOperation::ListComponentsOperation( @@ -8295,12 +8438,22 @@ namespace Aws const ListComponentsRequest &request, OnMessageFlushCallback onMessageFlushCallback) noexcept { - return ClientOperation::Activate(static_cast(&request), onMessageFlushCallback); - } - - Aws::Crt::String ListComponentsOperation::GetModelName() const noexcept - { - return m_operationModelContext.GetOperationName(); + bool synchronousSuccess = false; + m_selfReference = shared_from_this(); + auto activateFuture = ClientOperation::Activate( + static_cast(&request), + std::move(onMessageFlushCallback), + [this](TaggedResult &&unmodeledResult) + { + m_resultPromise.set_value(ListComponentsResult(std::move(unmodeledResult))); + m_selfReference = nullptr; + }, + synchronousSuccess); + if (!synchronousSuccess) + { + m_selfReference = nullptr; + } + return activateFuture; } CreateDebugPasswordOperationContext::CreateDebugPasswordOperationContext( @@ -8348,8 +8501,7 @@ namespace Aws std::future CreateDebugPasswordOperation::GetResult() noexcept { - return std::async( - m_asyncLaunchMode, [this]() { return CreateDebugPasswordResult(GetOperationResult().get()); }); + return m_resultPromise.get_future(); } CreateDebugPasswordOperation::CreateDebugPasswordOperation( @@ -8364,12 +8516,22 @@ namespace Aws const CreateDebugPasswordRequest &request, OnMessageFlushCallback onMessageFlushCallback) noexcept { - return ClientOperation::Activate(static_cast(&request), onMessageFlushCallback); - } - - Aws::Crt::String CreateDebugPasswordOperation::GetModelName() const noexcept - { - return m_operationModelContext.GetOperationName(); + bool synchronousSuccess = false; + m_selfReference = shared_from_this(); + auto activateFuture = ClientOperation::Activate( + static_cast(&request), + std::move(onMessageFlushCallback), + [this](TaggedResult &&unmodeledResult) + { + m_resultPromise.set_value(CreateDebugPasswordResult(std::move(unmodeledResult))); + m_selfReference = nullptr; + }, + synchronousSuccess); + if (!synchronousSuccess) + { + m_selfReference = nullptr; + } + return activateFuture; } GetThingShadowOperationContext::GetThingShadowOperationContext( @@ -8417,7 +8579,7 @@ namespace Aws std::future GetThingShadowOperation::GetResult() noexcept { - return std::async(m_asyncLaunchMode, [this]() { return GetThingShadowResult(GetOperationResult().get()); }); + return m_resultPromise.get_future(); } GetThingShadowOperation::GetThingShadowOperation( @@ -8432,12 +8594,22 @@ namespace Aws const GetThingShadowRequest &request, OnMessageFlushCallback onMessageFlushCallback) noexcept { - return ClientOperation::Activate(static_cast(&request), onMessageFlushCallback); - } - - Aws::Crt::String GetThingShadowOperation::GetModelName() const noexcept - { - return m_operationModelContext.GetOperationName(); + bool synchronousSuccess = false; + m_selfReference = shared_from_this(); + auto activateFuture = ClientOperation::Activate( + static_cast(&request), + std::move(onMessageFlushCallback), + [this](TaggedResult &&unmodeledResult) + { + m_resultPromise.set_value(GetThingShadowResult(std::move(unmodeledResult))); + m_selfReference = nullptr; + }, + synchronousSuccess); + if (!synchronousSuccess) + { + m_selfReference = nullptr; + } + return activateFuture; } SendConfigurationValidityReportOperationContext::SendConfigurationValidityReportOperationContext( @@ -8486,9 +8658,7 @@ namespace Aws std::future SendConfigurationValidityReportOperation:: GetResult() noexcept { - return std::async( - m_asyncLaunchMode, - [this]() { return SendConfigurationValidityReportResult(GetOperationResult().get()); }); + return m_resultPromise.get_future(); } SendConfigurationValidityReportOperation::SendConfigurationValidityReportOperation( @@ -8503,12 +8673,22 @@ namespace Aws const SendConfigurationValidityReportRequest &request, OnMessageFlushCallback onMessageFlushCallback) noexcept { - return ClientOperation::Activate(static_cast(&request), onMessageFlushCallback); - } - - Aws::Crt::String SendConfigurationValidityReportOperation::GetModelName() const noexcept - { - return m_operationModelContext.GetOperationName(); + bool synchronousSuccess = false; + m_selfReference = shared_from_this(); + auto activateFuture = ClientOperation::Activate( + static_cast(&request), + std::move(onMessageFlushCallback), + [this](TaggedResult &&unmodeledResult) + { + m_resultPromise.set_value(SendConfigurationValidityReportResult(std::move(unmodeledResult))); + m_selfReference = nullptr; + }, + synchronousSuccess); + if (!synchronousSuccess) + { + m_selfReference = nullptr; + } + return activateFuture; } UpdateThingShadowOperationContext::UpdateThingShadowOperationContext( @@ -8556,8 +8736,7 @@ namespace Aws std::future UpdateThingShadowOperation::GetResult() noexcept { - return std::async( - m_asyncLaunchMode, [this]() { return UpdateThingShadowResult(GetOperationResult().get()); }); + return m_resultPromise.get_future(); } UpdateThingShadowOperation::UpdateThingShadowOperation( @@ -8572,12 +8751,22 @@ namespace Aws const UpdateThingShadowRequest &request, OnMessageFlushCallback onMessageFlushCallback) noexcept { - return ClientOperation::Activate(static_cast(&request), onMessageFlushCallback); - } - - Aws::Crt::String UpdateThingShadowOperation::GetModelName() const noexcept - { - return m_operationModelContext.GetOperationName(); + bool synchronousSuccess = false; + m_selfReference = shared_from_this(); + auto activateFuture = ClientOperation::Activate( + static_cast(&request), + std::move(onMessageFlushCallback), + [this](TaggedResult &&unmodeledResult) + { + m_resultPromise.set_value(UpdateThingShadowResult(std::move(unmodeledResult))); + m_selfReference = nullptr; + }, + synchronousSuccess); + if (!synchronousSuccess) + { + m_selfReference = nullptr; + } + return activateFuture; } UpdateConfigurationOperationContext::UpdateConfigurationOperationContext( @@ -8625,8 +8814,7 @@ namespace Aws std::future UpdateConfigurationOperation::GetResult() noexcept { - return std::async( - m_asyncLaunchMode, [this]() { return UpdateConfigurationResult(GetOperationResult().get()); }); + return m_resultPromise.get_future(); } UpdateConfigurationOperation::UpdateConfigurationOperation( @@ -8641,12 +8829,22 @@ namespace Aws const UpdateConfigurationRequest &request, OnMessageFlushCallback onMessageFlushCallback) noexcept { - return ClientOperation::Activate(static_cast(&request), onMessageFlushCallback); - } - - Aws::Crt::String UpdateConfigurationOperation::GetModelName() const noexcept - { - return m_operationModelContext.GetOperationName(); + bool synchronousSuccess = false; + m_selfReference = shared_from_this(); + auto activateFuture = ClientOperation::Activate( + static_cast(&request), + std::move(onMessageFlushCallback), + [this](TaggedResult &&unmodeledResult) + { + m_resultPromise.set_value(UpdateConfigurationResult(std::move(unmodeledResult))); + m_selfReference = nullptr; + }, + synchronousSuccess); + if (!synchronousSuccess) + { + m_selfReference = nullptr; + } + return activateFuture; } ValidateAuthorizationTokenOperationContext::ValidateAuthorizationTokenOperationContext( @@ -8694,8 +8892,7 @@ namespace Aws std::future ValidateAuthorizationTokenOperation::GetResult() noexcept { - return std::async( - m_asyncLaunchMode, [this]() { return ValidateAuthorizationTokenResult(GetOperationResult().get()); }); + return m_resultPromise.get_future(); } ValidateAuthorizationTokenOperation::ValidateAuthorizationTokenOperation( @@ -8710,12 +8907,22 @@ namespace Aws const ValidateAuthorizationTokenRequest &request, OnMessageFlushCallback onMessageFlushCallback) noexcept { - return ClientOperation::Activate(static_cast(&request), onMessageFlushCallback); - } - - Aws::Crt::String ValidateAuthorizationTokenOperation::GetModelName() const noexcept - { - return m_operationModelContext.GetOperationName(); + bool synchronousSuccess = false; + m_selfReference = shared_from_this(); + auto activateFuture = ClientOperation::Activate( + static_cast(&request), + std::move(onMessageFlushCallback), + [this](TaggedResult &&unmodeledResult) + { + m_resultPromise.set_value(ValidateAuthorizationTokenResult(std::move(unmodeledResult))); + m_selfReference = nullptr; + }, + synchronousSuccess); + if (!synchronousSuccess) + { + m_selfReference = nullptr; + } + return activateFuture; } RestartComponentOperationContext::RestartComponentOperationContext( @@ -8763,8 +8970,7 @@ namespace Aws std::future RestartComponentOperation::GetResult() noexcept { - return std::async( - m_asyncLaunchMode, [this]() { return RestartComponentResult(GetOperationResult().get()); }); + return m_resultPromise.get_future(); } RestartComponentOperation::RestartComponentOperation( @@ -8779,12 +8985,22 @@ namespace Aws const RestartComponentRequest &request, OnMessageFlushCallback onMessageFlushCallback) noexcept { - return ClientOperation::Activate(static_cast(&request), onMessageFlushCallback); - } - - Aws::Crt::String RestartComponentOperation::GetModelName() const noexcept - { - return m_operationModelContext.GetOperationName(); + bool synchronousSuccess = false; + m_selfReference = shared_from_this(); + auto activateFuture = ClientOperation::Activate( + static_cast(&request), + std::move(onMessageFlushCallback), + [this](TaggedResult &&unmodeledResult) + { + m_resultPromise.set_value(RestartComponentResult(std::move(unmodeledResult))); + m_selfReference = nullptr; + }, + synchronousSuccess); + if (!synchronousSuccess) + { + m_selfReference = nullptr; + } + return activateFuture; } GetLocalDeploymentStatusOperationContext::GetLocalDeploymentStatusOperationContext( @@ -8832,8 +9048,7 @@ namespace Aws std::future GetLocalDeploymentStatusOperation::GetResult() noexcept { - return std::async( - m_asyncLaunchMode, [this]() { return GetLocalDeploymentStatusResult(GetOperationResult().get()); }); + return m_resultPromise.get_future(); } GetLocalDeploymentStatusOperation::GetLocalDeploymentStatusOperation( @@ -8848,12 +9063,22 @@ namespace Aws const GetLocalDeploymentStatusRequest &request, OnMessageFlushCallback onMessageFlushCallback) noexcept { - return ClientOperation::Activate(static_cast(&request), onMessageFlushCallback); - } - - Aws::Crt::String GetLocalDeploymentStatusOperation::GetModelName() const noexcept - { - return m_operationModelContext.GetOperationName(); + bool synchronousSuccess = false; + m_selfReference = shared_from_this(); + auto activateFuture = ClientOperation::Activate( + static_cast(&request), + std::move(onMessageFlushCallback), + [this](TaggedResult &&unmodeledResult) + { + m_resultPromise.set_value(GetLocalDeploymentStatusResult(std::move(unmodeledResult))); + m_selfReference = nullptr; + }, + synchronousSuccess); + if (!synchronousSuccess) + { + m_selfReference = nullptr; + } + return activateFuture; } GetSecretValueOperationContext::GetSecretValueOperationContext( @@ -8901,7 +9126,7 @@ namespace Aws std::future GetSecretValueOperation::GetResult() noexcept { - return std::async(m_asyncLaunchMode, [this]() { return GetSecretValueResult(GetOperationResult().get()); }); + return m_resultPromise.get_future(); } GetSecretValueOperation::GetSecretValueOperation( @@ -8916,12 +9141,22 @@ namespace Aws const GetSecretValueRequest &request, OnMessageFlushCallback onMessageFlushCallback) noexcept { - return ClientOperation::Activate(static_cast(&request), onMessageFlushCallback); - } - - Aws::Crt::String GetSecretValueOperation::GetModelName() const noexcept - { - return m_operationModelContext.GetOperationName(); + bool synchronousSuccess = false; + m_selfReference = shared_from_this(); + auto activateFuture = ClientOperation::Activate( + static_cast(&request), + std::move(onMessageFlushCallback), + [this](TaggedResult &&unmodeledResult) + { + m_resultPromise.set_value(GetSecretValueResult(std::move(unmodeledResult))); + m_selfReference = nullptr; + }, + synchronousSuccess); + if (!synchronousSuccess) + { + m_selfReference = nullptr; + } + return activateFuture; } UpdateStateOperationContext::UpdateStateOperationContext( @@ -8968,7 +9203,7 @@ namespace Aws std::future UpdateStateOperation::GetResult() noexcept { - return std::async(m_asyncLaunchMode, [this]() { return UpdateStateResult(GetOperationResult().get()); }); + return m_resultPromise.get_future(); } UpdateStateOperation::UpdateStateOperation( @@ -8983,12 +9218,22 @@ namespace Aws const UpdateStateRequest &request, OnMessageFlushCallback onMessageFlushCallback) noexcept { - return ClientOperation::Activate(static_cast(&request), onMessageFlushCallback); - } - - Aws::Crt::String UpdateStateOperation::GetModelName() const noexcept - { - return m_operationModelContext.GetOperationName(); + bool synchronousSuccess = false; + m_selfReference = shared_from_this(); + auto activateFuture = ClientOperation::Activate( + static_cast(&request), + std::move(onMessageFlushCallback), + [this](TaggedResult &&unmodeledResult) + { + m_resultPromise.set_value(UpdateStateResult(std::move(unmodeledResult))); + m_selfReference = nullptr; + }, + synchronousSuccess); + if (!synchronousSuccess) + { + m_selfReference = nullptr; + } + return activateFuture; } CancelLocalDeploymentOperationContext::CancelLocalDeploymentOperationContext( @@ -9036,8 +9281,7 @@ namespace Aws std::future CancelLocalDeploymentOperation::GetResult() noexcept { - return std::async( - m_asyncLaunchMode, [this]() { return CancelLocalDeploymentResult(GetOperationResult().get()); }); + return m_resultPromise.get_future(); } CancelLocalDeploymentOperation::CancelLocalDeploymentOperation( @@ -9052,12 +9296,22 @@ namespace Aws const CancelLocalDeploymentRequest &request, OnMessageFlushCallback onMessageFlushCallback) noexcept { - return ClientOperation::Activate(static_cast(&request), onMessageFlushCallback); - } - - Aws::Crt::String CancelLocalDeploymentOperation::GetModelName() const noexcept - { - return m_operationModelContext.GetOperationName(); + bool synchronousSuccess = false; + m_selfReference = shared_from_this(); + auto activateFuture = ClientOperation::Activate( + static_cast(&request), + std::move(onMessageFlushCallback), + [this](TaggedResult &&unmodeledResult) + { + m_resultPromise.set_value(CancelLocalDeploymentResult(std::move(unmodeledResult))); + m_selfReference = nullptr; + }, + synchronousSuccess); + if (!synchronousSuccess) + { + m_selfReference = nullptr; + } + return activateFuture; } ListNamedShadowsForThingOperationContext::ListNamedShadowsForThingOperationContext( @@ -9105,8 +9359,7 @@ namespace Aws std::future ListNamedShadowsForThingOperation::GetResult() noexcept { - return std::async( - m_asyncLaunchMode, [this]() { return ListNamedShadowsForThingResult(GetOperationResult().get()); }); + return m_resultPromise.get_future(); } ListNamedShadowsForThingOperation::ListNamedShadowsForThingOperation( @@ -9121,12 +9374,22 @@ namespace Aws const ListNamedShadowsForThingRequest &request, OnMessageFlushCallback onMessageFlushCallback) noexcept { - return ClientOperation::Activate(static_cast(&request), onMessageFlushCallback); - } - - Aws::Crt::String ListNamedShadowsForThingOperation::GetModelName() const noexcept - { - return m_operationModelContext.GetOperationName(); + bool synchronousSuccess = false; + m_selfReference = shared_from_this(); + auto activateFuture = ClientOperation::Activate( + static_cast(&request), + std::move(onMessageFlushCallback), + [this](TaggedResult &&unmodeledResult) + { + m_resultPromise.set_value(ListNamedShadowsForThingResult(std::move(unmodeledResult))); + m_selfReference = nullptr; + }, + synchronousSuccess); + if (!synchronousSuccess) + { + m_selfReference = nullptr; + } + return activateFuture; } void SubscribeToComponentUpdatesStreamHandler::OnStreamEvent( @@ -9204,8 +9467,7 @@ namespace Aws std::future SubscribeToComponentUpdatesOperation::GetResult() noexcept { - return std::async( - m_asyncLaunchMode, [this]() { return SubscribeToComponentUpdatesResult(GetOperationResult().get()); }); + return m_resultPromise.get_future(); } SubscribeToComponentUpdatesOperation::SubscribeToComponentUpdatesOperation( @@ -9221,12 +9483,22 @@ namespace Aws const SubscribeToComponentUpdatesRequest &request, OnMessageFlushCallback onMessageFlushCallback) noexcept { - return ClientOperation::Activate(static_cast(&request), onMessageFlushCallback); - } - - Aws::Crt::String SubscribeToComponentUpdatesOperation::GetModelName() const noexcept - { - return m_operationModelContext.GetOperationName(); + bool synchronousSuccess = false; + m_selfReference = shared_from_this(); + auto activateFuture = ClientOperation::Activate( + static_cast(&request), + std::move(onMessageFlushCallback), + [this](TaggedResult &&unmodeledResult) + { + m_resultPromise.set_value(SubscribeToComponentUpdatesResult(std::move(unmodeledResult))); + m_selfReference = nullptr; + }, + synchronousSuccess); + if (!synchronousSuccess) + { + m_selfReference = nullptr; + } + return activateFuture; } ListLocalDeploymentsOperationContext::ListLocalDeploymentsOperationContext( @@ -9274,8 +9546,7 @@ namespace Aws std::future ListLocalDeploymentsOperation::GetResult() noexcept { - return std::async( - m_asyncLaunchMode, [this]() { return ListLocalDeploymentsResult(GetOperationResult().get()); }); + return m_resultPromise.get_future(); } ListLocalDeploymentsOperation::ListLocalDeploymentsOperation( @@ -9290,12 +9561,22 @@ namespace Aws const ListLocalDeploymentsRequest &request, OnMessageFlushCallback onMessageFlushCallback) noexcept { - return ClientOperation::Activate(static_cast(&request), onMessageFlushCallback); - } - - Aws::Crt::String ListLocalDeploymentsOperation::GetModelName() const noexcept - { - return m_operationModelContext.GetOperationName(); + bool synchronousSuccess = false; + m_selfReference = shared_from_this(); + auto activateFuture = ClientOperation::Activate( + static_cast(&request), + std::move(onMessageFlushCallback), + [this](TaggedResult &&unmodeledResult) + { + m_resultPromise.set_value(ListLocalDeploymentsResult(std::move(unmodeledResult))); + m_selfReference = nullptr; + }, + synchronousSuccess); + if (!synchronousSuccess) + { + m_selfReference = nullptr; + } + return activateFuture; } StopComponentOperationContext::StopComponentOperationContext( @@ -9343,7 +9624,7 @@ namespace Aws std::future StopComponentOperation::GetResult() noexcept { - return std::async(m_asyncLaunchMode, [this]() { return StopComponentResult(GetOperationResult().get()); }); + return m_resultPromise.get_future(); } StopComponentOperation::StopComponentOperation( @@ -9358,12 +9639,22 @@ namespace Aws const StopComponentRequest &request, OnMessageFlushCallback onMessageFlushCallback) noexcept { - return ClientOperation::Activate(static_cast(&request), onMessageFlushCallback); - } - - Aws::Crt::String StopComponentOperation::GetModelName() const noexcept - { - return m_operationModelContext.GetOperationName(); + bool synchronousSuccess = false; + m_selfReference = shared_from_this(); + auto activateFuture = ClientOperation::Activate( + static_cast(&request), + std::move(onMessageFlushCallback), + [this](TaggedResult &&unmodeledResult) + { + m_resultPromise.set_value(StopComponentResult(std::move(unmodeledResult))); + m_selfReference = nullptr; + }, + synchronousSuccess); + if (!synchronousSuccess) + { + m_selfReference = nullptr; + } + return activateFuture; } PauseComponentOperationContext::PauseComponentOperationContext( @@ -9411,7 +9702,7 @@ namespace Aws std::future PauseComponentOperation::GetResult() noexcept { - return std::async(m_asyncLaunchMode, [this]() { return PauseComponentResult(GetOperationResult().get()); }); + return m_resultPromise.get_future(); } PauseComponentOperation::PauseComponentOperation( @@ -9426,12 +9717,22 @@ namespace Aws const PauseComponentRequest &request, OnMessageFlushCallback onMessageFlushCallback) noexcept { - return ClientOperation::Activate(static_cast(&request), onMessageFlushCallback); - } - - Aws::Crt::String PauseComponentOperation::GetModelName() const noexcept - { - return m_operationModelContext.GetOperationName(); + bool synchronousSuccess = false; + m_selfReference = shared_from_this(); + auto activateFuture = ClientOperation::Activate( + static_cast(&request), + std::move(onMessageFlushCallback), + [this](TaggedResult &&unmodeledResult) + { + m_resultPromise.set_value(PauseComponentResult(std::move(unmodeledResult))); + m_selfReference = nullptr; + }, + synchronousSuccess); + if (!synchronousSuccess) + { + m_selfReference = nullptr; + } + return activateFuture; } CreateLocalDeploymentOperationContext::CreateLocalDeploymentOperationContext( @@ -9479,8 +9780,7 @@ namespace Aws std::future CreateLocalDeploymentOperation::GetResult() noexcept { - return std::async( - m_asyncLaunchMode, [this]() { return CreateLocalDeploymentResult(GetOperationResult().get()); }); + return m_resultPromise.get_future(); } CreateLocalDeploymentOperation::CreateLocalDeploymentOperation( @@ -9495,12 +9795,22 @@ namespace Aws const CreateLocalDeploymentRequest &request, OnMessageFlushCallback onMessageFlushCallback) noexcept { - return ClientOperation::Activate(static_cast(&request), onMessageFlushCallback); - } - - Aws::Crt::String CreateLocalDeploymentOperation::GetModelName() const noexcept - { - return m_operationModelContext.GetOperationName(); + bool synchronousSuccess = false; + m_selfReference = shared_from_this(); + auto activateFuture = ClientOperation::Activate( + static_cast(&request), + std::move(onMessageFlushCallback), + [this](TaggedResult &&unmodeledResult) + { + m_resultPromise.set_value(CreateLocalDeploymentResult(std::move(unmodeledResult))); + m_selfReference = nullptr; + }, + synchronousSuccess); + if (!synchronousSuccess) + { + m_selfReference = nullptr; + } + return activateFuture; } GreengrassCoreIpcServiceModel::GreengrassCoreIpcServiceModel() noexcept From 422f31587e8208654bb1a0af919218acfcadbbc9 Mon Sep 17 00:00:00 2001 From: Bret Ambrose Date: Fri, 30 May 2025 14:48:04 -0700 Subject: [PATCH 38/43] Remove ifdefd out stuff --- eventstream_rpc/source/EventStreamClient.cpp | 655 ------------------- 1 file changed, 655 deletions(-) diff --git a/eventstream_rpc/source/EventStreamClient.cpp b/eventstream_rpc/source/EventStreamClient.cpp index 488ec7f2d..e11da0b88 100644 --- a/eventstream_rpc/source/EventStreamClient.cpp +++ b/eventstream_rpc/source/EventStreamClient.cpp @@ -1263,661 +1263,6 @@ namespace Aws void StreamResponseHandler::OnStreamClosed() {} -#ifdef NEVER - struct RawContinuationCallbackDataWrapper - { - RawContinuationCallbackDataWrapper( - Aws::Crt::Allocator *allocator, - const std::shared_ptr &callbackData) - : m_allocator(allocator), m_callbackData(callbackData) - { - } - - Aws::Crt::Allocator *m_allocator; - std::shared_ptr m_callbackData; - }; - - static void s_onContinuationTerminated(void *user_data) - { - if (user_data == nullptr) - { - return; - } - - struct RawContinuationCallbackDataWrapper *wrapper = - static_cast(user_data); - Aws::Crt::Delete(wrapper, wrapper->m_allocator); - } - - ClientContinuation::ClientContinuation( - struct aws_event_stream_rpc_client_connection *connection, - ClientContinuationHandler &continuationHandler, - Crt::Allocator *allocator) noexcept - : m_allocator(allocator), m_continuationHandler(continuationHandler), m_continuationToken(nullptr) - { - struct aws_event_stream_rpc_client_stream_continuation_options options; - options.on_continuation = ClientContinuation::s_onContinuationMessage; - options.on_continuation_closed = ClientContinuation::s_onContinuationClosed; - options.on_continuation_terminated = s_onContinuationTerminated; - - m_callbackData = Crt::MakeShared(m_allocator, this, m_allocator); - - m_continuationHandler.m_callbackData = m_callbackData; - options.user_data = reinterpret_cast( - Aws::Crt::New(allocator, allocator, m_callbackData)); - - if (connection) - { - m_continuationToken = aws_event_stream_rpc_client_connection_new_stream(connection, &options); - if (m_continuationToken == nullptr) - { - m_continuationHandler.m_callbackData = nullptr; - m_callbackData = nullptr; - } - } - } - - ClientContinuation::~ClientContinuation() noexcept - { - Release(); - } - - void ClientContinuation::Release() - { - if (m_callbackData != nullptr) - { - { - const std::lock_guard lock(m_callbackData->callbackMutex); - m_callbackData->continuationDestroyed = true; - } - } - - if (m_continuationToken) - { - aws_event_stream_rpc_client_continuation_release(m_continuationToken); - m_continuationToken = nullptr; - } - } - - void ClientContinuation::s_onContinuationMessage( - struct aws_event_stream_rpc_client_continuation_token *continuationToken, - const struct aws_event_stream_rpc_message_args *messageArgs, - void *userData) noexcept - { - (void)continuationToken; - /* The `userData` pointer is used to pass a `ContinuationCallbackData` object. */ - auto *callbackData = static_cast(userData)->m_callbackData.get(); - auto *thisContinuation = callbackData->clientContinuation; - - Crt::List continuationMessageHeaders; - for (size_t i = 0; i < messageArgs->headers_count; ++i) - { - continuationMessageHeaders.emplace_back( - EventStreamHeader(messageArgs->headers[i], thisContinuation->m_allocator)); - } - - Crt::Optional payload; - - if (messageArgs->payload) - { - payload = Crt::Optional(*messageArgs->payload); - } - else - { - payload = Crt::Optional(); - } - - const std::lock_guard lock(callbackData->callbackMutex); - if (callbackData->continuationDestroyed) - return; - thisContinuation->m_continuationHandler.OnContinuationMessage( - continuationMessageHeaders, payload, messageArgs->message_type, messageArgs->message_flags); - } - - void ClientContinuation::s_onContinuationClosed( - struct aws_event_stream_rpc_client_continuation_token *continuationToken, - void *userData) noexcept - { - (void)continuationToken; - - /* The `userData` pointer is used to pass a `ContinuationCallbackData` object. */ - auto *callbackData = static_cast(userData)->m_callbackData.get(); - - const std::lock_guard lock(callbackData->callbackMutex); - if (callbackData->continuationDestroyed) - return; - - auto *thisContinuation = callbackData->clientContinuation; - thisContinuation->m_continuationHandler.OnContinuationClosed(); - } - - std::future ClientContinuation::Activate( - const Crt::String &operationName, - const Crt::List &headers, - const Crt::Optional &payload, - MessageType messageType, - uint32_t messageFlags, - OnMessageFlushCallback onMessageFlushCallback) noexcept - { - struct aws_array_list headersArray; - OnMessageFlushCallbackContainer *callbackContainer = nullptr; - std::promise onFlushPromise; - - if (m_continuationToken == nullptr) - { - onFlushPromise.set_value({EVENT_STREAM_RPC_CONNECTION_CLOSED, 0}); - return onFlushPromise.get_future(); - } - - if (IsClosed()) - { - onFlushPromise.set_value({EVENT_STREAM_RPC_CONTINUATION_CLOSED, 0}); - return onFlushPromise.get_future(); - } - - s_fillNativeHeadersArray(headers, &headersArray, m_allocator); - - /* - * Regardless of how the promise gets moved around (or not), this future should stay valid as a return - * value. - * - * We pull it out early because the call to aws_event_stream_rpc_client_continuation_activate() may complete - * and delete the promise before we pull out the future afterwords. - */ - std::future retValue = onFlushPromise.get_future(); - - struct aws_event_stream_rpc_message_args msg_args; - msg_args.headers = (struct aws_event_stream_header_value_pair *)headersArray.data; - msg_args.headers_count = headers.size(); - msg_args.payload = payload.has_value() ? (aws_byte_buf *)(&(payload.value())) : nullptr; - msg_args.message_type = messageType; - msg_args.message_flags = messageFlags; - - /* This heap allocation is necessary so that the flush callback can still be invoked when this function - * returns. */ - callbackContainer = Crt::New(m_allocator, m_allocator); - callbackContainer->onMessageFlushCallback = onMessageFlushCallback; - callbackContainer->onFlushPromise = std::move(onFlushPromise); - - int errorCode = aws_event_stream_rpc_client_continuation_activate( - m_continuationToken, - Crt::ByteCursorFromCString(operationName.c_str()), - &msg_args, - s_protocolMessageCallback, - reinterpret_cast(callbackContainer)); - - /* Cleanup. */ - if (aws_array_list_is_valid(&headersArray)) - { - aws_array_list_clean_up(&headersArray); - } - - if (errorCode) - { - onFlushPromise = std::move(callbackContainer->onFlushPromise); - onFlushPromise.set_value({EVENT_STREAM_RPC_CRT_ERROR, errorCode}); - Crt::Delete(callbackContainer, m_allocator); - } - - return retValue; - } - - std::future ClientContinuation::SendMessage( - const Crt::List &headers, - const Crt::Optional &payload, - MessageType messageType, - uint32_t messageFlags, - OnMessageFlushCallback onMessageFlushCallback) noexcept - { - struct aws_array_list headersArray; - OnMessageFlushCallbackContainer *callbackContainer = nullptr; - std::promise onFlushPromise; - - if (IsClosed()) - { - onFlushPromise.set_value({EVENT_STREAM_RPC_CONTINUATION_CLOSED, 0}); - return onFlushPromise.get_future(); - } - - s_fillNativeHeadersArray(headers, &headersArray, m_allocator); - - struct aws_event_stream_rpc_message_args msg_args; - msg_args.headers = (struct aws_event_stream_header_value_pair *)headersArray.data; - msg_args.headers_count = headers.size(); - msg_args.payload = payload.has_value() ? (aws_byte_buf *)(&(payload.value())) : nullptr; - msg_args.message_type = messageType; - msg_args.message_flags = messageFlags; - - /* This heap allocation is necessary so that the flush callback can still be invoked when this function - * returns. */ - callbackContainer = Crt::New(m_allocator, m_allocator); - callbackContainer->onMessageFlushCallback = onMessageFlushCallback; - callbackContainer->onFlushPromise = std::move(onFlushPromise); - - int errorCode = AWS_OP_SUCCESS; - if (m_continuationToken) - { - if (aws_event_stream_rpc_client_continuation_send_message( - m_continuationToken, - &msg_args, - s_protocolMessageCallback, - reinterpret_cast(callbackContainer))) - { - errorCode = aws_last_error(); - } - } - - /* Cleanup. */ - if (aws_array_list_is_valid(&headersArray)) - { - aws_array_list_clean_up(&headersArray); - } - - if (errorCode) - { - onFlushPromise = std::move(callbackContainer->onFlushPromise); - AWS_LOGF_ERROR( - AWS_LS_EVENT_STREAM_RPC_CLIENT, - "A CRT error occurred while queueing a message to be sent on a stream: %s", - Crt::ErrorDebugString(errorCode)); - onFlushPromise.set_value({EVENT_STREAM_RPC_CRT_ERROR, errorCode}); - Crt::Delete(callbackContainer, m_allocator); - } - else - { - return callbackContainer->onFlushPromise.get_future(); - } - - return onFlushPromise.get_future(); - } - - bool ClientContinuation::IsClosed() noexcept - { - if (!m_continuationToken) - { - return true; - } - else - { - return aws_event_stream_rpc_client_continuation_is_closed(m_continuationToken); - } - } - - std::future ClientOperation::GetOperationResult() noexcept - { - { - const std::lock_guard lock(m_continuationMutex); - - if (m_clientContinuation.IsClosed() && !m_resultReceived) - { - AWS_LOGF_ERROR(AWS_LS_EVENT_STREAM_RPC_CLIENT, "The underlying stream is already closed."); - m_initialResponsePromise.set_value(TaggedResult({EVENT_STREAM_RPC_CONNECTION_CLOSED, 0})); - m_resultReceived = true; - } - } - - return m_initialResponsePromise.get_future(); - } - - const EventStreamHeader *ClientOperation::GetHeaderByName( - const Crt::List &headers, - const Crt::String &name) noexcept - { - for (auto it = headers.begin(); it != headers.end(); ++it) - { - if (name == it->GetHeaderName()) - { - return &(*it); - } - } - return nullptr; - } - - EventStreamRpcStatusCode ClientOperation::HandleData(const Crt::Optional &payload) - { - Crt::StringView payloadStringView; - if (payload.has_value()) - { - payloadStringView = Crt::ByteCursorToStringView(Crt::ByteCursorFromByteBuf(payload.value())); - } - - /* The value of this hashmap contains the function that allocates the response object from the - * payload. */ - /* Responses after the first message don't necessarily have the same shape as the first. */ - Crt::ScopedResource response; - if (m_messageCount == 1) - { - response = m_operationModelContext.AllocateInitialResponseFromPayload(payloadStringView, m_allocator); - } - else - { - response = m_operationModelContext.AllocateStreamingResponseFromPayload(payloadStringView, m_allocator); - } - - if (response.get() == nullptr) - { - AWS_LOGF_ERROR(AWS_LS_EVENT_STREAM_RPC_CLIENT, "Failed to allocate a response from the payload."); - return EVENT_STREAM_RPC_ALLOCATION_ERROR; - } - - if (m_messageCount == 1) - { - const std::lock_guard lock(m_continuationMutex); - m_resultReceived = true; - m_initialResponsePromise.set_value(TaggedResult(std::move(response))); - } - else - { - if (m_streamHandler) - m_streamHandler->OnStreamEvent(std::move(response)); - } - - return EVENT_STREAM_RPC_SUCCESS; - } - - EventStreamRpcStatusCode ClientOperation::HandleError( - const Crt::String &modelName, - const Crt::Optional &payload, - uint32_t messageFlags) - { - bool streamAlreadyTerminated = (messageFlags & AWS_EVENT_STREAM_RPC_MESSAGE_FLAG_TERMINATE_STREAM) != 0; - - Crt::StringView payloadStringView; - if (payload.has_value()) - { - payloadStringView = Crt::ByteCursorToStringView(Crt::ByteCursorFromByteBuf(payload.value())); - } - - /* The value of this hashmap contains the function that allocates the error from the - * payload. */ - Crt::ScopedResource error = - m_operationModelContext.AllocateOperationErrorFromPayload(modelName, payloadStringView, m_allocator); - if (error.get() == nullptr) - return EVENT_STREAM_RPC_UNMAPPED_DATA; - if (error->GetMessage().has_value()) - { - AWS_LOGF_ERROR( - AWS_LS_EVENT_STREAM_RPC_CLIENT, - "An error was received from the server: %s", - error->GetMessage().value().c_str()); - } - TaggedResult taggedResult(std::move(error)); - if (m_messageCount == 1) - { - { - const std::lock_guard lock(m_continuationMutex); - m_resultReceived = true; - m_initialResponsePromise.set_value(std::move(taggedResult)); - } - /* Close the stream unless the server already closed it for us. This condition is checked - * so that TERMINATE_STREAM messages aren't resent by the client. */ - if (!streamAlreadyTerminated && !m_clientContinuation.IsClosed()) - { - Close().wait(); - } - } - else - { - bool shouldCloseNow = true; - if (m_streamHandler) - shouldCloseNow = m_streamHandler->OnStreamError(std::move(error), {EVENT_STREAM_RPC_SUCCESS, 0}); - if (!streamAlreadyTerminated && shouldCloseNow && !m_clientContinuation.IsClosed()) - { - Close().wait(); - } - } - - return EVENT_STREAM_RPC_SUCCESS; - } - - void ClientOperation::OnContinuationMessage( - const Crt::List &headers, - const Crt::Optional &payload, - MessageType messageType, - uint32_t messageFlags) - { - EventStreamRpcStatusCode errorCode = EVENT_STREAM_RPC_SUCCESS; - const EventStreamHeader *modelHeader = nullptr; - const EventStreamHeader *contentHeader = nullptr; - Crt::String modelName; - - if (messageFlags & AWS_EVENT_STREAM_RPC_MESSAGE_FLAG_TERMINATE_STREAM) - { - const std::lock_guard lock(m_continuationMutex); - m_expectingClose = true; - } - - m_messageCount += 1; - - modelHeader = GetHeaderByName(headers, Crt::String(SERVICE_MODEL_TYPE_HEADER)); - if (modelHeader == nullptr) - { - /* Missing required service model type header. */ - AWS_LOGF_ERROR( - AWS_LS_EVENT_STREAM_RPC_CLIENT, - "A required header (%s) could not be found in the message.", - SERVICE_MODEL_TYPE_HEADER); - errorCode = EVENT_STREAM_RPC_UNMAPPED_DATA; - } - - /* Verify that the model name matches. */ - if (!errorCode) - { - modelHeader->GetValueAsString(modelName); - if (messageType == AWS_EVENT_STREAM_RPC_MESSAGE_TYPE_APPLICATION_MESSAGE) - { - if (m_messageCount == 1 && m_operationModelContext.GetInitialResponseModelName() != modelName) - { - AWS_LOGF_ERROR( - AWS_LS_EVENT_STREAM_RPC_CLIENT, - "The model name of the initial response did not match its expected model name."); - errorCode = EVENT_STREAM_RPC_UNMAPPED_DATA; - } - else if ( - m_messageCount > 1 && m_operationModelContext.GetStreamingResponseModelName().has_value() && - m_operationModelContext.GetStreamingResponseModelName().value() != modelName) - { - AWS_LOGF_ERROR( - AWS_LS_EVENT_STREAM_RPC_CLIENT, - "The model name of a subsequent response did not match its expected model name."); - errorCode = EVENT_STREAM_RPC_UNMAPPED_DATA; - } - } - } - - if (!errorCode) - { - Crt::String contentType; - contentHeader = GetHeaderByName(headers, Crt::String(CONTENT_TYPE_HEADER)); - if (contentHeader == nullptr) - { - /* Missing required content type header. */ - AWS_LOGF_ERROR( - AWS_LS_EVENT_STREAM_RPC_CLIENT, - "A required header (%s) could not be found in the message.", - CONTENT_TYPE_HEADER); - errorCode = EVENT_STREAM_RPC_UNSUPPORTED_CONTENT_TYPE; - } - else if (contentHeader->GetValueAsString(contentType) && contentType != CONTENT_TYPE_APPLICATION_JSON) - { - /* Missing required content type header. */ - AWS_LOGF_ERROR( - AWS_LS_EVENT_STREAM_RPC_CLIENT, - "The content type (%s) header was specified with an unsupported value (%s).", - CONTENT_TYPE_HEADER, - contentType.c_str()); - errorCode = EVENT_STREAM_RPC_UNSUPPORTED_CONTENT_TYPE; - } - } - - if (!errorCode) - { - if (messageType == AWS_EVENT_STREAM_RPC_MESSAGE_TYPE_APPLICATION_MESSAGE) - { - errorCode = HandleData(payload); - } - else - { - errorCode = HandleError(modelName, payload, messageFlags); - } - } - - if (errorCode) - { - if (m_messageCount == 1) - { - const std::lock_guard lock(m_continuationMutex); - m_resultReceived = true; - RpcError promiseValue = {(EventStreamRpcStatusCode)errorCode, 0}; - m_initialResponsePromise.set_value(TaggedResult(promiseValue)); - } - else - { - bool shouldClose = true; - if (m_streamHandler) - shouldClose = m_streamHandler->OnStreamError(nullptr, {errorCode, 0}); - if (!m_clientContinuation.IsClosed() && shouldClose) - { - Close().wait(); - } - } - } - } - - std::future ClientOperation::Activate( - const AbstractShapeBase *shape, - OnMessageFlushCallback onMessageFlushCallback) noexcept - { - /* Promises must be reset in case the client would like to send a subsequent request with the same - * `ClientOperation`. */ - m_initialResponsePromise = {}; - { - const std::lock_guard lock(m_continuationMutex); - m_resultReceived = false; - } - - Crt::List headers; - headers.emplace_back(EventStreamHeader( - Crt::String(CONTENT_TYPE_HEADER), Crt::String(CONTENT_TYPE_APPLICATION_JSON), m_allocator)); - headers.emplace_back( - EventStreamHeader(Crt::String(SERVICE_MODEL_TYPE_HEADER), GetModelName(), m_allocator)); - Crt::JsonObject payloadObject; - shape->SerializeToJsonObject(payloadObject); - Crt::String payloadString = payloadObject.View().WriteCompact(); - return m_clientContinuation.Activate( - GetModelName(), - headers, - Crt::ByteBufFromCString(payloadString.c_str()), - AWS_EVENT_STREAM_RPC_MESSAGE_TYPE_APPLICATION_MESSAGE, - 0, - onMessageFlushCallback); - } - - void ClientOperation::OnContinuationClosed() - { - const std::lock_guard lock(m_continuationMutex); - if (!m_resultReceived) - { - m_initialResponsePromise.set_value(TaggedResult({EVENT_STREAM_RPC_CONTINUATION_CLOSED, 0})); - m_resultReceived = true; - } - - if (m_expectingClose) - { - m_expectingClose = false; - if (!m_streamClosedCalled && m_streamHandler) - { - m_streamHandler->OnStreamClosed(); - m_streamClosedCalled = true; - } - m_closeReady.notify_one(); - } - } - - void ClientOperation::WithLaunchMode(std::launch mode) noexcept - { - m_asyncLaunchMode = mode; - } - - class AWS_EVENTSTREAMRPC_API ClientOperation : public ClientContinuationHandler - { - public: - ClientOperation( - ClientConnection &connection, - std::shared_ptr streamHandler, - const OperationModelContext &operationModelContext, - Crt::Allocator *allocator) noexcept; - ~ClientOperation() noexcept override; - - std::future Close(OnMessageFlushCallback onMessageFlushCallback = nullptr) noexcept; - - void WithLaunchMode(std::launch mode) noexcept; - - std::future Activate( - const AbstractShapeBase *shape, - OnMessageFlushCallback onMessageFlushCallback, - std::function &&onResultCallback) noexcept; - }; - - std::future ClientOperation::Close(OnMessageFlushCallback onMessageFlushCallback) noexcept - { - const std::lock_guard lock(m_continuationMutex); - if (m_expectingClose || m_clientContinuation.IsClosed()) - { - std::promise errorPromise; - errorPromise.set_value({EVENT_STREAM_RPC_CONTINUATION_CLOSED, 0}); - return errorPromise.get_future(); - } - else - { - std::promise onTerminatePromise; - - int errorCode = AWS_OP_ERR; - struct aws_event_stream_rpc_message_args msg_args; - msg_args.headers = nullptr; - msg_args.headers_count = 0; - msg_args.payload = nullptr; - msg_args.message_type = AWS_EVENT_STREAM_RPC_MESSAGE_TYPE_APPLICATION_MESSAGE; - msg_args.message_flags = AWS_EVENT_STREAM_RPC_MESSAGE_FLAG_TERMINATE_STREAM; - - /* This heap allocation is necessary so that the flush callback can still be invoked when this function - * returns. */ - OnMessageFlushCallbackContainer *callbackContainer = - Crt::New(m_allocator, m_allocator); - callbackContainer->onMessageFlushCallback = onMessageFlushCallback; - callbackContainer->onFlushPromise = std::move(onTerminatePromise); - - if (m_clientContinuation.m_continuationToken) - { - errorCode = aws_event_stream_rpc_client_continuation_send_message( - m_clientContinuation.m_continuationToken, - &msg_args, - s_protocolMessageCallback, - reinterpret_cast(callbackContainer)); - } - - if (errorCode) - { - onTerminatePromise = std::move(callbackContainer->onFlushPromise); - std::promise errorPromise; - AWS_LOGF_ERROR( - AWS_LS_EVENT_STREAM_RPC_CLIENT, - "A CRT error occurred while closing the stream: %s", - Crt::ErrorDebugString(errorCode)); - onTerminatePromise.set_value({EVENT_STREAM_RPC_CRT_ERROR, errorCode}); - Crt::Delete(callbackContainer, m_allocator); - } - else - { - m_expectingClose = true; - return callbackContainer->onFlushPromise.get_future(); - } - - return onTerminatePromise.get_future(); - } - } -#endif // NEVER - static const EventStreamHeader *s_GetHeaderByName( const Crt::List &headers, const Crt::String &name) noexcept From cdb0a7ab7ae02d5f84f6e8bbbbcef29c5a45a307 Mon Sep 17 00:00:00 2001 From: Bret Ambrose Date: Mon, 2 Jun 2025 13:20:51 -0700 Subject: [PATCH 39/43] Outbound streaming --- .../aws/eventstreamrpc/EventStreamClient.h | 13 ++ eventstream_rpc/source/EventStreamClient.cpp | 122 ++++++++++++++++-- eventstream_rpc/tests/EchoTestRpcModel.cpp | 16 +++ .../tests/include/awstest/EchoTestRpcModel.h | 24 ++++ 4 files changed, 161 insertions(+), 14 deletions(-) diff --git a/eventstream_rpc/include/aws/eventstreamrpc/EventStreamClient.h b/eventstream_rpc/include/aws/eventstreamrpc/EventStreamClient.h index 1cb58c76f..59517d3ab 100644 --- a/eventstream_rpc/include/aws/eventstreamrpc/EventStreamClient.h +++ b/eventstream_rpc/include/aws/eventstreamrpc/EventStreamClient.h @@ -156,6 +156,7 @@ namespace Aws EVENT_STREAM_RPC_CRT_ERROR, EVENT_STREAM_RPC_CONTINUATION_ALREADY_OPENED, EVENT_STREAM_RPC_CONTINUATION_CLOSE_IN_PROGRESS, + EVENT_STREAM_RPC_CONTINUATION_NOT_YET_OPENED, }; /** @@ -724,6 +725,18 @@ namespace Aws std::function &&onResultCallback, bool &synchronousSuccess) noexcept; + /** + * Sends a message on the stream + * + * @param shape Modeled representation of the message to send + * @param onMessageFlushCallback Optional callback to invoke when the message is written or fails to send + * + * @return Future which will be resolved once the message is sent. + */ + std::future SendStreamMessage( + const AbstractShapeBase *shape, + OnMessageFlushCallback &&onMessageFlushCallback) noexcept; + /** * Returns the canonical model name associated with this operation across any client language. * Namespace included. diff --git a/eventstream_rpc/source/EventStreamClient.cpp b/eventstream_rpc/source/EventStreamClient.cpp index e11da0b88..c3bb31b56 100644 --- a/eventstream_rpc/source/EventStreamClient.cpp +++ b/eventstream_rpc/source/EventStreamClient.cpp @@ -49,7 +49,6 @@ namespace Aws { aws_ref_count_init(&m_refCount, this, s_deleteMessageCallbackContainer); aws_ref_count_acquire(&m_refCount); // always start at 2, one for C++, one for C - AWS_ZERO_STRUCT(m_sharedState.m_node); m_sharedState.m_state = CallbackState::Incomplete; m_sharedState.m_onMessageFlushCallback = std::move(flushCallback); } @@ -84,10 +83,6 @@ namespace Aws callback->m_sharedState.m_state = CallbackState::Finished; onMessageFlushCallback = std::move(callback->m_sharedState.m_onMessageFlushCallback); onFlushPromise = std::move(callback->m_sharedState.m_onFlushPromise); - if (aws_linked_list_node_is_in_list(&callback->m_sharedState.m_node)) - { - aws_linked_list_remove(&callback->m_sharedState.m_node); - } } } @@ -120,13 +115,6 @@ namespace Aws Aws::Crt::Allocator *GetAllocator() const { return m_allocator; } - void AddToList(struct aws_linked_list *list) - { - std::lock_guard lock(m_sharedStateLock); - - aws_linked_list_push_back(list, &m_sharedState.m_node); - } - private: enum class CallbackState { @@ -141,7 +129,6 @@ namespace Aws struct { CallbackState m_state; - struct aws_linked_list_node m_node; OnMessageFlushCallback m_onMessageFlushCallback; std::promise m_onFlushPromise; } m_sharedState; @@ -349,6 +336,8 @@ namespace Aws return "EVENT_STREAM_RPC_CONTINUATION_ALREADY_OPENED"; case EVENT_STREAM_RPC_CONTINUATION_CLOSE_IN_PROGRESS: return "EVENT_STREAM_RPC_CONTINUATION_CLOSE_IN_PROGRESS"; + case EVENT_STREAM_RPC_CONTINUATION_NOT_YET_OPENED: + return "EVENT_STREAM_RPC_CONTINUATION_NOT_YET_OPENED"; } return "Unknown status code"; } @@ -1363,6 +1352,13 @@ namespace Aws OnMessageFlushCallback &&onMessageFlushCallback, bool &synchronousSuccess) noexcept; + std::future SendStreamMessage( + const Crt::List &headers, + const Crt::Optional &payload, + MessageType messageType, + uint32_t messageFlags, + OnMessageFlushCallback &&onMessageFlushCallback); + std::future Close(OnMessageFlushCallback &&onMessageFlushCallback = nullptr) noexcept; Crt::String GetModelName() const noexcept; @@ -1481,6 +1477,27 @@ namespace Aws synchronousSuccess); } + std::future ClientOperation::SendStreamMessage( + const AbstractShapeBase *shape, + OnMessageFlushCallback &&onMessageFlushCallback) noexcept + { + Crt::List headers; + headers.emplace_back(EventStreamHeader( + Crt::String(CONTENT_TYPE_HEADER), Crt::String(CONTENT_TYPE_APPLICATION_JSON), m_allocator)); + headers.emplace_back( + EventStreamHeader(Crt::String(SERVICE_MODEL_TYPE_HEADER), GetModelName(), m_allocator)); + Crt::JsonObject payloadObject; + shape->SerializeToJsonObject(payloadObject); + Crt::String payloadString = payloadObject.View().WriteCompact(); + + return m_impl->SendStreamMessage( + headers, + Crt::ByteBufFromCString(payloadString.c_str()), + AWS_EVENT_STREAM_RPC_MESSAGE_TYPE_APPLICATION_MESSAGE, + 0, + std::move(onMessageFlushCallback)); + } + ClientContinuationImpl::ClientContinuationImpl( Aws::Crt::Allocator *allocator, struct aws_event_stream_rpc_client_connection *connection) noexcept @@ -1640,7 +1657,7 @@ namespace Aws std::lock_guard lock(m_sharedStateLock); if (m_sharedState.m_continuation == nullptr) { - activationPromise.set_value({EVENT_STREAM_RPC_CONNECTION_CLOSED, 0}); + activationPromise.set_value({EVENT_STREAM_RPC_CONTINUATION_CLOSED, 0}); return activationFuture; } @@ -1713,6 +1730,83 @@ namespace Aws return activationFuture; } + std::future ClientContinuationImpl::SendStreamMessage( + const Crt::List &headers, + const Crt::Optional &payload, + MessageType messageType, + uint32_t messageFlags, + OnMessageFlushCallback &&onMessageFlushCallback) + { + int result = AWS_OP_SUCCESS; + struct aws_array_list headersArray; // guaranteed to be zeroed or valid if we reach the end of the function + OnMessageFlushCallbackContainer *sendFailureContainer = nullptr; + std::promise sendPromise; + std::future sendFuture = sendPromise.get_future(); + { + std::lock_guard lock(m_sharedStateLock); + if (m_sharedState.m_continuation == nullptr) + { + sendPromise.set_value({EVENT_STREAM_RPC_CONTINUATION_CLOSED, 0}); + return sendFuture; + } + + switch (m_sharedState.m_currentState) + { + case ContinuationStateType::PendingActivate: + case ContinuationStateType::None: + sendPromise.set_value({EVENT_STREAM_RPC_CONTINUATION_NOT_YET_OPENED, 0}); + return sendFuture; + + case ContinuationStateType::PendingClose: + case ContinuationStateType::Closed: + sendPromise.set_value({EVENT_STREAM_RPC_CONTINUATION_CLOSED, 0}); + return sendFuture; + + default: + break; + } + + // cleanup requirements mean we can't early out from here on + s_fillNativeHeadersArray(headers, &headersArray, m_allocator); + + struct aws_event_stream_rpc_message_args msg_args; + msg_args.headers = static_cast(headersArray.data); + msg_args.headers_count = headers.size(); + msg_args.payload = payload.has_value() ? (aws_byte_buf *)(&(payload.value())) : nullptr; + msg_args.message_type = messageType; + msg_args.message_flags = messageFlags; + + auto sendContainer = Crt::New( + m_allocator, m_allocator, std::move(onMessageFlushCallback), std::move(sendPromise)); + OnMessageFlushCallbackContainer::Release(sendContainer); // release our ref + + result = aws_event_stream_rpc_client_continuation_send_message( + m_sharedState.m_continuation, + &msg_args, + s_protocolMessageCallback, + reinterpret_cast(sendContainer)); + + if (result != AWS_OP_SUCCESS) + { + sendFailureContainer = sendContainer; + } + } + + if (sendFailureContainer) + { + OnMessageFlushCallbackContainer::Complete( + sendFailureContainer, {EVENT_STREAM_RPC_CRT_ERROR, aws_last_error()}); + OnMessageFlushCallbackContainer::Release(sendFailureContainer); + } + + if (aws_array_list_is_valid(&headersArray)) + { + aws_array_list_clean_up(&headersArray); + } + + return sendFuture; + } + // Intentionally returns an error code and not AWS_OP_ERR/AWS_OP_SUCCESS // shared state lock must be held before calling int ClientContinuationImpl::SendCloseMessage( diff --git a/eventstream_rpc/tests/EchoTestRpcModel.cpp b/eventstream_rpc/tests/EchoTestRpcModel.cpp index 059554ed2..63d3472c6 100644 --- a/eventstream_rpc/tests/EchoTestRpcModel.cpp +++ b/eventstream_rpc/tests/EchoTestRpcModel.cpp @@ -1230,6 +1230,14 @@ namespace Awstest return activateFuture; } + std::future CauseStreamServiceToErrorOperation::SendStreamMessage( + const EchoStreamingMessage &message, + OnMessageFlushCallback onMessageFlushCallback) + { + return ClientOperation::SendStreamMessage( + static_cast(&message), std::move(onMessageFlushCallback)); + } + void EchoStreamMessagesStreamHandler::OnStreamEvent(Aws::Crt::ScopedResource response) { OnStreamEvent(static_cast(response.get())); @@ -1326,6 +1334,14 @@ namespace Awstest return activateFuture; } + std::future EchoStreamMessagesOperation::SendStreamMessage( + const EchoStreamingMessage &message, + OnMessageFlushCallback onMessageFlushCallback) + { + return ClientOperation::SendStreamMessage( + static_cast(&message), std::move(onMessageFlushCallback)); + } + EchoMessageOperationContext::EchoMessageOperationContext(const EchoTestRpcServiceModel &serviceModel) noexcept : OperationModelContext(serviceModel) { diff --git a/eventstream_rpc/tests/include/awstest/EchoTestRpcModel.h b/eventstream_rpc/tests/include/awstest/EchoTestRpcModel.h index a9cf8de9f..19cdc201c 100644 --- a/eventstream_rpc/tests/include/awstest/EchoTestRpcModel.h +++ b/eventstream_rpc/tests/include/awstest/EchoTestRpcModel.h @@ -935,6 +935,18 @@ namespace Awstest const EchoStreamingRequest &request, OnMessageFlushCallback onMessageFlushCallback = nullptr) noexcept; + /** + * Send a EchoStreamingMessage stream event. + * + * Activate() must have completed before calling SendMessage(). + * + * Returns a Future which completes with a value indicating message + * flush success or what went wrong. + **/ + std::future SendStreamMessage( + const EchoStreamingMessage &event, + OnMessageFlushCallback onMessageFlushCallback = nullptr); + /** * Retrieve the result from activating the stream. */ @@ -1048,6 +1060,18 @@ namespace Awstest const EchoStreamingRequest &request, OnMessageFlushCallback onMessageFlushCallback = nullptr) noexcept; + /** + * Send a EchoStreamingMessage stream event. + * + * Activate() must have completed before calling SendMessage(). + * + * Returns a Future which completes with a value indicating message + * flush success or what went wrong. + **/ + std::future SendStreamMessage( + const EchoStreamingMessage &event, + OnMessageFlushCallback onMessageFlushCallback = nullptr); + /** * Retrieve the result from activating the stream. */ From a217deda04289cd6a15359d3100344b11022d6aa Mon Sep 17 00:00:00 2001 From: Bret Ambrose Date: Fri, 6 Jun 2025 13:37:10 -0700 Subject: [PATCH 40/43] Checkpoint before branching --- eventstream_rpc/source/EventStreamClient.cpp | 400 +++++++++--------- eventstream_rpc/tests/CMakeLists.txt | 33 +- eventstream_rpc/tests/EchoTestRpcModel.cpp | 48 ++- .../tests/EventStreamClientTest.cpp | 228 +++++++++- .../tests/include/awstest/EchoTestRpcModel.h | 18 + .../aws/greengrass/GreengrassCoreIpcModel.h | 102 +++++ .../source/GreengrassCoreIpcModel.cpp | 272 +++++++----- 7 files changed, 776 insertions(+), 325 deletions(-) diff --git a/eventstream_rpc/source/EventStreamClient.cpp b/eventstream_rpc/source/EventStreamClient.cpp index c3bb31b56..196370a34 100644 --- a/eventstream_rpc/source/EventStreamClient.cpp +++ b/eventstream_rpc/source/EventStreamClient.cpp @@ -1335,6 +1335,7 @@ namespace Aws public: ClientContinuationImpl( Aws::Crt::Allocator *allocator, + struct aws_client_bootstrap *bootstrap, struct aws_event_stream_rpc_client_connection *connection) noexcept; virtual ~ClientContinuationImpl(); @@ -1384,10 +1385,6 @@ namespace Aws void OnMessage(const struct aws_event_stream_rpc_message_args *messageArgs) noexcept; - int SendCloseMessage( - OnMessageFlushCallback &&closeFlushCallback, - std::promise &&closeFlushPromise) noexcept; - static void s_OnContinuationClosed( struct aws_event_stream_rpc_client_continuation_token *token, void *user_data) noexcept; @@ -1500,19 +1497,22 @@ namespace Aws ClientContinuationImpl::ClientContinuationImpl( Aws::Crt::Allocator *allocator, + struct aws_client_bootstrap *bootstrap, struct aws_event_stream_rpc_client_connection *connection) noexcept - : m_allocator(allocator), m_clientBootstrap(aws_client_bootstrap_acquire( - aws_event_stream_rpc_client_connection_get_client_bootstrap(connection))), - m_sharedState(), m_operationModelContext(nullptr) - { - struct aws_event_stream_rpc_client_stream_continuation_options continuation_options = { - .on_continuation = s_OnContinuationMessage, - .on_continuation_closed = s_OnContinuationClosed, - .on_continuation_terminated = s_OnContinuationTerminated, - .user_data = this, - }; - m_sharedState.m_continuation = - aws_event_stream_rpc_client_connection_new_stream(connection, &continuation_options); + : m_allocator(allocator), m_clientBootstrap(aws_client_bootstrap_acquire(bootstrap)), m_sharedState(), + m_operationModelContext(nullptr) + { + if (connection != nullptr) + { + struct aws_event_stream_rpc_client_stream_continuation_options continuation_options = { + .on_continuation = s_OnContinuationMessage, + .on_continuation_closed = s_OnContinuationClosed, + .on_continuation_terminated = s_OnContinuationTerminated, + .user_data = this, + }; + m_sharedState.m_continuation = + aws_event_stream_rpc_client_connection_new_stream(connection, &continuation_options); + } if (!m_sharedState.m_continuation) { @@ -1583,10 +1583,11 @@ namespace Aws std::future ClientContinuationImpl::ShutDown() noexcept { struct aws_event_stream_rpc_client_continuation_token *releaseContinuation = nullptr; +#ifdef NEVER OnMessageFlushCallbackContainer *closeCallbackContainer = nullptr; OnMessageFlushCallbackContainer *activationCallbackContainer = nullptr; std::function activationResponseCallback; - +#endif { std::lock_guard lock(m_sharedStateLock); if (m_sharedState.m_currentState == ContinuationStateType::None) @@ -1596,7 +1597,7 @@ namespace Aws m_sharedState.m_currentState = ContinuationStateType::Closed; m_sharedState.m_desiredState = ContinuationStateType::Closed; } - +#ifdef NEVER activationCallbackContainer = m_sharedState.m_activationCallbackContainer; m_sharedState.m_activationCallbackContainer = nullptr; @@ -1605,8 +1606,9 @@ namespace Aws activationResponseCallback = std::move(m_sharedState.m_activationResponseCallback); m_sharedState.m_activationResponseCallback = nullptr; // unsure if necessary, let's be sure +#endif } - +#ifdef NEVER if (activationResponseCallback) { activationResponseCallback( @@ -1614,8 +1616,7 @@ namespace Aws } /* - * Short-circuit and simulate both activate and close callbacks as necessary. Part of our contract is that - * when Shutdown returns, no further user-facing callbacks/promise completions will be performed. + * Short-circuit and simulate both activate and close callbacks as necessary. */ OnMessageFlushCallbackContainer::Complete( activationCallbackContainer, {EVENT_STREAM_RPC_CONTINUATION_CLOSED, AWS_ERROR_SUCCESS}); @@ -1624,7 +1625,7 @@ namespace Aws OnMessageFlushCallbackContainer::Complete( closeCallbackContainer, {EVENT_STREAM_RPC_CONTINUATION_CLOSED, AWS_ERROR_SUCCESS}); OnMessageFlushCallbackContainer::Release(closeCallbackContainer); - +#endif if (releaseContinuation != nullptr) { ReleaseContinuation(m_allocator, m_clientBootstrap, releaseContinuation); @@ -1647,79 +1648,89 @@ namespace Aws OnMessageFlushCallback &&onMessageFlushCallback, bool &synchronousSuccess) noexcept { + AWS_FATAL_ASSERT(static_cast(onResultCallback)); + int result = AWS_OP_SUCCESS; synchronousSuccess = false; struct aws_array_list headersArray; // guaranteed to be zeroed or valid if we reach the end of the function - OnMessageFlushCallbackContainer *activationFailureContainer = nullptr; std::promise activationPromise; std::future activationFuture = activationPromise.get_future(); + auto activationContainer = Crt::New( + m_allocator, m_allocator, std::move(onMessageFlushCallback), std::move(activationPromise)); + RpcError activationError = {}; + { std::lock_guard lock(m_sharedStateLock); - if (m_sharedState.m_continuation == nullptr) - { - activationPromise.set_value({EVENT_STREAM_RPC_CONTINUATION_CLOSED, 0}); - return activationFuture; - } - - switch (m_sharedState.m_currentState) + if (m_sharedState.m_continuation != nullptr) { - case ContinuationStateType::PendingActivate: - case ContinuationStateType::Activated: - activationPromise.set_value({EVENT_STREAM_RPC_CONTINUATION_ALREADY_OPENED, 0}); - return activationFuture; - - case ContinuationStateType::PendingClose: - case ContinuationStateType::Closed: - activationPromise.set_value({EVENT_STREAM_RPC_CONTINUATION_CLOSED, 0}); - return activationFuture; - - default: - break; - } - - AWS_FATAL_ASSERT(m_sharedState.m_currentState == ContinuationStateType::None); - - // cleanup requirements mean we can't early out from here on - s_fillNativeHeadersArray(headers, &headersArray, m_allocator); - - struct aws_event_stream_rpc_message_args msg_args; - msg_args.headers = static_cast(headersArray.data); - msg_args.headers_count = headers.size(); - msg_args.payload = payload.has_value() ? (aws_byte_buf *)(&(payload.value())) : nullptr; - msg_args.message_type = messageType; - msg_args.message_flags = messageFlags; - - m_sharedState.m_activationCallbackContainer = Crt::New( - m_allocator, m_allocator, std::move(onMessageFlushCallback), std::move(activationPromise)); - m_sharedState.m_activationResponseCallback = std::move(onResultCallback); - - result = aws_event_stream_rpc_client_continuation_activate( - m_sharedState.m_continuation, - Crt::ByteCursorFromCString(operation.c_str()), - &msg_args, - s_protocolMessageCallback, - reinterpret_cast(m_sharedState.m_activationCallbackContainer)); + if (m_sharedState.m_currentState == ContinuationStateType::None) + { + // cleanup requirements mean we can't early out from here on + s_fillNativeHeadersArray(headers, &headersArray, m_allocator); + + struct aws_event_stream_rpc_message_args msg_args; + msg_args.headers = static_cast(headersArray.data); + msg_args.headers_count = headers.size(); + msg_args.payload = payload.has_value() ? (aws_byte_buf *)(&(payload.value())) : nullptr; + msg_args.message_type = messageType; + msg_args.message_flags = messageFlags; + + result = aws_event_stream_rpc_client_continuation_activate( + m_sharedState.m_continuation, + Crt::ByteCursorFromCString(operation.c_str()), + &msg_args, + s_protocolMessageCallback, + reinterpret_cast(activationContainer)); + + if (result == AWS_OP_SUCCESS) + { + m_sharedState.m_currentState = ContinuationStateType::PendingActivate; + m_sharedState.m_desiredState = ContinuationStateType::Activated; - if (result != AWS_OP_SUCCESS) - { - activationFailureContainer = m_sharedState.m_activationCallbackContainer; - m_sharedState.m_activationCallbackContainer = nullptr; - m_sharedState.m_activationResponseCallback = std::function(); + m_sharedState.m_activationCallbackContainer = activationContainer; + activationContainer = nullptr; + m_sharedState.m_activationResponseCallback = std::move(onResultCallback); + synchronousSuccess = true; + } + else + { + activationError = {EVENT_STREAM_RPC_CRT_ERROR, aws_last_error()}; + } + } + else + { + switch (m_sharedState.m_currentState) + { + case ContinuationStateType::PendingActivate: + case ContinuationStateType::Activated: + activationError = {EVENT_STREAM_RPC_CONTINUATION_ALREADY_OPENED, 0}; + break; + + case ContinuationStateType::PendingClose: + case ContinuationStateType::Closed: + activationError = {EVENT_STREAM_RPC_CONTINUATION_CLOSED, 0}; + break; + + default: + AWS_FATAL_ASSERT(false); + break; + } + } } else { - m_sharedState.m_currentState = ContinuationStateType::PendingActivate; - m_sharedState.m_desiredState = ContinuationStateType::Activated; - synchronousSuccess = true; + // null continuation is only if the connection didn't exist at creation time + activationError = {EVENT_STREAM_RPC_CONNECTION_CLOSED, 0}; } } - if (activationFailureContainer) + if (!synchronousSuccess) { - OnMessageFlushCallbackContainer::Complete( - activationFailureContainer, {EVENT_STREAM_RPC_CRT_ERROR, aws_last_error()}); - OnMessageFlushCallbackContainer::Release(activationFailureContainer); - OnMessageFlushCallbackContainer::Release(activationFailureContainer); + AWS_FATAL_ASSERT(activationContainer != nullptr); + + OnMessageFlushCallbackContainer::Complete(activationContainer, activationError); + OnMessageFlushCallbackContainer::Release(activationContainer); + OnMessageFlushCallbackContainer::Release(activationContainer); } if (aws_array_list_is_valid(&headersArray)) @@ -1737,66 +1748,76 @@ namespace Aws uint32_t messageFlags, OnMessageFlushCallback &&onMessageFlushCallback) { - int result = AWS_OP_SUCCESS; + int result = AWS_OP_ERR; struct aws_array_list headersArray; // guaranteed to be zeroed or valid if we reach the end of the function - OnMessageFlushCallbackContainer *sendFailureContainer = nullptr; std::promise sendPromise; std::future sendFuture = sendPromise.get_future(); + auto sendContainer = Crt::New( + m_allocator, m_allocator, std::move(onMessageFlushCallback), std::move(sendPromise)); + RpcError sendError = {}; { std::lock_guard lock(m_sharedStateLock); - if (m_sharedState.m_continuation == nullptr) - { - sendPromise.set_value({EVENT_STREAM_RPC_CONTINUATION_CLOSED, 0}); - return sendFuture; - } - - switch (m_sharedState.m_currentState) + if (m_sharedState.m_continuation != nullptr) { - case ContinuationStateType::PendingActivate: - case ContinuationStateType::None: - sendPromise.set_value({EVENT_STREAM_RPC_CONTINUATION_NOT_YET_OPENED, 0}); - return sendFuture; - - case ContinuationStateType::PendingClose: - case ContinuationStateType::Closed: - sendPromise.set_value({EVENT_STREAM_RPC_CONTINUATION_CLOSED, 0}); - return sendFuture; - - default: - break; + if (m_sharedState.m_currentState == ContinuationStateType::Activated) + { + // cleanup requirements mean we can't early out from here on + s_fillNativeHeadersArray(headers, &headersArray, m_allocator); + + struct aws_event_stream_rpc_message_args msg_args; + msg_args.headers = static_cast(headersArray.data); + msg_args.headers_count = headers.size(); + msg_args.payload = payload.has_value() ? (aws_byte_buf *)(&(payload.value())) : nullptr; + msg_args.message_type = messageType; + msg_args.message_flags = messageFlags; + + result = aws_event_stream_rpc_client_continuation_send_message( + m_sharedState.m_continuation, + &msg_args, + s_protocolMessageCallback, + reinterpret_cast(sendContainer)); + + if (result != AWS_OP_SUCCESS) + { + sendError = {EVENT_STREAM_RPC_CRT_ERROR, aws_last_error()}; + } + else + { + OnMessageFlushCallbackContainer::Release(sendContainer); // release our ref + } + } + else + { + switch (m_sharedState.m_currentState) + { + case ContinuationStateType::PendingActivate: + case ContinuationStateType::None: + sendError = {EVENT_STREAM_RPC_CONTINUATION_NOT_YET_OPENED, 0}; + break; + + case ContinuationStateType::PendingClose: + case ContinuationStateType::Closed: + sendError = {EVENT_STREAM_RPC_CONTINUATION_CLOSED, 0}; + break; + + default: + AWS_FATAL_ASSERT(false); + break; + } + } } - - // cleanup requirements mean we can't early out from here on - s_fillNativeHeadersArray(headers, &headersArray, m_allocator); - - struct aws_event_stream_rpc_message_args msg_args; - msg_args.headers = static_cast(headersArray.data); - msg_args.headers_count = headers.size(); - msg_args.payload = payload.has_value() ? (aws_byte_buf *)(&(payload.value())) : nullptr; - msg_args.message_type = messageType; - msg_args.message_flags = messageFlags; - - auto sendContainer = Crt::New( - m_allocator, m_allocator, std::move(onMessageFlushCallback), std::move(sendPromise)); - OnMessageFlushCallbackContainer::Release(sendContainer); // release our ref - - result = aws_event_stream_rpc_client_continuation_send_message( - m_sharedState.m_continuation, - &msg_args, - s_protocolMessageCallback, - reinterpret_cast(sendContainer)); - - if (result != AWS_OP_SUCCESS) + else { - sendFailureContainer = sendContainer; + // null continuation is only if the connection didn't exist at creation time + sendError = {EVENT_STREAM_RPC_CONNECTION_CLOSED, 0}; } } - if (sendFailureContainer) + if (result == AWS_OP_ERR) { - OnMessageFlushCallbackContainer::Complete( - sendFailureContainer, {EVENT_STREAM_RPC_CRT_ERROR, aws_last_error()}); - OnMessageFlushCallbackContainer::Release(sendFailureContainer); + OnMessageFlushCallbackContainer::Complete(sendContainer, sendError); + OnMessageFlushCallbackContainer::Release(sendContainer); + OnMessageFlushCallbackContainer::Release(sendContainer); } if (aws_array_list_is_valid(&headersArray)) @@ -1807,90 +1828,68 @@ namespace Aws return sendFuture; } - // Intentionally returns an error code and not AWS_OP_ERR/AWS_OP_SUCCESS - // shared state lock must be held before calling - int ClientContinuationImpl::SendCloseMessage( - OnMessageFlushCallback &&closeFlushCallback, - std::promise &&closeFlushPromise) noexcept - { - int errorCode = AWS_ERROR_SUCCESS; - - struct aws_event_stream_rpc_message_args msg_args; - msg_args.headers = nullptr; - msg_args.headers_count = 0; - msg_args.payload = nullptr; - msg_args.message_type = AWS_EVENT_STREAM_RPC_MESSAGE_TYPE_APPLICATION_MESSAGE; - msg_args.message_flags = AWS_EVENT_STREAM_RPC_MESSAGE_FLAG_TERMINATE_STREAM; - - m_sharedState.m_closeCallbackContainer = Crt::New( - m_allocator, m_allocator, std::move(closeFlushCallback), std::move(closeFlushPromise)); - - int result = aws_event_stream_rpc_client_continuation_send_message( - m_sharedState.m_continuation, - &msg_args, - s_protocolMessageCallback, - reinterpret_cast(m_sharedState.m_closeCallbackContainer)); - - if (result) - { - errorCode = aws_last_error(); - AWS_LOGF_ERROR( - AWS_LS_EVENT_STREAM_RPC_CLIENT, - "A CRT error occurred while closing the stream: %s", - Crt::ErrorDebugString(errorCode)); - } - - return errorCode; - } - std::future ClientContinuationImpl::Close(OnMessageFlushCallback &&onMessageFlushCallback) noexcept { - int closeErrorCode = AWS_ERROR_SUCCESS; - OnMessageFlushCallbackContainer *closeFailureCallbackContainer = nullptr; + int closeResult = AWS_OP_ERR; std::promise closePromise; std::future closeFuture = closePromise.get_future(); + auto closeCallbackContainer = Crt::New( + m_allocator, m_allocator, std::move(onMessageFlushCallback), std::move(closePromise)); + RpcError closeError = {}; + { std::lock_guard lock(m_sharedStateLock); - if (m_sharedState.m_continuation == nullptr) - { - closePromise.set_value({EVENT_STREAM_RPC_CONTINUATION_CLOSED, 0}); - return closeFuture; - } - - switch (m_sharedState.m_currentState) + if (m_sharedState.m_continuation != nullptr) { - case ContinuationStateType::PendingActivate: - case ContinuationStateType::Activated: + if (m_sharedState.m_currentState == ContinuationStateType::PendingActivate || + m_sharedState.m_currentState == ContinuationStateType::Activated) { - closeErrorCode = SendCloseMessage(std::move(onMessageFlushCallback), std::move(closePromise)); - if (closeErrorCode != AWS_ERROR_SUCCESS) + struct aws_event_stream_rpc_message_args msg_args; + msg_args.headers = nullptr; + msg_args.headers_count = 0; + msg_args.payload = nullptr; + msg_args.message_type = AWS_EVENT_STREAM_RPC_MESSAGE_TYPE_APPLICATION_MESSAGE; + msg_args.message_flags = AWS_EVENT_STREAM_RPC_MESSAGE_FLAG_TERMINATE_STREAM; + + closeResult = aws_event_stream_rpc_client_continuation_send_message( + m_sharedState.m_continuation, + &msg_args, + s_protocolMessageCallback, + reinterpret_cast(closeCallbackContainer)); + + if (closeResult == AWS_OP_ERR) { - closeFailureCallbackContainer = m_sharedState.m_closeCallbackContainer; - m_sharedState.m_closeCallbackContainer = nullptr; + closeError = {EVENT_STREAM_RPC_CRT_ERROR, aws_last_error()}; } else { m_sharedState.m_currentState = ContinuationStateType::PendingClose; } - break; } - - case ContinuationStateType::PendingClose: - closePromise.set_value({EVENT_STREAM_RPC_CONTINUATION_CLOSE_IN_PROGRESS, 0}); - return closeFuture; - - default: - closePromise.set_value({EVENT_STREAM_RPC_CONTINUATION_CLOSED, 0}); - return closeFuture; + else + { + if (m_sharedState.m_currentState == ContinuationStateType::PendingClose) + { + closeError = {EVENT_STREAM_RPC_CONTINUATION_CLOSE_IN_PROGRESS, 0}; + } + else + { + closeError = {EVENT_STREAM_RPC_CONTINUATION_CLOSED, 0}; + } + } + } + else + { + // null continuation is only if the connection didn't exist at creation time + closeError = {EVENT_STREAM_RPC_CONNECTION_CLOSED, 0}; } } - if (closeFailureCallbackContainer != nullptr) + if (closeResult != AWS_OP_SUCCESS) { - OnMessageFlushCallbackContainer::Complete( - closeFailureCallbackContainer, {EVENT_STREAM_RPC_CRT_ERROR, closeErrorCode}); - OnMessageFlushCallbackContainer::Release(closeFailureCallbackContainer); - OnMessageFlushCallbackContainer::Release(closeFailureCallbackContainer); + OnMessageFlushCallbackContainer::Complete(closeCallbackContainer, closeError); + OnMessageFlushCallbackContainer::Release(closeCallbackContainer); + OnMessageFlushCallbackContainer::Release(closeCallbackContainer); } return closeFuture; @@ -2090,7 +2089,20 @@ namespace Aws { if (result.m_statusCode == EVENT_STREAM_RPC_SUCCESS) { - activationResultCallback(TaggedResult(std::move(result.m_message.value().m_shape))); + const auto &message = result.m_message.value(); + if (message.m_route == EventStreamMessageRoutingType::Response) + { + activationResultCallback(TaggedResult(std::move(result.m_message.value().m_shape))); + } + else + { + Crt::Allocator *allocator = m_allocator; + auto errorResponse = Crt::ScopedResource( + static_cast(result.m_message.value().m_shape.release()), + [allocator](OperationError *shape) { Crt::Delete(shape, allocator); }); + + activationResultCallback(TaggedResult(std::move(errorResponse))); + } } else { @@ -2150,7 +2162,7 @@ namespace Aws { std::lock_guard lock(m_sharedStateLock); return Aws::Crt::MakeShared( - m_allocator, m_allocator, m_sharedState.m_underlyingConnection); + m_allocator, m_allocator, m_bootstrap, m_sharedState.m_underlyingConnection); } } /* namespace Eventstreamrpc */ diff --git a/eventstream_rpc/tests/CMakeLists.txt b/eventstream_rpc/tests/CMakeLists.txt index 08dedfecb..3d7e02e82 100644 --- a/eventstream_rpc/tests/CMakeLists.txt +++ b/eventstream_rpc/tests/CMakeLists.txt @@ -30,6 +30,7 @@ file(GLOB TESTS ${TEST_HDRS} ${TEST_SRC}) set(TEST_BINARY_NAME ${PROJECT_NAME}-tests) +# Connectivity tests add_test_case(EventStreamClientCreateFailureInvalidHost) add_test_case(EventStreamClientCreateFailureInvalidPort) add_test_case(EventStreamConnectSuccess) @@ -43,6 +44,7 @@ add_test_case(EchoClientCloseWhileConnecting) add_test_case(EchoClientConnectWhileClosing) add_test_case(EchoClientOpenCloseStress) +# Basic non-streaming operation tests add_test_case(EchoClientOperationEchoSuccessString) add_test_case(EchoClientOperationEchoSuccessBoolean) add_test_case(EchoClientOperationEchoSuccessTime) @@ -52,14 +54,37 @@ add_test_case(EchoClientOperationEchoSuccessBlob) add_test_case(EchoClientOperationEchoSuccessStringList) add_test_case(EchoClientOperationEchoSuccessPairList) add_test_case(EchoClientOperationEchoSuccessProductMap) +add_test_case(EchoClientOperationEchoSuccessMultiple) +add_test_case(EchoClientOperationGetAllProductsSuccess) +add_test_case(EchoClientOperationGetAllCustomersSuccess) +add_test_case(EchoClientOperationCauseServiceErrorSuccess) +add_test_case(EchoClientOperationEchoFailureNeverConnected) +add_test_case(EchoClientOperationEchoFailureDisconnected) -#add_test_case(EchoClientOperationEchoSuccessMultiple) -#add_test_case(EchoClientOperationEchoFailureNeverConnected) -#add_test_case(EchoClientOperationEchoFailureDisconnected) +# Non-streaming race condition tests +add_test_case(EchoClientOperationUnactivatedShutdown) +add_test_case(EchoClientOperationUnactivatedClose) +add_test_case(EchoClientOperationUnactivatedCloseDropFuture) +add_test_case(EchoClientOperationActivateActivate) +#add_test_case(EchoClientOperationActivateWaitActivate) +#add_test_case(EchoClientOperationActivateCloseActivate) +#add_test_case(EchoClientOperationActivateClosedActivate) +#add_test_case(EchoClientOperationActivateCloseConnection) +#add_test_case(EchoClientOperationActivateCloseContinuation) +#add_test_case(EchoClientOperationActivateDoubleCloseContinuation) +#add_test_case(EchoClientOperationActivateWaitDoubleCloseContinuation) +#add_test_case(EchoClientOperationActivateWaitCloseContinuationWaitCloseContinuation) +#add_test_case(EchoClientOperationActivateShutdown) +#add_test_case(EchoClientOperationActivateShutdownDropFuture) +#add_test_case(EchoClientOperationActivateWaitCloseShutdown) +#add_test_case(EchoClientOperationActivateWaitCloseShutdownDropFuture) +# streaming echo messages tests +# streaming failure/error tests + +# streaming race condition tests -#add_test_case(StressTestClient) generate_cpp_test_driver(${TEST_BINARY_NAME}) aws_add_sanitizers(${TEST_BINARY_NAME}) target_include_directories(${TEST_BINARY_NAME} PUBLIC diff --git a/eventstream_rpc/tests/EchoTestRpcModel.cpp b/eventstream_rpc/tests/EchoTestRpcModel.cpp index 63d3472c6..567786fe3 100644 --- a/eventstream_rpc/tests/EchoTestRpcModel.cpp +++ b/eventstream_rpc/tests/EchoTestRpcModel.cpp @@ -1034,19 +1034,21 @@ namespace Awstest OnMessageFlushCallback onMessageFlushCallback) noexcept { bool synchronousSuccess = false; - m_selfReference = shared_from_this(); + std::lock_guard selfReferenceLock(m_selfReferenceLock); auto activateFuture = ClientOperation::Activate( static_cast(&request), std::move(onMessageFlushCallback), [this](TaggedResult &&unmodeledResult) { + std::lock_guard selfReferenceLock(m_selfReferenceLock); m_resultPromise.set_value(GetAllProductsResult(std::move(unmodeledResult))); m_selfReference = nullptr; }, synchronousSuccess); - if (!synchronousSuccess) + if (synchronousSuccess) { - m_selfReference = nullptr; + m_selfReference = shared_from_this(); + ; } return activateFuture; } @@ -1112,19 +1114,21 @@ namespace Awstest OnMessageFlushCallback onMessageFlushCallback) noexcept { bool synchronousSuccess = false; - m_selfReference = shared_from_this(); + std::lock_guard selfReferenceLock(m_selfReferenceLock); auto activateFuture = ClientOperation::Activate( static_cast(&request), std::move(onMessageFlushCallback), [this](TaggedResult &&unmodeledResult) { + std::lock_guard selfReferenceLock(m_selfReferenceLock); m_resultPromise.set_value(CauseServiceErrorResult(std::move(unmodeledResult))); m_selfReference = nullptr; }, synchronousSuccess); - if (!synchronousSuccess) + if (synchronousSuccess) { - m_selfReference = nullptr; + m_selfReference = shared_from_this(); + ; } return activateFuture; } @@ -1213,19 +1217,21 @@ namespace Awstest OnMessageFlushCallback onMessageFlushCallback) noexcept { bool synchronousSuccess = false; - m_selfReference = shared_from_this(); + std::lock_guard selfReferenceLock(m_selfReferenceLock); auto activateFuture = ClientOperation::Activate( static_cast(&request), std::move(onMessageFlushCallback), [this](TaggedResult &&unmodeledResult) { + std::lock_guard selfReferenceLock(m_selfReferenceLock); m_resultPromise.set_value(CauseStreamServiceToErrorResult(std::move(unmodeledResult))); m_selfReference = nullptr; }, synchronousSuccess); - if (!synchronousSuccess) + if (synchronousSuccess) { - m_selfReference = nullptr; + m_selfReference = shared_from_this(); + ; } return activateFuture; } @@ -1317,19 +1323,21 @@ namespace Awstest OnMessageFlushCallback onMessageFlushCallback) noexcept { bool synchronousSuccess = false; - m_selfReference = shared_from_this(); + std::lock_guard selfReferenceLock(m_selfReferenceLock); auto activateFuture = ClientOperation::Activate( static_cast(&request), std::move(onMessageFlushCallback), [this](TaggedResult &&unmodeledResult) { + std::lock_guard selfReferenceLock(m_selfReferenceLock); m_resultPromise.set_value(EchoStreamMessagesResult(std::move(unmodeledResult))); m_selfReference = nullptr; }, synchronousSuccess); - if (!synchronousSuccess) + if (synchronousSuccess) { - m_selfReference = nullptr; + m_selfReference = shared_from_this(); + ; } return activateFuture; } @@ -1401,19 +1409,21 @@ namespace Awstest OnMessageFlushCallback onMessageFlushCallback) noexcept { bool synchronousSuccess = false; - m_selfReference = shared_from_this(); + std::lock_guard selfReferenceLock(m_selfReferenceLock); auto activateFuture = ClientOperation::Activate( static_cast(&request), std::move(onMessageFlushCallback), [this](TaggedResult &&unmodeledResult) { + std::lock_guard selfReferenceLock(m_selfReferenceLock); m_resultPromise.set_value(EchoMessageResult(std::move(unmodeledResult))); m_selfReference = nullptr; }, synchronousSuccess); - if (!synchronousSuccess) + if (synchronousSuccess) { - m_selfReference = nullptr; + m_selfReference = shared_from_this(); + ; } return activateFuture; } @@ -1478,19 +1488,21 @@ namespace Awstest OnMessageFlushCallback onMessageFlushCallback) noexcept { bool synchronousSuccess = false; - m_selfReference = shared_from_this(); + std::lock_guard selfReferenceLock(m_selfReferenceLock); auto activateFuture = ClientOperation::Activate( static_cast(&request), std::move(onMessageFlushCallback), [this](TaggedResult &&unmodeledResult) { + std::lock_guard selfReferenceLock(m_selfReferenceLock); m_resultPromise.set_value(GetAllCustomersResult(std::move(unmodeledResult))); m_selfReference = nullptr; }, synchronousSuccess); - if (!synchronousSuccess) + if (synchronousSuccess) { - m_selfReference = nullptr; + m_selfReference = shared_from_this(); + ; } return activateFuture; } diff --git a/eventstream_rpc/tests/EventStreamClientTest.cpp b/eventstream_rpc/tests/EventStreamClientTest.cpp index a49fd1332..42be63c5c 100644 --- a/eventstream_rpc/tests/EventStreamClientTest.cpp +++ b/eventstream_rpc/tests/EventStreamClientTest.cpp @@ -776,8 +776,6 @@ static int s_TestEchoClientOperationEchoSuccessProductMap(struct aws_allocator * AWS_TEST_CASE(EchoClientOperationEchoSuccessProductMap, s_TestEchoClientOperationEchoSuccessProductMap); -#ifdef NEVER - static int s_TestEchoClientOperationEchoSuccessMultiple(struct aws_allocator *allocator, void *ctx) { ApiHandle apiHandle(allocator); @@ -820,6 +818,98 @@ static int s_TestEchoClientOperationEchoSuccessMultiple(struct aws_allocator *al AWS_TEST_CASE(EchoClientOperationEchoSuccessMultiple, s_TestEchoClientOperationEchoSuccessMultiple); +static int s_DoSimpleRequestWhileConnectedTest( + struct aws_allocator *allocator, + std::function testFunction) +{ + ApiHandle apiHandle(allocator); + EventStreamClientTestContext testContext(allocator); + if (!testContext.isValidEnvironment()) + { + printf("Environment Variables are not set for the test, skipping..."); + return AWS_OP_SKIP; + } + + { + ConnectionLifecycleHandler lifecycleHandler; + Awstest::EchoTestRpcClient client(*testContext.clientBootstrap, allocator); + auto connectedStatus = client.Connect(lifecycleHandler); + ASSERT_TRUE(connectedStatus.get().baseStatus == EVENT_STREAM_RPC_SUCCESS); + + return testFunction(testContext, client); + } +} + +static int s_TestEchoClientOperationGetAllProductsSuccess(struct aws_allocator *allocator, void *ctx) +{ + return s_DoSimpleRequestWhileConnectedTest( + allocator, + [](EventStreamClientTestContext &testContext, EchoTestRpcClient &client) + { + auto getAllProducts = client.NewGetAllProducts(); + GetAllProductsRequest getAllProductsRequest; + + auto requestFuture = getAllProducts->Activate(getAllProductsRequest, s_onMessageFlush); + requestFuture.wait(); + auto result = getAllProducts->GetResult().get(); + ASSERT_TRUE(result); + auto response = result.GetOperationResponse(); + ASSERT_NOT_NULL(response); + + return AWS_OP_SUCCESS; + }); +} + +AWS_TEST_CASE(EchoClientOperationGetAllProductsSuccess, s_TestEchoClientOperationGetAllProductsSuccess); + +static int s_TestEchoClientOperationGetAllCustomersSuccess(struct aws_allocator *allocator, void *ctx) +{ + return s_DoSimpleRequestWhileConnectedTest( + allocator, + [](EventStreamClientTestContext &testContext, EchoTestRpcClient &client) + { + auto getAllCustomers = client.NewGetAllCustomers(); + GetAllCustomersRequest getAllCustomersRequest; + + auto requestFuture = getAllCustomers->Activate(getAllCustomersRequest, s_onMessageFlush); + requestFuture.wait(); + auto result = getAllCustomers->GetResult().get(); + ASSERT_TRUE(result); + auto response = result.GetOperationResponse(); + ASSERT_NOT_NULL(response); + + return AWS_OP_SUCCESS; + }); +} + +AWS_TEST_CASE(EchoClientOperationGetAllCustomersSuccess, s_TestEchoClientOperationGetAllCustomersSuccess); + +static int s_TestEchoClientOperationCauseServiceErrorSuccess(struct aws_allocator *allocator, void *ctx) +{ + return s_DoSimpleRequestWhileConnectedTest( + allocator, + [](EventStreamClientTestContext &testContext, EchoTestRpcClient &client) + { + auto causeServiceError = client.NewCauseServiceError(); + CauseServiceErrorRequest causeServiceErrorRequest; + + auto requestFuture = causeServiceError->Activate(causeServiceErrorRequest, s_onMessageFlush); + requestFuture.wait(); + auto result = causeServiceError->GetResult().get(); + ASSERT_FALSE(result); + ASSERT_NOT_NULL(result.GetOperationError()); + auto error = result.GetOperationError(); + const auto &errorMessage = error->GetMessage().value(); + ASSERT_TRUE(errorMessage == "Intentionally thrown ServiceError"); + const auto &modelName = error->GetModelName(); + ASSERT_TRUE(modelName == "awstest#ServiceError"); + + return AWS_OP_SUCCESS; + }); +} + +AWS_TEST_CASE(EchoClientOperationCauseServiceErrorSuccess, s_TestEchoClientOperationCauseServiceErrorSuccess); + static int s_TestEchoClientOperationEchoFailureNeverConnected(struct aws_allocator *allocator, void *ctx) { ApiHandle apiHandle(allocator); @@ -844,7 +934,7 @@ static int s_TestEchoClientOperationEchoFailureNeverConnected(struct aws_allocat auto requestFuture = echoMessage->Activate(echoMessageRequest, s_onMessageFlush); ASSERT_INT_EQUALS(EVENT_STREAM_RPC_CONNECTION_CLOSED, requestFuture.get().baseStatus); - auto result = echoMessage->GetOperationResult().get(); + auto result = echoMessage->GetResult().get(); ASSERT_FALSE(result); auto error = result.GetRpcError(); @@ -882,13 +972,15 @@ static int s_TestEchoClientOperationEchoFailureDisconnected(struct aws_allocator echoMessageRequest.SetMessage(messageData); auto requestFuture = echoMessage->Activate(echoMessageRequest, s_onMessageFlush); - ASSERT_INT_EQUALS(EVENT_STREAM_RPC_CONNECTION_CLOSED, requestFuture.get().baseStatus); + auto activateStatus = requestFuture.get().baseStatus; + ASSERT_TRUE( + activateStatus == EVENT_STREAM_RPC_CONNECTION_CLOSED || activateStatus == EVENT_STREAM_RPC_CRT_ERROR); - auto result = echoMessage->GetOperationResult().get(); + auto result = echoMessage->GetResult().get(); ASSERT_FALSE(result); - auto error = result.GetRpcError(); - ASSERT_INT_EQUALS(EVENT_STREAM_RPC_CONNECTION_CLOSED, error.baseStatus); + auto resultStatus = result.GetRpcError().baseStatus; + ASSERT_TRUE(resultStatus == EVENT_STREAM_RPC_CONNECTION_CLOSED || resultStatus == EVENT_STREAM_RPC_CRT_ERROR); } return AWS_OP_SUCCESS; @@ -896,6 +988,128 @@ static int s_TestEchoClientOperationEchoFailureDisconnected(struct aws_allocator AWS_TEST_CASE(EchoClientOperationEchoFailureDisconnected, s_TestEchoClientOperationEchoFailureDisconnected); +static int s_DoSimpleRequestRaceCheckTest( + struct aws_allocator *allocator, + std::function testFunction) +{ + ApiHandle apiHandle(allocator); + EventStreamClientTestContext testContext(allocator); + if (!testContext.isValidEnvironment()) + { + printf("Environment Variables are not set for the test, skipping..."); + return AWS_OP_SKIP; + } + + { + ConnectionLifecycleHandler lifecycleHandler; + Awstest::EchoTestRpcClient client(*testContext.clientBootstrap, allocator); + auto connectedStatus = client.Connect(lifecycleHandler); + ASSERT_TRUE(connectedStatus.get().baseStatus == EVENT_STREAM_RPC_SUCCESS); + + for (size_t i = 0; i < 1000; ++i) + { + ASSERT_SUCCESS(testFunction(testContext, client)); + } + + return AWS_OP_SUCCESS; + } +} + +static int s_TestEchoClientOperationUnactivatedShutdown(struct aws_allocator *allocator, void *ctx) +{ + return s_DoSimpleRequestRaceCheckTest( + allocator, + [](EventStreamClientTestContext &testContext, EchoTestRpcClient &client) + { + auto echoMessage = client.NewEchoMessage(); + + // Drop the operation, invoking shutdown + return AWS_OP_SUCCESS; + }); +} + +AWS_TEST_CASE(EchoClientOperationUnactivatedShutdown, s_TestEchoClientOperationUnactivatedShutdown); + +static int s_TestEchoClientOperationUnactivatedClose(struct aws_allocator *allocator, void *ctx) +{ + return s_DoSimpleRequestRaceCheckTest( + allocator, + [](EventStreamClientTestContext &testContext, EchoTestRpcClient &client) + { + auto echoMessage = client.NewEchoMessage(); + + auto closeFuture = echoMessage->Close(); + auto closeStatus = closeFuture.get().baseStatus; + ASSERT_INT_EQUALS(EVENT_STREAM_RPC_CONTINUATION_CLOSED, closeStatus); + + // Drop the operation, invoking shutdown + return AWS_OP_SUCCESS; + }); +} + +AWS_TEST_CASE(EchoClientOperationUnactivatedClose, s_TestEchoClientOperationUnactivatedClose); + +static int s_TestEchoClientOperationUnactivatedCloseDropFuture(struct aws_allocator *allocator, void *ctx) +{ + return s_DoSimpleRequestRaceCheckTest( + allocator, + [](EventStreamClientTestContext &testContext, EchoTestRpcClient &client) + { + auto echoMessage = client.NewEchoMessage(); + + auto closeFuture = echoMessage->Close(); + + // Drop the operation and the close future, invoking shutdown + return AWS_OP_SUCCESS; + }); +} + +AWS_TEST_CASE(EchoClientOperationUnactivatedCloseDropFuture, s_TestEchoClientOperationUnactivatedCloseDropFuture); + +static int s_TestEchoClientOperationActivateActivate(struct aws_allocator *allocator, void *ctx) +{ + return s_DoSimpleRequestRaceCheckTest( + allocator, + [](EventStreamClientTestContext &testContext, EchoTestRpcClient &client) + { + auto getAllCustomers = client.NewGetAllCustomers(); + GetAllCustomersRequest getAllCustomersRequest; + + auto requestFuture = getAllCustomers->Activate(getAllCustomersRequest, s_onMessageFlush); + // auto requestFuture2 = getAllCustomers->Activate(getAllCustomersRequest, s_onMessageFlush); + // requestFuture2.wait(); + requestFuture.wait(); + auto result = getAllCustomers->GetResult().get(); + ASSERT_TRUE(result); + auto response = result.GetOperationResponse(); + ASSERT_NOT_NULL(response); + + // auto flush2ErrorStatus = requestFuture2.get().baseStatus; + // ASSERT_INT_EQUALS(EVENT_STREAM_RPC_CONTINUATION_ALREADY_OPENED, flush2ErrorStatus); + + return AWS_OP_SUCCESS; + }); +} + +AWS_TEST_CASE(EchoClientOperationActivateActivate, s_TestEchoClientOperationActivateActivate); + +// Non-streaming race condition tests +// add_test_case(EchoClientOperationActivateActivate) +// add_test_case(EchoClientOperationActivateWaitActivate) +// add_test_case(EchoClientOperationActivateCloseActivate) +// add_test_case(EchoClientOperationActivateClosedActivate) +// add_test_case(EchoClientOperationActivateCloseConnection) +// add_test_case(EchoClientOperationActivateCloseContinuation) +// add_test_case(EchoClientOperationActivateDoubleCloseContinuation) +// add_test_case(EchoClientOperationActivateWaitDoubleCloseContinuation) +// add_test_case(EchoClientOperationActivateWaitCloseContinuationWaitCloseContinuation) +// add_test_case(EchoClientOperationActivateShutdown) +// add_test_case(EchoClientOperationActivateShutdownDropFuture) +// add_test_case(EchoClientOperationActivateWaitCloseShutdown) +// add_test_case(EchoClientOperationActivateWaitCloseShutdownDropFuture) + +#ifdef NEVER + AWS_TEST_CASE_FIXTURE(EchoOperation, s_testSetup, s_TestEchoOperation, s_testTeardown, &s_testContext); static int s_TestEchoOperation(struct aws_allocator *allocator, void *ctx) { diff --git a/eventstream_rpc/tests/include/awstest/EchoTestRpcModel.h b/eventstream_rpc/tests/include/awstest/EchoTestRpcModel.h index 19cdc201c..1fb4b3b66 100644 --- a/eventstream_rpc/tests/include/awstest/EchoTestRpcModel.h +++ b/eventstream_rpc/tests/include/awstest/EchoTestRpcModel.h @@ -745,6 +745,9 @@ namespace Awstest private: std::promise m_resultPromise; + /* Enforces a happens-before relationship between setting the self-reference and clearing it. */ + std::mutex m_selfReferenceLock; + /* Keeps the operation alive while activation is in-progress. Internally, we capture `this` in the function * object that handles the result. If we did not do this, we risk a crash if the user drops their reference * before the future gets completed. */ @@ -818,6 +821,9 @@ namespace Awstest private: std::promise m_resultPromise; + /* Enforces a happens-before relationship between setting the self-reference and clearing it. */ + std::mutex m_selfReferenceLock; + /* Keeps the operation alive while activation is in-progress. Internally, we capture `this` in the function * object that handles the result. If we did not do this, we risk a crash if the user drops their reference * before the future gets completed. */ @@ -955,6 +961,9 @@ namespace Awstest private: std::promise m_resultPromise; + /* Enforces a happens-before relationship between setting the self-reference and clearing it. */ + std::mutex m_selfReferenceLock; + /* Keeps the operation alive while activation is in-progress. Internally, we capture `this` in the function * object that handles the result. If we did not do this, we risk a crash if the user drops their reference * before the future gets completed. */ @@ -1080,6 +1089,9 @@ namespace Awstest private: std::promise m_resultPromise; + /* Enforces a happens-before relationship between setting the self-reference and clearing it. */ + std::mutex m_selfReferenceLock; + /* Keeps the operation alive while activation is in-progress. Internally, we capture `this` in the function * object that handles the result. If we did not do this, we risk a crash if the user drops their reference * before the future gets completed. */ @@ -1152,6 +1164,9 @@ namespace Awstest private: std::promise m_resultPromise; + /* Enforces a happens-before relationship between setting the self-reference and clearing it. */ + std::mutex m_selfReferenceLock; + /* Keeps the operation alive while activation is in-progress. Internally, we capture `this` in the function * object that handles the result. If we did not do this, we risk a crash if the user drops their reference * before the future gets completed. */ @@ -1224,6 +1239,9 @@ namespace Awstest private: std::promise m_resultPromise; + /* Enforces a happens-before relationship between setting the self-reference and clearing it. */ + std::mutex m_selfReferenceLock; + /* Keeps the operation alive while activation is in-progress. Internally, we capture `this` in the function * object that handles the result. If we did not do this, we risk a crash if the user drops their reference * before the future gets completed. */ diff --git a/greengrass_ipc/include/aws/greengrass/GreengrassCoreIpcModel.h b/greengrass_ipc/include/aws/greengrass/GreengrassCoreIpcModel.h index c569872ce..8cdba9b4d 100644 --- a/greengrass_ipc/include/aws/greengrass/GreengrassCoreIpcModel.h +++ b/greengrass_ipc/include/aws/greengrass/GreengrassCoreIpcModel.h @@ -4546,6 +4546,9 @@ namespace Aws private: std::promise m_resultPromise; + /* Enforces a happens-before relationship between setting the self-reference and clearing it. */ + std::mutex m_selfReferenceLock; + /* Keeps the operation alive while activation is in-progress. Internally, we capture `this` in the function * object that handles the result. If we did not do this, we risk a crash if the user drops their reference * before the future gets completed. */ @@ -4619,6 +4622,9 @@ namespace Aws private: std::promise m_resultPromise; + /* Enforces a happens-before relationship between setting the self-reference and clearing it. */ + std::mutex m_selfReferenceLock; + /* Keeps the operation alive while activation is in-progress. Internally, we capture `this` in the function * object that handles the result. If we did not do this, we risk a crash if the user drops their reference * before the future gets completed. */ @@ -4692,6 +4698,9 @@ namespace Aws private: std::promise m_resultPromise; + /* Enforces a happens-before relationship between setting the self-reference and clearing it. */ + std::mutex m_selfReferenceLock; + /* Keeps the operation alive while activation is in-progress. Internally, we capture `this` in the function * object that handles the result. If we did not do this, we risk a crash if the user drops their reference * before the future gets completed. */ @@ -4828,6 +4837,9 @@ namespace Aws private: std::promise m_resultPromise; + /* Enforces a happens-before relationship between setting the self-reference and clearing it. */ + std::mutex m_selfReferenceLock; + /* Keeps the operation alive while activation is in-progress. Internally, we capture `this` in the function * object that handles the result. If we did not do this, we risk a crash if the user drops their reference * before the future gets completed. */ @@ -4901,6 +4913,9 @@ namespace Aws private: std::promise m_resultPromise; + /* Enforces a happens-before relationship between setting the self-reference and clearing it. */ + std::mutex m_selfReferenceLock; + /* Keeps the operation alive while activation is in-progress. Internally, we capture `this` in the function * object that handles the result. If we did not do this, we risk a crash if the user drops their reference * before the future gets completed. */ @@ -4974,6 +4989,9 @@ namespace Aws private: std::promise m_resultPromise; + /* Enforces a happens-before relationship between setting the self-reference and clearing it. */ + std::mutex m_selfReferenceLock; + /* Keeps the operation alive while activation is in-progress. Internally, we capture `this` in the function * object that handles the result. If we did not do this, we risk a crash if the user drops their reference * before the future gets completed. */ @@ -5049,6 +5067,9 @@ namespace Aws private: std::promise m_resultPromise; + /* Enforces a happens-before relationship between setting the self-reference and clearing it. */ + std::mutex m_selfReferenceLock; + /* Keeps the operation alive while activation is in-progress. Internally, we capture `this` in the function * object that handles the result. If we did not do this, we risk a crash if the user drops their reference * before the future gets completed. */ @@ -5179,6 +5200,9 @@ namespace Aws private: std::promise m_resultPromise; + /* Enforces a happens-before relationship between setting the self-reference and clearing it. */ + std::mutex m_selfReferenceLock; + /* Keeps the operation alive while activation is in-progress. Internally, we capture `this` in the function * object that handles the result. If we did not do this, we risk a crash if the user drops their reference * before the future gets completed. */ @@ -5252,6 +5276,9 @@ namespace Aws private: std::promise m_resultPromise; + /* Enforces a happens-before relationship between setting the self-reference and clearing it. */ + std::mutex m_selfReferenceLock; + /* Keeps the operation alive while activation is in-progress. Internally, we capture `this` in the function * object that handles the result. If we did not do this, we risk a crash if the user drops their reference * before the future gets completed. */ @@ -5395,6 +5422,9 @@ namespace Aws private: std::promise m_resultPromise; + /* Enforces a happens-before relationship between setting the self-reference and clearing it. */ + std::mutex m_selfReferenceLock; + /* Keeps the operation alive while activation is in-progress. Internally, we capture `this` in the function * object that handles the result. If we did not do this, we risk a crash if the user drops their reference * before the future gets completed. */ @@ -5468,6 +5498,9 @@ namespace Aws private: std::promise m_resultPromise; + /* Enforces a happens-before relationship between setting the self-reference and clearing it. */ + std::mutex m_selfReferenceLock; + /* Keeps the operation alive while activation is in-progress. Internally, we capture `this` in the function * object that handles the result. If we did not do this, we risk a crash if the user drops their reference * before the future gets completed. */ @@ -5544,6 +5577,9 @@ namespace Aws private: std::promise m_resultPromise; + /* Enforces a happens-before relationship between setting the self-reference and clearing it. */ + std::mutex m_selfReferenceLock; + /* Keeps the operation alive while activation is in-progress. Internally, we capture `this` in the function * object that handles the result. If we did not do this, we risk a crash if the user drops their reference * before the future gets completed. */ @@ -5617,6 +5653,9 @@ namespace Aws private: std::promise m_resultPromise; + /* Enforces a happens-before relationship between setting the self-reference and clearing it. */ + std::mutex m_selfReferenceLock; + /* Keeps the operation alive while activation is in-progress. Internally, we capture `this` in the function * object that handles the result. If we did not do this, we risk a crash if the user drops their reference * before the future gets completed. */ @@ -5763,6 +5802,9 @@ namespace Aws private: std::promise m_resultPromise; + /* Enforces a happens-before relationship between setting the self-reference and clearing it. */ + std::mutex m_selfReferenceLock; + /* Keeps the operation alive while activation is in-progress. Internally, we capture `this` in the function * object that handles the result. If we did not do this, we risk a crash if the user drops their reference * before the future gets completed. */ @@ -5839,6 +5881,9 @@ namespace Aws private: std::promise m_resultPromise; + /* Enforces a happens-before relationship between setting the self-reference and clearing it. */ + std::mutex m_selfReferenceLock; + /* Keeps the operation alive while activation is in-progress. Internally, we capture `this` in the function * object that handles the result. If we did not do this, we risk a crash if the user drops their reference * before the future gets completed. */ @@ -5915,6 +5960,9 @@ namespace Aws private: std::promise m_resultPromise; + /* Enforces a happens-before relationship between setting the self-reference and clearing it. */ + std::mutex m_selfReferenceLock; + /* Keeps the operation alive while activation is in-progress. Internally, we capture `this` in the function * object that handles the result. If we did not do this, we risk a crash if the user drops their reference * before the future gets completed. */ @@ -5988,6 +6036,9 @@ namespace Aws private: std::promise m_resultPromise; + /* Enforces a happens-before relationship between setting the self-reference and clearing it. */ + std::mutex m_selfReferenceLock; + /* Keeps the operation alive while activation is in-progress. Internally, we capture `this` in the function * object that handles the result. If we did not do this, we risk a crash if the user drops their reference * before the future gets completed. */ @@ -6061,6 +6112,9 @@ namespace Aws private: std::promise m_resultPromise; + /* Enforces a happens-before relationship between setting the self-reference and clearing it. */ + std::mutex m_selfReferenceLock; + /* Keeps the operation alive while activation is in-progress. Internally, we capture `this` in the function * object that handles the result. If we did not do this, we risk a crash if the user drops their reference * before the future gets completed. */ @@ -6134,6 +6188,9 @@ namespace Aws private: std::promise m_resultPromise; + /* Enforces a happens-before relationship between setting the self-reference and clearing it. */ + std::mutex m_selfReferenceLock; + /* Keeps the operation alive while activation is in-progress. Internally, we capture `this` in the function * object that handles the result. If we did not do this, we risk a crash if the user drops their reference * before the future gets completed. */ @@ -6210,6 +6267,9 @@ namespace Aws private: std::promise m_resultPromise; + /* Enforces a happens-before relationship between setting the self-reference and clearing it. */ + std::mutex m_selfReferenceLock; + /* Keeps the operation alive while activation is in-progress. Internally, we capture `this` in the function * object that handles the result. If we did not do this, we risk a crash if the user drops their reference * before the future gets completed. */ @@ -6283,6 +6343,9 @@ namespace Aws private: std::promise m_resultPromise; + /* Enforces a happens-before relationship between setting the self-reference and clearing it. */ + std::mutex m_selfReferenceLock; + /* Keeps the operation alive while activation is in-progress. Internally, we capture `this` in the function * object that handles the result. If we did not do this, we risk a crash if the user drops their reference * before the future gets completed. */ @@ -6356,6 +6419,9 @@ namespace Aws private: std::promise m_resultPromise; + /* Enforces a happens-before relationship between setting the self-reference and clearing it. */ + std::mutex m_selfReferenceLock; + /* Keeps the operation alive while activation is in-progress. Internally, we capture `this` in the function * object that handles the result. If we did not do this, we risk a crash if the user drops their reference * before the future gets completed. */ @@ -6432,6 +6498,9 @@ namespace Aws private: std::promise m_resultPromise; + /* Enforces a happens-before relationship between setting the self-reference and clearing it. */ + std::mutex m_selfReferenceLock; + /* Keeps the operation alive while activation is in-progress. Internally, we capture `this` in the function * object that handles the result. If we did not do this, we risk a crash if the user drops their reference * before the future gets completed. */ @@ -6505,6 +6574,9 @@ namespace Aws private: std::promise m_resultPromise; + /* Enforces a happens-before relationship between setting the self-reference and clearing it. */ + std::mutex m_selfReferenceLock; + /* Keeps the operation alive while activation is in-progress. Internally, we capture `this` in the function * object that handles the result. If we did not do this, we risk a crash if the user drops their reference * before the future gets completed. */ @@ -6581,6 +6653,9 @@ namespace Aws private: std::promise m_resultPromise; + /* Enforces a happens-before relationship between setting the self-reference and clearing it. */ + std::mutex m_selfReferenceLock; + /* Keeps the operation alive while activation is in-progress. Internally, we capture `this` in the function * object that handles the result. If we did not do this, we risk a crash if the user drops their reference * before the future gets completed. */ @@ -6654,6 +6729,9 @@ namespace Aws private: std::promise m_resultPromise; + /* Enforces a happens-before relationship between setting the self-reference and clearing it. */ + std::mutex m_selfReferenceLock; + /* Keeps the operation alive while activation is in-progress. Internally, we capture `this` in the function * object that handles the result. If we did not do this, we risk a crash if the user drops their reference * before the future gets completed. */ @@ -6726,6 +6804,9 @@ namespace Aws private: std::promise m_resultPromise; + /* Enforces a happens-before relationship between setting the self-reference and clearing it. */ + std::mutex m_selfReferenceLock; + /* Keeps the operation alive while activation is in-progress. Internally, we capture `this` in the function * object that handles the result. If we did not do this, we risk a crash if the user drops their reference * before the future gets completed. */ @@ -6801,6 +6882,9 @@ namespace Aws private: std::promise m_resultPromise; + /* Enforces a happens-before relationship between setting the self-reference and clearing it. */ + std::mutex m_selfReferenceLock; + /* Keeps the operation alive while activation is in-progress. Internally, we capture `this` in the function * object that handles the result. If we did not do this, we risk a crash if the user drops their reference * before the future gets completed. */ @@ -6877,6 +6961,9 @@ namespace Aws private: std::promise m_resultPromise; + /* Enforces a happens-before relationship between setting the self-reference and clearing it. */ + std::mutex m_selfReferenceLock; + /* Keeps the operation alive while activation is in-progress. Internally, we capture `this` in the function * object that handles the result. If we did not do this, we risk a crash if the user drops their reference * before the future gets completed. */ @@ -7013,6 +7100,9 @@ namespace Aws private: std::promise m_resultPromise; + /* Enforces a happens-before relationship between setting the self-reference and clearing it. */ + std::mutex m_selfReferenceLock; + /* Keeps the operation alive while activation is in-progress. Internally, we capture `this` in the function * object that handles the result. If we did not do this, we risk a crash if the user drops their reference * before the future gets completed. */ @@ -7088,6 +7178,9 @@ namespace Aws private: std::promise m_resultPromise; + /* Enforces a happens-before relationship between setting the self-reference and clearing it. */ + std::mutex m_selfReferenceLock; + /* Keeps the operation alive while activation is in-progress. Internally, we capture `this` in the function * object that handles the result. If we did not do this, we risk a crash if the user drops their reference * before the future gets completed. */ @@ -7161,6 +7254,9 @@ namespace Aws private: std::promise m_resultPromise; + /* Enforces a happens-before relationship between setting the self-reference and clearing it. */ + std::mutex m_selfReferenceLock; + /* Keeps the operation alive while activation is in-progress. Internally, we capture `this` in the function * object that handles the result. If we did not do this, we risk a crash if the user drops their reference * before the future gets completed. */ @@ -7234,6 +7330,9 @@ namespace Aws private: std::promise m_resultPromise; + /* Enforces a happens-before relationship between setting the self-reference and clearing it. */ + std::mutex m_selfReferenceLock; + /* Keeps the operation alive while activation is in-progress. Internally, we capture `this` in the function * object that handles the result. If we did not do this, we risk a crash if the user drops their reference * before the future gets completed. */ @@ -7309,6 +7408,9 @@ namespace Aws private: std::promise m_resultPromise; + /* Enforces a happens-before relationship between setting the self-reference and clearing it. */ + std::mutex m_selfReferenceLock; + /* Keeps the operation alive while activation is in-progress. Internally, we capture `this` in the function * object that handles the result. If we did not do this, we risk a crash if the user drops their reference * before the future gets completed. */ diff --git a/greengrass_ipc/source/GreengrassCoreIpcModel.cpp b/greengrass_ipc/source/GreengrassCoreIpcModel.cpp index 1bff67566..c39dfddfc 100644 --- a/greengrass_ipc/source/GreengrassCoreIpcModel.cpp +++ b/greengrass_ipc/source/GreengrassCoreIpcModel.cpp @@ -7058,19 +7058,21 @@ namespace Aws OnMessageFlushCallback onMessageFlushCallback) noexcept { bool synchronousSuccess = false; - m_selfReference = shared_from_this(); + std::lock_guard selfReferenceLock(m_selfReferenceLock); auto activateFuture = ClientOperation::Activate( static_cast(&request), std::move(onMessageFlushCallback), [this](TaggedResult &&unmodeledResult) { + std::lock_guard selfReferenceLock(m_selfReferenceLock); m_resultPromise.set_value(SubscribeToIoTCoreResult(std::move(unmodeledResult))); m_selfReference = nullptr; }, synchronousSuccess); - if (!synchronousSuccess) + if (synchronousSuccess) { - m_selfReference = nullptr; + m_selfReference = shared_from_this(); + ; } return activateFuture; } @@ -7136,19 +7138,21 @@ namespace Aws OnMessageFlushCallback onMessageFlushCallback) noexcept { bool synchronousSuccess = false; - m_selfReference = shared_from_this(); + std::lock_guard selfReferenceLock(m_selfReferenceLock); auto activateFuture = ClientOperation::Activate( static_cast(&request), std::move(onMessageFlushCallback), [this](TaggedResult &&unmodeledResult) { + std::lock_guard selfReferenceLock(m_selfReferenceLock); m_resultPromise.set_value(ResumeComponentResult(std::move(unmodeledResult))); m_selfReference = nullptr; }, synchronousSuccess); - if (!synchronousSuccess) + if (synchronousSuccess) { - m_selfReference = nullptr; + m_selfReference = shared_from_this(); + ; } return activateFuture; } @@ -7214,19 +7218,21 @@ namespace Aws OnMessageFlushCallback onMessageFlushCallback) noexcept { bool synchronousSuccess = false; - m_selfReference = shared_from_this(); + std::lock_guard selfReferenceLock(m_selfReferenceLock); auto activateFuture = ClientOperation::Activate( static_cast(&request), std::move(onMessageFlushCallback), [this](TaggedResult &&unmodeledResult) { + std::lock_guard selfReferenceLock(m_selfReferenceLock); m_resultPromise.set_value(PublishToIoTCoreResult(std::move(unmodeledResult))); m_selfReference = nullptr; }, synchronousSuccess); - if (!synchronousSuccess) + if (synchronousSuccess) { - m_selfReference = nullptr; + m_selfReference = shared_from_this(); + ; } return activateFuture; } @@ -7323,19 +7329,21 @@ namespace Aws OnMessageFlushCallback onMessageFlushCallback) noexcept { bool synchronousSuccess = false; - m_selfReference = shared_from_this(); + std::lock_guard selfReferenceLock(m_selfReferenceLock); auto activateFuture = ClientOperation::Activate( static_cast(&request), std::move(onMessageFlushCallback), [this](TaggedResult &&unmodeledResult) { + std::lock_guard selfReferenceLock(m_selfReferenceLock); m_resultPromise.set_value(SubscribeToConfigurationUpdateResult(std::move(unmodeledResult))); m_selfReference = nullptr; }, synchronousSuccess); - if (!synchronousSuccess) + if (synchronousSuccess) { - m_selfReference = nullptr; + m_selfReference = shared_from_this(); + ; } return activateFuture; } @@ -7401,19 +7409,21 @@ namespace Aws OnMessageFlushCallback onMessageFlushCallback) noexcept { bool synchronousSuccess = false; - m_selfReference = shared_from_this(); + std::lock_guard selfReferenceLock(m_selfReferenceLock); auto activateFuture = ClientOperation::Activate( static_cast(&request), std::move(onMessageFlushCallback), [this](TaggedResult &&unmodeledResult) { + std::lock_guard selfReferenceLock(m_selfReferenceLock); m_resultPromise.set_value(DeleteThingShadowResult(std::move(unmodeledResult))); m_selfReference = nullptr; }, synchronousSuccess); - if (!synchronousSuccess) + if (synchronousSuccess) { - m_selfReference = nullptr; + m_selfReference = shared_from_this(); + ; } return activateFuture; } @@ -7479,19 +7489,21 @@ namespace Aws OnMessageFlushCallback onMessageFlushCallback) noexcept { bool synchronousSuccess = false; - m_selfReference = shared_from_this(); + std::lock_guard selfReferenceLock(m_selfReferenceLock); auto activateFuture = ClientOperation::Activate( static_cast(&request), std::move(onMessageFlushCallback), [this](TaggedResult &&unmodeledResult) { + std::lock_guard selfReferenceLock(m_selfReferenceLock); m_resultPromise.set_value(PutComponentMetricResult(std::move(unmodeledResult))); m_selfReference = nullptr; }, synchronousSuccess); - if (!synchronousSuccess) + if (synchronousSuccess) { - m_selfReference = nullptr; + m_selfReference = shared_from_this(); + ; } return activateFuture; } @@ -7557,19 +7569,21 @@ namespace Aws OnMessageFlushCallback onMessageFlushCallback) noexcept { bool synchronousSuccess = false; - m_selfReference = shared_from_this(); + std::lock_guard selfReferenceLock(m_selfReferenceLock); auto activateFuture = ClientOperation::Activate( static_cast(&request), std::move(onMessageFlushCallback), [this](TaggedResult &&unmodeledResult) { + std::lock_guard selfReferenceLock(m_selfReferenceLock); m_resultPromise.set_value(DeferComponentUpdateResult(std::move(unmodeledResult))); m_selfReference = nullptr; }, synchronousSuccess); - if (!synchronousSuccess) + if (synchronousSuccess) { - m_selfReference = nullptr; + m_selfReference = shared_from_this(); + ; } return activateFuture; } @@ -7663,20 +7677,22 @@ namespace Aws OnMessageFlushCallback onMessageFlushCallback) noexcept { bool synchronousSuccess = false; - m_selfReference = shared_from_this(); + std::lock_guard selfReferenceLock(m_selfReferenceLock); auto activateFuture = ClientOperation::Activate( static_cast(&request), std::move(onMessageFlushCallback), [this](TaggedResult &&unmodeledResult) { + std::lock_guard selfReferenceLock(m_selfReferenceLock); m_resultPromise.set_value( SubscribeToValidateConfigurationUpdatesResult(std::move(unmodeledResult))); m_selfReference = nullptr; }, synchronousSuccess); - if (!synchronousSuccess) + if (synchronousSuccess) { - m_selfReference = nullptr; + m_selfReference = shared_from_this(); + ; } return activateFuture; } @@ -7742,19 +7758,21 @@ namespace Aws OnMessageFlushCallback onMessageFlushCallback) noexcept { bool synchronousSuccess = false; - m_selfReference = shared_from_this(); + std::lock_guard selfReferenceLock(m_selfReferenceLock); auto activateFuture = ClientOperation::Activate( static_cast(&request), std::move(onMessageFlushCallback), [this](TaggedResult &&unmodeledResult) { + std::lock_guard selfReferenceLock(m_selfReferenceLock); m_resultPromise.set_value(GetConfigurationResult(std::move(unmodeledResult))); m_selfReference = nullptr; }, synchronousSuccess); - if (!synchronousSuccess) + if (synchronousSuccess) { - m_selfReference = nullptr; + m_selfReference = shared_from_this(); + ; } return activateFuture; } @@ -7856,19 +7874,21 @@ namespace Aws OnMessageFlushCallback onMessageFlushCallback) noexcept { bool synchronousSuccess = false; - m_selfReference = shared_from_this(); + std::lock_guard selfReferenceLock(m_selfReferenceLock); auto activateFuture = ClientOperation::Activate( static_cast(&request), std::move(onMessageFlushCallback), [this](TaggedResult &&unmodeledResult) { + std::lock_guard selfReferenceLock(m_selfReferenceLock); m_resultPromise.set_value(SubscribeToTopicResult(std::move(unmodeledResult))); m_selfReference = nullptr; }, synchronousSuccess); - if (!synchronousSuccess) + if (synchronousSuccess) { - m_selfReference = nullptr; + m_selfReference = shared_from_this(); + ; } return activateFuture; } @@ -7934,19 +7954,21 @@ namespace Aws OnMessageFlushCallback onMessageFlushCallback) noexcept { bool synchronousSuccess = false; - m_selfReference = shared_from_this(); + std::lock_guard selfReferenceLock(m_selfReferenceLock); auto activateFuture = ClientOperation::Activate( static_cast(&request), std::move(onMessageFlushCallback), [this](TaggedResult &&unmodeledResult) { + std::lock_guard selfReferenceLock(m_selfReferenceLock); m_resultPromise.set_value(GetComponentDetailsResult(std::move(unmodeledResult))); m_selfReference = nullptr; }, synchronousSuccess); - if (!synchronousSuccess) + if (synchronousSuccess) { - m_selfReference = nullptr; + m_selfReference = shared_from_this(); + ; } return activateFuture; } @@ -8012,19 +8034,21 @@ namespace Aws OnMessageFlushCallback onMessageFlushCallback) noexcept { bool synchronousSuccess = false; - m_selfReference = shared_from_this(); + std::lock_guard selfReferenceLock(m_selfReferenceLock); auto activateFuture = ClientOperation::Activate( static_cast(&request), std::move(onMessageFlushCallback), [this](TaggedResult &&unmodeledResult) { + std::lock_guard selfReferenceLock(m_selfReferenceLock); m_resultPromise.set_value(GetClientDeviceAuthTokenResult(std::move(unmodeledResult))); m_selfReference = nullptr; }, synchronousSuccess); - if (!synchronousSuccess) + if (synchronousSuccess) { - m_selfReference = nullptr; + m_selfReference = shared_from_this(); + ; } return activateFuture; } @@ -8090,19 +8114,21 @@ namespace Aws OnMessageFlushCallback onMessageFlushCallback) noexcept { bool synchronousSuccess = false; - m_selfReference = shared_from_this(); + std::lock_guard selfReferenceLock(m_selfReferenceLock); auto activateFuture = ClientOperation::Activate( static_cast(&request), std::move(onMessageFlushCallback), [this](TaggedResult &&unmodeledResult) { + std::lock_guard selfReferenceLock(m_selfReferenceLock); m_resultPromise.set_value(PublishToTopicResult(std::move(unmodeledResult))); m_selfReference = nullptr; }, synchronousSuccess); - if (!synchronousSuccess) + if (synchronousSuccess) { - m_selfReference = nullptr; + m_selfReference = shared_from_this(); + ; } return activateFuture; } @@ -8205,19 +8231,21 @@ namespace Aws OnMessageFlushCallback onMessageFlushCallback) noexcept { bool synchronousSuccess = false; - m_selfReference = shared_from_this(); + std::lock_guard selfReferenceLock(m_selfReferenceLock); auto activateFuture = ClientOperation::Activate( static_cast(&request), std::move(onMessageFlushCallback), [this](TaggedResult &&unmodeledResult) { + std::lock_guard selfReferenceLock(m_selfReferenceLock); m_resultPromise.set_value(SubscribeToCertificateUpdatesResult(std::move(unmodeledResult))); m_selfReference = nullptr; }, synchronousSuccess); - if (!synchronousSuccess) + if (synchronousSuccess) { - m_selfReference = nullptr; + m_selfReference = shared_from_this(); + ; } return activateFuture; } @@ -8283,19 +8311,21 @@ namespace Aws OnMessageFlushCallback onMessageFlushCallback) noexcept { bool synchronousSuccess = false; - m_selfReference = shared_from_this(); + std::lock_guard selfReferenceLock(m_selfReferenceLock); auto activateFuture = ClientOperation::Activate( static_cast(&request), std::move(onMessageFlushCallback), [this](TaggedResult &&unmodeledResult) { + std::lock_guard selfReferenceLock(m_selfReferenceLock); m_resultPromise.set_value(VerifyClientDeviceIdentityResult(std::move(unmodeledResult))); m_selfReference = nullptr; }, synchronousSuccess); - if (!synchronousSuccess) + if (synchronousSuccess) { - m_selfReference = nullptr; + m_selfReference = shared_from_this(); + ; } return activateFuture; } @@ -8361,19 +8391,21 @@ namespace Aws OnMessageFlushCallback onMessageFlushCallback) noexcept { bool synchronousSuccess = false; - m_selfReference = shared_from_this(); + std::lock_guard selfReferenceLock(m_selfReferenceLock); auto activateFuture = ClientOperation::Activate( static_cast(&request), std::move(onMessageFlushCallback), [this](TaggedResult &&unmodeledResult) { + std::lock_guard selfReferenceLock(m_selfReferenceLock); m_resultPromise.set_value(AuthorizeClientDeviceActionResult(std::move(unmodeledResult))); m_selfReference = nullptr; }, synchronousSuccess); - if (!synchronousSuccess) + if (synchronousSuccess) { - m_selfReference = nullptr; + m_selfReference = shared_from_this(); + ; } return activateFuture; } @@ -8439,19 +8471,21 @@ namespace Aws OnMessageFlushCallback onMessageFlushCallback) noexcept { bool synchronousSuccess = false; - m_selfReference = shared_from_this(); + std::lock_guard selfReferenceLock(m_selfReferenceLock); auto activateFuture = ClientOperation::Activate( static_cast(&request), std::move(onMessageFlushCallback), [this](TaggedResult &&unmodeledResult) { + std::lock_guard selfReferenceLock(m_selfReferenceLock); m_resultPromise.set_value(ListComponentsResult(std::move(unmodeledResult))); m_selfReference = nullptr; }, synchronousSuccess); - if (!synchronousSuccess) + if (synchronousSuccess) { - m_selfReference = nullptr; + m_selfReference = shared_from_this(); + ; } return activateFuture; } @@ -8517,19 +8551,21 @@ namespace Aws OnMessageFlushCallback onMessageFlushCallback) noexcept { bool synchronousSuccess = false; - m_selfReference = shared_from_this(); + std::lock_guard selfReferenceLock(m_selfReferenceLock); auto activateFuture = ClientOperation::Activate( static_cast(&request), std::move(onMessageFlushCallback), [this](TaggedResult &&unmodeledResult) { + std::lock_guard selfReferenceLock(m_selfReferenceLock); m_resultPromise.set_value(CreateDebugPasswordResult(std::move(unmodeledResult))); m_selfReference = nullptr; }, synchronousSuccess); - if (!synchronousSuccess) + if (synchronousSuccess) { - m_selfReference = nullptr; + m_selfReference = shared_from_this(); + ; } return activateFuture; } @@ -8595,19 +8631,21 @@ namespace Aws OnMessageFlushCallback onMessageFlushCallback) noexcept { bool synchronousSuccess = false; - m_selfReference = shared_from_this(); + std::lock_guard selfReferenceLock(m_selfReferenceLock); auto activateFuture = ClientOperation::Activate( static_cast(&request), std::move(onMessageFlushCallback), [this](TaggedResult &&unmodeledResult) { + std::lock_guard selfReferenceLock(m_selfReferenceLock); m_resultPromise.set_value(GetThingShadowResult(std::move(unmodeledResult))); m_selfReference = nullptr; }, synchronousSuccess); - if (!synchronousSuccess) + if (synchronousSuccess) { - m_selfReference = nullptr; + m_selfReference = shared_from_this(); + ; } return activateFuture; } @@ -8674,19 +8712,21 @@ namespace Aws OnMessageFlushCallback onMessageFlushCallback) noexcept { bool synchronousSuccess = false; - m_selfReference = shared_from_this(); + std::lock_guard selfReferenceLock(m_selfReferenceLock); auto activateFuture = ClientOperation::Activate( static_cast(&request), std::move(onMessageFlushCallback), [this](TaggedResult &&unmodeledResult) { + std::lock_guard selfReferenceLock(m_selfReferenceLock); m_resultPromise.set_value(SendConfigurationValidityReportResult(std::move(unmodeledResult))); m_selfReference = nullptr; }, synchronousSuccess); - if (!synchronousSuccess) + if (synchronousSuccess) { - m_selfReference = nullptr; + m_selfReference = shared_from_this(); + ; } return activateFuture; } @@ -8752,19 +8792,21 @@ namespace Aws OnMessageFlushCallback onMessageFlushCallback) noexcept { bool synchronousSuccess = false; - m_selfReference = shared_from_this(); + std::lock_guard selfReferenceLock(m_selfReferenceLock); auto activateFuture = ClientOperation::Activate( static_cast(&request), std::move(onMessageFlushCallback), [this](TaggedResult &&unmodeledResult) { + std::lock_guard selfReferenceLock(m_selfReferenceLock); m_resultPromise.set_value(UpdateThingShadowResult(std::move(unmodeledResult))); m_selfReference = nullptr; }, synchronousSuccess); - if (!synchronousSuccess) + if (synchronousSuccess) { - m_selfReference = nullptr; + m_selfReference = shared_from_this(); + ; } return activateFuture; } @@ -8830,19 +8872,21 @@ namespace Aws OnMessageFlushCallback onMessageFlushCallback) noexcept { bool synchronousSuccess = false; - m_selfReference = shared_from_this(); + std::lock_guard selfReferenceLock(m_selfReferenceLock); auto activateFuture = ClientOperation::Activate( static_cast(&request), std::move(onMessageFlushCallback), [this](TaggedResult &&unmodeledResult) { + std::lock_guard selfReferenceLock(m_selfReferenceLock); m_resultPromise.set_value(UpdateConfigurationResult(std::move(unmodeledResult))); m_selfReference = nullptr; }, synchronousSuccess); - if (!synchronousSuccess) + if (synchronousSuccess) { - m_selfReference = nullptr; + m_selfReference = shared_from_this(); + ; } return activateFuture; } @@ -8908,19 +8952,21 @@ namespace Aws OnMessageFlushCallback onMessageFlushCallback) noexcept { bool synchronousSuccess = false; - m_selfReference = shared_from_this(); + std::lock_guard selfReferenceLock(m_selfReferenceLock); auto activateFuture = ClientOperation::Activate( static_cast(&request), std::move(onMessageFlushCallback), [this](TaggedResult &&unmodeledResult) { + std::lock_guard selfReferenceLock(m_selfReferenceLock); m_resultPromise.set_value(ValidateAuthorizationTokenResult(std::move(unmodeledResult))); m_selfReference = nullptr; }, synchronousSuccess); - if (!synchronousSuccess) + if (synchronousSuccess) { - m_selfReference = nullptr; + m_selfReference = shared_from_this(); + ; } return activateFuture; } @@ -8986,19 +9032,21 @@ namespace Aws OnMessageFlushCallback onMessageFlushCallback) noexcept { bool synchronousSuccess = false; - m_selfReference = shared_from_this(); + std::lock_guard selfReferenceLock(m_selfReferenceLock); auto activateFuture = ClientOperation::Activate( static_cast(&request), std::move(onMessageFlushCallback), [this](TaggedResult &&unmodeledResult) { + std::lock_guard selfReferenceLock(m_selfReferenceLock); m_resultPromise.set_value(RestartComponentResult(std::move(unmodeledResult))); m_selfReference = nullptr; }, synchronousSuccess); - if (!synchronousSuccess) + if (synchronousSuccess) { - m_selfReference = nullptr; + m_selfReference = shared_from_this(); + ; } return activateFuture; } @@ -9064,19 +9112,21 @@ namespace Aws OnMessageFlushCallback onMessageFlushCallback) noexcept { bool synchronousSuccess = false; - m_selfReference = shared_from_this(); + std::lock_guard selfReferenceLock(m_selfReferenceLock); auto activateFuture = ClientOperation::Activate( static_cast(&request), std::move(onMessageFlushCallback), [this](TaggedResult &&unmodeledResult) { + std::lock_guard selfReferenceLock(m_selfReferenceLock); m_resultPromise.set_value(GetLocalDeploymentStatusResult(std::move(unmodeledResult))); m_selfReference = nullptr; }, synchronousSuccess); - if (!synchronousSuccess) + if (synchronousSuccess) { - m_selfReference = nullptr; + m_selfReference = shared_from_this(); + ; } return activateFuture; } @@ -9142,19 +9192,21 @@ namespace Aws OnMessageFlushCallback onMessageFlushCallback) noexcept { bool synchronousSuccess = false; - m_selfReference = shared_from_this(); + std::lock_guard selfReferenceLock(m_selfReferenceLock); auto activateFuture = ClientOperation::Activate( static_cast(&request), std::move(onMessageFlushCallback), [this](TaggedResult &&unmodeledResult) { + std::lock_guard selfReferenceLock(m_selfReferenceLock); m_resultPromise.set_value(GetSecretValueResult(std::move(unmodeledResult))); m_selfReference = nullptr; }, synchronousSuccess); - if (!synchronousSuccess) + if (synchronousSuccess) { - m_selfReference = nullptr; + m_selfReference = shared_from_this(); + ; } return activateFuture; } @@ -9219,19 +9271,21 @@ namespace Aws OnMessageFlushCallback onMessageFlushCallback) noexcept { bool synchronousSuccess = false; - m_selfReference = shared_from_this(); + std::lock_guard selfReferenceLock(m_selfReferenceLock); auto activateFuture = ClientOperation::Activate( static_cast(&request), std::move(onMessageFlushCallback), [this](TaggedResult &&unmodeledResult) { + std::lock_guard selfReferenceLock(m_selfReferenceLock); m_resultPromise.set_value(UpdateStateResult(std::move(unmodeledResult))); m_selfReference = nullptr; }, synchronousSuccess); - if (!synchronousSuccess) + if (synchronousSuccess) { - m_selfReference = nullptr; + m_selfReference = shared_from_this(); + ; } return activateFuture; } @@ -9297,19 +9351,21 @@ namespace Aws OnMessageFlushCallback onMessageFlushCallback) noexcept { bool synchronousSuccess = false; - m_selfReference = shared_from_this(); + std::lock_guard selfReferenceLock(m_selfReferenceLock); auto activateFuture = ClientOperation::Activate( static_cast(&request), std::move(onMessageFlushCallback), [this](TaggedResult &&unmodeledResult) { + std::lock_guard selfReferenceLock(m_selfReferenceLock); m_resultPromise.set_value(CancelLocalDeploymentResult(std::move(unmodeledResult))); m_selfReference = nullptr; }, synchronousSuccess); - if (!synchronousSuccess) + if (synchronousSuccess) { - m_selfReference = nullptr; + m_selfReference = shared_from_this(); + ; } return activateFuture; } @@ -9375,19 +9431,21 @@ namespace Aws OnMessageFlushCallback onMessageFlushCallback) noexcept { bool synchronousSuccess = false; - m_selfReference = shared_from_this(); + std::lock_guard selfReferenceLock(m_selfReferenceLock); auto activateFuture = ClientOperation::Activate( static_cast(&request), std::move(onMessageFlushCallback), [this](TaggedResult &&unmodeledResult) { + std::lock_guard selfReferenceLock(m_selfReferenceLock); m_resultPromise.set_value(ListNamedShadowsForThingResult(std::move(unmodeledResult))); m_selfReference = nullptr; }, synchronousSuccess); - if (!synchronousSuccess) + if (synchronousSuccess) { - m_selfReference = nullptr; + m_selfReference = shared_from_this(); + ; } return activateFuture; } @@ -9484,19 +9542,21 @@ namespace Aws OnMessageFlushCallback onMessageFlushCallback) noexcept { bool synchronousSuccess = false; - m_selfReference = shared_from_this(); + std::lock_guard selfReferenceLock(m_selfReferenceLock); auto activateFuture = ClientOperation::Activate( static_cast(&request), std::move(onMessageFlushCallback), [this](TaggedResult &&unmodeledResult) { + std::lock_guard selfReferenceLock(m_selfReferenceLock); m_resultPromise.set_value(SubscribeToComponentUpdatesResult(std::move(unmodeledResult))); m_selfReference = nullptr; }, synchronousSuccess); - if (!synchronousSuccess) + if (synchronousSuccess) { - m_selfReference = nullptr; + m_selfReference = shared_from_this(); + ; } return activateFuture; } @@ -9562,19 +9622,21 @@ namespace Aws OnMessageFlushCallback onMessageFlushCallback) noexcept { bool synchronousSuccess = false; - m_selfReference = shared_from_this(); + std::lock_guard selfReferenceLock(m_selfReferenceLock); auto activateFuture = ClientOperation::Activate( static_cast(&request), std::move(onMessageFlushCallback), [this](TaggedResult &&unmodeledResult) { + std::lock_guard selfReferenceLock(m_selfReferenceLock); m_resultPromise.set_value(ListLocalDeploymentsResult(std::move(unmodeledResult))); m_selfReference = nullptr; }, synchronousSuccess); - if (!synchronousSuccess) + if (synchronousSuccess) { - m_selfReference = nullptr; + m_selfReference = shared_from_this(); + ; } return activateFuture; } @@ -9640,19 +9702,21 @@ namespace Aws OnMessageFlushCallback onMessageFlushCallback) noexcept { bool synchronousSuccess = false; - m_selfReference = shared_from_this(); + std::lock_guard selfReferenceLock(m_selfReferenceLock); auto activateFuture = ClientOperation::Activate( static_cast(&request), std::move(onMessageFlushCallback), [this](TaggedResult &&unmodeledResult) { + std::lock_guard selfReferenceLock(m_selfReferenceLock); m_resultPromise.set_value(StopComponentResult(std::move(unmodeledResult))); m_selfReference = nullptr; }, synchronousSuccess); - if (!synchronousSuccess) + if (synchronousSuccess) { - m_selfReference = nullptr; + m_selfReference = shared_from_this(); + ; } return activateFuture; } @@ -9718,19 +9782,21 @@ namespace Aws OnMessageFlushCallback onMessageFlushCallback) noexcept { bool synchronousSuccess = false; - m_selfReference = shared_from_this(); + std::lock_guard selfReferenceLock(m_selfReferenceLock); auto activateFuture = ClientOperation::Activate( static_cast(&request), std::move(onMessageFlushCallback), [this](TaggedResult &&unmodeledResult) { + std::lock_guard selfReferenceLock(m_selfReferenceLock); m_resultPromise.set_value(PauseComponentResult(std::move(unmodeledResult))); m_selfReference = nullptr; }, synchronousSuccess); - if (!synchronousSuccess) + if (synchronousSuccess) { - m_selfReference = nullptr; + m_selfReference = shared_from_this(); + ; } return activateFuture; } @@ -9796,19 +9862,21 @@ namespace Aws OnMessageFlushCallback onMessageFlushCallback) noexcept { bool synchronousSuccess = false; - m_selfReference = shared_from_this(); + std::lock_guard selfReferenceLock(m_selfReferenceLock); auto activateFuture = ClientOperation::Activate( static_cast(&request), std::move(onMessageFlushCallback), [this](TaggedResult &&unmodeledResult) { + std::lock_guard selfReferenceLock(m_selfReferenceLock); m_resultPromise.set_value(CreateLocalDeploymentResult(std::move(unmodeledResult))); m_selfReference = nullptr; }, synchronousSuccess); - if (!synchronousSuccess) + if (synchronousSuccess) { - m_selfReference = nullptr; + m_selfReference = shared_from_this(); + ; } return activateFuture; } From a35c8e6ddeaba5b963769776d30e6df4631c9077 Mon Sep 17 00:00:00 2001 From: Bret Ambrose Date: Mon, 9 Jun 2025 13:47:31 -0700 Subject: [PATCH 41/43] Remove double release complexity --- eventstream_rpc/source/EventStreamClient.cpp | 270 ++++-------------- .../tests/EventStreamClientTest.cpp | 21 +- 2 files changed, 66 insertions(+), 225 deletions(-) diff --git a/eventstream_rpc/source/EventStreamClient.cpp b/eventstream_rpc/source/EventStreamClient.cpp index 196370a34..5a395025c 100644 --- a/eventstream_rpc/source/EventStreamClient.cpp +++ b/eventstream_rpc/source/EventStreamClient.cpp @@ -12,6 +12,7 @@ #include #include +#include constexpr auto EVENTSTREAM_VERSION_HEADER = ":version"; constexpr auto EVENTSTREAM_VERSION_STRING = "0.1.0"; @@ -23,34 +24,16 @@ namespace Aws { namespace Eventstreamrpc { - static void s_deleteMessageCallbackContainer(void *); - /* - * This structure has evolved into a more complex form in order to support some additional requirements - * in the eventstream refactor. - * - * In order to maintain behavioral compatibility while removing blocking destruction, we need to be able to - * synchronously "cancel" pending callbacks/completions at the C++ level. The easiest way to accomplish - * this is by ref-counting the structure (so both the C++ and C continuations can reference it) and using - * a lock + completion state to control whether or not the completion actually happens (first one wins). - * - * Because C callbacks are just function pointers, using shared_ptrs is difficult/awkward. So instead we - * just use the C pattern of an internal ref-count that deletes on dropping to zero. - * - * We also want to support tracking the whole set of pending callbacks so that we can cancel them all - * at once while still efficiently completing then one-at-a-time, hence the intrusive linked list node. The - * linked list node is not always used (for example, the connect message callback is not added to any list). + * TODO document */ class OnMessageFlushCallbackContainer { public: OnMessageFlushCallbackContainer(Crt::Allocator *allocator, OnMessageFlushCallback &&flushCallback) - : m_allocator(allocator), m_refCount({}), m_sharedState({}) + : m_allocator(allocator) { - aws_ref_count_init(&m_refCount, this, s_deleteMessageCallbackContainer); - aws_ref_count_acquire(&m_refCount); // always start at 2, one for C++, one for C - m_sharedState.m_state = CallbackState::Incomplete; - m_sharedState.m_onMessageFlushCallback = std::move(flushCallback); + m_onMessageFlushCallback = std::move(flushCallback); } OnMessageFlushCallbackContainer( @@ -59,7 +42,7 @@ namespace Aws std::promise &&flushPromise) : OnMessageFlushCallbackContainer(allocator, std::move(flushCallback)) { - m_sharedState.m_onFlushPromise = std::move(flushPromise); + m_onFlushPromise = std::move(flushPromise); } ~OnMessageFlushCallbackContainer() = default; @@ -71,83 +54,36 @@ namespace Aws return; } - bool performCallback = false; - OnMessageFlushCallback onMessageFlushCallback; - std::promise onFlushPromise; + if (error.crtError != AWS_ERROR_SUCCESS) { - std::lock_guard lock(callback->m_sharedStateLock); - - if (callback->m_sharedState.m_state == CallbackState::Incomplete) - { - performCallback = true; - callback->m_sharedState.m_state = CallbackState::Finished; - onMessageFlushCallback = std::move(callback->m_sharedState.m_onMessageFlushCallback); - onFlushPromise = std::move(callback->m_sharedState.m_onFlushPromise); - } + AWS_LOGF_ERROR( + AWS_LS_EVENT_STREAM_RPC_CLIENT, + "A CRT error occurred while attempting to send a message: %s", + Crt::ErrorDebugString(error.crtError)); } - if (performCallback) + callback->m_onFlushPromise.set_value(error); + if (callback->m_onMessageFlushCallback) { - if (error.crtError != AWS_ERROR_SUCCESS) - { - AWS_LOGF_ERROR( - AWS_LS_EVENT_STREAM_RPC_CLIENT, - "A CRT error occurred while attempting to send a message: %s", - Crt::ErrorDebugString(error.crtError)); - } - onFlushPromise.set_value(error); - if (onMessageFlushCallback) - { - onMessageFlushCallback(error.crtError); - } - } - } - - static void Release(OnMessageFlushCallbackContainer *callback) - { - if (callback == nullptr) - { - return; + callback->m_onMessageFlushCallback(error.crtError); } - aws_ref_count_release(&callback->m_refCount); + Aws::Crt::Delete(callback, callback->m_allocator); } - Aws::Crt::Allocator *GetAllocator() const { return m_allocator; } - private: - enum class CallbackState - { - Incomplete, - Finished - }; - Crt::Allocator *m_allocator; - struct aws_ref_count m_refCount; - std::mutex m_sharedStateLock; - struct - { - CallbackState m_state; - OnMessageFlushCallback m_onMessageFlushCallback; - std::promise m_onFlushPromise; - } m_sharedState; + OnMessageFlushCallback m_onMessageFlushCallback; + std::promise m_onFlushPromise; }; - static void s_deleteMessageCallbackContainer(void *object) - { - auto container = reinterpret_cast(object); - - Aws::Crt::Delete(container, container->GetAllocator()); - } - static void s_protocolMessageCallback(int errorCode, void *userData) noexcept { auto *callbackData = static_cast(userData); auto rpcStatus = (errorCode == AWS_ERROR_SUCCESS) ? EVENT_STREAM_RPC_SUCCESS : EVENT_STREAM_RPC_CRT_ERROR; OnMessageFlushCallbackContainer::Complete(callbackData, {rpcStatus, errorCode}); - OnMessageFlushCallbackContainer::Release(callbackData); } MessageAmendment::MessageAmendment(Crt::Allocator *allocator) noexcept @@ -490,10 +426,7 @@ namespace Aws * 1. No callback is made while a lock is held. We perform callbacks by having a transactional callback context * that is moved out of shared state (under the lock), but the callback context does not perform its work until * the lock is released. - * 2. No destructor blocking or synchronization. In order to provide the best behavioral backwards - * compatibility, we "synthesize" the callbacks that would occur at destruction when we kick off the async - * cleanup process. When the asynchronous events occur that would normally trigger a callback occur, we ignore - * them via the has_shut_down flag. + * 2. Minimal destructor blocking. * 3. A self-reference (via shared_ptr member) guarantees the binding impl stays alive longer than the C * objects. The public binding object also keeps a shared_ptr to the impl, so final destruction only occurs * once both the public binding object's destructor has run and no C connection object is still alive. @@ -567,18 +500,27 @@ namespace Aws { if (m_error.crtError != AWS_ERROR_SUCCESS) { - (void)m_errorCallback(m_error); + if (m_errorCallback) + { + (void)m_errorCallback(m_error); + } } else { - m_connectionSuccessCallback(); + if (m_connectionSuccessCallback) + { + m_connectionSuccessCallback(); + } } m_connectPromise.set_value(m_error); break; } case ConnectionCallbackActionType::DisconnectionCallback: { - m_disconnectionCallback(m_error); + if (m_disconnectionCallback) + { + m_disconnectionCallback(m_error); + } break; } default: @@ -651,10 +593,9 @@ namespace Aws bool IsOpen() const noexcept; - void Shutdown() noexcept; + std::future Shutdown() noexcept; private: - void CloseInternal(bool isShutdown) noexcept; void MoveToDisconnected(RpcError error) noexcept; int SendConnectMessage(OnMessageFlushCallback &&onMessageFlushCallback) noexcept; @@ -676,6 +617,7 @@ namespace Aws std::shared_ptr m_selfReference; aws_client_bootstrap *m_bootstrap; aws_event_loop *m_eventLoop; + std::promise m_terminatedPromise; std::mutex m_sharedStateLock; struct @@ -684,8 +626,6 @@ namespace Aws ClientState m_currentState; ClientState m_desiredState; ConnectionCallbackContext m_callbackContext; - OnMessageFlushCallbackContainer *m_connectFlushContainer; - bool m_hasShutDown; } m_sharedState; static void s_onConnectionShutdown( @@ -707,13 +647,14 @@ namespace Aws ClientConnectionImpl::ClientConnectionImpl(Crt::Allocator *allocator, aws_client_bootstrap *bootstrap) noexcept : m_allocator(allocator), m_lifecycleHandler(nullptr), m_connectMessageAmender(nullptr), m_bootstrap(aws_client_bootstrap_acquire(bootstrap)), m_eventLoop(nullptr), - m_sharedState{nullptr, ClientState::Disconnected, ClientState::Disconnected, {}, nullptr, false} + m_sharedState{nullptr, ClientState::Disconnected, ClientState::Disconnected, {}} { m_eventLoop = aws_event_loop_group_get_next_loop(bootstrap->event_loop_group); } ClientConnectionImpl::~ClientConnectionImpl() noexcept { + m_terminatedPromise.set_value(); aws_client_bootstrap_release(m_bootstrap); } @@ -759,11 +700,10 @@ namespace Aws m_sharedState.m_currentState = ClientState::Disconnected; m_sharedState.m_desiredState = ClientState::Disconnected; m_sharedState.m_callbackContext.SetError(error); + + // the clear shared task has a shared_ptr; this will not dec to zero m_selfReference = nullptr; - if (!m_sharedState.m_hasShutDown) - { - localContext = std::move(m_sharedState.m_callbackContext); - } + localContext = std::move(m_sharedState.m_callbackContext); m_sharedState.m_callbackContext = {}; } @@ -771,52 +711,36 @@ namespace Aws aws_event_loop_schedule_task_now(m_eventLoop, &clearSharedTask->m_task); } - void ClientConnectionImpl::CloseInternal(bool isShutdown) noexcept + void ClientConnectionImpl::Close() noexcept { - ConnectionCallbackContext localContext = {}; - OnMessageFlushCallbackContainer *connectFlushContainer = nullptr; + struct aws_event_stream_rpc_client_connection *close_connection = nullptr; { std::lock_guard lock(m_sharedStateLock); m_sharedState.m_desiredState = ClientState::Disconnected; m_sharedState.m_callbackContext.SetError( {EVENT_STREAM_RPC_CONNECTION_CLOSED, AWS_ERROR_EVENT_STREAM_RPC_CONNECTION_CLOSED}); - if (isShutdown) - { - m_sharedState.m_hasShutDown = true; - } if (m_sharedState.m_currentState == ClientState::Connected || m_sharedState.m_currentState == ClientState::PendingConnack) { m_sharedState.m_currentState = ClientState::Disconnecting; - aws_event_stream_rpc_client_connection_close( - m_sharedState.m_underlyingConnection, AWS_ERROR_EVENT_STREAM_RPC_CONNECTION_CLOSED); - } - - if (isShutdown) - { - localContext = std::move(m_sharedState.m_callbackContext); - connectFlushContainer = m_sharedState.m_connectFlushContainer; - m_sharedState.m_connectFlushContainer = nullptr; + close_connection = m_sharedState.m_underlyingConnection; + aws_event_stream_rpc_client_connection_acquire(close_connection); } } - localContext.InvokeCallbacks(); - - OnMessageFlushCallbackContainer::Complete( - connectFlushContainer, - {EVENT_STREAM_RPC_CONNECTION_CLOSED, AWS_ERROR_EVENT_STREAM_RPC_CONNECTION_CLOSED}); - OnMessageFlushCallbackContainer::Release(connectFlushContainer); - } - - void ClientConnectionImpl::Shutdown() noexcept - { - CloseInternal(true); + if (close_connection) + { + aws_event_stream_rpc_client_connection_close( + close_connection, AWS_ERROR_EVENT_STREAM_RPC_CONNECTION_CLOSED); + aws_event_stream_rpc_client_connection_release(close_connection); + } } - void ClientConnectionImpl::Close() noexcept + std::future ClientConnectionImpl::Shutdown() noexcept { - CloseInternal(false); + Close(); + return m_terminatedPromise.get_future(); } std::future ClientConnectionImpl::Connect( @@ -919,12 +843,7 @@ namespace Aws msg_args.message_type = AWS_EVENT_STREAM_RPC_MESSAGE_TYPE_CONNECT; msg_args.message_flags = 0U; - // It shouldn't be possible to interrupt a connect with another connect, but let's handle that case anyways - OnMessageFlushCallbackContainer::Complete( - m_sharedState.m_connectFlushContainer, {EVENT_STREAM_RPC_CONNECTION_SETUP_FAILED, AWS_ERROR_SUCCESS}); - OnMessageFlushCallbackContainer::Release(m_sharedState.m_connectFlushContainer); - - m_sharedState.m_connectFlushContainer = + auto flushContainer = Crt::New(m_allocator, m_allocator, std::move(onMessageFlushCallback)); int errorCode = AWS_ERROR_SUCCESS; @@ -932,7 +851,7 @@ namespace Aws m_sharedState.m_underlyingConnection, &msg_args, s_protocolMessageCallback, - reinterpret_cast(m_sharedState.m_connectFlushContainer))) + reinterpret_cast(flushContainer))) { errorCode = aws_last_error(); } @@ -950,10 +869,7 @@ namespace Aws "A CRT error occurred while queueing a message to be sent on the connection: %s", Crt::ErrorDebugString(errorCode)); - OnMessageFlushCallbackContainer::Complete( - m_sharedState.m_connectFlushContainer, {EVENT_STREAM_RPC_CRT_ERROR, errorCode}); - OnMessageFlushCallbackContainer::Release(m_sharedState.m_connectFlushContainer); - m_sharedState.m_connectFlushContainer = nullptr; + OnMessageFlushCallbackContainer::Complete(flushContainer, {EVENT_STREAM_RPC_CRT_ERROR, errorCode}); } return (errorCode == AWS_ERROR_SUCCESS) ? AWS_OP_SUCCESS : AWS_OP_ERR; @@ -1032,12 +948,10 @@ namespace Aws std::lock_guard lock(impl->m_sharedStateLock); if (impl->m_sharedState.m_currentState != ClientState::PendingConnack || !successfulAck) { - if (!impl->m_sharedState.m_hasShutDown) - { - impl->m_sharedState.m_callbackContext.SetError( - (successfulAck) ? RpcError{EVENT_STREAM_RPC_CONNECTION_CLOSED, 0} - : RpcError{EVENT_STREAM_RPC_CONNECTION_ACCESS_DENIED, 0}); - } + impl->m_sharedState.m_callbackContext.SetError( + (successfulAck) ? RpcError{EVENT_STREAM_RPC_CONNECTION_CLOSED, 0} + : RpcError{EVENT_STREAM_RPC_CONNECTION_ACCESS_DENIED, 0}); + impl->m_sharedState.m_desiredState = ClientState::Disconnected; if (impl->m_sharedState.m_currentState != ClientState::Disconnecting) { @@ -1094,8 +1008,9 @@ namespace Aws ClientConnection::~ClientConnection() noexcept { - m_impl->Shutdown(); + auto terminationFuture = m_impl->Shutdown(); m_impl = nullptr; + terminationFuture.wait(); } std::future ClientConnection::Connect( @@ -1318,15 +1233,12 @@ namespace Aws ContinuationStateType m_currentState; ContinuationStateType m_desiredState; struct aws_event_stream_rpc_client_continuation_token *m_continuation; - OnMessageFlushCallbackContainer *m_activationCallbackContainer; std::function m_activationResponseCallback; - OnMessageFlushCallbackContainer *m_closeCallbackContainer; }; ContinuationSharedState::ContinuationSharedState() : m_currentState(ContinuationStateType::None), m_desiredState(ContinuationStateType::None), - m_continuation(nullptr), m_activationCallbackContainer(nullptr), m_activationResponseCallback(), - m_closeCallbackContainer(nullptr) + m_continuation(nullptr), m_activationResponseCallback() { } @@ -1558,6 +1470,8 @@ namespace Aws struct aws_event_stream_rpc_client_continuation_token *continuation) noexcept : m_task{}, m_allocator(allocator), m_continuation(continuation) { + // we don't acquire a reference here as we consider this a "transfer" of reference ownership from + // continuation binding to the task. aws_task_init(&m_task, s_releaseContinuation, this, "AwsEventstreamContinuationReleaseTask"); } @@ -1583,11 +1497,6 @@ namespace Aws std::future ClientContinuationImpl::ShutDown() noexcept { struct aws_event_stream_rpc_client_continuation_token *releaseContinuation = nullptr; -#ifdef NEVER - OnMessageFlushCallbackContainer *closeCallbackContainer = nullptr; - OnMessageFlushCallbackContainer *activationCallbackContainer = nullptr; - std::function activationResponseCallback; -#endif { std::lock_guard lock(m_sharedStateLock); if (m_sharedState.m_currentState == ContinuationStateType::None) @@ -1597,35 +1506,8 @@ namespace Aws m_sharedState.m_currentState = ContinuationStateType::Closed; m_sharedState.m_desiredState = ContinuationStateType::Closed; } -#ifdef NEVER - activationCallbackContainer = m_sharedState.m_activationCallbackContainer; - m_sharedState.m_activationCallbackContainer = nullptr; - - closeCallbackContainer = m_sharedState.m_closeCallbackContainer; - m_sharedState.m_closeCallbackContainer = nullptr; - - activationResponseCallback = std::move(m_sharedState.m_activationResponseCallback); - m_sharedState.m_activationResponseCallback = nullptr; // unsure if necessary, let's be sure -#endif - } -#ifdef NEVER - if (activationResponseCallback) - { - activationResponseCallback( - TaggedResult(RpcError{EVENT_STREAM_RPC_CONTINUATION_CLOSED, AWS_ERROR_SUCCESS})); } - /* - * Short-circuit and simulate both activate and close callbacks as necessary. - */ - OnMessageFlushCallbackContainer::Complete( - activationCallbackContainer, {EVENT_STREAM_RPC_CONTINUATION_CLOSED, AWS_ERROR_SUCCESS}); - OnMessageFlushCallbackContainer::Release(activationCallbackContainer); - - OnMessageFlushCallbackContainer::Complete( - closeCallbackContainer, {EVENT_STREAM_RPC_CONTINUATION_CLOSED, AWS_ERROR_SUCCESS}); - OnMessageFlushCallbackContainer::Release(closeCallbackContainer); -#endif if (releaseContinuation != nullptr) { ReleaseContinuation(m_allocator, m_clientBootstrap, releaseContinuation); @@ -1684,11 +1566,9 @@ namespace Aws if (result == AWS_OP_SUCCESS) { + activationContainer = nullptr; m_sharedState.m_currentState = ContinuationStateType::PendingActivate; m_sharedState.m_desiredState = ContinuationStateType::Activated; - - m_sharedState.m_activationCallbackContainer = activationContainer; - activationContainer = nullptr; m_sharedState.m_activationResponseCallback = std::move(onResultCallback); synchronousSuccess = true; } @@ -1729,8 +1609,6 @@ namespace Aws AWS_FATAL_ASSERT(activationContainer != nullptr); OnMessageFlushCallbackContainer::Complete(activationContainer, activationError); - OnMessageFlushCallbackContainer::Release(activationContainer); - OnMessageFlushCallbackContainer::Release(activationContainer); } if (aws_array_list_is_valid(&headersArray)) @@ -1781,10 +1659,6 @@ namespace Aws { sendError = {EVENT_STREAM_RPC_CRT_ERROR, aws_last_error()}; } - else - { - OnMessageFlushCallbackContainer::Release(sendContainer); // release our ref - } } else { @@ -1816,8 +1690,6 @@ namespace Aws if (result == AWS_OP_ERR) { OnMessageFlushCallbackContainer::Complete(sendContainer, sendError); - OnMessageFlushCallbackContainer::Release(sendContainer); - OnMessageFlushCallbackContainer::Release(sendContainer); } if (aws_array_list_is_valid(&headersArray)) @@ -1888,8 +1760,6 @@ namespace Aws if (closeResult != AWS_OP_SUCCESS) { OnMessageFlushCallbackContainer::Complete(closeCallbackContainer, closeError); - OnMessageFlushCallbackContainer::Release(closeCallbackContainer); - OnMessageFlushCallbackContainer::Release(closeCallbackContainer); } return closeFuture; @@ -1898,8 +1768,6 @@ namespace Aws void ClientContinuationImpl::OnClosed() noexcept { struct aws_event_stream_rpc_client_continuation_token *releaseContinuation = nullptr; - OnMessageFlushCallbackContainer *closeCallbackContainer = nullptr; - OnMessageFlushCallbackContainer *activationCallbackContainer = nullptr; std::function activationResponseCallback; { @@ -1910,12 +1778,6 @@ namespace Aws releaseContinuation = m_sharedState.m_continuation; m_sharedState.m_continuation = nullptr; - activationCallbackContainer = m_sharedState.m_activationCallbackContainer; - m_sharedState.m_activationCallbackContainer = nullptr; - - closeCallbackContainer = m_sharedState.m_closeCallbackContainer; - m_sharedState.m_closeCallbackContainer = nullptr; - activationResponseCallback = std::move(m_sharedState.m_activationResponseCallback); m_sharedState.m_activationResponseCallback = nullptr; } @@ -1926,14 +1788,6 @@ namespace Aws TaggedResult(RpcError{EVENT_STREAM_RPC_CONTINUATION_CLOSED, AWS_ERROR_SUCCESS})); } - OnMessageFlushCallbackContainer::Complete( - activationCallbackContainer, {EVENT_STREAM_RPC_CONTINUATION_CLOSED, AWS_ERROR_SUCCESS}); - OnMessageFlushCallbackContainer::Release(activationCallbackContainer); - - OnMessageFlushCallbackContainer::Complete( - closeCallbackContainer, {EVENT_STREAM_RPC_CONTINUATION_CLOSED, AWS_ERROR_SUCCESS}); - OnMessageFlushCallbackContainer::Release(closeCallbackContainer); - ReleaseContinuation(m_allocator, m_clientBootstrap, releaseContinuation); } diff --git a/eventstream_rpc/tests/EventStreamClientTest.cpp b/eventstream_rpc/tests/EventStreamClientTest.cpp index 42be63c5c..2c8806d09 100644 --- a/eventstream_rpc/tests/EventStreamClientTest.cpp +++ b/eventstream_rpc/tests/EventStreamClientTest.cpp @@ -933,12 +933,6 @@ static int s_TestEchoClientOperationEchoFailureNeverConnected(struct aws_allocat auto requestFuture = echoMessage->Activate(echoMessageRequest, s_onMessageFlush); ASSERT_INT_EQUALS(EVENT_STREAM_RPC_CONNECTION_CLOSED, requestFuture.get().baseStatus); - - auto result = echoMessage->GetResult().get(); - ASSERT_FALSE(result); - - auto error = result.GetRpcError(); - ASSERT_INT_EQUALS(EVENT_STREAM_RPC_CONNECTION_CLOSED, error.baseStatus); } return AWS_OP_SUCCESS; @@ -975,12 +969,6 @@ static int s_TestEchoClientOperationEchoFailureDisconnected(struct aws_allocator auto activateStatus = requestFuture.get().baseStatus; ASSERT_TRUE( activateStatus == EVENT_STREAM_RPC_CONNECTION_CLOSED || activateStatus == EVENT_STREAM_RPC_CRT_ERROR); - - auto result = echoMessage->GetResult().get(); - ASSERT_FALSE(result); - - auto resultStatus = result.GetRpcError().baseStatus; - ASSERT_TRUE(resultStatus == EVENT_STREAM_RPC_CONNECTION_CLOSED || resultStatus == EVENT_STREAM_RPC_CRT_ERROR); } return AWS_OP_SUCCESS; @@ -1076,16 +1064,16 @@ static int s_TestEchoClientOperationActivateActivate(struct aws_allocator *alloc GetAllCustomersRequest getAllCustomersRequest; auto requestFuture = getAllCustomers->Activate(getAllCustomersRequest, s_onMessageFlush); - // auto requestFuture2 = getAllCustomers->Activate(getAllCustomersRequest, s_onMessageFlush); - // requestFuture2.wait(); + auto requestFuture2 = getAllCustomers->Activate(getAllCustomersRequest, s_onMessageFlush); + requestFuture2.wait(); requestFuture.wait(); auto result = getAllCustomers->GetResult().get(); ASSERT_TRUE(result); auto response = result.GetOperationResponse(); ASSERT_NOT_NULL(response); - // auto flush2ErrorStatus = requestFuture2.get().baseStatus; - // ASSERT_INT_EQUALS(EVENT_STREAM_RPC_CONTINUATION_ALREADY_OPENED, flush2ErrorStatus); + auto flush2ErrorStatus = requestFuture2.get().baseStatus; + ASSERT_INT_EQUALS(EVENT_STREAM_RPC_CONTINUATION_ALREADY_OPENED, flush2ErrorStatus); return AWS_OP_SUCCESS; }); @@ -1094,7 +1082,6 @@ static int s_TestEchoClientOperationActivateActivate(struct aws_allocator *alloc AWS_TEST_CASE(EchoClientOperationActivateActivate, s_TestEchoClientOperationActivateActivate); // Non-streaming race condition tests -// add_test_case(EchoClientOperationActivateActivate) // add_test_case(EchoClientOperationActivateWaitActivate) // add_test_case(EchoClientOperationActivateCloseActivate) // add_test_case(EchoClientOperationActivateClosedActivate) From c8a685c646e9344ff8876e660cffefc3fc48bd23 Mon Sep 17 00:00:00 2001 From: Bret Ambrose Date: Mon, 9 Jun 2025 15:01:29 -0700 Subject: [PATCH 42/43] More testing --- eventstream_rpc/source/EventStreamClient.cpp | 59 ++++++++++++---- eventstream_rpc/tests/CMakeLists.txt | 4 +- .../tests/EventStreamClientTest.cpp | 69 ++++++++++++++++++- 3 files changed, 115 insertions(+), 17 deletions(-) diff --git a/eventstream_rpc/source/EventStreamClient.cpp b/eventstream_rpc/source/EventStreamClient.cpp index 5a395025c..aa67f29d9 100644 --- a/eventstream_rpc/source/EventStreamClient.cpp +++ b/eventstream_rpc/source/EventStreamClient.cpp @@ -1297,6 +1297,8 @@ namespace Aws void OnMessage(const struct aws_event_stream_rpc_message_args *messageArgs) noexcept; + EventStreamRpcStatusCode GetEmptyContinuationStatusCode() const; + static void s_OnContinuationClosed( struct aws_event_stream_rpc_client_continuation_token *token, void *user_data) noexcept; @@ -1326,6 +1328,18 @@ namespace Aws std::shared_ptr m_streamHandler; std::promise m_terminationPromise; + + /* + * An unfortunate bit of state needed to differentiate between two cases: + * (1) Failure to create the original continuation for any reason + * (2) A fully-closed continuation + * + * We want to differentiate these cases in order to communicate clear error codes when attempting + * to operate on the continuation. + * + * This is always true in case (2) and false in case (1) + */ + bool m_continuationValid; }; ClientOperation::ClientOperation( @@ -1412,7 +1426,7 @@ namespace Aws struct aws_client_bootstrap *bootstrap, struct aws_event_stream_rpc_client_connection *connection) noexcept : m_allocator(allocator), m_clientBootstrap(aws_client_bootstrap_acquire(bootstrap)), m_sharedState(), - m_operationModelContext(nullptr) + m_operationModelContext(nullptr), m_continuationValid(true) { if (connection != nullptr) { @@ -1430,6 +1444,7 @@ namespace Aws { m_sharedState.m_currentState = ContinuationStateType::Closed; m_sharedState.m_desiredState = ContinuationStateType::Closed; + m_continuationValid = false; } } @@ -1520,6 +1535,18 @@ namespace Aws return m_terminationPromise.get_future(); } + EventStreamRpcStatusCode ClientContinuationImpl::GetEmptyContinuationStatusCode() const + { + if (m_continuationValid) + { + return EVENT_STREAM_RPC_CONTINUATION_CLOSED; + } + else + { + return EVENT_STREAM_RPC_CONNECTION_CLOSED; + } + } + std::future ClientContinuationImpl::Activate( const Crt::String &operation, const Crt::List &headers, @@ -1599,8 +1626,7 @@ namespace Aws } else { - // null continuation is only if the connection didn't exist at creation time - activationError = {EVENT_STREAM_RPC_CONNECTION_CLOSED, 0}; + activationError = {GetEmptyContinuationStatusCode(), 0}; } } @@ -1682,8 +1708,7 @@ namespace Aws } else { - // null continuation is only if the connection didn't exist at creation time - sendError = {EVENT_STREAM_RPC_CONNECTION_CLOSED, 0}; + sendError = {GetEmptyContinuationStatusCode(), 0}; } } @@ -1752,8 +1777,7 @@ namespace Aws } else { - // null continuation is only if the connection didn't exist at creation time - closeError = {EVENT_STREAM_RPC_CONNECTION_CLOSED, 0}; + closeError = {GetEmptyContinuationStatusCode(), 0}; } } @@ -1928,12 +1952,15 @@ namespace Aws bool isResponse = false; { std::lock_guard lock(m_sharedStateLock); - isResponse = static_cast(m_sharedState.m_currentState == ContinuationStateType::PendingActivate); + isResponse = static_cast(m_sharedState.m_activationResponseCallback); if (isResponse) { activationResultCallback = std::move(m_sharedState.m_activationResponseCallback); m_sharedState.m_activationResponseCallback = nullptr; - m_sharedState.m_currentState = ContinuationStateType::Activated; + if (m_sharedState.m_currentState == ContinuationStateType::PendingActivate) + { + m_sharedState.m_currentState = ContinuationStateType::Activated; + } } } @@ -1969,14 +1996,20 @@ namespace Aws { AWS_FATAL_ASSERT(result.m_message.value().m_route == EventStreamMessageRoutingType::Stream); auto shape = std::move(result.m_message.value().m_shape); - m_streamHandler->OnStreamEvent(std::move(shape)); + if (m_streamHandler) + { + m_streamHandler->OnStreamEvent(std::move(shape)); + } } else { - bool shouldClose = m_streamHandler->OnStreamError(nullptr, {result.m_statusCode, 0}); - if (shouldClose) + if (m_streamHandler) { - Close(); + bool shouldClose = m_streamHandler->OnStreamError(nullptr, {result.m_statusCode, 0}); + if (shouldClose) + { + Close(); + } } } } diff --git a/eventstream_rpc/tests/CMakeLists.txt b/eventstream_rpc/tests/CMakeLists.txt index 3d7e02e82..03fab5b1f 100644 --- a/eventstream_rpc/tests/CMakeLists.txt +++ b/eventstream_rpc/tests/CMakeLists.txt @@ -66,8 +66,8 @@ add_test_case(EchoClientOperationUnactivatedShutdown) add_test_case(EchoClientOperationUnactivatedClose) add_test_case(EchoClientOperationUnactivatedCloseDropFuture) add_test_case(EchoClientOperationActivateActivate) -#add_test_case(EchoClientOperationActivateWaitActivate) -#add_test_case(EchoClientOperationActivateCloseActivate) +add_test_case(EchoClientOperationActivateWaitActivate) +add_test_case(EchoClientOperationActivateCloseActivate) #add_test_case(EchoClientOperationActivateClosedActivate) #add_test_case(EchoClientOperationActivateCloseConnection) #add_test_case(EchoClientOperationActivateCloseContinuation) diff --git a/eventstream_rpc/tests/EventStreamClientTest.cpp b/eventstream_rpc/tests/EventStreamClientTest.cpp index 2c8806d09..fea2d6216 100644 --- a/eventstream_rpc/tests/EventStreamClientTest.cpp +++ b/eventstream_rpc/tests/EventStreamClientTest.cpp @@ -1081,9 +1081,74 @@ static int s_TestEchoClientOperationActivateActivate(struct aws_allocator *alloc AWS_TEST_CASE(EchoClientOperationActivateActivate, s_TestEchoClientOperationActivateActivate); +static int s_TestEchoClientOperationActivateWaitActivate(struct aws_allocator *allocator, void *ctx) +{ + return s_DoSimpleRequestRaceCheckTest( + allocator, + [](EventStreamClientTestContext &testContext, EchoTestRpcClient &client) + { + auto getAllCustomers = client.NewGetAllCustomers(); + GetAllCustomersRequest getAllCustomersRequest; + + auto requestFuture = getAllCustomers->Activate(getAllCustomersRequest, s_onMessageFlush); + requestFuture.wait(); + auto result = getAllCustomers->GetResult().get(); + ASSERT_TRUE(result); + auto response = result.GetOperationResponse(); + ASSERT_NOT_NULL(response); + + auto requestFuture2 = getAllCustomers->Activate(getAllCustomersRequest, s_onMessageFlush); + requestFuture2.wait(); + + auto flush2ErrorStatus = requestFuture2.get().baseStatus; + ASSERT_TRUE( + flush2ErrorStatus == EVENT_STREAM_RPC_CONTINUATION_ALREADY_OPENED || + flush2ErrorStatus == EVENT_STREAM_RPC_CONTINUATION_CLOSED || + flush2ErrorStatus == EVENT_STREAM_RPC_CONTINUATION_CLOSE_IN_PROGRESS); + + return AWS_OP_SUCCESS; + }); +} + +AWS_TEST_CASE(EchoClientOperationActivateWaitActivate, s_TestEchoClientOperationActivateWaitActivate); + +static int s_TestEchoClientOperationActivateCloseActivate(struct aws_allocator *allocator, void *ctx) +{ + return s_DoSimpleRequestRaceCheckTest( + allocator, + [](EventStreamClientTestContext &testContext, EchoTestRpcClient &client) + { + auto getAllCustomers = client.NewGetAllCustomers(); + GetAllCustomersRequest getAllCustomersRequest; + + auto requestFuture = getAllCustomers->Activate(getAllCustomersRequest, s_onMessageFlush); + + auto closeFuture = getAllCustomers->Close(); + + requestFuture.wait(); + auto flushErrorStatus = requestFuture.get().baseStatus; + ASSERT_TRUE( + flushErrorStatus == EVENT_STREAM_RPC_SUCCESS || + flushErrorStatus == EVENT_STREAM_RPC_CONTINUATION_CLOSED); + + auto requestFuture2 = getAllCustomers->Activate(getAllCustomersRequest, s_onMessageFlush); + requestFuture2.wait(); + + auto flush2ErrorStatus = requestFuture2.get().baseStatus; + ASSERT_TRUE( + flush2ErrorStatus == EVENT_STREAM_RPC_CONTINUATION_ALREADY_OPENED || + flush2ErrorStatus == EVENT_STREAM_RPC_CONTINUATION_CLOSED || + flush2ErrorStatus == EVENT_STREAM_RPC_CONTINUATION_CLOSE_IN_PROGRESS); + + closeFuture.wait(); + + return AWS_OP_SUCCESS; + }); +} + +AWS_TEST_CASE(EchoClientOperationActivateCloseActivate, s_TestEchoClientOperationActivateCloseActivate); + // Non-streaming race condition tests -// add_test_case(EchoClientOperationActivateWaitActivate) -// add_test_case(EchoClientOperationActivateCloseActivate) // add_test_case(EchoClientOperationActivateClosedActivate) // add_test_case(EchoClientOperationActivateCloseConnection) // add_test_case(EchoClientOperationActivateCloseContinuation) From fc6224b0a1c54365e9c746ebc10d00c438ba6a3f Mon Sep 17 00:00:00 2001 From: Bret Ambrose Date: Tue, 10 Jun 2025 14:49:52 -0700 Subject: [PATCH 43/43] Checkpoint hitting unavoidable deadlock --- .../aws/eventstreamrpc/EventStreamClient.h | 137 +-- eventstream_rpc/source/EventStreamClient.cpp | 14 +- eventstream_rpc/tests/CMakeLists.txt | 5 +- eventstream_rpc/tests/EchoTestRpcModel.cpp | 144 +-- .../tests/EventStreamClientTest.cpp | 95 +- .../tests/include/awstest/EchoTestRpcModel.h | 60 +- .../aws/greengrass/GreengrassCoreIpcModel.h | 340 +------- .../source/GreengrassCoreIpcModel.cpp | 818 ++++++------------ 8 files changed, 463 insertions(+), 1150 deletions(-) diff --git a/eventstream_rpc/include/aws/eventstreamrpc/EventStreamClient.h b/eventstream_rpc/include/aws/eventstreamrpc/EventStreamClient.h index 59517d3ab..4bdbe0f2c 100644 --- a/eventstream_rpc/include/aws/eventstreamrpc/EventStreamClient.h +++ b/eventstream_rpc/include/aws/eventstreamrpc/EventStreamClient.h @@ -10,20 +10,13 @@ #include #include #include -#include -#include #include #include -#include - #include -#include -#include #include #include -#include namespace Aws { @@ -306,28 +299,6 @@ namespace Aws std::shared_ptr m_impl; }; -#ifdef NEVER - /** - * User data passed to callbacks for a new stream. - */ - class AWS_EVENTSTREAMRPC_API ContinuationCallbackData - { - public: - ContinuationCallbackData( - ClientContinuation *clientContinuation, - Crt::Allocator *allocator = Crt::g_allocator) noexcept - : clientContinuation(clientContinuation), allocator(allocator) - { - continuationDestroyed = false; - } - ContinuationCallbackData(const ContinuationCallbackData &lhs) noexcept = delete; - bool continuationDestroyed; - std::mutex callbackMutex; - ClientContinuation *clientContinuation; - Crt::Allocator *allocator; - }; -#endif - /** * Vestigial, do-nothing class that remains for backwards compatibility with the * original, publicly-visible class hierarchy. @@ -335,114 +306,9 @@ namespace Aws class AWS_EVENTSTREAMRPC_API ClientContinuationHandler { public: -#ifdef NEVER - /** - * Invoked when a message is received on this continuation. - */ - virtual void OnContinuationMessage( - const Crt::List &headers, - const Crt::Optional &payload, - MessageType messageType, - uint32_t messageFlags) = 0; - /** - * Invoked when the continuation is closed. - * - * Once the continuation is closed, no more messages may be sent or received. - * The continuation is closed when a message is sent or received with - * the TERMINATE_STREAM flag, or when the connection shuts down. - */ - virtual void OnContinuationClosed() = 0; -#endif virtual ~ClientContinuationHandler() noexcept = default; -#ifdef NEVER - private: - friend class ClientContinuation; - std::shared_ptr m_callbackData; -#endif }; -#ifdef NEVER - /** - * A wrapper for event-stream-rpc client continuation. - */ - class AWS_EVENTSTREAMRPC_API ClientContinuation final - { - public: - /** - * Create a new continuation. - * - * @note continuation_option's callbacks will not be invoked, and nothing will be sent across - * the wire until Activate() is invoked. - * @param connection Connection on which open a new stream. - * @param continuationHandler A set of callbacks that will be invoked for continuation events. - * @param allocator Allocator to use. - */ - ClientContinuation( - struct aws_event_stream_rpc_client_connection *connection, - ClientContinuationHandler &continuationHandler, - Crt::Allocator *allocator) noexcept; - ~ClientContinuation() noexcept; - - /** - * Initiate a new client stream. Send new message for the new stream. - * @param operation Name for the operation to be invoked by the peer endpoint. - * @param headers Headers for the eventstream message. - * @param payload Payload for the eventstream message. - * @param messageType Message type for the message. - * @param messageFlags Bitmask of aws_event_stream_rpc_message_flag values. - * @param onMessageFlushCallback Callback to be invoked upon the message being flushed to the underlying - * transport. - * @return Future that will be resolved when the message has either been written to the wire or it fails. - */ - std::future Activate( - const Crt::String &operation, - const Crt::List &headers, - const Crt::Optional &payload, - MessageType messageType, - uint32_t messageFlags, - OnMessageFlushCallback onMessageFlushCallback) noexcept; - - /** - * Check if the continuation has been closed. - * @return True if the continuation has been closed, false otherwise. - */ - bool IsClosed() noexcept; - - /** - * Send message on the continuation. - * @param headers List of additional event stream headers to include on the message. - * @param payload Message payload. - * @param messageType Message type for the message. - * @param messageFlags Bitmask of aws_event_stream_rpc_message_flag values. - * @param onMessageFlushCallback Callback to be invoked upon the message being flushed to the underlying - * transport. - * @return Future that will be resolved when the message has either been written to the wire or it fails. - */ - std::future SendMessage( - const Crt::List &headers, - const Crt::Optional &payload, - MessageType messageType, - uint32_t messageFlags, - OnMessageFlushCallback onMessageFlushCallback) noexcept; - - private: - friend class ClientOperation; - Crt::Allocator *m_allocator; - ClientContinuationHandler &m_continuationHandler; - struct aws_event_stream_rpc_client_continuation_token *m_continuationToken; - std::shared_ptr m_callbackData; - - void Release(); - - static void s_onContinuationMessage( - struct aws_event_stream_rpc_client_continuation_token *continuationToken, - const struct aws_event_stream_rpc_message_args *messageArgs, - void *userData) noexcept; - static void s_onContinuationClosed( - struct aws_event_stream_rpc_client_continuation_token *continuationToken, - void *userData) noexcept; - }; -#endif /** * Base class for types used by operations. */ @@ -722,8 +588,7 @@ namespace Aws std::future Activate( const AbstractShapeBase *shape, OnMessageFlushCallback &&onMessageFlushCallback, - std::function &&onResultCallback, - bool &synchronousSuccess) noexcept; + std::function &&onResultCallback) noexcept; /** * Sends a message on the stream diff --git a/eventstream_rpc/source/EventStreamClient.cpp b/eventstream_rpc/source/EventStreamClient.cpp index aa67f29d9..15ae59d0d 100644 --- a/eventstream_rpc/source/EventStreamClient.cpp +++ b/eventstream_rpc/source/EventStreamClient.cpp @@ -1262,8 +1262,7 @@ namespace Aws MessageType messageType, uint32_t messageFlags, std::function &&onResultCallback, - OnMessageFlushCallback &&onMessageFlushCallback, - bool &synchronousSuccess) noexcept; + OnMessageFlushCallback &&onMessageFlushCallback) noexcept; std::future SendStreamMessage( const Crt::List &headers, @@ -1377,8 +1376,7 @@ namespace Aws std::future ClientOperation::Activate( const AbstractShapeBase *shape, OnMessageFlushCallback &&onMessageFlushCallback, - std::function &&onResultCallback, - bool &synchronousSuccess) noexcept + std::function &&onResultCallback) noexcept { Crt::List headers; headers.emplace_back(EventStreamHeader( @@ -1396,8 +1394,7 @@ namespace Aws AWS_EVENT_STREAM_RPC_MESSAGE_TYPE_APPLICATION_MESSAGE, 0, std::move(onResultCallback), - std::move(onMessageFlushCallback), - synchronousSuccess); + std::move(onMessageFlushCallback)); } std::future ClientOperation::SendStreamMessage( @@ -1554,13 +1551,12 @@ namespace Aws MessageType messageType, uint32_t messageFlags, std::function &&onResultCallback, - OnMessageFlushCallback &&onMessageFlushCallback, - bool &synchronousSuccess) noexcept + OnMessageFlushCallback &&onMessageFlushCallback) noexcept { AWS_FATAL_ASSERT(static_cast(onResultCallback)); int result = AWS_OP_SUCCESS; - synchronousSuccess = false; + bool synchronousSuccess = false; struct aws_array_list headersArray; // guaranteed to be zeroed or valid if we reach the end of the function std::promise activationPromise; std::future activationFuture = activationPromise.get_future(); diff --git a/eventstream_rpc/tests/CMakeLists.txt b/eventstream_rpc/tests/CMakeLists.txt index 03fab5b1f..350d78515 100644 --- a/eventstream_rpc/tests/CMakeLists.txt +++ b/eventstream_rpc/tests/CMakeLists.txt @@ -68,9 +68,8 @@ add_test_case(EchoClientOperationUnactivatedCloseDropFuture) add_test_case(EchoClientOperationActivateActivate) add_test_case(EchoClientOperationActivateWaitActivate) add_test_case(EchoClientOperationActivateCloseActivate) -#add_test_case(EchoClientOperationActivateClosedActivate) -#add_test_case(EchoClientOperationActivateCloseConnection) -#add_test_case(EchoClientOperationActivateCloseContinuation) +add_test_case(EchoClientOperationActivateClosedActivate) +add_test_case(EchoClientOperationActivateCloseConnection) #add_test_case(EchoClientOperationActivateDoubleCloseContinuation) #add_test_case(EchoClientOperationActivateWaitDoubleCloseContinuation) #add_test_case(EchoClientOperationActivateWaitCloseContinuationWaitCloseContinuation) diff --git a/eventstream_rpc/tests/EchoTestRpcModel.cpp b/eventstream_rpc/tests/EchoTestRpcModel.cpp index 567786fe3..2740222bd 100644 --- a/eventstream_rpc/tests/EchoTestRpcModel.cpp +++ b/eventstream_rpc/tests/EchoTestRpcModel.cpp @@ -1018,14 +1018,15 @@ namespace Awstest std::future GetAllProductsOperation::GetResult() noexcept { - return m_resultPromise.get_future(); + return m_resultPromise->get_future(); } GetAllProductsOperation::GetAllProductsOperation( ClientConnection &connection, const GetAllProductsOperationContext &operationContext, Aws::Crt::Allocator *allocator) noexcept - : ClientOperation(connection, nullptr, operationContext, allocator) + : ClientOperation(connection, nullptr, operationContext, allocator), + m_resultPromise(Aws::Crt::MakeShared>(allocator)) { } @@ -1033,23 +1034,14 @@ namespace Awstest const GetAllProductsRequest &request, OnMessageFlushCallback onMessageFlushCallback) noexcept { - bool synchronousSuccess = false; - std::lock_guard selfReferenceLock(m_selfReferenceLock); + auto promiseReference = m_resultPromise; + auto activateFuture = ClientOperation::Activate( static_cast(&request), std::move(onMessageFlushCallback), - [this](TaggedResult &&unmodeledResult) - { - std::lock_guard selfReferenceLock(m_selfReferenceLock); - m_resultPromise.set_value(GetAllProductsResult(std::move(unmodeledResult))); - m_selfReference = nullptr; - }, - synchronousSuccess); - if (synchronousSuccess) - { - m_selfReference = shared_from_this(); - ; - } + [promiseReference](TaggedResult &&unmodeledResult) + { promiseReference->set_value(GetAllProductsResult(std::move(unmodeledResult))); }); + return activateFuture; } @@ -1098,14 +1090,15 @@ namespace Awstest std::future CauseServiceErrorOperation::GetResult() noexcept { - return m_resultPromise.get_future(); + return m_resultPromise->get_future(); } CauseServiceErrorOperation::CauseServiceErrorOperation( ClientConnection &connection, const CauseServiceErrorOperationContext &operationContext, Aws::Crt::Allocator *allocator) noexcept - : ClientOperation(connection, nullptr, operationContext, allocator) + : ClientOperation(connection, nullptr, operationContext, allocator), + m_resultPromise(Aws::Crt::MakeShared>(allocator)) { } @@ -1113,23 +1106,14 @@ namespace Awstest const CauseServiceErrorRequest &request, OnMessageFlushCallback onMessageFlushCallback) noexcept { - bool synchronousSuccess = false; - std::lock_guard selfReferenceLock(m_selfReferenceLock); + auto promiseReference = m_resultPromise; + auto activateFuture = ClientOperation::Activate( static_cast(&request), std::move(onMessageFlushCallback), - [this](TaggedResult &&unmodeledResult) - { - std::lock_guard selfReferenceLock(m_selfReferenceLock); - m_resultPromise.set_value(CauseServiceErrorResult(std::move(unmodeledResult))); - m_selfReference = nullptr; - }, - synchronousSuccess); - if (synchronousSuccess) - { - m_selfReference = shared_from_this(); - ; - } + [promiseReference](TaggedResult &&unmodeledResult) + { promiseReference->set_value(CauseServiceErrorResult(std::move(unmodeledResult))); }); + return activateFuture; } @@ -1200,7 +1184,7 @@ namespace Awstest std::future CauseStreamServiceToErrorOperation::GetResult() noexcept { - return m_resultPromise.get_future(); + return m_resultPromise->get_future(); } CauseStreamServiceToErrorOperation::CauseStreamServiceToErrorOperation( @@ -1208,7 +1192,8 @@ namespace Awstest std::shared_ptr streamHandler, const CauseStreamServiceToErrorOperationContext &operationContext, Aws::Crt::Allocator *allocator) noexcept - : ClientOperation(connection, streamHandler, operationContext, allocator) + : ClientOperation(connection, streamHandler, operationContext, allocator), + m_resultPromise(Aws::Crt::MakeShared>(allocator)) { } @@ -1216,23 +1201,14 @@ namespace Awstest const EchoStreamingRequest &request, OnMessageFlushCallback onMessageFlushCallback) noexcept { - bool synchronousSuccess = false; - std::lock_guard selfReferenceLock(m_selfReferenceLock); + auto promiseReference = m_resultPromise; + auto activateFuture = ClientOperation::Activate( static_cast(&request), std::move(onMessageFlushCallback), - [this](TaggedResult &&unmodeledResult) - { - std::lock_guard selfReferenceLock(m_selfReferenceLock); - m_resultPromise.set_value(CauseStreamServiceToErrorResult(std::move(unmodeledResult))); - m_selfReference = nullptr; - }, - synchronousSuccess); - if (synchronousSuccess) - { - m_selfReference = shared_from_this(); - ; - } + [promiseReference](TaggedResult &&unmodeledResult) + { promiseReference->set_value(CauseStreamServiceToErrorResult(std::move(unmodeledResult))); }); + return activateFuture; } @@ -1306,7 +1282,7 @@ namespace Awstest std::future EchoStreamMessagesOperation::GetResult() noexcept { - return m_resultPromise.get_future(); + return m_resultPromise->get_future(); } EchoStreamMessagesOperation::EchoStreamMessagesOperation( @@ -1314,7 +1290,8 @@ namespace Awstest std::shared_ptr streamHandler, const EchoStreamMessagesOperationContext &operationContext, Aws::Crt::Allocator *allocator) noexcept - : ClientOperation(connection, streamHandler, operationContext, allocator) + : ClientOperation(connection, streamHandler, operationContext, allocator), + m_resultPromise(Aws::Crt::MakeShared>(allocator)) { } @@ -1322,23 +1299,14 @@ namespace Awstest const EchoStreamingRequest &request, OnMessageFlushCallback onMessageFlushCallback) noexcept { - bool synchronousSuccess = false; - std::lock_guard selfReferenceLock(m_selfReferenceLock); + auto promiseReference = m_resultPromise; + auto activateFuture = ClientOperation::Activate( static_cast(&request), std::move(onMessageFlushCallback), - [this](TaggedResult &&unmodeledResult) - { - std::lock_guard selfReferenceLock(m_selfReferenceLock); - m_resultPromise.set_value(EchoStreamMessagesResult(std::move(unmodeledResult))); - m_selfReference = nullptr; - }, - synchronousSuccess); - if (synchronousSuccess) - { - m_selfReference = shared_from_this(); - ; - } + [promiseReference](TaggedResult &&unmodeledResult) + { promiseReference->set_value(EchoStreamMessagesResult(std::move(unmodeledResult))); }); + return activateFuture; } @@ -1393,14 +1361,15 @@ namespace Awstest std::future EchoMessageOperation::GetResult() noexcept { - return m_resultPromise.get_future(); + return m_resultPromise->get_future(); } EchoMessageOperation::EchoMessageOperation( ClientConnection &connection, const EchoMessageOperationContext &operationContext, Aws::Crt::Allocator *allocator) noexcept - : ClientOperation(connection, nullptr, operationContext, allocator) + : ClientOperation(connection, nullptr, operationContext, allocator), + m_resultPromise(Aws::Crt::MakeShared>(allocator)) { } @@ -1408,23 +1377,14 @@ namespace Awstest const EchoMessageRequest &request, OnMessageFlushCallback onMessageFlushCallback) noexcept { - bool synchronousSuccess = false; - std::lock_guard selfReferenceLock(m_selfReferenceLock); + auto promiseReference = m_resultPromise; + auto activateFuture = ClientOperation::Activate( static_cast(&request), std::move(onMessageFlushCallback), - [this](TaggedResult &&unmodeledResult) - { - std::lock_guard selfReferenceLock(m_selfReferenceLock); - m_resultPromise.set_value(EchoMessageResult(std::move(unmodeledResult))); - m_selfReference = nullptr; - }, - synchronousSuccess); - if (synchronousSuccess) - { - m_selfReference = shared_from_this(); - ; - } + [promiseReference](TaggedResult &&unmodeledResult) + { promiseReference->set_value(EchoMessageResult(std::move(unmodeledResult))); }); + return activateFuture; } @@ -1472,14 +1432,15 @@ namespace Awstest std::future GetAllCustomersOperation::GetResult() noexcept { - return m_resultPromise.get_future(); + return m_resultPromise->get_future(); } GetAllCustomersOperation::GetAllCustomersOperation( ClientConnection &connection, const GetAllCustomersOperationContext &operationContext, Aws::Crt::Allocator *allocator) noexcept - : ClientOperation(connection, nullptr, operationContext, allocator) + : ClientOperation(connection, nullptr, operationContext, allocator), + m_resultPromise(Aws::Crt::MakeShared>(allocator)) { } @@ -1487,23 +1448,14 @@ namespace Awstest const GetAllCustomersRequest &request, OnMessageFlushCallback onMessageFlushCallback) noexcept { - bool synchronousSuccess = false; - std::lock_guard selfReferenceLock(m_selfReferenceLock); + auto promiseReference = m_resultPromise; + auto activateFuture = ClientOperation::Activate( static_cast(&request), std::move(onMessageFlushCallback), - [this](TaggedResult &&unmodeledResult) - { - std::lock_guard selfReferenceLock(m_selfReferenceLock); - m_resultPromise.set_value(GetAllCustomersResult(std::move(unmodeledResult))); - m_selfReference = nullptr; - }, - synchronousSuccess); - if (synchronousSuccess) - { - m_selfReference = shared_from_this(); - ; - } + [promiseReference](TaggedResult &&unmodeledResult) + { promiseReference->set_value(GetAllCustomersResult(std::move(unmodeledResult))); }); + return activateFuture; } diff --git a/eventstream_rpc/tests/EventStreamClientTest.cpp b/eventstream_rpc/tests/EventStreamClientTest.cpp index fea2d6216..4b44030b8 100644 --- a/eventstream_rpc/tests/EventStreamClientTest.cpp +++ b/eventstream_rpc/tests/EventStreamClientTest.cpp @@ -1148,10 +1148,101 @@ static int s_TestEchoClientOperationActivateCloseActivate(struct aws_allocator * AWS_TEST_CASE(EchoClientOperationActivateCloseActivate, s_TestEchoClientOperationActivateCloseActivate); +static int s_TestEchoClientOperationActivateClosedActivate(struct aws_allocator *allocator, void *ctx) +{ + return s_DoSimpleRequestRaceCheckTest( + allocator, + [](EventStreamClientTestContext &testContext, EchoTestRpcClient &client) + { + auto getAllCustomers = client.NewGetAllCustomers(); + GetAllCustomersRequest getAllCustomersRequest; + + auto requestFuture = getAllCustomers->Activate(getAllCustomersRequest, s_onMessageFlush); + + auto closeFuture = getAllCustomers->Close(); + closeFuture.wait(); + + requestFuture.wait(); + auto flushErrorStatus = requestFuture.get().baseStatus; + ASSERT_TRUE( + flushErrorStatus == EVENT_STREAM_RPC_SUCCESS || + flushErrorStatus == EVENT_STREAM_RPC_CONTINUATION_CLOSED); + + auto requestFuture2 = getAllCustomers->Activate(getAllCustomersRequest, s_onMessageFlush); + requestFuture2.wait(); + + auto flush2ErrorStatus = requestFuture2.get().baseStatus; + ASSERT_INT_EQUALS(EVENT_STREAM_RPC_CONTINUATION_CLOSED, flush2ErrorStatus); + + return AWS_OP_SUCCESS; + }); +} + +AWS_TEST_CASE(EchoClientOperationActivateClosedActivate, s_TestEchoClientOperationActivateClosedActivate); + +static int s_DoClientScopedRaceCheckTest( + struct aws_allocator *allocator, + std::function testFunction) +{ + ApiHandle apiHandle(allocator); + for (size_t i = 0; i < 100; ++i) + { + + EventStreamClientTestContext testContext(allocator); + if (!testContext.isValidEnvironment()) + { + printf("Environment Variables are not set for the test, skipping..."); + return AWS_OP_SKIP; + } + + { + ConnectionLifecycleHandler lifecycleHandler; + Awstest::EchoTestRpcClient client(*testContext.clientBootstrap, allocator); + auto connectedStatus = client.Connect(lifecycleHandler); + ASSERT_TRUE(connectedStatus.get().baseStatus == EVENT_STREAM_RPC_SUCCESS); + + ASSERT_SUCCESS(testFunction(testContext, client)); + } + } + + return AWS_OP_SUCCESS; +} + +static int s_TestEchoClientOperationActivateCloseConnection(struct aws_allocator *allocator, void *ctx) +{ + return s_DoClientScopedRaceCheckTest( + allocator, + [](EventStreamClientTestContext &testContext, EchoTestRpcClient &client) + { + auto getAllCustomers = client.NewGetAllCustomers(); + GetAllCustomersRequest getAllCustomersRequest; + + auto requestFuture = getAllCustomers->Activate(getAllCustomersRequest, s_onMessageFlush); + + client.Close(); + + requestFuture.wait(); + auto flushErrorStatus = requestFuture.get().baseStatus; + ASSERT_TRUE( + flushErrorStatus == EVENT_STREAM_RPC_SUCCESS || flushErrorStatus == EVENT_STREAM_RPC_CONNECTION_CLOSED); + + auto requestFuture2 = getAllCustomers->Activate(getAllCustomersRequest, s_onMessageFlush); + requestFuture2.wait(); + + auto flush2ErrorStatus = requestFuture2.get().baseStatus; + ASSERT_TRUE( + flush2ErrorStatus == EVENT_STREAM_RPC_CONTINUATION_CLOSED || + flush2ErrorStatus == EVENT_STREAM_RPC_CONNECTION_CLOSED || + flush2ErrorStatus == EVENT_STREAM_RPC_CONTINUATION_ALREADY_OPENED); + + return AWS_OP_SUCCESS; + }); +} + +AWS_TEST_CASE(EchoClientOperationActivateCloseConnection, s_TestEchoClientOperationActivateCloseConnection); + // Non-streaming race condition tests -// add_test_case(EchoClientOperationActivateClosedActivate) // add_test_case(EchoClientOperationActivateCloseConnection) -// add_test_case(EchoClientOperationActivateCloseContinuation) // add_test_case(EchoClientOperationActivateDoubleCloseContinuation) // add_test_case(EchoClientOperationActivateWaitDoubleCloseContinuation) // add_test_case(EchoClientOperationActivateWaitCloseContinuationWaitCloseContinuation) diff --git a/eventstream_rpc/tests/include/awstest/EchoTestRpcModel.h b/eventstream_rpc/tests/include/awstest/EchoTestRpcModel.h index 1fb4b3b66..048e9fe55 100644 --- a/eventstream_rpc/tests/include/awstest/EchoTestRpcModel.h +++ b/eventstream_rpc/tests/include/awstest/EchoTestRpcModel.h @@ -743,15 +743,7 @@ namespace Awstest std::future GetResult() noexcept; private: - std::promise m_resultPromise; - - /* Enforces a happens-before relationship between setting the self-reference and clearing it. */ - std::mutex m_selfReferenceLock; - - /* Keeps the operation alive while activation is in-progress. Internally, we capture `this` in the function - * object that handles the result. If we did not do this, we risk a crash if the user drops their reference - * before the future gets completed. */ - std::shared_ptr m_selfReference; + std::shared_ptr> m_resultPromise; }; class AWS_ECHOTESTRPC_API CauseServiceErrorOperationContext : public OperationModelContext @@ -819,15 +811,7 @@ namespace Awstest std::future GetResult() noexcept; private: - std::promise m_resultPromise; - - /* Enforces a happens-before relationship between setting the self-reference and clearing it. */ - std::mutex m_selfReferenceLock; - - /* Keeps the operation alive while activation is in-progress. Internally, we capture `this` in the function - * object that handles the result. If we did not do this, we risk a crash if the user drops their reference - * before the future gets completed. */ - std::shared_ptr m_selfReference; + std::shared_ptr> m_resultPromise; }; class AWS_ECHOTESTRPC_API CauseStreamServiceToErrorStreamHandler : public StreamResponseHandler @@ -959,15 +943,7 @@ namespace Awstest std::future GetResult() noexcept; private: - std::promise m_resultPromise; - - /* Enforces a happens-before relationship between setting the self-reference and clearing it. */ - std::mutex m_selfReferenceLock; - - /* Keeps the operation alive while activation is in-progress. Internally, we capture `this` in the function - * object that handles the result. If we did not do this, we risk a crash if the user drops their reference - * before the future gets completed. */ - std::shared_ptr m_selfReference; + std::shared_ptr> m_resultPromise; }; class AWS_ECHOTESTRPC_API EchoStreamMessagesStreamHandler : public StreamResponseHandler @@ -1087,15 +1063,7 @@ namespace Awstest std::future GetResult() noexcept; private: - std::promise m_resultPromise; - - /* Enforces a happens-before relationship between setting the self-reference and clearing it. */ - std::mutex m_selfReferenceLock; - - /* Keeps the operation alive while activation is in-progress. Internally, we capture `this` in the function - * object that handles the result. If we did not do this, we risk a crash if the user drops their reference - * before the future gets completed. */ - std::shared_ptr m_selfReference; + std::shared_ptr> m_resultPromise; }; class AWS_ECHOTESTRPC_API EchoMessageOperationContext : public OperationModelContext @@ -1162,15 +1130,7 @@ namespace Awstest std::future GetResult() noexcept; private: - std::promise m_resultPromise; - - /* Enforces a happens-before relationship between setting the self-reference and clearing it. */ - std::mutex m_selfReferenceLock; - - /* Keeps the operation alive while activation is in-progress. Internally, we capture `this` in the function - * object that handles the result. If we did not do this, we risk a crash if the user drops their reference - * before the future gets completed. */ - std::shared_ptr m_selfReference; + std::shared_ptr> m_resultPromise; }; class AWS_ECHOTESTRPC_API GetAllCustomersOperationContext : public OperationModelContext @@ -1237,15 +1197,7 @@ namespace Awstest std::future GetResult() noexcept; private: - std::promise m_resultPromise; - - /* Enforces a happens-before relationship between setting the self-reference and clearing it. */ - std::mutex m_selfReferenceLock; - - /* Keeps the operation alive while activation is in-progress. Internally, we capture `this` in the function - * object that handles the result. If we did not do this, we risk a crash if the user drops their reference - * before the future gets completed. */ - std::shared_ptr m_selfReference; + std::shared_ptr> m_resultPromise; }; class AWS_ECHOTESTRPC_API EchoTestRpcServiceModel : public ServiceModel diff --git a/greengrass_ipc/include/aws/greengrass/GreengrassCoreIpcModel.h b/greengrass_ipc/include/aws/greengrass/GreengrassCoreIpcModel.h index 8cdba9b4d..0487b26ee 100644 --- a/greengrass_ipc/include/aws/greengrass/GreengrassCoreIpcModel.h +++ b/greengrass_ipc/include/aws/greengrass/GreengrassCoreIpcModel.h @@ -4544,15 +4544,7 @@ namespace Aws std::future GetResult() noexcept; private: - std::promise m_resultPromise; - - /* Enforces a happens-before relationship between setting the self-reference and clearing it. */ - std::mutex m_selfReferenceLock; - - /* Keeps the operation alive while activation is in-progress. Internally, we capture `this` in the function - * object that handles the result. If we did not do this, we risk a crash if the user drops their reference - * before the future gets completed. */ - std::shared_ptr m_selfReference; + std::shared_ptr> m_resultPromise; }; class AWS_GREENGRASSCOREIPC_API ResumeComponentOperationContext : public OperationModelContext @@ -4620,15 +4612,7 @@ namespace Aws std::future GetResult() noexcept; private: - std::promise m_resultPromise; - - /* Enforces a happens-before relationship between setting the self-reference and clearing it. */ - std::mutex m_selfReferenceLock; - - /* Keeps the operation alive while activation is in-progress. Internally, we capture `this` in the function - * object that handles the result. If we did not do this, we risk a crash if the user drops their reference - * before the future gets completed. */ - std::shared_ptr m_selfReference; + std::shared_ptr> m_resultPromise; }; class AWS_GREENGRASSCOREIPC_API PublishToIoTCoreOperationContext : public OperationModelContext @@ -4696,15 +4680,7 @@ namespace Aws std::future GetResult() noexcept; private: - std::promise m_resultPromise; - - /* Enforces a happens-before relationship between setting the self-reference and clearing it. */ - std::mutex m_selfReferenceLock; - - /* Keeps the operation alive while activation is in-progress. Internally, we capture `this` in the function - * object that handles the result. If we did not do this, we risk a crash if the user drops their reference - * before the future gets completed. */ - std::shared_ptr m_selfReference; + std::shared_ptr> m_resultPromise; }; class AWS_GREENGRASSCOREIPC_API SubscribeToConfigurationUpdateStreamHandler : public StreamResponseHandler @@ -4835,15 +4811,7 @@ namespace Aws std::future GetResult() noexcept; private: - std::promise m_resultPromise; - - /* Enforces a happens-before relationship between setting the self-reference and clearing it. */ - std::mutex m_selfReferenceLock; - - /* Keeps the operation alive while activation is in-progress. Internally, we capture `this` in the function - * object that handles the result. If we did not do this, we risk a crash if the user drops their reference - * before the future gets completed. */ - std::shared_ptr m_selfReference; + std::shared_ptr> m_resultPromise; }; class AWS_GREENGRASSCOREIPC_API DeleteThingShadowOperationContext : public OperationModelContext @@ -4911,15 +4879,7 @@ namespace Aws std::future GetResult() noexcept; private: - std::promise m_resultPromise; - - /* Enforces a happens-before relationship between setting the self-reference and clearing it. */ - std::mutex m_selfReferenceLock; - - /* Keeps the operation alive while activation is in-progress. Internally, we capture `this` in the function - * object that handles the result. If we did not do this, we risk a crash if the user drops their reference - * before the future gets completed. */ - std::shared_ptr m_selfReference; + std::shared_ptr> m_resultPromise; }; class AWS_GREENGRASSCOREIPC_API PutComponentMetricOperationContext : public OperationModelContext @@ -4987,15 +4947,7 @@ namespace Aws std::future GetResult() noexcept; private: - std::promise m_resultPromise; - - /* Enforces a happens-before relationship between setting the self-reference and clearing it. */ - std::mutex m_selfReferenceLock; - - /* Keeps the operation alive while activation is in-progress. Internally, we capture `this` in the function - * object that handles the result. If we did not do this, we risk a crash if the user drops their reference - * before the future gets completed. */ - std::shared_ptr m_selfReference; + std::shared_ptr> m_resultPromise; }; class AWS_GREENGRASSCOREIPC_API DeferComponentUpdateOperationContext : public OperationModelContext @@ -5065,15 +5017,7 @@ namespace Aws std::future GetResult() noexcept; private: - std::promise m_resultPromise; - - /* Enforces a happens-before relationship between setting the self-reference and clearing it. */ - std::mutex m_selfReferenceLock; - - /* Keeps the operation alive while activation is in-progress. Internally, we capture `this` in the function - * object that handles the result. If we did not do this, we risk a crash if the user drops their reference - * before the future gets completed. */ - std::shared_ptr m_selfReference; + std::shared_ptr> m_resultPromise; }; class AWS_GREENGRASSCOREIPC_API SubscribeToValidateConfigurationUpdatesStreamHandler @@ -5198,15 +5142,7 @@ namespace Aws std::future GetResult() noexcept; private: - std::promise m_resultPromise; - - /* Enforces a happens-before relationship between setting the self-reference and clearing it. */ - std::mutex m_selfReferenceLock; - - /* Keeps the operation alive while activation is in-progress. Internally, we capture `this` in the function - * object that handles the result. If we did not do this, we risk a crash if the user drops their reference - * before the future gets completed. */ - std::shared_ptr m_selfReference; + std::shared_ptr> m_resultPromise; }; class AWS_GREENGRASSCOREIPC_API GetConfigurationOperationContext : public OperationModelContext @@ -5274,15 +5210,7 @@ namespace Aws std::future GetResult() noexcept; private: - std::promise m_resultPromise; - - /* Enforces a happens-before relationship between setting the self-reference and clearing it. */ - std::mutex m_selfReferenceLock; - - /* Keeps the operation alive while activation is in-progress. Internally, we capture `this` in the function - * object that handles the result. If we did not do this, we risk a crash if the user drops their reference - * before the future gets completed. */ - std::shared_ptr m_selfReference; + std::shared_ptr> m_resultPromise; }; class AWS_GREENGRASSCOREIPC_API SubscribeToTopicStreamHandler : public StreamResponseHandler @@ -5420,15 +5348,7 @@ namespace Aws std::future GetResult() noexcept; private: - std::promise m_resultPromise; - - /* Enforces a happens-before relationship between setting the self-reference and clearing it. */ - std::mutex m_selfReferenceLock; - - /* Keeps the operation alive while activation is in-progress. Internally, we capture `this` in the function - * object that handles the result. If we did not do this, we risk a crash if the user drops their reference - * before the future gets completed. */ - std::shared_ptr m_selfReference; + std::shared_ptr> m_resultPromise; }; class AWS_GREENGRASSCOREIPC_API GetComponentDetailsOperationContext : public OperationModelContext @@ -5496,15 +5416,7 @@ namespace Aws std::future GetResult() noexcept; private: - std::promise m_resultPromise; - - /* Enforces a happens-before relationship between setting the self-reference and clearing it. */ - std::mutex m_selfReferenceLock; - - /* Keeps the operation alive while activation is in-progress. Internally, we capture `this` in the function - * object that handles the result. If we did not do this, we risk a crash if the user drops their reference - * before the future gets completed. */ - std::shared_ptr m_selfReference; + std::shared_ptr> m_resultPromise; }; class AWS_GREENGRASSCOREIPC_API GetClientDeviceAuthTokenOperationContext : public OperationModelContext @@ -5575,15 +5487,7 @@ namespace Aws std::future GetResult() noexcept; private: - std::promise m_resultPromise; - - /* Enforces a happens-before relationship between setting the self-reference and clearing it. */ - std::mutex m_selfReferenceLock; - - /* Keeps the operation alive while activation is in-progress. Internally, we capture `this` in the function - * object that handles the result. If we did not do this, we risk a crash if the user drops their reference - * before the future gets completed. */ - std::shared_ptr m_selfReference; + std::shared_ptr> m_resultPromise; }; class AWS_GREENGRASSCOREIPC_API PublishToTopicOperationContext : public OperationModelContext @@ -5651,15 +5555,7 @@ namespace Aws std::future GetResult() noexcept; private: - std::promise m_resultPromise; - - /* Enforces a happens-before relationship between setting the self-reference and clearing it. */ - std::mutex m_selfReferenceLock; - - /* Keeps the operation alive while activation is in-progress. Internally, we capture `this` in the function - * object that handles the result. If we did not do this, we risk a crash if the user drops their reference - * before the future gets completed. */ - std::shared_ptr m_selfReference; + std::shared_ptr> m_resultPromise; }; class AWS_GREENGRASSCOREIPC_API SubscribeToCertificateUpdatesStreamHandler : public StreamResponseHandler @@ -5800,15 +5696,7 @@ namespace Aws std::future GetResult() noexcept; private: - std::promise m_resultPromise; - - /* Enforces a happens-before relationship between setting the self-reference and clearing it. */ - std::mutex m_selfReferenceLock; - - /* Keeps the operation alive while activation is in-progress. Internally, we capture `this` in the function - * object that handles the result. If we did not do this, we risk a crash if the user drops their reference - * before the future gets completed. */ - std::shared_ptr m_selfReference; + std::shared_ptr> m_resultPromise; }; class AWS_GREENGRASSCOREIPC_API VerifyClientDeviceIdentityOperationContext : public OperationModelContext @@ -5879,15 +5767,7 @@ namespace Aws std::future GetResult() noexcept; private: - std::promise m_resultPromise; - - /* Enforces a happens-before relationship between setting the self-reference and clearing it. */ - std::mutex m_selfReferenceLock; - - /* Keeps the operation alive while activation is in-progress. Internally, we capture `this` in the function - * object that handles the result. If we did not do this, we risk a crash if the user drops their reference - * before the future gets completed. */ - std::shared_ptr m_selfReference; + std::shared_ptr> m_resultPromise; }; class AWS_GREENGRASSCOREIPC_API AuthorizeClientDeviceActionOperationContext : public OperationModelContext @@ -5958,15 +5838,7 @@ namespace Aws std::future GetResult() noexcept; private: - std::promise m_resultPromise; - - /* Enforces a happens-before relationship between setting the self-reference and clearing it. */ - std::mutex m_selfReferenceLock; - - /* Keeps the operation alive while activation is in-progress. Internally, we capture `this` in the function - * object that handles the result. If we did not do this, we risk a crash if the user drops their reference - * before the future gets completed. */ - std::shared_ptr m_selfReference; + std::shared_ptr> m_resultPromise; }; class AWS_GREENGRASSCOREIPC_API ListComponentsOperationContext : public OperationModelContext @@ -6034,15 +5906,7 @@ namespace Aws std::future GetResult() noexcept; private: - std::promise m_resultPromise; - - /* Enforces a happens-before relationship between setting the self-reference and clearing it. */ - std::mutex m_selfReferenceLock; - - /* Keeps the operation alive while activation is in-progress. Internally, we capture `this` in the function - * object that handles the result. If we did not do this, we risk a crash if the user drops their reference - * before the future gets completed. */ - std::shared_ptr m_selfReference; + std::shared_ptr> m_resultPromise; }; class AWS_GREENGRASSCOREIPC_API CreateDebugPasswordOperationContext : public OperationModelContext @@ -6110,15 +5974,7 @@ namespace Aws std::future GetResult() noexcept; private: - std::promise m_resultPromise; - - /* Enforces a happens-before relationship between setting the self-reference and clearing it. */ - std::mutex m_selfReferenceLock; - - /* Keeps the operation alive while activation is in-progress. Internally, we capture `this` in the function - * object that handles the result. If we did not do this, we risk a crash if the user drops their reference - * before the future gets completed. */ - std::shared_ptr m_selfReference; + std::shared_ptr> m_resultPromise; }; class AWS_GREENGRASSCOREIPC_API GetThingShadowOperationContext : public OperationModelContext @@ -6186,15 +6042,7 @@ namespace Aws std::future GetResult() noexcept; private: - std::promise m_resultPromise; - - /* Enforces a happens-before relationship between setting the self-reference and clearing it. */ - std::mutex m_selfReferenceLock; - - /* Keeps the operation alive while activation is in-progress. Internally, we capture `this` in the function - * object that handles the result. If we did not do this, we risk a crash if the user drops their reference - * before the future gets completed. */ - std::shared_ptr m_selfReference; + std::shared_ptr> m_resultPromise; }; class AWS_GREENGRASSCOREIPC_API SendConfigurationValidityReportOperationContext : public OperationModelContext @@ -6265,15 +6113,7 @@ namespace Aws std::future GetResult() noexcept; private: - std::promise m_resultPromise; - - /* Enforces a happens-before relationship between setting the self-reference and clearing it. */ - std::mutex m_selfReferenceLock; - - /* Keeps the operation alive while activation is in-progress. Internally, we capture `this` in the function - * object that handles the result. If we did not do this, we risk a crash if the user drops their reference - * before the future gets completed. */ - std::shared_ptr m_selfReference; + std::shared_ptr> m_resultPromise; }; class AWS_GREENGRASSCOREIPC_API UpdateThingShadowOperationContext : public OperationModelContext @@ -6341,15 +6181,7 @@ namespace Aws std::future GetResult() noexcept; private: - std::promise m_resultPromise; - - /* Enforces a happens-before relationship between setting the self-reference and clearing it. */ - std::mutex m_selfReferenceLock; - - /* Keeps the operation alive while activation is in-progress. Internally, we capture `this` in the function - * object that handles the result. If we did not do this, we risk a crash if the user drops their reference - * before the future gets completed. */ - std::shared_ptr m_selfReference; + std::shared_ptr> m_resultPromise; }; class AWS_GREENGRASSCOREIPC_API UpdateConfigurationOperationContext : public OperationModelContext @@ -6417,15 +6249,7 @@ namespace Aws std::future GetResult() noexcept; private: - std::promise m_resultPromise; - - /* Enforces a happens-before relationship between setting the self-reference and clearing it. */ - std::mutex m_selfReferenceLock; - - /* Keeps the operation alive while activation is in-progress. Internally, we capture `this` in the function - * object that handles the result. If we did not do this, we risk a crash if the user drops their reference - * before the future gets completed. */ - std::shared_ptr m_selfReference; + std::shared_ptr> m_resultPromise; }; class AWS_GREENGRASSCOREIPC_API ValidateAuthorizationTokenOperationContext : public OperationModelContext @@ -6496,15 +6320,7 @@ namespace Aws std::future GetResult() noexcept; private: - std::promise m_resultPromise; - - /* Enforces a happens-before relationship between setting the self-reference and clearing it. */ - std::mutex m_selfReferenceLock; - - /* Keeps the operation alive while activation is in-progress. Internally, we capture `this` in the function - * object that handles the result. If we did not do this, we risk a crash if the user drops their reference - * before the future gets completed. */ - std::shared_ptr m_selfReference; + std::shared_ptr> m_resultPromise; }; class AWS_GREENGRASSCOREIPC_API RestartComponentOperationContext : public OperationModelContext @@ -6572,15 +6388,7 @@ namespace Aws std::future GetResult() noexcept; private: - std::promise m_resultPromise; - - /* Enforces a happens-before relationship between setting the self-reference and clearing it. */ - std::mutex m_selfReferenceLock; - - /* Keeps the operation alive while activation is in-progress. Internally, we capture `this` in the function - * object that handles the result. If we did not do this, we risk a crash if the user drops their reference - * before the future gets completed. */ - std::shared_ptr m_selfReference; + std::shared_ptr> m_resultPromise; }; class AWS_GREENGRASSCOREIPC_API GetLocalDeploymentStatusOperationContext : public OperationModelContext @@ -6651,15 +6459,7 @@ namespace Aws std::future GetResult() noexcept; private: - std::promise m_resultPromise; - - /* Enforces a happens-before relationship between setting the self-reference and clearing it. */ - std::mutex m_selfReferenceLock; - - /* Keeps the operation alive while activation is in-progress. Internally, we capture `this` in the function - * object that handles the result. If we did not do this, we risk a crash if the user drops their reference - * before the future gets completed. */ - std::shared_ptr m_selfReference; + std::shared_ptr> m_resultPromise; }; class AWS_GREENGRASSCOREIPC_API GetSecretValueOperationContext : public OperationModelContext @@ -6727,15 +6527,7 @@ namespace Aws std::future GetResult() noexcept; private: - std::promise m_resultPromise; - - /* Enforces a happens-before relationship between setting the self-reference and clearing it. */ - std::mutex m_selfReferenceLock; - - /* Keeps the operation alive while activation is in-progress. Internally, we capture `this` in the function - * object that handles the result. If we did not do this, we risk a crash if the user drops their reference - * before the future gets completed. */ - std::shared_ptr m_selfReference; + std::shared_ptr> m_resultPromise; }; class AWS_GREENGRASSCOREIPC_API UpdateStateOperationContext : public OperationModelContext @@ -6802,15 +6594,7 @@ namespace Aws std::future GetResult() noexcept; private: - std::promise m_resultPromise; - - /* Enforces a happens-before relationship between setting the self-reference and clearing it. */ - std::mutex m_selfReferenceLock; - - /* Keeps the operation alive while activation is in-progress. Internally, we capture `this` in the function - * object that handles the result. If we did not do this, we risk a crash if the user drops their reference - * before the future gets completed. */ - std::shared_ptr m_selfReference; + std::shared_ptr> m_resultPromise; }; class AWS_GREENGRASSCOREIPC_API CancelLocalDeploymentOperationContext : public OperationModelContext @@ -6880,15 +6664,7 @@ namespace Aws std::future GetResult() noexcept; private: - std::promise m_resultPromise; - - /* Enforces a happens-before relationship between setting the self-reference and clearing it. */ - std::mutex m_selfReferenceLock; - - /* Keeps the operation alive while activation is in-progress. Internally, we capture `this` in the function - * object that handles the result. If we did not do this, we risk a crash if the user drops their reference - * before the future gets completed. */ - std::shared_ptr m_selfReference; + std::shared_ptr> m_resultPromise; }; class AWS_GREENGRASSCOREIPC_API ListNamedShadowsForThingOperationContext : public OperationModelContext @@ -6959,15 +6735,7 @@ namespace Aws std::future GetResult() noexcept; private: - std::promise m_resultPromise; - - /* Enforces a happens-before relationship between setting the self-reference and clearing it. */ - std::mutex m_selfReferenceLock; - - /* Keeps the operation alive while activation is in-progress. Internally, we capture `this` in the function - * object that handles the result. If we did not do this, we risk a crash if the user drops their reference - * before the future gets completed. */ - std::shared_ptr m_selfReference; + std::shared_ptr> m_resultPromise; }; class AWS_GREENGRASSCOREIPC_API SubscribeToComponentUpdatesStreamHandler : public StreamResponseHandler @@ -7098,15 +6866,7 @@ namespace Aws std::future GetResult() noexcept; private: - std::promise m_resultPromise; - - /* Enforces a happens-before relationship between setting the self-reference and clearing it. */ - std::mutex m_selfReferenceLock; - - /* Keeps the operation alive while activation is in-progress. Internally, we capture `this` in the function - * object that handles the result. If we did not do this, we risk a crash if the user drops their reference - * before the future gets completed. */ - std::shared_ptr m_selfReference; + std::shared_ptr> m_resultPromise; }; class AWS_GREENGRASSCOREIPC_API ListLocalDeploymentsOperationContext : public OperationModelContext @@ -7176,15 +6936,7 @@ namespace Aws std::future GetResult() noexcept; private: - std::promise m_resultPromise; - - /* Enforces a happens-before relationship between setting the self-reference and clearing it. */ - std::mutex m_selfReferenceLock; - - /* Keeps the operation alive while activation is in-progress. Internally, we capture `this` in the function - * object that handles the result. If we did not do this, we risk a crash if the user drops their reference - * before the future gets completed. */ - std::shared_ptr m_selfReference; + std::shared_ptr> m_resultPromise; }; class AWS_GREENGRASSCOREIPC_API StopComponentOperationContext : public OperationModelContext @@ -7252,15 +7004,7 @@ namespace Aws std::future GetResult() noexcept; private: - std::promise m_resultPromise; - - /* Enforces a happens-before relationship between setting the self-reference and clearing it. */ - std::mutex m_selfReferenceLock; - - /* Keeps the operation alive while activation is in-progress. Internally, we capture `this` in the function - * object that handles the result. If we did not do this, we risk a crash if the user drops their reference - * before the future gets completed. */ - std::shared_ptr m_selfReference; + std::shared_ptr> m_resultPromise; }; class AWS_GREENGRASSCOREIPC_API PauseComponentOperationContext : public OperationModelContext @@ -7328,15 +7072,7 @@ namespace Aws std::future GetResult() noexcept; private: - std::promise m_resultPromise; - - /* Enforces a happens-before relationship between setting the self-reference and clearing it. */ - std::mutex m_selfReferenceLock; - - /* Keeps the operation alive while activation is in-progress. Internally, we capture `this` in the function - * object that handles the result. If we did not do this, we risk a crash if the user drops their reference - * before the future gets completed. */ - std::shared_ptr m_selfReference; + std::shared_ptr> m_resultPromise; }; class AWS_GREENGRASSCOREIPC_API CreateLocalDeploymentOperationContext : public OperationModelContext @@ -7406,15 +7142,7 @@ namespace Aws std::future GetResult() noexcept; private: - std::promise m_resultPromise; - - /* Enforces a happens-before relationship between setting the self-reference and clearing it. */ - std::mutex m_selfReferenceLock; - - /* Keeps the operation alive while activation is in-progress. Internally, we capture `this` in the function - * object that handles the result. If we did not do this, we risk a crash if the user drops their reference - * before the future gets completed. */ - std::shared_ptr m_selfReference; + std::shared_ptr> m_resultPromise; }; class AWS_GREENGRASSCOREIPC_API GreengrassCoreIpcServiceModel : public ServiceModel diff --git a/greengrass_ipc/source/GreengrassCoreIpcModel.cpp b/greengrass_ipc/source/GreengrassCoreIpcModel.cpp index c39dfddfc..c20f3a32a 100644 --- a/greengrass_ipc/source/GreengrassCoreIpcModel.cpp +++ b/greengrass_ipc/source/GreengrassCoreIpcModel.cpp @@ -7041,7 +7041,7 @@ namespace Aws std::future SubscribeToIoTCoreOperation::GetResult() noexcept { - return m_resultPromise.get_future(); + return m_resultPromise->get_future(); } SubscribeToIoTCoreOperation::SubscribeToIoTCoreOperation( @@ -7049,7 +7049,8 @@ namespace Aws std::shared_ptr streamHandler, const SubscribeToIoTCoreOperationContext &operationContext, Aws::Crt::Allocator *allocator) noexcept - : ClientOperation(connection, streamHandler, operationContext, allocator) + : ClientOperation(connection, streamHandler, operationContext, allocator), + m_resultPromise(Aws::Crt::MakeShared>(allocator)) { } @@ -7057,23 +7058,14 @@ namespace Aws const SubscribeToIoTCoreRequest &request, OnMessageFlushCallback onMessageFlushCallback) noexcept { - bool synchronousSuccess = false; - std::lock_guard selfReferenceLock(m_selfReferenceLock); + auto promiseReference = m_resultPromise; + auto activateFuture = ClientOperation::Activate( static_cast(&request), std::move(onMessageFlushCallback), - [this](TaggedResult &&unmodeledResult) - { - std::lock_guard selfReferenceLock(m_selfReferenceLock); - m_resultPromise.set_value(SubscribeToIoTCoreResult(std::move(unmodeledResult))); - m_selfReference = nullptr; - }, - synchronousSuccess); - if (synchronousSuccess) - { - m_selfReference = shared_from_this(); - ; - } + [promiseReference](TaggedResult &&unmodeledResult) + { promiseReference->set_value(SubscribeToIoTCoreResult(std::move(unmodeledResult))); }); + return activateFuture; } @@ -7122,14 +7114,15 @@ namespace Aws std::future ResumeComponentOperation::GetResult() noexcept { - return m_resultPromise.get_future(); + return m_resultPromise->get_future(); } ResumeComponentOperation::ResumeComponentOperation( ClientConnection &connection, const ResumeComponentOperationContext &operationContext, Aws::Crt::Allocator *allocator) noexcept - : ClientOperation(connection, nullptr, operationContext, allocator) + : ClientOperation(connection, nullptr, operationContext, allocator), + m_resultPromise(Aws::Crt::MakeShared>(allocator)) { } @@ -7137,23 +7130,14 @@ namespace Aws const ResumeComponentRequest &request, OnMessageFlushCallback onMessageFlushCallback) noexcept { - bool synchronousSuccess = false; - std::lock_guard selfReferenceLock(m_selfReferenceLock); + auto promiseReference = m_resultPromise; + auto activateFuture = ClientOperation::Activate( static_cast(&request), std::move(onMessageFlushCallback), - [this](TaggedResult &&unmodeledResult) - { - std::lock_guard selfReferenceLock(m_selfReferenceLock); - m_resultPromise.set_value(ResumeComponentResult(std::move(unmodeledResult))); - m_selfReference = nullptr; - }, - synchronousSuccess); - if (synchronousSuccess) - { - m_selfReference = shared_from_this(); - ; - } + [promiseReference](TaggedResult &&unmodeledResult) + { promiseReference->set_value(ResumeComponentResult(std::move(unmodeledResult))); }); + return activateFuture; } @@ -7202,14 +7186,15 @@ namespace Aws std::future PublishToIoTCoreOperation::GetResult() noexcept { - return m_resultPromise.get_future(); + return m_resultPromise->get_future(); } PublishToIoTCoreOperation::PublishToIoTCoreOperation( ClientConnection &connection, const PublishToIoTCoreOperationContext &operationContext, Aws::Crt::Allocator *allocator) noexcept - : ClientOperation(connection, nullptr, operationContext, allocator) + : ClientOperation(connection, nullptr, operationContext, allocator), + m_resultPromise(Aws::Crt::MakeShared>(allocator)) { } @@ -7217,23 +7202,14 @@ namespace Aws const PublishToIoTCoreRequest &request, OnMessageFlushCallback onMessageFlushCallback) noexcept { - bool synchronousSuccess = false; - std::lock_guard selfReferenceLock(m_selfReferenceLock); + auto promiseReference = m_resultPromise; + auto activateFuture = ClientOperation::Activate( static_cast(&request), std::move(onMessageFlushCallback), - [this](TaggedResult &&unmodeledResult) - { - std::lock_guard selfReferenceLock(m_selfReferenceLock); - m_resultPromise.set_value(PublishToIoTCoreResult(std::move(unmodeledResult))); - m_selfReference = nullptr; - }, - synchronousSuccess); - if (synchronousSuccess) - { - m_selfReference = shared_from_this(); - ; - } + [promiseReference](TaggedResult &&unmodeledResult) + { promiseReference->set_value(PublishToIoTCoreResult(std::move(unmodeledResult))); }); + return activateFuture; } @@ -7312,7 +7288,7 @@ namespace Aws std::future SubscribeToConfigurationUpdateOperation::GetResult() noexcept { - return m_resultPromise.get_future(); + return m_resultPromise->get_future(); } SubscribeToConfigurationUpdateOperation::SubscribeToConfigurationUpdateOperation( @@ -7320,7 +7296,8 @@ namespace Aws std::shared_ptr streamHandler, const SubscribeToConfigurationUpdateOperationContext &operationContext, Aws::Crt::Allocator *allocator) noexcept - : ClientOperation(connection, streamHandler, operationContext, allocator) + : ClientOperation(connection, streamHandler, operationContext, allocator), + m_resultPromise(Aws::Crt::MakeShared>(allocator)) { } @@ -7328,23 +7305,14 @@ namespace Aws const SubscribeToConfigurationUpdateRequest &request, OnMessageFlushCallback onMessageFlushCallback) noexcept { - bool synchronousSuccess = false; - std::lock_guard selfReferenceLock(m_selfReferenceLock); + auto promiseReference = m_resultPromise; + auto activateFuture = ClientOperation::Activate( static_cast(&request), std::move(onMessageFlushCallback), - [this](TaggedResult &&unmodeledResult) - { - std::lock_guard selfReferenceLock(m_selfReferenceLock); - m_resultPromise.set_value(SubscribeToConfigurationUpdateResult(std::move(unmodeledResult))); - m_selfReference = nullptr; - }, - synchronousSuccess); - if (synchronousSuccess) - { - m_selfReference = shared_from_this(); - ; - } + [promiseReference](TaggedResult &&unmodeledResult) + { promiseReference->set_value(SubscribeToConfigurationUpdateResult(std::move(unmodeledResult))); }); + return activateFuture; } @@ -7393,14 +7361,15 @@ namespace Aws std::future DeleteThingShadowOperation::GetResult() noexcept { - return m_resultPromise.get_future(); + return m_resultPromise->get_future(); } DeleteThingShadowOperation::DeleteThingShadowOperation( ClientConnection &connection, const DeleteThingShadowOperationContext &operationContext, Aws::Crt::Allocator *allocator) noexcept - : ClientOperation(connection, nullptr, operationContext, allocator) + : ClientOperation(connection, nullptr, operationContext, allocator), + m_resultPromise(Aws::Crt::MakeShared>(allocator)) { } @@ -7408,23 +7377,14 @@ namespace Aws const DeleteThingShadowRequest &request, OnMessageFlushCallback onMessageFlushCallback) noexcept { - bool synchronousSuccess = false; - std::lock_guard selfReferenceLock(m_selfReferenceLock); + auto promiseReference = m_resultPromise; + auto activateFuture = ClientOperation::Activate( static_cast(&request), std::move(onMessageFlushCallback), - [this](TaggedResult &&unmodeledResult) - { - std::lock_guard selfReferenceLock(m_selfReferenceLock); - m_resultPromise.set_value(DeleteThingShadowResult(std::move(unmodeledResult))); - m_selfReference = nullptr; - }, - synchronousSuccess); - if (synchronousSuccess) - { - m_selfReference = shared_from_this(); - ; - } + [promiseReference](TaggedResult &&unmodeledResult) + { promiseReference->set_value(DeleteThingShadowResult(std::move(unmodeledResult))); }); + return activateFuture; } @@ -7473,14 +7433,15 @@ namespace Aws std::future PutComponentMetricOperation::GetResult() noexcept { - return m_resultPromise.get_future(); + return m_resultPromise->get_future(); } PutComponentMetricOperation::PutComponentMetricOperation( ClientConnection &connection, const PutComponentMetricOperationContext &operationContext, Aws::Crt::Allocator *allocator) noexcept - : ClientOperation(connection, nullptr, operationContext, allocator) + : ClientOperation(connection, nullptr, operationContext, allocator), + m_resultPromise(Aws::Crt::MakeShared>(allocator)) { } @@ -7488,23 +7449,14 @@ namespace Aws const PutComponentMetricRequest &request, OnMessageFlushCallback onMessageFlushCallback) noexcept { - bool synchronousSuccess = false; - std::lock_guard selfReferenceLock(m_selfReferenceLock); + auto promiseReference = m_resultPromise; + auto activateFuture = ClientOperation::Activate( static_cast(&request), std::move(onMessageFlushCallback), - [this](TaggedResult &&unmodeledResult) - { - std::lock_guard selfReferenceLock(m_selfReferenceLock); - m_resultPromise.set_value(PutComponentMetricResult(std::move(unmodeledResult))); - m_selfReference = nullptr; - }, - synchronousSuccess); - if (synchronousSuccess) - { - m_selfReference = shared_from_this(); - ; - } + [promiseReference](TaggedResult &&unmodeledResult) + { promiseReference->set_value(PutComponentMetricResult(std::move(unmodeledResult))); }); + return activateFuture; } @@ -7553,14 +7505,15 @@ namespace Aws std::future DeferComponentUpdateOperation::GetResult() noexcept { - return m_resultPromise.get_future(); + return m_resultPromise->get_future(); } DeferComponentUpdateOperation::DeferComponentUpdateOperation( ClientConnection &connection, const DeferComponentUpdateOperationContext &operationContext, Aws::Crt::Allocator *allocator) noexcept - : ClientOperation(connection, nullptr, operationContext, allocator) + : ClientOperation(connection, nullptr, operationContext, allocator), + m_resultPromise(Aws::Crt::MakeShared>(allocator)) { } @@ -7568,23 +7521,14 @@ namespace Aws const DeferComponentUpdateRequest &request, OnMessageFlushCallback onMessageFlushCallback) noexcept { - bool synchronousSuccess = false; - std::lock_guard selfReferenceLock(m_selfReferenceLock); + auto promiseReference = m_resultPromise; + auto activateFuture = ClientOperation::Activate( static_cast(&request), std::move(onMessageFlushCallback), - [this](TaggedResult &&unmodeledResult) - { - std::lock_guard selfReferenceLock(m_selfReferenceLock); - m_resultPromise.set_value(DeferComponentUpdateResult(std::move(unmodeledResult))); - m_selfReference = nullptr; - }, - synchronousSuccess); - if (synchronousSuccess) - { - m_selfReference = shared_from_this(); - ; - } + [promiseReference](TaggedResult &&unmodeledResult) + { promiseReference->set_value(DeferComponentUpdateResult(std::move(unmodeledResult))); }); + return activateFuture; } @@ -7660,7 +7604,7 @@ namespace Aws std::future SubscribeToValidateConfigurationUpdatesOperation:: GetResult() noexcept { - return m_resultPromise.get_future(); + return m_resultPromise->get_future(); } SubscribeToValidateConfigurationUpdatesOperation::SubscribeToValidateConfigurationUpdatesOperation( @@ -7668,7 +7612,9 @@ namespace Aws std::shared_ptr streamHandler, const SubscribeToValidateConfigurationUpdatesOperationContext &operationContext, Aws::Crt::Allocator *allocator) noexcept - : ClientOperation(connection, streamHandler, operationContext, allocator) + : ClientOperation(connection, streamHandler, operationContext, allocator), + m_resultPromise( + Aws::Crt::MakeShared>(allocator)) { } @@ -7676,24 +7622,16 @@ namespace Aws const SubscribeToValidateConfigurationUpdatesRequest &request, OnMessageFlushCallback onMessageFlushCallback) noexcept { - bool synchronousSuccess = false; - std::lock_guard selfReferenceLock(m_selfReferenceLock); + auto promiseReference = m_resultPromise; + auto activateFuture = ClientOperation::Activate( static_cast(&request), std::move(onMessageFlushCallback), - [this](TaggedResult &&unmodeledResult) - { - std::lock_guard selfReferenceLock(m_selfReferenceLock); - m_resultPromise.set_value( + [promiseReference](TaggedResult &&unmodeledResult) { + promiseReference->set_value( SubscribeToValidateConfigurationUpdatesResult(std::move(unmodeledResult))); - m_selfReference = nullptr; - }, - synchronousSuccess); - if (synchronousSuccess) - { - m_selfReference = shared_from_this(); - ; - } + }); + return activateFuture; } @@ -7742,14 +7680,15 @@ namespace Aws std::future GetConfigurationOperation::GetResult() noexcept { - return m_resultPromise.get_future(); + return m_resultPromise->get_future(); } GetConfigurationOperation::GetConfigurationOperation( ClientConnection &connection, const GetConfigurationOperationContext &operationContext, Aws::Crt::Allocator *allocator) noexcept - : ClientOperation(connection, nullptr, operationContext, allocator) + : ClientOperation(connection, nullptr, operationContext, allocator), + m_resultPromise(Aws::Crt::MakeShared>(allocator)) { } @@ -7757,23 +7696,14 @@ namespace Aws const GetConfigurationRequest &request, OnMessageFlushCallback onMessageFlushCallback) noexcept { - bool synchronousSuccess = false; - std::lock_guard selfReferenceLock(m_selfReferenceLock); + auto promiseReference = m_resultPromise; + auto activateFuture = ClientOperation::Activate( static_cast(&request), std::move(onMessageFlushCallback), - [this](TaggedResult &&unmodeledResult) - { - std::lock_guard selfReferenceLock(m_selfReferenceLock); - m_resultPromise.set_value(GetConfigurationResult(std::move(unmodeledResult))); - m_selfReference = nullptr; - }, - synchronousSuccess); - if (synchronousSuccess) - { - m_selfReference = shared_from_this(); - ; - } + [promiseReference](TaggedResult &&unmodeledResult) + { promiseReference->set_value(GetConfigurationResult(std::move(unmodeledResult))); }); + return activateFuture; } @@ -7857,7 +7787,7 @@ namespace Aws std::future SubscribeToTopicOperation::GetResult() noexcept { - return m_resultPromise.get_future(); + return m_resultPromise->get_future(); } SubscribeToTopicOperation::SubscribeToTopicOperation( @@ -7865,7 +7795,8 @@ namespace Aws std::shared_ptr streamHandler, const SubscribeToTopicOperationContext &operationContext, Aws::Crt::Allocator *allocator) noexcept - : ClientOperation(connection, streamHandler, operationContext, allocator) + : ClientOperation(connection, streamHandler, operationContext, allocator), + m_resultPromise(Aws::Crt::MakeShared>(allocator)) { } @@ -7873,23 +7804,14 @@ namespace Aws const SubscribeToTopicRequest &request, OnMessageFlushCallback onMessageFlushCallback) noexcept { - bool synchronousSuccess = false; - std::lock_guard selfReferenceLock(m_selfReferenceLock); + auto promiseReference = m_resultPromise; + auto activateFuture = ClientOperation::Activate( static_cast(&request), std::move(onMessageFlushCallback), - [this](TaggedResult &&unmodeledResult) - { - std::lock_guard selfReferenceLock(m_selfReferenceLock); - m_resultPromise.set_value(SubscribeToTopicResult(std::move(unmodeledResult))); - m_selfReference = nullptr; - }, - synchronousSuccess); - if (synchronousSuccess) - { - m_selfReference = shared_from_this(); - ; - } + [promiseReference](TaggedResult &&unmodeledResult) + { promiseReference->set_value(SubscribeToTopicResult(std::move(unmodeledResult))); }); + return activateFuture; } @@ -7938,14 +7860,15 @@ namespace Aws std::future GetComponentDetailsOperation::GetResult() noexcept { - return m_resultPromise.get_future(); + return m_resultPromise->get_future(); } GetComponentDetailsOperation::GetComponentDetailsOperation( ClientConnection &connection, const GetComponentDetailsOperationContext &operationContext, Aws::Crt::Allocator *allocator) noexcept - : ClientOperation(connection, nullptr, operationContext, allocator) + : ClientOperation(connection, nullptr, operationContext, allocator), + m_resultPromise(Aws::Crt::MakeShared>(allocator)) { } @@ -7953,23 +7876,14 @@ namespace Aws const GetComponentDetailsRequest &request, OnMessageFlushCallback onMessageFlushCallback) noexcept { - bool synchronousSuccess = false; - std::lock_guard selfReferenceLock(m_selfReferenceLock); + auto promiseReference = m_resultPromise; + auto activateFuture = ClientOperation::Activate( static_cast(&request), std::move(onMessageFlushCallback), - [this](TaggedResult &&unmodeledResult) - { - std::lock_guard selfReferenceLock(m_selfReferenceLock); - m_resultPromise.set_value(GetComponentDetailsResult(std::move(unmodeledResult))); - m_selfReference = nullptr; - }, - synchronousSuccess); - if (synchronousSuccess) - { - m_selfReference = shared_from_this(); - ; - } + [promiseReference](TaggedResult &&unmodeledResult) + { promiseReference->set_value(GetComponentDetailsResult(std::move(unmodeledResult))); }); + return activateFuture; } @@ -8018,14 +7932,15 @@ namespace Aws std::future GetClientDeviceAuthTokenOperation::GetResult() noexcept { - return m_resultPromise.get_future(); + return m_resultPromise->get_future(); } GetClientDeviceAuthTokenOperation::GetClientDeviceAuthTokenOperation( ClientConnection &connection, const GetClientDeviceAuthTokenOperationContext &operationContext, Aws::Crt::Allocator *allocator) noexcept - : ClientOperation(connection, nullptr, operationContext, allocator) + : ClientOperation(connection, nullptr, operationContext, allocator), + m_resultPromise(Aws::Crt::MakeShared>(allocator)) { } @@ -8033,23 +7948,14 @@ namespace Aws const GetClientDeviceAuthTokenRequest &request, OnMessageFlushCallback onMessageFlushCallback) noexcept { - bool synchronousSuccess = false; - std::lock_guard selfReferenceLock(m_selfReferenceLock); + auto promiseReference = m_resultPromise; + auto activateFuture = ClientOperation::Activate( static_cast(&request), std::move(onMessageFlushCallback), - [this](TaggedResult &&unmodeledResult) - { - std::lock_guard selfReferenceLock(m_selfReferenceLock); - m_resultPromise.set_value(GetClientDeviceAuthTokenResult(std::move(unmodeledResult))); - m_selfReference = nullptr; - }, - synchronousSuccess); - if (synchronousSuccess) - { - m_selfReference = shared_from_this(); - ; - } + [promiseReference](TaggedResult &&unmodeledResult) + { promiseReference->set_value(GetClientDeviceAuthTokenResult(std::move(unmodeledResult))); }); + return activateFuture; } @@ -8098,14 +8004,15 @@ namespace Aws std::future PublishToTopicOperation::GetResult() noexcept { - return m_resultPromise.get_future(); + return m_resultPromise->get_future(); } PublishToTopicOperation::PublishToTopicOperation( ClientConnection &connection, const PublishToTopicOperationContext &operationContext, Aws::Crt::Allocator *allocator) noexcept - : ClientOperation(connection, nullptr, operationContext, allocator) + : ClientOperation(connection, nullptr, operationContext, allocator), + m_resultPromise(Aws::Crt::MakeShared>(allocator)) { } @@ -8113,23 +8020,14 @@ namespace Aws const PublishToTopicRequest &request, OnMessageFlushCallback onMessageFlushCallback) noexcept { - bool synchronousSuccess = false; - std::lock_guard selfReferenceLock(m_selfReferenceLock); + auto promiseReference = m_resultPromise; + auto activateFuture = ClientOperation::Activate( static_cast(&request), std::move(onMessageFlushCallback), - [this](TaggedResult &&unmodeledResult) - { - std::lock_guard selfReferenceLock(m_selfReferenceLock); - m_resultPromise.set_value(PublishToTopicResult(std::move(unmodeledResult))); - m_selfReference = nullptr; - }, - synchronousSuccess); - if (synchronousSuccess) - { - m_selfReference = shared_from_this(); - ; - } + [promiseReference](TaggedResult &&unmodeledResult) + { promiseReference->set_value(PublishToTopicResult(std::move(unmodeledResult))); }); + return activateFuture; } @@ -8214,7 +8112,7 @@ namespace Aws std::future SubscribeToCertificateUpdatesOperation::GetResult() noexcept { - return m_resultPromise.get_future(); + return m_resultPromise->get_future(); } SubscribeToCertificateUpdatesOperation::SubscribeToCertificateUpdatesOperation( @@ -8222,7 +8120,8 @@ namespace Aws std::shared_ptr streamHandler, const SubscribeToCertificateUpdatesOperationContext &operationContext, Aws::Crt::Allocator *allocator) noexcept - : ClientOperation(connection, streamHandler, operationContext, allocator) + : ClientOperation(connection, streamHandler, operationContext, allocator), + m_resultPromise(Aws::Crt::MakeShared>(allocator)) { } @@ -8230,23 +8129,14 @@ namespace Aws const SubscribeToCertificateUpdatesRequest &request, OnMessageFlushCallback onMessageFlushCallback) noexcept { - bool synchronousSuccess = false; - std::lock_guard selfReferenceLock(m_selfReferenceLock); + auto promiseReference = m_resultPromise; + auto activateFuture = ClientOperation::Activate( static_cast(&request), std::move(onMessageFlushCallback), - [this](TaggedResult &&unmodeledResult) - { - std::lock_guard selfReferenceLock(m_selfReferenceLock); - m_resultPromise.set_value(SubscribeToCertificateUpdatesResult(std::move(unmodeledResult))); - m_selfReference = nullptr; - }, - synchronousSuccess); - if (synchronousSuccess) - { - m_selfReference = shared_from_this(); - ; - } + [promiseReference](TaggedResult &&unmodeledResult) + { promiseReference->set_value(SubscribeToCertificateUpdatesResult(std::move(unmodeledResult))); }); + return activateFuture; } @@ -8295,14 +8185,15 @@ namespace Aws std::future VerifyClientDeviceIdentityOperation::GetResult() noexcept { - return m_resultPromise.get_future(); + return m_resultPromise->get_future(); } VerifyClientDeviceIdentityOperation::VerifyClientDeviceIdentityOperation( ClientConnection &connection, const VerifyClientDeviceIdentityOperationContext &operationContext, Aws::Crt::Allocator *allocator) noexcept - : ClientOperation(connection, nullptr, operationContext, allocator) + : ClientOperation(connection, nullptr, operationContext, allocator), + m_resultPromise(Aws::Crt::MakeShared>(allocator)) { } @@ -8310,23 +8201,14 @@ namespace Aws const VerifyClientDeviceIdentityRequest &request, OnMessageFlushCallback onMessageFlushCallback) noexcept { - bool synchronousSuccess = false; - std::lock_guard selfReferenceLock(m_selfReferenceLock); + auto promiseReference = m_resultPromise; + auto activateFuture = ClientOperation::Activate( static_cast(&request), std::move(onMessageFlushCallback), - [this](TaggedResult &&unmodeledResult) - { - std::lock_guard selfReferenceLock(m_selfReferenceLock); - m_resultPromise.set_value(VerifyClientDeviceIdentityResult(std::move(unmodeledResult))); - m_selfReference = nullptr; - }, - synchronousSuccess); - if (synchronousSuccess) - { - m_selfReference = shared_from_this(); - ; - } + [promiseReference](TaggedResult &&unmodeledResult) + { promiseReference->set_value(VerifyClientDeviceIdentityResult(std::move(unmodeledResult))); }); + return activateFuture; } @@ -8375,14 +8257,15 @@ namespace Aws std::future AuthorizeClientDeviceActionOperation::GetResult() noexcept { - return m_resultPromise.get_future(); + return m_resultPromise->get_future(); } AuthorizeClientDeviceActionOperation::AuthorizeClientDeviceActionOperation( ClientConnection &connection, const AuthorizeClientDeviceActionOperationContext &operationContext, Aws::Crt::Allocator *allocator) noexcept - : ClientOperation(connection, nullptr, operationContext, allocator) + : ClientOperation(connection, nullptr, operationContext, allocator), + m_resultPromise(Aws::Crt::MakeShared>(allocator)) { } @@ -8390,23 +8273,14 @@ namespace Aws const AuthorizeClientDeviceActionRequest &request, OnMessageFlushCallback onMessageFlushCallback) noexcept { - bool synchronousSuccess = false; - std::lock_guard selfReferenceLock(m_selfReferenceLock); + auto promiseReference = m_resultPromise; + auto activateFuture = ClientOperation::Activate( static_cast(&request), std::move(onMessageFlushCallback), - [this](TaggedResult &&unmodeledResult) - { - std::lock_guard selfReferenceLock(m_selfReferenceLock); - m_resultPromise.set_value(AuthorizeClientDeviceActionResult(std::move(unmodeledResult))); - m_selfReference = nullptr; - }, - synchronousSuccess); - if (synchronousSuccess) - { - m_selfReference = shared_from_this(); - ; - } + [promiseReference](TaggedResult &&unmodeledResult) + { promiseReference->set_value(AuthorizeClientDeviceActionResult(std::move(unmodeledResult))); }); + return activateFuture; } @@ -8455,14 +8329,15 @@ namespace Aws std::future ListComponentsOperation::GetResult() noexcept { - return m_resultPromise.get_future(); + return m_resultPromise->get_future(); } ListComponentsOperation::ListComponentsOperation( ClientConnection &connection, const ListComponentsOperationContext &operationContext, Aws::Crt::Allocator *allocator) noexcept - : ClientOperation(connection, nullptr, operationContext, allocator) + : ClientOperation(connection, nullptr, operationContext, allocator), + m_resultPromise(Aws::Crt::MakeShared>(allocator)) { } @@ -8470,23 +8345,14 @@ namespace Aws const ListComponentsRequest &request, OnMessageFlushCallback onMessageFlushCallback) noexcept { - bool synchronousSuccess = false; - std::lock_guard selfReferenceLock(m_selfReferenceLock); + auto promiseReference = m_resultPromise; + auto activateFuture = ClientOperation::Activate( static_cast(&request), std::move(onMessageFlushCallback), - [this](TaggedResult &&unmodeledResult) - { - std::lock_guard selfReferenceLock(m_selfReferenceLock); - m_resultPromise.set_value(ListComponentsResult(std::move(unmodeledResult))); - m_selfReference = nullptr; - }, - synchronousSuccess); - if (synchronousSuccess) - { - m_selfReference = shared_from_this(); - ; - } + [promiseReference](TaggedResult &&unmodeledResult) + { promiseReference->set_value(ListComponentsResult(std::move(unmodeledResult))); }); + return activateFuture; } @@ -8535,14 +8401,15 @@ namespace Aws std::future CreateDebugPasswordOperation::GetResult() noexcept { - return m_resultPromise.get_future(); + return m_resultPromise->get_future(); } CreateDebugPasswordOperation::CreateDebugPasswordOperation( ClientConnection &connection, const CreateDebugPasswordOperationContext &operationContext, Aws::Crt::Allocator *allocator) noexcept - : ClientOperation(connection, nullptr, operationContext, allocator) + : ClientOperation(connection, nullptr, operationContext, allocator), + m_resultPromise(Aws::Crt::MakeShared>(allocator)) { } @@ -8550,23 +8417,14 @@ namespace Aws const CreateDebugPasswordRequest &request, OnMessageFlushCallback onMessageFlushCallback) noexcept { - bool synchronousSuccess = false; - std::lock_guard selfReferenceLock(m_selfReferenceLock); + auto promiseReference = m_resultPromise; + auto activateFuture = ClientOperation::Activate( static_cast(&request), std::move(onMessageFlushCallback), - [this](TaggedResult &&unmodeledResult) - { - std::lock_guard selfReferenceLock(m_selfReferenceLock); - m_resultPromise.set_value(CreateDebugPasswordResult(std::move(unmodeledResult))); - m_selfReference = nullptr; - }, - synchronousSuccess); - if (synchronousSuccess) - { - m_selfReference = shared_from_this(); - ; - } + [promiseReference](TaggedResult &&unmodeledResult) + { promiseReference->set_value(CreateDebugPasswordResult(std::move(unmodeledResult))); }); + return activateFuture; } @@ -8615,14 +8473,15 @@ namespace Aws std::future GetThingShadowOperation::GetResult() noexcept { - return m_resultPromise.get_future(); + return m_resultPromise->get_future(); } GetThingShadowOperation::GetThingShadowOperation( ClientConnection &connection, const GetThingShadowOperationContext &operationContext, Aws::Crt::Allocator *allocator) noexcept - : ClientOperation(connection, nullptr, operationContext, allocator) + : ClientOperation(connection, nullptr, operationContext, allocator), + m_resultPromise(Aws::Crt::MakeShared>(allocator)) { } @@ -8630,23 +8489,14 @@ namespace Aws const GetThingShadowRequest &request, OnMessageFlushCallback onMessageFlushCallback) noexcept { - bool synchronousSuccess = false; - std::lock_guard selfReferenceLock(m_selfReferenceLock); + auto promiseReference = m_resultPromise; + auto activateFuture = ClientOperation::Activate( static_cast(&request), std::move(onMessageFlushCallback), - [this](TaggedResult &&unmodeledResult) - { - std::lock_guard selfReferenceLock(m_selfReferenceLock); - m_resultPromise.set_value(GetThingShadowResult(std::move(unmodeledResult))); - m_selfReference = nullptr; - }, - synchronousSuccess); - if (synchronousSuccess) - { - m_selfReference = shared_from_this(); - ; - } + [promiseReference](TaggedResult &&unmodeledResult) + { promiseReference->set_value(GetThingShadowResult(std::move(unmodeledResult))); }); + return activateFuture; } @@ -8696,14 +8546,15 @@ namespace Aws std::future SendConfigurationValidityReportOperation:: GetResult() noexcept { - return m_resultPromise.get_future(); + return m_resultPromise->get_future(); } SendConfigurationValidityReportOperation::SendConfigurationValidityReportOperation( ClientConnection &connection, const SendConfigurationValidityReportOperationContext &operationContext, Aws::Crt::Allocator *allocator) noexcept - : ClientOperation(connection, nullptr, operationContext, allocator) + : ClientOperation(connection, nullptr, operationContext, allocator), + m_resultPromise(Aws::Crt::MakeShared>(allocator)) { } @@ -8711,23 +8562,14 @@ namespace Aws const SendConfigurationValidityReportRequest &request, OnMessageFlushCallback onMessageFlushCallback) noexcept { - bool synchronousSuccess = false; - std::lock_guard selfReferenceLock(m_selfReferenceLock); + auto promiseReference = m_resultPromise; + auto activateFuture = ClientOperation::Activate( static_cast(&request), std::move(onMessageFlushCallback), - [this](TaggedResult &&unmodeledResult) - { - std::lock_guard selfReferenceLock(m_selfReferenceLock); - m_resultPromise.set_value(SendConfigurationValidityReportResult(std::move(unmodeledResult))); - m_selfReference = nullptr; - }, - synchronousSuccess); - if (synchronousSuccess) - { - m_selfReference = shared_from_this(); - ; - } + [promiseReference](TaggedResult &&unmodeledResult) + { promiseReference->set_value(SendConfigurationValidityReportResult(std::move(unmodeledResult))); }); + return activateFuture; } @@ -8776,14 +8618,15 @@ namespace Aws std::future UpdateThingShadowOperation::GetResult() noexcept { - return m_resultPromise.get_future(); + return m_resultPromise->get_future(); } UpdateThingShadowOperation::UpdateThingShadowOperation( ClientConnection &connection, const UpdateThingShadowOperationContext &operationContext, Aws::Crt::Allocator *allocator) noexcept - : ClientOperation(connection, nullptr, operationContext, allocator) + : ClientOperation(connection, nullptr, operationContext, allocator), + m_resultPromise(Aws::Crt::MakeShared>(allocator)) { } @@ -8791,23 +8634,14 @@ namespace Aws const UpdateThingShadowRequest &request, OnMessageFlushCallback onMessageFlushCallback) noexcept { - bool synchronousSuccess = false; - std::lock_guard selfReferenceLock(m_selfReferenceLock); + auto promiseReference = m_resultPromise; + auto activateFuture = ClientOperation::Activate( static_cast(&request), std::move(onMessageFlushCallback), - [this](TaggedResult &&unmodeledResult) - { - std::lock_guard selfReferenceLock(m_selfReferenceLock); - m_resultPromise.set_value(UpdateThingShadowResult(std::move(unmodeledResult))); - m_selfReference = nullptr; - }, - synchronousSuccess); - if (synchronousSuccess) - { - m_selfReference = shared_from_this(); - ; - } + [promiseReference](TaggedResult &&unmodeledResult) + { promiseReference->set_value(UpdateThingShadowResult(std::move(unmodeledResult))); }); + return activateFuture; } @@ -8856,14 +8690,15 @@ namespace Aws std::future UpdateConfigurationOperation::GetResult() noexcept { - return m_resultPromise.get_future(); + return m_resultPromise->get_future(); } UpdateConfigurationOperation::UpdateConfigurationOperation( ClientConnection &connection, const UpdateConfigurationOperationContext &operationContext, Aws::Crt::Allocator *allocator) noexcept - : ClientOperation(connection, nullptr, operationContext, allocator) + : ClientOperation(connection, nullptr, operationContext, allocator), + m_resultPromise(Aws::Crt::MakeShared>(allocator)) { } @@ -8871,23 +8706,14 @@ namespace Aws const UpdateConfigurationRequest &request, OnMessageFlushCallback onMessageFlushCallback) noexcept { - bool synchronousSuccess = false; - std::lock_guard selfReferenceLock(m_selfReferenceLock); + auto promiseReference = m_resultPromise; + auto activateFuture = ClientOperation::Activate( static_cast(&request), std::move(onMessageFlushCallback), - [this](TaggedResult &&unmodeledResult) - { - std::lock_guard selfReferenceLock(m_selfReferenceLock); - m_resultPromise.set_value(UpdateConfigurationResult(std::move(unmodeledResult))); - m_selfReference = nullptr; - }, - synchronousSuccess); - if (synchronousSuccess) - { - m_selfReference = shared_from_this(); - ; - } + [promiseReference](TaggedResult &&unmodeledResult) + { promiseReference->set_value(UpdateConfigurationResult(std::move(unmodeledResult))); }); + return activateFuture; } @@ -8936,14 +8762,15 @@ namespace Aws std::future ValidateAuthorizationTokenOperation::GetResult() noexcept { - return m_resultPromise.get_future(); + return m_resultPromise->get_future(); } ValidateAuthorizationTokenOperation::ValidateAuthorizationTokenOperation( ClientConnection &connection, const ValidateAuthorizationTokenOperationContext &operationContext, Aws::Crt::Allocator *allocator) noexcept - : ClientOperation(connection, nullptr, operationContext, allocator) + : ClientOperation(connection, nullptr, operationContext, allocator), + m_resultPromise(Aws::Crt::MakeShared>(allocator)) { } @@ -8951,23 +8778,14 @@ namespace Aws const ValidateAuthorizationTokenRequest &request, OnMessageFlushCallback onMessageFlushCallback) noexcept { - bool synchronousSuccess = false; - std::lock_guard selfReferenceLock(m_selfReferenceLock); + auto promiseReference = m_resultPromise; + auto activateFuture = ClientOperation::Activate( static_cast(&request), std::move(onMessageFlushCallback), - [this](TaggedResult &&unmodeledResult) - { - std::lock_guard selfReferenceLock(m_selfReferenceLock); - m_resultPromise.set_value(ValidateAuthorizationTokenResult(std::move(unmodeledResult))); - m_selfReference = nullptr; - }, - synchronousSuccess); - if (synchronousSuccess) - { - m_selfReference = shared_from_this(); - ; - } + [promiseReference](TaggedResult &&unmodeledResult) + { promiseReference->set_value(ValidateAuthorizationTokenResult(std::move(unmodeledResult))); }); + return activateFuture; } @@ -9016,14 +8834,15 @@ namespace Aws std::future RestartComponentOperation::GetResult() noexcept { - return m_resultPromise.get_future(); + return m_resultPromise->get_future(); } RestartComponentOperation::RestartComponentOperation( ClientConnection &connection, const RestartComponentOperationContext &operationContext, Aws::Crt::Allocator *allocator) noexcept - : ClientOperation(connection, nullptr, operationContext, allocator) + : ClientOperation(connection, nullptr, operationContext, allocator), + m_resultPromise(Aws::Crt::MakeShared>(allocator)) { } @@ -9031,23 +8850,14 @@ namespace Aws const RestartComponentRequest &request, OnMessageFlushCallback onMessageFlushCallback) noexcept { - bool synchronousSuccess = false; - std::lock_guard selfReferenceLock(m_selfReferenceLock); + auto promiseReference = m_resultPromise; + auto activateFuture = ClientOperation::Activate( static_cast(&request), std::move(onMessageFlushCallback), - [this](TaggedResult &&unmodeledResult) - { - std::lock_guard selfReferenceLock(m_selfReferenceLock); - m_resultPromise.set_value(RestartComponentResult(std::move(unmodeledResult))); - m_selfReference = nullptr; - }, - synchronousSuccess); - if (synchronousSuccess) - { - m_selfReference = shared_from_this(); - ; - } + [promiseReference](TaggedResult &&unmodeledResult) + { promiseReference->set_value(RestartComponentResult(std::move(unmodeledResult))); }); + return activateFuture; } @@ -9096,14 +8906,15 @@ namespace Aws std::future GetLocalDeploymentStatusOperation::GetResult() noexcept { - return m_resultPromise.get_future(); + return m_resultPromise->get_future(); } GetLocalDeploymentStatusOperation::GetLocalDeploymentStatusOperation( ClientConnection &connection, const GetLocalDeploymentStatusOperationContext &operationContext, Aws::Crt::Allocator *allocator) noexcept - : ClientOperation(connection, nullptr, operationContext, allocator) + : ClientOperation(connection, nullptr, operationContext, allocator), + m_resultPromise(Aws::Crt::MakeShared>(allocator)) { } @@ -9111,23 +8922,14 @@ namespace Aws const GetLocalDeploymentStatusRequest &request, OnMessageFlushCallback onMessageFlushCallback) noexcept { - bool synchronousSuccess = false; - std::lock_guard selfReferenceLock(m_selfReferenceLock); + auto promiseReference = m_resultPromise; + auto activateFuture = ClientOperation::Activate( static_cast(&request), std::move(onMessageFlushCallback), - [this](TaggedResult &&unmodeledResult) - { - std::lock_guard selfReferenceLock(m_selfReferenceLock); - m_resultPromise.set_value(GetLocalDeploymentStatusResult(std::move(unmodeledResult))); - m_selfReference = nullptr; - }, - synchronousSuccess); - if (synchronousSuccess) - { - m_selfReference = shared_from_this(); - ; - } + [promiseReference](TaggedResult &&unmodeledResult) + { promiseReference->set_value(GetLocalDeploymentStatusResult(std::move(unmodeledResult))); }); + return activateFuture; } @@ -9176,14 +8978,15 @@ namespace Aws std::future GetSecretValueOperation::GetResult() noexcept { - return m_resultPromise.get_future(); + return m_resultPromise->get_future(); } GetSecretValueOperation::GetSecretValueOperation( ClientConnection &connection, const GetSecretValueOperationContext &operationContext, Aws::Crt::Allocator *allocator) noexcept - : ClientOperation(connection, nullptr, operationContext, allocator) + : ClientOperation(connection, nullptr, operationContext, allocator), + m_resultPromise(Aws::Crt::MakeShared>(allocator)) { } @@ -9191,23 +8994,14 @@ namespace Aws const GetSecretValueRequest &request, OnMessageFlushCallback onMessageFlushCallback) noexcept { - bool synchronousSuccess = false; - std::lock_guard selfReferenceLock(m_selfReferenceLock); + auto promiseReference = m_resultPromise; + auto activateFuture = ClientOperation::Activate( static_cast(&request), std::move(onMessageFlushCallback), - [this](TaggedResult &&unmodeledResult) - { - std::lock_guard selfReferenceLock(m_selfReferenceLock); - m_resultPromise.set_value(GetSecretValueResult(std::move(unmodeledResult))); - m_selfReference = nullptr; - }, - synchronousSuccess); - if (synchronousSuccess) - { - m_selfReference = shared_from_this(); - ; - } + [promiseReference](TaggedResult &&unmodeledResult) + { promiseReference->set_value(GetSecretValueResult(std::move(unmodeledResult))); }); + return activateFuture; } @@ -9255,14 +9049,15 @@ namespace Aws std::future UpdateStateOperation::GetResult() noexcept { - return m_resultPromise.get_future(); + return m_resultPromise->get_future(); } UpdateStateOperation::UpdateStateOperation( ClientConnection &connection, const UpdateStateOperationContext &operationContext, Aws::Crt::Allocator *allocator) noexcept - : ClientOperation(connection, nullptr, operationContext, allocator) + : ClientOperation(connection, nullptr, operationContext, allocator), + m_resultPromise(Aws::Crt::MakeShared>(allocator)) { } @@ -9270,23 +9065,14 @@ namespace Aws const UpdateStateRequest &request, OnMessageFlushCallback onMessageFlushCallback) noexcept { - bool synchronousSuccess = false; - std::lock_guard selfReferenceLock(m_selfReferenceLock); + auto promiseReference = m_resultPromise; + auto activateFuture = ClientOperation::Activate( static_cast(&request), std::move(onMessageFlushCallback), - [this](TaggedResult &&unmodeledResult) - { - std::lock_guard selfReferenceLock(m_selfReferenceLock); - m_resultPromise.set_value(UpdateStateResult(std::move(unmodeledResult))); - m_selfReference = nullptr; - }, - synchronousSuccess); - if (synchronousSuccess) - { - m_selfReference = shared_from_this(); - ; - } + [promiseReference](TaggedResult &&unmodeledResult) + { promiseReference->set_value(UpdateStateResult(std::move(unmodeledResult))); }); + return activateFuture; } @@ -9335,14 +9121,15 @@ namespace Aws std::future CancelLocalDeploymentOperation::GetResult() noexcept { - return m_resultPromise.get_future(); + return m_resultPromise->get_future(); } CancelLocalDeploymentOperation::CancelLocalDeploymentOperation( ClientConnection &connection, const CancelLocalDeploymentOperationContext &operationContext, Aws::Crt::Allocator *allocator) noexcept - : ClientOperation(connection, nullptr, operationContext, allocator) + : ClientOperation(connection, nullptr, operationContext, allocator), + m_resultPromise(Aws::Crt::MakeShared>(allocator)) { } @@ -9350,23 +9137,14 @@ namespace Aws const CancelLocalDeploymentRequest &request, OnMessageFlushCallback onMessageFlushCallback) noexcept { - bool synchronousSuccess = false; - std::lock_guard selfReferenceLock(m_selfReferenceLock); + auto promiseReference = m_resultPromise; + auto activateFuture = ClientOperation::Activate( static_cast(&request), std::move(onMessageFlushCallback), - [this](TaggedResult &&unmodeledResult) - { - std::lock_guard selfReferenceLock(m_selfReferenceLock); - m_resultPromise.set_value(CancelLocalDeploymentResult(std::move(unmodeledResult))); - m_selfReference = nullptr; - }, - synchronousSuccess); - if (synchronousSuccess) - { - m_selfReference = shared_from_this(); - ; - } + [promiseReference](TaggedResult &&unmodeledResult) + { promiseReference->set_value(CancelLocalDeploymentResult(std::move(unmodeledResult))); }); + return activateFuture; } @@ -9415,14 +9193,15 @@ namespace Aws std::future ListNamedShadowsForThingOperation::GetResult() noexcept { - return m_resultPromise.get_future(); + return m_resultPromise->get_future(); } ListNamedShadowsForThingOperation::ListNamedShadowsForThingOperation( ClientConnection &connection, const ListNamedShadowsForThingOperationContext &operationContext, Aws::Crt::Allocator *allocator) noexcept - : ClientOperation(connection, nullptr, operationContext, allocator) + : ClientOperation(connection, nullptr, operationContext, allocator), + m_resultPromise(Aws::Crt::MakeShared>(allocator)) { } @@ -9430,23 +9209,14 @@ namespace Aws const ListNamedShadowsForThingRequest &request, OnMessageFlushCallback onMessageFlushCallback) noexcept { - bool synchronousSuccess = false; - std::lock_guard selfReferenceLock(m_selfReferenceLock); + auto promiseReference = m_resultPromise; + auto activateFuture = ClientOperation::Activate( static_cast(&request), std::move(onMessageFlushCallback), - [this](TaggedResult &&unmodeledResult) - { - std::lock_guard selfReferenceLock(m_selfReferenceLock); - m_resultPromise.set_value(ListNamedShadowsForThingResult(std::move(unmodeledResult))); - m_selfReference = nullptr; - }, - synchronousSuccess); - if (synchronousSuccess) - { - m_selfReference = shared_from_this(); - ; - } + [promiseReference](TaggedResult &&unmodeledResult) + { promiseReference->set_value(ListNamedShadowsForThingResult(std::move(unmodeledResult))); }); + return activateFuture; } @@ -9525,7 +9295,7 @@ namespace Aws std::future SubscribeToComponentUpdatesOperation::GetResult() noexcept { - return m_resultPromise.get_future(); + return m_resultPromise->get_future(); } SubscribeToComponentUpdatesOperation::SubscribeToComponentUpdatesOperation( @@ -9533,7 +9303,8 @@ namespace Aws std::shared_ptr streamHandler, const SubscribeToComponentUpdatesOperationContext &operationContext, Aws::Crt::Allocator *allocator) noexcept - : ClientOperation(connection, streamHandler, operationContext, allocator) + : ClientOperation(connection, streamHandler, operationContext, allocator), + m_resultPromise(Aws::Crt::MakeShared>(allocator)) { } @@ -9541,23 +9312,14 @@ namespace Aws const SubscribeToComponentUpdatesRequest &request, OnMessageFlushCallback onMessageFlushCallback) noexcept { - bool synchronousSuccess = false; - std::lock_guard selfReferenceLock(m_selfReferenceLock); + auto promiseReference = m_resultPromise; + auto activateFuture = ClientOperation::Activate( static_cast(&request), std::move(onMessageFlushCallback), - [this](TaggedResult &&unmodeledResult) - { - std::lock_guard selfReferenceLock(m_selfReferenceLock); - m_resultPromise.set_value(SubscribeToComponentUpdatesResult(std::move(unmodeledResult))); - m_selfReference = nullptr; - }, - synchronousSuccess); - if (synchronousSuccess) - { - m_selfReference = shared_from_this(); - ; - } + [promiseReference](TaggedResult &&unmodeledResult) + { promiseReference->set_value(SubscribeToComponentUpdatesResult(std::move(unmodeledResult))); }); + return activateFuture; } @@ -9606,14 +9368,15 @@ namespace Aws std::future ListLocalDeploymentsOperation::GetResult() noexcept { - return m_resultPromise.get_future(); + return m_resultPromise->get_future(); } ListLocalDeploymentsOperation::ListLocalDeploymentsOperation( ClientConnection &connection, const ListLocalDeploymentsOperationContext &operationContext, Aws::Crt::Allocator *allocator) noexcept - : ClientOperation(connection, nullptr, operationContext, allocator) + : ClientOperation(connection, nullptr, operationContext, allocator), + m_resultPromise(Aws::Crt::MakeShared>(allocator)) { } @@ -9621,23 +9384,14 @@ namespace Aws const ListLocalDeploymentsRequest &request, OnMessageFlushCallback onMessageFlushCallback) noexcept { - bool synchronousSuccess = false; - std::lock_guard selfReferenceLock(m_selfReferenceLock); + auto promiseReference = m_resultPromise; + auto activateFuture = ClientOperation::Activate( static_cast(&request), std::move(onMessageFlushCallback), - [this](TaggedResult &&unmodeledResult) - { - std::lock_guard selfReferenceLock(m_selfReferenceLock); - m_resultPromise.set_value(ListLocalDeploymentsResult(std::move(unmodeledResult))); - m_selfReference = nullptr; - }, - synchronousSuccess); - if (synchronousSuccess) - { - m_selfReference = shared_from_this(); - ; - } + [promiseReference](TaggedResult &&unmodeledResult) + { promiseReference->set_value(ListLocalDeploymentsResult(std::move(unmodeledResult))); }); + return activateFuture; } @@ -9686,14 +9440,15 @@ namespace Aws std::future StopComponentOperation::GetResult() noexcept { - return m_resultPromise.get_future(); + return m_resultPromise->get_future(); } StopComponentOperation::StopComponentOperation( ClientConnection &connection, const StopComponentOperationContext &operationContext, Aws::Crt::Allocator *allocator) noexcept - : ClientOperation(connection, nullptr, operationContext, allocator) + : ClientOperation(connection, nullptr, operationContext, allocator), + m_resultPromise(Aws::Crt::MakeShared>(allocator)) { } @@ -9701,23 +9456,14 @@ namespace Aws const StopComponentRequest &request, OnMessageFlushCallback onMessageFlushCallback) noexcept { - bool synchronousSuccess = false; - std::lock_guard selfReferenceLock(m_selfReferenceLock); + auto promiseReference = m_resultPromise; + auto activateFuture = ClientOperation::Activate( static_cast(&request), std::move(onMessageFlushCallback), - [this](TaggedResult &&unmodeledResult) - { - std::lock_guard selfReferenceLock(m_selfReferenceLock); - m_resultPromise.set_value(StopComponentResult(std::move(unmodeledResult))); - m_selfReference = nullptr; - }, - synchronousSuccess); - if (synchronousSuccess) - { - m_selfReference = shared_from_this(); - ; - } + [promiseReference](TaggedResult &&unmodeledResult) + { promiseReference->set_value(StopComponentResult(std::move(unmodeledResult))); }); + return activateFuture; } @@ -9766,14 +9512,15 @@ namespace Aws std::future PauseComponentOperation::GetResult() noexcept { - return m_resultPromise.get_future(); + return m_resultPromise->get_future(); } PauseComponentOperation::PauseComponentOperation( ClientConnection &connection, const PauseComponentOperationContext &operationContext, Aws::Crt::Allocator *allocator) noexcept - : ClientOperation(connection, nullptr, operationContext, allocator) + : ClientOperation(connection, nullptr, operationContext, allocator), + m_resultPromise(Aws::Crt::MakeShared>(allocator)) { } @@ -9781,23 +9528,14 @@ namespace Aws const PauseComponentRequest &request, OnMessageFlushCallback onMessageFlushCallback) noexcept { - bool synchronousSuccess = false; - std::lock_guard selfReferenceLock(m_selfReferenceLock); + auto promiseReference = m_resultPromise; + auto activateFuture = ClientOperation::Activate( static_cast(&request), std::move(onMessageFlushCallback), - [this](TaggedResult &&unmodeledResult) - { - std::lock_guard selfReferenceLock(m_selfReferenceLock); - m_resultPromise.set_value(PauseComponentResult(std::move(unmodeledResult))); - m_selfReference = nullptr; - }, - synchronousSuccess); - if (synchronousSuccess) - { - m_selfReference = shared_from_this(); - ; - } + [promiseReference](TaggedResult &&unmodeledResult) + { promiseReference->set_value(PauseComponentResult(std::move(unmodeledResult))); }); + return activateFuture; } @@ -9846,14 +9584,15 @@ namespace Aws std::future CreateLocalDeploymentOperation::GetResult() noexcept { - return m_resultPromise.get_future(); + return m_resultPromise->get_future(); } CreateLocalDeploymentOperation::CreateLocalDeploymentOperation( ClientConnection &connection, const CreateLocalDeploymentOperationContext &operationContext, Aws::Crt::Allocator *allocator) noexcept - : ClientOperation(connection, nullptr, operationContext, allocator) + : ClientOperation(connection, nullptr, operationContext, allocator), + m_resultPromise(Aws::Crt::MakeShared>(allocator)) { } @@ -9861,23 +9600,14 @@ namespace Aws const CreateLocalDeploymentRequest &request, OnMessageFlushCallback onMessageFlushCallback) noexcept { - bool synchronousSuccess = false; - std::lock_guard selfReferenceLock(m_selfReferenceLock); + auto promiseReference = m_resultPromise; + auto activateFuture = ClientOperation::Activate( static_cast(&request), std::move(onMessageFlushCallback), - [this](TaggedResult &&unmodeledResult) - { - std::lock_guard selfReferenceLock(m_selfReferenceLock); - m_resultPromise.set_value(CreateLocalDeploymentResult(std::move(unmodeledResult))); - m_selfReference = nullptr; - }, - synchronousSuccess); - if (synchronousSuccess) - { - m_selfReference = shared_from_this(); - ; - } + [promiseReference](TaggedResult &&unmodeledResult) + { promiseReference->set_value(CreateLocalDeploymentResult(std::move(unmodeledResult))); }); + return activateFuture; }