1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147
|
#pragma once
#include <signal.h>
#include <websocketpp/config/asio_no_tls_client.hpp>
#include <websocketpp/client.hpp>
#include <websocketpp/common/thread.hpp>
#include <websocketpp/common/functional.hpp>
#include <thread>
#include <set>
#include <glibmm/spawn.h>
typedef websocketpp::client<websocketpp::config::asio_client> WSClient;
typedef websocketpp::config::asio_client::message_type::ptr message_ptr;
typedef websocketpp::lib::lock_guard<websocketpp::lib::mutex> scoped_lock;
#include "DataCell.hh"
namespace cadabra {
class GUIBase;
class DocumentThread;
/// \ingroup clientserver
///
/// Base class which talks to the server and sends Action objects back to the
/// DocumentThread.
///
/// ComputeThread is the base class which takes care of doing actual
/// computations with the cells in a document. It handles talking to
/// the server backend. It knows how to pass cells to the server and
/// ask them to be executed. Results are reported back to the GUI by
/// putting ActionBase objects onto its todo stack. ComputeThread never
/// directly modifies the document tree.
class ComputeThread {
public:
/// If the ComputeThread is constructed with a null pointer to the
/// gui, there will be no gui updates, just DTree updates.
ComputeThread(int server_port=0, std::string token="", std::string ip_address="127.0.0.1");
ComputeThread(const ComputeThread& )=delete; // You cannot copy this object
~ComputeThread();
/// Determine the objects that this compute thread should be
/// talking to.
void set_master(GUIBase *, DocumentThread *);
/// Main entry point, which will connect to the server and
/// then start an event loop to handle communication with the
/// server. Only terminates when the connection drops, so run
/// your GUI on a different thread.
void run();
/// In order to execute code on the server, call the
/// following from the GUI thread. This method returns as
/// soon as the request has been put on the network queue. If
/// no communication with the server is necessary, this
/// returns immediately. The ComputeThread will report the
/// result of the computation/processing by adding actions to
/// the DocumentThread owned pending_actions stack, by
/// calling queue_action. It will never modify the cell
/// directly, and will also never modify any other cells in
/// the document tree.
void execute_cell(DTree::iterator);
void execute_interactive(uint64_t id, const std::string& code);
/// Stop the current cell execution on the server and remove
/// all other cells from the run queue as well.
void stop();
/// Restart the kernel.
void restart_kernel();
/// Request completion of a string. Returns `false` if the current
/// cell cannot be completed, in which case the TAB which led to
/// the request should be interpreted literally and used for spacing.
/// The `alternative` argument is the serial number of the requested
/// completion, in case there is more than one possible completion.
bool complete(DTree::iterator, int pos, int alternative);
// Determine if there are still cells running on the server.
// FIXME: this does not guarantee thread-safety but at the moment
// is only used for updating status bars etc.
// FIXME: can be moved to DocumentThread.
int number_of_cells_executing(void) const;
/// Terminate the compute thread, in preparation for shutting
/// down the client altogether.
void terminate();
private:
GUIBase *gui;
DocumentThread *docthread;
// For debugging purposes, we keep record of the gui thread id,
// so that we can flag when code runs on the wrong thread.
// Gets initialised in the ComputeThread constructor.
std::thread::id gui_thread_id;
// Keeping track of cells which are running on the server, in
// a form which allows us to look them up quickly based only
// on the id (which is all that the server knows about).
// FIXME: moving this away into documentthread, so that we only need to refer to id's.
std::map<DataCell::id_t, DTree::iterator> running_cells;
// WebSocket++ things.
WSClient wsclient;
bool connection_is_open, restarting_kernel;
WSClient::connection_ptr connection;
websocketpp::connection_hdl our_connection_hdl;
void init();
void try_connect();
void try_spawn_server();
void on_open(websocketpp::connection_hdl hdl);
void on_fail(websocketpp::connection_hdl hdl);
void on_close(websocketpp::connection_hdl hdl);
void on_message(websocketpp::connection_hdl hdl, message_ptr msg);
void cell_finished_running(DataCell::id_t);
/// Set all cells to be non-running (e.g. after a kernel failure) and
/// report the status of each cell to the GUI.
void all_cells_nonrunning();
std::set<uint64_t> interactive_cells;
// Self-started server
Glib::Pid server_pid;
int server_stdout, server_stderr;
unsigned short port;
std::string authentication_token;
int forced_server_port;
std::string forced_server_token;
std::string forced_server_ip_address;
};
}
|