For many console applications it is handy to be able to receive input and output at the same time from multiple threads. This is supposed to allow receiving input commands from multiple threads, while simultaneously outputting.
Console.h
#pragma once
#include <iostream>
#include <string>
#include <queue>
#include <mutex>
#include <atomic>
#include <conio.h>
#include <boost/thread.hpp>
namespace kaarez {
namespace util {
class Console {
public:
static void initialize();
static void terminate();
static void write(std::string text);
static bool read(std::string& text);
private:
static void input_loop();
static void write_loop();
static std::string input_buffer; //Current input buffer
static std::string input_field; //Printed at the bottom of the console
static std::queue<std::string> writeQueue;
static std::queue<std::string> readQueue;
static std::condition_variable condition_variable;
static std::mutex write_mutex;
static std::mutex read_mutex;
static boost::thread* write_thread;
static boost::thread* input_thread;
static std::atomic<bool> stop_loops;
static std::atomic<bool> initialized;
};
boost::thread* Console::input_thread;
boost::thread* Console::write_thread;
std::string Console::input_buffer;
std::string Console::input_field;
std::queue<std::string> Console::writeQueue;
std::condition_variable Console::condition_variable;
std::mutex Console::write_mutex;
std::mutex Console::read_mutex;
std::queue<std::string> Console::readQueue;
std::atomic<bool> Console::stop_loops = false;
std::atomic<bool> Console::initialized = false;
void Console::input_loop() {
while (!stop_loops) {
uint32_t c = getch();
if (c == 8) { //Backspace
if (!input_buffer.empty()) {
input_buffer.pop_back();
input_field = input_buffer;
std::unique_lock<std::mutex> l(write_mutex);
condition_variable.notify_one();
}
}
else if (c == 13) { //Carriage return
if (!input_buffer.empty()) {
std::unique_lock<std::mutex> l(read_mutex);
readQueue.push(input_buffer);
write(input_buffer);
input_buffer.clear();
input_field = input_buffer;
}
}
else if (c >= 32) { //Accepted input characters
input_buffer.push_back(c);
input_field = input_buffer;
std::unique_lock<std::mutex> l(write_mutex);
condition_variable.notify_one();
}
}
}
void Console::write_loop() {
while (!stop_loops) {
std::unique_lock<std::mutex> l(write_mutex);
condition_variable.wait(l);
if (!writeQueue.empty()) { //Update input field and add a new line
std::string text = writeQueue.front();
writeQueue.pop();
std::cout << "\r" << text << " " << std::endl;
std::cout << "> " << input_field;
}
else { //Update input field
std::cout << "\r> " << input_field << " ";
}
}
}
void Console::write(std::string text) {
std::unique_lock<std::mutex> l(write_mutex);
writeQueue.push(text);
condition_variable.notify_one();
}
bool Console::read(std::string& text) {
std::unique_lock<std::mutex> l(read_mutex);
if (!readQueue.empty()) {
text = readQueue.front();
readQueue.pop();
return true;
}
return false;
}
void Console::initialize() {
if (!initialized) {
input_thread = new boost::thread(&input_loop);
write_thread = new boost::thread(&write_loop);
initialized = true;
}
}
void Console::terminate() {
stop_loops = true;
condition_variable.notify_one();
input_thread->interrupt();
input_thread->join();
write_thread->join();
}
}
}
Is there any flaws I have not noticed or some way I can improve performance? It's important that there's as little wait time when calling write().
Also, you are free to use the code for your own project if you want.