diff --git a/src/TargetController/README.md b/src/TargetController/README.md index bbf51619..cc8bf87c 100644 --- a/src/TargetController/README.md +++ b/src/TargetController/README.md @@ -53,6 +53,19 @@ until a timeout has been reached. Because it is a template function, it is able type at compile-time (see the `SuccessResponseType` alias in some command classes). If the TargetController responds with an error, or the timeout is reached, `CommandManager::sendCommandAndWaitForResponse()` will throw an exception. +#### Atomic operations + +In some instances, we need the TargetController to service a series of commands without any interruptions (servicing of +other commands). + +The TargetController allows for operations to be performed within "atomic sessions". Simply put, when the +TargetController starts a new atomic session, any commands that are part of the session will be placed into a dedicated +queue. When an atomic session is active, the TargetController will only process commands in the dedicated queue. +All other commands will be processed once the atomic session has ended. + +The `TargetControllerService` provides an RAII wrapper for atomic sessions. See +[Atomic sessions with the TargetControllerService](#atomic-sessions-with-the-TargetControllerService) for more. + #### The TargetControllerService class The `TargetControllerService` class encapsulates the TargetController's command-response mechanism and provides a @@ -76,6 +89,62 @@ All components within Bloom should use the `TargetControllerService` class to in **should not** directly issue commands via the `Bloom::TargetController::CommandManager`, unless there is a very good reason to do so. +##### Atomic sessions with the TargetControllerService + +The `TargetControllerService::makeAtomicSession()` member function returns an `TargetControllerService::AtomicSession` +RAII object, which starts an atomic session with the TargetController, at construction, and ends the session at +destruction. This allows us to perform operations within an atomic session, in an exception-safe manner: + +```c++ +auto tcService = Services::TargetControllerService(); + +{ + const auto atomicSession = tcService.makeAtomicSession(); + + /* + * These operations will take place in the atomic session - the TC will **NOT** service any other commands until + * these commands have been processed (and the atomic session has ended). + */ + tcService.writeMemory(...); + tcService.readMemory(...); + tcService.getProgramCounter(); + + /* + * Note: The TC does **NOT** support nested atomic sessions. Attempting to start another session in this block will + * result in an exception being thrown. + */ + { + const auto nestedAtomicSession = tcService.makeAtomicSession(); // This will fail - a session is already active. + } + + /* + * Also note: When using TargetControllerService::makeAtomicSession(), the returned AtomicSession object is tied to + * the TargetControllerService object that created it (in this example: tcService). + * + * So if you have **another** TargetControllerService object in this block, any operations performed via that + * object will **NOT** be part of the atomic session, and, they will deadlock the TC. So don't ever do this. + * You should never need more than one TargetControllerService object in a single block. + */ + + // Don't ever do this. + auto anotherTcService = Services::TargetControllerService(); + + // These operations will **NOT** be part of the atomic session, and they will cause a deadlock and timeout. + anotherTcService.writeMemory(...); + anotherTcService.readMemory(...); + + /* + * One more thing: The AtomicSession object should **NEVER** outlive the TargetControllerService object that + * created it. + * + * If this happens, the AtomicSession will have a dangling reference, which will result in UB. + */ +} + +// At this point, the atomic session will have ended. The TC will now process any other commands in the queue. +tcService.readMemory(...); // Will not be part of the atomic session +``` + ### Programming mode When a component needs to write to the target's program memory, it must enable programming mode on the target. This can