Browse Source

switch readline over to alternative interface for proper multithreading of it

Missingmew 4 years ago
parent
commit
391d5e014a
3 changed files with 73 additions and 42 deletions
  1. 15 7
      cli/include/ioman.h
  2. 53 35
      cli/src/ioman.cpp
  3. 5 0
      cli/src/main.cpp

+ 15 - 7
cli/include/ioman.h

@@ -14,6 +14,16 @@
 using boost::asio::ip::tcp;
 
 class IoMan {
+/* this is technically private and protected stuff which needs to be public for the readline callback */
+public:
+  std::mutex localmutex;
+  enum OutMsgType { normal, error, debug };
+  virtual void printMessage(std::string msg, OutMsgType type);
+  bool runmain;
+  std::vector<std::string> localinput;
+  std::condition_variable localcv;
+  std::mutex mainmutex;
+
 private:
   boost::asio::io_service ios;
   boost::asio::streambuf sendbuf;
@@ -30,13 +40,12 @@ private:
   3 threads: handleinput, handlenetwork, handleresponse
   */
   std::thread tinput, tnetwork, tresponse;
-  std::mutex mainmutex, inputmutex, networkmutex, responsemutex;
-  bool runmain, runinput, runnetwork, runresponse;
+  std::mutex inputmutex, networkmutex, responsemutex;
+  bool runinput, runnetwork, runresponse;
 
   std::vector<Json::Value> netinput;
-  std::vector<std::string> localinput;
-  std::mutex netmutex, localmutex;
-  std::condition_variable netcv, localcv;
+  std::mutex netmutex;
+  std::condition_variable netcv;
 
   /*
   to be put elsewhere
@@ -59,9 +68,8 @@ private:
   bool startlist;
 
 protected:
-  enum OutMsgType { normal, error, debug };
   std::mutex msgmutex;
-  virtual void printMessage(std::string msg, OutMsgType type);
+  
   virtual void printWelcomeMessage() = 0;
   virtual std::string getCmdPrompt() = 0;
   virtual std::string getUserPrompt() = 0;

+ 53 - 35
cli/src/ioman.cpp

@@ -11,12 +11,17 @@
 #include <readline/history.h>
 #include <readline/readline.h>
 
+#include <poll.h>
+
 using std::string;
 using std::vector;
 
 using boost::asio::buffer;
 using boost::asio::ip::tcp;
 
+/* this will be provided by main.cpp for the readline callback */
+extern IoMan *gIOMAN;
+
 IoMan::IoMan(char *ipcstring) : cmdman(fileman) {
   ipstring = std::string(ipcstring);
   port = 1234;
@@ -140,6 +145,7 @@ bool IoMan::init() {
   return true;
 }
 
+/* loop to fetch data from the network, doing light preprocessing on it to be handled by responseMain */
 void IoMan::networkMain() {
   vector<Json::Value> toput;
   char *recvjson;
@@ -226,6 +232,7 @@ void IoMan::networkMain() {
   }
 }
 
+/* loop to handle input from the user and responseMain, sending data via network if required */
 void IoMan::inputMain() {
   vector<string> toprocess;
   string command;
@@ -359,6 +366,7 @@ void IoMan::inputMain() {
   }
 }
 
+/* loop to handle responses that have been fetched by netMain and possibly add new commands to be handled by inputMain */
 void IoMan::responseMain() {
   vector<Json::Value> toprocess;
   vector<string> toput;
@@ -469,14 +477,49 @@ void IoMan::responseMain() {
   }
 }
 
+/* this is the handler that readlines alternative interface will use to process user input */
+void ioman_readlineHandler(char* line) {
+  vector<string> tokens;
+  if(!line){
+    printf("\nNULLBURGER\n");
+    gIOMAN->mainmutex.lock();
+    gIOMAN->runmain = 0;
+    gIOMAN->mainmutex.unlock();
+  }
+  else {
+    // split input line into tokens
+    boost::algorithm::split(tokens, std::string(line),
+                            boost::algorithm::is_any_of(" "),
+                            boost::algorithm::token_compress_on);
+    if (strlen(line) && tokens.size()) {
+      add_history(line);
+
+    gIOMAN->localmutex.lock();
+    gIOMAN->printMessage(string(__PRETTY_FUNCTION__) + string(" get localmutex"),
+                 gIOMAN->debug);
+    gIOMAN->localinput.push_back(line);
+    gIOMAN->localmutex.unlock();
+    gIOMAN->printMessage(string(__PRETTY_FUNCTION__) + string(" release localmutex"),
+                 gIOMAN->debug);
+    gIOMAN->localcv.notify_all();
+    }
+    free(line);
+  }
+}
+
+/* main user input loop */
 void IoMan::run() {
   printMessage("IoMan::run() begin", debug);
-  char *line = NULL, *user = NULL, *pass = NULL;
+  //~ char *line = NULL, *user = NULL, *pass = NULL;
+  char *user = NULL, *pass = NULL;
   vector<string> tokens;
   string work, command;
   CmdMan::CmdRet cmdret;
   Json::Value root;
   std::unique_lock<std::mutex> ulock;
+  struct pollfd inPipeStatus;
+  inPipeStatus.fd = STDIN_FILENO;
+  inPipeStatus.events = POLLIN;
 
   runmain = true;
 
@@ -509,50 +552,25 @@ void IoMan::run() {
     return;
   initmutex.unlock();
   initcv.notify_all();
+  
+  rl_callback_handler_install(getCmdPrompt().c_str(), (rl_vcpfunc_t*) &ioman_readlineHandler);
 
   mainmutex.lock();
   while (runmain) {
     mainmutex.unlock();
-    free(line);
-
-    line = readline(getCmdPrompt().c_str());
-
-    if (!runmain)
-      break;
+    
+    poll(&inPipeStatus, 1, 100);
 
-    // if no input, do not add to history, and go to reading next line
-    if (strlen(line) == 0) {
-      continue;
-    }
-
-    // split input line into tokens
-    boost::algorithm::split(tokens, std::string(line),
-                            boost::algorithm::is_any_of(" "),
-                            boost::algorithm::token_compress_on);
-
-    // if input contains only spaces, do not add to history, and go to reading
-    // next line
-    if (tokens.size() < 1) {
-      continue;
+    if (inPipeStatus.revents & POLLIN) {
+      rl_callback_read_char();
     }
-
-    // add the line to history
-    add_history(line);
-
-    localmutex.lock();
-    printMessage(string(__PRETTY_FUNCTION__) + string(" get localmutex"),
-                 debug);
-    localinput.push_back(line);
-    localmutex.unlock();
-    printMessage(string(__PRETTY_FUNCTION__) + string(" release localmutex"),
-                 debug);
-    localcv.notify_all();
-
     if (!connected)
       break;
     mainmutex.lock();
   }
   mainmutex.unlock();
+  
+ // Remove the handler
+ rl_callback_handler_remove();
 
-  free(line);
 }

+ 5 - 0
cli/src/main.cpp

@@ -7,6 +7,9 @@
 
 namespace bpo = boost::program_options;
 
+/* this is provided for the readline callback in IoMan */
+IoMan *gIOMAN;
+
 void show_help(char *exec) {
   std::printf("ccats command line interface\n");
   std::printf("usage: %s IP [Options]\n", exec);
@@ -68,8 +71,10 @@ int main(int argc, char **argv) {
     ioman = new UserIoMan(argv[1]);
   }
   if (ioman->init()) {
+    gIOMAN = ioman;
     ioman->run();
   }
   delete ioman;
+  gIOMAN = NULL;
   std::printf("done\n");
 }