one_process support. Some issues.

This commit is contained in:
David Rose 2009-08-29 23:08:00 +00:00
parent 034a977c3d
commit 4388b853e5
10 changed files with 485 additions and 273 deletions

View File

@ -2185,10 +2185,12 @@ class Packager:
# This is the main program that drives the runtime Python. It # This is the main program that drives the runtime Python. It
# is responsible for loading _vfsimporter.pyd, and then # is responsible for loading _vfsimporter.pyd, and then
# importing direct.p3d.AppRunner, to start an application # importing direct.p3d.AppRunner, to start an application
# running. Note that the .exe extension is automatically # running. The program comes in two parts: an executable, and
# replaced with the platform-specific extension appropriate # an associated dynamic library. Note that the .exe and .dll
# for an executable. # extensions are automatically replaced with the appropriate
# platform-specific extensions.
self.do_file('p3dpython.exe') self.do_file('p3dpython.exe')
self.do_file('libp3dpython.dll')
def do_freeze(self, filename, compileToExe = False): def do_freeze(self, filename, compileToExe = False):
""" Freezes all of the current Python code into either an """ Freezes all of the current Python code into either an

View File

@ -18,6 +18,7 @@
find_root_dir.cxx find_root_dir.h \ find_root_dir.cxx find_root_dir.h \
get_tinyxml.h \ get_tinyxml.h \
binaryXml.cxx binaryXml.h \ binaryXml.cxx binaryXml.h \
fhandle.h \
handleStream.cxx handleStream.h handleStream.I \ handleStream.cxx handleStream.h handleStream.I \
handleStreamBuf.cxx handleStreamBuf.h handleStreamBuf.I \ handleStreamBuf.cxx handleStreamBuf.h handleStreamBuf.I \
mkdir_complete.cxx mkdir_complete.h \ mkdir_complete.cxx mkdir_complete.h \
@ -51,7 +52,8 @@
p3dUndefinedObject.h \ p3dUndefinedObject.h \
p3dWinSplashWindow.h p3dWinSplashWindow.I \ p3dWinSplashWindow.h p3dWinSplashWindow.I \
p3dX11SplashWindow.h \ p3dX11SplashWindow.h \
p3dWindowParams.h p3dWindowParams.I p3dWindowParams.h p3dWindowParams.I \
run_p3dpython.h
#define INCLUDED_SOURCES \ #define INCLUDED_SOURCES \
p3d_plugin.cxx \ p3d_plugin.cxx \
@ -91,7 +93,7 @@
#end lib_target #end lib_target
#begin bin_target #begin lib_target
#define BUILD_TARGET $[HAVE_PYTHON] #define BUILD_TARGET $[HAVE_PYTHON]
#define USE_PACKAGES tinyxml python #define USE_PACKAGES tinyxml python
#define TARGET p3dpython #define TARGET p3dpython
@ -105,19 +107,31 @@
#define SOURCES \ #define SOURCES \
binaryXml.cxx binaryXml.h \ binaryXml.cxx binaryXml.h \
fhandle.h \
handleStream.cxx handleStream.h handleStream.I \ handleStream.cxx handleStream.h handleStream.I \
handleStreamBuf.cxx handleStreamBuf.h handleStreamBuf.I \ handleStreamBuf.cxx handleStreamBuf.h handleStreamBuf.I \
p3d_lock.h p3d_plugin.h \ p3d_lock.h p3d_plugin.h \
p3d_plugin_config.h \ p3d_plugin_config.h \
p3dCInstance.cxx \ p3dCInstance.cxx \
p3dCInstance.h p3dCInstance.I \ p3dCInstance.h p3dCInstance.I \
p3dPythonRun.cxx p3dPythonRun.h p3dPythonRun.I p3dPythonRun.cxx p3dPythonRun.h p3dPythonRun.I \
run_p3dpython.h
#define WIN_SYS_LIBS user32.lib #define WIN_SYS_LIBS user32.lib
// If you have to link with a static Python library, define it here. // If you have to link with a static Python library, define it here.
#define EXTRA_LIBS $[EXTRA_P3DPYTHON_LIBS] #define EXTRA_LIBS $[EXTRA_P3DPYTHON_LIBS]
#end lib_target
#begin bin_target
#define BUILD_TARGET $[HAVE_PYTHON]
#define TARGET p3dpython
#define SOURCES \
fhandle.h \
p3dPythonMain.cxx \
run_p3dpython.h
#end bin_target #end bin_target
#begin static_lib_target #begin static_lib_target

View File

@ -0,0 +1,29 @@
// Filename: fhandle.h
// Created by: drose (29Aug09)
//
////////////////////////////////////////////////////////////////////
//
// 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 FHANDLE_H
#define FHANDLE_H
// This header file simply defines the FHandle type, which is used to
// pass around a handle to an open file object.
#ifdef _WIN32
#include <windows.h>
typedef HANDLE FHandle;
#else
// On POSIX, we use a file descriptor as a "handle".
typedef int FHandle;
#endif
#endif

View File

@ -15,14 +15,7 @@
#ifndef HANDLESTREAMBUF_H #ifndef HANDLESTREAMBUF_H
#define HANDLESTREAMBUF_H #define HANDLESTREAMBUF_H
#ifdef _WIN32 #include "fhandle.h"
#include <windows.h>
typedef HANDLE FHandle;
#else
// On POSIX, we use a file descriptor as a "handle".
typedef int FHandle;
#endif
#include <iostream> #include <iostream>
using namespace std; using namespace std;

View File

