From 30936fe0a2d48005a873da32dd48e69261343474 Mon Sep 17 00:00:00 2001 From: Nav Date: Thu, 1 Jun 2023 22:13:07 +0100 Subject: [PATCH] Added atomic sessions in TC --- src/Services/TargetControllerService.cpp | 119 ++++++++++++++---- src/Services/TargetControllerService.hpp | 34 +++++ src/TargetController/AtomicSession.hpp | 25 ++++ src/TargetController/CommandManager.hpp | 6 +- .../Commands/CommandTypes.hpp | 2 + .../Commands/EndAtomicSession.hpp | 25 ++++ .../Commands/StartAtomicSession.hpp | 21 ++++ .../Responses/AtomicSessionId.hpp | 26 ++++ .../Responses/ResponseTypes.hpp | 1 + .../TargetControllerComponent.cpp | 75 ++++++++++- .../TargetControllerComponent.hpp | 29 ++++- 11 files changed, 333 insertions(+), 30 deletions(-) create mode 100644 src/TargetController/AtomicSession.hpp create mode 100644 src/TargetController/Commands/EndAtomicSession.hpp create mode 100644 src/TargetController/Commands/StartAtomicSession.hpp create mode 100644 src/TargetController/Responses/AtomicSessionId.hpp diff --git a/src/Services/TargetControllerService.cpp b/src/Services/TargetControllerService.cpp index 53ba896e..b5bb6949 100644 --- a/src/Services/TargetControllerService.cpp +++ b/src/Services/TargetControllerService.cpp @@ -1,6 +1,8 @@ #include "TargetControllerService.hpp" // Commands +#include "src/TargetController/Commands/StartAtomicSession.hpp" +#include "src/TargetController/Commands/EndAtomicSession.hpp" #include "src/TargetController/Commands/GetTargetDescriptor.hpp" #include "src/TargetController/Commands/GetTargetState.hpp" #include "src/TargetController/Commands/StopTargetExecution.hpp" @@ -23,8 +25,12 @@ #include "src/TargetController/Commands/DisableProgrammingMode.hpp" #include "src/TargetController/Commands/Shutdown.hpp" +#include "src/Exceptions/Exception.hpp" + namespace Bloom::Services { + using TargetController::Commands::StartAtomicSession; + using TargetController::Commands::EndAtomicSession; using TargetController::Commands::GetTargetDescriptor; using TargetController::Commands::GetTargetState; using TargetController::Commands::StopTargetExecution; @@ -67,24 +73,57 @@ namespace Bloom::Services using Targets::TargetPinState; using Targets::TargetPinStateMapping; + TargetControllerService::AtomicSession::AtomicSession(TargetControllerService& targetControllerService) + : targetControllerService(targetControllerService) + { + if (this->targetControllerService.activeAtomicSessionId.has_value()) { + throw Exceptions::Exception("Atomic session already active in TargetControllerService instance."); + } + + this->sessionId = this->targetControllerService.startAtomicSession(); + this->targetControllerService.activeAtomicSessionId = this->sessionId; + } + + TargetControllerService::AtomicSession::~AtomicSession() { + try { + this->targetControllerService.endAtomicSession(this->sessionId); + + } catch (const std::exception& exception) { + Logger::error( + "Failed to end atomic session (ID: " + std::to_string(this->sessionId) + ") - " + + std::string(exception.what()) + ); + } + + if ( + this->targetControllerService.activeAtomicSessionId.has_value() + && this->targetControllerService.activeAtomicSessionId == this->sessionId + ) { + this->targetControllerService.activeAtomicSessionId.reset(); + } + } + const TargetDescriptor& TargetControllerService::getTargetDescriptor() const { return this->commandManager.sendCommandAndWaitForResponse( std::make_unique(), - this->defaultTimeout + this->defaultTimeout, + this->activeAtomicSessionId )->targetDescriptor; } TargetState TargetControllerService::getTargetState() const { return this->commandManager.sendCommandAndWaitForResponse( std::make_unique(), - this->defaultTimeout + this->defaultTimeout, + this->activeAtomicSessionId )->targetState; } void TargetControllerService::stopTargetExecution() const { this->commandManager.sendCommandAndWaitForResponse( std::make_unique(), - this->defaultTimeout + this->defaultTimeout, + this->activeAtomicSessionId ); } @@ -104,7 +143,8 @@ namespace Bloom::Services this->commandManager.sendCommandAndWaitForResponse( std::move(resumeExecutionCommand), - this->defaultTimeout + this->defaultTimeout, + this->activeAtomicSessionId ); } @@ -117,7 +157,8 @@ namespace Bloom::Services this->commandManager.sendCommandAndWaitForResponse( std::move(stepExecutionCommand), - this->defaultTimeout + this->defaultTimeout, + this->activeAtomicSessionId ); } @@ -126,14 +167,16 @@ namespace Bloom::Services ) const { return this->commandManager.sendCommandAndWaitForResponse( std::make_unique(descriptorIds), - this->defaultTimeout + this->defaultTimeout, + this->activeAtomicSessionId )->registers; } void TargetControllerService::writeRegisters(const TargetRegisters& registers) const { this->commandManager.sendCommandAndWaitForResponse( std::make_unique(registers), - this->defaultTimeout + this->defaultTimeout, + this->activeAtomicSessionId ); } @@ -150,7 +193,8 @@ namespace Bloom::Services bytes, excludedAddressRanges ), - this->defaultTimeout + this->defaultTimeout, + this->activeAtomicSessionId )->data; } @@ -161,91 +205,124 @@ namespace Bloom::Services ) const { this->commandManager.sendCommandAndWaitForResponse( std::make_unique(memoryType, startAddress, buffer), - this->defaultTimeout + this->defaultTimeout, + this->activeAtomicSessionId ); } void TargetControllerService::eraseMemory(Targets::TargetMemoryType memoryType) const { this->commandManager.sendCommandAndWaitForResponse( std::make_unique(memoryType), - this->defaultTimeout + this->defaultTimeout, + this->activeAtomicSessionId ); } void TargetControllerService::setBreakpoint(TargetBreakpoint breakpoint) const { this->commandManager.sendCommandAndWaitForResponse( std::make_unique(breakpoint), - this->defaultTimeout + this->defaultTimeout, + this->activeAtomicSessionId ); } void TargetControllerService::removeBreakpoint(TargetBreakpoint breakpoint) const { this->commandManager.sendCommandAndWaitForResponse( std::make_unique(breakpoint), - this->defaultTimeout + this->defaultTimeout, + this->activeAtomicSessionId ); } TargetProgramCounter TargetControllerService::getProgramCounter() const { return this->commandManager.sendCommandAndWaitForResponse( std::make_unique(), - this->defaultTimeout + this->defaultTimeout, + this->activeAtomicSessionId )->programCounter; } void TargetControllerService::setProgramCounter(TargetProgramCounter address) const { this->commandManager.sendCommandAndWaitForResponse( std::make_unique(address), - this->defaultTimeout + this->defaultTimeout, + this->activeAtomicSessionId ); } TargetPinStateMapping TargetControllerService::getPinStates(int variantId) const { return this->commandManager.sendCommandAndWaitForResponse( std::make_unique(variantId), - this->defaultTimeout + this->defaultTimeout, + this->activeAtomicSessionId )->pinStatesByNumber; } void TargetControllerService::setPinState(TargetPinDescriptor pinDescriptor, TargetPinState pinState) const { this->commandManager.sendCommandAndWaitForResponse( std::make_unique(pinDescriptor, pinState), - this->defaultTimeout + this->defaultTimeout, + this->activeAtomicSessionId ); } TargetStackPointer TargetControllerService::getStackPointer() const { return this->commandManager.sendCommandAndWaitForResponse( std::make_unique(), - this->defaultTimeout + this->defaultTimeout, + this->activeAtomicSessionId )->stackPointer; } void TargetControllerService::resetTarget() const { this->commandManager.sendCommandAndWaitForResponse( std::make_unique(), - this->defaultTimeout + this->defaultTimeout, + this->activeAtomicSessionId ); } void TargetControllerService::enableProgrammingMode() const { this->commandManager.sendCommandAndWaitForResponse( std::make_unique(), - this->defaultTimeout + this->defaultTimeout, + this->activeAtomicSessionId ); } void TargetControllerService::disableProgrammingMode() const { this->commandManager.sendCommandAndWaitForResponse( std::make_unique(), - this->defaultTimeout + this->defaultTimeout, + this->activeAtomicSessionId ); } void TargetControllerService::shutdown() const { this->commandManager.sendCommandAndWaitForResponse( std::make_unique(), - this->defaultTimeout + this->defaultTimeout, + this->activeAtomicSessionId + ); + } + + TargetControllerService::AtomicSession TargetControllerService::makeAtomicSession() { + return AtomicSession(*this); + } + + TargetController::AtomicSessionIdType TargetControllerService::startAtomicSession() { + return this->commandManager.sendCommandAndWaitForResponse( + std::make_unique(), + this->defaultTimeout, + this->activeAtomicSessionId + )->sessionId; + } + + void TargetControllerService::endAtomicSession(TargetController::AtomicSessionIdType sessionId) { + this->commandManager.sendCommandAndWaitForResponse( + std::make_unique(sessionId), + this->defaultTimeout, + this->activeAtomicSessionId ); } } diff --git a/src/Services/TargetControllerService.hpp b/src/Services/TargetControllerService.hpp index 99fdbb95..236e7e8c 100644 --- a/src/Services/TargetControllerService.hpp +++ b/src/Services/TargetControllerService.hpp @@ -3,8 +3,10 @@ #include #include #include +#include #include "src/TargetController/CommandManager.hpp" +#include "src/TargetController/AtomicSession.hpp" #include "src/Targets/TargetState.hpp" #include "src/Targets/TargetRegister.hpp" @@ -23,6 +25,25 @@ namespace Bloom::Services class TargetControllerService { public: + /** + * RAII wrapper for atomic sessions. + */ + class AtomicSession + { + public: + explicit AtomicSession(TargetControllerService& targetControllerService); + ~AtomicSession(); + + AtomicSession(const AtomicSession&) = delete; + AtomicSession(const AtomicSession&&) = delete; + AtomicSession& operator = (const AtomicSession&) = delete; + AtomicSession& operator = (const AtomicSession&&) = delete; + + private: + TargetControllerService& targetControllerService; + TargetController::AtomicSessionIdType sessionId; + }; + TargetControllerService() = default; void setDefaultTimeout(std::chrono::milliseconds timeout) { @@ -192,9 +213,22 @@ namespace Bloom::Services */ void shutdown() const; + /** + * Starts a new atomic session with the TC, via an TargetControllerService::AtomicSession RAII object. + * The session will end when the object is destroyed. + * + * @return + */ + TargetControllerService::AtomicSession makeAtomicSession(); + private: TargetController::CommandManager commandManager = TargetController::CommandManager(); + std::optional activeAtomicSessionId = std::nullopt; + std::chrono::milliseconds defaultTimeout = std::chrono::milliseconds(60000); + + TargetController::AtomicSessionIdType startAtomicSession(); + void endAtomicSession(TargetController::AtomicSessionIdType sessionId); }; } diff --git a/src/TargetController/AtomicSession.hpp b/src/TargetController/AtomicSession.hpp new file mode 100644 index 00000000..f2452039 --- /dev/null +++ b/src/TargetController/AtomicSession.hpp @@ -0,0 +1,25 @@ +#pragma once + +#include +#include + +namespace Bloom::TargetController +{ + using AtomicSessionIdType = int; + static_assert(std::atomic::is_always_lock_free); + + class AtomicSession + { + public: + const AtomicSessionIdType id = ++(AtomicSession::lastSessionId); + + AtomicSession() = default; + AtomicSession(const AtomicSession&) = delete; + AtomicSession(const AtomicSession&&) = delete; + AtomicSession& operator = (AtomicSession&) = delete; + AtomicSession& operator = (AtomicSession&&) = delete; + + private: + static inline std::atomic lastSessionId = 0; + }; +} diff --git a/src/TargetController/CommandManager.hpp b/src/TargetController/CommandManager.hpp index caef1c66..7f582bda 100644 --- a/src/TargetController/CommandManager.hpp +++ b/src/TargetController/CommandManager.hpp @@ -7,6 +7,7 @@ #include "Commands/Command.hpp" #include "Responses/Response.hpp" #include "Responses/Error.hpp" +#include "AtomicSession.hpp" #include "TargetControllerComponent.hpp" #include "src/Exceptions/Exception.hpp" @@ -24,7 +25,8 @@ namespace Bloom::TargetController && std::is_base_of_v auto sendCommandAndWaitForResponse( std::unique_ptr command, - std::chrono::milliseconds timeout + std::chrono::milliseconds timeout, + std::optional atomicSessionId = std::nullopt ) const { using SuccessResponseType = typename CommandType::SuccessResponseType; @@ -33,7 +35,7 @@ namespace Bloom::TargetController "Issuing " + CommandType::name + " command (ID: " + std::to_string(commandId) + ") to TargetController" ); - TargetControllerComponent::registerCommand(std::move(command)); + TargetControllerComponent::registerCommand(std::move(command), atomicSessionId); auto optionalResponse = TargetControllerComponent::waitForResponse(commandId, timeout); diff --git a/src/TargetController/Commands/CommandTypes.hpp b/src/TargetController/Commands/CommandTypes.hpp index 2d637516..2bcec412 100644 --- a/src/TargetController/Commands/CommandTypes.hpp +++ b/src/TargetController/Commands/CommandTypes.hpp @@ -9,6 +9,8 @@ namespace Bloom::TargetController::Commands GENERIC, SHUTDOWN, GET_TARGET_DESCRIPTOR, + START_ATOMIC_SESSION, + END_ATOMIC_SESSION, STOP_TARGET_EXECUTION, RESUME_TARGET_EXECUTION, RESET_TARGET, diff --git a/src/TargetController/Commands/EndAtomicSession.hpp b/src/TargetController/Commands/EndAtomicSession.hpp new file mode 100644 index 00000000..acc3394d --- /dev/null +++ b/src/TargetController/Commands/EndAtomicSession.hpp @@ -0,0 +1,25 @@ +#pragma once + +#include "Command.hpp" + +#include "src/TargetController/Responses/AtomicSessionId.hpp" + +namespace Bloom::TargetController::Commands +{ + class EndAtomicSession: public Command + { + public: + static constexpr CommandType type = CommandType::END_ATOMIC_SESSION; + static const inline std::string name = "EndAtomicSession"; + + AtomicSessionIdType sessionId; + + explicit EndAtomicSession(AtomicSessionIdType sessionId) + : sessionId(sessionId) + {} + + [[nodiscard]] CommandType getType() const override { + return EndAtomicSession::type; + } + }; +} diff --git a/src/TargetController/Commands/StartAtomicSession.hpp b/src/TargetController/Commands/StartAtomicSession.hpp new file mode 100644 index 00000000..786ba4f4 --- /dev/null +++ b/src/TargetController/Commands/StartAtomicSession.hpp @@ -0,0 +1,21 @@ +#pragma once + +#include "Command.hpp" + +#include "src/TargetController/Responses/AtomicSessionId.hpp" + +namespace Bloom::TargetController::Commands +{ + class StartAtomicSession: public Command + { + public: + using SuccessResponseType = Responses::AtomicSessionId; + + static constexpr CommandType type = CommandType::START_ATOMIC_SESSION; + static const inline std::string name = "StartAtomicSession"; + + [[nodiscard]] CommandType getType() const override { + return StartAtomicSession::type; + } + }; +} diff --git a/src/TargetController/Responses/AtomicSessionId.hpp b/src/TargetController/Responses/AtomicSessionId.hpp new file mode 100644 index 00000000..2c59092d --- /dev/null +++ b/src/TargetController/Responses/AtomicSessionId.hpp @@ -0,0 +1,26 @@ +#pragma once + +#include + +#include "Response.hpp" + +#include "src/TargetController/AtomicSession.hpp" + +namespace Bloom::TargetController::Responses +{ + class AtomicSessionId: public Response + { + public: + static constexpr ResponseType type = ResponseType::ATOMIC_SESSION_ID; + + AtomicSessionIdType sessionId; + + explicit AtomicSessionId(AtomicSessionIdType sessionId) + : sessionId(sessionId) + {} + + [[nodiscard]] ResponseType getType() const override { + return AtomicSessionId::type; + } + }; +} diff --git a/src/TargetController/Responses/ResponseTypes.hpp b/src/TargetController/Responses/ResponseTypes.hpp index 53e93c64..22cec4b1 100644 --- a/src/TargetController/Responses/ResponseTypes.hpp +++ b/src/TargetController/Responses/ResponseTypes.hpp @@ -9,6 +9,7 @@ namespace Bloom::TargetController::Responses GENERIC, ERROR, STATE, + ATOMIC_SESSION_ID, TARGET_DESCRIPTOR, TARGET_REGISTERS_READ, TARGET_MEMORY_READ, diff --git a/src/TargetController/TargetControllerComponent.cpp b/src/TargetController/TargetControllerComponent.cpp index ff504eac..ee0333c5 100644 --- a/src/TargetController/TargetControllerComponent.cpp +++ b/src/TargetController/TargetControllerComponent.cpp @@ -20,6 +20,8 @@ namespace Bloom::TargetController using Commands::CommandIdType; using Commands::Command; + using Commands::StartAtomicSession; + using Commands::EndAtomicSession; using Commands::Shutdown; using Commands::GetTargetDescriptor; using Commands::GetTargetState; @@ -43,6 +45,7 @@ namespace Bloom::TargetController using Commands::DisableProgrammingMode; using Responses::Response; + using Responses::AtomicSessionId; using Responses::TargetRegistersRead; using Responses::TargetMemoryRead; using Responses::TargetPinStates; @@ -81,12 +84,23 @@ namespace Bloom::TargetController this->shutdown(); } - void TargetControllerComponent::registerCommand(std::unique_ptr command) { + void TargetControllerComponent::registerCommand( + std::unique_ptr command, + const std::optional& atomicSessionId + ) { if (TargetControllerComponent::state != TargetControllerState::ACTIVE) { throw Exception("Command rejected - TargetController not in active state."); } - auto commandQueueLock = TargetControllerComponent::commandQueue.acquireLock(); + if (atomicSessionId.has_value()) { + // This command is part of an atomic session - put it in the dedicated queue + const auto commandQueueLock = TargetControllerComponent::atomicSessionCommandQueue.acquireLock(); + TargetControllerComponent::atomicSessionCommandQueue.getValue().push(std::move(command)); + TargetControllerComponent::notifier.notify(); + return; + } + + const auto commandQueueLock = TargetControllerComponent::commandQueue.acquireLock(); TargetControllerComponent::commandQueue.getValue().push(std::move(command)); TargetControllerComponent::notifier.notify(); } @@ -140,6 +154,14 @@ namespace Bloom::TargetController EventManager::registerListener(this->eventListener); // Register command handlers + this->registerCommandHandler( + std::bind(&TargetControllerComponent::handleStartAtomicSession, this, std::placeholders::_1) + ); + + this->registerCommandHandler( + std::bind(&TargetControllerComponent::handleEndAtomicSession, this, std::placeholders::_1) + ); + this->registerCommandHandler( std::bind(&TargetControllerComponent::handleShutdown, this, std::placeholders::_1) ); @@ -257,6 +279,12 @@ namespace Bloom::TargetController this->state = TargetControllerState::INACTIVE; EventManager::deregisterListener(this->eventListener->getId()); + if (this->activeAtomicSession.has_value()) { + // Reject any commands on the dedicated queue + this->processQueuedCommands(); + this->endActiveAtomicSession(); + } + // Reject any commands still waiting in the queue this->processQueuedCommands(); @@ -367,7 +395,11 @@ namespace Bloom::TargetController void TargetControllerComponent::processQueuedCommands() { auto commands = std::queue>(); - { + if (this->activeAtomicSession.has_value()) { + const auto queueLock = TargetControllerComponent::atomicSessionCommandQueue.acquireLock(); + commands.swap(TargetControllerComponent::atomicSessionCommandQueue.getValue()); + + } else { const auto queueLock = TargetControllerComponent::commandQueue.acquireLock(); commands.swap(TargetControllerComponent::commandQueue.getValue()); } @@ -504,6 +536,29 @@ namespace Bloom::TargetController } } + void TargetControllerComponent::startAtomicSession() { + if (this->activeAtomicSession.has_value()) { + throw Exception("Atomic session already active - nested sessions are not supported"); + } + + this->activeAtomicSession.emplace(); + } + + void TargetControllerComponent::endActiveAtomicSession() { + if (!this->activeAtomicSession.has_value()) { + return; + } + + { + const auto commandQueueLock = TargetControllerComponent::atomicSessionCommandQueue.acquireLock(); + auto empty = std::queue>(); + TargetControllerComponent::atomicSessionCommandQueue.getValue().swap(empty); + } + + this->activeAtomicSession.reset(); + TargetControllerComponent::notifier.notify(); + } + void TargetControllerComponent::loadRegisterDescriptors() { const auto& targetDescriptor = this->getTargetDescriptor(); @@ -645,6 +700,20 @@ namespace Bloom::TargetController } } + std::unique_ptr TargetControllerComponent::handleStartAtomicSession(StartAtomicSession& command) { + this->startAtomicSession(); + return std::make_unique(this->activeAtomicSession->id); + } + + std::unique_ptr TargetControllerComponent::handleEndAtomicSession(EndAtomicSession& command) { + if (!this->activeAtomicSession.has_value() || this->activeAtomicSession->id != command.sessionId) { + throw Exception("Atomic session is not active"); + } + + this->endActiveAtomicSession(); + return std::make_unique(); + } + std::unique_ptr TargetControllerComponent::handleShutdown(Shutdown& command) { this->shutdown(); return std::make_unique(); diff --git a/src/TargetController/TargetControllerComponent.hpp b/src/TargetController/TargetControllerComponent.hpp index 63f96c17..a62a6e8c 100644 --- a/src/TargetController/TargetControllerComponent.hpp +++ b/src/TargetController/TargetControllerComponent.hpp @@ -17,9 +17,12 @@ #include "src/Helpers/ConditionVariableNotifier.hpp" #include "TargetControllerState.hpp" +#include "AtomicSession.hpp" // Commands #include "Commands/Command.hpp" +#include "Commands/StartAtomicSession.hpp" +#include "Commands/EndAtomicSession.hpp" #include "Commands/Shutdown.hpp" #include "Commands/GetTargetDescriptor.hpp" #include "Commands/GetTargetState.hpp" @@ -44,6 +47,7 @@ // Responses #include "Responses/Response.hpp" +#include "Responses/AtomicSessionId.hpp" #include "Responses/TargetDescriptor.hpp" #include "Responses/TargetState.hpp" #include "Responses/TargetRegistersRead.hpp" @@ -88,7 +92,10 @@ namespace Bloom::TargetController */ void run(); - static void registerCommand(std::unique_ptr command); + static void registerCommand( + std::unique_ptr command, + const std::optional& atomicSessionId + ); static std::optional> waitForResponse( Commands::CommandIdType commandId, @@ -96,9 +103,15 @@ namespace Bloom::TargetController ); private: - static inline SyncSafe< - std::queue> - > commandQueue; + static inline SyncSafe>> commandQueue; + + /** + * We have a dedicated queue for atomic sessions. + * + * During an atomic session, all commands for the session are placed into this dedicated queue. + * The TargetController will only serve commands from this dedicated queue, until the atomic session ends. + */ + static inline SyncSafe>> atomicSessionCommandQueue; static inline SyncSafe< std::map> @@ -112,6 +125,8 @@ namespace Bloom::TargetController ProjectConfig projectConfig; EnvironmentConfig environmentConfig; + std::optional activeAtomicSession = std::nullopt; + /** * The TargetController should be the sole owner of the target and debugTool. They are constructed and * destroyed within the TargetController. Under no circumstance should ownership of these resources be @@ -242,6 +257,10 @@ namespace Bloom::TargetController */ void releaseHardware(); + void startAtomicSession(); + + void endActiveAtomicSession(); + /** * Extracts address ranges and groups target register descriptors. */ @@ -304,6 +323,8 @@ namespace Bloom::TargetController void onDebugSessionFinishedEvent(const Events::DebugSessionFinished& event); // Command handlers + std::unique_ptr handleStartAtomicSession(Commands::StartAtomicSession& command); + std::unique_ptr handleEndAtomicSession(Commands::EndAtomicSession& command); std::unique_ptr handleShutdown(Commands::Shutdown& command); std::unique_ptr handleGetTargetDescriptor(Commands::GetTargetDescriptor& command); std::unique_ptr handleGetTargetState(Commands::GetTargetState& command);