Browse Source

much better object model

David Rose 16 years ago
parent
commit
4d2ffd8e77

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

@@ -40,6 +40,7 @@
     p3dObject.h p3dObject.I \
     p3dObject.h p3dObject.I \
     p3dPackage.h p3dPackage.I \
     p3dPackage.h p3dPackage.I \
     p3dPythonObject.h \
     p3dPythonObject.h \
+    p3dReferenceCount.h p3dReferenceCount.I \
     p3dSession.h p3dSession.I \
     p3dSession.h p3dSession.I \
     p3dSplashWindow.h p3dSplashWindow.I \
     p3dSplashWindow.h p3dSplashWindow.I \
     p3dStringObject.h \
     p3dStringObject.h \
@@ -64,6 +65,7 @@
     p3dObject.cxx \
     p3dObject.cxx \
     p3dPackage.cxx \
     p3dPackage.cxx \
     p3dPythonObject.cxx \
     p3dPythonObject.cxx \
+    p3dReferenceCount.cxx \
     p3dSession.cxx \
     p3dSession.cxx \
     p3dSplashWindow.cxx \
     p3dSplashWindow.cxx \
     p3dStringObject.cxx \
     p3dStringObject.cxx \

+ 252 - 211
direct/src/plugin/p3dInstance.cxx

@@ -71,6 +71,9 @@ P3DInstance(P3D_request_ready_func *func, void *user_data) :
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 P3DInstance::
 P3DInstance::
 ~P3DInstance() {
 ~P3DInstance() {
+  // TODO: maybe this should be a reference-counted object, so we
+  // don't delete it too soon.
+
   assert(_session == NULL);
   assert(_session == NULL);
 
 
   P3D_OBJECT_XDECREF(_browser_script_object);
   P3D_OBJECT_XDECREF(_browser_script_object);
@@ -90,7 +93,8 @@ P3DInstance::
     _splash_window = NULL;
     _splash_window = NULL;
   }
   }
 
 
-  // TODO: empty _pending_requests queue and _downloads map.
+  // TODO: empty _raw_requests and _baked_requests queues, and
+  // _downloads map.
 
 
   // TODO: Is it possible for someone to delete an instance while a
   // TODO: Is it possible for someone to delete an instance while a
   // download is still running?  Who will crash when this happens?
   // download is still running?  Who will crash when this happens?
