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

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
// 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();
}

View File

@ -33,6 +33,8 @@ public:
inline void open_write(FHandle handle);
inline void close();
inline FHandle get_handle() const;
private:
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;
#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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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