exit gracefully

This commit is contained in:
David Rose 2009-06-09 01:56:03 +00:00
parent 8c6229ff7f
commit 633eae3ba3
10 changed files with 299 additions and 33 deletions

View File

@ -0,0 +1,36 @@
// Filename: p3dCInstance.I
// Created by: drose (08Jun09)
//
////////////////////////////////////////////////////////////////////
//
// 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: P3DCInstance::get_p3d_filename
// Access: Public
// Description: Returns the p3d filename that was passed to the
// constructor.
////////////////////////////////////////////////////////////////////
inline const string &P3DCInstance::
get_p3d_filename() const {
return _p3d_filename;
}
////////////////////////////////////////////////////////////////////
// Function: P3DCInstance::get_instance_id
// Access: Public
// Description: Returns a unique integer for each instance in the
// system.
////////////////////////////////////////////////////////////////////
inline int P3DCInstance::
get_instance_id() const {
return _instance_id;
}

View File

@ -0,0 +1,70 @@
// Filename: p3dCInstance.cxx
// Created by: drose (08Jun09)
//
////////////////////////////////////////////////////////////////////
//
// 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 "p3dCInstance.h"
////////////////////////////////////////////////////////////////////
// Function: P3DCInstance::Constructor
// Access: Public
// Description: Constructs a new Instance from an XML description.
////////////////////////////////////////////////////////////////////
P3DCInstance::
P3DCInstance(TiXmlElement *xinstance) :
_func(NULL),
_window_type(P3D_WT_toplevel),
_win_x(0), _win_y(0),
_win_width(0), _win_height(0)
{
xinstance->Attribute("id", &_instance_id);
const char *p3d_filename = xinstance->Attribute("p3d_filename");
if (p3d_filename != NULL) {
_p3d_filename = p3d_filename;
}
const char *window_type = xinstance->Attribute("window_type");
if (window_type != NULL) {
if (strcmp(window_type, "embedded") == 0) {
_window_type = P3D_WT_embedded;
} else if (strcmp(window_type, "toplevel") == 0) {
_window_type = P3D_WT_toplevel;
} else if (strcmp(window_type, "fullscreen") == 0) {
_window_type = P3D_WT_fullscreen;
} else if (strcmp(window_type, "hidden") == 0) {
_window_type = P3D_WT_hidden;
}
}
xinstance->Attribute("win_x", &_win_x);
xinstance->Attribute("win_y", &_win_y);
xinstance->Attribute("win_width", &_win_width);
xinstance->Attribute("win_height", &_win_height);
#ifdef _WIN32
int hwnd;
if (xinstance->Attribute("parent_hwnd", &hwnd)) {
_parent_window._hwnd = (HWND)hwnd;
}
#endif
}
////////////////////////////////////////////////////////////////////
// Function: P3DCInstance::Destructor
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
P3DCInstance::
~P3DCInstance() {
}

View File

@ -0,0 +1,62 @@
// Filename: p3dCInstance.h
// Created by: drose (08Jun09)
//
////////////////////////////////////////////////////////////////////
//
// 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 P3DCINSTANCE_H
#define P3DCINSTANCE_H
#include "p3d_plugin.h"
#include <vector>
#include <tinyxml.h>
class P3DSession;
////////////////////////////////////////////////////////////////////
// Class : P3DCInstance
// Description : This is an instance of a Panda3D window, as seen in
// the parent-level process.
////////////////////////////////////////////////////////////////////
class P3DCInstance : public P3D_instance {
public:
P3DCInstance(TiXmlElement *xinstance);
~P3DCInstance();
inline const string &get_p3d_filename() const;
inline int get_instance_id() const;
private:
class Token {
public:
string _keyword;
string _value;
};
typedef vector<Token> Tokens;
P3D_request_ready_func *_func;
string _p3d_filename;
P3D_window_type _window_type;
int _win_x, _win_y;
int _win_width, _win_height;
P3D_window_handle _parent_window;
Tokens _tokens;
int _instance_id;
friend class P3DPythonRun;
};
#include "p3dCInstance.I"
#endif

View File

@ -24,6 +24,17 @@ get_p3d_filename() const {
return _p3d_filename;
}
////////////////////////////////////////////////////////////////////
// Function: P3DInstance::get_instance_id
// Access: Public
// Description: Returns a unique integer for each instance in the
// system.
////////////////////////////////////////////////////////////////////
inline int P3DInstance::
get_instance_id() const {
return _instance_id;
}
////////////////////////////////////////////////////////////////////
// Function: P3DInstance::get_session_key
// Access: Public

View File

