mirror of
https://github.com/FairRootGroup/FairMQ.git
synced 2025-10-15 17:41:45 +00:00
FairMQ: Move --catch-signals logic to control plugin
* Add StealDeviceControl() API to plugin services
This commit is contained in:
committed by
Mohammad Al-Turany
parent
7dcd09692c
commit
44a59f25a7
@@ -10,9 +10,21 @@
|
||||
|
||||
#include <termios.h> // for the interactive mode
|
||||
#include <poll.h> // for the interactive mode
|
||||
#include <csignal> // catching system signals
|
||||
#include <functional>
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace
|
||||
{
|
||||
std::function<void(int)> gSignalHandlerClosure;
|
||||
|
||||
extern "C" auto signal_handler(int signal) -> void
|
||||
{
|
||||
gSignalHandlerClosure(signal);
|
||||
}
|
||||
}
|
||||
|
||||
namespace fair
|
||||
{
|
||||
namespace mq
|
||||
@@ -23,6 +35,8 @@ namespace plugins
|
||||
Control::Control(const string name, const Plugin::Version version, const string maintainer, const string homepage, PluginServices* pluginServices)
|
||||
: Plugin(name, version, maintainer, homepage, pluginServices)
|
||||
, fControllerThread()
|
||||
, fSignalHandlerThread()
|
||||
, fDeviceTerminationRequested{false}
|
||||
, fEvents()
|
||||
, fEventsMutex()
|
||||
, fNewEvent()
|
||||
@@ -45,9 +59,21 @@ Control::Control(const string name, const Plugin::Version version, const string
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG(ERROR) << "Unrecognized control mode '" << control << "' requested via command line. " << "Ignoring and falling back to static control mode.";
|
||||
LOG(ERROR) << "Unrecognized control mode '" << control << "' requested. " << "Ignoring and falling back to static control mode.";
|
||||
fControllerThread = thread(&Control::StaticMode, this);
|
||||
}
|
||||
|
||||
LOG(DEBUG) << "catch-signals: " << GetProperty<int>("catch-signals");
|
||||
if (GetProperty<int>("catch-signals") > 0)
|
||||
{
|
||||
gSignalHandlerClosure = bind(&Control::SignalHandler, this, placeholders::_1);
|
||||
signal(SIGINT, signal_handler);
|
||||
signal(SIGTERM, signal_handler);
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG(WARN) << "Signal handling (e.g. Ctrl-C) has been deactivated.";
|
||||
}
|
||||
}
|
||||
catch (PluginServices::DeviceControlError& e)
|
||||
{
|
||||
@@ -58,132 +84,123 @@ Control::Control(const string name, const Plugin::Version version, const string
|
||||
|
||||
auto ControlPluginProgramOptions() -> Plugin::ProgOptions
|
||||
{
|
||||
auto pluginOptions = boost::program_options::options_description{"Control (builtin) Plugin"};
|
||||
namespace po = boost::program_options;
|
||||
auto pluginOptions = po::options_description{"Control (builtin) Plugin"};
|
||||
pluginOptions.add_options()
|
||||
("control", boost::program_options::value<string>()->default_value("interactive"), "Control mode, 'static' or 'interactive'");
|
||||
("control", po::value<string>()->default_value("interactive"), "Control mode, 'static' or 'interactive'")
|
||||
("catch-signals", po::value<int >()->default_value(1), "Enable signal handling (1/0).");
|
||||
return pluginOptions;
|
||||
}
|
||||
|
||||
auto Control::InteractiveMode() -> void
|
||||
{
|
||||
SubscribeToDeviceStateChange([&](DeviceState newState)
|
||||
try
|
||||
{
|
||||
SubscribeToDeviceStateChange([&](DeviceState newState)
|
||||
{
|
||||
lock_guard<mutex> lock{fEventsMutex};
|
||||
fEvents.push(newState);
|
||||
}
|
||||
fNewEvent.notify_one();
|
||||
});
|
||||
{
|
||||
lock_guard<mutex> lock{fEventsMutex};
|
||||
fEvents.push(newState);
|
||||
}
|
||||
fNewEvent.notify_one();
|
||||
});
|
||||
|
||||
ChangeDeviceState(DeviceStateTransition::InitDevice);
|
||||
while (WaitForNextState() != DeviceState::DeviceReady) {}
|
||||
ChangeDeviceState(DeviceStateTransition::InitTask);
|
||||
while (WaitForNextState() != DeviceState::Ready) {}
|
||||
ChangeDeviceState(DeviceStateTransition::Run);
|
||||
RunStartupSequence();
|
||||
|
||||
char input; // hold the user console input
|
||||
pollfd cinfd[1];
|
||||
cinfd[0].fd = fileno(stdin);
|
||||
cinfd[0].events = POLLIN;
|
||||
char input; // hold the user console input
|
||||
pollfd cinfd[1];
|
||||
cinfd[0].fd = fileno(stdin);
|
||||
cinfd[0].events = POLLIN;
|
||||
|
||||
struct termios t;
|
||||
tcgetattr(STDIN_FILENO, &t); // get the current terminal I/O structure
|
||||
t.c_lflag &= ~ICANON; // disable canonical input
|
||||
tcsetattr(STDIN_FILENO, TCSANOW, &t); // apply the new settings
|
||||
struct termios t;
|
||||
tcgetattr(STDIN_FILENO, &t); // get the current terminal I/O structure
|
||||
t.c_lflag &= ~ICANON; // disable canonical input
|
||||
tcsetattr(STDIN_FILENO, TCSANOW, &t); // apply the new settings
|
||||
|
||||
PrintInteractiveHelp();
|
||||
PrintInteractiveHelp();
|
||||
|
||||
bool keepRunning = true;
|
||||
bool keepRunning = true;
|
||||
|
||||
while (keepRunning)
|
||||
{
|
||||
if (poll(cinfd, 1, 500))
|
||||
while (keepRunning)
|
||||
{
|
||||
if (DeviceTerminated())
|
||||
if (poll(cinfd, 1, 500))
|
||||
{
|
||||
if (fDeviceTerminationRequested)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
cin >> input;
|
||||
|
||||
switch (input)
|
||||
{
|
||||
case 'i':
|
||||
LOG(INFO) << "[i] init device";
|
||||
ChangeDeviceState(DeviceStateTransition::InitDevice);
|
||||
break;
|
||||
case 'j':
|
||||
LOG(INFO) << "[j] init task";
|
||||
ChangeDeviceState(DeviceStateTransition::InitTask);
|
||||
break;
|
||||
case 'p':
|
||||
LOG(INFO) << "[p] pause";
|
||||
ChangeDeviceState(DeviceStateTransition::Pause);
|
||||
break;
|
||||
case 'r':
|
||||
LOG(INFO) << "[r] run";
|
||||
ChangeDeviceState(DeviceStateTransition::Run);
|
||||
break;
|
||||
case 's':
|
||||
LOG(INFO) << "[s] stop";
|
||||
ChangeDeviceState(DeviceStateTransition::Stop);
|
||||
break;
|
||||
case 't':
|
||||
LOG(INFO) << "[t] reset task";
|
||||
ChangeDeviceState(DeviceStateTransition::ResetTask);
|
||||
break;
|
||||
case 'd':
|
||||
LOG(INFO) << "[d] reset device";
|
||||
ChangeDeviceState(DeviceStateTransition::ResetDevice);
|
||||
break;
|
||||
case 'h':
|
||||
LOG(INFO) << "[h] help";
|
||||
PrintInteractiveHelp();
|
||||
break;
|
||||
// case 'x':
|
||||
// LOG(INFO) << "[x] ERROR";
|
||||
// ChangeDeviceState(DeviceStateTransition::ERROR_FOUND);
|
||||
// break;
|
||||
case 'q':
|
||||
LOG(INFO) << "[q] end";
|
||||
keepRunning = false;
|
||||
break;
|
||||
default:
|
||||
LOG(INFO) << "Invalid input: [" << input << "]";
|
||||
PrintInteractiveHelp();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (fDeviceTerminationRequested)
|
||||
{
|
||||
keepRunning = false;
|
||||
break;
|
||||
}
|
||||
|
||||
cin >> input;
|
||||
|
||||
switch (input)
|
||||
{
|
||||
case 'i':
|
||||
LOG(INFO) << "[i] init device";
|
||||
ChangeDeviceState(DeviceStateTransition::InitDevice);
|
||||
break;
|
||||
case 'j':
|
||||
LOG(INFO) << "[j] init task";
|
||||
ChangeDeviceState(DeviceStateTransition::InitTask);
|
||||
break;
|
||||
case 'p':
|
||||
LOG(INFO) << "[p] pause";
|
||||
ChangeDeviceState(DeviceStateTransition::Pause);
|
||||
break;
|
||||
case 'r':
|
||||
LOG(INFO) << "[r] run";
|
||||
ChangeDeviceState(DeviceStateTransition::Run);
|
||||
break;
|
||||
case 's':
|
||||
LOG(INFO) << "[s] stop";
|
||||
ChangeDeviceState(DeviceStateTransition::Stop);
|
||||
break;
|
||||
case 't':
|
||||
LOG(INFO) << "[t] reset task";
|
||||
ChangeDeviceState(DeviceStateTransition::ResetTask);
|
||||
break;
|
||||
case 'd':
|
||||
LOG(INFO) << "[d] reset device";
|
||||
ChangeDeviceState(DeviceStateTransition::ResetDevice);
|
||||
break;
|
||||
case 'h':
|
||||
LOG(INFO) << "[h] help";
|
||||
PrintInteractiveHelp();
|
||||
break;
|
||||
// case 'x':
|
||||
// LOG(INFO) << "[x] ERROR";
|
||||
// ChangeDeviceState(DeviceStateTransition::ERROR_FOUND);
|
||||
// break;
|
||||
case 'q':
|
||||
LOG(INFO) << "[q] end";
|
||||
|
||||
ChangeDeviceState(DeviceStateTransition::Stop);
|
||||
|
||||
ChangeDeviceState(DeviceStateTransition::ResetTask);
|
||||
while (WaitForNextState() != DeviceState::DeviceReady) {}
|
||||
|
||||
ChangeDeviceState(DeviceStateTransition::ResetDevice);
|
||||
while (WaitForNextState() != DeviceState::Idle) {}
|
||||
|
||||
ChangeDeviceState(DeviceStateTransition::End);
|
||||
|
||||
if (GetCurrentDeviceState() == DeviceState::Exiting)
|
||||
{
|
||||
keepRunning = false;
|
||||
}
|
||||
|
||||
LOG(INFO) << "Exiting.";
|
||||
break;
|
||||
default:
|
||||
LOG(INFO) << "Invalid input: [" << input << "]";
|
||||
PrintInteractiveHelp();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (DeviceTerminated())
|
||||
tcgetattr(STDIN_FILENO, &t); // get the current terminal I/O structure
|
||||
t.c_lflag |= ICANON; // re-enable canonical input
|
||||
tcsetattr(STDIN_FILENO, TCSANOW, &t); // apply the new settings
|
||||
|
||||
if (!fDeviceTerminationRequested)
|
||||
{
|
||||
keepRunning = false;
|
||||
RunShutdownSequence();
|
||||
}
|
||||
}
|
||||
|
||||
tcgetattr(STDIN_FILENO, &t); // get the current terminal I/O structure
|
||||
t.c_lflag |= ICANON; // re-enable canonical input
|
||||
tcsetattr(STDIN_FILENO, TCSANOW, &t); // apply the new settings
|
||||
|
||||
UnsubscribeFromDeviceStateChange();
|
||||
ReleaseDeviceControl();
|
||||
catch (PluginServices::DeviceControlError& e)
|
||||
{
|
||||
// If we are here, it means another plugin has taken control. That's fine, just print the exception message and do nothing else.
|
||||
LOG(DEBUG) << e.what();
|
||||
}
|
||||
}
|
||||
|
||||
auto Control::PrintInteractiveHelp() -> void
|
||||
@@ -207,42 +224,117 @@ auto Control::WaitForNextState() -> DeviceState
|
||||
|
||||
auto Control::StaticMode() -> void
|
||||
{
|
||||
SubscribeToDeviceStateChange([&](DeviceState newState)
|
||||
try
|
||||
{
|
||||
SubscribeToDeviceStateChange([&](DeviceState newState)
|
||||
{
|
||||
lock_guard<mutex> lock{fEventsMutex};
|
||||
fEvents.push(newState);
|
||||
{
|
||||
lock_guard<mutex> lock{fEventsMutex};
|
||||
fEvents.push(newState);
|
||||
}
|
||||
fNewEvent.notify_one();
|
||||
});
|
||||
|
||||
RunStartupSequence();
|
||||
|
||||
{
|
||||
// Wait for next state, which is DeviceState::Ready,
|
||||
// or for device termination request
|
||||
unique_lock<mutex> lock{fEventsMutex};
|
||||
while (fEvents.empty() && !fDeviceTerminationRequested)
|
||||
{
|
||||
fNewEvent.wait(lock);
|
||||
}
|
||||
}
|
||||
fNewEvent.notify_one();
|
||||
});
|
||||
|
||||
ChangeDeviceState(DeviceStateTransition::InitDevice);
|
||||
while (WaitForNextState() != DeviceState::DeviceReady) {}
|
||||
|
||||
ChangeDeviceState(DeviceStateTransition::InitTask);
|
||||
while (WaitForNextState() != DeviceState::Ready) {}
|
||||
ChangeDeviceState(DeviceStateTransition::Run);
|
||||
while (WaitForNextState() != DeviceState::Ready) {}
|
||||
if (!DeviceTerminated())
|
||||
if (!fDeviceTerminationRequested)
|
||||
{
|
||||
RunShutdownSequence();
|
||||
}
|
||||
}
|
||||
catch (PluginServices::DeviceControlError& e)
|
||||
{
|
||||
ChangeDeviceState(DeviceStateTransition::ResetTask);
|
||||
while (WaitForNextState() != DeviceState::DeviceReady) {}
|
||||
ChangeDeviceState(DeviceStateTransition::ResetDevice);
|
||||
while (WaitForNextState() != DeviceState::Idle) {}
|
||||
ChangeDeviceState(DeviceStateTransition::End);
|
||||
while (WaitForNextState() != DeviceState::Exiting) {}
|
||||
// If we are here, it means another plugin has taken control. That's fine, just print the exception message and do nothing else.
|
||||
LOG(DEBUG) << e.what();
|
||||
}
|
||||
}
|
||||
|
||||
auto Control::SignalHandler(int signal) -> void
|
||||
{
|
||||
|
||||
if (!fDeviceTerminationRequested)
|
||||
{
|
||||
fDeviceTerminationRequested = true;
|
||||
|
||||
StealDeviceControl();
|
||||
|
||||
LOG(INFO) << "Received device shutdown request (signal " << signal << ").";
|
||||
LOG(INFO) << "Waiting for graceful device shutdown. Hit Ctrl-C again to abort immediately.";
|
||||
|
||||
fSignalHandlerThread = thread(&Control::RunShutdownSequence, this);
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG(WARN) << "Received 2nd device shutdown request (signal " << signal << ").";
|
||||
LOG(WARN) << "Aborting immediately !";
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
auto Control::RunShutdownSequence() -> void
|
||||
{
|
||||
auto nextState = GetCurrentDeviceState();
|
||||
EmptyEventQueue();
|
||||
while (nextState != DeviceState::Exiting)
|
||||
{
|
||||
switch (nextState)
|
||||
{
|
||||
case DeviceState::Idle:
|
||||
ChangeDeviceState(DeviceStateTransition::End);
|
||||
break;
|
||||
case DeviceState::DeviceReady:
|
||||
ChangeDeviceState(DeviceStateTransition::ResetDevice);
|
||||
break;
|
||||
case DeviceState::Ready:
|
||||
ChangeDeviceState(DeviceStateTransition::ResetTask);
|
||||
break;
|
||||
case DeviceState::Running:
|
||||
ChangeDeviceState(DeviceStateTransition::Stop);
|
||||
break;
|
||||
case DeviceState::Paused:
|
||||
ChangeDeviceState(DeviceStateTransition::Resume);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
nextState = WaitForNextState();
|
||||
}
|
||||
|
||||
UnsubscribeFromDeviceStateChange();
|
||||
ReleaseDeviceControl();
|
||||
}
|
||||
|
||||
auto Control::RunStartupSequence() -> void
|
||||
{
|
||||
ChangeDeviceState(DeviceStateTransition::InitDevice);
|
||||
while (WaitForNextState() != DeviceState::DeviceReady) {}
|
||||
ChangeDeviceState(DeviceStateTransition::InitTask);
|
||||
while (WaitForNextState() != DeviceState::Ready) {}
|
||||
ChangeDeviceState(DeviceStateTransition::Run);
|
||||
while (WaitForNextState() != DeviceState::Running) {}
|
||||
}
|
||||
|
||||
auto Control::EmptyEventQueue() -> void
|
||||
{
|
||||
lock_guard<mutex> lock{fEventsMutex};
|
||||
fEvents = queue<DeviceState>{};
|
||||
}
|
||||
|
||||
Control::~Control()
|
||||
{
|
||||
if (fControllerThread.joinable())
|
||||
{
|
||||
fControllerThread.join();
|
||||
}
|
||||
if (fControllerThread.joinable()) fControllerThread.join();
|
||||
if (fSignalHandlerThread.joinable()) fSignalHandlerThread.join();
|
||||
}
|
||||
|
||||
} /* namespace plugins */
|
||||
|
Reference in New Issue
Block a user