@ -29,28 +29,27 @@ P3DPythonRun *P3DPythonRun::_global_ptr = NULL;
// Description: // Description:
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
P3DPythonRun:: P3DPythonRun::
P3DPythonRun(int argc, char *argv[]) { P3DPythonRun(const char *program_name, const char *archive_file,
FHandle input, FHandle output) {
_read_thread_continue = false; _read_thread_continue = false;
_program_continue = true; _program_continue = true;
_session_terminated = false;
INIT_LOCK(_commands_lock); INIT_LOCK(_commands_lock);
INIT_THREAD(_read_thread); INIT_THREAD(_read_thread);
_session_id = 0; _session_id = 0;
_next_sent_id = 0; _next_sent_id = 0;
if (argc >= 1) { if (program_name != NULL) {
_program_name = argv[0]; _program_name = program_name;
} }
if (argc >= 2) { if (archive_file != NULL) {
_archive_file = Filename::from_os_specific(argv[1]); _archive_file = Filename::from_os_specific(archive_file);
}
if (_archive_file.empty()) {
nout << "No archive filename specified on command line.\n";
exit(1);
} }
_py_argc = 1; _py_argc = 1;
_py_argv = (char **)malloc(2 * sizeof(char *)); _py_argv = (char **)malloc(2 * sizeof(char *));
_py_argv[0] = (char *)_program_name.c_str(); _py_argv[0] = (char *)_program_name.c_str();
_py_argv[1] = NULL; _py_argv[1] = NULL;
@ -66,28 +65,15 @@ P3DPythonRun(int argc, char *argv[]) {
// Initialize Python. It appears to be important to do this before // Initialize Python. It appears to be important to do this before
// we open the pipe streams and spawn the thread, below. // we open the pipe streams and spawn the thread, below.
PyEval_InitThreads();
Py_SetProgramName((char *)_program_name.c_str()); Py_SetProgramName((char *)_program_name.c_str());
Py_Initialize(); Py_Initialize();
PySys_SetArgv(_py_argc, _py_argv); PySys_SetArgv(_py_argc, _py_argv);
// Open the pipe streams with the input and output handles from the // Open the pipe streams with the input and output handles from the
// parent. // parent.
#ifdef _WIN32 _pipe_read.open_read(input);
HANDLE read = GetStdHandle(STD_INPUT_HANDLE); _pipe_write.open_write(output);
HANDLE write = GetStdHandle(STD_OUTPUT_HANDLE);
if (!SetStdHandle(STD_INPUT_HANDLE, INVALID_HANDLE_VALUE)) {
nout << "unable to reset input handle\n";
}
if (!SetStdHandle(STD_OUTPUT_HANDLE, INVALID_HANDLE_VALUE)) {
nout << "unable to reset input handle\n";
}
_pipe_read.open_read(read);
_pipe_write.open_write(write);
#else
_pipe_read.open_read(STDIN_FILENO);
_pipe_write.open_write(STDOUT_FILENO);
#endif // _WIN32
if (!_pipe_read) { if (!_pipe_read) {
nout << "unable to open read pipe\n"; nout << "unable to open read pipe\n";
@ -125,7 +111,6 @@ run_python() {
// "_d", so that the Panda DLL preloader can import the correct // "_d", so that the Panda DLL preloader can import the correct
// filenames. // filenames.
PyRun_SimpleString("import sys; sys.dll_suffix = '_d'"); PyRun_SimpleString("import sys; sys.dll_suffix = '_d'");
#endif #endif
// We'll need libpandaexpress to be imported before we can load // We'll need libpandaexpress to be imported before we can load
@ -696,6 +681,9 @@ check_comm() {
_commands.pop_front(); _commands.pop_front();
RELEASE_LOCK(_commands_lock); RELEASE_LOCK(_commands_lock);
handle_command(doc); handle_command(doc);
if (_session_terminated) {
return;
}
ACQUIRE_LOCK(_commands_lock); ACQUIRE_LOCK(_commands_lock);
} }
RELEASE_LOCK(_commands_lock); RELEASE_LOCK(_commands_lock);
@ -704,6 +692,7 @@ check_comm() {
// The low-level thread detected an error, for instance pipe // The low-level thread detected an error, for instance pipe
// closed. We should exit gracefully. // closed. We should exit gracefully.
terminate_session(); terminate_session();
return;
} }
// Sleep to yield the timeslice, but only if we're not running in // Sleep to yield the timeslice, but only if we're not running in
@ -758,6 +747,9 @@ wait_script_response(int response_id) {
_commands.erase(ci); _commands.erase(ci);
RELEASE_LOCK(_commands_lock); RELEASE_LOCK(_commands_lock);
handle_command(doc); handle_command(doc);
if (_session_terminated) {
return NULL;
}
ACQUIRE_LOCK(_commands_lock); ACQUIRE_LOCK(_commands_lock);
break; break;
} }
@ -791,6 +783,7 @@ wait_script_response(int response_id) {
if (!_program_continue) { if (!_program_continue) {
terminate_session(); terminate_session();
return NULL;
} }
#ifdef _WIN32 #ifdef _WIN32
@ -839,6 +832,9 @@ py_request_func(PyObject *args) {
} }
TiXmlDocument *doc = wait_script_response(response_id); TiXmlDocument *doc = wait_script_response(response_id);
if (_session_terminated) {
return Py_BuildValue("");
}
assert(doc != NULL); assert(doc != NULL);
TiXmlElement *xcommand = doc->FirstChildElement("command"); TiXmlElement *xcommand = doc->FirstChildElement("command");
assert(xcommand != NULL); assert(xcommand != NULL);
@ -1230,10 +1226,7 @@ terminate_session() {
} }
Py_DECREF(result); Py_DECREF(result);
// The task manager is cleaned up. Let's exit immediately here, _session_terminated = true;
// rather than returning all the way up. This just makes it easier
// when we call terminate_session() from a deeply-nested loop.
exit(0);
} }
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
@ -1561,16 +1554,18 @@ rt_thread_run() {
} }
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
// Function: main // Function: run_p3dpython
// Description: Starts the program running. // Description: This externally-visible function is the main entry
// point to this DLL, and it starts the whole thing
// running. Returns true on success, false on failure.
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
int bool
main(int argc, char *argv[]) { run_p3dpython(const char *program_name, const char *archive_file,
P3DPythonRun::_global_ptr = new P3DPythonRun(argc, argv); FHandle input, FHandle output) {
P3DPythonRun::_global_ptr =
if (!P3DPythonRun::_global_ptr->run_python()) { new P3DPythonRun(program_name, archive_file, input, output);
nout << "Couldn't initialize Python.\n"; bool result = P3DPythonRun::_global_ptr->run_python();
return 1; delete P3DPythonRun::_global_ptr;
} P3DPythonRun::_global_ptr = NULL;
return 0; return result;
} }

