summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorVernon Mauery <vernon.mauery@linux.intel.com>2018-11-09 08:43:24 -0800
committerVernon Mauery <vernon.mauery@linux.intel.com>2019-02-01 13:01:18 -0800
commit6f353e868d6fef90e35ef13da7c7ae912becb97a (patch)
tree1c6f6ab825c931cdd1d31e97eaa462b49c6f964b
parent7e4a651799df95e44b4c99825adf18a8ef878f52 (diff)
downloadphosphor-net-ipmid-6f353e868d6fef90e35ef13da7c7ae912becb97a.tar.gz
phosphor-net-ipmid-6f353e868d6fef90e35ef13da7c7ae912becb97a.zip
netipmid: move sol timers to asio
The IPMI SOL console was using sd_event-based timers directly (without any abstraction). This moves to a much higher level abstraction that is very easy to use: asio timers. Change-Id: Id5df76a1918cdfae420e01884d664234810b7abd Signed-off-by: Vernon Mauery <vernon.mauery@linux.intel.com>
-rw-r--r--sd_event_loop.cpp230
-rw-r--r--sd_event_loop.hpp100
-rw-r--r--sol/sol_context.cpp133
-rw-r--r--sol/sol_context.hpp54
-rw-r--r--sol/sol_manager.cpp12
5 files changed, 154 insertions, 375 deletions
diff --git a/sd_event_loop.cpp b/sd_event_loop.cpp
index 8d24d23..5be974a 100644
--- a/sd_event_loop.cpp
+++ b/sd_event_loop.cpp
@@ -61,77 +61,6 @@ void EventLoop::startRmcpReceive()
});
}
-static int charAccTimerHandler(sd_event_source* s, uint64_t usec,
- void* userdata)
-{
- auto bufferSize = std::get<sol::Manager&>(singletonPool).dataBuffer.size();
-
- try
- {
- // The instance is hardcoded to 1, in the case of supporting multiple
- // payload instances we would need to populate it from userdata
- uint8_t instance = 1;
-
- if (bufferSize > 0)
- {
- auto& context =
- std::get<sol::Manager&>(singletonPool).getContext(instance);
-
- int rc = context.sendOutboundPayload();
-
- if (rc == 0)
- {
- return 0;
- }
- }
-
- std::get<eventloop::EventLoop&>(singletonPool)
- .switchTimer(instance, Timers::ACCUMULATE, true);
- }
- catch (std::exception& e)
- {
- log<level::ERR>(e.what());
- }
-
- return 0;
-}
-
-static int retryTimerHandler(sd_event_source* s, uint64_t usec, void* userdata)
-{
- try
- {
- // The instance is hardcoded to 1, in the case of supporting multiple
- // payload instances we would need to populate it from userdata
- uint8_t instance = 1;
-
- auto& context =
- std::get<sol::Manager&>(singletonPool).getContext(instance);
-
- if (context.retryCounter)
- {
- --context.retryCounter;
- std::get<eventloop::EventLoop&>(singletonPool)
- .switchTimer(instance, Timers::RETRY, true);
- context.resendPayload(sol::Context::noClear);
- }
- else
- {
- context.retryCounter = context.maxRetryCount;
- context.resendPayload(sol::Context::clear);
- std::get<eventloop::EventLoop&>(singletonPool)
- .switchTimer(instance, Timers::RETRY, false);
- std::get<eventloop::EventLoop&>(singletonPool)
- .switchTimer(instance, Timers::ACCUMULATE, true);
- }
- }
- catch (std::exception& e)
- {
- log<level::ERR>(e.what());
- }
-
- return 0;
-}
-
int EventLoop::startEventLoop()
{
sdbusplus::asio::sd_event_wrapper sdEvents(*io);
@@ -179,163 +108,4 @@ int EventLoop::startEventLoop()
return EXIT_SUCCESS;
}
-void EventLoop::startSOLPayloadInstance(uint8_t payloadInst,
- IntervalType accumulateInterval,
- IntervalType retryInterval)
-{
- auto instance = payloadInst;
- sd_event_source* accTimerSource = nullptr;
- sd_event_source* retryTimerSource = nullptr;
- int rc = 0;
- uint64_t currentTime = 0;
-
- rc = sd_event_now(event, CLOCK_MONOTONIC, &currentTime);
- if (rc < 0)
- {
- log<level::ERR>("Failed to get the current timestamp",
- entry("RC=%d", rc));
- throw std::runtime_error("Failed to get current timestamp");
- }
-
- // Create character accumulate timer
- rc = sd_event_add_time(event, &accTimerSource, CLOCK_MONOTONIC,
- currentTime + accumulateInterval.count(), 0,
- charAccTimerHandler, static_cast<void*>(&instance));
- if (rc < 0)
- {
- log<level::ERR>("Failed to setup the accumulate timer",
- entry("RC=%d", rc));
- throw std::runtime_error("Failed to setup accumulate timer");
- }
-
- // Create retry interval timer and add to the event loop
- rc = sd_event_add_time(event, &retryTimerSource, CLOCK_MONOTONIC,
- currentTime + retryInterval.count(), 0,
- retryTimerHandler, static_cast<void*>(&instance));
- if (rc < 0)
- {
- log<level::ERR>("Failed to setup the retry timer", entry("RC=%d", rc));
- throw std::runtime_error("Failed to setup retry timer");
- }
-
- // Enable the Character Accumulate Timer
- rc = sd_event_source_set_enabled(accTimerSource, SD_EVENT_ONESHOT);
- if (rc < 0)
- {
- log<level::ERR>("Failed to enable the accumulate timer",
- entry("RC=%d", rc));
- throw std::runtime_error("Failed to enable accumulate timer");
- }
-
- // Disable the Retry Interval Timer
- rc = sd_event_source_set_enabled(retryTimerSource, SD_EVENT_OFF);
- if (rc < 0)
- {
- log<level::ERR>("Failed to disable the retry timer",
- entry("RC=%d", rc));
- throw std::runtime_error("Failed to disable retry timer");
- }
-
- EventSource accEventSource(accTimerSource);
- EventSource retryEventSource(retryTimerSource);
- accTimerSource = nullptr;
- retryTimerSource = nullptr;
-
- TimerMap timer;
- timer.emplace(Timers::ACCUMULATE, std::make_tuple(std::move(accEventSource),
- accumulateInterval));
- timer.emplace(Timers::RETRY,
- std::make_tuple(std::move(retryEventSource), retryInterval));
- payloadInfo.emplace(instance, std::move(timer));
-}
-
-void EventLoop::stopSOLPayloadInstance(uint8_t payloadInst)
-{
- auto iter = payloadInfo.find(payloadInst);
- if (iter == payloadInfo.end())
- {
- log<level::ERR>("SOL Payload instance not found",
- entry("PAYLOADINST=%d", payloadInst));
- throw std::runtime_error("SOL payload instance not found");
- }
-
- int rc = 0;
-
- /* Destroy the character accumulate timer event source */
- rc = sd_event_source_set_enabled(
- (std::get<0>(iter->second.at(Timers::ACCUMULATE))).get(), SD_EVENT_OFF);
- if (rc < 0)
- {
- log<level::ERR>("Failed to disable the character accumulate timer",
- entry("RC=%d", rc));
- payloadInfo.erase(payloadInst);
- throw std::runtime_error("Failed to disable accumulate timer");
- }
-
- /* Destroy the retry interval timer event source */
- rc = sd_event_source_set_enabled(
- (std::get<0>(iter->second.at(Timers::RETRY))).get(), SD_EVENT_OFF);
- if (rc < 0)
- {
- log<level::ERR>("Failed to disable the retry timer",
- entry("RC=%d", rc));
- payloadInfo.erase(payloadInst);
- throw std::runtime_error("Failed to disable retry timer");
- }
-
- payloadInfo.erase(payloadInst);
-}
-
-void EventLoop::switchTimer(uint8_t payloadInst, Timers type, bool status)
-{
- auto iter = payloadInfo.find(payloadInst);
- if (iter == payloadInfo.end())
- {
- log<level::ERR>("SOL Payload instance not found",
- entry("PAYLOADINST=%d", payloadInst));
- throw std::runtime_error("SOL Payload instance not found");
- }
-
- int rc = 0;
- auto source = (std::get<0>(iter->second.at(type))).get();
- auto interval = std::get<1>(iter->second.at(type));
-
- // Turn OFF the timer
- if (!status)
- {
- rc = sd_event_source_set_enabled(source, SD_EVENT_OFF);
- if (rc < 0)
- {
- log<level::ERR>("Failed to disable the timer", entry("RC=%d", rc));
- throw std::runtime_error("Failed to disable timer");
- }
- return;
- }
-
- // Turn ON the timer
- uint64_t currentTime = 0;
- rc = sd_event_now(event, CLOCK_MONOTONIC, &currentTime);
- if (rc < 0)
- {
- log<level::ERR>("Failed to get the current timestamp",
- entry("RC=%d", rc));
- throw std::runtime_error("Failed to get current timestamp");
- }
-
- rc = sd_event_source_set_time(source, currentTime + interval.count());
- if (rc < 0)
- {
- log<level::ERR>("sd_event_source_set_time function failed",
- entry("RC=%d", rc));
- throw std::runtime_error("sd_event_source_set_time function failed");
- }
-
- rc = sd_event_source_set_enabled(source, SD_EVENT_ONESHOT);
- if (rc < 0)
- {
- log<level::ERR>("Failed to enable the timer", entry("RC=%d", rc));
- throw std::runtime_error("Failed to enable timer");
- }
-}
-
} // namespace eventloop
diff --git a/sd_event_loop.hpp b/sd_event_loop.hpp
index 0d9c268..10b24ed 100644
--- a/sd_event_loop.hpp
+++ b/sd_event_loop.hpp
@@ -11,35 +11,6 @@
namespace eventloop
{
-/** @struct EventSourceDeleter
- *
- * Custom deleter for the sd_event_source*.
- */
-struct EventSourceDeleter
-{
- void operator()(sd_event_source* event) const
- {
- event = sd_event_source_unref(event);
- }
-};
-
-using EventSource = std::unique_ptr<sd_event_source, EventSourceDeleter>;
-using IntervalType = std::chrono::microseconds;
-
-/** @enum Timers
- *
- * For SOL functioning, there are two timers involved. The character accumulate
- * interval timer is the amount of time that the BMC will wait before
- * transmitting a partial SOL packet. The retry interval timer is the time that
- * BMC will wait before the first retry and the time between retries when
- * sending SOL packets to the remote console.
- */
-enum class Timers
-{
- ACCUMULATE, /**< Character Accumulate Timer */
- RETRY, /**< Retry Interval Timer */
-};
-
class EventLoop
{
public:
@@ -53,22 +24,6 @@ class EventLoop
EventLoop(EventLoop&&) = delete;
EventLoop& operator=(EventLoop&&) = delete;
- /** @brief Timer Map
- *
- * The key for the timer map is the timer type. There are two types of
- * timers, character accumulate timer and retry interval timer. The
- * entries in the values is the event source for the timer and the
- * interval.
- */
- using TimerMap = std::map<Timers, std::tuple<EventSource, IntervalType>>;
-
- /** @brief SOL Payload Map.
- *
- * The key for the payload map is the payload instance, the entries in
- * the value are a map of timers.
- */
- using PayloadMap = std::map<uint8_t, TimerMap>;
-
/** @brief Initialise the event loop and add the handler for incoming
* IPMI packets.
*
@@ -76,56 +31,6 @@ class EventLoop
*/
int startEventLoop();
- /** @brief Initialize the timers for the SOL payload instance
- *
- * This API would add the Character accumulate interval timer event
- * source and the retry interval timer event source for the SOL payload
- * instance to the event loop.
- *
- * @param[in] payloadInst - SOL payload instance.
- * @param[in] accumulateInterval - Character accumulate interval.
- * @param[in] retryInterval - Retry interval.
- */
- void startSOLPayloadInstance(uint8_t payloadInst,
- IntervalType accumulateInterval,
- IntervalType retryInterval);
-
- /** @brief Stop the timers for the SOL payload instance.
- *
- * This would remove the character accumulate interval timer event
- * source and the retry interval timer event source from the event
- * loop.
- *
- * @param[in] payloadInst - SOL payload instance
- */
- void stopSOLPayloadInstance(uint8_t payloadInst);
-
- /** @brief Modify the timer event source to enable/disable.
- *
- * When the timer is enabled, the timer it set to fire again at
- * timer's interval for the instance added to the event loop iteration
- * timestamp. When the timer is disabled, the event source for the
- * timer is disabled.
- *
- * @param[in] payloadInst - SOL payload instance.
- * @param[in] type - Timer type.
- * @param[in] status - on/off the event source.
- */
- void switchTimer(uint8_t payloadInst, Timers type, bool status);
-
- /** @brief Modify the retry interval timer event source to enable/
- * disable
- *
- * When the timer is enabled, the timer it set to fire again at
- * retry interval for the instance added to the event loop iteration
- * timestamp. When the timer is disabled the event source for the
- * retry interval timer is disabled.
- *
- * @param[in] payloadInst - SOL payload instance.
- * @param[in] status - on/off the event source.
- */
- void switchRetryTimer(uint8_t payloadInst, bool status);
-
/** @brief Event loop object. */
sd_event* event = nullptr;
@@ -143,11 +48,6 @@ class EventLoop
/** @brief boost::asio udp socket
*/
std::shared_ptr<boost::asio::ip::udp::socket> udpSocket = nullptr;
-
- /** @brief Map to keep information regarding IPMI payload instance and
- * timers for character accumulate interval and retry interval.
- */
- PayloadMap payloadInfo;
};
} // namespace eventloop
diff --git a/sol/sol_context.cpp b/sol/sol_context.cpp
index fffb66f..215f725 100644
--- a/sol/sol_context.cpp
+++ b/sol/sol_context.cpp
@@ -8,9 +8,62 @@
namespace sol
{
-
using namespace phosphor::logging;
+Context::Context(std::shared_ptr<boost::asio::io_context> io,
+ uint8_t maxRetryCount, uint8_t sendThreshold, uint8_t instance,
+ session::SessionID sessionID) :
+ accumulateTimer(*io),
+ retryTimer(*io), maxRetryCount(maxRetryCount), retryCounter(maxRetryCount),
+ sendThreshold(sendThreshold), payloadInstance(instance),
+ sessionID(sessionID)
+{
+ session = std::get<session::Manager&>(singletonPool).getSession(sessionID);
+ enableAccumulateTimer(true);
+}
+
+void Context::enableAccumulateTimer(bool enable)
+{
+ // fetch the timeout from the SOL manager
+ std::chrono::microseconds interval =
+ std::get<sol::Manager&>(singletonPool).accumulateInterval;
+ if (enable)
+ {
+ accumulateTimer.expires_after(interval);
+ accumulateTimer.async_wait([this](const boost::system::error_code& ec) {
+ if (!ec)
+ {
+ charAccTimerHandler();
+ }
+ });
+ }
+ else
+ {
+ accumulateTimer.cancel();
+ }
+}
+
+void Context::enableRetryTimer(bool enable)
+{
+ if (enable)
+ {
+ // fetch the timeout from the SOL manager
+ std::chrono::microseconds interval =
+ std::get<sol::Manager&>(singletonPool).retryInterval;
+ retryTimer.expires_after(interval);
+ retryTimer.async_wait([this](const boost::system::error_code& ec) {
+ if (!ec)
+ {
+ retryTimerHandler();
+ }
+ });
+ }
+ else
+ {
+ retryTimer.cancel();
+ }
+}
+
void Context::processInboundPayload(uint8_t seqNum, uint8_t ackSeqNum,
uint8_t count, bool status,
const std::vector<uint8_t>& input)
@@ -54,10 +107,8 @@ void Context::processInboundPayload(uint8_t seqNum, uint8_t ackSeqNum,
if (status || ((count != expectedCharCount) && ackSeqNum))
{
resendPayload(noClear);
- std::get<eventloop::EventLoop&>(singletonPool)
- .switchTimer(payloadInstance, eventloop::Timers::RETRY, false);
- std::get<eventloop::EventLoop&>(singletonPool)
- .switchTimer(payloadInstance, eventloop::Timers::RETRY, true);
+ enableRetryTimer(false);
+ enableRetryTimer(true);
return;
}
/*
@@ -70,8 +121,7 @@ void Context::processInboundPayload(uint8_t seqNum, uint8_t ackSeqNum,
std::get<sol::Manager&>(singletonPool).dataBuffer.erase(count);
// Once it is acknowledged stop the retry interval timer
- std::get<eventloop::EventLoop&>(singletonPool)
- .switchTimer(payloadInstance, eventloop::Timers::RETRY, false);
+ enableRetryTimer(false);
retryCounter = maxRetryCount;
expectedCharCount = 0;
@@ -111,8 +161,7 @@ void Context::processInboundPayload(uint8_t seqNum, uint8_t ackSeqNum,
}
else
{
- std::get<eventloop::EventLoop&>(singletonPool)
- .switchTimer(payloadInstance, eventloop::Timers::ACCUMULATE, true);
+ enableAccumulateTimer(true);
}
}
@@ -123,8 +172,7 @@ void Context::prepareResponse(uint8_t ackSeqNum, uint8_t count, bool ack)
/* Sent a ACK only response */
if (payloadCache.size() != 0 || (bufferSize < sendThreshold))
{
- std::get<eventloop::EventLoop&>(singletonPool)
- .switchTimer(payloadInstance, eventloop::Timers::ACCUMULATE, true);
+ enableAccumulateTimer(true);
std::vector<uint8_t> outPayload(sizeof(Payload));
auto response = reinterpret_cast<Payload*>(outPayload.data());
@@ -148,10 +196,8 @@ void Context::prepareResponse(uint8_t ackSeqNum, uint8_t count, bool ack)
std::copy_n(handle, readSize, payloadCache.data() + sizeof(Payload));
expectedCharCount = readSize;
- std::get<eventloop::EventLoop&>(singletonPool)
- .switchTimer(payloadInstance, eventloop::Timers::RETRY, true);
- std::get<eventloop::EventLoop&>(singletonPool)
- .switchTimer(payloadInstance, eventloop::Timers::ACCUMULATE, false);
+ enableRetryTimer(true);
+ enableAccumulateTimer(false);
sendPayload(payloadCache);
}
@@ -160,8 +206,7 @@ int Context::sendOutboundPayload()
{
if (payloadCache.size() != 0)
{
- std::get<eventloop::EventLoop&>(singletonPool)
- .switchTimer(payloadInstance, eventloop::Timers::ACCUMULATE, true);
+ enableAccumulateTimer(true);
return -1;
}
@@ -179,10 +224,8 @@ int Context::sendOutboundPayload()
std::copy_n(handle, readSize, payloadCache.data() + sizeof(Payload));
expectedCharCount = readSize;
- std::get<eventloop::EventLoop&>(singletonPool)
- .switchTimer(payloadInstance, eventloop::Timers::RETRY, true);
- std::get<eventloop::EventLoop&>(singletonPool)
- .switchTimer(payloadInstance, eventloop::Timers::ACCUMULATE, false);
+ enableRetryTimer(true);
+ enableAccumulateTimer(false);
sendPayload(payloadCache);
@@ -204,12 +247,54 @@ void Context::resendPayload(bool clear)
void Context::sendPayload(const std::vector<uint8_t>& out) const
{
- auto session =
- std::get<session::Manager&>(singletonPool).getSession(sessionID);
-
message::Handler msgHandler(session->channelPtr, sessionID);
msgHandler.sendSOLPayload(out);
}
+void Context::charAccTimerHandler()
+{
+ auto bufferSize = std::get<sol::Manager&>(singletonPool).dataBuffer.size();
+
+ try
+ {
+ if (bufferSize > 0)
+ {
+ int rc = sendOutboundPayload();
+ if (rc == 0)
+ {
+ return;
+ }
+ }
+ enableAccumulateTimer(true);
+ }
+ catch (std::exception& e)
+ {
+ log<level::ERR>(e.what());
+ }
+}
+
+void Context::retryTimerHandler()
+{
+ try
+ {
+ if (retryCounter)
+ {
+ --retryCounter;
+ enableRetryTimer(true);
+ resendPayload(sol::Context::noClear);
+ }
+ else
+ {
+ retryCounter = maxRetryCount;
+ resendPayload(sol::Context::clear);
+ enableRetryTimer(false);
+ enableAccumulateTimer(true);
+ }
+ }
+ catch (std::exception& e)
+ {
+ log<level::ERR>(e.what());
+ }
+}
} // namespace sol
diff --git a/sol/sol_context.hpp b/sol/sol_context.hpp
index 220040b..8d0fc87 100644
--- a/sol/sol_context.hpp
+++ b/sol/sol_context.hpp
@@ -3,6 +3,9 @@
#include "console_buffer.hpp"
#include "session.hpp"
+#include <boost/asio/io_context.hpp>
+#include <boost/asio/steady_timer.hpp>
+
namespace sol
{
@@ -149,34 +152,37 @@ struct SequenceNumbers
class Context
{
public:
- Context() = default;
+ Context() = delete;
~Context() = default;
Context(const Context&) = delete;
Context& operator=(const Context&) = delete;
- Context(Context&&) = default;
- Context& operator=(Context&&) = default;
+ Context(Context&&) = delete;
+ Context& operator=(Context&&) = delete;
/** @brief Context Constructor.
*
* This is issued by the SOL Manager when a SOL payload instance is
* started for the activate payload command.
*
+ * @param[in] io - boost::asio io context for event scheduling.
* @param[in] maxRetryCount - Retry count max value.
* @param[in] sendThreshold - Character send threshold.
* @param[in] instance - SOL payload instance.
* @param[in] sessionID - BMC session ID.
*/
- Context(uint8_t maxRetryCount, uint8_t sendThreshold, uint8_t instance,
- session::SessionID sessionID) :
- maxRetryCount(maxRetryCount),
- retryCounter(maxRetryCount), sendThreshold(sendThreshold),
- payloadInstance(instance), sessionID(sessionID)
- {
- }
+ Context(std::shared_ptr<boost::asio::io_context> io, uint8_t maxRetryCount,
+ uint8_t sendThreshold, uint8_t instance,
+ session::SessionID sessionID);
static constexpr auto clear = true;
static constexpr auto noClear = false;
+ /** @brief accumulate timer */
+ boost::asio::steady_timer accumulateTimer;
+
+ /** @brief retry timer */
+ boost::asio::steady_timer retryTimer;
+
/** @brief Retry count max value. */
const uint8_t maxRetryCount = 0;
@@ -192,6 +198,28 @@ class Context
/** @brief Session ID. */
const session::SessionID sessionID = 0;
+ /** @brief session pointer
+ */
+ std::shared_ptr<session::Session> session;
+
+ /** @brief enable/disable accumulate timer
+ *
+ * The timeout interval is managed by the SOL Manager;
+ * this function only enables or disable the timer
+ *
+ * @param[in] enable - enable(true) or disable(false) accumulation timer
+ */
+ void enableAccumulateTimer(bool enable);
+
+ /** @brief enable/disable retry timer
+ *
+ * The timeout interval is managed by the SOL Manager;
+ * this function only enables or disable the timer
+ *
+ * @param[in] enable - enable(true) or disable(false) retry timer
+ */
+ void enableRetryTimer(bool enable);
+
/** @brief Process the Inbound SOL payload.
*
* The SOL payload from the remote console is processed and the
@@ -253,6 +281,12 @@ class Context
* @param[in] out - buffer containing the SOL payload.
*/
void sendPayload(const std::vector<uint8_t>& out) const;
+
+ /** @brief accumlate timer handler called by timer */
+ void charAccTimerHandler();
+
+ /** @brief retry timer handler called by timer */
+ void retryTimerHandler();
};
} // namespace sol
diff --git a/sol/sol_manager.cpp b/sol/sol_manager.cpp
index fc0efe9..2046fe4 100644
--- a/sol/sol_manager.cpp
+++ b/sol/sol_manager.cpp
@@ -102,16 +102,9 @@ void Manager::startPayloadInstance(uint8_t payloadInstance,
}
// Create the SOL Context data for payload instance
- auto context = std::make_unique<Context>(retryCount, sendThreshold,
+ auto context = std::make_unique<Context>(io, retryCount, sendThreshold,
payloadInstance, sessionID);
- std::get<eventloop::EventLoop&>(singletonPool)
- .startSOLPayloadInstance(
- payloadInstance,
- std::chrono::duration_cast<eventloop::IntervalType>(
- accumulateInterval),
- std::chrono::duration_cast<eventloop::IntervalType>(retryInterval));
-
payloadMap.emplace(payloadInstance, std::move(context));
}
@@ -125,9 +118,6 @@ void Manager::stopPayloadInstance(uint8_t payloadInstance)
payloadMap.erase(iter);
- std::get<eventloop::EventLoop&>(singletonPool)
- .stopSOLPayloadInstance(payloadInstance);
-
if (payloadMap.empty())
{
stopHostConsole();
OpenPOWER on IntegriCloud