Browse Source

inbound scripting controls

David Rose 16 years ago
parent
commit
ed037be160

+ 20 - 2
direct/src/plugin/load_plugin.cxx

@@ -115,6 +115,8 @@ find_extension_dot(const string &filename) {
   return string::npos;
 }
 
+// Forward reference for function defined below.
+static void unload_dso();
 
 ////////////////////////////////////////////////////////////////////
 //     Function: load_plugin
@@ -265,8 +267,8 @@ load_plugin(const string &p3d_plugin_filename) {
 
 ////////////////////////////////////////////////////////////////////
 //     Function: unload_plugin
-//  Description: Removes the plugin from memory space and clears all
-//               of the pointers.
+//  Description: Calls finalize, then removes the plugin from memory
+//               space and clears all of the pointers.
 ////////////////////////////////////////////////////////////////////
 void
 unload_plugin() {
@@ -274,8 +276,24 @@ unload_plugin() {
     return;
   }
 
+  cerr << "unload_plugin called\n";
+
   P3D_finalize();
+  unload_dso();
+}
 
+////////////////////////////////////////////////////////////////////
+//     Function: unload_dso
+//  Description: Removes the plugin from memory space and clears all
+//               of the pointers.  This is only intended to be called
+//               by load_plugin(), above, in the specific case that
+//               the plugin loaded but could not successfully
+//               initialize itself.  All user code should call
+//               unload_plugin(), above, which first calls
+//               P3D_finalize().
+////////////////////////////////////////////////////////////////////
+static void
+unload_dso() {
 #ifdef _WIN32
   assert(module != NULL);
   FreeLibrary(module);

+ 13 - 0
direct/src/plugin/p3dInstance.I

@@ -71,6 +71,19 @@ get_python_version() const {
   return _python_version;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: P3DInstance::get_request_ready_func
+//       Access: Public
+//  Description: Returns a pointer to the asynchronous notification
+//               function that was passed to the constructor, if any,
+//               or NULL if asynchronous notifications are not
+//               required.
+////////////////////////////////////////////////////////////////////
+inline P3D_request_ready_func *P3DInstance::
+get_request_ready_func() const {
+  return _func;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: P3DInstance::is_started
 //       Access: Public

+ 7 - 9
direct/src/plugin/p3dInstance.cxx

@@ -88,6 +88,8 @@ P3DInstance::
 
   // TODO: Is it possible for someone to delete an instance while a
   // download is still running?  Who will crash when this happens?
+
+  nout << "deleting instance " << this << "\n" << flush;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -169,6 +171,7 @@ set_wparams(const P3DWindowParams &wparams) {
 P3DObject *P3DInstance::
 get_script_object() const {
   assert(_session != NULL);
+  nout << "Called P3DInstance::get_script_object()\n";
 
   TiXmlDocument *doc = new TiXmlDocument;
   TiXmlDeclaration *decl = new TiXmlDeclaration("1.0", "utf-8", "");
@@ -239,10 +242,10 @@ get_request() {
       // If we received a notify request, process the notification
       // immediately--it might be interesting to this instance.
       const char *message = result->_request._notify._message;
-      if (strcmp(message, "window_opened") == 0) {
+      if (strcmp(message, "onwindowopen") == 0) {
         // The process told us that it just succesfully opened its
         // window.
-        nout << "Instance " << this << " got window_opened\n" << flush;
+        nout << "Instance " << this << " got onwindowopen\n" << flush;
         _instance_window_opened = true;
         if (_splash_window != NULL) {
           nout << "Deleting splash window\n" << flush;
@@ -273,14 +276,9 @@ add_request(P3D_request *request) {
   _request_pending = true;
   RELEASE_LOCK(_request_lock);
 
-  // Asynchronous notification for anyone who cares.
-  if (_func != NULL) {
-    _func(this);
-  }
-
-  // Synchronous notification for pollers.
+  // Tell the world we've got a new request.
   P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
-  inst_mgr->signal_request_ready();
+  inst_mgr->signal_request_ready(this);
 }
 
 ////////////////////////////////////////////////////////////////////

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

@@ -64,6 +64,8 @@ public:
   inline const string &get_session_key() const;
   inline const string &get_python_version() const;
 
+  inline P3D_request_ready_func *get_request_ready_func() const;
+
   void add_package(P3DPackage *package);
   
   void start_download(P3DDownload *download);

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

@@ -38,6 +38,10 @@ P3DInstanceManager() {
   _is_initialized = false;
   _unique_session_index = 0;
 
+  _notify_thread_continue = false;
+  _started_notify_thread = false;
+  INIT_THREAD(_notify_thread);
+
 #ifdef _WIN32
   // Ensure the appropriate Windows common controls are available to
   // this application.
@@ -55,11 +59,19 @@ P3DInstanceManager() {
 ////////////////////////////////////////////////////////////////////
 P3DInstanceManager::
 ~P3DInstanceManager() {
-  // Actually, this destructor is never called, since this is a global
-  // object that never gets deleted.
+  nout << "~P3DInstanceManager\n" << flush;
+  if (_started_notify_thread) {
+    _notify_ready.acquire();
+    _notify_thread_continue = false;
+    _notify_ready.notify();
+    _notify_ready.release();
+    JOIN_THREAD(_notify_thread);
+    _started_notify_thread = false;
+  }
 
   assert(_instances.empty());
   assert(_sessions.empty());
+  nout << "done ~P3DInstanceManager\n" << flush;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -254,11 +266,29 @@ get_unique_session_index() {
 //     Function: P3DInstanceManager::signal_request_ready
 //       Access: Public
 //  Description: May be called in any thread to indicate that a new
-//               P3D_request is available in some instance.  This will
-//               wake up a sleeping wait_request() call, if any.
+//               P3D_request is available in the indicated instance.
 ////////////////////////////////////////////////////////////////////
 void P3DInstanceManager::
-signal_request_ready() {
+signal_request_ready(P3DInstance *inst) {
+  if (inst->get_request_ready_func() != NULL) {
+    // This instance requires asynchronous notifications of requests.
+    // Thus, we should tell the notify thread to wake up and make the
+    // callback.
+    _notify_ready.acquire();
+    _notify_instances.push_back(inst);
+    _notify_ready.notify();
+    _notify_ready.release();
+
+    // Oh, and we should spawn the thread if we haven't already.
+    if (!_started_notify_thread) {
+      _notify_thread_continue = true;
+      SPAWN_THREAD(_notify_thread, nt_thread_run, this);
+      _started_notify_thread = true;
+    }
+  }
+
+  // Then, wake up the main thread, in case it's sleeping on
+  // wait_request().
   _request_ready.acquire();
   _request_ready.notify();
   _request_ready.release();
@@ -287,3 +317,63 @@ get_global_ptr() {
   }
   return _global_ptr;
 }
+
+////////////////////////////////////////////////////////////////////
+//     Function: P3DInstanceManager::delete_global_ptr
+//       Access: Public, Static
+//  Description: This is called only at plugin shutdown time; it
+//               deletes the global instance manager pointer and
+//               clears it to NULL.
+////////////////////////////////////////////////////////////////////
+void P3DInstanceManager::
+delete_global_ptr() {
+  if (_global_ptr != NULL) {
+    delete _global_ptr;
+    _global_ptr = NULL;
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: P3DInstanceManager::nt_thread_run
+//       Access: Private
+//  Description: The main function for the notify thread.
+////////////////////////////////////////////////////////////////////
+void P3DInstanceManager::
+nt_thread_run() {
+  // The notify thread exists because we need to be able to send
+  // asynchronous notifications of request events.  These request
+  // events were detected in the various read threads associated with
+  // each session, but we can't call back into the host space from the
+  // read thread, since if the host immediately response to a callback
+  // by calling back into the p3d_plugin space, now we have our read
+  // thread doing stuff in here that's not related to the read thread.
+  // Even worse, some of the things it might need to do might require
+  // a separate read thread to be running!
+
+  _notify_ready.acquire();
+  while (_notify_thread_continue) {
+    NotifyInstances instances;
+    while (!_notify_instances.empty()) {
+      instances.clear();
+      instances.swap(_notify_instances);
+
+      // Go ahead and drop the lock while we make the callback, to
+      // reduce the risk of deadlock.  We don't want to be holding any
+      // locks when we call into client code.
+      _notify_ready.release();
+      NotifyInstances::iterator ni;
+      for (ni = instances.begin(); ni != instances.end(); ++ni) {
+        // TODO: a race condition here when instances are deleted.
+        P3DInstance *inst = (*ni);
+        P3D_request_ready_func *func = inst->get_request_ready_func();
+        assert(inst != NULL);
+        (*func)(inst);
+      }
+      _notify_ready.acquire();
+    }
+
+    _notify_ready.wait();
+  }
+  _notify_ready.release();
+  nout << "exiting nt_thread_run\n" << flush;
+}

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

@@ -20,6 +20,7 @@
 
 #include <set>
 #include <map>
+#include <vector>
 
 class P3DInstance;
 class P3DSession;
@@ -61,11 +62,19 @@ public:
   inline int get_num_instances() const;
 
   int get_unique_session_index();
-  void signal_request_ready();
+  void signal_request_ready(P3DInstance *inst);
 
   P3D_class_definition *make_class_definition() const;
 
   static P3DInstanceManager *get_global_ptr();
+  static void delete_global_ptr();
+
+private:
+  // The notify thread.  This thread runs only for the purpose of
+  // generating asynchronous notifications of requests, to callers who
+  // ask for it.
+  THREAD_CALLBACK_DECLARATION(P3DInstanceManager, nt_thread_run);
+  void nt_thread_run();
 
 private:
   bool _is_initialized;
@@ -84,7 +93,20 @@ private:
 
   int _unique_session_index;
 
+  // This condition var is waited on the main thread and signaled in a
+  // sub-thread when new request notices arrive.
   P3DConditionVar _request_ready;
+
+  // We may need a thread to send async request notices to callers.
+  bool _notify_thread_continue;
+  bool _started_notify_thread;
+  THREAD _notify_thread;
+  // This queue of instances that need to send notifications is
+  // protected by _notify_ready's mutex.
+  typedef vector<P3DInstance *> NotifyInstances;
+  NotifyInstances _notify_instances;
+  P3DConditionVar _notify_ready;
+
   static P3DInstanceManager *_global_ptr;
 };
 

+ 4 - 46
direct/src/plugin/p3dPythonRun.cxx

@@ -30,6 +30,7 @@ P3DPythonRun(int argc, char *argv[]) {
   _read_thread_continue = false;
   _program_continue = true;
   INIT_LOCK(_commands_lock);
+  INIT_THREAD(_read_thread);
 
   _program_name = argv[0];
   _py_argc = 1;
@@ -508,15 +509,7 @@ spawn_read_thread() {
   // anonymous pipe in Windows).
 
   _read_thread_continue = true;
-#ifdef _WIN32
-  _read_thread = CreateThread(NULL, 0, &win_rt_thread_run, this, 0, NULL);
-#else
-  pthread_attr_t attr;
-  pthread_attr_init(&attr);
-  pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM);
-  pthread_create(&_read_thread, &attr, &posix_rt_thread_run, (void *)this);
-  pthread_attr_destroy(&attr);
-#endif
+  SPAWN_THREAD(_read_thread, rt_thread_run, this);
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -529,16 +522,8 @@ join_read_thread() {
   nout << "waiting for thread\n";
   _read_thread_continue = false;
   _pipe_read.close();
-  
-#ifdef _WIN32
-  assert(_read_thread != NULL);
-  WaitForSingleObject(_read_thread, INFINITE);
-  CloseHandle(_read_thread);
-  _read_thread = NULL;
-#else
-  void *return_val;
-  pthread_join(_read_thread, &return_val);
-#endif
+
+  JOIN_THREAD(_read_thread);
   nout << "done waiting for thread\n";
 }
 
@@ -919,33 +904,6 @@ rt_thread_run() {
   }
 }
 
-#ifdef _WIN32
-////////////////////////////////////////////////////////////////////
-//     Function: P3DPythonRun::win_rt_thread_run
-//       Access: Private, Static
-//  Description: The Windows flavor of the thread callback function.
-////////////////////////////////////////////////////////////////////
-DWORD P3DPythonRun::
-win_rt_thread_run(LPVOID data) {
-  ((P3DPythonRun *)data)->rt_thread_run();
-  return 0;
-}
-#endif
-
-#ifndef _WIN32
-////////////////////////////////////////////////////////////////////
-//     Function: P3DPythonRun::posix_rt_thread_run
-//       Access: Private, Static
-//  Description: The Posix flavor of the thread callback function.
-////////////////////////////////////////////////////////////////////
-void *P3DPythonRun::
-posix_rt_thread_run(void *data) {
-  ((P3DPythonRun *)data)->rt_thread_run();
-  return NULL;
-}
-#endif
-
-
 ////////////////////////////////////////////////////////////////////
 //     Function: main
 //  Description: Starts the program running.

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

@@ -95,13 +95,8 @@ private:
 
 private:
   // This method runs only within the read thread.
-
+  THREAD_CALLBACK_DECLARATION(P3DPythonRun, rt_thread_run);
   void rt_thread_run();
-#ifdef _WIN32
-  static DWORD WINAPI win_rt_thread_run(LPVOID data);
-#else
-  static void *posix_rt_thread_run(void *data);
-#endif
 
 private:
   typedef pmap<int, P3DCInstance *> Instances;
@@ -130,11 +125,7 @@ private:
 
   bool _read_thread_continue;
   bool _program_continue;
-#ifdef _WIN32
-  HANDLE _read_thread;
-#else
-  pthread_t _read_thread;
-#endif
+  THREAD _read_thread;
 
 public:
   static P3DPythonRun *_global_ptr;

+ 5 - 52
direct/src/plugin/p3dSession.cxx

@@ -54,6 +54,7 @@ P3DSession(P3DInstance *inst) {
   _panda3d_callback = NULL;
 
   INIT_LOCK(_instances_lock);
+  INIT_THREAD(_read_thread);
 
   P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
 
@@ -471,22 +472,8 @@ void P3DSession::
 spawn_read_thread() {
   assert(!_read_thread_continue);
 
-  // We have to use direct OS calls to create the thread instead of
-  // Panda constructs, because it has to be an actual thread, not
-  // necessarily a Panda thread (we can't use Panda's simple threads
-  // implementation, because we can't get overlapped I/O on an
-  // anonymous pipe in Windows).
-
   _read_thread_continue = true;
-#ifdef _WIN32
-  _read_thread = CreateThread(NULL, 0, &win_rt_thread_run, this, 0, NULL);
-#else
-  pthread_attr_t attr;
-  pthread_attr_init(&attr);
-  pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM);
-  pthread_create(&_read_thread, &attr, &posix_rt_thread_run, (void *)this);
-  pthread_attr_destroy(&attr);
-#endif
+  SPAWN_THREAD(_read_thread, rt_thread_run, this);
   _started_read_thread = true;
 }
 
@@ -503,17 +490,8 @@ join_read_thread() {
 
   _read_thread_continue = false;
   _pipe_read.close();
-  
-#ifdef _WIN32
-  assert(_read_thread != NULL);
-  WaitForSingleObject(_read_thread, INFINITE);
-  CloseHandle(_read_thread);
-  _read_thread = NULL;
-#else
-  void *return_val;
-  pthread_join(_read_thread, &return_val);
-#endif
 
+  JOIN_THREAD(_read_thread);
   _started_read_thread = false;
 }
 
@@ -576,6 +554,7 @@ rt_handle_request(TiXmlDocument *doc) {
 
   TiXmlElement *xrequest = doc->FirstChildElement("request");
   if (xrequest != (TiXmlElement *)NULL) {
+    nout << "Handling request\n" << flush;
     int instance_id;
     if (xrequest->QueryIntAttribute("instance_id", &instance_id) == TIXML_SUCCESS) {
       // Look up the particular instance this is related to.
@@ -591,6 +570,7 @@ rt_handle_request(TiXmlDocument *doc) {
       }
       RELEASE_LOCK(_instances_lock);
     }
+    nout << "done handling request\n" << flush;
   }
 
   delete doc;
@@ -645,33 +625,6 @@ rt_terminate() {
   }
 }
 
-#ifdef _WIN32
-////////////////////////////////////////////////////////////////////
-//     Function: P3DSession::win_rt_thread_run
-//       Access: Private, Static
-//  Description: The Windows flavor of the thread callback function.
-////////////////////////////////////////////////////////////////////
-DWORD P3DSession::
-win_rt_thread_run(LPVOID data) {
-  ((P3DSession *)data)->rt_thread_run();
-  return 0;
-}
-#endif
-
-#ifndef _WIN32
-////////////////////////////////////////////////////////////////////
-//     Function: P3DSession::posix_rt_thread_run
-//       Access: Private, Static
-//  Description: The Posix flavor of the thread callback function.
-////////////////////////////////////////////////////////////////////
-void *P3DSession::
-posix_rt_thread_run(void *data) {
-  ((P3DSession *)data)->rt_thread_run();
-  return NULL;
-}
-#endif
-
-
 #ifdef _WIN32
 ////////////////////////////////////////////////////////////////////
 //     Function: P3DSession::win_create_process

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

@@ -60,17 +60,12 @@ private:
 
 private:
   // These methods run only within the read thread.
+  THREAD_CALLBACK_DECLARATION(P3DSession, rt_thread_run);
   void rt_thread_run();
   void rt_terminate();
   void rt_handle_request(TiXmlDocument *doc);
   P3D_request *rt_make_p3d_request(TiXmlElement *xrequest);
 
-#ifdef _WIN32
-  static DWORD WINAPI win_rt_thread_run(LPVOID data);
-#else
-  static void *posix_rt_thread_run(void *data);
-#endif
-
 #ifdef _WIN32
   static HANDLE 
   win_create_process(const string &program, const string &start_dir,
@@ -135,11 +130,7 @@ private:
   HandleStream _pipe_write;
 
   bool _read_thread_continue;
-#ifdef _WIN32
-  HANDLE _read_thread;
-#else
-  pthread_t _read_thread;
-#endif
+  THREAD _read_thread;
 
   friend class PackageCallback;
 };

+ 49 - 1
direct/src/plugin/p3d_lock.h

@@ -16,17 +16,42 @@
 #define P3D_LOCK_H
 
 // Provides some simple macros that implement platform-independet
-// mutex locks.
+// mutex locks, as well as platform-independent thread constructs.
 
 #ifdef _WIN32
 
 // Windows case
+
+// Locks are straightforward.
 #define LOCK CRITICAL_SECTION
 #define INIT_LOCK(lock) InitializeCriticalSection(&(lock))
 #define ACQUIRE_LOCK(lock) EnterCriticalSection(&(lock))
 #define RELEASE_LOCK(lock) LeaveCriticalSection(&(lock))
 #define DESTROY_LOCK(lock) DeleteCriticalSection(&(lock))
 
+// Threads.
+#define THREAD HANDLE
+#define INIT_THREAD(thread) (thread) = NULL;
+#define SPAWN_THREAD(thread, callback_function, this) \
+  (thread) = CreateThread(NULL, 0, &win_ ## callback_function, (this), 0, NULL)
+#define JOIN_THREAD(thread) \
+  assert((thread) != NULL); \
+  WaitForSingleObject((thread), INFINITE); \
+  CloseHandle((thread)); \
+  (thread) = NULL;
+
+// Declare this macro within your class declaration.  This implements
+// the callback function wrapper necessary to hook into the above
+// SPAWN_THREAD call.  The wrapper will in turn call the method
+// function you provide.
+#define THREAD_CALLBACK_DECLARATION(class, callback_function) \
+  static DWORD class::                                        \
+  win_ ## callback_function(LPVOID data) {        \
+    ((class *)data)->callback_function();       \
+    return 0;                                   \
+  }
+
+
 #else  // _WIN32
 
 // Posix case
@@ -47,6 +72,29 @@
 #define RELEASE_LOCK(lock) pthread_mutex_unlock(&(lock))
 #define DESTROY_LOCK(lock) pthread_mutex_destroy(&(lock))
 
+#define THREAD pthread_t
+#define INIT_THREAD(thread) (thread) = 0;
+#define SPAWN_THREAD(thread, callback_function, this) \
+  pthread_attr_t attr; \
+  pthread_attr_init(&attr); \
+  pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM); \
+  pthread_create(&(thread), &attr, &posix_ ## callback_function, (void *)(this)); \
+  pthread_attr_destroy(&attr);
+
+#define JOIN_THREAD(thread) \
+  assert((thread) != 0); \
+  void *return_val; \
+pthread_join((thread), &return_val); \
+  (thread) = 0;
+
+// As above, declare this macro within your class declaration.
+#define THREAD_CALLBACK_DECLARATION(class, callback_function) \
+  static void *class::                                               \
+  posix_ ## callback_function(void *data) {        \
+    ((class *)data)->callback_function();       \
+    return NULL;                                   \
+  }
+
 #endif  // _WIN32
 
 #endif

+ 42 - 41
direct/src/plugin/p3d_plugin.cxx

@@ -29,7 +29,7 @@
 // module from parallel access by multiple threads in the host.
 
 bool initialized_lock = false;
-LOCK _lock;
+LOCK _api_lock;
 
 ofstream log;
 string plugin_output_filename;
@@ -44,10 +44,10 @@ P3D_initialize(int api_version, const char *output_filename) {
   }
 
   if (!initialized_lock) {
-    INIT_LOCK(_lock);
+    INIT_LOCK(_api_lock);
     initialized_lock = true;
   }
-  ACQUIRE_LOCK(_lock);
+  ACQUIRE_LOCK(_api_lock);
 
   plugin_output_filename = string();
   if (output_filename != NULL) {
@@ -63,21 +63,22 @@ P3D_initialize(int api_version, const char *output_filename) {
 
   P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
   bool result = inst_mgr->initialize();
-  RELEASE_LOCK(_lock);
+  RELEASE_LOCK(_api_lock);
   return result;
 }
 
 void 
 P3D_finalize() {
+  P3DInstanceManager::delete_global_ptr();
 }
 
 P3D_instance *
 P3D_new_instance(P3D_request_ready_func *func, void *user_data) {
   assert(P3DInstanceManager::get_global_ptr()->is_initialized());
-  ACQUIRE_LOCK(_lock);
+  ACQUIRE_LOCK(_api_lock);
   P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
   P3DInstance *result = inst_mgr->create_instance(func, user_data);
-  RELEASE_LOCK(_lock);
+  RELEASE_LOCK(_api_lock);
   return result;
 }
 
@@ -88,21 +89,21 @@ P3D_instance_start(P3D_instance *instance, const char *p3d_filename,
   if (p3d_filename == NULL) {
     p3d_filename = "";
   }
-  ACQUIRE_LOCK(_lock);
+  ACQUIRE_LOCK(_api_lock);
   P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
   bool result = inst_mgr->start_instance
     ((P3DInstance *)instance, p3d_filename, tokens, num_tokens);
-  RELEASE_LOCK(_lock);
+  RELEASE_LOCK(_api_lock);
   return result;
 }
 
 void
 P3D_instance_finish(P3D_instance *instance) {
   assert(P3DInstanceManager::get_global_ptr()->is_initialized());
-  ACQUIRE_LOCK(_lock);
+  ACQUIRE_LOCK(_api_lock);
   P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
   inst_mgr->finish_instance((P3DInstance *)instance);
-  RELEASE_LOCK(_lock);
+  RELEASE_LOCK(_api_lock);
 }
 
 void
@@ -115,97 +116,97 @@ P3D_instance_setup_window(P3D_instance *instance,
   P3DWindowParams wparams(window_type, win_x, win_y,
                           win_width, win_height, parent_window);
 
-  ACQUIRE_LOCK(_lock);
+  ACQUIRE_LOCK(_api_lock);
   ((P3DInstance *)instance)->set_wparams(wparams);
-  RELEASE_LOCK(_lock);
+  RELEASE_LOCK(_api_lock);
 }
 
 P3D_class_definition *
 P3D_make_class_definition() {
   assert(P3DInstanceManager::get_global_ptr()->is_initialized());
-  ACQUIRE_LOCK(_lock);
+  ACQUIRE_LOCK(_api_lock);
 
   P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
   P3D_class_definition *result = inst_mgr->make_class_definition();
   
-  RELEASE_LOCK(_lock);
+  RELEASE_LOCK(_api_lock);
   return result;
 }
 
 P3D_object *
 P3D_new_none_object() {
   assert(P3DInstanceManager::get_global_ptr()->is_initialized());
-  ACQUIRE_LOCK(_lock);
+  ACQUIRE_LOCK(_api_lock);
 
   P3D_object *result = new P3DNoneObject();
   
-  RELEASE_LOCK(_lock);
+  RELEASE_LOCK(_api_lock);
   return result;
 }
 
 P3D_object *
 P3D_new_bool_object(bool value) {
   assert(P3DInstanceManager::get_global_ptr()->is_initialized());
-  ACQUIRE_LOCK(_lock);
+  ACQUIRE_LOCK(_api_lock);
 
   P3D_object *result = new P3DBoolObject(value);
   
-  RELEASE_LOCK(_lock);
+  RELEASE_LOCK(_api_lock);
   return result;
 }
 
 P3D_object *
 P3D_new_int_object(int value) {
   assert(P3DInstanceManager::get_global_ptr()->is_initialized());
-  ACQUIRE_LOCK(_lock);
+  ACQUIRE_LOCK(_api_lock);
 
   P3D_object *result = new P3DIntObject(value);
   
-  RELEASE_LOCK(_lock);
+  RELEASE_LOCK(_api_lock);
   return result;
 }
 
 P3D_object *
 P3D_new_float_object(double value) {
   assert(P3DInstanceManager::get_global_ptr()->is_initialized());
-  ACQUIRE_LOCK(_lock);
+  ACQUIRE_LOCK(_api_lock);
 
   P3D_object *result = new P3DFloatObject(value);
   
-  RELEASE_LOCK(_lock);
+  RELEASE_LOCK(_api_lock);
   return result;
 }
 
 P3D_object *
 P3D_new_string_object(const char *str, int length) {
   assert(P3DInstanceManager::get_global_ptr()->is_initialized());
-  ACQUIRE_LOCK(_lock);
+  ACQUIRE_LOCK(_api_lock);
 
   P3D_object *result = new P3DStringObject(string(str, length));
   
-  RELEASE_LOCK(_lock);
+  RELEASE_LOCK(_api_lock);
   return result;
 }
 
 P3D_object *
 P3D_new_list_object() {
   assert(P3DInstanceManager::get_global_ptr()->is_initialized());
-  ACQUIRE_LOCK(_lock);
+  ACQUIRE_LOCK(_api_lock);
 
   P3D_object *result = new P3DListObject;
   
-  RELEASE_LOCK(_lock);
+  RELEASE_LOCK(_api_lock);
   return result;
 }
 
 P3D_object *
 P3D_instance_get_script_object(P3D_instance *instance) {
   assert(P3DInstanceManager::get_global_ptr()->is_initialized());
-  ACQUIRE_LOCK(_lock);
+  ACQUIRE_LOCK(_api_lock);
 
   P3D_object *result = ((P3DInstance *)instance)->get_script_object();
   
-  RELEASE_LOCK(_lock);
+  RELEASE_LOCK(_api_lock);
   return result;
 }
 
@@ -213,55 +214,55 @@ void
 P3D_instance_set_script_object(P3D_instance *instance, 
                                P3D_object *object) {
   assert(P3DInstanceManager::get_global_ptr()->is_initialized());
-  ACQUIRE_LOCK(_lock);
+  ACQUIRE_LOCK(_api_lock);
 
   // TODO.
   
-  RELEASE_LOCK(_lock);
+  RELEASE_LOCK(_api_lock);
 }
 
 
 P3D_request *
 P3D_instance_get_request(P3D_instance *instance) {
   assert(P3DInstanceManager::get_global_ptr()->is_initialized());
-  ACQUIRE_LOCK(_lock);
+  ACQUIRE_LOCK(_api_lock);
   P3D_request *result = ((P3DInstance *)instance)->get_request();
-  RELEASE_LOCK(_lock);
+  RELEASE_LOCK(_api_lock);
   return result;
 }
 
 P3D_instance *
 P3D_check_request(bool wait) {
   assert(P3DInstanceManager::get_global_ptr()->is_initialized());
-  ACQUIRE_LOCK(_lock);
+  ACQUIRE_LOCK(_api_lock);
   P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
   P3D_instance *inst = inst_mgr->check_request();
 
   if (inst != NULL || !wait) {
-    RELEASE_LOCK(_lock);
+    RELEASE_LOCK(_api_lock);
     return inst;
   }
   
   // Now we have to block until a request is available.
   while (inst == NULL && inst_mgr->get_num_instances() != 0) {
-    RELEASE_LOCK(_lock);
+    RELEASE_LOCK(_api_lock);
     inst_mgr->wait_request();
-    ACQUIRE_LOCK(_lock);
+    ACQUIRE_LOCK(_api_lock);
     inst = inst_mgr->check_request();
   }
 
-  RELEASE_LOCK(_lock);
+  RELEASE_LOCK(_api_lock);
   return inst;
 }
 
 void
 P3D_request_finish(P3D_request *request, bool handled) {
   assert(P3DInstanceManager::get_global_ptr()->is_initialized());
-  ACQUIRE_LOCK(_lock);
+  ACQUIRE_LOCK(_api_lock);
   if (request != (P3D_request *)NULL) {
     ((P3DInstance *)request->_instance)->finish_request(request, handled);
   }
-  RELEASE_LOCK(_lock);
+  RELEASE_LOCK(_api_lock);
 }
 
 bool
@@ -272,11 +273,11 @@ P3D_instance_feed_url_stream(P3D_instance *instance, int unique_id,
                              const void *this_data, 
                              size_t this_data_size) {
   assert(P3DInstanceManager::get_global_ptr()->is_initialized());
-  ACQUIRE_LOCK(_lock);
+  ACQUIRE_LOCK(_api_lock);
   bool result = ((P3DInstance *)instance)->
     feed_url_stream(unique_id, result_code, http_status_code,
                     total_expected_data, 
                     (const unsigned char *)this_data, this_data_size);
-  RELEASE_LOCK(_lock);
+  RELEASE_LOCK(_api_lock);
   return result;
 }

+ 24 - 8
direct/src/plugin_npapi/ppInstance.cxx

@@ -79,6 +79,10 @@ PPInstance::
     _p3d_inst = NULL;
   }
 
+  if (_script_object != NULL) {
+    browser->releaseobject(_script_object);
+  }
+
   // Free the tokens we allocated.
   Tokens::iterator ti;
   for (ti = _tokens.begin(); ti != _tokens.end(); ++ti) {
@@ -386,6 +390,22 @@ handle_request(P3D_request *request) {
     }
     break;
 
+  case P3D_RT_notify:
+    {
+      logfile << "Got P3D_RT_notify: " << request->_request._notify._message
+              << "\n" << flush;
+
+      if (_script_object != NULL &&
+          strcmp(request->_request._notify._message, "onpythonload") == 0) {
+        // Now that Python is running, initialize our script_object
+        // with the proper P3D object pointer.
+        P3D_object *obj = P3D_instance_get_script_object(_p3d_inst);
+        logfile << "late obj = " << obj << "\n" << flush;
+        _script_object->set_p3d_object(obj);
+      }
+    }
+    break;
+
   default:
     // Some request types are not handled.
     logfile << "Unhandled request: " << request->_request_type << "\n";
@@ -405,6 +425,7 @@ NPObject *PPInstance::
 get_script_object() {
   logfile << "get_script_object\n" << flush;
   if (_script_object != NULL) {
+    logfile << "returning _script_object ref = " << _script_object->referenceCount << "\n";
     return _script_object;
   }
 
@@ -416,6 +437,9 @@ get_script_object() {
   }
 
   _script_object = PPObject::make_new(this, obj);
+  logfile << "_script_object ref = " << _script_object->referenceCount << "\n";
+  browser->retainobject(_script_object);
+  logfile << "after retain, _script_object ref = " << _script_object->referenceCount << "\n";
   logfile << "ppobj = " << _script_object << "\n" << flush;
   return _script_object;
 }
@@ -601,14 +625,6 @@ create_instance() {
     }
     P3D_instance_start(_p3d_inst, _p3d_filename.c_str(), tokens, _tokens.size());
     send_window();
-
-    if (_script_object != NULL) {
-      // Now that we have an instance, initialize our script_object
-      // with the proper P3D object pointer.
-      P3D_object *obj = P3D_instance_get_script_object(_p3d_inst);
-      logfile << "late obj = " << obj << "\n" << flush;
-      _script_object->set_p3d_object(obj);
-    }
   }
 }
 

+ 11 - 4
direct/src/plugin_npapi/startup.cxx

@@ -87,7 +87,7 @@ window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
 
 ////////////////////////////////////////////////////////////////////
 //     Function: request_ready
-//  Description: This function is attached as an asyncronous callback
+//  Description: This function is attached as an asynchronous callback
 //               to each instance; it will be notified when the
 //               instance has a request ready.  This function may be
 //               called in a sub-thread.
@@ -99,14 +99,17 @@ request_ready(P3D_instance *instance) {
     //    << " thread = " << GetCurrentThreadId()
     << "\n" << flush;
 
+#ifdef _WIN32
   // Since we might be in a sub-thread at this point, use a Windows
   // message to forward this event to the main thread.
 
-#ifdef _WIN32
   PostThreadMessage(main_thread_id, WM_USER, 0, 0);
+
 #else
-  // TODO: send the message to the main thread properly.
-  handle_request_loop();
+  // On Mac, we ignore this asynchronous event, and rely on detecting
+  // it within HandleEvent().  TODO: enable a timer to ensure we get
+  // HandleEvent callbacks in a timely manner?  Or maybe we should
+  // enable a one-shot timer in response to this asynchronous event?
 #endif
 }
 
@@ -366,6 +369,10 @@ NPP_Print(NPP instance, NPPrint *platformPrint) {
 int16
 NPP_HandleEvent(NPP instance, void *event) {
   //  logfile << "HandleEvent\n";
+
+  // Here's a fine opportunity to check for new requests.
+  handle_request_loop();
+
   return 0;
 }
 

+ 6 - 2
direct/src/plugin_standalone/panda3d.cxx

@@ -251,7 +251,8 @@ run(int argc, char *argv[]) {
   }
 
   // All instances have finished; we can exit.
-
+  cerr << "clean exit\n";
+  unload_plugin();
   return 0;
 }
 
@@ -404,7 +405,8 @@ handle_request(P3D_request *request) {
     cerr << "Got P3D_RT_notify: " << request->_request._notify._message
          << "\n";
     // Ignore notifications.
-
+    
+    /*
     {
       // Temporary.
       P3D_object *obj = P3D_instance_get_script_object(request->_instance);
@@ -428,6 +430,8 @@ handle_request(P3D_request *request) {
         P3D_OBJECT_FINISH(obj);
       }
     }
+    */
+
     break;
 
   default:

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

@@ -22,7 +22,7 @@ See pack3d.py for a script that generates these p3d files.
 import sys
 from direct.showbase import VFSImporter
 from direct.showbase.DirectObject import DirectObject
-from pandac.PandaModules import VirtualFileSystem, Filename, Multifile, loadPrcFileData, getModelPath, HTTPClient
+from pandac.PandaModules import VirtualFileSystem, Filename, Multifile, loadPrcFileData, unloadPrcFile, getModelPath, HTTPClient
 from direct.stdpy import file
 from direct.task.TaskManagerGlobal import taskMgr
 from direct.showbase import AppRunnerGlobal
@@ -64,6 +64,9 @@ class AppRunner(DirectObject):
         # This is the default requestFunc that is installed if we
         # never call setRequestFunc().
         def defaultRequestFunc(*args):
+            if args[1] == 'notify':
+                # Quietly ignore notifies.
+                return
             print "Ignoring request: %s" % (args,)
         self.requestFunc = defaultRequestFunc
 
@@ -149,6 +152,10 @@ class AppRunner(DirectObject):
         # for this instance.
         self.instanceId = instanceId
         
+        # Now that we have an instanceId, we can response to queries
+        # and such.
+        self.sendRequest('notify', 'onpythonload')
+
         tokenDict = dict(tokens)
         fname = Filename.fromOsSpecific(p3dFilename)
         if not p3dFilename:
@@ -246,7 +253,7 @@ class AppRunner(DirectObject):
     def windowEvent(self, win):
         print "Got window event in runp3d"
 
-        self.sendRequest('notify', 'window_opened')
+        self.sendRequest('notify', 'onwindowopen')
 
     def parseSysArgs(self):
         """ Converts sys.argv into (p3dFilename, tokens). """