View File

@ -22,6 +22,7 @@
#include <windows.h> #include <windows.h>
#endif #endif
#include "run_p3dpython.h"
#include "p3d_lock.h" #include "p3d_lock.h"
#include "handleStream.h" #include "handleStream.h"
#include "p3dCInstance.h" #include "p3dCInstance.h"
@ -65,7 +66,8 @@ using namespace std;
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
class P3DPythonRun { class P3DPythonRun {
public: public:
P3DPythonRun(int argc, char *argv[]); P3DPythonRun(const char *program_name, const char *archive_file,
FHandle input, FHandle output);
~P3DPythonRun(); ~P3DPythonRun();
bool run_python(); bool run_python();
@ -153,6 +155,7 @@ private:
bool _read_thread_continue; bool _read_thread_continue;
bool _program_continue; bool _program_continue;
bool _session_terminated;
THREAD _read_thread; THREAD _read_thread;
public: public:

View File

@ -26,6 +26,7 @@
#include "p3dConcreteStruct.h" #include "p3dConcreteStruct.h"
#include "binaryXml.h" #include "binaryXml.h"
#include "mkdir_complete.h" #include "mkdir_complete.h"
#include "run_p3dpython.h"
#include <ctype.h> #include <ctype.h>
@ -34,6 +35,7 @@
#include <sys/wait.h> #include <sys/wait.h>
#include <sys/select.h> #include <sys/select.h>
#include <signal.h> #include <signal.h>
#include <dlfcn.h>
#endif #endif
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
@ -53,6 +55,7 @@ P3DSession(P3DInstance *inst) {
_python_version = inst->get_python_version(); _python_version = inst->get_python_version();
_start_dir = inst_mgr->get_root_dir() + "/start"; _start_dir = inst_mgr->get_root_dir() + "/start";
_p3dpython_one_process = false;
_p3dpython_started = false; _p3dpython_started = false;
_p3dpython_running = false; _p3dpython_running = false;
@ -100,67 +103,86 @@ shutdown() {
static const int max_wait_ms = 2000; static const int max_wait_ms = 2000;
if (_p3dpython_one_process) {
// Since it's running in a thread, we can't reliably force-kill
// it. So, just wait.
nout << "Waiting for Python thread to exit\n";
JOIN_THREAD(_p3dpython_thread);
nout << "Done waiting.\n";
_p3dpython_one_process = false;
} else {
// Python's running in a sub-process, the preferred way. In
// this case, we can wait a brief amount of time before it
// closes itself; but if it doesn't, we can safely force-kill
// it.
#ifdef _WIN32 #ifdef _WIN32
// Now give the process a chance to terminate itself cleanly. // Wait for a certain amount of time for the process to stop by
if (WaitForSingleObject(_p3dpython_handle, max_wait_ms) == WAIT_TIMEOUT) { // itself.
// It didn't shut down cleanly, so kill it the hard way. if (WaitForSingleObject(_p3dpython_handle, max_wait_ms) == WAIT_TIMEOUT) {
nout << "Force-killing python process.\n"; // It didn't shut down cleanly, so kill it the hard way.
TerminateProcess(_p3dpython_handle, 2); nout << "Force-killing python process.\n";
} TerminateProcess(_p3dpython_handle, 2);
CloseHandle(_p3dpython_handle);
#else // _WIN32
// Wait for a certain amount of time for the process to stop by
// itself.
struct timeval start;
gettimeofday(&start, NULL);
int start_ms = start.tv_sec * 1000 + start.tv_usec / 1000;
int status;
pid_t result = waitpid(_p3dpython_pid, &status, WNOHANG);
while (result != _p3dpython_pid) {
if (result == -1) {
perror("waitpid");
break;
}
struct timeval now;
gettimeofday(&now, NULL);
int now_ms = now.tv_sec * 1000 + now.tv_usec / 1000;
int elapsed = now_ms - start_ms;
if (elapsed > max_wait_ms) {
// Tired of waiting. Kill the process.
nout << "Force-killing python process, pid " << _p3dpython_pid
<< "\n";
kill(_p3dpython_pid, SIGKILL);
start_ms = now_ms;
} }
// Yield the timeslice and wait some more. CloseHandle(_p3dpython_handle);
struct timeval tv;
tv.tv_sec = 0;
tv.tv_usec = 1;
select(0, NULL, NULL, NULL, &tv);
result = waitpid(_p3dpython_pid, &status, WNOHANG);
}
nout << "Python process has successfully stopped.\n";
if (WIFEXITED(status)) {
nout << " exited normally, status = "
<< WEXITSTATUS(status) << "\n";
} else if (WIFSIGNALED(status)) {
nout << " signalled by " << WTERMSIG(status) << ", core = "
<< WCOREDUMP(status) << "\n";
} else if (WIFSTOPPED(status)) {
nout << " stopped by " << WSTOPSIG(status) << "\n";
}
#else // _WIN32
// Wait for a certain amount of time for the process to stop by
// itself.
struct timeval start;
gettimeofday(&start, NULL);
int start_ms = start.tv_sec * 1000 + start.tv_usec / 1000;
int status;
pid_t result = waitpid(_p3dpython_pid, &status, WNOHANG);
while (result != _p3dpython_pid) {
if (result == -1) {
perror("waitpid");
break;
}
struct timeval now;
gettimeofday(&now, NULL);
int now_ms = now.tv_sec * 1000 + now.tv_usec / 1000;
int elapsed = now_ms - start_ms;
if (elapsed > max_wait_ms) {
// Tired of waiting. Kill the process.
nout << "Force-killing python process, pid " << _p3dpython_pid
<< "\n";
kill(_p3dpython_pid, SIGKILL);
start_ms = now_ms;
}
// Yield the timeslice and wait some more.
struct timeval tv;
tv.tv_sec = 0;
tv.tv_usec = 1;
select(0, NULL, NULL, NULL, &tv);
result = waitpid(_p3dpython_pid, &status, WNOHANG);
}
nout << "Python process has successfully stopped.\n";
if (WIFEXITED(status)) {
nout << " exited normally, status = "
<< WEXITSTATUS(status) << "\n";
} else if (WIFSIGNALED(status)) {
nout << " signalled by " << WTERMSIG(status) << ", core = "
<< WCOREDUMP(status) << "\n";
} else if (WIFSTOPPED(status)) {
nout << " stopped by " << WSTOPSIG(status) << "\n";
}
#endif // _WIN32 #endif // _WIN32
}
_p3dpython_running = false; _p3dpython_running = false;
_p3dpython_started = false; _p3dpython_started = false;
// Close the pipe now.
_pipe_read.close();
} }
// If there are any leftover commands in the queue (presumably // If there are any leftover commands in the queue (presumably
@ -657,10 +679,9 @@ start_p3dpython(P3DInstance *inst) {
// Change the current directory to the standard start directory, but // Change the current directory to the standard start directory, but
// only if the runtime environment told us the original current // only if the runtime environment told us the original current
// directory isn't meaningful. // directory isn't meaningful.
string start_dir; _use_start_dir = !inst_mgr->get_keep_cwd();
if (!inst_mgr->get_keep_cwd()) { if (_use_start_dir) {
start_dir = _start_dir; mkdir_complete(_start_dir, nout);
mkdir_complete(start_dir, nout);
} }
#ifdef _WIN32 #ifdef _WIN32
@ -725,16 +746,30 @@ start_p3dpython(P3DInstance *inst) {
<< "PRC_PATH set to: " << prc_path << "\n"; << "PRC_PATH set to: " << prc_path << "\n";
} }
string p3dpython = P3D_PLUGIN_P3DPYTHON; // Get the name of the executable and dynamic library to run.
if (p3dpython.empty()) { // Ideally, we'll run the executable successfully, in a sub-process;
p3dpython = _python_root_dir + "/p3dpython"; // this will in turn load and run the dynamic library. If that
// fails for some reason, we can fall back to loading and running
// the library directly.
_p3dpython_exe = _python_root_dir + "/p3dpython";
#ifdef _WIN32 #ifdef _WIN32
p3dpython += ".exe"; _p3dpython_exe += ".exe";
#endif
_p3dpython_dll = P3D_PLUGIN_P3DPYTHON;
if (_p3dpython_dll.empty()) {
_p3dpython_dll = _python_root_dir + "/libp3dpython";
#ifdef _WIN32
_p3dpython_dll += ".dll";
#elif defined(__APPLE__)
_p3dpython_dll += ".dylib";
#else
_p3dpython_dll += ".so";
#endif #endif
} }
// Populate the new process' environment. // Populate the new process' environment.
string env; _env = string();
// These are the enviroment variables we forward from the current // These are the enviroment variables we forward from the current
// environment, if they are set. // environment, if they are set.
@ -748,41 +783,41 @@ start_p3dpython(P3DInstance *inst) {
for (int ki = 0; keep[ki] != NULL; ++ki) { for (int ki = 0; keep[ki] != NULL; ++ki) {
char *value = getenv(keep[ki]); char *value = getenv(keep[ki]);
if (value != NULL) { if (value != NULL) {
env += keep[ki]; _env += keep[ki];
env += "="; _env += "=";
env += value; _env += value;
env += '\0'; _env += '\0';
} }
} }
// Define some new environment variables. // Define some new environment variables.
env += "PATH="; _env += "PATH=";
env += search_path; _env += search_path;
env += '\0'; _env += '\0';
env += "LD_LIBRARY_PATH="; _env += "LD_LIBRARY_PATH=";
env += search_path; _env += search_path;
env += '\0'; _env += '\0';
env += "DYLD_LIBRARY_PATH="; _env += "DYLD_LIBRARY_PATH=";
env += search_path; _env += search_path;
env += '\0'; _env += '\0';
env += "PYTHONPATH="; _env += "PYTHONPATH=";
env += python_path; _env += python_path;
env += '\0'; _env += '\0';
env += "PYTHONHOME="; _env += "PYTHONHOME=";
env += _python_root_dir; _env += _python_root_dir;
env += '\0'; _env += '\0';
env += "PRC_PATH="; _env += "PRC_PATH=";
env += prc_path; _env += prc_path;
env += '\0'; _env += '\0';
env += "PANDA_PRC_PATH="; _env += "PANDA_PRC_PATH=";
env += prc_path; _env += prc_path;
env += '\0'; _env += '\0';
// Define each package's root directory in an environment variable // Define each package's root directory in an environment variable
// named after the package, for the convenience of the packages in // named after the package, for the convenience of the packages in
@ -793,11 +828,11 @@ start_p3dpython(P3DInstance *inst) {
for (string::const_iterator si = package_name.begin(); for (string::const_iterator si = package_name.begin();
si != package_name.end(); si != package_name.end();
++si) { ++si) {
env += toupper(*si); _env += toupper(*si);
} }
env += string("_ROOT="); _env += string("_ROOT=");
env += package->get_package_dir(); _env += package->get_package_dir();
env += '\0'; _env += '\0';
} }
// Get the log filename from the p3d_info.xml file. // Get the log filename from the p3d_info.xml file.
@ -809,6 +844,7 @@ start_p3dpython(P3DInstance *inst) {
} }
bool console_output = (inst->get_fparams().lookup_token_int("console_output") != 0); bool console_output = (inst->get_fparams().lookup_token_int("console_output") != 0);
bool one_process = (inst->get_fparams().lookup_token_int("one_process") != 0);
#ifdef P3D_PLUGIN_LOG_BASENAME3 #ifdef P3D_PLUGIN_LOG_BASENAME3
if (log_basename.empty()) { if (log_basename.empty()) {
@ -840,24 +876,82 @@ start_p3dpython(P3DInstance *inst) {
_log_pathname += ".log"; _log_pathname += ".log";
} }
string archive_file = inst->_panda3d->get_archive_file_pathname(); // Create the pipes for communication.
nout << "Attempting to start python from " << p3dpython << "\n";
#ifdef _WIN32 #ifdef _WIN32
_p3dpython_handle = win_create_process // Create a bi-directional pipe to communicate with the sub-process.
(p3dpython, archive_file, start_dir, env, _log_pathname, HANDLE r_to, w_to, r_from, w_from;
_pipe_read, _pipe_write);
bool started_p3dpython = (_p3dpython_handle != INVALID_HANDLE_VALUE); // Create the pipe to the process.
if (!CreatePipe(&r_to, &w_to, NULL, 0)) {
nout << "failed to create pipe\n";
} else {
// 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, NULL, 0)) {
nout << "failed to create pipe\n";
} 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);
}
_output = w_from;
_input = r_to;
_pipe_read.open_read(r_from);
_pipe_write.open_write(w_to);
#else #else
_p3dpython_pid = posix_create_process // Create a bi-directional pipe to communicate with the sub-process.
(p3dpython, archive_file, start_dir, env, _log_pathname, int to_fd[2];
_pipe_read, _pipe_write); if (pipe(to_fd) < 0) {
bool started_p3dpython = (_p3dpython_pid > 0); perror("failed to create pipe");
}
int from_fd[2];
if (pipe(from_fd) < 0) {
perror("failed to create pipe");
}
_input = to_fd[0];
_output = from_fd[1];
_pipe_read.open_read(from_fd[0]);
_pipe_write.open_write(to_fd[1]);
#endif // _WIN32
// Get the filename to the Panda3D multifile. We need to pass this
// to p3dpython.
_mf_filename = inst->_panda3d->get_archive_file_pathname();
bool started_p3dpython;
if (one_process) {
nout << "one_process is set; running Python within parent process.\n";
started_p3dpython = false;
} else {
nout << "Attempting to start python from " << _p3dpython_exe
<< " and " << _p3dpython_dll << "\n";
#ifdef _WIN32
_p3dpython_handle = win_create_process();
started_p3dpython = (_p3dpython_handle != INVALID_HANDLE_VALUE);
#else
_p3dpython_pid = posix_create_process();
started_p3dpython = (_p3dpython_pid > 0);
#endif #endif
if (!started_p3dpython) {
nout << "Failed to create process.\n";
}
}
if (!started_p3dpython) { if (!started_p3dpython) {
nout << "Failed to create process.\n"; // Well, we couldn't run python in a sub-process, for some reason.
return; // Fall back to running it in a sub-thread within the same
// process. This isn't nearly as good, but I guess it's better
// than nothing.
INIT_THREAD(_p3dpython_thread);
SPAWN_THREAD(_p3dpython_thread, p3dpython_thread_run, this);
_p3dpython_one_process = true;
} }
_p3dpython_started = true; _p3dpython_started = true;
_p3dpython_running = true; _p3dpython_running = true;
@ -1019,60 +1113,37 @@ rt_terminate() {
#ifdef _WIN32 #ifdef _WIN32
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
// Function: P3DSession::win_create_process // Function: P3DSession::win_create_process
// Access: Private, Static // Access: Private
// Description: Creates a sub-process to run the named program // Description: Creates a sub-process to run _p3dpython_exe, with
// executable, with the indicated environment string. // the appropriate command-line arguments, and the
// Standard error is logged to log_pathname, if that // environment string defined in _env. Standard error
// string is nonempty. // is logged to _log_pathname, if that string is
// nonempty.
// //
// Opens the two HandleStreams as the read and write // Opens the two HandleStreams _pipe_read and
// pipes to the child process's standard output and // _pipe_write as the read and write pipes to the child
// standard input, respectively. // process's standard output and standard input,
// respectively.
// //
// Returns the handle to the created process on success, // Returns the handle to the created process on success,
// or INVALID_HANDLE_VALUE on falure. // or INVALID_HANDLE_VALUE on falure.
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
HANDLE P3DSession:: HANDLE P3DSession::
win_create_process(const string &program, const string &archive_file, win_create_process() {
const string &start_dir,
const string &env, const string &log_pathname,
HandleStream &pipe_read, HandleStream &pipe_write) {
// Create a bi-directional pipe to communicate with the sub-process.
HANDLE r_to, w_to, r_from, w_from;
// Create the pipe to the process.
if (!CreatePipe(&r_to, &w_to, NULL, 0)) {
nout << "failed to create pipe\n";
} else {
// 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, NULL, 0)) {
nout << "failed to create pipe\n";
} 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);
}
HANDLE error_handle = GetStdHandle(STD_ERROR_HANDLE); HANDLE error_handle = GetStdHandle(STD_ERROR_HANDLE);
bool got_log_pathname = !log_pathname.empty(); bool got_log_pathname = !_log_pathname.empty();
if (got_log_pathname) { if (got_log_pathname) {
// Open the named file for output and redirect the child's stderr // Open the named file for output and redirect the child's stderr
// into it. // into it.
HANDLE handle = CreateFile HANDLE handle = CreateFile
(log_pathname.c_str(), GENERIC_WRITE, (_log_pathname.c_str(), GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
NULL, CREATE_ALWAYS, 0, NULL); NULL, CREATE_ALWAYS, 0, NULL);
if (handle != INVALID_HANDLE_VALUE) { if (handle != INVALID_HANDLE_VALUE) {
error_handle = handle; error_handle = handle;
SetHandleInformation(error_handle, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT); SetHandleInformation(error_handle, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT);
} else { } else {
nout << "Unable to open " << log_pathname << "\n"; nout << "Unable to open " << _log_pathname << "\n";
} }
} }
@ -1085,8 +1156,8 @@ win_create_process(const string &program, const string &archive_file,
ZeroMemory(&startup_info, sizeof(STARTUPINFO)); ZeroMemory(&startup_info, sizeof(STARTUPINFO));
startup_info.cb = sizeof(startup_info); startup_info.cb = sizeof(startup_info);
startup_info.hStdError = error_handle; startup_info.hStdError = error_handle;
startup_info.hStdOutput = w_from; startup_info.hStdOutput = _output;
startup_info.hStdInput = r_to; startup_info.hStdInput = _input;
startup_info.dwFlags |= STARTF_USESTDHANDLES; startup_info.dwFlags |= STARTF_USESTDHANDLES;
// Make sure the "python" console window is hidden. // Make sure the "python" console window is hidden.
@ -1096,15 +1167,18 @@ win_create_process(const string &program, const string &archive_file,
// If the start directory is empty, meaning not to change the // If the start directory is empty, meaning not to change the
// current directory, then pass NULL in to CreateProcess(). // current directory, then pass NULL in to CreateProcess().
const char *start_dir_cstr = NULL; const char *start_dir_cstr = NULL;
if (!start_dir.empty()) { if (_use_start_dir) {
start_dir_cstr = start_dir.c_str(); start_dir_cstr = _start_dir.c_str();
} }
// Construct the command-line string, containing the quoted
// command-line arguments.
ostringstream stream; ostringstream stream;
stream << "\"" << program << "\" \"" << archive_file << "\""; stream << "\"" << _p3dpython_exe << "\" \"" << _p3dpython_dll
<< "\" \"" << _mf_filename << "\"";
// I'm not sure why CreateProcess wants a non-const char pointer for // I'm not sure why CreateProcess wants a non-const char pointer for
// the command-line argument, but I'm not taking chances. It gets a // its command-line string, but I'm not taking chances. It gets a
// non-const char array that it can modify. // non-const char array that it can modify.
string command_line_str = stream.str(); string command_line_str = stream.str();
char *command_line = new char[command_line_str.size() + 1]; char *command_line = new char[command_line_str.size() + 1];
@ -1112,29 +1186,26 @@ win_create_process(const string &program, const string &archive_file,
PROCESS_INFORMATION process_info; PROCESS_INFORMATION process_info;
BOOL result = CreateProcess BOOL result = CreateProcess
(program.c_str(), command_line, NULL, NULL, TRUE, 0, (_p3dpython_exe.c_str(), command_line, NULL, NULL, TRUE, 0,
(void *)env.c_str(), start_dir_cstr, (void *)_env.c_str(), _start_dir_cstr,
&startup_info, &process_info); &startup_info, &process_info);
bool started_program = (result != 0); bool started_program = (result != 0);
delete[] command_line; delete[] command_line;
// Close the pipe handles that are now owned by the child. // Close the pipe handles that are now owned by the child.
CloseHandle(w_from); CloseHandle(_output);
CloseHandle(r_to); CloseHandle(_input);
if (got_log_pathname) { if (got_log_pathname) {
CloseHandle(error_handle); CloseHandle(error_handle);
} }
if (!started_program) { if (!started_program) {
CloseHandle(r_from); _pipe_read.close();
CloseHandle(w_to); _pipe_write.close();
return INVALID_HANDLE_VALUE; return INVALID_HANDLE_VALUE;
} }
pipe_read.open_read(r_from);
pipe_write.open_write(w_to);
CloseHandle(process_info.hThread); CloseHandle(process_info.hThread);
return process_info.hProcess; return process_info.hProcess;
} }
@ -1144,54 +1215,40 @@ win_create_process(const string &program, const string &archive_file,
#ifndef _WIN32 #ifndef _WIN32
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
// Function: P3DSession::posix_create_process // Function: P3DSession::posix_create_process
// Access: Private, Static // Access: Private
// Description: Creates a sub-process to run the named program // Description: Creates a sub-process to run _p3dpython_exe, with
// executable, with the indicated environment string. // the appropriate command-line arguments, and the
// environment string defined in _env. Standard error
// is logged to _log_pathname, if that string is
// nonempty.
// //
// Opens the two HandleStreams as the read and write // Opens the two HandleStreams _pipe_read and
// pipes to the child process's standard output and // _pipe_write as the read and write pipes to the child
// standard input, respectively. Returns true on // process's standard output and standard input,
// success, false on failure. // respectively.
// //
// Returns the pid of the created process on success, or // Returns the handle to the created process on success,
// -1 on falure. // or INVALID_HANDLE_VALUE on falure.
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
int P3DSession:: int P3DSession::
posix_create_process(const string &program, const string &archive_file, posix_create_process() {
const string &start_dir,
const string &env, const string &log_pathname,
HandleStream &pipe_read, HandleStream &pipe_write) {
// Create a bi-directional pipe to communicate with the sub-process.
int to_fd[2];
if (pipe(to_fd) < 0) {
perror("failed to create pipe");
}
int from_fd[2];
if (pipe(from_fd) < 0) {
perror("failed to create pipe");
}
// Fork and exec. // Fork and exec.
pid_t child = fork(); pid_t child = fork();
if (child < 0) { if (child < 0) {
close(to_fd[0]);
close(to_fd[1]);
close(from_fd[0]);
close(from_fd[1]);
perror("fork"); perror("fork");
return -1; return -1;
} }
if (child == 0) { if (child == 0) {
// Here we are in the child process. // Here we are in the child process.
bool got_log_pathname = !log_pathname.empty(); bool got_log_pathname = !_log_pathname.empty();
if (got_log_pathname) { if (got_log_pathname) {
// Open the named file for output and redirect the child's stderr // Open the named file for output and redirect the child's stderr
// into it. // into it.
int logfile_fd = open(log_pathname.c_str(), int logfile_fd = open(_log_pathname.c_str(),
O_WRONLY | O_CREAT | O_TRUNC, 0666); O_WRONLY | O_CREAT | O_TRUNC, 0666);
if (logfile_fd < 0) { if (logfile_fd < 0) {
nout << "Unable to open " << log_pathname << "\n"; nout << "Unable to open " << _log_pathname << "\n";
} else { } else {
dup2(logfile_fd, STDERR_FILENO); dup2(logfile_fd, STDERR_FILENO);
close(logfile_fd); close(logfile_fd);
@ -1200,41 +1257,108 @@ posix_create_process(const string &program, const string &archive_file,
// Set the appropriate ends of the bi-directional pipe as the // Set the appropriate ends of the bi-directional pipe as the
// standard input and standard output of the child process. // standard input and standard output of the child process.
dup2(to_fd[0], STDIN_FILENO); dup2(_input, STDIN_FILENO);
dup2(from_fd[1], STDOUT_FILENO); dup2(_output, STDOUT_FILENO);
close(to_fd[1]); _pipe_read.close();
close(from_fd[0]); _pipe_write.close();
if (!start_dir.empty()) { if (_use_start_dir) {
if (chdir(start_dir.c_str()) < 0) { if (chdir(_start_dir.c_str()) < 0) {
nout << "Could not chdir to " << start_dir << "\n"; nout << "Could not chdir to " << _start_dir << "\n";
_exit(1); // This is a warning, not an error. We don't actually care
// that much about the starting directory.
} }
} }
// build up an array of char strings for the environment. // build up an array of char strings for the environment.
vector<const char *> ptrs; vector<const char *> ptrs;
size_t p = 0; size_t p = 0;
size_t zero = env.find('\0', p); size_t zero = _env.find('\0', p);
while (zero != string::npos) { while (zero != string::npos) {
ptrs.push_back(env.data() + p); ptrs.push_back(_env.data() + p);
p = zero + 1; p = zero + 1;
zero = env.find('\0', p); zero = _env.find('\0', p);
} }
ptrs.push_back((char *)NULL); ptrs.push_back((char *)NULL);
execle(program.c_str(), execle(_p3dpython_exe.c_str(),
program.c_str(), archive_file.c_str(), (char *)0, _p3dpython_exe.c_str(), _p3dpython_dll.c_str(), _mf_filename.c_str(), (char *)0,
&ptrs[0]); &ptrs[0]);
nout << "Failed to exec " << program << "\n"; nout << "Failed to exec " << _p3dpython_exe << "\n";
_exit(1); _exit(1);
} }
pipe_read.open_read(from_fd[0]); close(_input);
pipe_write.open_write(to_fd[1]); close(_output);
close(to_fd[0]);
close(from_fd[1]);
return child; return child;
} }
#endif // _WIN32 #endif // _WIN32
////////////////////////////////////////////////////////////////////
// Function: P3DSession::p3dpython_thread_run
// Access: Private
// Description: This method is called in a sub-thread to fire up
// p3dpython within this same process, but only if the
// above attempt to create a sub-process failed.
////////////////////////////////////////////////////////////////////
void P3DSession::
p3dpython_thread_run() {
nout << "running p3dpython_thread_run()\n";
// Set the environment. Hopefully this won't be too destructive to
// the current process, and hopefully these changes will be read
// properly by Python.
size_t p = 0;
size_t zero = _env.find('\0', p);
while (zero != string::npos) {
const char *start = _env.data() + p;
const char *equals = strchr(start, '=');
if (equals != NULL) {
string variable(start, equals - start);
#ifdef _WIN32
_putenv_s(variable.c_str(), equals + 1);
#else
setenv(variable.c_str(), equals + 1, true);
#endif // _WIN32
}
p = zero + 1;
zero = _env.find('\0', p);
}
// Now load the library.
#ifdef _WIN32
SetErrorMode(0);
HMODULE module = LoadLibrary(_p3dpython_dll.c_str());
if (module == NULL) {
// Couldn't load the DLL.
nout << "Couldn't load " << _p3dpython_dll << "\n";
return;
}
#define get_func GetProcAddress
#else // _WIN32
// Posix case.
void *module = dlopen(_p3dpython_dll.c_str(), RTLD_GLOBAL);
if (module == NULL) {
// Couldn't load the .so.
nout << "Couldn't load " << _p3dpython_dll << "\n";
return;
}
#define get_func dlsym
#endif // _WIN32
run_p3dpython_func *run_p3dpython = (run_p3dpython_func *)get_func(module, "run_p3dpython");
if (run_p3dpython == NULL) {
nout << "Couldn't find run_p3dpython\n";
return;
}
if (!run_p3dpython(_p3dpython_dll.c_str(), _mf_filename.c_str(),
_input, _output)) {
nout << "Failure on startup.\n";
}
}

View File

@ -75,19 +75,16 @@ private:
void rt_handle_request(TiXmlDocument *doc); void rt_handle_request(TiXmlDocument *doc);
#ifdef _WIN32 #ifdef _WIN32
static HANDLE HANDLE win_create_process();
win_create_process(const string &program, const string &archive_file,
const string &start_dir,
const string &env, const string &output_filename,
HandleStream &pipe_read, HandleStream &pipe_write);
#else #else
static int int posix_create_process();
posix_create_process(const string &program, const string &archive_file,
const string &start_dir,
const string &env, const string &output_filename,
HandleStream &pipe_read, HandleStream &pipe_write);
#endif #endif
// In case we can't get a separate process, we'll run p3dpython in a
// sub-thread.
THREAD_CALLBACK_DECLARATION(P3DSession, p3dpython_thread_run);
void p3dpython_thread_run();
private: private:
int _session_id; int _session_id;
string _session_key; string _session_key;
@ -95,6 +92,15 @@ private:
string _log_pathname; string _log_pathname;
string _python_root_dir; string _python_root_dir;
string _start_dir; string _start_dir;
bool _use_start_dir;
// This information is passed to create_process(), or to
// p3dpython_thread_run().
string _p3dpython_exe;
string _p3dpython_dll;
string _mf_filename;
string _env;
FHandle _input, _output;
typedef map<int, P3DInstance *> Instances; typedef map<int, P3DInstance *> Instances;
Instances _instances; Instances _instances;
@ -114,12 +120,21 @@ private:
P3DPackage *_panda3d; P3DPackage *_panda3d;
// Members for communicating with the p3dpython child process. // If this is true, then CreateProcess() or fork() failed (or we had
// one_process set true in the tokens), and we're forced to run
// p3dpython in a sub-thread within the same process, rather than in
// a separate process. This means we can't have multiple sessions
// running simultaneously, because Python don't play that way.
bool _p3dpython_one_process;
// Members for communicating with the p3dpython child process (or
// thread, as the case may be).
#ifdef _WIN32 #ifdef _WIN32
HANDLE _p3dpython_handle; HANDLE _p3dpython_handle;
#else #else
int _p3dpython_pid; int _p3dpython_pid;
#endif #endif
THREAD _p3dpython_thread;
bool _p3dpython_started; bool _p3dpython_started;
bool _p3dpython_running; bool _p3dpython_running;

View File

@ -0,0 +1,37 @@
// Filename: run_p3dpython.h
// Created by: drose (29Aug09)
//
////////////////////////////////////////////////////////////////////
//
// 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 RUN_P3DPYTHON_H
#define RUN_P3DPYTHON_H
// This header file defines the prototype for run_p3dpython(), the
// main entry point to this DLL.
#include "fhandle.h"
#ifdef _WIN32
#define EXPCL_P3DPYTHON __declspec(dllexport)
#else
#define EXPCL_P3DPYTHON
#endif
typedef bool run_p3dpython_func(const char *program_name, const char *archive_file,
FHandle input, FHandle output);
EXPCL_P3DPYTHON extern "C" bool
run_p3dpython(const char *program_name, const char *archive_file,
FHandle input, FHandle output);
#endif

View File

@ -12,7 +12,7 @@
prc:c dtoolutil:c dtoolbase:c dtool:m \ prc:c dtoolutil:c dtoolbase:c dtool:m \
interrogatedb:c dconfig:c dtoolconfig:m \ interrogatedb:c dconfig:c dtoolconfig:m \
express:c downloader:c pandaexpress:m \ express:c downloader:c pandaexpress:m \
pystub $[if $[WINDOWS_PLATFORM],pystub,]
#define OSX_SYS_FRAMEWORKS Foundation AppKit Carbon #define OSX_SYS_FRAMEWORKS Foundation AppKit Carbon