Browse Source

clean exit and log reporting

David Rose 16 years ago
parent
commit
194979b51d

+ 75 - 0
direct/src/p3d/AppRunner.py

@@ -113,6 +113,11 @@ class AppRunner(DirectObject):
         # hosts we have imported packages from.
         # hosts we have imported packages from.
         self.hosts = {}
         self.hosts = {}
 
 
+        # Application code can assign a callable object here; if so,
+        # it will be invoked when an uncaught exception propagates to
+        # the top of the TaskMgr.run() loop.
+        self.exceptionHandler = None
+
         # Managing packages for runtime download.
         # Managing packages for runtime download.
         self.downloadingPackages = []
         self.downloadingPackages = []
         self.downloadTask = None
         self.downloadTask = None
@@ -157,6 +162,49 @@ class AppRunner(DirectObject):
         # call back to the main thread.
         # call back to the main thread.
         self.accept('AppRunner_startIfReady', self.__startIfReady)
         self.accept('AppRunner_startIfReady', self.__startIfReady)
 
 
+    def getToken(self, tokenName):
+        """ Returns the value of the indicated web token as a string,
+        if it was set, or None if it was not. """
+
+        return self.tokenDict.get(tokenName.lower(), None)
+
+    def getTokenInt(self, tokenName):
+        """ Returns the value of the indicated web token as an integer
+        value, if it was set, or None if it was not, or not an
+        integer. """
+
+        value = self.getToken(tokenName)
+        if value is not None:
+            try:
+                value = int(value)
+            except ValueError:
+                value = None
+        return value
+
+    def getTokenFloat(self, tokenName):
+        """ Returns the value of the indicated web token as a
+        floating-point value value, if it was set, or None if it was
+        not, or not a number. """
+
+        value = self.getToken(tokenName)
+        if value is not None:
+            try:
+                value = float(value)
+            except ValueError:
+                value = None
+        return value
+
+    def getTokenBool(self, tokenName):
+        """ Returns the value of the indicated web token as a boolean
+        value, if it was set, or None if it was not. """
+
+        value = self.getTokenInt(tokenName)
+        if value is not None:
+            value = bool(value)
+        return value
+
+        
+
     def installPackage(self, packageName, version = None, hostUrl = None):
     def installPackage(self, packageName, version = None, hostUrl = None):
 
 
         """ Installs the named package, downloading it first if
         """ Installs the named package, downloading it first if
@@ -248,6 +296,33 @@ class AppRunner(DirectObject):
         # shuts down.
         # shuts down.
         taskMgr.doMethodLater(0.5, sys.exit, 'exit')
         taskMgr.doMethodLater(0.5, sys.exit, 'exit')
 
 
+    def run(self):
+        """ This method calls taskMgr.run(), with an optional
+        exception handler.  This is generally the program's main loop
+        when running in a p3d environment (except on unusual platforms
+        like the iPhone, which have to hand the main loop off to the
+        OS, and don't use this interface. """
+
+        try:
+            taskMgr.run()
+
+        except SystemExit:
+            # Presumably the window has already been shut down here, but shut
+            # it down again for good measure.
+            if hasattr(__builtin__, "base"):
+                base.destroy()
+
+            self.notify.info("Normal exit.")
+            raise
+
+        except:
+            # Some unexpected Python exception; pass it to the
+            # optional handler, if it is defined.
+            if self.exceptionHandler:
+                self.exceptionHandler()
+            else:
+                raise
+
     def setSessionId(self, sessionId):
     def setSessionId(self, sessionId):
         """ This message should come in at startup. """
         """ This message should come in at startup. """
         self.sessionId = sessionId
         self.sessionId = sessionId

+ 11 - 0
direct/src/plugin/handleStream.I