@ -17,6 +17,8 @@
#include <sstream>
int P3DInstance::_next_instance_id = 0;
////////////////////////////////////////////////////////////////////
// Function: P3DInstance::Constructor
// Access: Public
@ -37,11 +39,13 @@ P3DInstance(P3D_request_ready_func *func,
_win_width(win_width), _win_height(win_height),
_parent_window(parent_window)
{
P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
_instance_id = _next_instance_id;
++_next_instance_id;
INIT_LOCK(_request_lock);
// For the moment, all sessions will be unique.
P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
ostringstream strm;
strm << inst_mgr->get_unique_session_index();
_session_key = strm.str();
@ -200,6 +204,7 @@ feed_url_stream(int unique_id,
TiXmlElement *P3DInstance::
make_xml() {
TiXmlElement *xinstance = new TiXmlElement("instance");
xinstance->SetAttribute("id", _instance_id);
xinstance->SetAttribute("p3d_filename", _p3d_filename);
switch (_window_type) {

View File

@ -56,6 +56,7 @@ public:
size_t this_data_size);
inline const string &get_p3d_filename() const;
inline int get_instance_id() const;
inline const string &get_session_key() const;
inline const string &get_python_version() const;
@ -79,6 +80,7 @@ private:
P3D_window_handle _parent_window;
Tokens _tokens;
int _instance_id;
string _session_key;
string _python_version;
P3DSession *_session;
@ -87,6 +89,8 @@ private:
typedef deque<P3D_request *> Requests;
Requests _pending_requests;
static int _next_instance_id;
friend class P3DSession;
};

View File

@ -21,7 +21,8 @@
////////////////////////////////////////////////////////////////////
P3DPythonRun::
P3DPythonRun(int argc, char *argv[]) {
_read_thread_alive = false;
_read_thread_continue = false;
_program_continue = true;
INIT_LOCK(_commands_lock);
_program_name = argv[0];
@ -101,6 +102,11 @@ run_python() {
PyErr_Print();
return false;
}
_exit = PyObject_GetAttrString(appmf, "exit");
if (_exit == NULL) {
PyErr_Print();
return false;
}
_setupWindow = PyObject_GetAttrString(appmf, "setupWindow");
if (_setupWindow == NULL) {
PyErr_Print();
@ -174,12 +180,27 @@ 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);
}
TiXmlElement *xcommand = doc->FirstChildElement("command");
if (xcommand != NULL) {
const char *cmd = xcommand->Attribute("cmd");
if (cmd != NULL) {
if (strcmp(cmd, "start_instance") == 0) {
TiXmlElement *xinstance = xcommand->FirstChildElement("instance");
if (xinstance != (TiXmlElement *)NULL) {
P3DCInstance *inst = new P3DCInstance(xinstance);
start_instance(inst);
}
} else if (strcmp(cmd, "terminate_instance") == 0) {
int id;
if (xcommand->Attribute("id", &id)) {
terminate_instance(id);
}
} else {
cerr << "Unhandled command " << cmd << "\n";
}
}
}
}
////////////////////////////////////////////////////////////////////
@ -203,6 +224,13 @@ check_comm() {
delete doc;
ACQUIRE_LOCK(_commands_lock);
}
if (!_program_continue) {
// The low-level thread detected an error, for instance pipe
// closed. We should exit gracefully.
terminate_session();
}
RELEASE_LOCK(_commands_lock);
}
@ -236,9 +264,9 @@ py_check_comm(PyObject *, PyObject *args) {
////////////////////////////////////////////////////////////////////
void P3DPythonRun::
spawn_read_thread() {
assert(!_read_thread_alive);
assert(!_read_thread_continue);
_read_thread_alive = true;
_read_thread_continue = true;
#ifdef _WIN32
_read_thread = CreateThread(NULL, 0, &win_rt_thread_run, this, 0, NULL);
#endif
@ -252,7 +280,7 @@ spawn_read_thread() {
void P3DPythonRun::
join_read_thread() {
cerr << "waiting for thread\n";
_read_thread_alive = false;
_read_thread_continue = false;
#ifdef _WIN32
assert(_read_thread != NULL);
@ -272,7 +300,7 @@ join_read_thread() {
void P3DPythonRun::
start_instance(P3DCInstance *inst) {
cerr << "starting instance " << inst->get_p3d_filename() << "\n";
_instances.push_back(inst);
_instances[inst->get_instance_id()] = inst;
string window_type;
switch (inst->_window_type) {
@ -310,6 +338,51 @@ start_instance(P3DCInstance *inst) {
Py_XDECREF(result);
}
////////////////////////////////////////////////////////////////////
// Function: P3DPythonRun::terminate_instance
// Access: Private
// Description: Stops the instance with the indicated id.
////////////////////////////////////////////////////////////////////
void P3DPythonRun::
terminate_instance(int id) {
Instances::iterator ii = _instances.find(id);
if (ii == _instances.end()) {
cerr << "Can't stop instance " << id << ": not started.\n";
return;
}
P3DCInstance *inst = (*ii).second;
_instances.erase(ii);
delete inst;
// TODO: we don't currently have any way to stop just one instance
// of a multi-instance session. We could maybe close its window or
// something.
terminate_session();
}
////////////////////////////////////////////////////////////////////
// Function: P3DPythonRun::terminate_session
// Access: Private
// Description: Stops all currently-running instances.
////////////////////////////////////////////////////////////////////
void P3DPythonRun::
terminate_session() {
Instances::iterator ii;
for (ii = _instances.begin(); ii != _instances.end(); ++ii) {
P3DCInstance *inst = (*ii).second;
delete inst;
}
_instances.clear();
PyObject *result = PyObject_CallFunction(_exit, "");
if (result == NULL) {
PyErr_Print();
}
Py_XDECREF(result);
}
////////////////////////////////////////////////////////////////////
// Function: P3DPythonRun::rt_thread_run
// Access: Private
@ -318,13 +391,14 @@ start_instance(P3DCInstance *inst) {
void P3DPythonRun::
rt_thread_run() {
cerr << "thread reading.\n";
while (_read_thread_alive) {
while (_read_thread_continue) {
TiXmlDocument *doc = new TiXmlDocument;
_pipe_read >> *doc;
if (!_pipe_read || _pipe_read.eof()) {
// Some error on reading. Abort.
cerr << "Error on reading.\n";
_program_continue = false;
return;
}

View File

@ -18,7 +18,7 @@
#include <iostream>
#include <string>
#include <deque>
#include <vector>
#include <map>
#include <assert.h>
#include <Python.h>
#include <tinyxml.h>
@ -63,6 +63,8 @@ private:
void join_read_thread();
void start_instance(P3DCInstance *inst);
void terminate_instance(int id);
void terminate_session();
private:
// This method runs only within the read thread.
@ -72,7 +74,7 @@ private:
#endif
private:
typedef vector<P3DCInstance *> Instances;
typedef map<int, P3DCInstance *> Instances;
Instances _instances;
string _program_name;
@ -80,6 +82,7 @@ private:
char **_py_argv;
PyObject *_runPackedApp;
PyObject *_exit;
PyObject *_setupWindow;
// The remaining members are manipulated by the read thread.
@ -90,7 +93,8 @@ private:
HandleStream _pipe_read;
HandleStream _pipe_write;
bool _read_thread_alive;
bool _read_thread_continue;
bool _program_continue;
#ifdef _WIN32
HANDLE _read_thread;
#endif

View File

@ -167,24 +167,17 @@ start_instance(P3DInstance *inst) {
bool inserted = _instances.insert(inst).second;
assert(inserted);
P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
TiXmlDocument doc;
TiXmlDeclaration *decl = new TiXmlDeclaration("1.0", "", "");
TiXmlElement *xcommand = new TiXmlElement("command");
xcommand->SetAttribute("cmd", "start_instance");
TiXmlElement *xinstance = inst->make_xml();
doc.LinkEndChild(decl);
doc.LinkEndChild(xinstance);
doc.LinkEndChild(xcommand);
xcommand->LinkEndChild(xinstance);
cerr << "sending: " << doc << "\n";
_pipe_write << doc << flush;
/*
P3DPython *python = inst_mgr->start_python(_python_version);
if (python != NULL) {
python->start_session(this, inst);
}
*/
}
////////////////////////////////////////////////////////////////////
@ -196,12 +189,16 @@ start_instance(P3DInstance *inst) {
////////////////////////////////////////////////////////////////////
void P3DSession::
terminate_instance(P3DInstance *inst) {
/*
if (_python != NULL) {
_python->terminate_session(this);
assert(_python == NULL);
}
*/
TiXmlDocument doc;
TiXmlDeclaration *decl = new TiXmlDeclaration("1.0", "", "");
TiXmlElement *xcommand = new TiXmlElement("command");
xcommand->SetAttribute("cmd", "terminate_instance");
xcommand->SetAttribute("id", inst->get_instance_id());
doc.LinkEndChild(decl);
doc.LinkEndChild(xcommand);
_pipe_write << doc << flush;
if (inst->_session == this) {
inst->_session = NULL;

View File

@ -153,6 +153,9 @@ def add_check_comm(func, this):
return taskMgr.add(func, "check_comm", sort = -50, extraArgs = [this],
appendTask = True)
def exit():
taskMgr.stop()
if __name__ == '__main__':
try:
runPackedApp(sys.argv[1:])