Browse Source

command-and-response

David Rose 16 years ago
parent
commit
891051485d

+ 2 - 0
direct/src/plugin/Sources.pp

@@ -21,6 +21,7 @@
     p3d_plugin_config.h \
     p3d_plugin_common.h \
     p3dBoolObject.h \
+    p3dConditionVar.h p3dConditionVar.I \
     p3dDownload.h p3dDownload.I \
     p3dFileDownload.h p3dFileDownload.I \
     p3dFileParams.h p3dFileParams.I \
@@ -41,6 +42,7 @@
   #define INCLUDED_SOURCES \
     p3d_plugin.cxx \
     p3dBoolObject.cxx \
+    p3dConditionVar.cxx \
     p3dDownload.cxx \
     p3dFileDownload.cxx \
     p3dFileParams.cxx \

+ 14 - 0
direct/src/plugin/p3dConditionVar.I

@@ -0,0 +1,14 @@
+// Filename: p3dConditionVar.I
+// Created by:  drose (02Jul09)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) Carnegie Mellon University.  All rights reserved.
+//
+// All use of this software is subject to the terms of the revised BSD
+// license.  You should have received a copy of this license along
+// with this source code in a file named "LICENSE."
+//
+////////////////////////////////////////////////////////////////////
+

+ 136 - 0
direct/src/plugin/p3dConditionVar.cxx