@@ -73,6 +73,17 @@ close() {
   _buf.close();
   _buf.close();
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: HandleStream::close_handle
+//       Access: Public
+//  Description: Closes the underlying handle, *without* attempting to
+//               flush the stream.
+////////////////////////////////////////////////////////////////////
+inline void HandleStream::
+close_handle() {
+  _buf.close_handle();
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: HandleStream::get_handle
 //     Function: HandleStream::get_handle
 //       Access: Public
 //       Access: Public

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

@@ -32,6 +32,7 @@ public:
   inline void open_read(FHandle handle);
   inline void open_read(FHandle handle);
   inline void open_write(FHandle handle);
   inline void open_write(FHandle handle);
   inline void close();
   inline void close();
+  inline void close_handle();
 
 
   inline FHandle get_handle() const;
   inline FHandle get_handle() const;
 
 

+ 37 - 10
direct/src/plugin/handleStreamBuf.cxx

@@ -36,12 +36,9 @@ HandleStreamBuf() {
   _is_open_read = false;
   _is_open_read = false;
   _is_open_write = false;
   _is_open_write = false;
   
   
-#ifdef _WIN32
-  // Windows case.
-  _handle = NULL;
-#else
-  _handle = -1;
-#endif  // _WIN32
+  _handle = INVALID_HANDLE_VALUE;
+
+  INIT_LOCK(_lock);
 
 
   _buffer = new char[handle_buffer_size];
   _buffer = new char[handle_buffer_size];
   char *ebuf = _buffer + handle_buffer_size;
   char *ebuf = _buffer + handle_buffer_size;
@@ -59,6 +56,8 @@ HandleStreamBuf::
   close();
   close();
 
 
   delete[] _buffer;
   delete[] _buffer;
+
+  DESTROY_LOCK(_lock);
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -123,6 +122,20 @@ close() {
   // Make sure the write buffer is flushed.
   // Make sure the write buffer is flushed.
   sync();
   sync();
 
 
+  close_handle();
+
+  pbump(pbase() - pptr());
+  gbump(egptr() - gptr());
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: HandleStreamBuf::close_handle
+//       Access: Public
+//  Description: Closes the underlying handle, *without* attempting to
+//               flush the stream.
+////////////////////////////////////////////////////////////////////
+void HandleStreamBuf::
+close_handle() {
 #ifdef _WIN32
 #ifdef _WIN32
   if (_handle != NULL) {
   if (_handle != NULL) {
     CloseHandle(_handle);
     CloseHandle(_handle);
@@ -137,9 +150,6 @@ close() {
 
 
   _is_open_read = false;
   _is_open_read = false;
   _is_open_write = false;
   _is_open_write = false;
-
-  pbump(pbase() - pptr());
-  gbump(egptr() - gptr());
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -150,11 +160,15 @@ close() {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 int HandleStreamBuf::
 int HandleStreamBuf::
 overflow(int ch) {
 overflow(int ch) {
+  ACQUIRE_LOCK(_lock);
+
   bool okflag = true;
   bool okflag = true;
 
 
+  assert(pptr() >= pbase());
   size_t n = pptr() - pbase();
   size_t n = pptr() - pbase();
   if (n != 0) {
   if (n != 0) {
     size_t wrote = write_chars(pbase(), n);
     size_t wrote = write_chars(pbase(), n);
+    assert(wrote <= n);
     pbump(-(int)wrote);
     pbump(-(int)wrote);
     if (wrote != n) {
     if (wrote != n) {
       okflag = false;
       okflag = false;
@@ -172,6 +186,8 @@ overflow(int ch) {
     }
     }
   }
   }
 
 
+  RELEASE_LOCK(_lock);
+
   if (!okflag) {
   if (!okflag) {
     return EOF;
     return EOF;
   }
   }
@@ -186,11 +202,16 @@ overflow(int ch) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 int HandleStreamBuf::
 int HandleStreamBuf::
 sync() {
 sync() {
+  ACQUIRE_LOCK(_lock);
+  assert(pptr() >= pbase());
   size_t n = pptr() - pbase();
   size_t n = pptr() - pbase();
 
 
   size_t wrote = write_chars(pbase(), n);
   size_t wrote = write_chars(pbase(), n);
+  assert(wrote <= n);
   pbump(-(int)wrote);
   pbump(-(int)wrote);
 
 
+  RELEASE_LOCK(_lock);
+
   if (n != wrote) {
   if (n != wrote) {
     return EOF;
     return EOF;
   }
   }
@@ -205,6 +226,7 @@ sync() {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 int HandleStreamBuf::
 int HandleStreamBuf::
 underflow() {
 underflow() {
+  ACQUIRE_LOCK(_lock);
   // Sometimes underflow() is called even if the buffer is not empty.
   // Sometimes underflow() is called even if the buffer is not empty.
   if (gptr() >= egptr()) {
   if (gptr() >= egptr()) {
     // Mark the buffer filled (with buffer_size bytes).
     // Mark the buffer filled (with buffer_size bytes).
@@ -218,6 +240,7 @@ underflow() {
       // Oops, we didn't read what we thought we would.
       // Oops, we didn't read what we thought we would.
       if (read_count == 0) {
       if (read_count == 0) {
         gbump(num_bytes);
         gbump(num_bytes);
+        RELEASE_LOCK(_lock);
         return EOF;
         return EOF;
       }
       }
 
 
@@ -229,7 +252,10 @@ underflow() {
     }
     }
   }
   }
 
 
-  return (unsigned char)*gptr();
+  unsigned char next = *gptr();
+  RELEASE_LOCK(_lock);
+
+  return next;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -309,6 +335,7 @@ write_chars(const char *start, size_t length) {
   DWORD bytes_written = 0;
   DWORD bytes_written = 0;
   BOOL success = WriteFile(_handle, start, length, &bytes_written, NULL);
   BOOL success = WriteFile(_handle, start, length, &bytes_written, NULL);
   if (!success) {
   if (!success) {
+    assert(bytes_written <= length);
     DWORD error = GetLastError();
     DWORD error = GetLastError();
     if (error != ERROR_NO_DATA && error != ERROR_BROKEN_PIPE) {
     if (error != ERROR_NO_DATA && error != ERROR_BROKEN_PIPE) {
       cerr << "Error writing " << length
       cerr << "Error writing " << length

+ 3 - 0
direct/src/plugin/handleStreamBuf.h

@@ -16,6 +16,7 @@
 #define HANDLESTREAMBUF_H
 #define HANDLESTREAMBUF_H
 
 
 #include "fhandle.h"
 #include "fhandle.h"
+#include "p3d_lock.h"
 #include <iostream>
 #include <iostream>
 
 
 using namespace std;
 using namespace std;
@@ -34,6 +35,7 @@ public:
   bool is_open_read() const;
   bool is_open_read() const;
   bool is_open_write() const;
   bool is_open_write() const;
   void close();
   void close();
+  void close_handle();
 
 
   inline FHandle get_handle() const;
   inline FHandle get_handle() const;
 
 
@@ -51,6 +53,7 @@ private:
   bool _is_open_write;
   bool _is_open_write;
 
 
   FHandle _handle;
   FHandle _handle;
+  LOCK _lock;
 
 
   char *_buffer;
   char *_buffer;
 };
 };

+ 6 - 6
direct/src/plugin/p3dInstance.I

@@ -61,14 +61,14 @@ get_session_key() const {
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: P3DInstance::get_python_version
+//     Function: P3DInstance::get_session
 //       Access: Public
 //       Access: Public
-//  Description: Returns a string that uniquely identifies this
-//               instance's required Python version.
+//  Description: Returns the P3DSession that is hosting this instance,
+//               or NULL if the instance is not running.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-inline const string &P3DInstance::
-get_python_version() const {
-  return _python_version;
+inline P3DSession *P3DInstance::
+get_session() const {
+  return _session;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////

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

@@ -58,6 +58,7 @@ P3DInstance(P3D_request_ready_func *func,
 {
 {
   _browser_script_object = NULL;
   _browser_script_object = NULL;
   _panda_script_object = new P3DMainObject;
   _panda_script_object = new P3DMainObject;
+  _panda_script_object->set_instance(this);
   _user_data = user_data;
   _user_data = user_data;
   _request_pending = false;
   _request_pending = false;
   _temp_p3d_filename = NULL;
   _temp_p3d_filename = NULL;
@@ -113,6 +114,7 @@ P3DInstance::
 
 
   nout << "panda_script_object ref = "
   nout << "panda_script_object ref = "
        << _panda_script_object->_ref_count << "\n";
        << _panda_script_object->_ref_count << "\n";
+  _panda_script_object->set_instance(NULL);
   P3D_OBJECT_DECREF(_panda_script_object);
   P3D_OBJECT_DECREF(_panda_script_object);
 
 
   // Tell all of the packages that we're no longer in business for
   // Tell all of the packages that we're no longer in business for
@@ -458,7 +460,7 @@ bake_requests() {
       // No more requests to process right now.
       // No more requests to process right now.
       return;
       return;
     }
     }
-    
+
     // Now we've got a request in XML form; convert it to P3D_request
     // Now we've got a request in XML form; convert it to P3D_request
     // form.
     // form.
     TiXmlElement *xrequest = doc->FirstChildElement("request");
     TiXmlElement *xrequest = doc->FirstChildElement("request");
@@ -1000,6 +1002,11 @@ make_p3d_request(TiXmlElement *xrequest) {
         // We no longer need to keep this reference.
         // We no longer need to keep this reference.
         _session->drop_p3dobj(object_id);
         _session->drop_p3dobj(object_id);
       }
       }
+    
+    } else if (strcmp(rtype, "stop") == 0) {
+      // A stop request from Python code.  This is kind of weird, but OK.
+      request = new P3D_request;
+      request->_request_type = P3D_RT_stop;
 
 
     } else {
     } else {
       nout << "Ignoring request of type " << rtype << "\n";
       nout << "Ignoring request of type " << rtype << "\n";

+ 2 - 2
direct/src/plugin/p3dInstance.h

@@ -82,7 +82,8 @@ public:
 
 
   inline int get_instance_id() const;
   inline int get_instance_id() const;
   inline const string &get_session_key() const;
   inline const string &get_session_key() const;
-  inline const string &get_python_version() const;
+
+  inline P3DSession *get_session() const;
 
 
   inline P3D_request_ready_func *get_request_ready_func() const;
   inline P3D_request_ready_func *get_request_ready_func() const;
 
 
@@ -158,7 +159,6 @@ private:
 
 
   int _instance_id;
   int _instance_id;
   string _session_key;
   string _session_key;
-  string _python_version;
   string _log_basename;
   string _log_basename;
   bool _hidden;
   bool _hidden;
   bool _allow_python_dev;
   bool _allow_python_dev;

+ 15 - 0
direct/src/plugin/p3dInstanceManager.I

@@ -89,6 +89,21 @@ get_log_directory() const {
   return _log_directory;
   return _log_directory;
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: P3DInstanceManager::get_log_pathname
+//       Access: Public
+//  Description: Returns the filename of the system log file; this
+//               file is responsible for downloading and installing
+//               updates, and launching applications.  This is
+//               different from the session log file(s), which
+//               represent the output from a particular Python
+//               session.
+////////////////////////////////////////////////////////////////////
+inline const string &P3DInstanceManager::
+get_log_pathname() const {
+  return _log_pathname;
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: P3DInstanceManager::get_keep_cwd
 //     Function: P3DInstanceManager::get_keep_cwd
 //       Access: Public
 //       Access: Public

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

@@ -58,6 +58,7 @@ public:
   inline const string &get_root_dir() const;
   inline const string &get_root_dir() const;
   inline const string &get_platform() const;
   inline const string &get_platform() const;
   inline const string &get_log_directory() const;
   inline const string &get_log_directory() const;
+  inline const string &get_log_pathname() const;
   inline bool get_keep_cwd() const;
   inline bool get_keep_cwd() const;
 
 
   P3DInstance *
   P3DInstance *

+ 103 - 0
direct/src/plugin/p3dMainObject.cxx

@@ -13,6 +13,11 @@
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 
 
 #include "p3dMainObject.h"
 #include "p3dMainObject.h"
+#include "p3dInstance.h"
+#include "p3dSession.h"
+#include "p3dUndefinedObject.h"
+#include "p3dStringObject.h"
+#include "p3dInstanceManager.h"
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: P3DMainObject::Constructor
 //     Function: P3DMainObject::Constructor
@@ -178,6 +183,13 @@ set_property(const string &property, P3D_object *value) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 bool P3DMainObject::
 bool P3DMainObject::
 has_method(const string &method_name) {
 has_method(const string &method_name) {
+  // Some special-case methods implemented in-place.
+  if (method_name == "read_game_log") {
+    return true;
+  } else if (method_name == "read_system_log") {
+    return true;
+  }
+
   if (_pyobj == NULL) {
   if (_pyobj == NULL) {
     // No methods until we get our pyobj.
     // No methods until we get our pyobj.
     return false;
     return false;
@@ -202,6 +214,12 @@ has_method(const string &method_name) {
 P3D_object *P3DMainObject::
 P3D_object *P3DMainObject::
 call(const string &method_name, bool needs_response,
 call(const string &method_name, bool needs_response,
      P3D_object *params[], int num_params) {
      P3D_object *params[], int num_params) {
+  if (method_name == "read_game_log") {
+    return call_read_game_log();
+  } else if (method_name == "read_system_log") {
+    return call_read_system_log();
+  }
+
   if (_pyobj == NULL) {
   if (_pyobj == NULL) {
     // No methods until we get our pyobj.
     // No methods until we get our pyobj.
     return NULL;
     return NULL;
@@ -263,3 +281,88 @@ P3D_object *P3DMainObject::
 get_pyobj() const {
 get_pyobj() const {
   return _pyobj;
   return _pyobj;
 }
 }
+
+////////////////////////////////////////////////////////////////////
+//     Function: P3DMainObject::set_instance
+//       Access: Public
+//  Description: Sets a callback pointer to the instance that owns
+//               this object.  When this instance destructs, it clears
+//               this pointer to NULL.
+////////////////////////////////////////////////////////////////////
+void P3DMainObject::
+set_instance(P3DInstance *inst) {
+  _inst = inst;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: P3DMainObject::call_read_game_log
+//       Access: Private
+//  Description: Reads the entire logfile as a string, and returns it
+//               to the calling JavaScript process.
+////////////////////////////////////////////////////////////////////
+P3D_object *P3DMainObject::
+call_read_game_log() {
+  nout << "call_read_game_log: " << _inst << "\n";
+  if (_inst == NULL) {
+    return new P3DUndefinedObject();
+  }
+
+  P3DSession *session = _inst->get_session();
+  nout << "session = " << session << "\n";
+
+  string log_pathname = session->get_log_pathname();
+  nout << "log_pathname = " << log_pathname << "\n";
+
+  ifstream log(log_pathname.c_str(), ios::in);
+
+  // Get the size of the file.
+  log.seekg(0, ios::end);
+  size_t size = (size_t)log.tellg();
+  log.seekg(0, ios::beg);
+
+  // Read the entire file into memory all at once.
+  char *buffer = new char[size];
+  if (buffer == NULL) {
+    return NULL;
+  }
+
+  log.read(buffer, size);
+  P3D_object *result = new P3DStringObject(buffer, size);
+  delete[] buffer;
+  
+  return result;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: P3DMainObject::call_read_system_log
+//       Access: Private
+//  Description: As above, but reads the system log, the logfile for
+//               the installation process.
+////////////////////////////////////////////////////////////////////
+P3D_object *P3DMainObject::
+call_read_system_log() {
+  nout << "call_read_system_log: " << _inst << "\n";
+
+  P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
+  string log_pathname = inst_mgr->get_log_pathname();
+  nout << "log_pathname = " << log_pathname << "\n";
+
+  ifstream log(log_pathname.c_str(), ios::in);
+
+  // Get the size of the file.
+  log.seekg(0, ios::end);
+  size_t size = (size_t)log.tellg();
+  log.seekg(0, ios::beg);
+
+  // Read the entire file into memory all at once.
+  char *buffer = new char[size];
+  if (buffer == NULL) {
+    return NULL;
+  }
+
+  log.read(buffer, size);
+  P3D_object *result = new P3DStringObject(buffer, size);
+  delete[] buffer;
+  
+  return result;
+}

+ 8 - 0
direct/src/plugin/p3dMainObject.h

@@ -20,6 +20,7 @@
 #include <map>
 #include <map>
 
 
 class P3DSession;
 class P3DSession;
+class P3DInstance;
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //       Class : P3DMainObject
 //       Class : P3DMainObject
@@ -63,8 +64,15 @@ public:
   void set_pyobj(P3D_object *pyobj);
   void set_pyobj(P3D_object *pyobj);
   P3D_object *get_pyobj() const;
   P3D_object *get_pyobj() const;
 
 
+  void set_instance(P3DInstance *inst);
+
+private:
+  P3D_object *call_read_game_log();
+  P3D_object *call_read_system_log();
+
 private:
 private:
   P3D_object *_pyobj;
   P3D_object *_pyobj;
+  P3DInstance *_inst;
 
 
   // This map is used to store properties and retrieve until
   // This map is used to store properties and retrieve until
   // set_pyobj() is called for the firs ttime.  At that point, the
   // set_pyobj() is called for the firs ttime.  At that point, the

+ 3 - 17
direct/src/plugin/p3dPythonMain.cxx

@@ -32,7 +32,6 @@ main(int argc, char *argv[]) {
   const char *archive_file = NULL;
   const char *archive_file = NULL;
   const char *input_handle_str = NULL;
   const char *input_handle_str = NULL;
   const char *output_handle_str = NULL;
   const char *output_handle_str = NULL;
-  const char *error_handle_str = NULL;
   const char *interactive_console_str = NULL;
   const char *interactive_console_str = NULL;
 
 
   if (argc > 1) {
   if (argc > 1) {
@@ -45,10 +44,7 @@ main(int argc, char *argv[]) {
     output_handle_str = argv[3];
     output_handle_str = argv[3];
   }
   }
   if (argc > 4) {
   if (argc > 4) {
-    error_handle_str = argv[4];
-  }
-  if (argc > 5) {
-    interactive_console_str = argv[5];
+    interactive_console_str = argv[4];
   }
   }
 
 
   if (archive_file == NULL || *archive_file == '\0') {
   if (archive_file == NULL || *archive_file == '\0') {
@@ -74,15 +70,6 @@ main(int argc, char *argv[]) {
     }
     }
   }
   }
 
 
-  FHandle error_handle = invalid_fhandle;
-  if (error_handle_str != NULL && *error_handle_str) {
-    stringstream stream(error_handle_str);
-    stream >> error_handle;
-    if (!stream) {
-      error_handle = invalid_fhandle;
-    }
-  }
-
   bool interactive_console = false;
   bool interactive_console = false;
   if (interactive_console_str != NULL && *interactive_console_str) {
   if (interactive_console_str != NULL && *interactive_console_str) {
     stringstream stream(interactive_console_str);
     stringstream stream(interactive_console_str);
@@ -93,12 +80,11 @@ main(int argc, char *argv[]) {
     }
     }
   }
   }
 
 
-  cerr << "handles: " << input_handle << ", " << output_handle
-       << ", " << error_handle << "\n";
+  cerr << "handles: " << input_handle << ", " << output_handle << "\n";
   cerr << "interactive_console = " << interactive_console << "\n";
   cerr << "interactive_console = " << interactive_console << "\n";
 
 
   if (!run_p3dpython(program_name, archive_file, input_handle, output_handle, 
   if (!run_p3dpython(program_name, archive_file, input_handle, output_handle, 
-                     error_handle, interactive_console)) {
+                     NULL, interactive_console)) {
     cerr << "Failure on startup.\n";
     cerr << "Failure on startup.\n";
     return 1;
     return 1;
   }
   }

+ 30 - 16
direct/src/plugin/p3dPythonRun.cxx

@@ -31,7 +31,7 @@ P3DPythonRun *P3DPythonRun::_global_ptr = NULL;
 P3DPythonRun::
 P3DPythonRun::
 P3DPythonRun(const char *program_name, const char *archive_file,
 P3DPythonRun(const char *program_name, const char *archive_file,
              FHandle input_handle, FHandle output_handle, 
              FHandle input_handle, FHandle output_handle, 
-             FHandle error_handle, bool interactive_console) {
+             const char *log_pathname, bool interactive_console) {
   _read_thread_continue = false;
   _read_thread_continue = false;
   _program_continue = true;
   _program_continue = true;
   _session_terminated = false;
   _session_terminated = false;
@@ -74,11 +74,14 @@ P3DPythonRun(const char *program_name, const char *archive_file,
   PySys_SetArgv(_py_argc, _py_argv);
   PySys_SetArgv(_py_argc, _py_argv);
 
 
   // Open the error output before we do too much more.
   // Open the error output before we do too much more.
-  _error_log.open_write(error_handle);
-  if (!_error_log.fail()) {
-    // Set up the indicated error log as the Notify output.
-    _error_log.setf(ios::unitbuf);
-    Notify::ptr()->set_ostream_ptr(&_error_log, false);
+  if (log_pathname != NULL && *log_pathname != '\0') {
+    Filename f = Filename::from_os_specific(log_pathname);
+    f.set_text();
+    if (f.open_write(_error_log)) {
+      // Set up the indicated error log as the Notify output.
+      _error_log.setf(ios::unitbuf);
+      Notify::ptr()->set_ostream_ptr(&_error_log, false);
+    }
   }
   }
 
 
   // Open the pipe streams with the input and output handles from the
   // Open the pipe streams with the input and output handles from the
@@ -103,6 +106,9 @@ P3DPythonRun(const char *program_name, const char *archive_file,
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 P3DPythonRun::
 P3DPythonRun::
 ~P3DPythonRun() {
 ~P3DPythonRun() {
+  // Close the write pipe, so the parent process will terminate us.
+  _pipe_write.close();
+
   Py_Finalize();
   Py_Finalize();
 
 
   join_read_thread();
   join_read_thread();
@@ -339,22 +345,32 @@ run_python() {
 
 
   Py_DECREF(check_comm);
   Py_DECREF(check_comm);
 
 
-  // Finally, get lost in taskMgr.run().
-  bool okflag = true;
-  PyObject *done = PyObject_CallMethod(_taskMgr, (char *)"run", (char *)"");
+  // Finally, get lost in AppRunner.run() (which is really a call to
+  // taskMgr.run()).
+  PyObject *done = PyObject_CallMethod(_runner, (char *)"run", (char *)"");
   if (done == NULL) {
   if (done == NULL) {
+    // An uncaught application exception, and not handled by
+    // appRunner.exceptionHandler.
     PyErr_Print();
     PyErr_Print();
-    okflag = false;
-  } else {
-    Py_DECREF(done);
+
+    if (_interactive_console) {
+      // Give an interactive user a chance to explore the exception.
+      run_interactive_console();
+      return true;
+    }
+
+    // We're done.
+    return false;
   }
   }
 
 
+  // A normal exit from the taskManager.  We're presumably done.
+  Py_DECREF(done);
+
   if (_interactive_console) {
   if (_interactive_console) {
     run_interactive_console();
     run_interactive_console();
-    okflag = true;
   }
   }
 
 
-  return okflag;
+  return true;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -1032,8 +1048,6 @@ spawn_read_thread() {
 void P3DPythonRun::
 void P3DPythonRun::
 join_read_thread() {
 join_read_thread() {
   _read_thread_continue = false;
   _read_thread_continue = false;
-  _pipe_read.close();
-
   JOIN_THREAD(_read_thread);
   JOIN_THREAD(_read_thread);
 }
 }
 
 

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

@@ -26,6 +26,7 @@
 #include "p3d_lock.h"
 #include "p3d_lock.h"
 #include "handleStream.h"
 #include "handleStream.h"
 #include "p3dCInstance.h"
 #include "p3dCInstance.h"
+#include "pandaFileStreamBuf.h"
 #include "pythonTask.h"
 #include "pythonTask.h"
 #include "pmap.h"
 #include "pmap.h"
 #include "pdeque.h"
 #include "pdeque.h"
@@ -68,7 +69,7 @@ class P3DPythonRun {
 public:
 public:
   P3DPythonRun(const char *program_name, const char *archive_file,
   P3DPythonRun(const char *program_name, const char *archive_file,
                FHandle input_handle, FHandle output_handle, 
                FHandle input_handle, FHandle output_handle, 
-               FHandle error_handle, bool interactive_console);
+               const char *log_pathname, bool interactive_console);
   ~P3DPythonRun();
   ~P3DPythonRun();
 
 
   bool run_python();
   bool run_python();
@@ -155,7 +156,7 @@ private:
 
 
   HandleStream _pipe_read;
   HandleStream _pipe_read;
   HandleStream _pipe_write;
   HandleStream _pipe_write;
-  HandleStream _error_log;
+  pofstream _error_log;
 
 
   bool _read_thread_continue;
   bool _read_thread_continue;
   bool _program_continue;
   bool _program_continue;

+ 7 - 5
direct/src/plugin/p3dSession.I

@@ -25,14 +25,16 @@ get_session_key() const {
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: P3DSession::get_python_version
+//     Function: P3DSession::get_log_pathname
 //       Access: Public
 //       Access: Public
-//  Description: Returns a string that uniquely identifies this
-//               session's required Python version.
+//  Description: Returns the log filename for this particular session,
+//               if the session was started and if it has a log file.
+//               Returns empty string if the session never started or
+//               if it lacks a log file.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 inline const string &P3DSession::
 inline const string &P3DSession::
-get_python_version() const {
-  return _python_version;
+get_log_pathname() const {
+  return _log_pathname;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////

+ 58 - 46
direct/src/plugin/p3dSession.cxx

@@ -52,7 +52,6 @@ P3DSession(P3DInstance *inst) {
   P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
   P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
   _session_id = inst_mgr->get_unique_id();
   _session_id = inst_mgr->get_unique_id();
   _session_key = inst->get_session_key();
   _session_key = inst->get_session_key();
-  _python_version = inst->get_python_version();
 
 
   _start_dir = inst_mgr->get_root_dir() + "/start";
   _start_dir = inst_mgr->get_root_dir() + "/start";
   _p3dpython_one_process = false;
   _p3dpython_one_process = false;
@@ -180,9 +179,6 @@ shutdown() {
 
 
     _p3dpython_running = false;
     _p3dpython_running = false;
     _p3dpython_started = false;
     _p3dpython_started = false;
-
-    // Close the pipe now.
-    _pipe_read.close();
   }
   }
 
 
   // If there are any leftover commands in the queue (presumably
   // If there are any leftover commands in the queue (presumably
@@ -195,6 +191,9 @@ shutdown() {
   _commands.clear();
   _commands.clear();
 
 
   join_read_thread();
   join_read_thread();
+
+  // Close the pipe now.
+  _pipe_read.close();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -211,7 +210,6 @@ void P3DSession::
 start_instance(P3DInstance *inst) {
 start_instance(P3DInstance *inst) {
   assert(inst->_session == NULL);
   assert(inst->_session == NULL);
   assert(inst->get_session_key() == _session_key);
   assert(inst->get_session_key() == _session_key);
-  assert(inst->get_python_version() == _python_version);
 
 
   inst->ref();
   inst->ref();
   ACQUIRE_LOCK(_instances_lock);
   ACQUIRE_LOCK(_instances_lock);
@@ -684,6 +682,10 @@ start_p3dpython(P3DInstance *inst) {
     mkdir_complete(_start_dir, nout);
     mkdir_complete(_start_dir, nout);
   }
   }
 
 
+  // Also make sure the prc directory is present.
+  string prc_root = inst_mgr->get_root_dir() + "/prc";
+  mkdir_complete(prc_root, nout);
+
 #ifdef _WIN32
 #ifdef _WIN32
   char sep = ';';
   char sep = ';';
 #else
 #else
@@ -716,7 +718,14 @@ start_p3dpython(P3DInstance *inst) {
 
 
   string dyld_path = search_path;
   string dyld_path = search_path;
   string python_path = search_path;
   string python_path = search_path;
-  string prc_path = search_path;
+  string prc_path = prc_root + sep + search_path;
+
+  string prc_name = inst->get_fparams().lookup_token("prc_name");
+  if (!prc_name.empty()) {
+    // Add the prc_name to the path too, even if this directory doesn't
+    // actually exist.
+    prc_path = inst_mgr->get_root_dir() + "/" + prc_name + sep + prc_path;    
+  }
 
 
   if (keep_pythonpath) {
   if (keep_pythonpath) {
     // With keep_pythonpath true, we preserve the PYTHONPATH setting
     // With keep_pythonpath true, we preserve the PYTHONPATH setting
@@ -929,34 +938,8 @@ start_p3dpython(P3DInstance *inst) {
   // Create the error stream for log output.  This means opening the
   // Create the error stream for log output.  This means opening the
   // logfile, if we have one, or keeping the standard error if we
   // logfile, if we have one, or keeping the standard error if we
   // don't.
   // don't.
-  _got_error_handle = false;
 #ifdef _WIN32
 #ifdef _WIN32
-  _error_handle = GetStdHandle(STD_ERROR_HANDLE);
-  if (!_log_pathname.empty()) {
-    HANDLE handle = CreateFile
-      (_log_pathname.c_str(), GENERIC_WRITE, 
-       FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
-       NULL, CREATE_ALWAYS, 0, NULL);
-    if (handle != INVALID_HANDLE_VALUE) {
-      _error_handle = handle;
-      SetHandleInformation(_error_handle, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT);
-      _got_error_handle = true;
-    } else {
-      nout << "Unable to open " << _log_pathname << "\n";
-    }
-  }
 #else  // _WIN32
 #else  // _WIN32
-  _error_handle = STDERR_FILENO;
-  if (!_log_pathname.empty()) {
-    int logfile_fd = open(_log_pathname.c_str(), 
-                          O_WRONLY | O_CREAT | O_TRUNC, 0666);
-    if (logfile_fd < 0) {
-      nout << "Unable to open " << _log_pathname << "\n";
-    } else {
-      _error_handle = logfile_fd;
-      _got_error_handle = true;
-    }
-  }
 #endif  // _WIN32
 #endif  // _WIN32
 
 
   // Get the filename of the Panda3D multifile.  We need to pass this
   // Get the filename of the Panda3D multifile.  We need to pass this
@@ -1049,7 +1032,6 @@ join_read_thread() {
   }
   }
 
 
   _read_thread_continue = false;
   _read_thread_continue = false;
-  _pipe_read.close();
 
 
   JOIN_THREAD(_read_thread);
   JOIN_THREAD(_read_thread);
   _started_read_thread = false;
   _started_read_thread = false;
@@ -1171,10 +1153,34 @@ win_create_process() {
   // Make sure we see an error dialog if there is a missing DLL.
   // Make sure we see an error dialog if there is a missing DLL.
   SetErrorMode(0);
   SetErrorMode(0);
 
 
+  // Open the log file.
+  HANDLE error_handle = GetStdHandle(STD_ERROR_HANDLE);
+  bool got_error_handle = false;
+  if (!_log_pathname.empty()) {
+    HANDLE handle = CreateFile
+      (_log_pathname.c_str(), GENERIC_WRITE, 
+       FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+       NULL, CREATE_ALWAYS, 0, NULL);
+    if (handle != INVALID_HANDLE_VALUE) {
+      error_handle = handle;
+      SetHandleInformation(error_handle, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT);
+      got_error_handle = true;
+    } else {
+      nout << "Unable to open " << _log_pathname << "\n";
+    }
+  }
+
   STARTUPINFO startup_info;
   STARTUPINFO startup_info;
   ZeroMemory(&startup_info, sizeof(STARTUPINFO));
   ZeroMemory(&startup_info, sizeof(STARTUPINFO));
   startup_info.cb = sizeof(startup_info); 
   startup_info.cb = sizeof(startup_info); 
 
 
+  // Set up the I/O handles.  We send stderr and stdout to our
+  // error_handle.
+  startup_info.hStdError = error_handle;
+  startup_info.hStdOutput = error_handle;
+  startup_info.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
+  startup_info.dwFlags |= STARTF_USESTDHANDLES;
+
   // Make sure the "python" console window is hidden.
   // Make sure the "python" console window is hidden.
   startup_info.wShowWindow = SW_HIDE;
   startup_info.wShowWindow = SW_HIDE;
   startup_info.dwFlags |= STARTF_USESHOWWINDOW;
   startup_info.dwFlags |= STARTF_USESHOWWINDOW;
@@ -1191,8 +1197,7 @@ win_create_process() {
   ostringstream stream;
   ostringstream stream;
   stream << "\"" << _p3dpython_exe << "\" \"" << _mf_filename
   stream << "\"" << _p3dpython_exe << "\" \"" << _mf_filename
          << "\" \"" << _input_handle << "\" \"" << _output_handle
          << "\" \"" << _input_handle << "\" \"" << _output_handle
-         << "\" \"" << _error_handle << "\" \"" << _interactive_console
-         << "\"";
+         << "\" \"" << _interactive_console << "\"";
 
 
   // I'm not sure why CreateProcess wants a non-const char pointer for
   // I'm not sure why CreateProcess wants a non-const char pointer for
   // its command-line string, but I'm not taking chances.  It gets a
   // its command-line string, but I'm not taking chances.  It gets a
@@ -1213,8 +1218,8 @@ win_create_process() {
   // Close the pipe handles that are now owned by the child.
   // Close the pipe handles that are now owned by the child.
   CloseHandle(_output_handle);
   CloseHandle(_output_handle);
   CloseHandle(_input_handle);
   CloseHandle(_input_handle);
-  if (_got_error_handle) {
-    CloseHandle(_error_handle);
+  if (got_error_handle) {
+    CloseHandle(error_handle);
   }
   }
 
 
   if (!started_program) {
   if (!started_program) {
@@ -1263,6 +1268,20 @@ posix_create_process() {
     _pipe_read.close();
     _pipe_read.close();
     _pipe_write.close();
     _pipe_write.close();
 
 
+    if (!_log_pathname.empty()) {
+      // Open a logfile.
+      int logfile_fd = open(_log_pathname.c_str(), 
+                            O_WRONLY | O_CREAT | O_TRUNC, 0666);
+      if (logfile_fd < 0) {
+        nout << "Unable to open " << _log_pathname << "\n";
+      } else {
+        // Redirect stderr and stdout onto our logfile.
+        dup2(logfile_fd, STDERR_FILENO);
+        dup2(logfile_fd, STDOUT_FILENO);
+        close(logfile_fd);
+      }
+    }
+
     if (_use_start_dir) {
     if (_use_start_dir) {
       if (chdir(_start_dir.c_str()) < 0) {
       if (chdir(_start_dir.c_str()) < 0) {
         nout << "Could not chdir to " << _start_dir << "\n";
         nout << "Could not chdir to " << _start_dir << "\n";
@@ -1290,13 +1309,9 @@ posix_create_process() {
     output_handle_stream << _output_handle;
     output_handle_stream << _output_handle;
     string output_handle_str = output_handle_stream.str();
     string output_handle_str = output_handle_stream.str();
 
 
-    stringstream error_handle_stream;
-    error_handle_stream << _error_handle;
-    string error_handle_str = error_handle_stream.str();
-
     execle(_p3dpython_exe.c_str(), _p3dpython_exe.c_str(),
     execle(_p3dpython_exe.c_str(), _p3dpython_exe.c_str(),
            _mf_filename.c_str(), input_handle_str.c_str(),
            _mf_filename.c_str(), input_handle_str.c_str(),
-           output_handle_str.c_str(), error_handle_str.c_str(),
+           output_handle_str.c_str(),
            _interactive_console ? "1" : "0", (char *)0, &ptrs[0]);
            _interactive_console ? "1" : "0", (char *)0, &ptrs[0]);
     nout << "Failed to exec " << _p3dpython_exe << "\n";
     nout << "Failed to exec " << _p3dpython_exe << "\n";
     _exit(1);
     _exit(1);
@@ -1305,9 +1320,6 @@ posix_create_process() {
   // Close the handles that are now owned by the child.
   // Close the handles that are now owned by the child.
   close(_input_handle);
   close(_input_handle);
   close(_output_handle);
   close(_output_handle);
-  if (_got_error_handle) {
-    close(_error_handle);
-  }
 
 
   return child;
   return child;
 }
 }
@@ -1391,7 +1403,7 @@ p3dpython_thread_run() {
   }
   }
 
 
   if (!run_p3dpython(libp3dpython.c_str(), _mf_filename.c_str(),
   if (!run_p3dpython(libp3dpython.c_str(), _mf_filename.c_str(),
-                     _input_handle, _output_handle, _error_handle,
+                     _input_handle, _output_handle, _log_pathname.c_str(),
                      _interactive_console)) {
                      _interactive_console)) {
     nout << "Failure on startup.\n";
     nout << "Failure on startup.\n";
   }
   }

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

@@ -43,7 +43,7 @@ public:
   void shutdown();
   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_log_pathname() const;
 
 
   void start_instance(P3DInstance *inst);
   void start_instance(P3DInstance *inst);
   void terminate_instance(P3DInstance *inst);
   void terminate_instance(P3DInstance *inst);
@@ -88,7 +88,6 @@ private:
 private:
 private:
   int _session_id;
   int _session_id;
   string _session_key;
   string _session_key;
-  string _python_version;
   string _log_pathname;
   string _log_pathname;
   string _python_root_dir;
   string _python_root_dir;
   string _start_dir;
   string _start_dir;
@@ -100,8 +99,7 @@ private:
   string _p3dpython_dll;
   string _p3dpython_dll;
   string _mf_filename;
   string _mf_filename;
   string _env;
   string _env;
-  FHandle _input_handle, _output_handle, _error_handle;
-  bool _got_error_handle;
+  FHandle _input_handle, _output_handle;
   bool _interactive_console;
   bool _interactive_console;
 
 
   typedef map<int, P3DInstance *> Instances;
   typedef map<int, P3DInstance *> Instances;

+ 9 - 0
direct/src/plugin/p3dStringObject.cxx

@@ -23,6 +23,15 @@ P3DStringObject::
 P3DStringObject(const string &value) : _value(value) {
 P3DStringObject(const string &value) : _value(value) {
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: P3DStringObject::Constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+P3DStringObject::
+P3DStringObject(const char *data, size_t size) : _value(data, size) {
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: P3DStringObject::Copy Constructor
 //     Function: P3DStringObject::Copy Constructor
 //       Access: Public
 //       Access: Public

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

@@ -25,6 +25,7 @@
 class P3DStringObject : public P3DObject {
 class P3DStringObject : public P3DObject {
 public:
 public:
   P3DStringObject(const string &value);
   P3DStringObject(const string &value);
+  P3DStringObject(const char *data, size_t size);
   P3DStringObject(const P3DStringObject &copy);
   P3DStringObject(const P3DStringObject &copy);
 
 
 public:
 public:

+ 2 - 2
direct/src/plugin/run_p3dpython.cxx

@@ -24,10 +24,10 @@
 bool
 bool
 run_p3dpython(const char *program_name, const char *archive_file,
 run_p3dpython(const char *program_name, const char *archive_file,
               FHandle input_handle, FHandle output_handle, 
               FHandle input_handle, FHandle output_handle, 
-              FHandle error_handle, bool interactive_console) {
+              const char *log_pathname, bool interactive_console) {
   P3DPythonRun::_global_ptr = 
   P3DPythonRun::_global_ptr = 
     new P3DPythonRun(program_name, archive_file, input_handle, output_handle, 
     new P3DPythonRun(program_name, archive_file, input_handle, output_handle, 
-                     error_handle, interactive_console);
+                     log_pathname, interactive_console);
   bool result = P3DPythonRun::_global_ptr->run_python();
   bool result = P3DPythonRun::_global_ptr->run_python();
   delete P3DPythonRun::_global_ptr;
   delete P3DPythonRun::_global_ptr;
   P3DPythonRun::_global_ptr = NULL;
   P3DPythonRun::_global_ptr = NULL;

+ 2 - 2
direct/src/plugin/run_p3dpython.h

@@ -29,12 +29,12 @@
 typedef bool 
 typedef bool 
 run_p3dpython_func(const char *program_name, const char *archive_file,
 run_p3dpython_func(const char *program_name, const char *archive_file,
                    FHandle input_handle, FHandle output_handle, 
                    FHandle input_handle, FHandle output_handle, 
-                   FHandle error_handle, bool interactive_console);
+                   const char *log_pathname, bool interactive_console);
 
 
 extern "C" EXPCL_P3DPYTHON bool
 extern "C" EXPCL_P3DPYTHON bool
 run_p3dpython(const char *program_name, const char *archive_file,
 run_p3dpython(const char *program_name, const char *archive_file,
               FHandle input_handle, FHandle output_handle, 
               FHandle input_handle, FHandle output_handle, 
-              FHandle error_handle, bool interactive_console);
+              const char *log_pathname, bool interactive_console);
 
 
 #endif
 #endif
 
 

+ 1 - 0
direct/src/showbase/VFSImporter.py

@@ -182,6 +182,7 @@ class VFSLoader:
         
         
         filename = Filename(self.filename)
         filename = Filename(self.filename)
         filename.setExtension('py')
         filename.setExtension('py')
+        filename.setText()
         vfile = vfs.getFile(filename)
         vfile = vfs.getFile(filename)
         if not vfile:
         if not vfile:
             raise IOError
             raise IOError