IVGCVSW-3972 Implement the Disconnect functionality

 * Added Disconnect method to the ProfilingService class
 * Added unit test

Signed-off-by: Matteo Martincigh <matteo.martincigh@arm.com>
Signed-off-by: Jim Flynn <jim.flynn@arm.com>
Change-Id: I5cc57abd3e1239cdf8afe21ee4883c1f73cd0e80
diff --git a/src/profiling/ProfilingService.cpp b/src/profiling/ProfilingService.cpp
index 7918441..b481695 100644
--- a/src/profiling/ProfilingService.cpp
+++ b/src/profiling/ProfilingService.cpp
@@ -103,6 +103,26 @@
     }
 }
 
+void ProfilingService::Disconnect()
+{
+    ProfilingState currentState = m_StateMachine.GetCurrentState();
+    switch (currentState)
+    {
+    case ProfilingState::Uninitialised:
+    case ProfilingState::NotConnected:
+    case ProfilingState::WaitingForAck:
+        return; // NOP
+    case ProfilingState::Active:
+        // Stop the command thread (if running)
+        Stop();
+
+        break;
+    default:
+        throw RuntimeException(boost::str(boost::format("Unknown profiling service state: %1")
+                                          % static_cast<int>(currentState)));
+    }
+}
+
 const ICounterDirectory& ProfilingService::GetCounterDirectory() const
 {
     return m_CounterDirectory;
@@ -244,7 +264,18 @@
 void ProfilingService::Reset()
 {
     // Reset the profiling service
+    Stop();
+    // ...then delete all the counter data and configuration...
+    m_CounterIndex.clear();
+    m_CounterValues.clear();
+    m_CounterDirectory.Clear();
 
+    // ...finally reset the profiling state machine
+    m_StateMachine.Reset();
+}
+
+void ProfilingService::Stop()
+{
     // The order in which we reset/stop the components is not trivial!
 
     // First stop the threads (Command Handler first)...
@@ -253,15 +284,13 @@
     m_PeriodicCounterCapture.Stop();
 
     // ...then destroy the profiling connection...
+    if (m_ProfilingConnection != nullptr && m_ProfilingConnection->IsOpen())
+    {
+        m_ProfilingConnection->Close();
+    }
     m_ProfilingConnection.reset();
 
-    // ...then delete all the counter data and configuration...
-    m_CounterIndex.clear();
-    m_CounterValues.clear();
-    m_CounterDirectory.Clear();
-
-    // ...finally reset the profiling state machine
-    m_StateMachine.Reset();
+    m_StateMachine.TransitionToState(ProfilingState::NotConnected);
 }
 
 inline void ProfilingService::CheckCounterUid(uint16_t counterUid) const
diff --git a/src/profiling/ProfilingService.hpp b/src/profiling/ProfilingService.hpp
index dd70af4..d4fa856 100644
--- a/src/profiling/ProfilingService.hpp
+++ b/src/profiling/ProfilingService.hpp
@@ -45,6 +45,9 @@
     // Updates the profiling service, making it transition to a new state if necessary
     void Update();
 
+    // Disconnects the profiling service from the external server
+    void Disconnect();
+
     // Getters for the profiling service state
     const ICounterDirectory& GetCounterDirectory() const;
     ProfilingState GetCurrentState() const;
@@ -70,6 +73,7 @@
     void Initialize();
     void InitializeCounterValue(uint16_t counterUid);
     void Reset();
+    void Stop();
 
     // Helper function
     void CheckCounterUid(uint16_t counterUid) const;
diff --git a/src/profiling/ProfilingStateMachine.cpp b/src/profiling/ProfilingStateMachine.cpp
index 9d3a81f..58fac96 100644
--- a/src/profiling/ProfilingStateMachine.cpp
+++ b/src/profiling/ProfilingStateMachine.cpp
@@ -53,7 +53,7 @@
         do
         {
             if (!IsOneOfStates(currentState, ProfilingState::Uninitialised, ProfilingState::NotConnected,
-                               ProfilingState::Active))
+                               ProfilingState::Active, ProfilingState::WaitingForAck))
             {
                 ThrowStateTransitionException(currentState, newState);
             }
diff --git a/src/profiling/test/ProfilingTests.cpp b/src/profiling/test/ProfilingTests.cpp
index 554b7e1..446c609 100644
--- a/src/profiling/test/ProfilingTests.cpp
+++ b/src/profiling/test/ProfilingTests.cpp
@@ -424,8 +424,8 @@
                       armnn::Exception);
 
     ProfilingStateMachine profilingState14(ProfilingState::WaitingForAck);