@@ -239,10 +243,7 @@ set_browser_script_object(P3D_object *browser_script_object) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 bool P3DInstance::
 bool P3DInstance::
 has_request() {
 has_request() {
-  ACQUIRE_LOCK(_request_lock);
-  bool any_requests = !_pending_requests.empty();
-  RELEASE_LOCK(_request_lock);
-  return any_requests;
+  return _request_pending;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -256,51 +257,104 @@ has_request() {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 P3D_request *P3DInstance::
 P3D_request *P3DInstance::
 get_request() {
 get_request() {
-  P3D_request *result = NULL;
-  ACQUIRE_LOCK(_request_lock);
-  if (!_pending_requests.empty()) {
-    result = _pending_requests.front();
-    _pending_requests.pop_front();
-    _request_pending = !_pending_requests.empty();
+  bake_requests();
+  if (_baked_requests.empty()) {
+    // No requests ready.
+    _request_pending = false;
+    return NULL;
   }
   }
-  RELEASE_LOCK(_request_lock);
 
 
-  if (result != NULL) {
-    switch (result->_request_type) {
-    case P3D_RT_notify:
-      handle_notify_request(result);
-      break;
-
-    case P3D_RT_script:
-      handle_script_request(result);
-      // Completely eat this request; don't return it to the caller.
-      finish_request(result, true);
-      return get_request();
-
-    default:
-      // Other kinds of requests don't require special handling at
-      // this level; pass it up unmolested.
-      break;
+  P3D_request *request = _baked_requests.front();
+  _baked_requests.pop_front();
+  _request_pending = !_baked_requests.empty();
+  
+  return request;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: P3DInstance::bake_requests
+//       Access: Public
+//  Description: Copies requests from the _raw_requests queue, which
+//               is built up in one or more sub-threads, into the
+//               _baked_requests queue, which is publicly presented
+//               to the browser.  Along the way, some requests (like
+//               script requests) are handled immediately.
+//
+//               At the end of this call, _baked_requests will contain
+//               the current set of requests pending for the browser.
+//
+//               This method should only be called in the main thread.
+////////////////////////////////////////////////////////////////////
+void P3DInstance::
+bake_requests() {
+  while (true) {
+    // Get the latest request from the read thread.
+    TiXmlDocument *doc = NULL;
+    ACQUIRE_LOCK(_request_lock);
+    if (!_raw_requests.empty()) {
+      doc = _raw_requests.front();
+      _raw_requests.pop_front();
+    }
+    RELEASE_LOCK(_request_lock);
+    
+    if (doc == NULL) {
+      // No more requests to process right now.
+      return;
+    }
+    nout << "received: " << *doc << "\n" << flush;
+    
+    // Now we've got a request in XML form; convert it to P3D_request
+    // form.
+    TiXmlElement *xrequest = doc->FirstChildElement("request");
+    assert(xrequest != (TiXmlElement *)NULL);
+    P3D_request *request = make_p3d_request(xrequest);
+    delete doc;
+    
+    if (request != NULL) {
+      _baked_requests.push_back(request);
     }
     }
   }
   }
+}
 
 
-  return result;
+////////////////////////////////////////////////////////////////////
+//     Function: P3DInstance::add_raw_request
+//       Access: Public
+//  Description: May be called in any thread to add a new XML request
+//               to the pending_request queue for this instance.  The
+//               XML document will be deleted when the request is
+//               eventually handled.
+////////////////////////////////////////////////////////////////////
+void P3DInstance::
+add_raw_request(TiXmlDocument *doc) {
+  ACQUIRE_LOCK(_request_lock);
+  _raw_requests.push_back(doc);
+  _request_pending = true;
+  RELEASE_LOCK(_request_lock);
+
+  // We don't decode the XML yet, since we might be running in any
+  // thread here.  We'll decode it in the main thread, where it's
+  // safe.
+
+  // Tell the world we've got a new request.
+  P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
+  inst_mgr->signal_request_ready(this);
+  _session->signal_request_ready(this);
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: P3DInstance::add_request
+//     Function: P3DInstance::add_baked_request
 //       Access: Public
 //       Access: Public
-//  Description: May be called in any thread to add a new P3D_request
-//               to the pending_request queue for this instance.
+//  Description: May be called in the main thread only to add a new
+//               request to the baked_request queue.  This request
+//               queue is directly passed on the browser without
+//               further processing at this level.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void P3DInstance::
 void P3DInstance::
-add_request(P3D_request *request) {
+add_baked_request(P3D_request *request) {
   request->_instance = this;
   request->_instance = this;
 
 
-  ACQUIRE_LOCK(_request_lock);
-  _pending_requests.push_back(request);
+  _baked_requests.push_back(request);
   _request_pending = true;
   _request_pending = true;
-  RELEASE_LOCK(_request_lock);
 
 
   // Tell the world we've got a new request.
   // Tell the world we've got a new request.
   P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
   P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
@@ -335,18 +389,6 @@ finish_request(P3D_request *request, bool handled) {
   case P3D_RT_notify:
   case P3D_RT_notify:
     free((char *)request->_request._notify._message);
     free((char *)request->_request._notify._message);
     break;
     break;
-
-  case P3D_RT_script:
-    {
-      P3D_OBJECT_DECREF(request->_request._script._object);
-      if (request->_request._script._property_name != NULL) {
-        free((char *)request->_request._script._property_name);
-      }
-      for (int i = 0; i < request->_request._script._num_values; ++i) {
-        P3D_OBJECT_DECREF(request->_request._script._values[i]);
-      }
-    }
-    break;
   }
   }
 
 
   delete request;
   delete request;
@@ -430,7 +472,7 @@ start_download(P3DDownload *download) {
   request->_request._get_url._url = strdup(download->get_url().c_str());
   request->_request._get_url._url = strdup(download->get_url().c_str());
   request->_request._get_url._unique_id = download_id;
   request->_request._get_url._unique_id = download_id;
 
 
-  add_request(request);
+  add_baked_request(request);
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -446,7 +488,7 @@ request_stop() {
     _requested_stop = true;
     _requested_stop = true;
     P3D_request *request = new P3D_request;
     P3D_request *request = new P3D_request;
     request->_request_type = P3D_RT_stop;
     request->_request_type = P3D_RT_stop;
-    add_request(request);
+    add_baked_request(request);
   }
   }
 }
 }
 
 
@@ -497,6 +539,93 @@ send_browser_script_object() {
   _session->send_command(doc);
   _session->send_command(doc);
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: P3DInstance::make_p3d_request
+//       Access: Private
+//  Description: Creates a new P3D_request structure from the XML.
+//               Returns NULL if no request is needed.
+////////////////////////////////////////////////////////////////////
+P3D_request *P3DInstance::
+make_p3d_request(TiXmlElement *xrequest) {
+  P3D_request *request = NULL;
+
+  const char *rtype = xrequest->Attribute("rtype");
+  if (rtype != NULL) {
+    if (strcmp(rtype, "notify") == 0) {
+      const char *message = xrequest->Attribute("message");
+      if (message != NULL) {
+        request = new P3D_request;
+        request->_request_type = P3D_RT_notify;
+        request->_request._notify._message = strdup(message);
+      }
+
+    } else if (strcmp(rtype, "script") == 0) {
+      // We don't actually build a P3D_request for a script request;
+      // we always just handle it immediately.
+      const char *operation = xrequest->Attribute("operation");
+      TiXmlElement *xobject = xrequest->FirstChildElement("object");
+      const char *property_name = xrequest->Attribute("property_name");
+      int needs_response = 0;
+      xrequest->Attribute("needs_response", &needs_response);
+      int unique_id = 0;
+      xrequest->Attribute("unique_id", &unique_id);
+        
+      vector<P3D_object *> values;
+      TiXmlElement *xvalue = xrequest->FirstChildElement("value");
+      while (xvalue != NULL) {
+        P3D_object *value = _session->xml_to_p3dobj(xvalue);
+        values.push_back(value);
+        xvalue = xvalue->NextSiblingElement("value");
+      }
+      
+      P3D_object **values_ptr = NULL;
+      if (!values.empty()) {
+        values_ptr = &values[0];
+      }
+
+      if (operation != NULL && xobject != NULL) {
+        P3D_object *object = _session->xml_to_p3dobj(xobject);
+
+        if (property_name == NULL) {
+          property_name = "";
+        }
+
+        handle_script_request(operation, object, property_name,
+                              values_ptr, values.size(),
+                              (needs_response != 0), unique_id);
+      }
+
+      for (size_t i = 0; i < values.size(); ++i) {
+        P3D_OBJECT_DECREF(values[i]);
+      }
+
+    } else if (strcmp(rtype, "notify") == 0) {
+      const char *message = xrequest->Attribute("message");
+      if (message != NULL) {
+        request = new P3D_request;
+        request->_request_type = P3D_RT_notify;
+        request->_request._notify._message = strdup(message);
+        handle_notify_request(message);
+      }
+
+    } else if (strcmp(rtype, "drop_p3dobj") == 0) {
+      int object_id;
+      if (xrequest->QueryIntAttribute("object_id", &object_id) == TIXML_SUCCESS) {
+        // We no longer need to keep this reference.
+        _session->drop_p3dobj(object_id);
+      }
+
+    } else {
+      nout << "Ignoring request of type " << rtype << "\n";
+    }
+  }
+
+  if (request != NULL) {
+    request->_instance = this;
+  }
+  return request;
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: P3DInstance::handle_notify_request
 //     Function: P3DInstance::handle_notify_request
 //       Access: Private
 //       Access: Private
@@ -504,13 +633,10 @@ send_browser_script_object() {
 //               received from the subprocess.
 //               received from the subprocess.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void P3DInstance::
 void P3DInstance::
-handle_notify_request(P3D_request *request) {
-  assert(request->_request_type == P3D_RT_notify);
-
+handle_notify_request(const string &message) {
   // We look for certain notify events that have particular meaning
   // We look for certain notify events that have particular meaning
   // to this instance.
   // to this instance.
-  const char *message = request->_request._notify._message;
-  if (strcmp(message, "onwindowopen") == 0) {
+  if (message == "onwindowopen") {
     // The process told us that it just succesfully opened its
     // The process told us that it just succesfully opened its
     // window.  Tear down the splash window.
     // window.  Tear down the splash window.
     _instance_window_opened = true;
     _instance_window_opened = true;
@@ -528,172 +654,87 @@ handle_notify_request(P3D_request *request) {
 //               received from the subprocess.
 //               received from the subprocess.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void P3DInstance::
 void P3DInstance::
-handle_script_request(P3D_request *request) {
-  assert(request->_request_type == P3D_RT_script);
-
-  P3D_object *object = request->_request._script._object;
-  bool needs_response = request->_request._script._needs_response;
-  int unique_id = request->_request._script._unique_id;
-  switch (request->_request._script._op) {
-  case P3D_SO_get_property:
-    {
-      P3D_object *result = P3D_OBJECT_GET_PROPERTY(object, request->_request._script._property_name);
-
-      // We've got the property value; feed it back down to the
-      // subprocess.
-      TiXmlDocument *doc = new TiXmlDocument;
-      TiXmlDeclaration *decl = new TiXmlDeclaration("1.0", "utf-8", "");
-      TiXmlElement *xcommand = new TiXmlElement("command");
-      xcommand->SetAttribute("cmd", "script_response");
-      xcommand->SetAttribute("unique_id", unique_id);
-      
-      doc->LinkEndChild(decl);
-      doc->LinkEndChild(xcommand);
-      if (result != NULL) {
-        xcommand->LinkEndChild(_session->p3dobj_to_xml(result));
-        P3D_OBJECT_DECREF(result);
-      }
+handle_script_request(const string &operation, P3D_object *object, 
+                      const string &property_name,
+                      P3D_object *values[], int num_values,
+                      bool needs_response, int unique_id) {
 
 
-      if (needs_response) {
-        _session->send_command(doc);
-      } else {
-        delete doc;
-      }
-    }
-    break;
-
-  case P3D_SO_set_property:
-    {
-      bool result;
-      if (request->_request._script._num_values == 1) {
-        result = P3D_OBJECT_SET_PROPERTY(object, request->_request._script._property_name,
-                                         request->_request._script._values[0]);
-      } else {
-        // Wrong number of values.  Error.
-        result = false;
-      }
+  TiXmlDocument *doc = new TiXmlDocument;
+  TiXmlDeclaration *decl = new TiXmlDeclaration("1.0", "utf-8", "");
+  TiXmlElement *xcommand = new TiXmlElement("command");
+  xcommand->SetAttribute("cmd", "script_response");
+  xcommand->SetAttribute("unique_id", unique_id);
+  
+  doc->LinkEndChild(decl);
+  doc->LinkEndChild(xcommand);
 
 
-      // Feed the result back down to the subprocess.
-      TiXmlDocument *doc = new TiXmlDocument;
-      TiXmlDeclaration *decl = new TiXmlDeclaration("1.0", "utf-8", "");
-      TiXmlElement *xcommand = new TiXmlElement("command");
-      xcommand->SetAttribute("cmd", "script_response");
-      xcommand->SetAttribute("unique_id", unique_id);
-      
-      doc->LinkEndChild(decl);
-      doc->LinkEndChild(xcommand);
+  if (operation == "get_property") {
+    P3D_object *result = P3D_OBJECT_GET_PROPERTY(object, property_name.c_str());
 
 
-      TiXmlElement *xvalue = new TiXmlElement("value");
-      xvalue->SetAttribute("type", "bool");
-      xvalue->SetAttribute("value", (int)result);
-      xcommand->LinkEndChild(xvalue);
-      
-      if (needs_response) {
-        _session->send_command(doc);
-      } else {
-        delete doc;
-      }
+    // We've got the property value; feed it back down to the
+    // subprocess.
+    
+    if (result != NULL) {
+      xcommand->LinkEndChild(_session->p3dobj_to_xml(result));
+      P3D_OBJECT_DECREF(result);
     }
     }
-    break;
-
-  case P3D_SO_del_property:
-    {
-      bool result = P3D_OBJECT_SET_PROPERTY(object, request->_request._script._property_name,
-                                            NULL);
-
-      // Feed the result back down to the subprocess.
-      TiXmlDocument *doc = new TiXmlDocument;
-      TiXmlDeclaration *decl = new TiXmlDeclaration("1.0", "utf-8", "");
-      TiXmlElement *xcommand = new TiXmlElement("command");
-      xcommand->SetAttribute("cmd", "script_response");
-      xcommand->SetAttribute("unique_id", unique_id);
-      
-      doc->LinkEndChild(decl);
-      doc->LinkEndChild(xcommand);
 
 
-      TiXmlElement *xvalue = new TiXmlElement("value");
-      xvalue->SetAttribute("type", "bool");
-      xvalue->SetAttribute("value", (int)result);
-      xcommand->LinkEndChild(xvalue);
-      
-      if (needs_response) {
-        _session->send_command(doc);
-      } else {
-        delete doc;
-      }
+  } else if (operation == "set_property") {
+    bool result;
+    if (num_values == 1) {
+      result = P3D_OBJECT_SET_PROPERTY(object, property_name.c_str(), values[0]);
+    } else {
+      // Wrong number of values.  Error.
+      result = false;
     }
     }
-    break;
-
-  case P3D_SO_call:
-    {
-      P3D_object *result = 
-        P3D_OBJECT_CALL(object, request->_request._script._property_name,
-                        request->_request._script._values,
-                        request->_request._script._num_values);
-
-      // Feed the result back down to the subprocess.
-      TiXmlDocument *doc = new TiXmlDocument;
-      TiXmlDeclaration *decl = new TiXmlDeclaration("1.0", "utf-8", "");
-      TiXmlElement *xcommand = new TiXmlElement("command");
-      xcommand->SetAttribute("cmd", "script_response");
-      xcommand->SetAttribute("unique_id", unique_id);
-      
-      doc->LinkEndChild(decl);
-      doc->LinkEndChild(xcommand);
+    
+    TiXmlElement *xvalue = new TiXmlElement("value");
+    xvalue->SetAttribute("type", "bool");
+    xvalue->SetAttribute("value", (int)result);
+    xcommand->LinkEndChild(xvalue);
 
 
-      if (result != NULL) {
-        xcommand->LinkEndChild(_session->p3dobj_to_xml(result));
-        P3D_OBJECT_DECREF(result);
-      }
-      
-      if (needs_response) {
-        _session->send_command(doc);
-      } else {
-        delete doc;
-      }
+  } else if (operation == "del_property") {
+    bool result = P3D_OBJECT_SET_PROPERTY(object, property_name.c_str(), NULL);
+    
+    TiXmlElement *xvalue = new TiXmlElement("value");
+    xvalue->SetAttribute("type", "bool");
+    xvalue->SetAttribute("value", (int)result);
+    xcommand->LinkEndChild(xvalue);
+
+  } else if (operation == "call") {
+    P3D_object *result =
+      P3D_OBJECT_CALL(object, property_name.c_str(), values, num_values);
+    
+    if (result != NULL) {
+      xcommand->LinkEndChild(_session->p3dobj_to_xml(result));
+      P3D_OBJECT_DECREF(result);
     }
     }
-    break;
 
 
-  case P3D_SO_eval:
-    {
-      P3D_object *result;
-      if (request->_request._script._num_values == 1) {
-        P3D_object *expression = request->_request._script._values[0];
-        int size = P3D_OBJECT_GET_STRING(expression, NULL, 0);
-        char *buffer = new char[size + 1];
-        P3D_OBJECT_GET_STRING(expression, buffer, size + 1);
-        result = P3D_OBJECT_EVAL(object, buffer);
-        logfile << " eval " << *object << ": " << buffer << ", result = " << result << "\n";
-        delete[] buffer;
-      } else {
-        // Wrong number of values.  Error.
-        result = NULL;
-      }
-
-      // Feed the result back down to the subprocess.
-      TiXmlDocument *doc = new TiXmlDocument;
-      TiXmlDeclaration *decl = new TiXmlDeclaration("1.0", "utf-8", "");
-      TiXmlElement *xcommand = new TiXmlElement("command");
-      xcommand->SetAttribute("cmd", "script_response");
-      xcommand->SetAttribute("unique_id", unique_id);
-      
-      doc->LinkEndChild(decl);
-      doc->LinkEndChild(xcommand);
-
-      if (result != NULL) {
-        xcommand->LinkEndChild(_session->p3dobj_to_xml(result));
-        P3D_OBJECT_DECREF(result);
-      }
-
-      logfile << "eval  response: " << *doc << "\n";
-      
-      if (needs_response) {
-        _session->send_command(doc);
-      } else {
-        delete doc;
-      }
+  } else if (operation == "eval") {
+    P3D_object *result;
+    if (num_values == 1) {
+      P3D_object *expression = values[0];
+      int size = P3D_OBJECT_GET_STRING(expression, NULL, 0);
+      char *buffer = new char[size + 1];
+      P3D_OBJECT_GET_STRING(expression, buffer, size + 1);
+      result = P3D_OBJECT_EVAL(object, buffer);
+      logfile << " eval " << *object << ": " << buffer << ", result = " << result << "\n";
+      delete[] buffer;
+    } else {
+      // Wrong number of values.  Error.
+      result = NULL;
     }
     }
-    break;
+    
+    if (result != NULL) {
+      xcommand->LinkEndChild(_session->p3dobj_to_xml(result));
+      P3D_OBJECT_DECREF(result);
+    }
+  }
+
+  if (needs_response) {
+    _session->send_command(doc);
+  } else {
+    delete doc;
   }
   }
 }
 }
 
 

+ 23 - 6
direct/src/plugin/p3dInstance.h

@@ -19,6 +19,7 @@
 #include "p3dFileDownload.h"
 #include "p3dFileDownload.h"
 #include "p3dFileParams.h"
 #include "p3dFileParams.h"
 #include "p3dWindowParams.h"
 #include "p3dWindowParams.h"
+#include "p3dReferenceCount.h"
 #include "get_tinyxml.h"
 #include "get_tinyxml.h"
 
 
 #include <deque>
 #include <deque>
@@ -35,7 +36,7 @@ class P3DObject;
 // Description : This is an instance of a Panda3D window, as seen in
 // Description : This is an instance of a Panda3D window, as seen in
 //               the parent-level process.
 //               the parent-level process.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-class P3DInstance : public P3D_instance {
+class P3DInstance : public P3D_instance, public P3DReferenceCount {
 public:
 public:
   P3DInstance(P3D_request_ready_func *func, void *user_data);
   P3DInstance(P3D_request_ready_func *func, void *user_data);
   ~P3DInstance();
   ~P3DInstance();
@@ -51,7 +52,9 @@ public:
 
 
   bool has_request();
   bool has_request();
   P3D_request *get_request();
   P3D_request *get_request();
-  void add_request(P3D_request *request);
+  void bake_requests();
+  void add_raw_request(TiXmlDocument *doc);
+  void add_baked_request(P3D_request *request);
   void finish_request(P3D_request *request, bool handled);
   void finish_request(P3D_request *request, bool handled);
 
 
   bool feed_url_stream(int unique_id,
   bool feed_url_stream(int unique_id,
@@ -88,8 +91,12 @@ private:
   };
   };
 
 
   void send_browser_script_object();
   void send_browser_script_object();
-  void handle_notify_request(P3D_request *request);
-  void handle_script_request(P3D_request *request);
+  P3D_request *make_p3d_request(TiXmlElement *xrequest);
+  void handle_notify_request(const string &message);
+  void handle_script_request(const string &operation, P3D_object *object, 
+                             const string &property_name,
+                             P3D_object *values[], int num_values,
+                             bool needs_response, int unique_id);
   void make_splash_window();
   void make_splash_window();
   void install_progress(P3DPackage *package, double progress);
   void install_progress(P3DPackage *package, double progress);
 
 
@@ -105,7 +112,10 @@ private:
   int _instance_id;
   int _instance_id;
   string _session_key;
   string _session_key;
   string _python_version;
   string _python_version;
+
+  // Not ref-counted: session is the parent.
   P3DSession *_session;
   P3DSession *_session;
+
   P3DSplashWindow *_splash_window;
   P3DSplashWindow *_splash_window;
   bool _instance_window_opened;
   bool _instance_window_opened;
 
 
@@ -115,11 +125,18 @@ private:
   typedef map<int, P3DDownload *> Downloads;
   typedef map<int, P3DDownload *> Downloads;
   Downloads _downloads;
   Downloads _downloads;
 
 
+  // The _raw_requests queue might be filled up by the read thread, so
+  // we protect it in a lock.
   LOCK _request_lock;
   LOCK _request_lock;
-  typedef deque<P3D_request *> Requests;
-  Requests _pending_requests;
+  typedef deque<TiXmlDocument *> RawRequests;
+  RawRequests _raw_requests;
   bool _requested_stop;
   bool _requested_stop;
 
 
+  // The _baked_requests queue is only touched in the main thread; no
+  // lock needed.
+  typedef deque<P3D_request *> BakedRequests;
+  BakedRequests _baked_requests;
+
   static int _next_instance_id;
   static int _next_instance_id;
 
 
   friend class P3DSession;
   friend class P3DSession;

+ 5 - 2
direct/src/plugin/p3dInstanceManager.cxx

@@ -138,6 +138,7 @@ initialize() {
 P3DInstance *P3DInstanceManager::
 P3DInstance *P3DInstanceManager::
 create_instance(P3D_request_ready_func *func, void *user_data) {
 create_instance(P3D_request_ready_func *func, void *user_data) {
   P3DInstance *inst = new P3DInstance(func, user_data);
   P3DInstance *inst = new P3DInstance(func, user_data);
+  inst->ref();
   _instances.insert(inst);
   _instances.insert(inst);
 
 
   return inst;
   return inst;
@@ -164,6 +165,7 @@ start_instance(P3DInstance *inst, const string &p3d_filename,
   Sessions::iterator si = _sessions.find(inst->get_session_key());
   Sessions::iterator si = _sessions.find(inst->get_session_key());
   if (si == _sessions.end()) {
   if (si == _sessions.end()) {
     session = new P3DSession(inst);
     session = new P3DSession(inst);
+    session->ref();
     bool inserted = _sessions.insert(Sessions::value_type(session->get_session_key(), session)).second;
     bool inserted = _sessions.insert(Sessions::value_type(session->get_session_key(), session)).second;
     assert(inserted);
     assert(inserted);
   } else {
   } else {
@@ -197,10 +199,11 @@ finish_instance(P3DInstance *inst) {
   // session.
   // session.
   if (session->get_num_instances() == 0) {
   if (session->get_num_instances() == 0) {
     _sessions.erase(session->get_session_key());
     _sessions.erase(session->get_session_key());
-    delete session;
+    session->shutdown();
+    unref_delete(session);
   }
   }
 
 
-  delete inst;
+  unref_delete(inst);
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////

+ 8 - 1
direct/src/plugin/p3dPythonObject.cxx

@@ -24,6 +24,8 @@ P3DPythonObject(P3DSession *session, int object_id) :
   _session(session),
   _session(session),
   _object_id(object_id)
   _object_id(object_id)
 {
 {
+  _session->ref();
+  nout << "new P3DPythonObject " << this << " : " << object_id << "\n" << flush;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -33,7 +35,12 @@ P3DPythonObject(P3DSession *session, int object_id) :
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 P3DPythonObject::
 P3DPythonObject::
 ~P3DPythonObject() {
 ~P3DPythonObject() {
-  // TODO.
+  nout << "del P3DPythonObject " << this << " : " << _object_id << "\n";
+  // When the P3DPythonObject wrapper goes away, we have to inform the
+  // child process that we no longer need the corresponding PyObject
+  // to be kept around.
+  _session->drop_pyobj(_object_id);
+  unref_delete(_session);
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////

+ 25 - 0
direct/src/plugin/p3dPythonRun.cxx

@@ -259,6 +259,18 @@ handle_command(TiXmlDocument *doc) {
         // Response from a script request.
         // Response from a script request.
         assert(!needs_response);
         assert(!needs_response);
         nout << "Ignoring unexpected script_response\n";
         nout << "Ignoring unexpected script_response\n";
+
+      } else if (strcmp(cmd, "drop_pyobj") == 0) {
+        int object_id;
+        if (xcommand->QueryIntAttribute("object_id", &object_id) == TIXML_SUCCESS) {
+          nout << "got drop_pyobj(" << object_id << ")\n" << flush;
+          SentObjects::iterator si = _sent_objects.find(object_id);
+          if (si != _sent_objects.end()) {
+            PyObject *obj = (*si).second;
+            Py_DECREF(obj);
+            _sent_objects.erase(si);
+          }
+        }
         
         
       } else {
       } else {
         nout << "Unhandled command " << cmd << "\n";
         nout << "Unhandled command " << cmd << "\n";
@@ -666,6 +678,19 @@ py_request_func(PyObject *args) {
     nout << "sent: " << doc << "\n" << flush;
     nout << "sent: " << doc << "\n" << flush;
     _pipe_write << doc << flush;
     _pipe_write << doc << flush;
 
 
+  } else if (strcmp(request_type, "drop_p3dobj") == 0) {
+    // Release a particular P3D_object that we were holding a
+    // reference to.
+    int object_id;
+    if (!PyArg_ParseTuple(extra_args, "i", &object_id)) {
+      return NULL;
+    }
+    nout << "got drop_p3dobj(" << object_id << ")\n" << flush;
+
+    xrequest->SetAttribute("object_id", object_id);
+    nout << "sent: " << doc << "\n" << flush;
+    _pipe_write << doc << flush;
+
   } else {
   } else {
     string message = string("Unsupported request type: ") + string(request_type);
     string message = string("Unsupported request type: ") + string(request_type);
     PyErr_SetString(PyExc_ValueError, message.c_str());
     PyErr_SetString(PyExc_ValueError, message.c_str());

+ 72 - 0
direct/src/plugin/p3dReferenceCount.I

@@ -0,0 +1,72 @@
+// Filename: p3dReferenceCount.I
+// Created by:  drose (09Jul09)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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."
+//
+////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: P3DReferenceCount::Constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+inline P3DReferenceCount::
+P3DReferenceCount() {
+  _ref_count = 0;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: P3DReferenceCount::Destructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+inline P3DReferenceCount::
+~P3DReferenceCount() {
+  assert(_ref_count == 0);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: P3DReferenceCount::ref
+//       Access: Public
+//  Description: Explicitly increments the reference count.
+////////////////////////////////////////////////////////////////////
+inline void P3DReferenceCount::
+ref() const {
+  ++((P3DReferenceCount *)this)->_ref_count;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: P3DReferenceCount::unref
+//       Access: Published, Virtual
+//  Description: Explicitly decrements the reference count.  Usually,
+//               you should call unref_delete() instead.
+//
+//               The return value is true if the new reference count
+//               is nonzero, false if it is zero.
+////////////////////////////////////////////////////////////////////
+inline bool P3DReferenceCount::
+unref() const {
+  return --(((P3DReferenceCount *)this)->_ref_count) != 0;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: unref_delete
+//  Description: This global helper function will unref the given
+//               P3DReferenceCount object, and if the reference count
+//               reaches zero, automatically delete it.
+////////////////////////////////////////////////////////////////////
+template<class RefCountType>
+inline void
+unref_delete(RefCountType *ptr) {
+  if (!ptr->unref()) {
+    delete ptr;
+  } 
+}

+ 15 - 0
direct/src/plugin/p3dReferenceCount.cxx

@@ -0,0 +1,15 @@
+// Filename: p3dReferenceCount.cxx
+// Created by:  drose (09Jul09)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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 "p3dReferenceCount.h"

+ 44 - 0
direct/src/plugin/p3dReferenceCount.h

@@ -0,0 +1,44 @@
+// Filename: p3dReferenceCount.h
+// Created by:  drose (09Jul09)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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 P3DREFERENCECOUNT_H
+#define P3DREFERENCECOUNT_H
+
+#include "p3d_plugin_common.h"
+
+////////////////////////////////////////////////////////////////////
+//       Class : P3DReferenceCount
+// Description : A base class for reference-counted objects in this
+//               module.  We follow the Panda convention, rather than
+//               the Pythong convention: the reference count of a new
+//               object is initially 0.
+////////////////////////////////////////////////////////////////////
+class P3DReferenceCount {
+public:
+  inline P3DReferenceCount();
+  inline ~P3DReferenceCount();
+
+  inline void ref() const;
+  inline bool unref() const;
+
+private:
+  int _ref_count;
+};
+
+template<class RefCountType>
+inline void unref_delete(RefCountType *ptr);
+
+#include "p3dReferenceCount.I"
+
+#endif

+ 96 - 95
direct/src/plugin/p3dSession.cxx

@@ -69,14 +69,26 @@ P3DSession(P3DInstance *inst) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: P3DSession::Destructor
 //     Function: P3DSession::Destructor
 //       Access: Public
 //       Access: Public
-//  Description: Terminates the session by shutting down Python and
-//               stopping the subprocess.
+//  Description: 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 P3DSession::
 P3DSession::
 ~P3DSession() {
 ~P3DSession() {
+  assert(!_p3dpython_running);
+  DESTROY_LOCK(_instances_lock);  
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: P3DSession::shutdown
+//       Access: Public
+//  Description: Terminates the session by shutting down Python and
+//               stopping the subprocess.
+////////////////////////////////////////////////////////////////////
+void P3DSession::
+shutdown() {
   if (_panda3d_callback != NULL) {
   if (_panda3d_callback != NULL) {
     _panda3d->cancel_callback(_panda3d_callback);
     _panda3d->cancel_callback(_panda3d_callback);
     delete _panda3d_callback;
     delete _panda3d_callback;
+    _panda3d_callback = NULL;
   }
   }
 
 
   if (_p3dpython_running) {
   if (_p3dpython_running) {
@@ -106,12 +118,13 @@ P3DSession::
     }
     }
 
 
     CloseHandle(_p3dpython_handle);
     CloseHandle(_p3dpython_handle);
-
 #else  // _WIN32
 #else  // _WIN32
 
 
     // TODO: posix kill().
     // TODO: posix kill().
 
 
 #endif  // _WIN32
 #endif  // _WIN32
+
+    _p3dpython_running = false;
   }
   }
 
 
   // If there are any leftover commands in the queue (presumably
   // If there are any leftover commands in the queue (presumably
@@ -124,7 +137,6 @@ P3DSession::
   _commands.clear();
   _commands.clear();
 
 
   join_read_thread();
   join_read_thread();
-  DESTROY_LOCK(_instances_lock);
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -143,6 +155,7 @@ start_instance(P3DInstance *inst) {
   assert(inst->get_session_key() == _session_key);
   assert(inst->get_session_key() == _session_key);
   assert(inst->get_python_version() == _python_version);
   assert(inst->get_python_version() == _python_version);
 
 
+  inst->ref();
   ACQUIRE_LOCK(_instances_lock);
   ACQUIRE_LOCK(_instances_lock);
   inst->_session = this;
   inst->_session = this;
   bool inserted = _instances.insert(Instances::value_type(inst->get_instance_id(), inst)).second;
   bool inserted = _instances.insert(Instances::value_type(inst->get_instance_id(), inst)).second;
@@ -200,6 +213,7 @@ terminate_instance(P3DInstance *inst) {
     _instances.erase(inst->get_instance_id());
     _instances.erase(inst->get_instance_id());
   }
   }
   RELEASE_LOCK(_instances_lock);
   RELEASE_LOCK(_instances_lock);
+  unref_delete(inst);
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -284,6 +298,16 @@ command_and_response(TiXmlDocument *command) {
       return NULL;
       return NULL;
     }
     }
 
 
+    // Make sure we bake requests while we are waiting, to process
+    // 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.)
+    Instances::iterator ii;
+    for (ii = _instances.begin(); ii != _instances.end(); ++ii) {
+      P3DInstance *inst = (*ii).second;
+      inst->bake_requests();
+    }
+
 #ifdef _WIN32
 #ifdef _WIN32
     // Make sure we process the Windows event loop while we're
     // Make sure we process the Windows event loop while we're
     // waiting, or everything that depends on Windows messages--in
     // waiting, or everything that depends on Windows messages--in
@@ -370,6 +394,7 @@ xml_to_p3dobj(const TiXmlElement *xvalue) {
       }
       }
 
 
       P3D_object *obj = (*si).second;
       P3D_object *obj = (*si).second;
+
       P3D_OBJECT_INCREF(obj);
       P3D_OBJECT_INCREF(obj);
       return obj;
       return obj;
     }
     }
@@ -472,6 +497,69 @@ p3dobj_to_xml(P3D_object *obj) {
   return xvalue;
   return xvalue;
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: P3DSession::signal_request_ready
+//       Access: Public
+//  Description: May be called in any thread to indicate that a new
+//               P3D_request is available in the indicated instance.
+////////////////////////////////////////////////////////////////////
+void P3DSession::
+signal_request_ready(P3DInstance *inst) {
+  // Since a new request might require baking, we should wake up a
+  // blocked command_and_request() process, so the main thread can go
+  // back and bake the new request.
+
+  // Technically, a response isn't really ready now, but we still need
+  // the main thread to wake up and look around for a bit.
+  _response_ready.acquire();
+  _response_ready.notify();
+  _response_ready.release();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: P3DSession::drop_pyobj
+//       Access: Public
+//  Description: If the session is still active, issues the command to
+//               the child process to release the indicated PyObject
+//               from its table.  This is intended to be called
+//               strictly by the P3DPythonObject destructor.
+////////////////////////////////////////////////////////////////////
+void P3DSession::
+drop_pyobj(int object_id) {
+  nout << "got drop_pyobj(" << object_id << ")\n" << flush;
+  if (_p3dpython_running) {
+    TiXmlDocument doc;
+    TiXmlDeclaration *decl = new TiXmlDeclaration("1.0", "utf-8", "");
+    TiXmlElement *xcommand = new TiXmlElement("command");
+    xcommand->SetAttribute("cmd", "drop_pyobj");
+    xcommand->SetAttribute("object_id", object_id);
+    doc.LinkEndChild(decl);
+    doc.LinkEndChild(xcommand);
+    nout << "sent: " << doc << "\n" << flush;
+    _pipe_write << doc << flush;
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: P3DSession::drop_p3dobj
+//       Access: Public
+//  Description: Responds to a drop_p3dobj message from the child
+//               process indicating that a particular P3D_object is no
+//               longer being used by the child.  This removes the
+//               corresponding P3D_object from our tables and
+//               decrements its reference count.
+////////////////////////////////////////////////////////////////////
+void P3DSession::
+drop_p3dobj(int object_id) {
+  nout << "got drop_p3dobj(" << object_id << ")\n" << flush;
+  SentObjects::iterator si = _sent_objects.find(object_id);
+  if (si != _sent_objects.end()) {
+    P3D_object *obj = (*si).second;
+    P3D_OBJECT_DECREF(obj);
+    _sent_objects.erase(si);
+  }
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: P3DSession::install_progress
 //     Function: P3DSession::install_progress
 //       Access: Private
 //       Access: Private
@@ -676,103 +764,16 @@ rt_handle_request(TiXmlDocument *doc) {
       ii = _instances.find(instance_id);
       ii = _instances.find(instance_id);
       if (ii != _instances.end()) {
       if (ii != _instances.end()) {
         P3DInstance *inst = (*ii).second;
         P3DInstance *inst = (*ii).second;
-        P3D_request *request = rt_make_p3d_request(xrequest);
-        if (request != NULL) {
-          inst->add_request(request);
-        }
+        inst->add_raw_request(doc);
+        doc = NULL;
       }
       }
       RELEASE_LOCK(_instances_lock);
       RELEASE_LOCK(_instances_lock);
     }
     }
   }
   }
 
 
-  delete doc;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: P3DSession::rt_make_p3d_request
-//       Access: Private
-//  Description: Creates a new P3D_request structure from the XML.
-////////////////////////////////////////////////////////////////////
-P3D_request *P3DSession::
-rt_make_p3d_request(TiXmlElement *xrequest) {
-  P3D_request *request = NULL;
-
-  const char *rtype = xrequest->Attribute("rtype");
-  if (rtype != NULL) {
-    if (strcmp(rtype, "notify") == 0) {
-      const char *message = xrequest->Attribute("message");
-      if (message != NULL) {
-        request = new P3D_request;
-        request->_request_type = P3D_RT_notify;
-        request->_request._notify._message = strdup(message);
-      }
-
-    } else if (strcmp(rtype, "script") == 0) {
-      const char *operation = xrequest->Attribute("operation");
-      TiXmlElement *xobject = xrequest->FirstChildElement("object");
-      const char *property_name = xrequest->Attribute("property_name");
-      int needs_response = 0;
-      xrequest->Attribute("needs_response", &needs_response);
-      int unique_id = 0;
-      xrequest->Attribute("unique_id", &unique_id);
-
-      if (operation != NULL && xobject != NULL) {
-        P3D_script_operation op;
-        if (strcmp(operation, "get_property") == 0) {
-          op = P3D_SO_get_property;
-        } else if (strcmp(operation, "set_property") == 0) {
-          op = P3D_SO_set_property;
-        } else if (strcmp(operation, "del_property") == 0) {
-          op = P3D_SO_del_property;
-        } else if (strcmp(operation, "call") == 0) {
-          op = P3D_SO_call;
-        } else if (strcmp(operation, "eval") == 0) {
-          op = P3D_SO_eval;
-        } else {
-          // An unexpected operation.
-          return NULL;
-        }
-
-        request = new P3D_request;
-        request->_request_type = P3D_RT_script;
-        request->_request._script._op = op;
-        request->_request._script._object = xml_to_p3dobj(xobject);
-        request->_request._script._property_name = NULL;
-        if (property_name != NULL) {
-          request->_request._script._property_name = strdup(property_name);
-        }
-        request->_request._script._needs_response = (needs_response != 0);
-        request->_request._script._unique_id = unique_id;
-        
-        // Fill in the value(s).
-        vector<P3D_object *> values;
-        TiXmlElement *xvalue = xrequest->FirstChildElement("value");
-        while (xvalue != NULL) {
-          P3D_object *value = xml_to_p3dobj(xvalue);
-          values.push_back(value);
-          xvalue = xvalue->NextSiblingElement("value");
-        }
-
-        if (values.empty()) {
-          // No values.
-          request->_request._script._values = NULL;
-          request->_request._script._num_values = 0;
-        } else {
-          // Some values.
-          int num_values = (int)values.size();
-          P3D_object **valuesp = new P3D_object *[num_values];
-          memcpy(valuesp, &values[0], num_values * sizeof(P3D_object *));
-          request->_request._script._values = valuesp;
-          request->_request._script._num_values = num_values;
-        }
-      }          
-
-    } else {
-      nout << "Ignoring request of type " << rtype << "\n";
-    }
+  if (doc != NULL) {
+    delete doc;
   }
   }
-
-  return request;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////

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

@@ -19,6 +19,7 @@
 #include "handleStream.h"
 #include "handleStream.h"
 #include "p3dPackage.h"
 #include "p3dPackage.h"
 #include "p3dConditionVar.h"
 #include "p3dConditionVar.h"
+#include "p3dReferenceCount.h"
 #include "get_tinyxml.h"
 #include "get_tinyxml.h"
 
 
 #include <map>
 #include <map>
@@ -34,11 +35,13 @@ class P3DProgressWindow;
 //               might include one or more P3DInstance objects running
 //               might include one or more P3DInstance objects running
 //               in the same memory space with each other.
 //               in the same memory space with each other.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-class P3DSession {
+class P3DSession : public P3DReferenceCount {
 public:
 public:
   P3DSession(P3DInstance *inst);
   P3DSession(P3DInstance *inst);
   ~P3DSession();
   ~P3DSession();
 
 
+  void shutdown();
+
   inline const string &get_session_key() const;
   inline const string &get_session_key() const;
   inline const string &get_python_version() const;
   inline const string &get_python_version() const;
 
 
@@ -52,6 +55,11 @@ public:
   P3D_object *xml_to_p3dobj(const TiXmlElement *xvalue);
   P3D_object *xml_to_p3dobj(const TiXmlElement *xvalue);
   TiXmlElement *p3dobj_to_xml(P3D_object *obj);
   TiXmlElement *p3dobj_to_xml(P3D_object *obj);
 
 
+  void signal_request_ready(P3DInstance *inst);
+
+  void drop_pyobj(int object_id);
+  void drop_p3dobj(int object_id);
+
 private:
 private:
   void install_progress(P3DPackage *package, double progress);
   void install_progress(P3DPackage *package, double progress);
   void start_p3dpython();
   void start_p3dpython();
@@ -65,7 +73,6 @@ private:
   void rt_thread_run();
   void rt_thread_run();
   void rt_terminate();
   void rt_terminate();
   void rt_handle_request(TiXmlDocument *doc);
   void rt_handle_request(TiXmlDocument *doc);
-  P3D_request *rt_make_p3d_request(TiXmlElement *xrequest);
 
 
 #ifdef _WIN32
 #ifdef _WIN32
   static HANDLE 
   static HANDLE 

+ 0 - 23
direct/src/plugin/p3d_plugin.h

@@ -580,7 +580,6 @@ typedef enum {
   P3D_RT_get_url,
   P3D_RT_get_url,
   P3D_RT_post_url,
   P3D_RT_post_url,
   P3D_RT_notify,
   P3D_RT_notify,
-  P3D_RT_script,
 } P3D_request_type;
 } P3D_request_type;
 
 
 /* Structures corresponding to the request types in the above enum. */
 /* Structures corresponding to the request types in the above enum. */
@@ -624,27 +623,6 @@ typedef struct {
   const char *_message;
   const char *_message;
 } P3D_request_notify;
 } P3D_request_notify;
 
 
-/* A script object request.  This is used to call out into the
-   browser_script_object (above).  This request is handled internally
-   by the core API, and may safely be ignored by the host.
-*/
-typedef enum {
-  P3D_SO_get_property,
-  P3D_SO_set_property,
-  P3D_SO_del_property,
-  P3D_SO_call,
-  P3D_SO_eval,
-} P3D_script_operation;
-typedef struct {
-  P3D_object *_object;
-  P3D_script_operation _op;
-  const char *_property_name;
-  P3D_object **_values;
-  int _num_values;
-  bool _needs_response;
-  int _unique_id;
-} P3D_request_script;
-
 /* This is the overall structure that represents a single request.  It
 /* This is the overall structure that represents a single request.  It
    is returned by P3D_instance_get_request(). */
    is returned by P3D_instance_get_request(). */
 typedef struct {
 typedef struct {
@@ -655,7 +633,6 @@ typedef struct {
     P3D_request_get_url _get_url;
     P3D_request_get_url _get_url;
     P3D_request_post_url _post_url;
     P3D_request_post_url _post_url;
     P3D_request_notify _notify;
     P3D_request_notify _notify;
-    P3D_request_script _script;
   } _request;
   } _request;
 } P3D_request;
 } P3D_request;
 
 

+ 1 - 0
direct/src/plugin/p3d_plugin_composite1.cxx

@@ -13,6 +13,7 @@
 #include "p3dObject.cxx"
 #include "p3dObject.cxx"
 #include "p3dPackage.cxx"
 #include "p3dPackage.cxx"
 #include "p3dPythonObject.cxx"
 #include "p3dPythonObject.cxx"
+#include "p3dReferenceCount.cxx"
 #include "p3dSession.cxx"
 #include "p3dSession.cxx"
 #include "p3dSplashWindow.cxx"
 #include "p3dSplashWindow.cxx"
 #include "p3dStringObject.cxx"
 #include "p3dStringObject.cxx"

+ 15 - 2
direct/src/showutil/runp3d.py

@@ -377,13 +377,20 @@ class AppRunner(DirectObject):
         uniqueId = self.nextScriptId
         uniqueId = self.nextScriptId
         self.nextScriptId += 1
         self.nextScriptId += 1
         self.sendRequest('script', operation, object,
         self.sendRequest('script', operation, object,
-                         propertyName, value, needsResponse, uniqueId);
+                         propertyName, value, needsResponse, uniqueId)
 
 
         if needsResponse:
         if needsResponse:
             # Now wait for the response to come in.
             # Now wait for the response to come in.
             result = self.sendRequest('wait_script_response', uniqueId)
             result = self.sendRequest('wait_script_response', uniqueId)
             return result
             return result
 
 
+    def dropObject(self, objectId):
+        """ Inform the parent process that we no longer have an
+        interest in the P3D_object corresponding to the indicated
+        objectId. """
+
+        self.sendRequest('drop_p3dobj', objectId)
+
     def parseSysArgs(self):
     def parseSysArgs(self):
         """ Converts sys.argv into (p3dFilename, tokens). """
         """ Converts sys.argv into (p3dFilename, tokens). """
         import getopt
         import getopt
@@ -444,6 +451,12 @@ class BrowserObject:
         # the object to its parent.
         # the object to its parent.
         self.__dict__['_BrowserObject__boundMethod'] = (None, None)
         self.__dict__['_BrowserObject__boundMethod'] = (None, None)
 
 
+    def __del__(self):
+        # When the BrowserObject destructs, tell the parent process it
+        # doesn't need to keep around its corresponding P3D_object any
+        # more.
+        self.__runner.dropObject(self.__objectId)
+
     def __str__(self):
     def __str__(self):
         parentObj, attribName = self.__boundMethod
         parentObj, attribName = self.__boundMethod
         if parentObj:
         if parentObj:
@@ -546,7 +559,7 @@ class BrowserObject:
             else:
             else:
                 raise IndexError(key)
                 raise IndexError(key)
 
 
-        return value;
+        return value
 
 
     def __setitem__(self, key, value):
     def __setitem__(self, key, value):
         result = self.__runner.scriptRequest('set_property', self,
         result = self.__runner.scriptRequest('set_property', self,