diff --git a/direct/src/plugin/p3dConditionVar.cxx b/direct/src/plugin/p3dConditionVar.cxx index 3b4b28f03c..405e2a4ea9 100644 --- a/direct/src/plugin/p3dConditionVar.cxx +++ b/direct/src/plugin/p3dConditionVar.cxx @@ -139,7 +139,7 @@ wait(double timeout) { ts.tv_nsec += (int)((timeout - seconds) * 1000000.0); int result = pthread_cond_timedwait(&_cvar, &_lock, &ts); - assert(result == 0); + assert(result == 0 || errno == ETIMEDOUT); #endif // _WIN32 } diff --git a/direct/src/plugin/p3dPythonRun.cxx b/direct/src/plugin/p3dPythonRun.cxx index efc6370d80..bd72e1a221 100755 --- a/direct/src/plugin/p3dPythonRun.cxx +++ b/direct/src/plugin/p3dPythonRun.cxx @@ -237,6 +237,9 @@ handle_command(TiXmlDocument *doc) { // does make debugging the logs a bit easier. _next_sent_id = _session_id * 1000; + PyObject *obj = PyObject_CallMethod(_runner, (char*)"setSessionId", (char *)"i", _session_id); + Py_XDECREF(obj); + } else if (strcmp(cmd, "start_instance") == 0) { assert(!needs_response); TiXmlElement *xinstance = xcommand->FirstChildElement("instance"); diff --git a/direct/src/plugin/p3dSession.cxx b/direct/src/plugin/p3dSession.cxx index 6d3a32041b..6e869a26c3 100644 --- a/direct/src/plugin/p3dSession.cxx +++ b/direct/src/plugin/p3dSession.cxx @@ -44,8 +44,6 @@ P3DSession(P3DInstance *inst) { _python_version = inst->get_python_version(); _p3dpython_running = false; - _response = NULL; - _got_response_id = -1; _started_read_thread = false; _read_thread_continue = false; @@ -275,17 +273,8 @@ command_and_response(TiXmlDocument *command) { // only one thread will be waiting at a time. nout << "waiting for response " << response_id << "\n" << flush; _response_ready.acquire(); - while (_response == NULL || _got_response_id != response_id) { - if (_response != NULL) { - // This is a bogus response. Since we're the only thread waiting, - // it follows that no one is waiting for this response, so we can - // throw it away. - nout << "Discarding bogus response: " << *_response << "\n" << flush; - delete _response; - _response = NULL; - _got_response_id = -1; - } - + Responses::iterator ri = _responses.find(response_id); + while (ri == _responses.end()) { if (!_p3dpython_running) { // Hmm, looks like Python has gone away. @@ -299,11 +288,23 @@ command_and_response(TiXmlDocument *command) { // recursive script requests. (The child process might have to // wait for us to process some of these before it can fulfill the // command we're actually waiting for.) + + // Release the mutex while we do this, so we can safely call back + // in recursively. + _response_ready.release(); Instances::iterator ii; for (ii = _instances.begin(); ii != _instances.end(); ++ii) { P3DInstance *inst = (*ii).second; inst->bake_requests(); } + _response_ready.acquire(); + + ri = _responses.find(response_id); + if (ri != _responses.end()) { + // We got the response we were waiting for while we had the + // mutex unlocked. + break; + } #ifdef _WIN32 // Make sure we process the Windows event loop while we're @@ -314,23 +315,19 @@ command_and_response(TiXmlDocument *command) { // A single PeekMessage() seems to be sufficient. MSG msg; PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE | PM_NOYIELD); +#endif // _WIN32 // We wait with a timeout, so we can go back and spin the event // loop some more. _response_ready.wait(0.1); nout << "." << flush; -#else // _WIN32 - - // On non-Windows platforms, we can just wait indefinitely. - _response_ready.wait(); -#endif // _WIN32 + + ri = _responses.find(response_id); } // When we exit the loop, we've found the desired response. - nout << "got response: " << *_response << "\n" << flush; - - TiXmlDocument *response = _response; - _response = NULL; - _got_response_id = -1; + TiXmlDocument *response = (*ri).second; + nout << "got response: " << *response << "\n" << flush; + _responses.erase(ri); _response_ready.release(); @@ -747,16 +744,8 @@ rt_handle_request(TiXmlDocument *doc) { // This is a response to a previous command-and-response. Send // it to the parent thread. _response_ready.acquire(); - if (_response != NULL) { - // Hey, there's already a response there. Since there's only - // one thread waiting at a time on the command-response cycle, - // this must be a bogus response that never got picked up. - // Discard it. - nout << "Discarding bogus response: " << *_response << "\n"; - delete _response; - } - _response = doc; - _got_response_id = response_id; + bool inserted = _responses.insert(Responses::value_type(response_id, doc)).second; + assert(inserted); _response_ready.notify(); _response_ready.release(); return; diff --git a/direct/src/plugin/p3dSession.h b/direct/src/plugin/p3dSession.h index aa0c030667..a1ec296102 100644 --- a/direct/src/plugin/p3dSession.h +++ b/direct/src/plugin/p3dSession.h @@ -132,9 +132,9 @@ private: #endif bool _p3dpython_running; - // The _response_ready mutex protects this pointer. - TiXmlDocument *_response; - int _got_response_id; + // The _response_ready mutex protects this structure. + typedef map Responses; + Responses _responses; P3DConditionVar _response_ready; // The remaining members are manipulated by or for the read thread. diff --git a/direct/src/showutil/runp3d.py b/direct/src/showutil/runp3d.py index 729510c469..259089e977 100755 --- a/direct/src/showutil/runp3d.py +++ b/direct/src/showutil/runp3d.py @@ -51,7 +51,8 @@ class ScriptAttributes: class AppRunner(DirectObject): def __init__(self): DirectObject.__init__(self) - + + self.sessionId = 0 self.packedAppEnvironmentInitialized = False self.gotWindow = False self.gotP3DFilename = False @@ -94,6 +95,11 @@ class AppRunner(DirectObject): if AppRunnerGlobal.appRunner is None: AppRunnerGlobal.appRunner = self + def setSessionId(self, sessionId): + """ This message should come in at startup. """ + self.sessionId = sessionId + self.nextScriptId = self.sessionId * 1000 + 10000 + def initPackedAppEnvironment(self): """ This function sets up the Python environment suitably for running a packed app. It should only run once in any given @@ -272,6 +278,9 @@ class AppRunner(DirectObject): settings, for future windows; or applies them directly to the main window if the window has already been opened. """ + print "session %s, nextScriptId = %s" % (self.sessionId, self.nextScriptId) + + print "setupWindow %s, %s, %s, %s, %s, %s, %s" % (windowType, x, y, width, height, parent, subprocessWindow) if self.started and base.win: @@ -400,7 +409,7 @@ class AppRunner(DirectObject): waiting for the browser to process the request. """ uniqueId = self.nextScriptId - self.nextScriptId += 1 + self.nextScriptId = (self.nextScriptId + 1) % 0xffffffff self.sendRequest('script', operation, object, propertyName, value, needsResponse, uniqueId) @@ -677,7 +686,7 @@ class MethodWrapper: raise AttributeError(name) if __name__ == '__main__': - runner = AppRunner() + runner = AppRunner(0) runner.gotWindow = True try: runner.setP3DFilename(*runner.parseSysArgs())