much better object model

This commit is contained in:
David Rose 2009-07-09 23:59:17 +00:00
parent 98177dc123
commit 4d2ffd8e77
14 changed files with 567 additions and 342 deletions

View File

@ -40,6 +40,7 @@
p3dObject.h p3dObject.I \
p3dPackage.h p3dPackage.I \
p3dPythonObject.h \
p3dReferenceCount.h p3dReferenceCount.I \
p3dSession.h p3dSession.I \
p3dSplashWindow.h p3dSplashWindow.I \
p3dStringObject.h \
@ -64,6 +65,7 @@
p3dObject.cxx \
p3dPackage.cxx \
p3dPythonObject.cxx \
p3dReferenceCount.cxx \
p3dSession.cxx \
p3dSplashWindow.cxx \
p3dStringObject.cxx \

View File

@ -71,6 +71,9 @@ P3DInstance(P3D_request_ready_func *func, void *user_data) :
////////////////////////////////////////////////////////////////////
P3DInstance::
~P3DInstance() {
// TODO: maybe this should be a reference-counted object, so we
// don't delete it too soon.
assert(_session == NULL);
P3D_OBJECT_XDECREF(_browser_script_object);
@ -90,7 +93,8 @@ P3DInstance::
_splash_window = NULL;
}
// TODO: empty _pending_requests queue and _downloads map.
// TODO: empty _raw_requests and _baked_requests queues, and
// _downloads map.
// TODO: Is it possible for someone to delete an instance while a
// download is still running? Who will crash when this happens?
@ -239,10 +243,7 @@ set_browser_script_object(P3D_object *browser_script_object) {
////////////////////////////////////////////////////////////////////
bool P3DInstance::
has_request() {
ACQUIRE_LOCK(_request_lock);
bool any_requests = !_pending_requests.empty();
RELEASE_LOCK(_request_lock);
return any_requests;
return _request_pending;
}
////////////////////////////////////////////////////////////////////
@ -256,52 +257,105 @@ has_request() {
////////////////////////////////////////////////////////////////////
P3D_request *P3DInstance::
get_request() {
P3D_request *result = NULL;
ACQUIRE_LOCK(_request_lock);
if (!_pending_requests.empty()) {
result = _pending_requests.front();
_pending_requests.pop_front();
_request_pending = !_pending_requests.empty();
}
RELEASE_LOCK(_request_lock);
if (result != NULL) {
switch (result->_request_type) {
case P3D_RT_notify:
handle_notify_request(result);
break;
case P3D_RT_script:
handle_script_request(result);
// Completely eat this request; don't return it to the caller.
finish_request(result, true);
return get_request();
default:
// Other kinds of requests don't require special handling at
// this level; pass it up unmolested.
break;
}
bake_requests();
if (_baked_requests.empty()) {
// No requests ready.
_request_pending = false;
return NULL;
}
return result;
P3D_request *request = _baked_requests.front();
_baked_requests.pop_front();
_request_pending = !_baked_requests.empty();
return request;
}
////////////////////////////////////////////////////////////////////
// Function: P3DInstance::add_request
// Function: P3DInstance::bake_requests
// Access: Public
// Description: May be called in any thread to add a new P3D_request
// to the pending_request queue for this instance.
// Description: Copies requests from the _raw_requests queue, which
// is built up in one or more sub-threads, into the
// _baked_requests queue, which is publicly presented
// to the browser. Along the way, some requests (like
// script requests) are handled immediately.
//
// At the end of this call, _baked_requests will contain
// the current set of requests pending for the browser.
//
// This method should only be called in the main thread.
////////////////////////////////////////////////////////////////////
void P3DInstance::
add_request(P3D_request *request) {
request->_instance = this;
bake_requests() {
while (true) {
// Get the latest request from the read thread.
TiXmlDocument *doc = NULL;
ACQUIRE_LOCK(_request_lock);
if (!_raw_requests.empty()) {
doc = _raw_requests.front();
_raw_requests.pop_front();
}
RELEASE_LOCK(_request_lock);
if (doc == NULL) {
// No more requests to process right now.
return;
}
nout << "received: " << *doc << "\n" << flush;
// Now we've got a request in XML form; convert it to P3D_request
// form.
TiXmlElement *xrequest = doc->FirstChildElement("request");
assert(xrequest != (TiXmlElement *)NULL);
P3D_request *request = make_p3d_request(xrequest);
delete doc;
if (request != NULL) {
_baked_requests.push_back(request);
}
}
}
////////////////////////////////////////////////////////////////////
// Function: P3DInstance::add_raw_request
// Access: Public
// Description: May be called in any thread to add a new XML request
// to the pending_request queue for this instance. The
// XML document will be deleted when the request is
// eventually handled.
////////////////////////////////////////////////////////////////////
void P3DInstance::
add_raw_request(TiXmlDocument *doc) {
ACQUIRE_LOCK(_request_lock);
_pending_requests.push_back(request);
_raw_requests.push_back(doc);
_request_pending = true;
RELEASE_LOCK(_request_lock);
// We don't decode the XML yet, since we might be running in any
// thread here. We'll decode it in the main thread, where it's
// safe.
// Tell the world we've got a new request.
P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
inst_mgr->signal_request_ready(this);
_session->signal_request_ready(this);
}
////////////////////////////////////////////////////////////////////
// Function: P3DInstance::add_baked_request
// Access: Public
// Description: May be called in the main thread only to add a new
// request to the baked_request queue. This request
// queue is directly passed on the browser without
// further processing at this level.
////////////////////////////////////////////////////////////////////
void P3DInstance::
add_baked_request(P3D_request *request) {
request->_instance = this;
_baked_requests.push_back(request);
_request_pending = true;
// Tell the world we've got a new request.
P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
inst_mgr->signal_request_ready(this);
@ -335,18 +389,6 @@ finish_request(P3D_request *request, bool handled) {
case P3D_RT_notify:
free((char *)request->_request._notify._message);
break;
case P3D_RT_script:
{
P3D_OBJECT_DECREF(request->_request._script._object);
if (request->_request._script._property_name != NULL) {
free((char *)request->_request._script._property_name);
}
for (int i = 0; i < request->_request._script._num_values; ++i) {
P3D_OBJECT_DECREF(request->_request._script._values[i]);
}
}
break;
}
delete request;
@ -430,7 +472,7 @@ start_download(P3DDownload *download) {
request->_request._get_url._url = strdup(download->get_url().c_str());
request->_request._get_url._unique_id = download_id;
add_request(request);
add_baked_request(request);
}
////////////////////////////////////////////////////////////////////
@ -446,7 +488,7 @@ request_stop() {
_requested_stop = true;
P3D_request *request = new P3D_request;
request->_request_type = P3D_RT_stop;
add_request(request);
add_baked_request(request);
}
}
@ -497,6 +539,93 @@ send_browser_script_object() {
_session->send_command(doc);
}
////////////////////////////////////////////////////////////////////
// Function: P3DInstance::make_p3d_request
// Access: Private
// Description: Creates a new P3D_request structure from the XML.
// Returns NULL if no request is needed.
////////////////////////////////////////////////////////////////////
P3D_request *P3DInstance::
make_p3d_request(TiXmlElement *xrequest) {
P3D_request *request = NULL;
const char *rtype = xrequest->Attribute("rtype");
if (rtype != NULL) {
if (strcmp(rtype, "notify") == 0) {
const char *message = xrequest->Attribute("message");
if (message != NULL) {
request = new P3D_request;
request->_request_type = P3D_RT_notify;
request->_request._notify._message = strdup(message);
}
} else if (strcmp(rtype, "script") == 0) {
// We don't actually build a P3D_request for a script request;
// we always just handle it immediately.
const char *operation = xrequest->Attribute("operation");
TiXmlElement *xobject = xrequest->FirstChildElement("object");
const char *property_name = xrequest->Attribute("property_name");
int needs_response = 0;
xrequest->Attribute("needs_response", &needs_response);
int unique_id = 0;
xrequest->Attribute("unique_id", &unique_id);
vector<P3D_object *> values;
TiXmlElement *xvalue = xrequest->FirstChildElement("value");
while (xvalue != NULL) {
P3D_object *value = _session->xml_to_p3dobj(xvalue);
values.push_back(value);
xvalue = xvalue->NextSiblingElement("value");
}
P3D_object **values_ptr = NULL;
if (!values.empty()) {
values_ptr = &values[0];
}
if (operation != NULL && xobject != NULL) {
P3D_object *object = _session->xml_to_p3dobj(xobject);
if (property_name == NULL) {
property_name = "";
}
handle_script_request(operation, object, property_name,
values_ptr, values.size(),
(needs_response != 0), unique_id);
}
for (size_t i = 0; i < values.size(); ++i) {
P3D_OBJECT_DECREF(values[i]);
}
} else if (strcmp(rtype, "notify") == 0) {
const char *message = xrequest->Attribute("message");
if (message != NULL) {
request = new P3D_request;
request->_request_type = P3D_RT_notify;
request->_request._notify._message = strdup(message);
handle_notify_request(message);
}
} else if (strcmp(rtype, "drop_p3dobj") == 0) {
int object_id;
if (xrequest->QueryIntAttribute("object_id", &object_id) == TIXML_SUCCESS) {
// We no longer need to keep this reference.
_session->drop_p3dobj(object_id);
}
} else {
nout << "Ignoring request of type " << rtype << "\n";
}
}
if (request != NULL) {
request->_instance = this;
}
return request;
}
////////////////////////////////////////////////////////////////////
// Function: P3DInstance::handle_notify_request
// Access: Private
@ -504,13 +633,10 @@ send_browser_script_object() {
// received from the subprocess.
////////////////////////////////////////////////////////////////////
void P3DInstance::
handle_notify_request(P3D_request *request) {
assert(request->_request_type == P3D_RT_notify);
handle_notify_request(const string &message) {
// We look for certain notify events that have particular meaning
// to this instance.
const char *message = request->_request._notify._message;
if (strcmp(message, "onwindowopen") == 0) {
if (message == "onwindowopen") {
// The process told us that it just succesfully opened its
// window. Tear down the splash window.
_instance_window_opened = true;
@ -528,172 +654,87 @@ handle_notify_request(P3D_request *request) {
// received from the subprocess.
////////////////////////////////////////////////////////////////////
void P3DInstance::
handle_script_request(P3D_request *request) {
assert(request->_request_type == P3D_RT_script);
handle_script_request(const string &operation, P3D_object *object,
const string &property_name,
P3D_object *values[], int num_values,
bool needs_response, int unique_id) {
P3D_object *object = request->_request._script._object;
bool needs_response = request->_request._script._needs_response;
int unique_id = request->_request._script._unique_id;
switch (request->_request._script._op) {
case P3D_SO_get_property:
{
P3D_object *result = P3D_OBJECT_GET_PROPERTY(object, request->_request._script._property_name);
TiXmlDocument *doc = new TiXmlDocument;
TiXmlDeclaration *decl = new TiXmlDeclaration("1.0", "utf-8", "");
TiXmlElement *xcommand = new TiXmlElement("command");
xcommand->SetAttribute("cmd", "script_response");
xcommand->SetAttribute("unique_id", unique_id);
doc->LinkEndChild(decl);
doc->LinkEndChild(xcommand);
// We've got the property value; feed it back down to the
// subprocess.
TiXmlDocument *doc = new TiXmlDocument;
TiXmlDeclaration *decl = new TiXmlDeclaration("1.0", "utf-8", "");
TiXmlElement *xcommand = new TiXmlElement("command");
xcommand->SetAttribute("cmd", "script_response");
xcommand->SetAttribute("unique_id", unique_id);
doc->LinkEndChild(decl);
doc->LinkEndChild(xcommand);
if (result != NULL) {
xcommand->LinkEndChild(_session->p3dobj_to_xml(result));
P3D_OBJECT_DECREF(result);
}
if (operation == "get_property") {
P3D_object *result = P3D_OBJECT_GET_PROPERTY(object, property_name.c_str());
if (needs_response) {
_session->send_command(doc);
} else {
delete doc;
}
// We've got the property value; feed it back down to the
// subprocess.
if (result != NULL) {
xcommand->LinkEndChild(_session->p3dobj_to_xml(result));
P3D_OBJECT_DECREF(result);
}
break;
case P3D_SO_set_property:
{
bool result;
if (request->_request._script._num_values == 1) {
result = P3D_OBJECT_SET_PROPERTY(object, request->_request._script._property_name,
request->_request._script._values[0]);
} else {
// Wrong number of values. Error.
result = false;
}
// Feed the result back down to the subprocess.
TiXmlDocument *doc = new TiXmlDocument;
TiXmlDeclaration *decl = new TiXmlDeclaration("1.0", "utf-8", "");
TiXmlElement *xcommand = new TiXmlElement("command");
xcommand->SetAttribute("cmd", "script_response");
xcommand->SetAttribute("unique_id", unique_id);
doc->LinkEndChild(decl);
doc->LinkEndChild(xcommand);
TiXmlElement *xvalue = new TiXmlElement("value");
xvalue->SetAttribute("type", "bool");
xvalue->SetAttribute("value", (int)result);
xcommand->LinkEndChild(xvalue);
if (needs_response) {
_session->send_command(doc);
} else {
delete doc;
}
} else if (operation == "set_property") {
bool result;
if (num_values == 1) {
result = P3D_OBJECT_SET_PROPERTY(object, property_name.c_str(), values[0]);
} else {
// Wrong number of values. Error.
result = false;
}
break;
TiXmlElement *xvalue = new TiXmlElement("value");
xvalue->SetAttribute("type", "bool");
xvalue->SetAttribute("value", (int)result);
xcommand->LinkEndChild(xvalue);
case P3D_SO_del_property:
{
bool result = P3D_OBJECT_SET_PROPERTY(object, request->_request._script._property_name,
NULL);
} else if (operation == "del_property") {
bool result = P3D_OBJECT_SET_PROPERTY(object, property_name.c_str(), NULL);
TiXmlElement *xvalue = new TiXmlElement("value");
xvalue->SetAttribute("type", "bool");
xvalue->SetAttribute("value", (int)result);
xcommand->LinkEndChild(xvalue);
// Feed the result back down to the subprocess.
TiXmlDocument *doc = new TiXmlDocument;
TiXmlDeclaration *decl = new TiXmlDeclaration("1.0", "utf-8", "");
TiXmlElement *xcommand = new TiXmlElement("command");
xcommand->SetAttribute("cmd", "script_response");
xcommand->SetAttribute("unique_id", unique_id);
doc->LinkEndChild(decl);
doc->LinkEndChild(xcommand);
TiXmlElement *xvalue = new TiXmlElement("value");
xvalue->SetAttribute("type", "bool");
xvalue->SetAttribute("value", (int)result);
xcommand->LinkEndChild(xvalue);
if (needs_response) {
_session->send_command(doc);
} else {
delete doc;
}
} else if (operation == "call") {
P3D_object *result =
P3D_OBJECT_CALL(object, property_name.c_str(), values, num_values);
if (result != NULL) {
xcommand->LinkEndChild(_session->p3dobj_to_xml(result));
P3D_OBJECT_DECREF(result);
}
break;
case P3D_SO_call:
{
P3D_object *result =
P3D_OBJECT_CALL(object, request->_request._script._property_name,
request->_request._script._values,
request->_request._script._num_values);
// Feed the result back down to the subprocess.
TiXmlDocument *doc = new TiXmlDocument;
TiXmlDeclaration *decl = new TiXmlDeclaration("1.0", "utf-8", "");
TiXmlElement *xcommand = new TiXmlElement("command");
xcommand->SetAttribute("cmd", "script_response");
xcommand->SetAttribute("unique_id", unique_id);
doc->LinkEndChild(decl);
doc->LinkEndChild(xcommand);
if (result != NULL) {
xcommand->LinkEndChild(_session->p3dobj_to_xml(result));
P3D_OBJECT_DECREF(result);
}
if (needs_response) {
_session->send_command(doc);
} else {
delete doc;
}
} else if (operation == "eval") {
P3D_object *result;
if (num_values == 1) {
P3D_object *expression = values[0];
int size = P3D_OBJECT_GET_STRING(expression, NULL, 0);
char *buffer = new char[size + 1];
P3D_OBJECT_GET_STRING(expression, buffer, size + 1);
result = P3D_OBJECT_EVAL(object, buffer);
logfile << " eval " << *object << ": " << buffer << ", result = " << result << "\n";
delete[] buffer;
} else {
// Wrong number of values. Error.
result = NULL;
}
break;
case P3D_SO_eval:
{
P3D_object *result;
if (request->_request._script._num_values == 1) {
P3D_object *expression = request->_request._script._values[0];
int size = P3D_OBJECT_GET_STRING(expression, NULL, 0);
char *buffer = new char[size + 1];
P3D_OBJECT_GET_STRING(expression, buffer, size + 1);
result = P3D_OBJECT_EVAL(object, buffer);
logfile << " eval " << *object << ": " << buffer << ", result = " << result << "\n";
delete[] buffer;
} else {
// Wrong number of values. Error.
result = NULL;
}
// Feed the result back down to the subprocess.
TiXmlDocument *doc = new TiXmlDocument;
TiXmlDeclaration *decl = new TiXmlDeclaration("1.0", "utf-8", "");
TiXmlElement *xcommand = new TiXmlElement("command");
xcommand->SetAttribute("cmd", "script_response");
xcommand->SetAttribute("unique_id", unique_id);
doc->LinkEndChild(decl);
doc->LinkEndChild(xcommand);
if (result != NULL) {
xcommand->LinkEndChild(_session->p3dobj_to_xml(result));
P3D_OBJECT_DECREF(result);
}
logfile << "eval response: " << *doc << "\n";
if (needs_response) {
_session->send_command(doc);
} else {
delete doc;
}
if (result != NULL) {
xcommand->LinkEndChild(_session->p3dobj_to_xml(result));
P3D_OBJECT_DECREF(result);
}
break;
}
if (needs_response) {
_session->send_command(doc);
} else {
delete doc;
}
}

View File

@ -19,6 +19,7 @@
#include "p3dFileDownload.h"
#include "p3dFileParams.h"
#include "p3dWindowParams.h"
#include "p3dReferenceCount.h"
#include "get_tinyxml.h"
#include <deque>
@ -35,7 +36,7 @@ class P3DObject;
// Description : This is an instance of a Panda3D window, as seen in
// the parent-level process.
////////////////////////////////////////////////////////////////////
class P3DInstance : public P3D_instance {
class P3DInstance : public P3D_instance, public P3DReferenceCount {
public:
P3DInstance(P3D_request_ready_func *func, void *user_data);
~P3DInstance();
@ -51,7 +52,9 @@ public:
bool has_request();
P3D_request *get_request();
void add_request(P3D_request *request);
void bake_requests();
void add_raw_request(TiXmlDocument *doc);
void add_baked_request(P3D_request *request);
void finish_request(P3D_request *request, bool handled);
bool feed_url_stream(int unique_id,
@ -88,8 +91,12 @@ private:
};
void send_browser_script_object();
void handle_notify_request(P3D_request *request);
void handle_script_request(P3D_request *request);
P3D_request *make_p3d_request(TiXmlElement *xrequest);
void handle_notify_request(const string &message);
void handle_script_request(const string &operation, P3D_object *object,
const string &property_name,
P3D_object *values[], int num_values,
bool needs_response, int unique_id);
void make_splash_window();
void install_progress(P3DPackage *package, double progress);
@ -105,7 +112,10 @@ private:
int _instance_id;
string _session_key;
string _python_version;
// Not ref-counted: session is the parent.
P3DSession *_session;
P3DSplashWindow *_splash_window;
bool _instance_window_opened;
@ -115,11 +125,18 @@ private:
typedef map<int, P3DDownload *> Downloads;
Downloads _downloads;
// The _raw_requests queue might be filled up by the read thread, so
// we protect it in a lock.
LOCK _request_lock;
typedef deque<P3D_request *> Requests;
Requests _pending_requests;
typedef deque<TiXmlDocument *> RawRequests;
RawRequests _raw_requests;
bool _requested_stop;
// The _baked_requests queue is only touched in the main thread; no
// lock needed.
typedef deque<P3D_request *> BakedRequests;
BakedRequests _baked_requests;
static int _next_instance_id;
friend class P3DSession;

View File

@ -138,6 +138,7 @@ initialize() {
P3DInstance *P3DInstanceManager::
create_instance(P3D_request_ready_func *func, void *user_data) {
P3DInstance *inst = new P3DInstance(func, user_data);
inst->ref();
_instances.insert(inst);
return inst;
@ -164,6 +165,7 @@ start_instance(P3DInstance *inst, const string &p3d_filename,
Sessions::iterator si = _sessions.find(inst->get_session_key());
if (si == _sessions.end()) {
session = new P3DSession(inst);
session->ref();
bool inserted = _sessions.insert(Sessions::value_type(session->get_session_key(), session)).second;
assert(inserted);
} else {
@ -197,10 +199,11 @@ finish_instance(P3DInstance *inst) {
// session.
if (session->get_num_instances() == 0) {
_sessions.erase(session->get_session_key());
delete session;
session->shutdown();
unref_delete(session);
}
delete inst;
unref_delete(inst);
}
////////////////////////////////////////////////////////////////////

View File

@ -24,6 +24,8 @@ P3DPythonObject(P3DSession *session, int object_id) :
_session(session),
_object_id(object_id)
{
_session->ref();
nout << "new P3DPythonObject " << this << " : " << object_id << "\n" << flush;
}
////////////////////////////////////////////////////////////////////
@ -33,7 +35,12 @@ P3DPythonObject(P3DSession *session, int object_id) :
////////////////////////////////////////////////////////////////////
P3DPythonObject::
~P3DPythonObject() {
// TODO.
nout << "del P3DPythonObject " << this << " : " << _object_id << "\n";
// When the P3DPythonObject wrapper goes away, we have to inform the
// child process that we no longer need the corresponding PyObject
// to be kept around.
_session->drop_pyobj(_object_id);
unref_delete(_session);
}
////////////////////////////////////////////////////////////////////

View File

@ -259,6 +259,18 @@ handle_command(TiXmlDocument *doc) {
// Response from a script request.
assert(!needs_response);
nout << "Ignoring unexpected script_response\n";
} else if (strcmp(cmd, "drop_pyobj") == 0) {
int object_id;
if (xcommand->QueryIntAttribute("object_id", &object_id) == TIXML_SUCCESS) {
nout << "got drop_pyobj(" << object_id << ")\n" << flush;
SentObjects::iterator si = _sent_objects.find(object_id);
if (si != _sent_objects.end()) {
PyObject *obj = (*si).second;
Py_DECREF(obj);
_sent_objects.erase(si);
}
}
} else {
nout << "Unhandled command " << cmd << "\n";
@ -666,6 +678,19 @@ py_request_func(PyObject *args) {
nout << "sent: " << doc << "\n" << flush;
_pipe_write << doc << flush;
} else if (strcmp(request_type, "drop_p3dobj") == 0) {
// Release a particular P3D_object that we were holding a
// reference to.
int object_id;
if (!PyArg_ParseTuple(extra_args, "i", &object_id)) {
return NULL;
}
nout << "got drop_p3dobj(" << object_id << ")\n" << flush;
xrequest->SetAttribute("object_id", object_id);
nout << "sent: " << doc << "\n" << flush;
_pipe_write << doc << flush;
} else {
string message = string("Unsupported request type: ") + string(request_type);
PyErr_SetString(PyExc_ValueError, message.c_str());

View File

@ -0,0 +1,72 @@
// Filename: p3dReferenceCount.I
// Created by: drose (09Jul09)
//
////////////////////////////////////////////////////////////////////
//
// 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."
//
////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////
// Function: P3DReferenceCount::Constructor
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
inline P3DReferenceCount::
P3DReferenceCount() {
_ref_count = 0;
}
////////////////////////////////////////////////////////////////////
// Function: P3DReferenceCount::Destructor
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
inline P3DReferenceCount::
~P3DReferenceCount() {
assert(_ref_count == 0);
}
////////////////////////////////////////////////////////////////////
// Function: P3DReferenceCount::ref
// Access: Public
// Description: Explicitly increments the reference count.
////////////////////////////////////////////////////////////////////
inline void P3DReferenceCount::
ref() const {
++((P3DReferenceCount *)this)->_ref_count;
}
////////////////////////////////////////////////////////////////////
// Function: P3DReferenceCount::unref
// Access: Published, Virtual
// Description: Explicitly decrements the reference count. Usually,
// you should call unref_delete() instead.
//
// The return value is true if the new reference count
// is nonzero, false if it is zero.
////////////////////////////////////////////////////////////////////
inline bool P3DReferenceCount::
unref() const {
return --(((P3DReferenceCount *)this)->_ref_count) != 0;
}
////////////////////////////////////////////////////////////////////
// Function: unref_delete
// Description: This global helper function will unref the given
// P3DReferenceCount object, and if the reference count
// reaches zero, automatically delete it.
////////////////////////////////////////////////////////////////////
template<class RefCountType>
inline void
unref_delete(RefCountType *ptr) {
if (!ptr->unref()) {
delete ptr;
}
}

View File

@ -0,0 +1,15 @@
// Filename: p3dReferenceCount.cxx
// Created by: drose (09Jul09)
//
////////////////////////////////////////////////////////////////////
//
// 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 "p3dReferenceCount.h"

View File

@ -0,0 +1,44 @@
// Filename: p3dReferenceCount.h
// Created by: drose (09Jul09)
//
////////////////////////////////////////////////////////////////////
//
// 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 P3DREFERENCECOUNT_H
#define P3DREFERENCECOUNT_H
#include "p3d_plugin_common.h"
////////////////////////////////////////////////////////////////////
// Class : P3DReferenceCount
// Description : A base class for reference-counted objects in this
// module. We follow the Panda convention, rather than
// the Pythong convention: the reference count of a new
// object is initially 0.
////////////////////////////////////////////////////////////////////
class P3DReferenceCount {
public:
inline P3DReferenceCount();
inline ~P3DReferenceCount();
inline void ref() const;
inline bool unref() const;
private:
int _ref_count;
};
template<class RefCountType>
inline void unref_delete(RefCountType *ptr);
#include "p3dReferenceCount.I"
#endif

View File

@ -69,14 +69,26 @@ P3DSession(P3DInstance *inst) {
////////////////////////////////////////////////////////////////////
// Function: P3DSession::Destructor
// Access: Public
// Description: Terminates the session by shutting down Python and
// stopping the subprocess.
// Description:
////////////////////////////////////////////////////////////////////
P3DSession::
~P3DSession() {
assert(!_p3dpython_running);
DESTROY_LOCK(_instances_lock);
}
////////////////////////////////////////////////////////////////////
// Function: P3DSession::shutdown
// Access: Public
// Description: Terminates the session by shutting down Python and
// stopping the subprocess.
////////////////////////////////////////////////////////////////////
void P3DSession::
shutdown() {
if (_panda3d_callback != NULL) {
_panda3d->cancel_callback(_panda3d_callback);
delete _panda3d_callback;
_panda3d_callback = NULL;
}
if (_p3dpython_running) {
@ -106,12 +118,13 @@ P3DSession::
}
CloseHandle(_p3dpython_handle);
#else // _WIN32
// TODO: posix kill().
#endif // _WIN32
_p3dpython_running = false;
}
// If there are any leftover commands in the queue (presumably
@ -124,7 +137,6 @@ P3DSession::
_commands.clear();
join_read_thread();
DESTROY_LOCK(_instances_lock);
}
////////////////////////////////////////////////////////////////////
@ -143,6 +155,7 @@ start_instance(P3DInstance *inst) {
assert(inst->get_session_key() == _session_key);
assert(inst->get_python_version() == _python_version);
inst->ref();
ACQUIRE_LOCK(_instances_lock);
inst->_session = this;
bool inserted = _instances.insert(Instances::value_type(inst->get_instance_id(), inst)).second;
@ -200,6 +213,7 @@ terminate_instance(P3DInstance *inst) {
_instances.erase(inst->get_instance_id());
}
RELEASE_LOCK(_instances_lock);
unref_delete(inst);
}
////////////////////////////////////////////////////////////////////
@ -284,6 +298,16 @@ command_and_response(TiXmlDocument *command) {
return NULL;
}
// Make sure we bake requests while we are waiting, to process
// recursive script requests. (The child process might have to
// wait for us to process some of these before it can fulfill the
// command we're actually waiting for.)
Instances::iterator ii;
for (ii = _instances.begin(); ii != _instances.end(); ++ii) {
P3DInstance *inst = (*ii).second;
inst->bake_requests();
}
#ifdef _WIN32
// Make sure we process the Windows event loop while we're
// waiting, or everything that depends on Windows messages--in
@ -370,6 +394,7 @@ xml_to_p3dobj(const TiXmlElement *xvalue) {
}
P3D_object *obj = (*si).second;
P3D_OBJECT_INCREF(obj);
return obj;
}
@ -472,6 +497,69 @@ p3dobj_to_xml(P3D_object *obj) {
return xvalue;
}
////////////////////////////////////////////////////////////////////
// Function: P3DSession::signal_request_ready
// Access: Public
// Description: May be called in any thread to indicate that a new
// P3D_request is available in the indicated instance.
////////////////////////////////////////////////////////////////////
void P3DSession::
signal_request_ready(P3DInstance *inst) {
// Since a new request might require baking, we should wake up a
// blocked command_and_request() process, so the main thread can go
// back and bake the new request.
// Technically, a response isn't really ready now, but we still need
// the main thread to wake up and look around for a bit.
_response_ready.acquire();
_response_ready.notify();
_response_ready.release();
}
////////////////////////////////////////////////////////////////////
// Function: P3DSession::drop_pyobj
// Access: Public
// Description: If the session is still active, issues the command to
// the child process to release the indicated PyObject
// from its table. This is intended to be called
// strictly by the P3DPythonObject destructor.
////////////////////////////////////////////////////////////////////
void P3DSession::
drop_pyobj(int object_id) {
nout << "got drop_pyobj(" << object_id << ")\n" << flush;
if (_p3dpython_running) {
TiXmlDocument doc;
TiXmlDeclaration *decl = new TiXmlDeclaration("1.0", "utf-8", "");
TiXmlElement *xcommand = new TiXmlElement("command");
xcommand->SetAttribute("cmd", "drop_pyobj");
xcommand->SetAttribute("object_id", object_id);
doc.LinkEndChild(decl);
doc.LinkEndChild(xcommand);
nout << "sent: " << doc << "\n" << flush;
_pipe_write << doc << flush;
}
}
////////////////////////////////////////////////////////////////////
// Function: P3DSession::drop_p3dobj
// Access: Public
// Description: Responds to a drop_p3dobj message from the child
// process indicating that a particular P3D_object is no
// longer being used by the child. This removes the
// corresponding P3D_object from our tables and
// decrements its reference count.
////////////////////////////////////////////////////////////////////
void P3DSession::
drop_p3dobj(int object_id) {
nout << "got drop_p3dobj(" << object_id << ")\n" << flush;
SentObjects::iterator si = _sent_objects.find(object_id);
if (si != _sent_objects.end()) {
P3D_object *obj = (*si).second;
P3D_OBJECT_DECREF(obj);
_sent_objects.erase(si);
}
}
////////////////////////////////////////////////////////////////////
// Function: P3DSession::install_progress
// Access: Private
@ -676,103 +764,16 @@ rt_handle_request(TiXmlDocument *doc) {
ii = _instances.find(instance_id);
if (ii != _instances.end()) {
P3DInstance *inst = (*ii).second;
P3D_request *request = rt_make_p3d_request(xrequest);
if (request != NULL) {
inst->add_request(request);
}
inst->add_raw_request(doc);
doc = NULL;
}
RELEASE_LOCK(_instances_lock);
}
}
delete doc;
}
////////////////////////////////////////////////////////////////////
// Function: P3DSession::rt_make_p3d_request
// Access: Private
// Description: Creates a new P3D_request structure from the XML.
////////////////////////////////////////////////////////////////////
P3D_request *P3DSession::
rt_make_p3d_request(TiXmlElement *xrequest) {
P3D_request *request = NULL;
const char *rtype = xrequest->Attribute("rtype");
if (rtype != NULL) {
if (strcmp(rtype, "notify") == 0) {
const char *message = xrequest->Attribute("message");
if (message != NULL) {
request = new P3D_request;
request->_request_type = P3D_RT_notify;
request->_request._notify._message = strdup(message);
}
} else if (strcmp(rtype, "script") == 0) {
const char *operation = xrequest->Attribute("operation");
TiXmlElement *xobject = xrequest->FirstChildElement("object");
const char *property_name = xrequest->Attribute("property_name");
int needs_response = 0;
xrequest->Attribute("needs_response", &needs_response);
int unique_id = 0;
xrequest->Attribute("unique_id", &unique_id);
if (operation != NULL && xobject != NULL) {
P3D_script_operation op;
if (strcmp(operation, "get_property") == 0) {
op = P3D_SO_get_property;
} else if (strcmp(operation, "set_property") == 0) {
op = P3D_SO_set_property;
} else if (strcmp(operation, "del_property") == 0) {
op = P3D_SO_del_property;
} else if (strcmp(operation, "call") == 0) {
op = P3D_SO_call;
} else if (strcmp(operation, "eval") == 0) {
op = P3D_SO_eval;
} else {
// An unexpected operation.
return NULL;
}
request = new P3D_request;
request->_request_type = P3D_RT_script;
request->_request._script._op = op;
request->_request._script._object = xml_to_p3dobj(xobject);
request->_request._script._property_name = NULL;
if (property_name != NULL) {
request->_request._script._property_name = strdup(property_name);
}
request->_request._script._needs_response = (needs_response != 0);
request->_request._script._unique_id = unique_id;
// Fill in the value(s).
vector<P3D_object *> values;
TiXmlElement *xvalue = xrequest->FirstChildElement("value");
while (xvalue != NULL) {
P3D_object *value = xml_to_p3dobj(xvalue);
values.push_back(value);
xvalue = xvalue->NextSiblingElement("value");
}
if (values.empty()) {
// No values.
request->_request._script._values = NULL;
request->_request._script._num_values = 0;
} else {
// Some values.
int num_values = (int)values.size();
P3D_object **valuesp = new P3D_object *[num_values];
memcpy(valuesp, &values[0], num_values * sizeof(P3D_object *));
request->_request._script._values = valuesp;
request->_request._script._num_values = num_values;
}
}
} else {
nout << "Ignoring request of type " << rtype << "\n";
}
if (doc != NULL) {
delete doc;
}
return request;
}
////////////////////////////////////////////////////////////////////

View File

@ -19,6 +19,7 @@
#include "handleStream.h"
#include "p3dPackage.h"
#include "p3dConditionVar.h"
#include "p3dReferenceCount.h"
#include "get_tinyxml.h"
#include <map>
@ -34,11 +35,13 @@ class P3DProgressWindow;
// might include one or more P3DInstance objects running
// in the same memory space with each other.
////////////////////////////////////////////////////////////////////
class P3DSession {
class P3DSession : public P3DReferenceCount {
public:
P3DSession(P3DInstance *inst);
~P3DSession();
void shutdown();
inline const string &get_session_key() const;
inline const string &get_python_version() const;
@ -52,6 +55,11 @@ public:
P3D_object *xml_to_p3dobj(const TiXmlElement *xvalue);
TiXmlElement *p3dobj_to_xml(P3D_object *obj);
void signal_request_ready(P3DInstance *inst);
void drop_pyobj(int object_id);
void drop_p3dobj(int object_id);
private:
void install_progress(P3DPackage *package, double progress);
void start_p3dpython();
@ -65,7 +73,6 @@ private:
void rt_thread_run();
void rt_terminate();
void rt_handle_request(TiXmlDocument *doc);
P3D_request *rt_make_p3d_request(TiXmlElement *xrequest);
#ifdef _WIN32
static HANDLE

View File

@ -580,7 +580,6 @@ typedef enum {
P3D_RT_get_url,
P3D_RT_post_url,
P3D_RT_notify,
P3D_RT_script,
} P3D_request_type;
/* Structures corresponding to the request types in the above enum. */
@ -624,27 +623,6 @@ typedef struct {
const char *_message;
} P3D_request_notify;
/* A script object request. This is used to call out into the
browser_script_object (above). This request is handled internally
by the core API, and may safely be ignored by the host.
*/
typedef enum {
P3D_SO_get_property,
P3D_SO_set_property,
P3D_SO_del_property,
P3D_SO_call,
P3D_SO_eval,
} P3D_script_operation;
typedef struct {
P3D_object *_object;
P3D_script_operation _op;
const char *_property_name;
P3D_object **_values;
int _num_values;
bool _needs_response;
int _unique_id;
} P3D_request_script;
/* This is the overall structure that represents a single request. It
is returned by P3D_instance_get_request(). */
typedef struct {
@ -655,7 +633,6 @@ typedef struct {
P3D_request_get_url _get_url;
P3D_request_post_url _post_url;
P3D_request_notify _notify;
P3D_request_script _script;
} _request;
} P3D_request;

View File

@ -13,6 +13,7 @@
#include "p3dObject.cxx"
#include "p3dPackage.cxx"
#include "p3dPythonObject.cxx"
#include "p3dReferenceCount.cxx"
#include "p3dSession.cxx"
#include "p3dSplashWindow.cxx"
#include "p3dStringObject.cxx"

View File

@ -377,13 +377,20 @@ class AppRunner(DirectObject):
uniqueId = self.nextScriptId
self.nextScriptId += 1
self.sendRequest('script', operation, object,
propertyName, value, needsResponse, uniqueId);
propertyName, value, needsResponse, uniqueId)
if needsResponse:
# Now wait for the response to come in.
result = self.sendRequest('wait_script_response', uniqueId)
return result
def dropObject(self, objectId):
""" Inform the parent process that we no longer have an
interest in the P3D_object corresponding to the indicated
objectId. """
self.sendRequest('drop_p3dobj', objectId)
def parseSysArgs(self):
""" Converts sys.argv into (p3dFilename, tokens). """
import getopt
@ -444,6 +451,12 @@ class BrowserObject:
# the object to its parent.
self.__dict__['_BrowserObject__boundMethod'] = (None, None)
def __del__(self):
# When the BrowserObject destructs, tell the parent process it
# doesn't need to keep around its corresponding P3D_object any
# more.
self.__runner.dropObject(self.__objectId)
def __str__(self):
parentObj, attribName = self.__boundMethod
if parentObj:
@ -546,7 +559,7 @@ class BrowserObject:
else:
raise IndexError(key)
return value;
return value
def __setitem__(self, key, value):
result = self.__runner.scriptRequest('set_property', self,