Przeglądaj źródła

attempts to make windows plugin more stable

David Rose 16 lat temu
rodzic
commit
d5b4f7810e

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

@@ -22,8 +22,9 @@
     fileSpec.cxx fileSpec.h fileSpec.I \
     find_root_dir.cxx find_root_dir.h \
     get_tinyxml.h \
+    binaryXml.cxx binaryXml.h \
     handleStream.cxx handleStream.h handleStream.I \
-    handleStreamBuf.cxx handleStreamBuf.h \
+    handleStreamBuf.cxx handleStreamBuf.h handleStreamBuf.I \
     mkdir_complete.cxx mkdir_complete.h \
     p3d_lock.h p3d_plugin.h \
     p3d_plugin_config.h \
@@ -98,14 +99,17 @@
     pipeline:c event:c nativenet:c panda:m
 
   #define SOURCES \
+    binaryXml.cxx binaryXml.h \
     handleStream.cxx handleStream.h handleStream.I \
-    handleStreamBuf.cxx handleStreamBuf.h \
+    handleStreamBuf.cxx handleStreamBuf.h handleStreamBuf.I \
     p3d_lock.h p3d_plugin.h \
     p3d_plugin_config.h \
     p3dCInstance.cxx \
     p3dCInstance.h p3dCInstance.I \
     p3dPythonRun.cxx p3dPythonRun.h p3dPythonRun.I
 
+  #define WIN_SYS_LIBS user32.lib
+
 #end bin_target
 
 #begin static_lib_target

+ 142 - 0
direct/src/plugin/binaryXml.cxx

