/*
* GenericShell.h
*
* Template for a generic shell
* Copyright (c) 2008, 2013, 2014 Linas Vepstas <linas@linas.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License v3 as
* published by the Free Software Foundation and including the exceptions
* at http://opencog.org/wiki/Licenses
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program; if not, write to:
* Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef _OPENCOG_GENERIC_SHELL_H
#define _OPENCOG_GENERIC_SHELL_H
#include <condition_variable>
#include <mutex>
#include <string>
#include <thread>
#include <opencog/util/concurrent_queue.h>
/**
* The GenericShell class implements an "escape" from the default cogserver
* command processor. It is useful when a module has a large number of
* commands, or a peculiar and complex syntax that must be handled. It
* works by by-passing all of the command-processing apparatus in the
* cogserver, and instead passing all socket I/O to the "eval" method
* in this class. The eval method is then free to parse the input in
* any way desired.
*
* If instead a module has only a small number of simple commands that
* need to be implemented, then the DECLARE_CMD_REQUEST function, defined
* in Request.h, provides a simpler and easier way implementing commands.
*/
namespace opencog {
/** \addtogroup grp_server
* @{
*/
class ConsoleSocket;
class GenericEval;
class GenericShell
{
private:
std::mutex _pending_mtx;
std::string _pending_output;
ConsoleSocket* socket;
std::thread* evalthr;
std::thread* pollthr;
concurrent_queue<std::string> evalque;
volatile bool _init_done;
void enqueue_work(const std::string&);
protected:
std::string abort_prompt;
std::string normal_prompt;
std::string pending_prompt;
bool show_output;
bool show_prompt;
volatile bool self_destruct;
bool apply_discipline;
virtual void thread_init(void);
virtual void line_discipline(const std::string &expr);
// Concurrency handling
std::condition_variable _poll_cv;
std::mutex _poll_mtx;
void wake_poll();
void eval_loop();
void poll_loop();
void poll_and_send();
std::condition_variable _eval_cv;
std::mutex _eval_mtx;
bool _eval_done;
GenericEval* _evaluator;
void start_eval();
void finish_eval();
void while_not_done();
virtual void user_interrupt();
// Output handling.
virtual void put_output(const std::string&);
virtual std::string get_output();
virtual std::string poll_output();
public:
GenericShell(void);
virtual ~GenericShell();
virtual void set_socket(ConsoleSocket *);
virtual void eval(const std::string &);
virtual const std::string& get_prompt(void);
virtual void hush_output(bool);
virtual void hush_prompt(bool);
virtual void discipline(bool);
virtual GenericEval* get_evaluator(void) = 0;
// Monitor statistics
const char* _name;
bool eval_done() const { return _eval_done; }
size_t pending() const { return _pending_output.size(); }
size_t queued() const { return evalque.size(); }
// Return true if the current thread is this shell's eval thread
bool is_eval_thread() const
{ return evalthr and evalthr->get_id() == std::this_thread::get_id(); }
};
/** @}*/
}
#endif // _OPENCOG_GENERIC_SHELL_H