-    BOOST_CHECK_THROW(profilingState14.TransitionToState(ProfilingState::NotConnected),
-                      armnn::Exception);
+    profilingState14.TransitionToState(ProfilingState::NotConnected);
+    BOOST_CHECK(profilingState14.GetCurrentState() == ProfilingState::NotConnected);
 
     ProfilingStateMachine profilingState15(ProfilingState::Active);
     BOOST_CHECK_THROW(profilingState15.TransitionToState(ProfilingState::Uninitialised),
@@ -2281,6 +2281,7 @@
     unsigned int processNameSize = processName.empty() ? 0 : boost::numeric_cast<unsigned int>(processName.size()) + 1;
     unsigned int streamMetadataPacketsize = 118 + processNameSize;
 
+    // Reset the profiling service to the uninitialized state
     armnn::Runtime::CreationOptions::ExternalProfilingOptions options;
     options.m_EnableProfiling = true;
     ProfilingService& profilingService = ProfilingService::Instance();
@@ -2354,6 +2355,7 @@
     unsigned int processNameSize = processName.empty() ? 0 : boost::numeric_cast<unsigned int>(processName.size()) + 1;
     unsigned int streamMetadataPacketsize = 118 + processNameSize;
 
+    // Reset the profiling service to the uninitialized state
     armnn::Runtime::CreationOptions::ExternalProfilingOptions options;
     options.m_EnableProfiling = true;
     ProfilingService& profilingService = ProfilingService::Instance();
@@ -3012,4 +3014,62 @@
     profilingService.ResetExternalProfilingOptions(options, true);
 }
 
+BOOST_AUTO_TEST_CASE(CheckProfilingServiceDisconnect)
+{
+    // Swap the profiling connection factory in the profiling service instance with our mock one
+    SwapProfilingConnectionFactoryHelper helper;
+
+    // Reset the profiling service to the uninitialized state
+    armnn::Runtime::CreationOptions::ExternalProfilingOptions options;
+    options.m_EnableProfiling = true;
+    ProfilingService& profilingService = ProfilingService::Instance();
+    profilingService.ResetExternalProfilingOptions(options, true);
+
+    // Try to disconnect the profiling service while in the "Uninitialised" state
+    BOOST_CHECK(profilingService.GetCurrentState() == ProfilingState::Uninitialised);
+    profilingService.Disconnect();
+    BOOST_CHECK(profilingService.GetCurrentState() == ProfilingState::Uninitialised); // The state should not change
+
+    // Try to disconnect the profiling service while in the "NotConnected" state
+    profilingService.Update(); // Initialize the counter directory
+    BOOST_CHECK(profilingService.GetCurrentState() == ProfilingState::NotConnected);
+    profilingService.Disconnect();
+    BOOST_CHECK(profilingService.GetCurrentState() == ProfilingState::NotConnected); // The state should not change
+
+    // Try to disconnect the profiling service while in the "WaitingForAck" state
+    profilingService.Update(); // Create the profiling connection
+    BOOST_CHECK(profilingService.GetCurrentState() == ProfilingState::WaitingForAck);
+    profilingService.Disconnect();
+    BOOST_CHECK(profilingService.GetCurrentState() == ProfilingState::WaitingForAck); // The state should not change
+
+    // Try to disconnect the profiling service while in the "Active" state
+    profilingService.Update(); // Start the command handler and the send thread
+
+    // Wait for the Stream Metadata packet the be sent
+    // (we are not testing the connection acknowledgement here so it will be ignored by this test)
+    helper.WaitForProfilingPacketsSent();
+
+    // Force the profiling service to the "Active" state
+    helper.ForceTransitionToState(ProfilingState::Active);
+    BOOST_CHECK(profilingService.GetCurrentState() == ProfilingState::Active);
+
+    // Get the mock profiling connection
+    MockProfilingConnection* mockProfilingConnection = helper.GetMockProfilingConnection();
+    BOOST_CHECK(mockProfilingConnection);
+
+    // Check that the profiling connection is open
+    BOOST_CHECK(mockProfilingConnection->IsOpen());
+
+    profilingService.Disconnect();
+    BOOST_CHECK(profilingService.GetCurrentState() == ProfilingState::NotConnected); // The state should have changed
+
+    // Check that the profiling connection has been reset
+    mockProfilingConnection = helper.GetMockProfilingConnection();
+    BOOST_CHECK(mockProfilingConnection == nullptr);
+
+    // Reset the profiling service to stop any running thread
+    options.m_EnableProfiling = false;
+    profilingService.ResetExternalProfilingOptions(options, true);
+}
+
 BOOST_AUTO_TEST_SUITE_END()