support python in a child process, multiple pythons now possible

This commit is contained in:
David Rose 2009-06-09 00:12:32 +00:00
parent 7b00b88f29
commit 10d51dafb6
9 changed files with 233 additions and 66 deletions

View File

@ -37,6 +37,8 @@
handleStream.cxx handleStream.h handleStream.I \
handleStreamBuf.cxx handleStreamBuf.h \
p3d_lock.h \
p3dCInstance.cxx \
p3dCInstance.h p3dCInstance.I \
p3dPythonRun.cxx p3dPythonRun.h p3dPythonRun.I
#end bin_target

View File

@ -258,7 +258,7 @@ read_chars(char *start, size_t length) {
BOOL success = ReadFile(_handle, start, length, &bytes_read, NULL);
if (!success) {
DWORD error = GetLastError();
if (error != ERROR_HANDLE_EOF) {
if (error != ERROR_HANDLE_EOF && error != ERROR_BROKEN_PIPE) {
cerr
<< "Error reading " << length
<< " bytes, windows error code 0x" << hex
@ -311,10 +311,12 @@ write_chars(const char *start, size_t length) {
BOOL success = WriteFile(_handle, start, length, &bytes_written, NULL);
if (!success) {
DWORD error = GetLastError();
cerr
<< "Error writing " << length
<< " bytes, windows error code 0x" << hex
<< error << dec << ".\n";
if (error != ERROR_BROKEN_PIPE) {
cerr
<< "Error writing " << length
<< " bytes, windows error code 0x" << hex
<< error << dec << ".\n";
}
return bytes_written;
}
assert(bytes_written == length);

View File

@ -19,7 +19,7 @@
// Description: Returns the p3d filename that was passed to the
// constructor.
////////////////////////////////////////////////////////////////////
INLINE const string &P3DInstance::
inline const string &P3DInstance::
get_p3d_filename() const {
return _p3d_filename;
}
@ -35,7 +35,7 @@ get_p3d_filename() const {
// each unique session required for different
// P3DInstances.
////////////////////////////////////////////////////////////////////
INLINE const string &P3DInstance::
inline const string &P3DInstance::
get_session_key() const {
return _session_key;
}
@ -46,7 +46,7 @@ get_session_key() const {
// Description: Returns a string that uniquely identifies this
// instance's required Python version.
////////////////////////////////////////////////////////////////////
INLINE const string &P3DInstance::
inline const string &P3DInstance::
get_python_version() const {
return _python_version;
}

View File

@ -38,7 +38,6 @@ P3DInstance(P3D_request_ready_func *func,
_parent_window(parent_window)
{
P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
cerr << "creating instance\n";
INIT_LOCK(_request_lock);
@ -192,6 +191,51 @@ feed_url_stream(int unique_id,
size_t this_data_size) {
}
////////////////////////////////////////////////////////////////////
// Function: P3DInstance::make_xml
// Access: Public
// Description: Returns a newly-allocated XML structure that
// corresponds to the data within this instance.
////////////////////////////////////////////////////////////////////
TiXmlElement *P3DInstance::
make_xml() {
TiXmlElement *xinstance = new TiXmlElement("instance");
xinstance->SetAttribute("p3d_filename", _p3d_filename);
switch (_window_type) {
case P3D_WT_embedded:
xinstance->SetAttribute("window_type", "embedded");
xinstance->SetAttribute("win_x", _win_x);
xinstance->SetAttribute("win_y", _win_y);
xinstance->SetAttribute("win_width", _win_width);
xinstance->SetAttribute("win_height", _win_height);
#ifdef _WIN32
xinstance->SetAttribute("parent_hwnd", (int)_parent_window._hwnd);
#endif
break;
case P3D_WT_toplevel:
xinstance->SetAttribute("window_type", "toplevel");
xinstance->SetAttribute("win_x", _win_x);
xinstance->SetAttribute("win_y", _win_y);
xinstance->SetAttribute("win_width", _win_width);
xinstance->SetAttribute("win_height", _win_height);
break;
case P3D_WT_fullscreen:
xinstance->SetAttribute("window_type", "fullscreen");
xinstance->SetAttribute("win_width", _win_width);
xinstance->SetAttribute("win_height", _win_height);
break;
case P3D_WT_hidden:
xinstance->SetAttribute("window_type", "hidden");
break;
}
return xinstance;
}
////////////////////////////////////////////////////////////////////
// Function: P3DInstance::fill_tokens
// Access: Private
@ -211,3 +255,4 @@ fill_tokens(const P3D_token *tokens[], size_t tokens_size) {
_tokens.push_back(token);
}
}

View File

@ -19,12 +19,14 @@
#include <vector>
#include <deque>
#include <tinyxml.h>
class P3DSession;
////////////////////////////////////////////////////////////////////
// Class : P3DInstance
// Description : This is an instance of a Panda3D window.
// Description : This is an instance of a Panda3D window, as seen in
// the parent-level process.
////////////////////////////////////////////////////////////////////
class P3DInstance : public P3D_instance {
public:
@ -53,9 +55,11 @@ public:
const unsigned char *this_data,
size_t this_data_size);
INLINE const string &get_p3d_filename() const;
INLINE const string &get_session_key() const;
INLINE const string &get_python_version() const;
inline const string &get_p3d_filename() const;
inline const string &get_session_key() const;
inline const string &get_python_version() const;
TiXmlElement *make_xml();
private:
void fill_tokens(const P3D_token *tokens[], size_t tokens_size);

View File

@ -24,6 +24,18 @@ P3DPythonRun(int argc, char *argv[]) {
_read_thread_alive = false;
INIT_LOCK(_commands_lock);
_program_name = argv[0];
_py_argc = 1;
_py_argv = (char **)malloc(2 * sizeof(char *));
_py_argv[0] = argv[0];
_py_argv[1] = NULL;
// Initialize Python. It appears to be important to do this before
// we open the pipe streams and spawn the thread, below.
Py_SetProgramName((char *)_program_name.c_str());
Py_Initialize();
PySys_SetArgv(_py_argc, _py_argv);
// Open the pipe streams with the input and output handles from the
// parent.
#ifdef _WIN32
@ -35,6 +47,7 @@ P3DPythonRun(int argc, char *argv[]) {
if (!SetStdHandle(STD_OUTPUT_HANDLE, INVALID_HANDLE_VALUE)) {
cerr << "unable to reset input handle\n";
}
_pipe_read.open_read(read);
_pipe_write.open_write(write);
#endif // _WIN32
@ -46,17 +59,6 @@ P3DPythonRun(int argc, char *argv[]) {
}
spawn_read_thread();
_program_name = argv[0];
_py_argc = 1;
_py_argv = (char **)malloc(2 * sizeof(char *));
_py_argv[0] = argv[0];
_py_argv[1] = NULL;
// Initialize Python.
Py_SetProgramName((char *)_program_name.c_str());
Py_Initialize();
PySys_SetArgv(_py_argc, _py_argv);
}
////////////////////////////////////////////////////////////////////
@ -172,6 +174,12 @@ run_python() {
void P3DPythonRun::
handle_command(TiXmlDocument *doc) {
cerr << "got command: " << *doc << "\n";
TiXmlHandle h(doc);
TiXmlElement *xinstance = h.FirstChild("instance").ToElement();
if (xinstance != (TiXmlElement *)NULL) {
P3DCInstance *inst = new P3DCInstance(xinstance);
start_instance(inst);
}
}
////////////////////////////////////////////////////////////////////
@ -255,6 +263,53 @@ join_read_thread() {
cerr << "done waiting for thread\n";
}
////////////////////////////////////////////////////////////////////
// Function: P3DPythonRun::start_instance
// Access: Private
// Description: Starts the indicated instance running within the
// Python process.
////////////////////////////////////////////////////////////////////
void P3DPythonRun::
start_instance(P3DCInstance *inst) {
cerr << "starting instance " << inst->get_p3d_filename() << "\n";
_instances.push_back(inst);
string window_type;
switch (inst->_window_type) {
case P3D_WT_embedded:
window_type = "embedded";
break;
case P3D_WT_toplevel:
window_type = "toplevel";
break;
case P3D_WT_fullscreen:
window_type = "fullscreen";
break;
case P3D_WT_hidden:
window_type = "hidden";
break;
}
PyObject *result = PyObject_CallFunction
(_setupWindow, "siiiii", window_type.c_str(),
inst->_win_x, inst->_win_y,
inst->_win_width, inst->_win_height,
#ifdef _WIN32
(int)(inst->_parent_window._hwnd)
#endif
);
if (result == NULL) {
PyErr_Print();
}
Py_XDECREF(result);
result = PyObject_CallFunction(_runPackedApp, "[s]", inst->get_p3d_filename().c_str());
if (result == NULL) {
PyErr_Print();
}
Py_XDECREF(result);
}
////////////////////////////////////////////////////////////////////
// Function: P3DPythonRun::rt_thread_run
// Access: Private
@ -262,8 +317,10 @@ join_read_thread() {
////////////////////////////////////////////////////////////////////
void P3DPythonRun::
rt_thread_run() {
cerr << "thread reading.\n";
while (_read_thread_alive) {
TiXmlDocument *doc = new TiXmlDocument;
_pipe_read >> *doc;
if (!_pipe_read || _pipe_read.eof()) {
// Some error on reading. Abort.

View File

@ -18,11 +18,13 @@
#include <iostream>
#include <string>
#include <deque>
#include <vector>
#include <assert.h>
#include <Python.h>
#include <tinyxml.h>
#include "p3d_lock.h"
#include "handleStream.h"
#include "p3dCInstance.h"
#ifdef _WIN32
#include <windows.h>
@ -60,6 +62,8 @@ private:
void spawn_read_thread();
void join_read_thread();
void start_instance(P3DCInstance *inst);
private:
// This method runs only within the read thread.
void rt_thread_run();
@ -68,6 +72,17 @@ private:
#endif
private:
typedef vector<P3DCInstance *> Instances;
Instances _instances;
string _program_name;
int _py_argc;
char **_py_argv;
PyObject *_runPackedApp;
PyObject *_setupWindow;
// The remaining members are manipulated by the read thread.
typedef deque<TiXmlDocument *> Commands;
Commands _commands;
LOCK _commands_lock;
@ -79,13 +94,6 @@ private:
#ifdef _WIN32
HANDLE _read_thread;
#endif
string _program_name;
int _py_argc;
char **_py_argv;
PyObject *_runPackedApp;
PyObject *_setupWindow;
};
#include "p3dPythonRun.I"

View File

@ -43,6 +43,9 @@ P3DSession(P3DInstance *inst) {
// environment, if they are set.
const char *keep[] = {
"TEMP", "HOME", "USER",
#ifdef _WIN32
"SYSTEMROOT",
#endif
NULL
};
for (int ki = 0; keep[ki] != NULL; ++ki) {
@ -66,36 +69,43 @@ P3DSession(P3DInstance *inst) {
// Create a bi-directional pipe to communicate with the sub-process.
#ifdef _WIN32
cerr << "creating pipe\n";
HANDLE orig_stdin = GetStdHandle(STD_INPUT_HANDLE);
HANDLE orig_stdout = GetStdHandle(STD_OUTPUT_HANDLE);
SECURITY_ATTRIBUTES inheritable;
inheritable.nLength = sizeof(inheritable);
inheritable.lpSecurityDescriptor = NULL;
inheritable.bInheritHandle = true;
HANDLE r_to, w_to, r_from, w_from;
// Create the pipe to the process.
if (!CreatePipe(&r_to, &w_to, &inheritable, 0)) {
if (!CreatePipe(&r_to, &w_to, NULL, 0)) {
cerr << "failed to create pipe\n";
} else {
SetStdHandle(STD_INPUT_HANDLE, r_to);
// Make sure the right end of the pipe is inheritable.
SetHandleInformation(r_to, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT);
SetHandleInformation(w_to, HANDLE_FLAG_INHERIT, 0);
}
// Create the pipe from the process.
if (!CreatePipe(&r_from, &w_from, &inheritable, 0)) {
if (!CreatePipe(&r_from, &w_from, NULL, 0)) {
cerr << "failed to create pipe\n";
} else {
SetStdHandle(STD_OUTPUT_HANDLE, w_from);
} else {
// Make sure the right end of the pipe is inheritable.
SetHandleInformation(w_from, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT);
SetHandleInformation(r_from, HANDLE_FLAG_INHERIT, 0);
}
cerr << "creating process " << p3dpython << "\n";
// Make sure we see an error dialog if there is a missing DLL.
SetErrorMode(0);
// Pass the appropriate ends of the bi-directional pipe as the
// standard input and standard output of the child process.
STARTUPINFO startup_info;
ZeroMemory(&startup_info, sizeof(STARTUPINFO));
startup_info.cb = sizeof(startup_info);
startup_info.hStdError = GetStdHandle(STD_ERROR_HANDLE);
startup_info.hStdOutput = w_from;
startup_info.hStdInput = r_to;
startup_info.dwFlags |= STARTF_USESTDHANDLES;
BOOL result = CreateProcess
(p3dpython.c_str(), NULL, NULL, NULL, TRUE, 0,
/* (void *)env.c_str() */NULL, _python_root_dir.c_str(), NULL, &_p3dpython);
cerr << "result = " << result << "\n";
(void *)env.c_str(), _python_root_dir.c_str(),
&startup_info, &_p3dpython);
_started_p3dpython = (result != 0);
if (!_started_p3dpython) {
@ -104,10 +114,9 @@ P3DSession(P3DInstance *inst) {
cerr << "Created process: " << _p3dpython.dwProcessId << "\n";
}
CloseHandle(r_to);
// Close the pipe handles that are now owned by the child.
CloseHandle(w_from);
SetStdHandle(STD_INPUT_HANDLE, orig_stdin);
SetStdHandle(STD_OUTPUT_HANDLE, orig_stdout);
CloseHandle(r_to);
_pipe_read.open_read(r_from);
_pipe_write.open_write(w_to);
@ -162,11 +171,13 @@ start_instance(P3DInstance *inst) {
TiXmlDocument doc;
TiXmlDeclaration *decl = new TiXmlDeclaration("1.0", "", "");
TiXmlElement *element = new TiXmlElement("instance");
TiXmlElement *xinstance = inst->make_xml();
doc.LinkEndChild(decl);
doc.LinkEndChild(element);
doc.LinkEndChild(xinstance);
_pipe_write << doc;
cerr << "sending: " << doc << "\n";
_pipe_write << doc << flush;
/*
P3DPython *python = inst_mgr->start_python(_python_version);

View File

@ -16,6 +16,7 @@
#include <iostream>
#include <string>
#include <math.h>
#ifdef _WIN32
#include "wingetopt.h"
@ -301,26 +302,63 @@ main(int argc, char *argv[]) {
return 1;
}
int num_instances = argc - 1;
P3D_window_handle parent_window;
if (window_type == P3D_WT_embedded) {
// The user asked for an embedded window. Create a toplevel
// window to be its parent, of the requested size.
if (win_width == 0 && win_height == 0) {
win_width = 640;
win_height = 480;
}
make_parent_window(parent_window, win_width, win_height);
// Center the child window(s) within the parent window.
RECT rect;
GetClientRect(parent_window._hwnd, &rect);
// And center the graphics window within that parent window.
win_x = (int)(win_width * 0.1);
win_y = (int)(win_height * 0.1);
win_width = (int)(win_width * 0.8);
win_height = (int)(win_height * 0.8);
win_x = (int)(rect.right * 0.1);
win_y = (int)(rect.bottom * 0.1);
win_width = (int)(rect.right * 0.8);
win_height = (int)(rect.bottom * 0.8);
// Subdivide the window into num_x_spans * num_y_spans sub-windows.
int num_y_spans = int(sqrt((double)num_instances));
int num_x_spans = (num_instances + num_y_spans - 1) / num_y_spans;
int inst_width = win_width / num_x_spans;
int inst_height = win_height / num_y_spans;
for (int yi = 0; yi < num_y_spans; ++yi) {
for (int xi = 0; xi < num_x_spans; ++xi) {
int i = yi * num_x_spans + xi;
if (i >= num_instances) {
continue;
}
// Create instance i at window slot (xi, yi).
int inst_x = win_x + xi * inst_width;
int inst_y = win_y + yi * inst_height;
P3D_create_instance
(NULL, argv[i + 1],
P3D_WT_embedded, inst_x, inst_y, inst_width, inst_height, parent_window,
NULL, 0);
}
}
} else {
// Not an embedded window. Create each window with the same parameters.
for (int i = 0; i < num_instances; ++i) {
P3D_create_instance
(NULL, argv[i + 1],
window_type, win_x, win_y, win_width, win_height, parent_window,
NULL, 0);
}
}
// For now, only one instance at a time is supported. Ignore the
// remaining command-line parameters.
P3D_create_instance
(NULL, argv[1],
window_type, win_x, win_y, win_width, win_height, parent_window,
NULL, 0);
#ifdef _WIN32
// Wait for new messages from Windows, and new requests from the
// plugin.