Simplify readline support

And don't use std::mutex
This commit is contained in:
Howard Chu 2017-08-21 12:55:06 +01:00
parent 4466b6d1b0
commit c088d38a57
No known key found for this signature in database
GPG key ID: FD2A70B44AB11BA7
3 changed files with 88 additions and 143 deletions

View file

@ -52,11 +52,10 @@ namespace epee
, m_has_read_request(false) , m_has_read_request(false)
, m_read_status(state_init) , m_read_status(state_init)
{ {
m_reader_thread = boost::thread(std::bind(&async_stdin_reader::reader_thread_func, this));
#ifdef HAVE_READLINE #ifdef HAVE_READLINE
m_readline_buffer.start(); m_readline_buffer.start();
m_readline_thread = boost::thread(std::bind(&async_stdin_reader::readline_thread_func, this));
#endif #endif
m_reader_thread = boost::thread(std::bind(&async_stdin_reader::reader_thread_func, this));
} }
~async_stdin_reader() ~async_stdin_reader()
@ -115,7 +114,6 @@ namespace epee
m_reader_thread.join(); m_reader_thread.join();
#ifdef HAVE_READLINE #ifdef HAVE_READLINE
m_readline_buffer.stop(); m_readline_buffer.stop();
m_readline_thread.join();
#endif #endif
} }
} }
@ -193,16 +191,6 @@ namespace epee
return true; return true;
} }
#ifdef HAVE_READLINE
void readline_thread_func()
{
while (m_run.load(std::memory_order_relaxed))
{
m_readline_buffer.process();
}
}
#endif
void reader_thread_func() void reader_thread_func()
{ {
while (true) while (true)
@ -212,12 +200,20 @@ namespace epee
std::string line; std::string line;
bool read_ok = true; bool read_ok = true;
#ifdef HAVE_READLINE
reread:
#endif
if (wait_stdin_data()) if (wait_stdin_data())
{ {
if (m_run.load(std::memory_order_relaxed)) if (m_run.load(std::memory_order_relaxed))
{ {
#ifdef HAVE_READLINE #ifdef HAVE_READLINE
m_readline_buffer.get_line(line); switch (m_readline_buffer.get_line(line))
{
case rdln::empty: goto eof;
case rdln::partial: goto reread;
case rdln::full: break;
}
#else #else
std::getline(std::cin, line); std::getline(std::cin, line);
#endif #endif
@ -229,6 +225,9 @@ namespace epee
read_ok = false; read_ok = false;
} }
if (std::cin.eof()) { if (std::cin.eof()) {
#ifdef HAVE_READLINE
eof:
#endif
m_read_status = state_eos; m_read_status = state_eos;
m_response_cv.notify_one(); m_response_cv.notify_one();
break; break;
@ -263,7 +262,6 @@ namespace epee
boost::thread m_reader_thread; boost::thread m_reader_thread;
std::atomic<bool> m_run; std::atomic<bool> m_run;
#ifdef HAVE_READLINE #ifdef HAVE_READLINE
boost::thread m_readline_thread;
rdln::readline_buffer m_readline_buffer; rdln::readline_buffer m_readline_buffer;
#endif #endif

View file

@ -8,25 +8,25 @@
namespace rdln namespace rdln
{ {
typedef enum { empty, partial, full } linestatus;
class readline_buffer : public std::stringbuf class readline_buffer : public std::stringbuf
{ {
public: public:
readline_buffer(); readline_buffer();
void start(); void start();
void stop(); void stop();
int process();
bool is_running() const bool is_running() const
{ {
return m_cout_buf != NULL; return m_cout_buf != NULL;
} }
void get_line(std::string& line) const; linestatus get_line(std::string& line) const;
void set_prompt(const std::string& prompt); void set_prompt(const std::string& prompt);
static void add_completion(const std::string& command); static void add_completion(const std::string& command);
static const std::vector<std::string>& get_completions(); static const std::vector<std::string>& get_completions();
protected: protected:
virtual int sync(); virtual int sync();
private: private:
std::streambuf* m_cout_buf; std::streambuf* m_cout_buf;
static std::vector<std::string>& completion_commands(); static std::vector<std::string>& completion_commands();

View file

@ -3,19 +3,15 @@
#include <readline/history.h> #include <readline/history.h>
#include <sys/select.h> #include <sys/select.h>
#include <unistd.h> #include <unistd.h>
#include <mutex>
#include <condition_variable>
#include <boost/thread.hpp> #include <boost/thread.hpp>
#include <boost/algorithm/string.hpp> #include <boost/algorithm/string.hpp>
static int process_input();
static void install_line_handler(); static void install_line_handler();
static void remove_line_handler(); static void remove_line_handler();
static std::string last_line; static boost::mutex sync_mutex;
static std::string last_prompt; static rdln::linestatus line_stat;
static std::mutex line_mutex, sync_mutex, process_mutex; static char *the_line;
static std::condition_variable have_line;
namespace namespace
{ {
@ -55,7 +51,6 @@ rdln::readline_buffer::readline_buffer()
void rdln::readline_buffer::start() void rdln::readline_buffer::start()
{ {
std::unique_lock<std::mutex> lock(process_mutex);
if(m_cout_buf != NULL) if(m_cout_buf != NULL)
return; return;
m_cout_buf = std::cout.rdbuf(); m_cout_buf = std::cout.rdbuf();
@ -65,9 +60,6 @@ void rdln::readline_buffer::start()
void rdln::readline_buffer::stop() void rdln::readline_buffer::stop()
{ {
std::unique_lock<std::mutex> lock_process(process_mutex);
std::unique_lock<std::mutex> lock_sync(sync_mutex);
have_line.notify_all();
if(m_cout_buf == NULL) if(m_cout_buf == NULL)
return; return;
std::cout.rdbuf(m_cout_buf); std::cout.rdbuf(m_cout_buf);
@ -75,20 +67,26 @@ void rdln::readline_buffer::stop()
remove_line_handler(); remove_line_handler();
} }
void rdln::readline_buffer::get_line(std::string& line) const rdln::linestatus rdln::readline_buffer::get_line(std::string& line) const
{ {
std::unique_lock<std::mutex> lock(line_mutex); boost::lock_guard<boost::mutex> lock(sync_mutex);
have_line.wait(lock); line_stat = rdln::partial;
line = last_line; rl_callback_read_char();
if (line_stat == rdln::full)
{
line = the_line;
free(the_line);
the_line = NULL;
}
return line_stat;
} }
void rdln::readline_buffer::set_prompt(const std::string& prompt) void rdln::readline_buffer::set_prompt(const std::string& prompt)
{ {
last_prompt = prompt;
if(m_cout_buf == NULL) if(m_cout_buf == NULL)
return; return;
std::lock_guard<std::mutex> lock(sync_mutex); boost::lock_guard<boost::mutex> lock(sync_mutex);
rl_set_prompt(last_prompt.c_str()); rl_set_prompt(prompt.c_str());
rl_redisplay(); rl_redisplay();
} }
@ -104,128 +102,80 @@ const std::vector<std::string>& rdln::readline_buffer::get_completions()
return completion_commands(); return completion_commands();
} }
int rdln::readline_buffer::process()
{
process_mutex.lock();
if(m_cout_buf == NULL)
{
process_mutex.unlock();
boost::this_thread::sleep_for(boost::chrono::milliseconds( 1 ));
return 0;
}
int count = process_input();
process_mutex.unlock();
boost::this_thread::sleep_for(boost::chrono::milliseconds( 1 ));
return count;
}
int rdln::readline_buffer::sync() int rdln::readline_buffer::sync()
{ {
std::lock_guard<std::mutex> lock(sync_mutex); boost::lock_guard<boost::mutex> lock(sync_mutex);
char* saved_line; #if RL_READLINE_VERSION < 0x0700
int saved_point; char lbuf[2] = {0,0};
char *line = NULL;
saved_point = rl_point; int end = 0, point = 0;
saved_line = rl_copy_text(0, rl_end); #endif
rl_set_prompt(""); if (rl_end || *rl_prompt)
rl_replace_line("", 0); {
rl_redisplay(); #if RL_READLINE_VERSION >= 0x0700
rl_clear_visible_line();
#else
line = rl_line_buffer;
end = rl_end;
point = rl_point;
rl_line_buffer = lbuf;
rl_end = 0;
rl_point = 0;
rl_save_prompt();
rl_redisplay();
#endif
}
do do
{ {
m_cout_buf->sputc( this->sgetc() ); m_cout_buf->sputc( this->sgetc() );
} }
while ( this->snextc() != EOF ); while ( this->snextc() != EOF );
rl_set_prompt(last_prompt.c_str());
rl_replace_line(saved_line, 0);
rl_point = saved_point;
rl_redisplay();
free(saved_line);
return 0;
}
static int process_input() #if RL_READLINE_VERSION < 0x0700
{ if (end || *rl_prompt)
int count;
struct timeval t;
fd_set fds;
t.tv_sec = 0;
t.tv_usec = 1000;
FD_ZERO(&fds);
FD_SET(STDIN_FILENO, &fds);
count = select(STDIN_FILENO + 1, &fds, NULL, NULL, &t);
if (count < 1)
{ {
return count; rl_restore_prompt();
rl_line_buffer = line;
rl_end = end;
rl_point = point;
} }
rl_callback_read_char(); #endif
return count; rl_on_new_line();
rl_redisplay();
return 0;
} }
static void handle_line(char* line) static void handle_line(char* line)
{ {
free(line); bool exit = false;
if (line)
{
line_stat = rdln::full;
the_line = line;
std::string test_line = line;
boost::trim_right(test_line);
if(!test_line.empty())
{
add_history(test_line.c_str());
history_set_pos(history_length);
if (test_line == "exit" || test_line == "q")
exit = true;
}
} else
/* EOF */
{
line_stat = rdln::empty;
exit = true;
}
rl_done = 1; rl_done = 1;
if (exit)
rl_set_prompt("");
return; return;
} }
static int handle_enter(int x, int y)
{
std::lock_guard<std::mutex> lock(sync_mutex);
char* line = NULL;
line = rl_copy_text(0, rl_end);
std::string test_line = line;
free(line);
boost::trim_right(test_line);
rl_crlf();
rl_on_new_line();
if(test_line.empty())
{
last_line = "";
rl_set_prompt(last_prompt.c_str());
rl_replace_line("", 1);
rl_redisplay();
have_line.notify_one();
return 0;
}
rl_set_prompt("");
rl_replace_line("", 1);
rl_redisplay();
if (!test_line.empty())
{
last_line = test_line;
add_history(test_line.c_str());
history_set_pos(history_length);
}
if(last_line != "exit" && last_line != "q")
{
rl_set_prompt(last_prompt.c_str());
rl_replace_line("", 1);
rl_redisplay();
}
have_line.notify_one();
return 0;
}
static int startup_hook()
{
rl_bind_key(RETURN, handle_enter);
rl_bind_key(NEWLINE, handle_enter);
return 0;
}
static char* completion_matches(const char* text, int state) static char* completion_matches(const char* text, int state)
{ {
static size_t list_index; static size_t list_index;
@ -258,7 +208,6 @@ static char** attempted_completion(const char* text, int start, int end)
static void install_line_handler() static void install_line_handler()
{ {
rl_startup_hook = startup_hook;
rl_attempted_completion_function = attempted_completion; rl_attempted_completion_function = attempted_completion;
rl_callback_handler_install("", handle_line); rl_callback_handler_install("", handle_line);
stifle_history(500); stifle_history(500);
@ -269,8 +218,6 @@ static void remove_line_handler()
rl_replace_line("", 0); rl_replace_line("", 0);
rl_set_prompt(""); rl_set_prompt("");
rl_redisplay(); rl_redisplay();
rl_unbind_key(RETURN);
rl_unbind_key(NEWLINE);
rl_callback_handler_remove(); rl_callback_handler_remove();
} }