Skip to content
Snippets Groups Projects
Commit 76e60165 authored by Christophe Geuzaine's avatar Christophe Geuzaine
Browse files

made client startup non blocking + prepare SINFO refactoring
parent 5772f317
No related branches found
No related tags found
No related merge requests found
......@@ -17,6 +17,7 @@ int GmshDaemon(std::string socket)
return 1;
}
client.Start();
client.Info("Server sucessfully started. Listening...");
// read large data file,
// initialize mpi job,
......
......@@ -125,17 +125,18 @@ class GmshSocket{
// seconds and microseconds == 0 we check for available data and
// return immediately, i.e., we do polling). Returns 0 when data is
// available.
int Select(int socket, int seconds, int microseconds)
int Select(int seconds, int microseconds, int socket=-1)
{
int s = (socket < 0) ? _sock : socket;
struct timeval tv;
tv.tv_sec = seconds;
tv.tv_usec = microseconds;
fd_set rfds;
FD_ZERO(&rfds);
FD_SET(socket, &rfds);
FD_SET(s, &rfds);
// select checks all IO descriptors between 0 and its first arg,
// minus 1... hence the +1 below
return select(socket + 1, &rfds, NULL, NULL, &tv);
return select(s + 1, &rfds, NULL, NULL, &tv);
}
void SendString(int type, const char *str)
{
......@@ -146,6 +147,19 @@ class GmshSocket{
// send body
_SendData(str, len);
}
void Info(const char *str){ SendString(INFO, str); }
void Warning(const char *str){ SendString(WARNING, str); }
void Error(const char *str){ SendString(ERROR, str); }
void Progress(const char *str){ SendString(PROGRESS, str); }
void MergeFile(const char *str){ SendString(MERGE_FILE, str); }
void ParseString(const char *str){ SendString(PARSE_STRING, str); }
void SpeedTest(const char *str){ SendString(SPEED_TEST, str); }
void Option(int num, const char *str)
{
if(num < 1) num = 1;
if(num > 5) num = 5;
SendString(OPTION_1 + num - 1, str);
}
int ReceiveHeader(int *type, int *len)
{
bool swap = false;
......@@ -201,8 +215,7 @@ class GmshClient : public GmshSocket {
#if !defined(WIN32) || defined(__CYGWIN__)
// UNIX socket (testing ":" is not enough with Windows paths)
_sock = socket(PF_UNIX, SOCK_STREAM, 0);
if(_sock < 0)
return -1; // Error: Couldn't create socket
if(_sock < 0) return -1;
// try to connect socket to given name
struct sockaddr_un addr_un;
memset((char *) &addr_un, 0, sizeof(addr_un));
......@@ -220,8 +233,7 @@ class GmshClient : public GmshSocket {
else{
// TCP/IP socket
_sock = socket(AF_INET, SOCK_STREAM, 0);
if(_sock < 0)
return -1; // Error: Couldn't create socket
if(_sock < 0) return -1;
// try to connect socket to host:port
const char *port = strstr(sockname, ":");
int portno = atoi(port + 1);
......@@ -233,7 +245,7 @@ class GmshClient : public GmshSocket {
struct hostent *server;
if(!(server = gethostbyname(remote))){
CloseSocket(_sock);
return -3; // Error: No such host
return -3; // no such host
}
struct sockaddr_in addr_in;
memset((char *) &addr_in, 0, sizeof(addr_in));
......@@ -247,11 +259,7 @@ class GmshClient : public GmshSocket {
}
}
CloseSocket(_sock);
return -2; // Error: Couldn't connect
}
int Select(int seconds, int microseconds)
{
return GmshSocket::Select(_sock, seconds, microseconds);
return -2; // couldn't connect
}
void Start()
{
......@@ -264,19 +272,6 @@ class GmshClient : public GmshSocket {
SendString(START, tmp);
}
void Stop(){ SendString(STOP, "Goodbye!"); }
void Info(const char *str){ SendString(INFO, str); }
void Warning(const char *str){ SendString(WARNING, str); }
void Error(const char *str){ SendString(ERROR, str); }
void Progress(const char *str){ SendString(PROGRESS, str); }
void MergeFile(const char *str){ SendString(MERGE_FILE, str); }
void ParseString(const char *str){ SendString(PARSE_STRING, str); }
void SpeedTest(const char *str){ SendString(SPEED_TEST, str); }
void Option(int num, const char *str)
{
if(num < 1) num = 1;
if(num > 5) num = 5;
SendString(OPTION_1 + num - 1, str);
}
void Disconnect(){ CloseSocket(_sock); }
};
......@@ -287,10 +282,9 @@ class GmshServer : public GmshSocket{
GmshServer() : GmshSocket(), _portno(-1) {}
virtual ~GmshServer(){}
virtual int SystemCall(const char *str) = 0;
virtual int NonBlockingWait(int socket, int num, double waitint) = 0;
int StartClient(const char *command, const char *sockname=0, int timeout=5)
virtual int NonBlockingWait(int socket, int num, double waitint, double timeout) = 0;
int Start(int num, const char *command, const char *sockname, double timeout)
{
bool justwait = (!command || !strlen(command));
_sockname = sockname;
// no socket? launch the command directly
......@@ -308,8 +302,7 @@ class GmshServer : public GmshSocket{
unlink(_sockname);
// create a socket
tmpsock = socket(PF_UNIX, SOCK_STREAM, 0);
if(tmpsock < 0)
return -1; // Error: Couldn't create socket
if(tmpsock < 0) throw "Couldn't create socket";
// bind the socket to its name
struct sockaddr_un addr_un;
memset((char *) &addr_un, 0, sizeof(addr_un));
......@@ -317,12 +310,12 @@ class GmshServer : public GmshSocket{
addr_un.sun_family = AF_UNIX;
if(bind(tmpsock, (struct sockaddr *)&addr_un, sizeof(addr_un)) < 0){
CloseSocket(tmpsock);
return -2; // Error: Couldn't bind socket to name
throw "Couldn't bind socket to name";
}
// change permissions on the socket name in case it has to be rm'd later
chmod(_sockname, 0666);
#else
return -7; // Unix sockets not available on Windows
throw "Unix sockets not available on Windows";
#endif
}
else{
......@@ -336,7 +329,7 @@ class GmshServer : public GmshSocket{
#else
if(tmpsock == INVALID_SOCKET)
#endif
return -1; // Error: Couldn't create socket
throw "Couldn't create socket";
// bind the socket to its name
struct sockaddr_in addr_in;
memset((char *) &addr_in, 0, sizeof(addr_in));
......@@ -345,33 +338,33 @@ class GmshServer : public GmshSocket{
addr_in.sin_port = htons(_portno);
if(bind(tmpsock, (struct sockaddr *)&addr_in, sizeof(addr_in)) < 0){
CloseSocket(tmpsock);
return -2; // Error: Couldn't bind socket to name
throw "Couldn't bind socket to name";
}
}
if(!justwait)
if(command && strlen(command)){
SystemCall(command); // Start the solver
}
else{
timeout = 0.; // no command launched: don't set a timeout
}
// listen on socket (queue up to 20 connections before having
// them automatically rejected)
if(listen(tmpsock, 20)){
CloseSocket(tmpsock);
return -3; // Error: Socket listen failed
throw "Socket listen failed";
}
if(justwait){
// wait indefinitely until we get data
if(NonBlockingWait(tmpsock, -1, 0.5)){
// wait until we get data
int ret = NonBlockingWait(tmpsock, num, 0.5, timeout);
if(ret){
CloseSocket(tmpsock);
return -6; // not an actual error: we just stopped listening
}
if(ret == 2){
throw "Socket listening timeout";
}
else{
// Wait at most timeout seconds for data, issue error if no
// connection in that amount of time
if(!Select(tmpsock, timeout, 0)){
CloseSocket(tmpsock);
return -4; // Error: Socket listening timeout
return -1; // stopped listening
}
}
......@@ -381,8 +374,6 @@ class GmshServer : public GmshSocket{
struct sockaddr_un from_un;
socklen_t len = sizeof(from_un);
_sock = accept(tmpsock, (struct sockaddr *)&from_un, &len);
#else
_sock = -7; // Unix sockets not available on Windows
#endif
}
else{
......@@ -393,7 +384,7 @@ class GmshServer : public GmshSocket{
CloseSocket(tmpsock);
if(_sock < 0)
return -5; // Error: Socket accept failed
throw "Socket accept failed";
return _sock;
}
int Shutdown()
......
......@@ -62,14 +62,14 @@ std::string SanitizeTeXString(const char *in, int equation)
return out;
}
std::string FixWindowsPath(const char *in)
std::string FixWindowsPath(std::string in)
{
#if defined(__CYGWIN__)
char tmp[1024];
cygwin_conv_to_win32_path(in, tmp);
cygwin_conv_to_win32_path(in.c_str(), tmp);
return std::string(tmp);
#else
return std::string(in);
return in;
#endif
}
......@@ -115,6 +115,21 @@ std::vector<std::string> SplitWhiteSpace(std::string in, unsigned int len)
return out;
}
std::string ReplacePercentS(std::string in, std::string val)
{
std::string out;
for(unsigned int i = 0; i < in.size(); i++){
if(in[i] == '%' && i + 1 < in.size() && in[i + 1] == 's'){
out += val;
i++;
}
else{
out += in[i];
}
}
return out;
}
void ReplaceMultiFormat(const char *in, const char *val, char *out)
{
unsigned int i = 0, j = 0;
......
......@@ -13,10 +13,11 @@
void SwapBytes(char *array, int size, int n);
std::string ExtractDoubleQuotedString(const char *str, int len);
std::string SanitizeTeXString(const char *in, int equation);
std::string FixWindowsPath(const char *in);
std::string FixWindowsPath(std::string in);
std::string FixRelativePath(std::string reference, std::string in);
std::vector<std::string> SplitFileName(std::string fileName);
std::vector<std::string> SplitWhiteSpace(std::string in, unsigned int len);
std::string ReplacePercentS(std::string in, std::string val);
void ReplaceMultiFormat(const char *in, const char *val, char *out);
#endif
......@@ -26,23 +26,30 @@ class myGmshServer : public GmshServer{
myGmshServer() : GmshServer() {}
~myGmshServer() {}
int SystemCall(const char *str){ return ::SystemCall(str); }
int NonBlockingWait(int socket, int num, double waitint)
int NonBlockingWait(int socket, int num, double waitint, double timeout)
{
// This routine polls the socket at least every 'waitint' seconds and
// returns 0 if data is available or 1 if there was en error or if the
// process was killed. Otherwise it just tends to current GUI events
// (this is easier to manage than non-blocking IO, and simpler than
// using the "real" solution, i.e., threads. Another possibility would
// This routine polls the socket at least every 'waitint' seconds,
// and for at most timout seconds (or indefinitely if
// timeout==0). The routine returns 0 as soon as data is available
// and 1 if there was en error or if the process was
// killed. Otherwise it just tends to current GUI events (this is
// easier to manage than non-blocking IO, and simpler than using
// the "real" solution, i.e., threads. Another possibility would
// be to use Fl::add_fd())
double start = GetTimeInSeconds();
while(1){
if((num >= 0 && SINFO[num].pid < 0) || (num < 0 && !CTX::instance()->solver.listen)){
// process has been killed or we stopped listening
return 1;
if(timeout > 0 && GetTimeInSeconds() - start > timeout) {
return 2; // timout
}
if((num >= 0 && SINFO[num].pid < 0) ||
(num < 0 && !CTX::instance()->solver.listen)){
return 1; // process has been killed or we stopped listening
}
// check if there is data (call select with a zero timeout to
// return immediately, i.e., do polling)
int ret = Select(socket, 0, 0);
int ret = Select(0, 0, socket);
if(ret == 0){
// nothing available: wait at most waitint seconds, and in the
......@@ -50,8 +57,7 @@ class myGmshServer : public GmshServer{
FlGui::instance()->wait(waitint);
}
else if(ret > 0){
// data is there
return 0;
return 0; // data is there!
}
else{
// an error happened
......@@ -73,7 +79,7 @@ std::string GetSocketName(int num)
std::ostringstream tmp;
tmp << CTX::instance()->homeDir << CTX::instance()->solver.socketName;
if(num >= 0) tmp << "-" << num;
sockname = FixWindowsPath(tmp.str().c_str());
sockname = FixWindowsPath(tmp.str());
}
else{
// TCP/IP socket
......@@ -97,19 +103,21 @@ int Solver(int num, const char *args)
GmshServer *server = new myGmshServer;
if(num >= 0){
prog = FixWindowsPath(SINFO[num].executable_name.c_str());
prog = FixWindowsPath(SINFO[num].executable_name);
if(!SINFO[num].client_server) {
command = prog + " " + args;
#if !defined(WIN32)
command += " &";
#endif
server->StartClient(command.c_str());
server->Start(num, command.c_str(), 0, 0.);
delete server;
return 1;
}
}
else{
if(!CTX::instance()->solver.listen){
Msg::Info("Stopped listening for solver connections");
delete server;
return 0;
}
// we don't know who will (maybe) contact us
......@@ -128,51 +136,31 @@ int Solver(int num, const char *args)
#endif
}
int sock = server->StartClient(command.c_str(), sockname.c_str());
if(num >= 0){
SINFO[num].pid = 0;
SINFO[num].server = 0;
}
bool initOption[5] = {true, true, true, true, true};
if(sock < 0) {
switch (sock) {
case -1:
Msg::Error("Couldn't create socket '%s'", sockname.c_str());
break;
case -2:
Msg::Error("Couldn't bind socket to name '%s'", sockname.c_str());
break;
case -3:
Msg::Error("Socket listen failed on '%s'", sockname.c_str());
break;
case -4:
Msg::Error("Socket listen timeout on '%s'", sockname.c_str());
Msg::Error("Is '%s' correctly installed?", prog.c_str());
break;
case -5:
Msg::Error("Socket accept failed on '%s'", sockname.c_str());
break;
case -6:
Msg::Info("Stopped listening for solver connections");
server->Shutdown();
break;
case -7:
Msg::Error("Unix sockets not available on Windows without Cygwin");
Msg::Error("Use TCP/IP sockets instead");
break;
case -8:
Msg::Error("Could not initialize Windows sockets");
break;
int sock;
try{
sock = server->Start(num, command.c_str(), sockname.c_str(), 5.);
}
catch(const char *err){
Msg::Error("%s (on socket '%s')", err, sockname.c_str());
sock = -1;
}
if(sock < 0){
if(num >= 0){
for(int i = 0; i < SINFO[num].nboptions; i++)
FlGui::instance()->solver[num]->choice[i]->clear();
}
server->Shutdown();
delete server;
return 0;
}
if(num >= 0){
SINFO[num].pid = 0;
SINFO[num].server = 0;
}
bool initOption[5] = {true, true, true, true, true};
Msg::StatusBar(2, false, "Running '%s'", prog.c_str());
while(1) {
......@@ -182,7 +170,7 @@ int Solver(int num, const char *args)
if(stop || (num >= 0 && SINFO[num].pid < 0))
break;
stop = server->NonBlockingWait(sock, num, 0.1);
stop = server->NonBlockingWait(sock, num, 0.1, 0.);
if(stop || (num >= 0 && SINFO[num].pid < 0))
break;
......@@ -312,16 +300,19 @@ int Solver(int num, const char *args)
FlGui::instance()->solver[num]->choice[i]->value(0);
}
}
// only necessary in case of error
SINFO[num].server = 0;
}
server->Shutdown();
delete server;
if(num >= 0){
Msg::StatusBar(2, false, "");
}
else{
Msg::Info("Client disconnected: starting new connection");
delete server;
goto new_connection;
}
......
......@@ -17,7 +17,7 @@
static void help_license_cb(Fl_Widget *w, void *data)
{
std::string prog = FixWindowsPath(CTX::instance()->webBrowser.c_str());
std::string prog = FixWindowsPath(CTX::instance()->webBrowser);
char cmd[1024];
ReplaceMultiFormat(prog.c_str(), "http://geuz.org/gmsh/doc/LICENSE.txt", cmd);
SystemCall(cmd);
......@@ -25,7 +25,7 @@ static void help_license_cb(Fl_Widget *w, void *data)
static void help_credits_cb(Fl_Widget *w, void *data)
{
std::string prog = FixWindowsPath(CTX::instance()->webBrowser.c_str());
std::string prog = FixWindowsPath(CTX::instance()->webBrowser);
char cmd[1024];
ReplaceMultiFormat(prog.c_str(), "http://geuz.org/gmsh/doc/CREDITS.txt", cmd);
SystemCall(cmd);
......
......@@ -150,13 +150,13 @@ static void file_remote_cb(Fl_Widget *w, void *data)
{
std::string str((const char*)data);
if(str == "connect"){
Msg::Info("Starting remote Gmsh");
if(str == "start"){
Msg::Info("Starting remote Gmsh server");
if(SINFO[MAX_NUM_SOLVERS].server){
Msg::Info("A server is already running, trying to stop it first");
SINFO[MAX_NUM_SOLVERS].server->SendString(GmshSocket::STOP, "DISCONNECTING!");
Msg::Error("A server is already running");
}
SINFO[MAX_NUM_SOLVERS].name = "Gmsh Daemon";
else{
SINFO[MAX_NUM_SOLVERS].name = "Gmsh Server";
SINFO[MAX_NUM_SOLVERS].executable_name = "./gmsh";
SINFO[MAX_NUM_SOLVERS].socket_command = "-socket %s";
SINFO[MAX_NUM_SOLVERS].nboptions = 0;
......@@ -165,22 +165,23 @@ static void file_remote_cb(Fl_Widget *w, void *data)
SINFO[MAX_NUM_SOLVERS].merge_views = 1;
Solver(MAX_NUM_SOLVERS, "");
}
else if(str == "disconnect"){
}
else if(str == "stop"){
if(SINFO[MAX_NUM_SOLVERS].server){
Msg::Info("Stopping remote Gmsh");
Msg::Info("Stopping remote Gmsh server");
SINFO[MAX_NUM_SOLVERS].server->SendString(GmshSocket::STOP, "DISCONNECTING!");
}
else{
Msg::Warning("Cannot disconnect remote Gmsh: server not running");
Msg::Error("Cannot stop remote Gmsh: server not running");
}
}
else if(str == "test"){
if(SINFO[MAX_NUM_SOLVERS].server){
Msg::Info("Testing remote Gmsh daemon");
Msg::Info("Testing remote Gmsh server");
SINFO[MAX_NUM_SOLVERS].server->SendString(9999, "GENERATE A VIEW!");
}
else{
Msg::Warning("Cannot test remote Gmsh: must be connected first!");
Msg::Error("Cannot test remote Gmsh: server not running");
}
}
}
......@@ -520,7 +521,7 @@ static void help_command_line_cb(Fl_Widget *w, void *data)
static void help_online_cb(Fl_Widget *w, void *data)
{
std::string prog = FixWindowsPath(CTX::instance()->webBrowser.c_str());
std::string prog = FixWindowsPath(CTX::instance()->webBrowser);
char cmd[1024];
ReplaceMultiFormat(prog.c_str(), "http://geuz.org/gmsh/doc/texinfo/", cmd);
SystemCall(cmd);
......@@ -573,8 +574,8 @@ static void geometry_physical_cb(Fl_Widget *w, void *data)
static void geometry_edit_cb(Fl_Widget *w, void *data)
{
std::string prog = FixWindowsPath(CTX::instance()->editor.c_str());
std::string file = FixWindowsPath(GModel::current()->getFileName().c_str());
std::string prog = FixWindowsPath(CTX::instance()->editor);
std::string file = FixWindowsPath(GModel::current()->getFileName());
char cmd[1024];
ReplaceMultiFormat(prog.c_str(), file.c_str(), cmd);
SystemCall(cmd);
......@@ -2237,10 +2238,10 @@ static Fl_Menu_Item sysbar_table[] = {
{"Vertically", 0, (Fl_Callback *)file_window_cb, (void*)"split_v"},
{"Clear", 0, (Fl_Callback *)file_window_cb, (void*)"split_u"},
{0},
#if 0 // test remote gmsh daemon
{"Connect...", 0, (Fl_Callback *)file_remote_cb, (void*)"connect"},
{"Test remote!", 0, (Fl_Callback *)file_remote_cb, (void*)"test"},
{"Disconnect", 0, (Fl_Callback *)file_remote_cb, (void*)"disconnect", FL_MENU_DIVIDER},
#if 1 // test remote Gmsh server
{"Start server...", 0, (Fl_Callback *)file_remote_cb, (void*)"start"},
{"Test server!", 0, (Fl_Callback *)file_remote_cb, (void*)"test"},
{"Stop server", 0, (Fl_Callback *)file_remote_cb, (void*)"stop", FL_MENU_DIVIDER},
#endif
{"Rename...", FL_META+'r', (Fl_Callback *)file_rename_cb, 0},
{"Save As...", FL_META+'s', (Fl_Callback *)file_save_as_cb, 0},
......
......@@ -50,7 +50,7 @@ void solver_cb(Fl_Widget *w, void *data)
std::string inputFile(FlGui::instance()->solver[num]->input[0]->value());
if(SINFO[num].nboptions && inputFile.size()) {
std::string file = FixWindowsPath(inputFile.c_str());
std::string file = FixWindowsPath(inputFile);
char tmp[256], tmp2[256];
sprintf(tmp, "\"%s\"", file.c_str());
sprintf(tmp2, SINFO[num].name_command.c_str(), tmp);
......@@ -70,7 +70,7 @@ static void solver_file_open_cb(Fl_Widget *w, void *data)
if(file_chooser(0, 0, "Choose", tmp)) {
FlGui::instance()->solver[num]->input[0]->value(file_chooser_get_name(1).c_str());
if(SINFO[num].nboptions) {
std::string file = FixWindowsPath(file_chooser_get_name(1).c_str());
std::string file = FixWindowsPath(file_chooser_get_name(1));
sprintf(tmp, "\"%s\"", file.c_str());
sprintf(tmp2, SINFO[num].name_command.c_str(), tmp);
sprintf(tmp, "%s %s", SINFO[num].option_command.c_str(), tmp2);
......@@ -82,7 +82,7 @@ static void solver_file_open_cb(Fl_Widget *w, void *data)
static void solver_file_edit_cb(Fl_Widget *w, void *data)
{
int num = (int)(long)data;
std::string prog = FixWindowsPath(CTX::instance()->editor.c_str());
std::string prog = FixWindowsPath(CTX::instance()->editor);
std::string file = FixWindowsPath(FlGui::instance()->solver[num]->input[0]->value());
char cmd[1024];
ReplaceMultiFormat(prog.c_str(), file.c_str(), cmd);
......@@ -139,7 +139,6 @@ static void solver_command_cb(Fl_Widget *w, void *data)
sprintf(command, SINFO[num].button_command[idx].c_str(),
SINFO[num].option[usedopts][val].c_str());
else{
Msg::Warning("Invalid choice (%d) for option %d", val, usedopts);
strcpy(command, "");
}
}
......
......@@ -19,8 +19,8 @@ Build Gmsh using CMake's graphical user interface
-------------------------------------------------
* Launch CMake and fill-in the two top input fields (telling where the
Gmsh source is located and where you want the Gmsh binary to be
created).
Gmsh source directory is located and where you want the Gmsh binary
to be created).
* Click on "Add entry" and define the variable CMAKE_PREFIX_PATH, of
type "PATH", pointing to the location(s) of any external package(s)
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment