From d5b4f7810e87379389f8a6f27de0c7d72174c0e6 Mon Sep 17 00:00:00 2001 From: David Rose Date: Tue, 14 Jul 2009 04:50:25 +0000 Subject: [PATCH] attempts to make windows plugin more stable --- direct/src/plugin/Sources.pp | 8 +- direct/src/plugin/binaryXml.cxx | 142 +++++++++++++++++++++ direct/src/plugin/binaryXml.h | 32 +++++ direct/src/plugin/handleStream.I | 21 ++- direct/src/plugin/handleStream.h | 2 + direct/src/plugin/handleStreamBuf.I | 11 ++ direct/src/plugin/handleStreamBuf.h | 5 +- direct/src/plugin/p3dInstance.cxx | 31 +++++ direct/src/plugin/p3dInstance.h | 1 + direct/src/plugin/p3dPythonRun.cxx | 49 ++++--- direct/src/plugin/p3dSession.cxx | 46 ++++--- direct/src/plugin/p3dWinSplashWindow.cxx | 9 +- direct/src/plugin/p3d_lock.h | 14 ++ direct/src/plugin_npapi/nppanda3d_common.h | 7 + direct/src/plugin_npapi/ppInstance.cxx | 97 +++++++++----- direct/src/plugin_npapi/ppInstance.h | 4 +- direct/src/plugin_npapi/startup.cxx | 2 +- 17 files changed, 401 insertions(+), 80 deletions(-) create mode 100644 direct/src/plugin/binaryXml.cxx create mode 100644 direct/src/plugin/binaryXml.h diff --git a/direct/src/plugin/Sources.pp b/direct/src/plugin/Sources.pp index 200a32115b..c3486c5f52 100644 --- a/direct/src/plugin/Sources.pp +++ b/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 diff --git a/direct/src/plugin/binaryXml.cxx b/direct/src/plugin/binaryXml.cxx new file mode 100644 index 0000000000..5a3f919f9e --- /dev/null +++ b/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 + +// 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; +} diff --git a/direct/src/plugin/binaryXml.h b/direct/src/plugin/binaryXml.h new file mode 100644 index 0000000000..4b2609e846 --- /dev/null +++ b/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 + +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 diff --git a/direct/src/plugin/handleStream.I b/direct/src/plugin/handleStream.I index 37b00c4bc1..f99835ddf8 100644 --- a/direct/src/plugin/handleStream.I +++ b/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(); +} diff --git a/direct/src/plugin/handleStream.h b/direct/src/plugin/handleStream.h index 080e82719a..bceaad39e9 100644 --- a/direct/src/plugin/handleStream.h +++ b/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; }; diff --git a/direct/src/plugin/handleStreamBuf.I b/direct/src/plugin/handleStreamBuf.I index 22dff6f076..01599482cd 100755 --- a/direct/src/plugin/handleStreamBuf.I +++ b/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; +} diff --git a/direct/src/plugin/handleStreamBuf.h b/direct/src/plugin/handleStreamBuf.h index 6c2df240ff..bb7c1e200b 100644 --- a/direct/src/plugin/handleStreamBuf.h +++ b/direct/src/plugin/handleStreamBuf.h @@ -23,7 +23,6 @@ typedef HANDLE FHandle; typedef int FHandle; #endif -#include #include 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 diff --git a/direct/src/plugin/p3dInstance.cxx b/direct/src/plugin/p3dInstance.cxx index 68f39e9719..e22f92e61c 100644 --- a/direct/src/plugin/p3dInstance.cxx +++ b/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 diff --git a/direct/src/plugin/p3dInstance.h b/direct/src/plugin/p3dInstance.h index 7829338654..5802f7b3de 100644 --- a/direct/src/plugin/p3dInstance.h +++ b/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(); diff --git a/direct/src/plugin/p3dPythonRun.cxx b/direct/src/plugin/p3dPythonRun.cxx index bd72e1a221..7eff431bc8 100755 --- a/direct/src/plugin/p3dPythonRun.cxx +++ b/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; diff --git a/direct/src/plugin/p3dSession.cxx b/direct/src/plugin/p3dSession.cxx index 6e869a26c3..d7dd5c1ec4 100644 --- a/direct/src/plugin/p3dSession.cxx +++ b/direct/src/plugin/p3dSession.cxx @@ -22,6 +22,7 @@ #include "p3dIntObject.h" #include "p3dFloatObject.h" #include "p3dPythonObject.h" +#include "binaryXml.h" #ifndef _WIN32 #include @@ -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; } //////////////////////////////////////////////////////////////////// diff --git a/direct/src/plugin/p3dWinSplashWindow.cxx b/direct/src/plugin/p3dWinSplashWindow.cxx index 13f64938d3..a1309f658b 100755 --- a/direct/src/plugin/p3dWinSplashWindow.cxx +++ b/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; } diff --git a/direct/src/plugin/p3d_lock.h b/direct/src/plugin/p3d_lock.h index f18589ffdb..8f1ff3149c 100755 --- a/direct/src/plugin/p3d_lock.h +++ b/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 diff --git a/direct/src/plugin_npapi/nppanda3d_common.h b/direct/src/plugin_npapi/nppanda3d_common.h index ffb89605c4..3a60da9731 100644 --- a/direct/src/plugin_npapi/nppanda3d_common.h +++ b/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; diff --git a/direct/src/plugin_npapi/ppInstance.cxx b/direct/src/plugin_npapi/ppInstance.cxx index faa769e3ff..6a45403b59 100644 --- a/direct/src/plugin_npapi/ppInstance.cxx +++ b/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; } diff --git a/direct/src/plugin_npapi/ppInstance.h b/direct/src/plugin_npapi/ppInstance.h index 871e9086d1..956dc78455 100644 --- a/direct/src/plugin_npapi/ppInstance.h +++ b/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); diff --git a/direct/src/plugin_npapi/startup.cxx b/direct/src/plugin_npapi/startup.cxx index 94ab3f7453..50a4af82f4 100644 --- a/direct/src/plugin_npapi/startup.cxx +++ b/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);