Commit 3f8b65ee authored by whitlocb's avatar whitlocb
Browse files

I completed porting libsim to Windows. Reading from console now works too....

I completed porting libsim to Windows. Reading from console now works too. Some changes are more broad. I was unable to use the engine return socket for return data and for asynchronous data at the same time due to how qsocketnotifier works on Windows. So, until the engine API is asynchronous, another socket to the viewer is required. This should be okay with previous SimV2's since the changes were only in the runtime library.

git-svn-id: http://visit.ilight.com/svn/visit/trunk/src@13571 18c085ea-50e0-402c-830e-de6fd14e8384
parent a405e06d
......@@ -290,6 +290,9 @@ Engine::Engine() : viewerArgs()
netmgr = NULL;
lb = NULL;
procAtts = NULL;
simxfer = NULL;
simConnection = NULL;
simulationCommandCallback = NULL;
simulationCommandCallbackData = NULL;
metaData = NULL;
......@@ -365,6 +368,8 @@ Engine::~Engine()
{
delete xfer;
delete lb;
delete simxfer;
delete silAtts;
delete metaData;
delete commandFromSim;
......@@ -524,6 +529,8 @@ Engine::Initialize(int *argc, char **argv[], bool sigs)
VisItInit::Initialize(*argc, *argv, 0,1, true, sigs);
#endif
simxfer = new Xfer;
// Configure external options.
RuntimeSetting::parse_command_line(*argc, const_cast<const char**>(*argv));
......@@ -803,6 +810,14 @@ Engine::SetUpViewerInterface(int *argc, char **argv[])
else
vtkConnection = viewerP->GetReadConnection(1);
if(simulationPluginsEnabled)
{
if(reverseLaunch)
simConnection = viewer->GetWriteConnection(2);
else
simConnection = viewerP->GetReadConnection(2);
}
// Parse the command line.
ProcessCommandLine(*argc, *argv);
......@@ -994,9 +1009,9 @@ Engine::SetUpViewerInterface(int *argc, char **argv[])
metaData = new avtDatabaseMetaData;
silAtts = new SILAttributes;
commandFromSim = new SimulationCommand;
xfer->Add(metaData);
xfer->Add(silAtts);
xfer->Add(commandFromSim);
simxfer->Add(metaData);
simxfer->Add(silAtts);
simxfer->Add(commandFromSim);
//
// Hook up the viewer connections to Xfer
......@@ -1008,6 +1023,9 @@ Engine::SetUpViewerInterface(int *argc, char **argv[])
xfer->SetInputConnection(viewer->GetWriteConnection());
else
xfer->SetInputConnection(viewerP->GetWriteConnection());
if(simulationPluginsEnabled)
simxfer->SetOutputConnection(simConnection);
}
else
{
......@@ -1020,6 +1038,9 @@ Engine::SetUpViewerInterface(int *argc, char **argv[])
xfer->SetInputConnection(viewer->GetWriteConnection());
else
xfer->SetInputConnection(viewerP->GetWriteConnection());
if(simulationPluginsEnabled)
simxfer->SetOutputConnection(simConnection);
#endif
if(reverseLaunch)
xfer->SetOutputConnection(viewer->GetReadConnection());
......@@ -1222,7 +1243,7 @@ Engine::ReverseLaunchViewer(int *argc, char **argv[])
0, // ssh port
false, // ssh tunnelling
1, // num read sockets
2); // num write sockets
simulationPluginsEnabled ? 3 : 2); // num write sockets
}
CATCH(VisItException)
{
......@@ -1295,10 +1316,11 @@ Engine::ConnectViewer(int *argc, char **argv[])
{
// The viewer launched the engine
viewerP = new ParentProcess;
int nwrite = simulationPluginsEnabled ? 3 : 2;
#ifdef PARALLEL
viewerP->Connect(1, 2, argc, argv, PAR_UIProcess());
viewerP->Connect(1, nwrite, argc, argv, PAR_UIProcess());
#else
viewerP->Connect(1, 2, argc, argv, true);
viewerP->Connect(1, nwrite, argc, argv, true);
#endif
}
}
......@@ -3267,7 +3289,7 @@ Engine::PopulateSimulationMetaData(const std::string &db,
// Send the metadata and SIL to the viewer
if(!quitRPC->GetQuit())
{
xfer->SetUpdate(true);
simxfer->SetUpdate(true);
metaData->Notify();
silAtts->SelectAll();
silAtts->Notify();
......@@ -3345,7 +3367,7 @@ Engine::SimulationInitiateCommand(const std::string &command)
{
// Allow the command to be sent, even if we're in the middle of an
// Xfer::Process. This fixes a synchronization bug.
xfer->SetUpdate(true);
simxfer->SetUpdate(true);
commandFromSim->SetCommand(command);
commandFromSim->Notify();
......
......@@ -376,6 +376,8 @@ class ENGINE_MAIN_API Engine
// The metadata, filename, format, control data for a simulation
std::string filename;
std::string format;
Xfer *simxfer;
Connection *simConnection;
avtDatabaseMetaData *metaData;
SILAttributes *silAtts;
SimulationCommand *commandFromSim;
......
......@@ -94,12 +94,12 @@
//
// ****************************************************************************
EngineProxy::EngineProxy() : RemoteProxyBase("-engine")
EngineProxy::EngineProxy(bool sim) : RemoteProxyBase("-engine")
{
engineP = NULL;
// Indicate that we want 2 write sockets from the engine.
nWrite = 2;
nWrite = sim ? 3 : 2;
// Initialize the engine information that we can query.
numProcs = 1;
......@@ -108,6 +108,7 @@ EngineProxy::EngineProxy() : RemoteProxyBase("-engine")
// Create the status attributes that we use to communicate status
// information to the client.
simxfer = sim ? (new Xfer) : NULL;
statusAtts = new StatusAttributes;
metaData = new avtDatabaseMetaData;
silAtts = new SILAttributes;
......@@ -156,6 +157,8 @@ EngineProxy::EngineProxy() : RemoteProxyBase("-engine")
EngineProxy::~EngineProxy()
{
delete statusAtts;
delete simxfer;
delete metaData;
delete silAtts;
delete commandFromSim;
......@@ -197,7 +200,8 @@ EngineProxy::Connect(const stringVector &args)
int argc = args.size();
engineP = new ParentProcess;
engineP->Connect(1, 2, &argc, &argv, true);
int nwrite = (simxfer != NULL) ? 3 : 2;
engineP->Connect(1, nwrite, &argc, &argv, true);
delete [] argv;
// Use engineP's connections for xfer.
......@@ -300,14 +304,20 @@ EngineProxy::SetupComponentRPCs()
xfer.Add(&constructDataBinningRPC);
xfer.Add(&namedSelectionRPC);
xfer.Add(&setEFileOpenOptionsRPC);
xfer.Add(&exprList);
//
// Add other state objects to the transfer object
//
xfer.Add(&exprList);
xfer.Add(metaData);
xfer.Add(silAtts);
xfer.Add(commandFromSim);
Xfer *x = &xfer;
if(simxfer != NULL && component != NULL)
{
x = simxfer;
simxfer->SetInputConnection(component->GetWriteConnection(2));
}
x->Add(metaData);
x->Add(silAtts);
x->Add(commandFromSim);
// Extract some information about the engine from the command line
// arguments that were used to create it.
......@@ -1734,8 +1744,8 @@ EngineProxy::ExportDatabase(const int id, const ExportDBAttributes *atts)
int
EngineProxy::GetWriteSocket()
{
if (xfer.GetInputConnection())
return xfer.GetInputConnection()->GetDescriptor();
if (simxfer != NULL && simxfer->GetInputConnection() != NULL)
return simxfer->GetInputConnection()->GetDescriptor();
else
return -1;
}
......@@ -1757,9 +1767,9 @@ EngineProxy::GetWriteSocket()
void
EngineProxy::ReadDataAndProcess()
{
int amountRead = xfer.GetInputConnection()->Fill();
int amountRead = simxfer->GetInputConnection()->Fill();
if (amountRead > 0)
xfer.Process();
simxfer->Process();
else
EXCEPTION0(LostConnectionException);
}
......
......@@ -320,7 +320,7 @@ class ParentProcess;
class ENGINE_PROXY_API EngineProxy : public RemoteProxyBase
{
public:
EngineProxy();
EngineProxy(bool sim = false);
virtual ~EngineProxy();
// This version of Create is specifically for reverse launch.
......@@ -472,6 +472,7 @@ private:
StatusAttributes *statusAtts;
// Metadata, SIL published by a simulation
Xfer *simxfer;
avtDatabaseMetaData *metaData;
SILAttributes *silAtts;
SimulationCommand *commandFromSim;
......
......@@ -43,6 +43,9 @@
#ifdef _WIN32
#include <winsock2.h>
#include <direct.h>
#include <sys/stat.h>
#include <shlobj.h>
#include <shlwapi.h>
#else
#include <dlfcn.h>
#include <netdb.h>
......@@ -1213,12 +1216,41 @@ static VISIT_SOCKET AcceptConnection(void)
* Author: Jeremy Meredith, B Division, Lawrence Livermore National Laboratory
*
* Modifications:
* Brad Whitlock, Mon Jan 17 18:31:23 PST 2011
* I added a Windows implementation.
*
*******************************************************************************/
static const char *GetHomeDirectory(void)
{
#ifdef _WIN32
return "C:/Users/Brad";
char visituserpath[MAX_PATH], expvisituserpath[MAX_PATH];
static char *returnpath = NULL;
int haveVISITUSERHOME=0, pathlen = 0;
TCHAR szPath[MAX_PATH];
LIBSIM_API_ENTER(GetHomeDirectory);
if(SUCCEEDED(SHGetFolderPath(NULL, CSIDL_PERSONAL, NULL,
SHGFP_TYPE_CURRENT, szPath)))
{
SNPRINTF(visituserpath, 512, "%s", szPath);
haveVISITUSERHOME = 1;
}
if (haveVISITUSERHOME)
ExpandEnvironmentStrings(visituserpath, expvisituserpath, MAX_PATH);
else
strcpy(expvisituserpath, "C:\\Users");
if(returnpath != NULL)
free(returnpath);
pathlen = 1 + strlen(expvisituserpath);
returnpath = (char*)malloc(pathlen);
strcpy(returnpath, expvisituserpath);
LIBSIM_API_LEAVE1(GetHomeDirectory, "homedir=%s", returnpath);
return returnpath;
#else
struct passwd *users_passwd_entry = NULL;
......@@ -1249,6 +1281,11 @@ static void EnsureSimulationDirectoryExists(void)
char str[1024];
LIBSIM_API_ENTER(EnsureSimulationDirectoryExists);
#ifdef _WIN32
SNPRINTF(str, 1024, "%s/Simulations", GetHomeDirectory());
VisItMkdir(str, 7*64 + 7*8 + 7);
LIBSIM_MESSAGE1("mkdir %s", str);
#else
SNPRINTF(str, 1024, "%s/.visit", GetHomeDirectory());
VisItMkdir(str, 7*64 + 7*8 + 7);
LIBSIM_MESSAGE1("mkdir %s", str);
......@@ -1256,6 +1293,7 @@ static void EnsureSimulationDirectoryExists(void)
SNPRINTF(str, 1024, "%s/.visit/simulations", GetHomeDirectory());
VisItMkdir(str, 7*64 + 7*8 + 7);
LIBSIM_MESSAGE1("mkdir %s", str);
#endif
LIBSIM_API_LEAVE(EnsureSimulationDirectoryExists);
}
......@@ -1348,6 +1386,8 @@ static int LoadVisItLibrary_Windows(void)
SetErrorMode(0);
dl_handle = LoadLibrary(lib);
#if 0
/* TODO: Make the error reporting work. */
if (dl_handle == NULL)
{
WCHAR msg[1024];
......@@ -1360,6 +1400,7 @@ LIBSIM_MESSAGE1("Error: %x", GetLastError());
/*SNPRINTF(lastError, 1024, "Failed to open the VisIt library: %s\n", msg);*/
}
#endif
LIBSIM_API_LEAVE(LoadVisItLibrary_Windows);
......@@ -1716,7 +1757,9 @@ int VisItSetupEnvironment(void)
LIBSIM_API_ENTER(VisItSetupEnvironment);
GetVisItDirectory(visitpath, 1024);
/* Tell Windows that we want to get DLLs from this path */
/* Tell Windows that we want to get DLLs from this path. We DO need this
* in order for dependent DLLs to be located.
*/
SetDllDirectory(visitpath);
/* Set the VisIt home dir. */
......@@ -1729,7 +1772,7 @@ int VisItSetupEnvironment(void)
LIBSIM_MESSAGE(tmp);
putenv(tmp);
// Initiate the use of a Winsock DLL (WS2_32.DLL), necessary for sockets.
/* Initiate the use of a Winsock DLL (WS2_32.DLL), necessary for sockets. */
wVersionRequested = MAKEWORD(2,2);
WSAStartup(wVersionRequested, &wsaData);
#else
......@@ -1822,7 +1865,12 @@ int VisItInitializeSocketAndDumpSimFile(const char *name,
if ( !absoluteFilename )
{
SNPRINTF(simulationFileName, 255, "%s/.visit/simulations/%012d.%s.sim2",
SNPRINTF(simulationFileName, 255,
#ifdef _WIN32
"%s/Simulations/%012d.%s.sim2",
#else
"%s/.visit/simulations/%012d.%s.sim2",
#endif
GetHomeDirectory(), (int)time(NULL), name);
}
else
......@@ -1893,28 +1941,16 @@ VisItDetectInput(int blocking, int consoleFileDescriptor)
}
#ifdef _WIN32
static int inputThreadsStarted = 0;
static HANDLE stdinevent = 0;
/*
* Win32 implementation of VisItDetectInputWithTimeout.
*/
static int selectThreadStarted = 0;
static int consoleThreadStarted = 0;
static WSAEVENT listenevent = 0;
static HANDLE engineevent = 0;
static HANDLE listeneventCB = 0;
static HANDLE engineeventCB = 0;
DWORD WINAPI
stdin_thread(LPVOID param)
{
while(1)
{
WaitForSingleObject(GetStdHandle(STD_INPUT_HANDLE), INFINITE);
fprintf(stderr, "stdin thread signaling input\n");
SetEvent(stdinevent);
}
return 0;
}
#define SELECT_ENGINE_SOCKET
static void
VLogWindowsSocketError()
{
......@@ -2016,6 +2052,14 @@ VLogWindowsSocketError()
fprintf(stderr, "\n");
}
/*
* We run this function on another thread so select can block and then we
* send events to the main thread which is stuck in WaitForMultipleObjects
* inside its VisItDetectInputWithTimeout function. We use this multiple
* thread approach so we can wait for both socket input and console input,
* which is central to VisItDetectInput.
*/
DWORD WINAPI
select_thread(LPVOID param)
{
......@@ -2024,18 +2068,15 @@ select_thread(LPVOID param)
{
fd_set readSet;
int ignored = 0, status = 0;
struct timeval SmallTimeout = {0, 50000};
FD_ZERO(&readSet);
/* If we're connected, select on the control socket */
#ifdef SELECT_ENGINE_SOCKET
if(engineSocket != VISIT_INVALID_SOCKET)
{
FD_SET(engineSocket, &readSet);
fprintf(stderr, "select_thread: selecting engine control socket\n");
/*fprintf(stderr, "select_thread: selecting engine control socket\n");*/
}
#endif
/* If we're connected, do *not* select on the listen socket */
/* This forces us to have only one client at a time. */
......@@ -2043,91 +2084,66 @@ select_thread(LPVOID param)
listenSocket != VISIT_INVALID_SOCKET)
{
FD_SET(listenSocket, &readSet);
fprintf(stderr, "select_thread: selecting listen socket for inbound connections\n");
/* fprintf(stderr, "select_thread: selecting listen socket for inbound connections\n");*/
}
fprintf(stderr, "select_thread: calling select\n");
/* fprintf(stderr, "select_thread: calling select\n");*/
status = select(ignored, &readSet, (fd_set*)NULL,
(fd_set*)NULL, NULL);
//haveEngineSocket ? &SmallTimeout : NULL);
if(status == SOCKET_ERROR)
{
fprintf(stderr, "SOCKET_ERROR\n");
VLogWindowsSocketError();
}
else if(status == 0)
{
// timed out
//fprintf(stderr, "timed out!\n");
}
else if(status > 0)
{
if (listenSocket != VISIT_INVALID_SOCKET &&
FD_ISSET(listenSocket, &readSet))
{
// fprintf(stderr, "Send a listen event!\n");
SetEvent(listenevent);
// wait for it to be done.
/* wait for it to be done. */
WaitForSingleObject(listeneventCB, INFINITE);
}
#ifdef SELECT_ENGINE_SOCKET
else if (engineSocket != VISIT_INVALID_SOCKET &&
FD_ISSET(engineSocket, &readSet))
{
// fprintf(stderr, "Send an engine event!\n");
SetEvent(engineevent);
// wait for it to be done.
/* wait for it to be done. */
WaitForSingleObject(engineeventCB, INFINITE);
}
#endif
}
else
{
// error
}
}
return 0;
}
/*
* Wait for input from the sockets or console on a specified timeout interval.
*/
int
VisItDetectInputWithTimeout(int blocking, int timeoutVal,
int consoleFileDescriptor)
{
HANDLE handles[3];
int n, msec;
int n0, n, msec;
LIBSIM_API_ENTER2(VisItDetectInput, "blocking=%d, consoleFile=%d",
blocking, consoleFileDescriptor);
msec = timeoutVal / 1000;
if(!inputThreadsStarted)
if(!selectThreadStarted)
{
DWORD stdin_threadid, select_threadid;
DWORD select_threadid;
stdinevent = CreateEvent(NULL, FALSE, FALSE, NULL);
listenevent = CreateEvent(NULL, FALSE, FALSE, NULL);
engineevent = CreateEvent(NULL, FALSE, FALSE, NULL);
listeneventCB = CreateEvent(NULL, FALSE, FALSE, NULL);
engineeventCB = CreateEvent(NULL, FALSE, FALSE, NULL);
#if 0
fprintf(stderr, "Creating stdin thread\n");
if(!CreateThread(NULL, 0, stdin_thread,
NULL, 0, &stdin_threadid))
{
LIBSIM_API_LEAVE1(VisItDetectInput,
"Unable to create console input thread. return %d",
-4);
return -4;
}
#endif
fprintf(stderr, "Creating socket input thread\n");
/*fprintf(stderr, "Creating socket input thread\n");*/
if(!CreateThread(NULL, 0, select_thread,
NULL, 0, &select_threadid))
{
......@@ -2137,16 +2153,21 @@ VisItDetectInputWithTimeout(int blocking, int timeoutVal,
return -4;
}
inputThreadsStarted = 1;
selectThreadStarted = 1;
}
// Wait for any of these events to occur.
/* Wait for any of these events to occur. */
waitforevents:
n0 = 2;
handles[0] = listenevent;
handles[1] = engineevent;
//handles[2] = GetStdHandle(STD_INPUT_HANDLE);
n = WaitForMultipleObjects(2, handles, FALSE, blocking ? INFINITE : msec);
if(consoleFileDescriptor >= 0)
{
handles[2] = GetStdHandle(STD_INPUT_HANDLE);
n0++;
}
//fprintf(stderr, "MsgWaitForMultipleObjects: returned %d\n", n);
n = WaitForMultipleObjects(n0, handles, FALSE, blocking ? INFINITE : msec);
if(n == WAIT_TIMEOUT)
{
......@@ -2157,20 +2178,21 @@ VisItDetectInputWithTimeout(int blocking, int timeoutVal,
}
else if(n == WAIT_OBJECT_0)
{
// we received an event on the listen socket. If it's an event
// indicating that there's input then tell the user it's okay
// to read from the listen socket.
/* We received an event on the listen socket. If it's an event
* indicating that there's input then tell the user it's okay
* to read from the listen socket.
*/
LIBSIM_API_LEAVE1(VisItDetectInput,
"WAIT_OBJECT_0: Listen socket input. return %d",
"WAIT_OBJECT_0: Listen socket input. return %d",
1);
// SetEvent(listeneventCB);
return 1;
}
else if(n == WAIT_OBJECT_0+1)
{
// we received an event for the engine socket. If it's an event
// indicating that there's input then tell the user it's okay
// to read from the engine socket.
/* we received an event for the engine socket. If it's an event
* indicating that there's input then tell the user it's okay
* to read from the engine socket.
*/
LIBSIM_API_LEAVE1(VisItDetectInput,
"WAIT_OBJECT_0+1: Engine socket input. return %d",
2);
......@@ -2178,12 +2200,39 @@ VisItDetectInputWithTimeout(int blocking, int timeoutVal,
}
else if(n == WAIT_OBJECT_0+2)
{
// we received a stdin event. Tell the client that it's
// okay to read from the console.
/* We received a stdin event but it might not be a key event.
* Peek at the events to look for key presses. If we find some,
* tell the client that it's okay to read from the console.
*/
DWORD nEvents = 0;
INPUT_RECORD input[100];
int retval = 0; /* no input timeout */
if(PeekConsoleInput(GetStdHandle(STD_INPUT_HANDLE),
input, 100, &nEvents))
{
DWORD j;
for(j = 0; j < nEvents; ++j)
{
if(input[j].EventType == KEY_EVENT)
{
retval = 3; /* read console input */
break;
}
}
}
/* We did not have any real key events so go back to waiting for them. */
if(retval != 3)
{
/* Gobble up the junk events so we don't keep getting them. */
DWORD maxevents = nEvents;
ReadConsoleInput(GetStdHandle(STD_INPUT_HANDLE),
input, maxevents, &nEvents);
goto waitforevents;
}
LIBSIM_API_LEAVE1(VisItDetectInput,
"WAIT_OBJECT_0+2: Console socket input. return %d",
"WAIT_OBJECT_0+2: Console input. return %d",
3);
fprintf(stderr, "WAIT_OBJECT_0+2\n");
return 3;
}
......@@ -2328,6 +2377,60 @@ VisItDetectInputWithTimeout(int blocking, int timeoutVal,
}
#endif
/*******************************************************************************
*
* Name: VisItReadConsole
*