aboutsummaryrefslogtreecommitdiff
path: root/contrib/epee/include/console_handler.h
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/epee/include/console_handler.h')
-rw-r--r--contrib/epee/include/console_handler.h389
1 files changed, 276 insertions, 113 deletions
diff --git a/contrib/epee/include/console_handler.h b/contrib/epee/include/console_handler.h
index 238abc80c..778679e75 100644
--- a/contrib/epee/include/console_handler.h
+++ b/contrib/epee/include/console_handler.h
@@ -1,6 +1,6 @@
// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net
// All rights reserved.
-//
+//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
// * Redistributions of source code must retain the above copyright
@@ -11,7 +11,7 @@
// * Neither the name of the Andrey N. Sabelnikov nor the
// names of its contributors may be used to endorse or promote products
// derived from this software without specific prior written permission.
-//
+//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
@@ -22,141 +22,301 @@
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-//
-
-
+//
#pragma once
+#include <atomic>
+#include <condition_variable>
+#include <functional>
+#include <mutex>
+#include <thread>
+
namespace epee
{
+ class async_stdin_reader
+ {
+ public:
+ async_stdin_reader()
+ : m_run(true)
+ , m_has_read_request(false)
+ , m_read_status(state_init)
+ {
+ m_reader_thread = std::thread(std::bind(&async_stdin_reader::reader_thread_func, this));
+ }
+ ~async_stdin_reader()
+ {
+ stop();
+ }
+ // Not thread safe. Only one thread can call this method at once.
+ bool get_line(std::string& line)
+ {
+ if (!start_read())
+ return false;
+ std::unique_lock<std::mutex> lock(m_response_mutex);
+ while (state_init == m_read_status)
+ {
+ m_response_cv.wait(lock);
+ }
- template<class t_server>
- bool empty_commands_handler(t_server* psrv, const std::string& command)
- {
- return true;
- }
-
+ bool res = false;
+ if (state_success == m_read_status)
+ {
+ line = m_line;
+ res = true;
+ }
- template<class t_server, class chain_handler>
- bool default_console_handler(t_server* psrv, chain_handler ch_handler, const std::string usage = "")
- {
- TRY_ENTRY();
- bool continue_handle = true;
- while(continue_handle)
+ m_read_status = state_init;
+
+ return res;
+ }
+
+ void stop()
+ {
+ if (m_run)
+ {
+ m_run.store(false, std::memory_order_relaxed);
+
+#if defined(WIN32)
+ ::CloseHandle(::GetStdHandle(STD_INPUT_HANDLE));
+#endif
+
+ m_request_cv.notify_one();
+ m_reader_thread.join();
+ }
+ }
+
+ private:
+ bool start_read()
+ {
+ std::unique_lock<std::mutex> lock(m_request_mutex);
+ if (!m_run.load(std::memory_order_relaxed) || m_has_read_request)
+ return false;
+
+ m_has_read_request = true;
+ m_request_cv.notify_one();
+ return true;
+ }
+
+ bool wait_read()
{
- char command_buff[400] = {0};
- std::string command;
- std::cin.getline(command_buff, 399);
- if(std::cin.eof() || std::cin.fail())
+ std::unique_lock<std::mutex> lock(m_request_mutex);
+ while (m_run.load(std::memory_order_relaxed) && !m_has_read_request)
{
- LOG_PRINT("std::cin.eof() or std::cin.fail(), stopping...", LOG_LEVEL_0);
- continue_handle = false;
- break;
+ m_request_cv.wait(lock);
}
- command = command_buff;
- if(!command.compare("exit") || !command.compare("q") )
+ if (m_has_read_request)
{
- psrv->send_stop_signal();
- continue_handle = false;
- }else if ( !command.compare(0, 7, "set_log"))
+ m_has_read_request = false;
+ return true;
+ }
+
+ return false;
+ }
+
+ bool wait_stdin_data()
+ {
+#if !defined(WIN32)
+ int stdin_fileno = ::fileno(stdin);
+
+ while (m_run.load(std::memory_order_relaxed))
{
- //parse set_log command
- if(command.size() != 9)
+ fd_set read_set;
+ FD_ZERO(&read_set);
+ FD_SET(stdin_fileno, &read_set);
+
+ struct timeval tv;
+ tv.tv_sec = 0;
+ tv.tv_usec = 100 * 1000;
+
+ int retval = ::select(stdin_fileno + 1, &read_set, NULL, NULL, &tv);
+ if (retval < 0)
+ return false;
+ else if (0 < retval)
+ return true;
+ }
+#endif
+
+ return true;
+ }
+
+ void reader_thread_func()
+ {
+ while (true)
+ {
+ if (!wait_read())
+ break;
+
+ std::string line;
+ bool read_ok = true;
+ if (wait_stdin_data())
{
- std::cout << "wrong syntax: " << command << std::endl << "use set_log n" << std::endl;
- continue;
+ if (m_run.load(std::memory_order_relaxed))
+ {
+ std::getline(std::cin, line);
+ read_ok = !std::cin.eof() && !std::cin.fail();
+ }
}
- int n = 0;
- if(!string_tools::get_xtype_from_string(n, command.substr(8, 1)))
+ else
{
- std::cout << "wrong syntax: " << command << std::endl << "use set_log n" << std::endl;
- continue;
+ read_ok = false;
+ }
+
+ {
+ std::unique_lock<std::mutex> lock(m_response_mutex);
+ if (m_run.load(std::memory_order_relaxed))
+ {
+ m_line = std::move(line);
+ m_read_status = read_ok ? state_success : state_error;
+ }
+ else
+ {
+ m_read_status = state_cancelled;
+ }
+ m_response_cv.notify_one();
}
- log_space::get_set_log_detalisation_level(true, n);
- LOG_PRINT_L0("New log level set " << n);
- }
- else if(ch_handler(psrv, command))
- continue;
- else
- {
- std::cout << "unknown command: " << command << std::endl;
- std::cout << usage;
}
}
+
+ enum t_state
+ {
+ state_init,
+ state_success,
+ state_error,
+ state_cancelled
+ };
+
+ private:
+ std::thread m_reader_thread;
+ std::atomic<bool> m_run;
+
+ std::string m_line;
+ bool m_has_read_request;
+ t_state m_read_status;
+
+ std::mutex m_request_mutex;
+ std::mutex m_response_mutex;
+ std::condition_variable m_request_cv;
+ std::condition_variable m_response_cv;
+ };
+
+
+ template<class t_server>
+ bool empty_commands_handler(t_server* psrv, const std::string& command)
+ {
return true;
- CATCH_ENTRY_L0("console_handler", false);
}
- template<class chain_handler>
- bool default_console_handler2(chain_handler ch_handler, const std::string usage)
+
+ class async_console_handler
{
- TRY_ENTRY();
- bool continue_handle = true;
- while(continue_handle)
+ public:
+ async_console_handler()
{
- char command_buff[400] = {0};
- std::string command;
- std::cin.getline(command_buff, 399);
- if(std::cin.eof() || std::cin.fail())
- {
+ }
- LOG_PRINT("std::cin.eof() or std::cin.fail(), stopping...", LOG_LEVEL_0);
- continue_handle = false;
- break;
- }
- command = command_buff;
+ template<class t_server, class chain_handler>
+ bool run(t_server* psrv, chain_handler ch_handler, const std::string& prompt = "#", const std::string& usage = "")
+ {
+ return run(prompt, usage, [&](const std::string& cmd) { return ch_handler(psrv, cmd); }, [&] { psrv->send_stop_signal(); });
+ }
- if(!command.compare("exit") || !command.compare("q") )
- {
- continue_handle = false;
- }else if ( !command.compare(0, 7, "set_log"))
+ template<class chain_handler>
+ bool run(chain_handler ch_handler, const std::string& prompt = "#", const std::string& usage = "")
+ {
+ return run(prompt, usage, [&](const std::string& cmd) { return ch_handler(cmd); }, [] { });
+ }
+
+ void stop()
+ {
+ m_stdin_reader.stop();
+ }
+
+ private:
+ template<typename t_cmd_handler, typename t_exit_handler>
+ bool run(const std::string& prompt, const std::string& usage, const t_cmd_handler& cmd_handler, const t_exit_handler& exit_handler)
+ {
+ TRY_ENTRY();
+ bool continue_handle = true;
+ while(continue_handle)
{
- //parse set_log command
- if(command.size() != 9)
+ if (!prompt.empty())
+ {
+ epee::log_space::set_console_color(epee::log_space::console_color_yellow, true);
+ std::cout << prompt;
+ if (' ' != prompt.back())
+ std::cout << ' ';
+ epee::log_space::reset_console_color();
+ std::cout.flush();
+ }
+
+ std::string command;
+ if(!m_stdin_reader.get_line(command))
+ {
+ LOG_PRINT("Failed to read line. Stopping...", LOG_LEVEL_0);
+ continue_handle = false;
+ break;
+ }
+
+ LOG_PRINT_L2("Read command: " << command);
+ if(0 == command.compare("exit") || 0 == command.compare("q"))
+ {
+ continue_handle = false;
+ }else if (!command.compare(0, 7, "set_log"))
+ {
+ //parse set_log command
+ if(command.size() != 9)
+ {
+ std::cout << "wrong syntax: " << command << std::endl << "use set_log n" << std::endl;
+ continue;
+ }
+ uint16_t n = 0;
+ if(!string_tools::get_xtype_from_string(n, command.substr(8, 1)))
+ {
+ std::cout << "wrong syntax: " << command << std::endl << "use set_log n" << std::endl;
+ continue;
+ }
+ log_space::get_set_log_detalisation_level(true, n);
+ LOG_PRINT_L0("New log level set " << n);
+ }else if (command.empty())
{
- std::cout << "wrong syntax: " << command << std::endl << "use set_log n" << std::endl;
continue;
}
- int n = 0;
- if(!string_tools::get_xtype_from_string(n, command.substr(8, 1)))
+ else if(cmd_handler(command))
{
- std::cout << "wrong syntax: " << command << std::endl << "use set_log n" << std::endl;
continue;
+ } else
+ {
+ std::cout << "unknown command: " << command << std::endl;
+ std::cout << usage;
}
- log_space::get_set_log_detalisation_level(true, n);
- LOG_PRINT_L0("New log level set " << n);
- }
- else if(ch_handler(command))
- continue;
- else
- {
- std::cout << "unknown command: " << command << std::endl;
- std::cout << usage;
}
+ exit_handler();
+ return true;
+ CATCH_ENTRY_L0("console_handler", false);
}
- return true;
- CATCH_ENTRY_L0("console_handler", false);
- }
-
+ private:
+ async_stdin_reader m_stdin_reader;
+ };
template<class t_server, class t_handler>
- bool start_default_console(t_server* ptsrv, t_handler handlr, const std::string& usage = "")
+ bool start_default_console(t_server* ptsrv, t_handler handlr, const std::string& prompt, const std::string& usage = "")
{
- boost::thread( boost::bind(default_console_handler<t_server, t_handler>, ptsrv, handlr, usage) );
+ std::shared_ptr<async_console_handler> console_handler = std::make_shared<async_console_handler>();
+ boost::thread(boost::bind(&async_console_handler::run<t_server, t_handler>, console_handler, ptsrv, handlr, prompt, usage)).detach();
return true;
}
template<class t_server>
- bool start_default_console(t_server* ptsrv, const std::string& usage = "")
+ bool start_default_console(t_server* ptsrv, const std::string& prompt, const std::string& usage = "")
{
- return start_default_console(ptsrv, empty_commands_handler<t_server>, usage);
+ return start_default_console(ptsrv, empty_commands_handler<t_server>, prompt, usage);
}
template<class t_server, class t_handler>
@@ -166,15 +326,16 @@ namespace epee
}
template<class t_server, class t_handler>
- bool run_default_console_handler_no_srv_param(t_server* ptsrv, t_handler handlr, const std::string& usage = "")
+ bool run_default_console_handler_no_srv_param(t_server* ptsrv, t_handler handlr, const std::string& prompt, const std::string& usage = "")
{
- return default_console_handler(ptsrv, boost::bind<bool>(no_srv_param_adapter<t_server, t_handler>, _1, _2, handlr), usage);
+ async_console_handler console_handler;
+ return console_handler.run(ptsrv, boost::bind<bool>(no_srv_param_adapter<t_server, t_handler>, _1, _2, handlr), prompt, usage);
}
template<class t_server, class t_handler>
- bool start_default_console_handler_no_srv_param(t_server* ptsrv, t_handler handlr, const std::string& usage = "")
+ bool start_default_console_handler_no_srv_param(t_server* ptsrv, t_handler handlr, const std::string& prompt, const std::string& usage = "")
{
- boost::thread( boost::bind(run_default_console_handler_no_srv_param<t_server, t_handler>, ptsrv, handlr, usage) );
+ boost::thread( boost::bind(run_default_console_handler_no_srv_param<t_server, t_handler>, ptsrv, handlr, prompt, usage) );
return true;
}
@@ -209,7 +370,8 @@ namespace epee
typedef std::map<std::string, std::pair<console_command_handler, std::string> > command_handlers_map;
std::unique_ptr<boost::thread> m_console_thread;
command_handlers_map m_command_handlers;
- public:
+ async_console_handler m_console_handler;
+ public:
std::string get_usage()
{
std::stringstream ss;
@@ -217,7 +379,7 @@ namespace epee
for(auto& x:m_command_handlers)
if(x.first.size() > max_command_len)
max_command_len = x.first.size();
-
+
for(auto& x:m_command_handlers)
{
ss.width(max_command_len + 3);
@@ -255,24 +417,22 @@ namespace epee
start_default_console_handler_no_srv_param(&srv, boost::bind(&console_handlers_binder::process_command_str, this, _1));
return true;
}*/
-
- bool start_handling(const std::string& usage_string = "")
+
+ bool start_handling(const std::string& prompt, const std::string& usage_string = "")
{
- m_console_thread.reset(new boost::thread(boost::bind(&console_handlers_binder::run_handling, this, usage_string) ));
+ m_console_thread.reset(new boost::thread(boost::bind(&console_handlers_binder::run_handling, this, prompt, usage_string)));
+ m_console_thread->detach();
return true;
}
- bool stop_handling()
+ void stop_handling()
{
- if(m_console_thread.get())
- m_console_thread->interrupt();
- return true;
+ m_console_handler.stop();
}
-
- bool run_handling(const std::string usage_string)
+ bool run_handling(const std::string& prompt, const std::string& usage_string)
{
- return default_console_handler2(boost::bind(&console_handlers_binder::process_command_str, this, _1), usage_string);
+ return m_console_handler.run(boost::bind(&console_handlers_binder::process_command_str, this, _1), prompt, usage_string);
}
/*template<class t_srv>
@@ -280,7 +440,6 @@ namespace epee
{
return run_default_console_handler_no_srv_param(&srv, boost::bind<bool>(&console_handlers_binder::process_command_str, this, _1), usage_string);
}*/
-
};
/* work around because of broken boost bind */
@@ -292,19 +451,23 @@ namespace epee
return console_handlers_binder::process_command_str(cmd);
}
public:
- bool start_handling(t_server* psrv, const std::string& usage_string = "")
+ bool start_handling(t_server* psrv, const std::string& prompt, const std::string& usage_string = "")
{
- boost::thread(boost::bind(&srv_console_handlers_binder<t_server>::run_handling, this, psrv, usage_string) );
+ boost::thread(boost::bind(&srv_console_handlers_binder<t_server>::run_handling, this, psrv, prompt, usage_string)).detach();
return true;
}
- bool run_handling(t_server* psrv, const std::string usage_string)
+ bool run_handling(t_server* psrv, const std::string& prompt, const std::string& usage_string)
{
- return default_console_handler(psrv, boost::bind(&srv_console_handlers_binder<t_server>::process_command_str, this, _1, _2), usage_string);
+ return m_console_handler.run(psrv, boost::bind(&srv_console_handlers_binder<t_server>::process_command_str, this, _1, _2), prompt, usage_string);
}
- };
+ void stop_handling()
+ {
+ m_console_handler.stop();
+ }
+ private:
+ async_console_handler m_console_handler;
+ };
}
-
-