command-and-response

This commit is contained in:
David Rose 2009-07-03 01:24:50 +00:00
parent 774ba59dc9
commit 891051485d
13 changed files with 404 additions and 109 deletions

View File

@ -21,6 +21,7 @@
p3d_plugin_config.h \
p3d_plugin_common.h \
p3dBoolObject.h \
p3dConditionVar.h p3dConditionVar.I \
p3dDownload.h p3dDownload.I \
p3dFileDownload.h p3dFileDownload.I \
p3dFileParams.h p3dFileParams.I \
@ -41,6 +42,7 @@
#define INCLUDED_SOURCES \
p3d_plugin.cxx \
p3dBoolObject.cxx \
p3dConditionVar.cxx \
p3dDownload.cxx \
p3dFileDownload.cxx \
p3dFileParams.cxx \

View File

@ -0,0 +1,14 @@
// Filename: p3dConditionVar.I
// Created by: drose (02Jul09)
//
////////////////////////////////////////////////////////////////////
//
// 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."
//
////////////////////////////////////////////////////////////////////

View File

@ -0,0 +1,136 @@
// Filename: p3dConditionVar.cxx
// Created by: drose (02Jul09)
//
////////////////////////////////////////////////////////////////////
//
// 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 "p3dConditionVar.h"
////////////////////////////////////////////////////////////////////
// Function: P3DConditionVar::Constructor
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
P3DConditionVar::
P3DConditionVar() {
#ifdef _WIN32
InitializeCriticalSection(&_lock);
// Create an auto-reset event.
_event_signal = CreateEvent(NULL, false, false, NULL);
#else // _WIN32
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_NORMAL);
int result = pthread_mutex_init(&_lock, &attr);
pthread_mutexattr_destroy(&attr);
assert(result == 0);
result = pthread_cond_init(&_cvar, NULL);
assert(result == 0);
#endif // _WIN32
}
////////////////////////////////////////////////////////////////////
// Function: P3DConditionVar::Destructor
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
P3DConditionVar::
~P3DConditionVar() {
#ifdef _WIN32
DeleteCriticalSection(&_lock);
CloseHandle(_event_signal);
#else // _WIN32
int result = pthread_mutex_destroy(&_lock);
assert(result == 0);
result = pthread_cond_destroy(&_cvar);
assert(result == 0);
#endif // _WIN32
}
////////////////////////////////////////////////////////////////////
// Function: P3DConditionVar::acquire
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
void P3DConditionVar::
acquire() {
#ifdef _WIN32
EnterCriticalSection(&_lock);
#else // _WIN32
int result = pthread_mutex_lock(&_lock);
assert(result == 0);
#endif // _WIN32
}
////////////////////////////////////////////////////////////////////
// Function: P3DConditionVar::wait
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
void P3DConditionVar::
wait() {
#ifdef _WIN32
LeaveCriticalSection(&_lock);
DWORD result = WaitForSingleObject(_event_signal, INFINITE);
assert(result == WAIT_OBJECT_0);
EnterCriticalSection(&_lock);
#else // _WIN32
int result = pthread_cond_wait(&_cvar, &_mutex._lock);
assert(result == 0);
#endif // _WIN32
}
////////////////////////////////////////////////////////////////////
// Function: P3DConditionVar::notify
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
void P3DConditionVar::
notify() {
#ifdef _WIN32
SetEvent(_event_signal);
#else // _WIN32
int result = pthread_cond_signal(&_cvar);
assert(result == 0);
#endif // _WIN32
}
////////////////////////////////////////////////////////////////////
// Function: P3DConditionVar::release
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
void P3DConditionVar::
release() {
#ifdef _WIN32
LeaveCriticalSection(&_lock);
#else // _WIN32
int result = pthread_mutex_unlock(&_lock);
assert(result == 0);
#endif // _WIN32
}

View File