@@ -0,0 +1,142 @@
+// Filename: binaryXml.cxx
+// Created by:  drose (13Jul09)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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 "binaryXml.h"
+#include <sstream>
+
+// Actually, we haven't implemented the binary I/O for XML files yet.
+// We just map these directly to the classic formatted I/O for now.
+
+static const bool debug_xml_output = true;
+
+////////////////////////////////////////////////////////////////////
+//     Function: write_xml
+//  Description: Writes the indicated TinyXml document to the given
+//               stream.
+////////////////////////////////////////////////////////////////////
+void
+write_xml(HandleStream &out, TiXmlDocument *doc, ostream &logfile) {
+  ostringstream strm;
+  strm << *doc;
+  string data = strm.str();
+
+  size_t length = data.length();
+  out.write((char *)&length, sizeof(length));
+  out.write(data.data(), length);
+  out << flush;
+
+  if (debug_xml_output) {
+    logfile << "sent: " << data << "\n" << flush;
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: read_xml
+//  Description: Reads a TinyXml document from the given stream, and
+//               returns it.  If the document is not yet available,
+//               blocks until it is, or until there is an error
+//               condition on the input.
+//
+//               The return value is NULL if there is an error, or the
+//               newly-allocated document if it is successfully read.
+//               If not NULL, the document has been allocated with
+//               new, and should be eventually freed by the caller
+//               with delete.
+////////////////////////////////////////////////////////////////////
+TiXmlDocument *
+read_xml(HandleStream &in, ostream &logfile) {
+
+#ifdef _WIN32
+  HANDLE handle = in.get_handle();
+
+  size_t length;
+  DWORD bytes_read = 0;
+  logfile << "ReadFile\n" << flush;
+  BOOL success = ReadFile(handle, &length, sizeof(length), &bytes_read, NULL);
+  logfile << "done ReadFile\n" << flush;
+  if (!success) {
+    DWORD error = GetLastError();
+    if (error != ERROR_HANDLE_EOF && error != ERROR_BROKEN_PIPE) {
+      logfile << "Error reading " << sizeof(length)
+              << " bytes, windows error code 0x" << hex
+              << error << dec << ".\n";
+    }
+    return NULL;
+  }
+  assert(bytes_read == sizeof(length));
+
+  if (debug_xml_output) {
+    ostringstream logout;
+    logout << "reading " << length << " bytes\n";
+    logfile << logout.str() << flush;
+  }
+
+  char *buffer = new char[length];
+
+  bytes_read = 0;
+  success = ReadFile(handle, buffer, length, &bytes_read, NULL);
+  if (!success) {
+    DWORD error = GetLastError();
+    if (error != ERROR_HANDLE_EOF && error != ERROR_BROKEN_PIPE) {
+      logfile << "Error reading " << length
+              << " bytes, windows error code 0x" << hex
+              << error << dec << ".\n";
+    }
+    delete[] buffer;
+    return NULL;
+  }
+  assert(bytes_read == length);
+
+  string data(buffer, length);
+  delete[] buffer;
+
+#else
+  size_t length;
+  in.read((char *)&length, sizeof(length));
+  if (in.gcount() != sizeof(length)) {
+    logfile << "read " << in.gcount() << " bytes instead of " << sizeof(length)
+            << "\n";
+    return NULL;
+  }
+
+  if (debug_xml_output) {
+    ostringstream logout;
+    logout << "reading " << length << " bytes\n";
+    logfile << logout.str() << flush;
+  }
+
+  char *buffer = new char[length];
+  in.read(buffer, length);
+  if (in.gcount() != length) {
+    delete[] buffer;
+    return NULL;
+  }
+
+  string data(buffer, length);
+  delete[] buffer;
+
+#endif  // _WIN32
+
+  istringstream strm(data);
+  TiXmlDocument *doc = new TiXmlDocument;
+  strm >> *doc;
+
+  if (debug_xml_output) {
+    ostringstream logout;
+    logout << "received: " << *doc << "\n";
+    logfile << logout.str() << flush;
+  }
+    
+  return doc;
+}

+ 32 - 0
direct/src/plugin/binaryXml.h

@@ -0,0 +1,32 @@
+// Filename: binaryXml.h
+// Created by:  drose (13Jul09)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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 BINARYXML_H
+#define BINARYXML_H
+
+#include "get_tinyxml.h"
+#include "handleStream.h"
+#include <iostream>
+
+using namespace std;
+
+// A pair of functions to input and output the TinyXml constructs on
+// the indicated streams.  We could, of course, use the TinyXml output
+// operators, but this is a smidge more efficient and gives us more
+// control.
+
+void write_xml(HandleStream &out, TiXmlDocument *doc, ostream &logfile);
+TiXmlDocument *read_xml(HandleStream &in, ostream &logfile);
+
+#endif

+ 16 - 5
direct/src/plugin/handleStream.I

@@ -14,7 +14,7 @@
 
 ////////////////////////////////////////////////////////////////////
 //     Function: HandleStream::Constructor
-//       Access: Published
+//       Access: Public
 //  Description:
 ////////////////////////////////////////////////////////////////////
 inline HandleStream::
@@ -23,7 +23,7 @@ HandleStream() : iostream(&_buf) {
 
 ////////////////////////////////////////////////////////////////////
 //     Function: HandleStream::Destructor
-//       Access: Published
+//       Access: Public
 //  Description:
 ////////////////////////////////////////////////////////////////////
 inline HandleStream::
@@ -33,7 +33,7 @@ inline HandleStream::
 
 ////////////////////////////////////////////////////////////////////
 //     Function: HandleStream::open_read
-//       Access: Published
+//       Access: Public
 //  Description: Attempts to open the given handle for input.  The
 //               stream may not be simultaneously open for input and
 //               output.
@@ -49,7 +49,7 @@ open_read(FHandle handle) {
 
 ////////////////////////////////////////////////////////////////////
 //     Function: HandleStream::open_write
-//       Access: Published
+//       Access: Public
 //  Description: Attempts to open the given handle for output.  The
 //               stream may not be simultaneously open for input and
 //               output.
@@ -65,10 +65,21 @@ open_write(FHandle handle) {
 
 ////////////////////////////////////////////////////////////////////
 //     Function: HandleStream::close
-//       Access: Published
+//       Access: Public
 //  Description: 
 ////////////////////////////////////////////////////////////////////
 inline void HandleStream::
 close() {
   _buf.close();
 }
+
+////////////////////////////////////////////////////////////////////
+//     Function: HandleStream::get_handle
+//       Access: Public
+//  Description: Returns the handle that was passed to open_read() or
+//               open_write().
+////////////////////////////////////////////////////////////////////
+inline FHandle HandleStream::
+get_handle() const {
+  return _buf.get_handle();
+}

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

@@ -33,6 +33,8 @@ public:
   inline void open_write(FHandle handle);
   inline void close();
 
+  inline FHandle get_handle() const;
+
 private:
   HandleStreamBuf _buf;
 };

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

@@ -12,3 +12,14 @@
 //
 ////////////////////////////////////////////////////////////////////
 
+
+////////////////////////////////////////////////////////////////////
+//     Function: HandleStreamBuf::get_handle
+//       Access: Public
+//  Description: Returns the handle that was passed to open_read() or
+//               open_write().
+////////////////////////////////////////////////////////////////////
+inline FHandle HandleStreamBuf::
+get_handle() const {
+  return _handle;
+}

+ 4 - 1
direct/src/plugin/handleStreamBuf.h

@@ -23,7 +23,6 @@ typedef HANDLE FHandle;
 typedef int FHandle;
 #endif
 
-#include <iostream>
 #include <iostream>
 
 using namespace std;
@@ -43,6 +42,8 @@ public:
   bool is_open_write() const;
   void close();
 
+  inline FHandle get_handle() const;
+
 protected:
   virtual int overflow(int c);
   virtual int sync();
@@ -61,4 +62,6 @@ private:
   char *_buffer;
 };
 
+#include "handleStreamBuf.I"
+
 #endif

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

@@ -361,6 +361,7 @@ bake_requests() {
 ////////////////////////////////////////////////////////////////////
 void P3DInstance::
 add_raw_request(TiXmlDocument *doc) {
+  nout << "add_raw_request " << this << "\n" << flush;
   ACQUIRE_LOCK(_request_lock);
   _raw_requests.push_back(doc);
   _request_pending = true;
@@ -374,6 +375,7 @@ add_raw_request(TiXmlDocument *doc) {
   P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
   inst_mgr->signal_request_ready(this);
   _session->signal_request_ready(this);
+  nout << "done add_raw_request " << this << "\n" << flush;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -640,6 +642,35 @@ request_stop() {
   }
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: P3DInstance::pump_messages
+//       Access: Public
+//  Description: Windows only: pump the message queue on this
+//               instance's parent window, so that any child-window
+//               operations will be able to continue.
+////////////////////////////////////////////////////////////////////
+void P3DInstance::
+pump_messages() {
+#ifdef _WIN32
+  if (_got_wparams) {
+    HWND hwnd = _wparams.get_parent_window()._hwnd;
+    if (hwnd != NULL) {
+      MSG msg;
+      nout << "  peeking " << hwnd << "\n" << flush;
+
+      // It appears to be bad to pump messages for any other
+      // window--Mozilla is apparently not reentrant in this way.
+      if (PeekMessage(&msg, hwnd, 0, 0, PM_REMOVE | PM_NOYIELD)) {
+        nout << "  pumping " << msg.message << "\n" << flush;
+        TranslateMessage(&msg);
+        DispatchMessage(&msg);
+        nout << "  done pumping\n" << flush;
+      }
+    }
+  }
+#endif  // _WIN32
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: P3DInstance::make_xml
 //       Access: Public

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

@@ -82,6 +82,7 @@ public:
   void start_download(P3DDownload *download);
   inline bool is_started() const;
   void request_stop();
+  void pump_messages();
 
   TiXmlElement *make_xml();
 

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

@@ -14,6 +14,7 @@
 
 #include "p3dPythonRun.h"
 #include "asyncTaskManager.h"
+#include "binaryXml.h"
 
 // There is only one P3DPythonRun object in any given process space.
 // Makes the statics easier to deal with, and we don't need multiple
@@ -299,8 +300,7 @@ handle_command(TiXmlDocument *doc) {
           xresponse->SetAttribute("response_id", want_response_id);
           doc.LinkEndChild(decl);
           doc.LinkEndChild(xresponse);
-          nout << "sent: " << doc << "\n" << flush;
-          _pipe_write << doc << flush;
+          write_xml(_pipe_write, &doc, nout);
         }
       }
     }
@@ -473,8 +473,7 @@ handle_pyobj_command(TiXmlElement *xcommand, bool needs_response,
   }
 
   if (needs_response) {
-    nout << "sent: " << doc << "\n" << flush;
-    _pipe_write << doc << flush;
+    write_xml(_pipe_write, &doc, nout);
   }
 }
 
@@ -488,6 +487,7 @@ handle_pyobj_command(TiXmlElement *xcommand, bool needs_response,
 ////////////////////////////////////////////////////////////////////
 void P3DPythonRun::
 check_comm() {
+  nout << ":" << flush;
   ACQUIRE_LOCK(_commands_lock);
   while (!_commands.empty()) {
     TiXmlDocument *doc = _commands.front();
@@ -532,7 +532,7 @@ task_check_comm(GenericAsyncTask *task, void *user_data) {
 ////////////////////////////////////////////////////////////////////
 TiXmlDocument *P3DPythonRun::
 wait_script_response(int response_id) {
-  nout << "Waiting script_response " << response_id << "\n";
+  nout << "waiting script_response " << response_id << "\n" << flush;
   while (true) {
     ACQUIRE_LOCK(_commands_lock);
     
@@ -550,7 +550,7 @@ wait_script_response(int response_id) {
               // This is the response we were waiting for.
               _commands.erase(ci);
               RELEASE_LOCK(_commands_lock);
-              nout << "received script_response: " << *doc << "\n" << flush;
+              nout << "got script_response " << unique_id << "\n" << flush;
               return doc;
             }
           }
@@ -564,6 +564,7 @@ wait_script_response(int response_id) {
           // This command will be wanting a response.  We'd better
           // honor it right away, or we risk deadlock with the browser
           // process and the Python process waiting for each other.
+          nout << "honoring response " << want_response_id << "\n" << flush;
           _commands.erase(ci);
           RELEASE_LOCK(_commands_lock);
           handle_command(doc);
@@ -580,6 +581,27 @@ wait_script_response(int response_id) {
     
     RELEASE_LOCK(_commands_lock);
 
+#ifdef _WIN32
+    // Make sure we process the Windows event loop while we're
+    // waiting, or everything that depends on Windows messages will
+    // starve.
+
+    // We appear to be best off with just a single PeekMessage() call
+    // here; the full message pump seems to cause problems.
+    MSG msg;
+    PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE | PM_NOYIELD);
+    /*
+      if (PeekMessage(&msg, hwnd, 0, 0, PM_REMOVE | PM_NOYIELD)) {
+        nout << "  pumping " << msg.message << "\n" << flush;
+        TranslateMessage(&msg);
+        DispatchMessage(&msg);
+        nout << "  done pumping\n" << flush;
+      }
+    */
+#endif  // _WIN32
+
+    nout << "." << flush;
+
     // It hasn't shown up yet.  Give the sub-thread a chance to
     // process the input and append it to the queue.
     Thread::force_yield();
@@ -648,8 +670,7 @@ py_request_func(PyObject *args) {
     }
 
     xrequest->SetAttribute("message", message);
-    nout << "sent: " << doc << "\n" << flush;
-    _pipe_write << doc << flush;
+    write_xml(_pipe_write, &doc, nout);
 
   } else if (strcmp(request_type, "script") == 0) {
     // Meddling with a scripting variable on the browser side.
@@ -693,8 +714,7 @@ py_request_func(PyObject *args) {
       xrequest->LinkEndChild(xvalue);
     }
 
-    nout << "sent: " << doc << "\n" << flush;
-    _pipe_write << doc << flush;
+    write_xml(_pipe_write, &doc, nout);
 
   } else if (strcmp(request_type, "drop_p3dobj") == 0) {
     // Release a particular P3D_object that we were holding a
@@ -706,8 +726,7 @@ py_request_func(PyObject *args) {
     nout << "got drop_p3dobj(" << object_id << ")\n" << flush;
 
     xrequest->SetAttribute("object_id", object_id);
-    nout << "sent: " << doc << "\n" << flush;
-    _pipe_write << doc << flush;
+    write_xml(_pipe_write, &doc, nout);
 
   } else {
     string message = string("Unsupported request type: ") + string(request_type);
@@ -1150,10 +1169,8 @@ xml_to_pyobj(TiXmlElement *xvalue) {
 void P3DPythonRun::
 rt_thread_run() {
   while (_read_thread_continue) {
-    TiXmlDocument *doc = new TiXmlDocument;
-
-    _pipe_read >> *doc;
-    if (!_pipe_read || _pipe_read.eof()) {
+    TiXmlDocument *doc = read_xml(_pipe_read, nout);
+    if (doc == NULL) {
       // Some error on reading.  Abort.
       _program_continue = false;
       return;

+ 27 - 19
direct/src/plugin/p3dSession.cxx

@@ -22,6 +22,7 @@
 #include "p3dIntObject.h"
 #include "p3dFloatObject.h"
 #include "p3dPythonObject.h"
+#include "binaryXml.h"
 
 #ifndef _WIN32
 #include <fcntl.h>
@@ -94,8 +95,7 @@ shutdown() {
     xcommand->SetAttribute("cmd", "exit");
     doc.LinkEndChild(decl);
     doc.LinkEndChild(xcommand);
-    nout << "sent: " << doc << "\n" << flush;
-    _pipe_write << doc << flush;
+    write_xml(_pipe_write, &doc, nout);
 
     // Also close the pipe, to help underscore the point.
     _pipe_write.close();
@@ -226,8 +226,7 @@ void P3DSession::
 send_command(TiXmlDocument *command) {
   if (_p3dpython_running) {
     // Python is running.  Send the command.
-    nout << "sent: " << *command << "\n" << flush;
-    _pipe_write << *command << flush;
+    write_xml(_pipe_write, command, nout);
     delete command;
   } else {
     // Python not yet running.  Queue up the command instead.
@@ -265,13 +264,14 @@ command_and_response(TiXmlDocument *command) {
   assert(xcommand != NULL);
   xcommand->SetAttribute("want_response_id", response_id);
 
-  nout << "sent: " << *command << "\n" << flush;
-  _pipe_write << *command << flush;
+  write_xml(_pipe_write, command, nout);
   delete command;
 
   // Now block, waiting for a response to be delivered.  We assume
   // only one thread will be waiting at a time.
   nout << "waiting for response " << response_id << "\n" << flush;
+  int tick_start = GetTickCount();
+
   _response_ready.acquire();
   Responses::iterator ri = _responses.find(response_id);
   while (ri == _responses.end()) {
@@ -296,6 +296,7 @@ command_and_response(TiXmlDocument *command) {
     for (ii = _instances.begin(); ii != _instances.end(); ++ii) {
       P3DInstance *inst = (*ii).second;
       inst->bake_requests();
+      inst->pump_messages();
     }
     _response_ready.acquire();
 
@@ -312,9 +313,18 @@ command_and_response(TiXmlDocument *command) {
     // particular, the CreateWindow() call within the subprocess--will
     // starve, and we could end up with deadlock.
 
-    // A single PeekMessage() seems to be sufficient.
     MSG msg;
     PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE | PM_NOYIELD);
+    /*
+    MSG msg;
+    if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE | PM_NOYIELD)) {
+      //      nout << "  pumping " << msg.message << "\n" << flush;
+      TranslateMessage(&msg);
+      DispatchMessage(&msg);
+      //      nout << "  done pumping\n" << flush;
+    }
+    */
+      
 #endif  // _WIN32
 
     // We wait with a timeout, so we can go back and spin the event
@@ -529,8 +539,7 @@ drop_pyobj(int object_id) {
     xcommand->SetAttribute("object_id", object_id);
     doc.LinkEndChild(decl);
     doc.LinkEndChild(xcommand);
-    nout << "sent: " << doc << "\n" << flush;
-    _pipe_write << doc << flush;
+    write_xml(_pipe_write, &doc, nout);
   }
 }
 
@@ -657,20 +666,16 @@ start_p3dpython() {
   xcommand->SetAttribute("session_id", _session_id);
   doc.LinkEndChild(decl);
   doc.LinkEndChild(xcommand);
-  nout << "sent: " << doc << "\n" << flush;
-  _pipe_write << doc;
+  write_xml(_pipe_write, &doc, nout);
   
   // Also feed it any commands we may have queued up from before the
   // process was started.
   Commands::iterator ci;
   for (ci = _commands.begin(); ci != _commands.end(); ++ci) {
-    nout << "sent: " << *(*ci) << "\n" << flush;
-    _pipe_write << *(*ci);
+    write_xml(_pipe_write, (*ci), nout);
     delete (*ci);
   }
   _commands.clear();
-
-  _pipe_write << flush;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -715,10 +720,8 @@ join_read_thread() {
 void P3DSession::
 rt_thread_run() {
   while (_read_thread_continue) {
-    TiXmlDocument *doc = new TiXmlDocument;
-
-    _pipe_read >> *doc;
-    if (!_pipe_read || _pipe_read.eof()) {
+    TiXmlDocument *doc = read_xml(_pipe_read, nout);
+    if (doc == NULL) {
       // Some error on reading.  Abort.
       rt_terminate();
       return;
@@ -727,6 +730,8 @@ rt_thread_run() {
     // Successfully read an XML document.
     rt_handle_request(doc);
   }
+
+  logfile << "Exiting rt_thread_run in " << this << "\n" << flush;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -737,6 +742,7 @@ rt_thread_run() {
 ////////////////////////////////////////////////////////////////////
 void P3DSession::
 rt_handle_request(TiXmlDocument *doc) {
+  nout << "rt_handle_request in " << this << "\n" << flush;
   TiXmlElement *xresponse = doc->FirstChildElement("response");
   if (xresponse != (TiXmlElement *)NULL) {
     int response_id;
@@ -748,6 +754,7 @@ rt_handle_request(TiXmlDocument *doc) {
       assert(inserted);
       _response_ready.notify();
       _response_ready.release();
+      nout << "done a, rt_handle_request in " << this << "\n" << flush;
       return;
     }
   }
@@ -772,6 +779,7 @@ rt_handle_request(TiXmlDocument *doc) {
   if (doc != NULL) {
     delete doc;
   }
+  nout << "done rt_handle_request in " << this << "\n" << flush;
 }
 
 ////////////////////////////////////////////////////////////////////

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

@@ -200,7 +200,14 @@ stop_thread() {
   // Post a silly message to spin the message loop.
   PostThreadMessage(_thread_id, WM_USER, 0, 0);
 
-  WaitForSingleObject(_thread, INFINITE);
+  // We can't actually wait for the thread to finish, since there
+  // might a deadlock there: the thread can't finish deleting its
+  // window unless we're pumping the message loop for the parent,
+  // which won't happen if we're sitting here waiting.  No worries; we
+  // don't *really* need to wait for the thread.
+
+  //  WaitForSingleObject(_thread, INFINITE);
+
   CloseHandle(_thread);
   _thread = NULL;
 }

+ 14 - 0
direct/src/plugin/p3d_lock.h

@@ -23,11 +23,25 @@
 // Windows case
 
 // Locks are straightforward.
+class _lock {
+public:
+  CRITICAL_SECTION _l;
+  int _count;
+};
+
+#define LOCK _lock
+#define INIT_LOCK(lock) { InitializeCriticalSection(&(lock)._l); (lock)._count = 0; }
+#define ACQUIRE_LOCK(lock) { EnterCriticalSection(&(lock)._l); ++((lock)._count); if ((lock)._count > 1) { nout << "count = " << (lock)._count << "\n"; } }
+#define RELEASE_LOCK(lock) { --((lock)._count); LeaveCriticalSection(&(lock)._l); }
+#define DESTROY_LOCK(lock) DeleteCriticalSection(&(lock)._l)
+
+/*
 #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

+ 7 - 0
direct/src/plugin_npapi/nppanda3d_common.h

@@ -62,6 +62,13 @@ extern ofstream logfile;
 
 #include "load_plugin.h"
 
+// Uncomment the following to enable use of the PluginThreadAsyncCall
+// function.  (It's commented out for now to assist development of the
+// case in which this is not available.)
+#if defined(NPVERS_HAS_PLUGIN_THREAD_ASYNC_CALL) && NP_VERSION_MINOR >= NPVERS_HAS_PLUGIN_THREAD_ASYNC_CALL
+#define HAS_PLUGIN_THREAD_ASYNC_CALL 1
+#endif
+
 // Appears in startup.cxx.
 extern NPNetscapeFuncs *browser;
 

+ 63 - 34
direct/src/plugin_npapi/ppInstance.cxx

@@ -436,31 +436,6 @@ handle_request(P3D_request *request) {
   P3D_request_finish(request, handled);
 }
 
-////////////////////////////////////////////////////////////////////
-//     Function: PPInstance::handle_request_loop
-//       Access: Public, Static
-//  Description: Checks for any new requests from the plugin, and
-//               dispatches them to the appropriate PPInstance.  This
-//               function is called only in the main thread.
-////////////////////////////////////////////////////////////////////
-void PPInstance::
-handle_request_loop() {
-  if (!is_plugin_loaded()) {
-    return;
-  }
-
-  P3D_instance *p3d_inst = P3D_check_request(false);
-  while (p3d_inst != (P3D_instance *)NULL) {
-    P3D_request *request = P3D_instance_get_request(p3d_inst);
-    if (request != (P3D_request *)NULL) {
-      PPInstance *inst = (PPInstance *)(p3d_inst->_user_data);
-      assert(inst != NULL);
-      inst->handle_request(request);
-    }
-    p3d_inst = P3D_check_request(false);
-  }
-}
-
 ////////////////////////////////////////////////////////////////////
 //     Function: PPInstance::handle_event
 //       Access: Public
@@ -469,8 +444,12 @@ handle_request_loop() {
 ////////////////////////////////////////////////////////////////////
 void PPInstance::
 handle_event(void *event) {
-  // This is a good time to check for new requests.
+#ifndef HAS_PLUGIN_THREAD_ASYNC_CALL
+  // If we can't ask Mozilla to call us back using
+  // NPN_PluginThreadAsyncCall(), then we'll take advantage of the
+  // event loop to do it now.
   handle_request_loop();
+#endif
 
   if (_p3d_inst == NULL) {
     // Ignore events that come in before we've launched the instance.
@@ -631,14 +610,24 @@ request_ready(P3D_instance *instance) {
     //    << " thread = " << GetCurrentThreadId()
     << "\n" << flush;
 
+  PPInstance *inst = (PPInstance *)(instance->_user_data);
+  assert(inst != NULL);
+
+#ifdef HAS_PLUGIN_THREAD_ASYNC_CALL
+  // Since we are running at least Gecko 1.9, and we have this very
+  // useful function, let's use it to ask the browser to call us back
+  // in the main thread.
+  browser->pluginthreadasynccall(inst->_npp_instance, browser_sync_callback, NULL);
+#else  // HAS_PLUGIN_THREAD_ASYNC_CALL
+
+  // If we're using an older version of Gecko, we have to do this some
+  // other, OS-dependent way.
+
 #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.
+  // Use a Windows message to forward this event to the main thread.
 
   // Get the window handle for the window associated with this
   // instance.
-  PPInstance *inst = (PPInstance *)(instance->_user_data);
-  assert(inst != NULL);
   const NPWindow *win = inst->get_window();
   if (win != NULL && win->type == NPWindowTypeWindow) {
     PostMessage((HWND)(win->window), WM_USER, 0, 0);
@@ -656,6 +645,8 @@ request_ready(P3D_instance *instance) {
   handle_request_loop();
 #endif  // __APPLE__
 #endif  // _WIN32
+
+#endif  // HAS_PLUGIN_THREAD_ASYNC_CALL
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -1101,6 +1092,45 @@ output_np_variant(ostream &out, const NPVariant &result) {
   }
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: PPInstance::handle_request_loop
+//       Access: Private, Static
+//  Description: Checks for any new requests from the plugin, and
+//               dispatches them to the appropriate PPInstance.  This
+//               function is called only in the main thread.
+////////////////////////////////////////////////////////////////////
+void PPInstance::
+handle_request_loop() {
+  if (!is_plugin_loaded()) {
+    return;
+  }
+
+  P3D_instance *p3d_inst = P3D_check_request(false);
+  while (p3d_inst != (P3D_instance *)NULL) {
+    P3D_request *request = P3D_instance_get_request(p3d_inst);
+    if (request != (P3D_request *)NULL) {
+      PPInstance *inst = (PPInstance *)(p3d_inst->_user_data);
+      assert(inst != NULL);
+      inst->handle_request(request);
+    }
+    p3d_inst = P3D_check_request(false);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PPInstance::browser_sync_callback
+//       Access: Private, Static
+//  Description: This callback hook is passed to
+//               NPN_PluginThreadAsyncCall() (if that function is
+//               available) to forward a request to the main thread.
+//               The callback is actually called in the main thread.
+////////////////////////////////////////////////////////////////////
+void PPInstance::
+browser_sync_callback(void *) {
+  logfile << "browser_sync_callback\n";
+  handle_request_loop();
+}
+
 
 #ifdef _WIN32
 ////////////////////////////////////////////////////////////////////
@@ -1113,14 +1143,12 @@ output_np_variant(ostream &out, const NPVariant &result) {
 ////////////////////////////////////////////////////////////////////
 LONG PPInstance::
 window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
+#ifndef HAS_PLUGIN_THREAD_ASYNC_CALL
   // Since we're here in the main thread, call handle_request_loop()
   // to see if there are any new requests to be serviced by the main
   // thread.
-
-  // This might end up recursing repeatedly into
-  // handle_request_loop().  Not sure if this is bad or not.
-  // *Something* appears to be a little unstable.
   handle_request_loop();
+#endif
 
   switch (msg) {
   case WM_ERASEBKGND:
@@ -1129,6 +1157,7 @@ window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
     return true;
 
   case WM_TIMER:
+  case WM_USER:
     break;
   }
 

+ 3 - 1
direct/src/plugin_npapi/ppInstance.h

@@ -51,7 +51,6 @@ public:
   void stream_as_file(NPStream *stream, const char *fname);
 
   void handle_request(P3D_request *request);
-  static void handle_request_loop();
 
   void handle_event(void *event);
 
@@ -78,6 +77,9 @@ private:
   void create_instance();
   void send_window();
 
+  static void handle_request_loop();
+  static void browser_sync_callback(void *);
+
 #ifdef _WIN32
   static LONG 
   window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam);

+ 1 - 1
direct/src/plugin_npapi/startup.cxx

@@ -329,7 +329,7 @@ NPP_Print(NPP instance, NPPrint *platformPrint) {
 ////////////////////////////////////////////////////////////////////
 int16
 NPP_HandleEvent(NPP instance, void *event) {
-  //  logfile << "HandleEvent\n" << flush;
+  logfile << "HandleEvent\n" << flush;
 
   PPInstance *inst = (PPInstance *)(instance->pdata);
   assert(inst != NULL);