@@ -0,0 +1,136 @@
+// Filename: p3dConditionVar.cxx
+// Created by:  drose (02Jul09)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) Carnegie Mellon University.  All rights reserved.
+//
+// All use of this software is subject to the terms of the revised BSD
+// license.  You should have received a copy of this license along
+// with this source code in a file named "LICENSE."
+//
+////////////////////////////////////////////////////////////////////
+
+#include "p3dConditionVar.h"
+
+////////////////////////////////////////////////////////////////////
+//     Function: P3DConditionVar::Constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+P3DConditionVar::
+P3DConditionVar() {
+#ifdef _WIN32
+  InitializeCriticalSection(&_lock);
+
+  // Create an auto-reset event.
+  _event_signal = CreateEvent(NULL, false, false, NULL);
+
+#else  // _WIN32
+  pthread_mutexattr_t attr;
+  pthread_mutexattr_init(&attr);
+  pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_NORMAL);
+  int result = pthread_mutex_init(&_lock, &attr);
+  pthread_mutexattr_destroy(&attr);
+  assert(result == 0);
+
+  result = pthread_cond_init(&_cvar, NULL);
+  assert(result == 0);
+
+#endif  // _WIN32
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: P3DConditionVar::Destructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+P3DConditionVar::
+~P3DConditionVar() {
+#ifdef _WIN32
+  DeleteCriticalSection(&_lock);
+  CloseHandle(_event_signal);
+
+#else  // _WIN32
+  int result = pthread_mutex_destroy(&_lock);
+  assert(result == 0);
+
+  result = pthread_cond_destroy(&_cvar);
+  assert(result == 0);
+
+#endif  // _WIN32
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: P3DConditionVar::acquire
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void P3DConditionVar::
+acquire() {
+#ifdef _WIN32
+  EnterCriticalSection(&_lock);
+
+#else  // _WIN32
+  int result = pthread_mutex_lock(&_lock);
+  assert(result == 0);
+
+#endif  // _WIN32
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: P3DConditionVar::wait
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void P3DConditionVar::
+wait() {
+#ifdef _WIN32
+  LeaveCriticalSection(&_lock);
+
+  DWORD result = WaitForSingleObject(_event_signal, INFINITE);
+  assert(result == WAIT_OBJECT_0);
+
+  EnterCriticalSection(&_lock);
+
+#else  // _WIN32
+  int result = pthread_cond_wait(&_cvar, &_mutex._lock);
+  assert(result == 0);
+
+#endif  // _WIN32
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: P3DConditionVar::notify
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void P3DConditionVar::
+notify() {
+#ifdef _WIN32
+  SetEvent(_event_signal);
+
+#else  // _WIN32
+  int result = pthread_cond_signal(&_cvar);
+  assert(result == 0);
+
+#endif  // _WIN32
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: P3DConditionVar::release
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void P3DConditionVar::
+release() {
+#ifdef _WIN32
+  LeaveCriticalSection(&_lock);
+
+#else  // _WIN32
+  int result = pthread_mutex_unlock(&_lock);
+  assert(result == 0);
+
+#endif  // _WIN32
+}

+ 49 - 0
direct/src/plugin/p3dConditionVar.h

@@ -0,0 +1,49 @@
+// Filename: p3dConditionVar.h
+// Created by:  drose (02Jul09)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) Carnegie Mellon University.  All rights reserved.
+//
+// All use of this software is subject to the terms of the revised BSD
+// license.  You should have received a copy of this license along
+// with this source code in a file named "LICENSE."
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef P3DCONDITIONVAR_H
+#define P3DCONDITIONVAR_H
+
+#include "p3d_plugin_common.h"
+
+////////////////////////////////////////////////////////////////////
+//       Class : P3DConditionVar
+// Description : A simple condition-variable like object.  It doesn't
+//               support the full condition-var semantics, but it
+//               works well enough with one waiter and one signaller.
+////////////////////////////////////////////////////////////////////
+class P3DConditionVar {
+public:
+  P3DConditionVar();
+  ~P3DConditionVar();
+
+  void acquire();
+  void wait();
+  void notify();
+  void release();
+  
+private:
+#ifdef _WIN32
+  CRITICAL_SECTION _lock;
+  HANDLE _event_signal;
+
+#else  // _WIN32
+  pthread_mutex_t _lock;
+  pthread_cond_t _cvar;
+#endif  // _WIN32
+};
+
+#include "p3dConditionVar.I"
+
+#endif

+ 0 - 32
direct/src/plugin/p3dInstance.cxx

@@ -345,38 +345,6 @@ feed_url_stream(int unique_id,
   return download_ok;
 }
 
-////////////////////////////////////////////////////////////////////
-//     Function: P3DInstance::feed_value
-//       Access: Public
-//  Description: Called by the host in response to a get_property or
-//               call request.  The value object must have been
-//               freshly allocated; it will be deleted by this method.
-////////////////////////////////////////////////////////////////////
-void P3DInstance::
-feed_value(int unique_id, P3DObject *value) {
-  if (_session != NULL) {
-    TiXmlDocument *doc = new TiXmlDocument;
-    TiXmlDeclaration *decl = new TiXmlDeclaration("1.0", "utf-8", "");
-    TiXmlElement *xcommand = new TiXmlElement("command");
-    xcommand->SetAttribute("cmd", "feed_value");
-    xcommand->SetAttribute("instance_id", get_instance_id());
-    xcommand->SetAttribute("unique_id", unique_id);
-    if (value != NULL) {
-      TiXmlElement *xvalue = value->make_xml();
-      xcommand->LinkEndChild(xvalue);
-    }
-    
-    doc->LinkEndChild(decl);
-    doc->LinkEndChild(xcommand);
-
-    _session->send_command(doc);
-  }
-
-  if (value != NULL) {
-    delete value;
-  }
-}
-
 ////////////////////////////////////////////////////////////////////
 //     Function: P3DInstance::add_package
 //       Access: Public

+ 0 - 1
direct/src/plugin/p3dInstance.h

@@ -63,7 +63,6 @@ public:
                        size_t total_expected_data,
                        const unsigned char *this_data, 
                        size_t this_data_size);
-  void feed_value(int unique_id, P3DObject *value);
 
   inline int get_instance_id() const;
   inline const string &get_session_key() const;

+ 8 - 38
direct/src/plugin/p3dInstanceManager.cxx

@@ -38,14 +38,6 @@ P3DInstanceManager() {
   _is_initialized = false;
   _unique_session_index = 0;
 
-  _request_seq = 0;
-#ifdef _WIN32
-  _request_ready = CreateEvent(NULL, false, false, NULL);
-#else
-  INIT_LOCK(_request_ready_lock);
-  pthread_cond_init(&_request_ready_cvar, NULL);
-#endif
-
 #ifdef _WIN32
   // Ensure the appropriate Windows common controls are available to
   // this application.
@@ -68,13 +60,6 @@ P3DInstanceManager::
 
   assert(_instances.empty());
   assert(_sessions.empty());
-
-#ifdef _WIN32
-  CloseHandle(_request_ready);
-#else
-  DESTROY_LOCK(_request_ready_lock);
-  pthread_cond_destroy(&_request_ready_cvar);
-#endif
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -211,30 +196,21 @@ check_request() {
 ////////////////////////////////////////////////////////////////////
 void P3DInstanceManager::
 wait_request() {
-  int seq = _request_seq;
-
+  _request_ready.acquire();
   while (true) {
     if (check_request() != (P3DInstance *)NULL) {
+      _request_ready.release();
       return;
     }
     if (_instances.empty()) {
+      _request_ready.release();
       return;
     }
     
     // No pending requests; go to sleep.
-#ifdef _WIN32
-    if (seq == _request_seq) {
-      WaitForSingleObject(_request_ready, INFINITE);
-    }
-#else
-    ACQUIRE_LOCK(_request_ready_lock);
-    if (seq == _request_seq) {
-      pthread_cond_wait(&_request_ready_cvar, &_request_ready_lock);
-    }
-    RELEASE_LOCK(_request_ready_lock);
-#endif
-    seq = _request_seq;
+    _request_ready.wait();
   }
+  _request_ready.release();
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -283,15 +259,9 @@ get_unique_session_index() {
 ////////////////////////////////////////////////////////////////////
 void P3DInstanceManager::
 signal_request_ready() {
-#ifdef _WIN32
-  ++_request_seq;
-  SetEvent(_request_ready);
-#else
-  ACQUIRE_LOCK(_request_ready_lock);
-  ++_request_seq;
-  pthread_cond_signal(&_request_ready_cvar);
-  RELEASE_LOCK(_request_ready_lock);
-#endif
+  _request_ready.acquire();
+  _request_ready.notify();
+  _request_ready.release();
 }
 
 ////////////////////////////////////////////////////////////////////

+ 2 - 8
direct/src/plugin/p3dInstanceManager.h

@@ -16,6 +16,7 @@
 #define P3DINSTANCEMANAGER_H
 
 #include "p3d_plugin_common.h"
+#include "p3dConditionVar.h"
 
 #include <set>
 #include <map>
@@ -83,14 +84,7 @@ private:
 
   int _unique_session_index;
 
-  // Implements a condition-var like behavior.
-  volatile int _request_seq;
-#ifdef _WIN32
-  HANDLE _request_ready;
-#else
-  LOCK _request_ready_lock;
-  pthread_cond_t _request_ready_cvar;
-#endif
+  P3DConditionVar _request_ready;
   static P3DInstanceManager *_global_ptr;
 };
 

+ 55 - 18
direct/src/plugin/p3dPythonRun.cxx

@@ -192,9 +192,17 @@ handle_command(TiXmlDocument *doc) {
   nout << "got command: " << *doc << "\n";
   TiXmlElement *xcommand = doc->FirstChildElement("command");
   if (xcommand != NULL) {
+    bool needs_response = false;
+    int want_response_id;
+    if (xcommand->QueryIntAttribute("want_response_id", &want_response_id) == TIXML_SUCCESS) {
+      // This command will be waiting for a response.
+      needs_response = true;
+    }
+
     const char *cmd = xcommand->Attribute("cmd");
     if (cmd != NULL) {
       if (strcmp(cmd, "start_instance") == 0) {
+        assert(!needs_response);
         TiXmlElement *xinstance = xcommand->FirstChildElement("instance");
         if (xinstance != (TiXmlElement *)NULL) {
           P3DCInstance *inst = new P3DCInstance(xinstance);
@@ -202,12 +210,14 @@ handle_command(TiXmlDocument *doc) {
         }
 
       } else if (strcmp(cmd, "terminate_instance") == 0) {
+        assert(!needs_response);
         int instance_id;
         if (xcommand->QueryIntAttribute("instance_id", &instance_id) == TIXML_SUCCESS) {
           terminate_instance(instance_id);
         }
 
       } else if (strcmp(cmd, "setup_window") == 0) {
+        assert(!needs_response);
         int instance_id;
         TiXmlElement *xwparams = xcommand->FirstChildElement("wparams");
         if (xwparams != (TiXmlElement *)NULL && 
@@ -216,34 +226,61 @@ handle_command(TiXmlDocument *doc) {
         }
 
       } else if (strcmp(cmd, "exit") == 0) {
+        assert(!needs_response);
         terminate_session();
 
-      } else if (strcmp(cmd, "feed_value") == 0) {
-        int instance_id, unique_id;
-        if (xcommand->QueryIntAttribute("instance_id", &instance_id) == TIXML_SUCCESS &&
-            xcommand->QueryIntAttribute("unique_id", &unique_id) == TIXML_SUCCESS) {
-          // TODO: deal with instance_id.
-          TiXmlElement *xvalue = xcommand->FirstChildElement("value");
-          if (xvalue != NULL) {
-            PyObject *value = from_xml_value(xvalue);
-            PyObject *result = PyObject_CallMethod
-              (_runner, (char*)"feedValue", (char*)"iOi", unique_id, value, true);
-            Py_DECREF(value);
-            Py_XDECREF(result);
-          } else {
-            PyObject *result = PyObject_CallMethod
-              (_runner, (char*)"feedValue", (char*)"iOi", unique_id, Py_None, false);
-            Py_XDECREF(result);
-          }
-        }
+      } else if (strcmp(cmd, "pyobj") == 0) {
+        // Manipulate or query a python object.  Presumably this
+        // command will want a response.
+        assert(needs_response);
+
+        handle_pyobj_command(xcommand, want_response_id);
         
       } else {
         nout << "Unhandled command " << cmd << "\n";
+        if (needs_response) {
+          // Better send a response.
+          TiXmlDocument doc;
+          TiXmlDeclaration *decl = new TiXmlDeclaration("1.0", "utf-8", "");
+          TiXmlElement *xresponse = new TiXmlElement("response");
+          xresponse->SetAttribute("response_id", want_response_id);
+          doc.LinkEndChild(decl);
+          doc.LinkEndChild(xresponse);
+          nout << "sending " << doc << "\n" << flush;
+          _pipe_write << doc << flush;
+        }
       }
     }
   }
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: P3DPythonRun::handle_pyobj_command
+//       Access: Private
+//  Description: Handles the pyobj command, which queries or modifies
+//               a Python object from the browser scripts.
+////////////////////////////////////////////////////////////////////
+void P3DPythonRun::
+handle_pyobj_command(TiXmlElement *xcommand, int want_response_id) {
+  TiXmlDocument doc;
+  TiXmlDeclaration *decl = new TiXmlDeclaration("1.0", "utf-8", "");
+  TiXmlElement *xresponse = new TiXmlElement("response");
+  xresponse->SetAttribute("response_id", want_response_id);
+  doc.LinkEndChild(decl);
+  doc.LinkEndChild(xresponse);
+
+  const char *op = xcommand->Attribute("op");
+  if (op != NULL) {
+    if (strcmp(op, "get_script_object") == 0) {
+      // Get the toplevel Python object.
+      xresponse->SetAttribute("object", "fooby");
+    }
+  }
+
+  nout << "sending " << doc << "\n" << flush;
+  _pipe_write << doc << flush;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: P3DPythonRun::check_comm
 //       Access: Private

+ 2 - 0
direct/src/plugin/p3dPythonRun.h

@@ -70,6 +70,8 @@ public:
 
 private:
   void handle_command(TiXmlDocument *doc);
+  void handle_pyobj_command(TiXmlElement *xcommand, int want_response_id);
+
   AsyncTask::DoneStatus check_comm(GenericAsyncTask *task);
   static AsyncTask::DoneStatus st_check_comm(GenericAsyncTask *task, void *user_data);
 

+ 117 - 2
direct/src/plugin/p3dSession.cxx

@@ -36,6 +36,10 @@ P3DSession(P3DInstance *inst) {
   _python_version = inst->get_python_version();
 
   _p3dpython_running = false;
+  _next_response_id = 0;
+  _response = NULL;
+  _got_response_id = -1;
+
   _started_read_thread = false;
   _read_thread_continue = false;
 
@@ -210,6 +214,78 @@ send_command(TiXmlDocument *command) {
   }
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: P3DSession::command_and_response
+//       Access: Public
+//  Description: Sends the indicated command to the running Python
+//               process, and waits for a response.  Returns the
+//               newly-allocated response on success, or NULL on
+//               failure.
+//
+//               The command must be a newly-allocated TiXmlDocument;
+//               it will be deleted after it has been delivered to the
+//               process.
+//
+//               This will fail if the python process is not running
+//               or if it suddenly stops.
+////////////////////////////////////////////////////////////////////
+TiXmlDocument *P3DSession::
+command_and_response(TiXmlDocument *command) {
+  if (!_p3dpython_running) {
+    return NULL;
+  }
+
+  int response_id = _next_response_id;
+  ++_next_response_id;
+
+  // Add the "want_response_id" attribute to the toplevel command, so
+  // the sub-process knows we'll be waiting for its response.
+  TiXmlElement *xcommand = command->FirstChildElement("command");
+  assert(xcommand != NULL);
+  xcommand->SetAttribute("want_response_id", response_id);
+
+  _pipe_write << *command << flush;
+  delete command;
+
+  // Now block, waiting for a response to be delivered.  We assume
+  // 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";
+      delete _response;
+      _response = NULL;
+      _got_response_id = -1;
+    }
+
+    if (!_p3dpython_running) {
+      // Hmm, looks like Python has gone away.
+
+      // TODO: make sure _p3dpython_running gets set to false when the
+      // process dies unexpectedly.
+      _response_ready.release();
+      return NULL;
+    }
+
+    _response_ready.wait();
+  }
+  // When we exit the loop, we've found the desired response.
+
+  TiXmlDocument *response = _response;
+  _response = NULL;
+  _got_response_id = -1;
+
+  _response_ready.release();
+
+  nout << "Got response: " << *response << "\n" << flush;
+
+  return response;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: P3DSession::install_progress
 //       Access: Private
@@ -316,6 +392,22 @@ start_p3dpython() {
   }
   _pipe_write << flush;
   _commands.clear();
+
+  // Temp testing code.
+  {
+    TiXmlDocument *doc = new TiXmlDocument;
+    TiXmlDeclaration *decl = new TiXmlDeclaration("1.0", "utf-8", "");
+    TiXmlElement *xcommand = new TiXmlElement("command");
+    xcommand->SetAttribute("cmd", "pyobj");
+    xcommand->SetAttribute("op", "get");
+    doc->LinkEndChild(decl);
+    doc->LinkEndChild(xcommand);
+    TiXmlDocument *response = command_and_response(doc);
+    nout << "response pointer: " << response << "\n";
+    if (response != NULL) {
+      delete response;
+    }
+  }
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -409,10 +501,33 @@ void P3DSession::
 rt_handle_request(TiXmlDocument *doc) {
   nout << "Session got request: " << *doc << "\n" << flush;
 
+  TiXmlElement *xresponse = doc->FirstChildElement("response");
+  if (xresponse != (TiXmlElement *)NULL) {
+    int response_id;
+    if (xresponse->QueryIntAttribute("response_id", &response_id) == TIXML_SUCCESS) {
+      // 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;
+      _response_ready.notify();
+      _response_ready.release();
+      return;
+    }
+  }
+
   TiXmlElement *xrequest = doc->FirstChildElement("request");
   if (xrequest != (TiXmlElement *)NULL) {
-    int instance_id ;
-    if (xrequest->Attribute("instance_id", &instance_id)) {
+    int instance_id;
+    if (xrequest->QueryIntAttribute("instance_id", &instance_id) == TIXML_SUCCESS) {
       // Look up the particular instance this is related to.
       ACQUIRE_LOCK(_instances_lock);
       Instances::const_iterator ii;

+ 9 - 0
direct/src/plugin/p3dSession.h

@@ -18,6 +18,7 @@
 #include "p3d_plugin_common.h"
 #include "handleStream.h"
 #include "p3dPackage.h"
+#include "p3dConditionVar.h"
 #include "get_tinyxml.h"
 
 #include <map>
@@ -47,6 +48,7 @@ public:
   inline int get_num_instances() const;
 
   void send_command(TiXmlDocument *command);
+  TiXmlDocument *command_and_response(TiXmlDocument *command);
 
 private:
   void install_progress(P3DPackage *package, double progress);
@@ -120,6 +122,13 @@ private:
 #endif
   bool _p3dpython_running;
 
+  int _next_response_id;
+
+  // The _response_ready mutex protects this pointer.
+  TiXmlDocument *_response;
+  int _got_response_id;
+  P3DConditionVar _response_ready;
+
   // The remaining members are manipulated by or for the read thread.
   bool _started_read_thread;
   HandleStream _pipe_read;

+ 9 - 9
direct/src/plugin/p3d_plugin_composite1.cxx

@@ -1,20 +1,20 @@
 #include "p3d_plugin.cxx"
+#include "p3dBoolObject.cxx"
+#include "p3dConditionVar.cxx"
 #include "p3dDownload.cxx"
 #include "p3dFileDownload.cxx"
 #include "p3dFileParams.cxx"
+#include "p3dFloatObject.cxx"
 #include "p3dInstance.cxx"
 #include "p3dInstanceManager.cxx"
+#include "p3dIntObject.cxx"
+#include "p3dListObject.cxx"
 #include "p3dMultifileReader.cxx"
 #include "p3dNoneObject.cxx"
+#include "p3dObject.cxx"
 #include "p3dPackage.cxx"
-#include "p3dSplashWindow.cxx"
 #include "p3dSession.cxx"
-#include "p3dWindowParams.cxx"
-#include "p3dWinSplashWindow.cxx"
-#include "p3dObject.cxx"
-#include "p3dBoolObject.cxx"
-#include "p3dIntObject.cxx"
-#include "p3dFloatObject.cxx"
-#include "p3dListObject.cxx"
+#include "p3dSplashWindow.cxx"
 #include "p3dStringObject.cxx"
-
+#include "p3dWinSplashWindow.cxx"
+#include "p3dWindowParams.cxx"