@ -0,0 +1,49 @@
// Filename: p3dConditionVar.h
// Created by: drose (02Jul09)
//
////////////////////////////////////////////////////////////////////
//
// 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 P3DCONDITIONVAR_H
#define P3DCONDITIONVAR_H
#include "p3d_plugin_common.h"
////////////////////////////////////////////////////////////////////
// Class : P3DConditionVar
// Description : A simple condition-variable like object. It doesn't
// support the full condition-var semantics, but it
// works well enough with one waiter and one signaller.
////////////////////////////////////////////////////////////////////
class P3DConditionVar {
public:
P3DConditionVar();
~P3DConditionVar();
void acquire();
void wait();
void notify();
void release();
private:
#ifdef _WIN32
CRITICAL_SECTION _lock;
HANDLE _event_signal;
#else // _WIN32
pthread_mutex_t _lock;
pthread_cond_t _cvar;
#endif // _WIN32
};
#include "p3dConditionVar.I"
#endif

View File

@ -345,38 +345,6 @@ feed_url_stream(int unique_id,
return download_ok;
}
////////////////////////////////////////////////////////////////////
// Function: P3DInstance::feed_value
// Access: Public
// Description: Called by the host in response to a get_property or
// call request. The value object must have been
// freshly allocated; it will be deleted by this method.
////////////////////////////////////////////////////////////////////
void P3DInstance::
feed_value(int unique_id, P3DObject *value) {
if (_session != NULL) {
TiXmlDocument *doc = new TiXmlDocument;
TiXmlDeclaration *decl = new TiXmlDeclaration("1.0", "utf-8", "");
TiXmlElement *xcommand = new TiXmlElement("command");
xcommand->SetAttribute("cmd", "feed_value");
xcommand->SetAttribute("instance_id", get_instance_id());
xcommand->SetAttribute("unique_id", unique_id);
if (value != NULL) {
TiXmlElement *xvalue = value->make_xml();
xcommand->LinkEndChild(xvalue);
}
doc->LinkEndChild(decl);
doc->LinkEndChild(xcommand);
_session->send_command(doc);
}
if (value != NULL) {
delete value;
}
}
////////////////////////////////////////////////////////////////////
// Function: P3DInstance::add_package
// Access: Public

View File

@ -63,7 +63,6 @@ public:
size_t total_expected_data,
const unsigned char *this_data,
size_t this_data_size);
void feed_value(int unique_id, P3DObject *value);
inline int get_instance_id() const;
inline const string &get_session_key() const;

View File

