attempts to make windows plugin more stable

This commit is contained in:
David Rose 2009-07-14 04:50:25 +00:00
parent 850773c4eb
commit d5b4f7810e
17 changed files with 401 additions and 80 deletions

View File

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

View File

@ -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;
}

View File

@ -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

View File

@ -14,7 +14,7 @@
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
// Function: HandleStream::Constructor // Function: HandleStream::Constructor
// Access: Published // Access: Public
// Description: // Description:
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
inline HandleStream:: inline HandleStream::
@ -23,7 +23,7 @@ HandleStream() : iostream(&_buf) {
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
// Function: HandleStream::Destructor // Function: HandleStream::Destructor
// Access: Published // Access: Public
// Description: // Description:
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
inline HandleStream:: inline HandleStream::
@ -33,7 +33,7 @@ inline HandleStream::
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
// Function: HandleStream::open_read // Function: HandleStream::open_read
// Access: Published // Access: Public
// Description: Attempts to open the given handle for input. The // Description: Attempts to open the given handle for input. The
// stream may not be simultaneously open for input and // stream may not be simultaneously open for input and
// output. // output.
@ -49,7 +49,7 @@ open_read(FHandle handle) {
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
// Function: HandleStream::open_write // Function: HandleStream::open_write
// Access: Published // Access: Public
// Description: Attempts to open the given handle for output. The // Description: Attempts to open the given handle for output. The
// stream may not be simultaneously open for input and // stream may not be simultaneously open for input and
// output. // output.
@ -65,10 +65,21 @@ open_write(FHandle handle) {
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
// Function: HandleStream::close // Function: HandleStream::close
// Access: Published // Access: Public
// Description: // Description:
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
inline void HandleStream:: inline void HandleStream::
close() { close() {
_buf.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();
}

View File

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

View File

@ -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;
}

View File

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

View File

@ -361,6 +361,7 @@ bake_requests() {
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
void P3DInstance:: void P3DInstance::
add_raw_request(TiXmlDocument *doc) { add_raw_request(TiXmlDocument *doc) {
nout << "add_raw_request " << this << "\n" << flush;
ACQUIRE_LOCK(_request_lock); ACQUIRE_LOCK(_request_lock);
_raw_requests.push_back(doc); _raw_requests.push_back(doc);
_request_pending = true; _request_pending = true;
@ -374,6 +375,7 @@ add_raw_request(TiXmlDocument *doc) {
P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr(); P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
inst_mgr->signal_request_ready(this); inst_mgr->signal_request_ready(this);
_session->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 // Function: P3DInstance::make_xml
// Access: Public // Access: Public

View File

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

View File

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

View File

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

View File

@ -200,7 +200,14 @@ stop_thread() {
// Post a silly message to spin the message loop. // Post a silly message to spin the message loop.
PostThreadMessage(_thread_id, WM_USER, 0, 0); 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); CloseHandle(_thread);
_thread = NULL; _thread = NULL;
} }

View File

@ -23,11 +23,25 @@
// Windows case // Windows case
// Locks are straightforward. // 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 LOCK CRITICAL_SECTION
#define INIT_LOCK(lock) InitializeCriticalSection(&(lock)) #define INIT_LOCK(lock) InitializeCriticalSection(&(lock))
#define ACQUIRE_LOCK(lock) EnterCriticalSection(&(lock)) #define ACQUIRE_LOCK(lock) EnterCriticalSection(&(lock))
#define RELEASE_LOCK(lock) LeaveCriticalSection(&(lock)) #define RELEASE_LOCK(lock) LeaveCriticalSection(&(lock))
#define DESTROY_LOCK(lock) DeleteCriticalSection(&(lock)) #define DESTROY_LOCK(lock) DeleteCriticalSection(&(lock))
*/
// Threads. // Threads.
#define THREAD HANDLE #define THREAD HANDLE

View File

@ -62,6 +62,13 @@ extern ofstream logfile;
#include "load_plugin.h" #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. // Appears in startup.cxx.
extern NPNetscapeFuncs *browser; extern NPNetscapeFuncs *browser;

View File

@ -436,31 +436,6 @@ handle_request(P3D_request *request) {
P3D_request_finish(request, handled); 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 // Function: PPInstance::handle_event
// Access: Public // Access: Public
@ -469,8 +444,12 @@ handle_request_loop() {
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
void PPInstance:: void PPInstance::
handle_event(void *event) { 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(); handle_request_loop();
#endif
if (_p3d_inst == NULL) { if (_p3d_inst == NULL) {
// Ignore events that come in before we've launched the instance. // Ignore events that come in before we've launched the instance.
@ -631,14 +610,24 @@ request_ready(P3D_instance *instance) {
// << " thread = " << GetCurrentThreadId() // << " thread = " << GetCurrentThreadId()
<< "\n" << flush; << "\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 #ifdef _WIN32
// Since we might be in a sub-thread at this point, use a Windows // Use a Windows message to forward this event to the main thread.
// message to forward this event to the main thread.
// Get the window handle for the window associated with this // Get the window handle for the window associated with this
// instance. // instance.
PPInstance *inst = (PPInstance *)(instance->_user_data);
assert(inst != NULL);
const NPWindow *win = inst->get_window(); const NPWindow *win = inst->get_window();
if (win != NULL && win->type == NPWindowTypeWindow) { if (win != NULL && win->type == NPWindowTypeWindow) {
PostMessage((HWND)(win->window), WM_USER, 0, 0); PostMessage((HWND)(win->window), WM_USER, 0, 0);
@ -656,6 +645,8 @@ request_ready(P3D_instance *instance) {
handle_request_loop(); handle_request_loop();
#endif // __APPLE__ #endif // __APPLE__
#endif // _WIN32 #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 #ifdef _WIN32
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
@ -1113,14 +1143,12 @@ output_np_variant(ostream &out, const NPVariant &result) {
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
LONG PPInstance:: LONG PPInstance::
window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { 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() // 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 // to see if there are any new requests to be serviced by the main
// thread. // 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(); handle_request_loop();
#endif
switch (msg) { switch (msg) {
case WM_ERASEBKGND: case WM_ERASEBKGND:
@ -1129,6 +1157,7 @@ window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
return true; return true;
case WM_TIMER: case WM_TIMER:
case WM_USER:
break; break;
} }

View File

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

View File

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