Added atomic sessions in TC

This commit is contained in:
Nav
2023-06-01 22:13:07 +01:00
parent 2ea7c1e67b
commit 30936fe0a2
11 changed files with 333 additions and 30 deletions

View File

@@ -0,0 +1,25 @@
#pragma once
#include <atomic>
#include <cstdint>
namespace Bloom::TargetController
{
using AtomicSessionIdType = int;
static_assert(std::atomic<AtomicSessionIdType>::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<AtomicSessionIdType> lastSessionId = 0;
};
}

View File

@@ -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<Responses::Response, typename CommandType::SuccessResponseType>
auto sendCommandAndWaitForResponse(
std::unique_ptr<CommandType> command,
std::chrono::milliseconds timeout
std::chrono::milliseconds timeout,
std::optional<AtomicSessionIdType> 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);

View File

@@ -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,

View File

@@ -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;
}
};
}

View File

@@ -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;
}
};
}

View File

@@ -0,0 +1,26 @@
#pragma once
#include <cstdint>
#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;
}
};
}

View File

@@ -9,6 +9,7 @@ namespace Bloom::TargetController::Responses
GENERIC,
ERROR,
STATE,
ATOMIC_SESSION_ID,
TARGET_DESCRIPTOR,
TARGET_REGISTERS_READ,
TARGET_MEMORY_READ,

View File

@@ -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> command) {
void TargetControllerComponent::registerCommand(
std::unique_ptr<Command> command,
const std::optional<AtomicSessionIdType>& 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<StartAtomicSession>(
std::bind(&TargetControllerComponent::handleStartAtomicSession, this, std::placeholders::_1)
);
this->registerCommandHandler<EndAtomicSession>(
std::bind(&TargetControllerComponent::handleEndAtomicSession, this, std::placeholders::_1)
);
this->registerCommandHandler<Shutdown>(
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<std::unique_ptr<Command>>();
{
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<std::unique_ptr<Commands::Command>>();
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<AtomicSessionId> TargetControllerComponent::handleStartAtomicSession(StartAtomicSession& command) {
this->startAtomicSession();
return std::make_unique<AtomicSessionId>(this->activeAtomicSession->id);
}
std::unique_ptr<Response> 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<Response>();
}
std::unique_ptr<Response> TargetControllerComponent::handleShutdown(Shutdown& command) {
this->shutdown();
return std::make_unique<Response>();

View File

@@ -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<Commands::Command> command);
static void registerCommand(
std::unique_ptr<Commands::Command> command,
const std::optional<AtomicSessionIdType>& atomicSessionId
);
static std::optional<std::unique_ptr<Responses::Response>> waitForResponse(
Commands::CommandIdType commandId,
@@ -96,9 +103,15 @@ namespace Bloom::TargetController
);
private:
static inline SyncSafe<
std::queue<std::unique_ptr<Commands::Command>>
> commandQueue;
static inline SyncSafe<std::queue<std::unique_ptr<Commands::Command>>> 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<std::queue<std::unique_ptr<Commands::Command>>> atomicSessionCommandQueue;
static inline SyncSafe<
std::map<Commands::CommandIdType, std::unique_ptr<Responses::Response>>
@@ -112,6 +125,8 @@ namespace Bloom::TargetController
ProjectConfig projectConfig;
EnvironmentConfig environmentConfig;
std::optional<AtomicSession> 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<Responses::AtomicSessionId> handleStartAtomicSession(Commands::StartAtomicSession& command);
std::unique_ptr<Responses::Response> handleEndAtomicSession(Commands::EndAtomicSession& command);
std::unique_ptr<Responses::Response> handleShutdown(Commands::Shutdown& command);
std::unique_ptr<Responses::TargetDescriptor> handleGetTargetDescriptor(Commands::GetTargetDescriptor& command);
std::unique_ptr<Responses::TargetState> handleGetTargetState(Commands::GetTargetState& command);