@ -38,14 +38,6 @@ P3DInstanceManager() {
_is_initialized = false;
_unique_session_index = 0;
_request_seq = 0;
#ifdef _WIN32
_request_ready = CreateEvent(NULL, false, false, NULL);
#else
INIT_LOCK(_request_ready_lock);
pthread_cond_init(&_request_ready_cvar, NULL);
#endif
#ifdef _WIN32
// Ensure the appropriate Windows common controls are available to
// this application.
@ -68,13 +60,6 @@ P3DInstanceManager::
assert(_instances.empty());
assert(_sessions.empty());
#ifdef _WIN32
CloseHandle(_request_ready);
#else
DESTROY_LOCK(_request_ready_lock);
pthread_cond_destroy(&_request_ready_cvar);
#endif
}
////////////////////////////////////////////////////////////////////
@ -211,30 +196,21 @@ check_request() {
////////////////////////////////////////////////////////////////////
void P3DInstanceManager::
wait_request() {
int seq = _request_seq;
_request_ready.acquire();
while (true) {
if (check_request() != (P3DInstance *)NULL) {
_request_ready.release();
return;
}
if (_instances.empty()) {
_request_ready.release();
return;
}
// No pending requests; go to sleep.
#ifdef _WIN32
if (seq == _request_seq) {
WaitForSingleObject(_request_ready, INFINITE);
}
#else
ACQUIRE_LOCK(_request_ready_lock);
if (seq == _request_seq) {
pthread_cond_wait(&_request_ready_cvar, &_request_ready_lock);
}
RELEASE_LOCK(_request_ready_lock);
#endif
seq = _request_seq;
_request_ready.wait();
}
_request_ready.release();
}
////////////////////////////////////////////////////////////////////
@ -283,15 +259,9 @@ get_unique_session_index() {
////////////////////////////////////////////////////////////////////
void P3DInstanceManager::
signal_request_ready() {
#ifdef _WIN32
++_request_seq;
SetEvent(_request_ready);
#else
ACQUIRE_LOCK(_request_ready_lock);
++_request_seq;
pthread_cond_signal(&_request_ready_cvar);
RELEASE_LOCK(_request_ready_lock);
#endif
_request_ready.acquire();
_request_ready.notify();
_request_ready.release();
}
////////////////////////////////////////////////////////////////////

View File

@ -16,6 +16,7 @@
#define P3DINSTANCEMANAGER_H
#include "p3d_plugin_common.h"
#include "p3dConditionVar.h"
#include <set>
#include <map>
@ -83,14 +84,7 @@ private:
int _unique_session_index;
// Implements a condition-var like behavior.
volatile int _request_seq;
#ifdef _WIN32
HANDLE _request_ready;
#else
LOCK _request_ready_lock;
pthread_cond_t _request_ready_cvar;
#endif
P3DConditionVar _request_ready;
static P3DInstanceManager *_global_ptr;
};

View File

@ -192,9 +192,17 @@ handle_command(TiXmlDocument *doc) {
nout << "got command: " << *doc << "\n";
TiXmlElement *xcommand = doc->FirstChildElement("command");
if (xcommand != NULL) {
bool needs_response = false;
int want_response_id;
if (xcommand->QueryIntAttribute("want_response_id", &want_response_id) == TIXML_SUCCESS) {
// This command will be waiting for a response.
needs_response = true;
}
const char *cmd = xcommand->Attribute("cmd");
if (cmd != NULL) {
if (strcmp(cmd, "start_instance") == 0) {
assert(!needs_response);
TiXmlElement *xinstance = xcommand->FirstChildElement("instance");
if (xinstance != (TiXmlElement *)NULL) {
P3DCInstance *inst = new P3DCInstance(xinstance);
@ -202,12 +210,14 @@ handle_command(TiXmlDocument *doc) {
}
} else if (strcmp(cmd, "terminate_instance") == 0) {
assert(!needs_response);
int instance_id;
if (xcommand->QueryIntAttribute("instance_id", &instance_id) == TIXML_SUCCESS) {
terminate_instance(instance_id);
}
} else if (strcmp(cmd, "setup_window") == 0) {
assert(!needs_response);
int instance_id;
TiXmlElement *xwparams = xcommand->FirstChildElement("wparams");
if (xwparams != (TiXmlElement *)NULL &&
@ -216,34 +226,61 @@ handle_command(TiXmlDocument *doc) {
}
} else if (strcmp(cmd, "exit") == 0) {
assert(!needs_response);
terminate_session();
} else if (strcmp(cmd, "feed_value") == 0) {
int instance_id, unique_id;
if (xcommand->QueryIntAttribute("instance_id", &instance_id) == TIXML_SUCCESS &&
xcommand->QueryIntAttribute("unique_id", &unique_id) == TIXML_SUCCESS) {
// TODO: deal with instance_id.
TiXmlElement *xvalue = xcommand->FirstChildElement("value");
if (xvalue != NULL) {
PyObject *value = from_xml_value(xvalue);
PyObject *result = PyObject_CallMethod
(_runner, (char*)"feedValue", (char*)"iOi", unique_id, value, true);
Py_DECREF(value);
Py_XDECREF(result);
} else {
PyObject *result = PyObject_CallMethod
(_runner, (char*)"feedValue", (char*)"iOi", unique_id, Py_None, false);
Py_XDECREF(result);
}
}
} else if (strcmp(cmd, "pyobj") == 0) {
// Manipulate or query a python object. Presumably this
// command will want a response.
assert(needs_response);
handle_pyobj_command(xcommand, want_response_id);
} else {
nout << "Unhandled command " << cmd << "\n";
if (needs_response) {
// Better send a response.
TiXmlDocument doc;
TiXmlDeclaration *decl = new TiXmlDeclaration("1.0", "utf-8", "");
TiXmlElement *xresponse = new TiXmlElement("response");
xresponse->SetAttribute("response_id", want_response_id);
doc.LinkEndChild(decl);
doc.LinkEndChild(xresponse);
nout << "sending " << doc << "\n" << flush;
_pipe_write << doc << flush;
}
}
}
}
}
////////////////////////////////////////////////////////////////////
// Function: P3DPythonRun::handle_pyobj_command
// Access: Private
// Description: Handles the pyobj command, which queries or modifies
// a Python object from the browser scripts.
////////////////////////////////////////////////////////////////////
void P3DPythonRun::
handle_pyobj_command(TiXmlElement *xcommand, int want_response_id) {
TiXmlDocument doc;
TiXmlDeclaration *decl = new TiXmlDeclaration("1.0", "utf-8", "");
TiXmlElement *xresponse = new TiXmlElement("response");
xresponse->SetAttribute("response_id", want_response_id);
doc.LinkEndChild(decl);
doc.LinkEndChild(xresponse);
const char *op = xcommand->Attribute("op");
if (op != NULL) {
if (strcmp(op, "get_script_object") == 0) {
// Get the toplevel Python object.
xresponse->SetAttribute("object", "fooby");
}
}
nout << "sending " << doc << "\n" << flush;
_pipe_write << doc << flush;
}
////////////////////////////////////////////////////////////////////
// Function: P3DPythonRun::check_comm
// Access: Private

View File

@ -70,6 +70,8 @@ public:
private:
void handle_command(TiXmlDocument *doc);
void handle_pyobj_command(TiXmlElement *xcommand, int want_response_id);
AsyncTask::DoneStatus check_comm(GenericAsyncTask *task);
static AsyncTask::DoneStatus st_check_comm(GenericAsyncTask *task, void *user_data);

View File

@ -36,6 +36,10 @@ P3DSession(P3DInstance *inst) {
_python_version = inst->get_python_version();
_p3dpython_running = false;
_next_response_id = 0;
_response = NULL;
_got_response_id = -1;
_started_read_thread = false;
_read_thread_continue = false;
@ -210,6 +214,78 @@ send_command(TiXmlDocument *command) {
}
}
////////////////////////////////////////////////////////////////////
// Function: P3DSession::command_and_response
// Access: Public
// Description: Sends the indicated command to the running Python
// process, and waits for a response. Returns the
// newly-allocated response on success, or NULL on
// failure.
//
// The command must be a newly-allocated TiXmlDocument;
// it will be deleted after it has been delivered to the
// process.
//
// This will fail if the python process is not running
// or if it suddenly stops.
////////////////////////////////////////////////////////////////////
TiXmlDocument *P3DSession::
command_and_response(TiXmlDocument *command) {
if (!_p3dpython_running) {
return NULL;
}
int response_id = _next_response_id;
++_next_response_id;
// Add the "want_response_id" attribute to the toplevel command, so
// the sub-process knows we'll be waiting for its response.
TiXmlElement *xcommand = command->FirstChildElement("command");
assert(xcommand != NULL);
xcommand->SetAttribute("want_response_id", response_id);
_pipe_write << *command << flush;
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;
_response_ready.acquire();
while (_response == NULL || _got_response_id != response_id) {
if (_response != NULL) {
// This is a bogus response. Since we're the only thread waiting,
// it follows that no one is waiting for this response, so we can
// throw it away.
nout << "Discarding bogus response: " << *_response << "\n";
delete _response;
_response = NULL;
_got_response_id = -1;
}
if (!_p3dpython_running) {
// Hmm, looks like Python has gone away.
// TODO: make sure _p3dpython_running gets set to false when the
// process dies unexpectedly.
_response_ready.release();
return NULL;
}
_response_ready.wait();
}
// When we exit the loop, we've found the desired response.
TiXmlDocument *response = _response;
_response = NULL;
_got_response_id = -1;
_response_ready.release();
nout << "Got response: " << *response << "\n" << flush;
return response;
}
////////////////////////////////////////////////////////////////////
// Function: P3DSession::install_progress
// Access: Private
@ -316,6 +392,22 @@ start_p3dpython() {
}
_pipe_write << flush;
_commands.clear();
// Temp testing code.
{
TiXmlDocument *doc = new TiXmlDocument;
TiXmlDeclaration *decl = new TiXmlDeclaration("1.0", "utf-8", "");
TiXmlElement *xcommand = new TiXmlElement("command");
xcommand->SetAttribute("cmd", "pyobj");
xcommand->SetAttribute("op", "get");
doc->LinkEndChild(decl);
doc->LinkEndChild(xcommand);
TiXmlDocument *response = command_and_response(doc);
nout << "response pointer: " << response << "\n";
if (response != NULL) {
delete response;
}
}
}
////////////////////////////////////////////////////////////////////
@ -409,10 +501,33 @@ void P3DSession::
rt_handle_request(TiXmlDocument *doc) {
nout << "Session got request: " << *doc << "\n" << flush;
TiXmlElement *xresponse = doc->FirstChildElement("response");
if (xresponse != (TiXmlElement *)NULL) {
int response_id;
if (xresponse->QueryIntAttribute("response_id", &response_id) == TIXML_SUCCESS) {
// This is a response to a previous command-and-response. Send
// it to the parent thread.
_response_ready.acquire();
if (_response != NULL) {
// Hey, there's already a response there. Since there's only
// one thread waiting at a time on the command-response cycle,
// this must be a bogus response that never got picked up.
// Discard it.
nout << "Discarding bogus response: " << *_response << "\n";
delete _response;
}
_response = doc;
_got_response_id = response_id;
_response_ready.notify();
_response_ready.release();
return;
}
}
TiXmlElement *xrequest = doc->FirstChildElement("request");
if (xrequest != (TiXmlElement *)NULL) {
int instance_id ;
if (xrequest->Attribute("instance_id", &instance_id)) {
int instance_id;
if (xrequest->QueryIntAttribute("instance_id", &instance_id) == TIXML_SUCCESS) {
// Look up the particular instance this is related to.
ACQUIRE_LOCK(_instances_lock);
Instances::const_iterator ii;

View File

@ -18,6 +18,7 @@
#include "p3d_plugin_common.h"
#include "handleStream.h"
#include "p3dPackage.h"
#include "p3dConditionVar.h"
#include "get_tinyxml.h"
#include <map>
@ -47,6 +48,7 @@ public:
inline int get_num_instances() const;
void send_command(TiXmlDocument *command);
TiXmlDocument *command_and_response(TiXmlDocument *command);
private:
void install_progress(P3DPackage *package, double progress);
@ -120,6 +122,13 @@ private:
#endif
bool _p3dpython_running;
int _next_response_id;
// The _response_ready mutex protects this pointer.
TiXmlDocument *_response;
int _got_response_id;
P3DConditionVar _response_ready;
// The remaining members are manipulated by or for the read thread.
bool _started_read_thread;
HandleStream _pipe_read;

View File

@ -1,20 +1,20 @@
#include "p3d_plugin.cxx"
#include "p3dBoolObject.cxx"
#include "p3dConditionVar.cxx"
#include "p3dDownload.cxx"
#include "p3dFileDownload.cxx"
#include "p3dFileParams.cxx"
#include "p3dFloatObject.cxx"
#include "p3dInstance.cxx"
#include "p3dInstanceManager.cxx"
#include "p3dIntObject.cxx"
#include "p3dListObject.cxx"
#include "p3dMultifileReader.cxx"
#include "p3dNoneObject.cxx"
#include "p3dPackage.cxx"
#include "p3dSplashWindow.cxx"
#include "p3dSession.cxx"
#include "p3dWindowParams.cxx"
#include "p3dWinSplashWindow.cxx"
#include "p3dObject.cxx"
#include "p3dBoolObject.cxx"
#include "p3dIntObject.cxx"
#include "p3dFloatObject.cxx"
#include "p3dListObject.cxx"
#include "p3dPackage.cxx"
#include "p3dSession.cxx"
#include "p3dSplashWindow.cxx"
#include "p3dStringObject.cxx"
#include "p3dWinSplashWindow.cxx"
#include "p3dWindowParams.cxx"