inbound scripting controls

This commit is contained in:
David Rose 2009-07-04 19:12:24 +00:00
parent cd80487c05
commit ed037be160
16 changed files with 315 additions and 196 deletions

View File

@ -115,6 +115,8 @@ find_extension_dot(const string &filename) {
return string::npos;
}
// Forward reference for function defined below.
static void unload_dso();
////////////////////////////////////////////////////////////////////
// Function: load_plugin
@ -265,8 +267,8 @@ load_plugin(const string &p3d_plugin_filename) {
////////////////////////////////////////////////////////////////////
// Function: unload_plugin
// Description: Removes the plugin from memory space and clears all
// of the pointers.
// Description: Calls finalize, then removes the plugin from memory
// space and clears all of the pointers.
////////////////////////////////////////////////////////////////////
void
unload_plugin() {
@ -274,8 +276,24 @@ unload_plugin() {
return;
}
P3D_finalize();
cerr << "unload_plugin called\n";
P3D_finalize();
unload_dso();
}
////////////////////////////////////////////////////////////////////
// Function: unload_dso
// Description: Removes the plugin from memory space and clears all
// of the pointers. This is only intended to be called
// by load_plugin(), above, in the specific case that
// the plugin loaded but could not successfully
// initialize itself. All user code should call
// unload_plugin(), above, which first calls
// P3D_finalize().
////////////////////////////////////////////////////////////////////
static void
unload_dso() {
#ifdef _WIN32
assert(module != NULL);
FreeLibrary(module);

View File

@ -71,6 +71,19 @@ get_python_version() const {
return _python_version;
}
////////////////////////////////////////////////////////////////////
// Function: P3DInstance::get_request_ready_func
// Access: Public
// Description: Returns a pointer to the asynchronous notification
// function that was passed to the constructor, if any,
// or NULL if asynchronous notifications are not
// required.
////////////////////////////////////////////////////////////////////
inline P3D_request_ready_func *P3DInstance::
get_request_ready_func() const {
return _func;
}
////////////////////////////////////////////////////////////////////
// Function: P3DInstance::is_started
// Access: Public

View File

@ -88,6 +88,8 @@ P3DInstance::
// TODO: Is it possible for someone to delete an instance while a
// download is still running? Who will crash when this happens?
nout << "deleting instance " << this << "\n" << flush;
}
////////////////////////////////////////////////////////////////////
@ -169,6 +171,7 @@ set_wparams(const P3DWindowParams &wparams) {
P3DObject *P3DInstance::
get_script_object() const {
assert(_session != NULL);
nout << "Called P3DInstance::get_script_object()\n";
TiXmlDocument *doc = new TiXmlDocument;
TiXmlDeclaration *decl = new TiXmlDeclaration("1.0", "utf-8", "");
@ -239,10 +242,10 @@ get_request() {
// If we received a notify request, process the notification
// immediately--it might be interesting to this instance.
const char *message = result->_request._notify._message;
if (strcmp(message, "window_opened") == 0) {
if (strcmp(message, "onwindowopen") == 0) {
// The process told us that it just succesfully opened its
// window.
nout << "Instance " << this << " got window_opened\n" << flush;
nout << "Instance " << this << " got onwindowopen\n" << flush;
_instance_window_opened = true;
if (_splash_window != NULL) {
nout << "Deleting splash window\n" << flush;
@ -273,14 +276,9 @@ add_request(P3D_request *request) {
_request_pending = true;
RELEASE_LOCK(_request_lock);
// Asynchronous notification for anyone who cares.
if (_func != NULL) {
_func(this);
}
// Synchronous notification for pollers.
// Tell the world we've got a new request.
P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
inst_mgr->signal_request_ready();
inst_mgr->signal_request_ready(this);
}
////////////////////////////////////////////////////////////////////

View File

@ -64,6 +64,8 @@ public:
inline const string &get_session_key() const;
inline const string &get_python_version() const;
inline P3D_request_ready_func *get_request_ready_func() const;
void add_package(P3DPackage *package);
void start_download(P3DDownload *download);

View File

@ -38,6 +38,10 @@ P3DInstanceManager() {
_is_initialized = false;
_unique_session_index = 0;
_notify_thread_continue = false;
_started_notify_thread = false;
INIT_THREAD(_notify_thread);
#ifdef _WIN32
// Ensure the appropriate Windows common controls are available to
// this application.
@ -55,11 +59,19 @@ P3DInstanceManager() {
////////////////////////////////////////////////////////////////////
P3DInstanceManager::
~P3DInstanceManager() {
// Actually, this destructor is never called, since this is a global
// object that never gets deleted.
nout << "~P3DInstanceManager\n" << flush;
if (_started_notify_thread) {
_notify_ready.acquire();
_notify_thread_continue = false;
_notify_ready.notify();
_notify_ready.release();
JOIN_THREAD(_notify_thread);
_started_notify_thread = false;
}
assert(_instances.empty());
assert(_sessions.empty());
nout << "done ~P3DInstanceManager\n" << flush;
}
////////////////////////////////////////////////////////////////////
@ -254,11 +266,29 @@ get_unique_session_index() {
// Function: P3DInstanceManager::signal_request_ready
// Access: Public
// Description: May be called in any thread to indicate that a new
// P3D_request is available in some instance. This will
// wake up a sleeping wait_request() call, if any.
// P3D_request is available in the indicated instance.
////////////////////////////////////////////////////////////////////
void P3DInstanceManager::
signal_request_ready() {
signal_request_ready(P3DInstance *inst) {
if (inst->get_request_ready_func() != NULL) {
// This instance requires asynchronous notifications of requests.
// Thus, we should tell the notify thread to wake up and make the
// callback.
_notify_ready.acquire();
_notify_instances.push_back(inst);
_notify_ready.notify();
_notify_ready.release();
// Oh, and we should spawn the thread if we haven't already.
if (!_started_notify_thread) {
_notify_thread_continue = true;
SPAWN_THREAD(_notify_thread, nt_thread_run, this);
_started_notify_thread = true;
}
}
// Then, wake up the main thread, in case it's sleeping on
// wait_request().
_request_ready.acquire();
_request_ready.notify();
_request_ready.release();
@ -287,3 +317,63 @@ get_global_ptr() {
}
return _global_ptr;
}
////////////////////////////////////////////////////////////////////
// Function: P3DInstanceManager::delete_global_ptr
// Access: Public, Static
// Description: This is called only at plugin shutdown time; it
// deletes the global instance manager pointer and
// clears it to NULL.
////////////////////////////////////////////////////////////////////
void P3DInstanceManager::
delete_global_ptr() {
if (_global_ptr != NULL) {
delete _global_ptr;
_global_ptr = NULL;
}
}
////////////////////////////////////////////////////////////////////
// Function: P3DInstanceManager::nt_thread_run
// Access: Private
// Description: The main function for the notify thread.
////////////////////////////////////////////////////////////////////
void P3DInstanceManager::
nt_thread_run() {
// The notify thread exists because we need to be able to send
// asynchronous notifications of request events. These request
// events were detected in the various read threads associated with
// each session, but we can't call back into the host space from the
// read thread, since if the host immediately response to a callback
// by calling back into the p3d_plugin space, now we have our read
// thread doing stuff in here that's not related to the read thread.
// Even worse, some of the things it might need to do might require
// a separate read thread to be running!
_notify_ready.acquire();
while (_notify_thread_continue) {
NotifyInstances instances;
while (!_notify_instances.empty()) {
instances.clear();
instances.swap(_notify_instances);
// Go ahead and drop the lock while we make the callback, to
// reduce the risk of deadlock. We don't want to be holding any
// locks when we call into client code.
_notify_ready.release();
NotifyInstances::iterator ni;
for (ni = instances.begin(); ni != instances.end(); ++ni) {
// TODO: a race condition here when instances are deleted.
P3DInstance *inst = (*ni);
P3D_request_ready_func *func = inst->get_request_ready_func();
assert(inst != NULL);
(*func)(inst);
}
_notify_ready.acquire();
}
_notify_ready.wait();
}
_notify_ready.release();
nout << "exiting nt_thread_run\n" << flush;
}

View File

@ -20,6 +20,7 @@
#include <set>
#include <map>
#include <vector>
class P3DInstance;
class P3DSession;
@ -61,11 +62,19 @@ public:
inline int get_num_instances() const;
int get_unique_session_index();
void signal_request_ready();
void signal_request_ready(P3DInstance *inst);
P3D_class_definition *make_class_definition() const;
static P3DInstanceManager *get_global_ptr();
static void delete_global_ptr();
private:
// The notify thread. This thread runs only for the purpose of
// generating asynchronous notifications of requests, to callers who
// ask for it.
THREAD_CALLBACK_DECLARATION(P3DInstanceManager, nt_thread_run);
void nt_thread_run();
private:
bool _is_initialized;
@ -84,7 +93,20 @@ private:
int _unique_session_index;
// This condition var is waited on the main thread and signaled in a
// sub-thread when new request notices arrive.
P3DConditionVar _request_ready;
// We may need a thread to send async request notices to callers.
bool _notify_thread_continue;
bool _started_notify_thread;
THREAD _notify_thread;
// This queue of instances that need to send notifications is
// protected by _notify_ready's mutex.
typedef vector<P3DInstance *> NotifyInstances;
NotifyInstances _notify_instances;
P3DConditionVar _notify_ready;
static P3DInstanceManager *_global_ptr;
};

View File

@ -30,6 +30,7 @@ P3DPythonRun(int argc, char *argv[]) {
_read_thread_continue = false;
_program_continue = true;
INIT_LOCK(_commands_lock);
INIT_THREAD(_read_thread);
_program_name = argv[0];
_py_argc = 1;
@ -508,15 +509,7 @@ spawn_read_thread() {
// anonymous pipe in Windows).
_read_thread_continue = true;
#ifdef _WIN32
_read_thread = CreateThread(NULL, 0, &win_rt_thread_run, this, 0, NULL);
#else
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM);
pthread_create(&_read_thread, &attr, &posix_rt_thread_run, (void *)this);
pthread_attr_destroy(&attr);
#endif
SPAWN_THREAD(_read_thread, rt_thread_run, this);
}
////////////////////////////////////////////////////////////////////
@ -529,16 +522,8 @@ join_read_thread() {
nout << "waiting for thread\n";
_read_thread_continue = false;
_pipe_read.close();
#ifdef _WIN32
assert(_read_thread != NULL);
WaitForSingleObject(_read_thread, INFINITE);
CloseHandle(_read_thread);
_read_thread = NULL;
#else
void *return_val;
pthread_join(_read_thread, &return_val);
#endif
JOIN_THREAD(_read_thread);
nout << "done waiting for thread\n";
}
@ -919,33 +904,6 @@ rt_thread_run() {
}
}
#ifdef _WIN32
////////////////////////////////////////////////////////////////////
// Function: P3DPythonRun::win_rt_thread_run
// Access: Private, Static
// Description: The Windows flavor of the thread callback function.
////////////////////////////////////////////////////////////////////
DWORD P3DPythonRun::
win_rt_thread_run(LPVOID data) {
((P3DPythonRun *)data)->rt_thread_run();
return 0;
}
#endif
#ifndef _WIN32
////////////////////////////////////////////////////////////////////
// Function: P3DPythonRun::posix_rt_thread_run
// Access: Private, Static
// Description: The Posix flavor of the thread callback function.
////////////////////////////////////////////////////////////////////
void *P3DPythonRun::
posix_rt_thread_run(void *data) {
((P3DPythonRun *)data)->rt_thread_run();
return NULL;
}
#endif
////////////////////////////////////////////////////////////////////
// Function: main
// Description: Starts the program running.

View File

@ -95,13 +95,8 @@ private:
private:
// This method runs only within the read thread.
THREAD_CALLBACK_DECLARATION(P3DPythonRun, rt_thread_run);
void rt_thread_run();
#ifdef _WIN32
static DWORD WINAPI win_rt_thread_run(LPVOID data);
#else
static void *posix_rt_thread_run(void *data);
#endif
private:
typedef pmap<int, P3DCInstance *> Instances;
@ -130,11 +125,7 @@ private:
bool _read_thread_continue;
bool _program_continue;
#ifdef _WIN32
HANDLE _read_thread;
#else
pthread_t _read_thread;
#endif
THREAD _read_thread;
public:
static P3DPythonRun *_global_ptr;

View File

@ -54,6 +54,7 @@ P3DSession(P3DInstance *inst) {
_panda3d_callback = NULL;
INIT_LOCK(_instances_lock);
INIT_THREAD(_read_thread);
P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
@ -471,22 +472,8 @@ void P3DSession::
spawn_read_thread() {
assert(!_read_thread_continue);
// We have to use direct OS calls to create the thread instead of
// Panda constructs, because it has to be an actual thread, not
// necessarily a Panda thread (we can't use Panda's simple threads
// implementation, because we can't get overlapped I/O on an
// anonymous pipe in Windows).
_read_thread_continue = true;
#ifdef _WIN32
_read_thread = CreateThread(NULL, 0, &win_rt_thread_run, this, 0, NULL);
#else
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM);
pthread_create(&_read_thread, &attr, &posix_rt_thread_run, (void *)this);
pthread_attr_destroy(&attr);
#endif
SPAWN_THREAD(_read_thread, rt_thread_run, this);
_started_read_thread = true;
}
@ -503,17 +490,8 @@ join_read_thread() {
_read_thread_continue = false;
_pipe_read.close();
#ifdef _WIN32
assert(_read_thread != NULL);
WaitForSingleObject(_read_thread, INFINITE);
CloseHandle(_read_thread);
_read_thread = NULL;
#else
void *return_val;
pthread_join(_read_thread, &return_val);
#endif
JOIN_THREAD(_read_thread);
_started_read_thread = false;
}
@ -576,6 +554,7 @@ rt_handle_request(TiXmlDocument *doc) {
TiXmlElement *xrequest = doc->FirstChildElement("request");
if (xrequest != (TiXmlElement *)NULL) {
nout << "Handling request\n" << flush;
int instance_id;
if (xrequest->QueryIntAttribute("instance_id", &instance_id) == TIXML_SUCCESS) {
// Look up the particular instance this is related to.
@ -591,6 +570,7 @@ rt_handle_request(TiXmlDocument *doc) {
}
RELEASE_LOCK(_instances_lock);
}
nout << "done handling request\n" << flush;
}
delete doc;
@ -645,33 +625,6 @@ rt_terminate() {
}
}
#ifdef _WIN32
////////////////////////////////////////////////////////////////////
// Function: P3DSession::win_rt_thread_run
// Access: Private, Static
// Description: The Windows flavor of the thread callback function.
////////////////////////////////////////////////////////////////////
DWORD P3DSession::
win_rt_thread_run(LPVOID data) {
((P3DSession *)data)->rt_thread_run();
return 0;
}
#endif
#ifndef _WIN32
////////////////////////////////////////////////////////////////////
// Function: P3DSession::posix_rt_thread_run
// Access: Private, Static
// Description: The Posix flavor of the thread callback function.
////////////////////////////////////////////////////////////////////
void *P3DSession::
posix_rt_thread_run(void *data) {
((P3DSession *)data)->rt_thread_run();
return NULL;
}
#endif
#ifdef _WIN32
////////////////////////////////////////////////////////////////////
// Function: P3DSession::win_create_process

View File

@ -60,17 +60,12 @@ private:
private:
// These methods run only within the read thread.
THREAD_CALLBACK_DECLARATION(P3DSession, rt_thread_run);
void rt_thread_run();
void rt_terminate();
void rt_handle_request(TiXmlDocument *doc);
P3D_request *rt_make_p3d_request(TiXmlElement *xrequest);
#ifdef _WIN32
static DWORD WINAPI win_rt_thread_run(LPVOID data);
#else
static void *posix_rt_thread_run(void *data);
#endif
#ifdef _WIN32
static HANDLE
win_create_process(const string &program, const string &start_dir,
@ -135,11 +130,7 @@ private:
HandleStream _pipe_write;
bool _read_thread_continue;
#ifdef _WIN32
HANDLE _read_thread;
#else
pthread_t _read_thread;
#endif
THREAD _read_thread;
friend class PackageCallback;
};

View File

@ -16,17 +16,42 @@
#define P3D_LOCK_H
// Provides some simple macros that implement platform-independet
// mutex locks.
// mutex locks, as well as platform-independent thread constructs.
#ifdef _WIN32
// Windows case
// Locks are straightforward.
#define LOCK CRITICAL_SECTION
#define INIT_LOCK(lock) InitializeCriticalSection(&(lock))
#define ACQUIRE_LOCK(lock) EnterCriticalSection(&(lock))
#define RELEASE_LOCK(lock) LeaveCriticalSection(&(lock))
#define DESTROY_LOCK(lock) DeleteCriticalSection(&(lock))
// Threads.
#define THREAD HANDLE
#define INIT_THREAD(thread) (thread) = NULL;
#define SPAWN_THREAD(thread, callback_function, this) \
(thread) = CreateThread(NULL, 0, &win_ ## callback_function, (this), 0, NULL)
#define JOIN_THREAD(thread) \
assert((thread) != NULL); \
WaitForSingleObject((thread), INFINITE); \
CloseHandle((thread)); \
(thread) = NULL;
// Declare this macro within your class declaration. This implements
// the callback function wrapper necessary to hook into the above
// SPAWN_THREAD call. The wrapper will in turn call the method
// function you provide.
#define THREAD_CALLBACK_DECLARATION(class, callback_function) \
static DWORD class:: \
win_ ## callback_function(LPVOID data) { \
((class *)data)->callback_function(); \
return 0; \
}
#else // _WIN32
// Posix case
@ -47,6 +72,29 @@
#define RELEASE_LOCK(lock) pthread_mutex_unlock(&(lock))
#define DESTROY_LOCK(lock) pthread_mutex_destroy(&(lock))
#define THREAD pthread_t
#define INIT_THREAD(thread) (thread) = 0;
#define SPAWN_THREAD(thread, callback_function, this) \
pthread_attr_t attr; \
pthread_attr_init(&attr); \
pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM); \
pthread_create(&(thread), &attr, &posix_ ## callback_function, (void *)(this)); \
pthread_attr_destroy(&attr);
#define JOIN_THREAD(thread) \
assert((thread) != 0); \
void *return_val; \
pthread_join((thread), &return_val); \
(thread) = 0;
// As above, declare this macro within your class declaration.
#define THREAD_CALLBACK_DECLARATION(class, callback_function) \
static void *class:: \
posix_ ## callback_function(void *data) { \
((class *)data)->callback_function(); \
return NULL; \
}
#endif // _WIN32
#endif

View File

@ -29,7 +29,7 @@
// module from parallel access by multiple threads in the host.
bool initialized_lock = false;
LOCK _lock;
LOCK _api_lock;
ofstream log;
string plugin_output_filename;
@ -44,10 +44,10 @@ P3D_initialize(int api_version, const char *output_filename) {
}
if (!initialized_lock) {
INIT_LOCK(_lock);
INIT_LOCK(_api_lock);
initialized_lock = true;
}
ACQUIRE_LOCK(_lock);
ACQUIRE_LOCK(_api_lock);
plugin_output_filename = string();
if (output_filename != NULL) {
@ -63,21 +63,22 @@ P3D_initialize(int api_version, const char *output_filename) {
P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
bool result = inst_mgr->initialize();
RELEASE_LOCK(_lock);
RELEASE_LOCK(_api_lock);
return result;
}
void
P3D_finalize() {
P3DInstanceManager::delete_global_ptr();
}
P3D_instance *
P3D_new_instance(P3D_request_ready_func *func, void *user_data) {
assert(P3DInstanceManager::get_global_ptr()->is_initialized());
ACQUIRE_LOCK(_lock);
ACQUIRE_LOCK(_api_lock);
P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
P3DInstance *result = inst_mgr->create_instance(func, user_data);
RELEASE_LOCK(_lock);
RELEASE_LOCK(_api_lock);
return result;
}
@ -88,21 +89,21 @@ P3D_instance_start(P3D_instance *instance, const char *p3d_filename,
if (p3d_filename == NULL) {
p3d_filename = "";
}
ACQUIRE_LOCK(_lock);
ACQUIRE_LOCK(_api_lock);
P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
bool result = inst_mgr->start_instance
((P3DInstance *)instance, p3d_filename, tokens, num_tokens);
RELEASE_LOCK(_lock);
RELEASE_LOCK(_api_lock);
return result;
}
void
P3D_instance_finish(P3D_instance *instance) {
assert(P3DInstanceManager::get_global_ptr()->is_initialized());
ACQUIRE_LOCK(_lock);
ACQUIRE_LOCK(_api_lock);
P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
inst_mgr->finish_instance((P3DInstance *)instance);
RELEASE_LOCK(_lock);
RELEASE_LOCK(_api_lock);
}
void
@ -115,97 +116,97 @@ P3D_instance_setup_window(P3D_instance *instance,
P3DWindowParams wparams(window_type, win_x, win_y,
win_width, win_height, parent_window);
ACQUIRE_LOCK(_lock);
ACQUIRE_LOCK(_api_lock);
((P3DInstance *)instance)->set_wparams(wparams);
RELEASE_LOCK(_lock);
RELEASE_LOCK(_api_lock);
}
P3D_class_definition *
P3D_make_class_definition() {
assert(P3DInstanceManager::get_global_ptr()->is_initialized());
ACQUIRE_LOCK(_lock);
ACQUIRE_LOCK(_api_lock);
P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
P3D_class_definition *result = inst_mgr->make_class_definition();
RELEASE_LOCK(_lock);
RELEASE_LOCK(_api_lock);
return result;
}
P3D_object *
P3D_new_none_object() {
assert(P3DInstanceManager::get_global_ptr()->is_initialized());
ACQUIRE_LOCK(_lock);
ACQUIRE_LOCK(_api_lock);
P3D_object *result = new P3DNoneObject();
RELEASE_LOCK(_lock);
RELEASE_LOCK(_api_lock);
return result;
}
P3D_object *
P3D_new_bool_object(bool value) {
assert(P3DInstanceManager::get_global_ptr()->is_initialized());
ACQUIRE_LOCK(_lock);
ACQUIRE_LOCK(_api_lock);
P3D_object *result = new P3DBoolObject(value);
RELEASE_LOCK(_lock);
RELEASE_LOCK(_api_lock);
return result;
}
P3D_object *
P3D_new_int_object(int value) {
assert(P3DInstanceManager::get_global_ptr()->is_initialized());
ACQUIRE_LOCK(_lock);
ACQUIRE_LOCK(_api_lock);
P3D_object *result = new P3DIntObject(value);
RELEASE_LOCK(_lock);
RELEASE_LOCK(_api_lock);
return result;
}
P3D_object *
P3D_new_float_object(double value) {
assert(P3DInstanceManager::get_global_ptr()->is_initialized());
ACQUIRE_LOCK(_lock);
ACQUIRE_LOCK(_api_lock);
P3D_object *result = new P3DFloatObject(value);
RELEASE_LOCK(_lock);
RELEASE_LOCK(_api_lock);
return result;
}
P3D_object *
P3D_new_string_object(const char *str, int length) {
assert(P3DInstanceManager::get_global_ptr()->is_initialized());
ACQUIRE_LOCK(_lock);
ACQUIRE_LOCK(_api_lock);
P3D_object *result = new P3DStringObject(string(str, length));
RELEASE_LOCK(_lock);
RELEASE_LOCK(_api_lock);
return result;
}
P3D_object *
P3D_new_list_object() {
assert(P3DInstanceManager::get_global_ptr()->is_initialized());
ACQUIRE_LOCK(_lock);
ACQUIRE_LOCK(_api_lock);
P3D_object *result = new P3DListObject;
RELEASE_LOCK(_lock);
RELEASE_LOCK(_api_lock);
return result;
}
P3D_object *
P3D_instance_get_script_object(P3D_instance *instance) {
assert(P3DInstanceManager::get_global_ptr()->is_initialized());
ACQUIRE_LOCK(_lock);
ACQUIRE_LOCK(_api_lock);
P3D_object *result = ((P3DInstance *)instance)->get_script_object();
RELEASE_LOCK(_lock);
RELEASE_LOCK(_api_lock);
return result;
}
@ -213,55 +214,55 @@ void
P3D_instance_set_script_object(P3D_instance *instance,
P3D_object *object) {
assert(P3DInstanceManager::get_global_ptr()->is_initialized());
ACQUIRE_LOCK(_lock);
ACQUIRE_LOCK(_api_lock);
// TODO.
RELEASE_LOCK(_lock);
RELEASE_LOCK(_api_lock);
}
P3D_request *
P3D_instance_get_request(P3D_instance *instance) {
assert(P3DInstanceManager::get_global_ptr()->is_initialized());
ACQUIRE_LOCK(_lock);
ACQUIRE_LOCK(_api_lock);
P3D_request *result = ((P3DInstance *)instance)->get_request();
RELEASE_LOCK(_lock);
RELEASE_LOCK(_api_lock);
return result;
}
P3D_instance *
P3D_check_request(bool wait) {
assert(P3DInstanceManager::get_global_ptr()->is_initialized());
ACQUIRE_LOCK(_lock);
ACQUIRE_LOCK(_api_lock);
P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
P3D_instance *inst = inst_mgr->check_request();
if (inst != NULL || !wait) {
RELEASE_LOCK(_lock);
RELEASE_LOCK(_api_lock);
return inst;
}
// Now we have to block until a request is available.
while (inst == NULL && inst_mgr->get_num_instances() != 0) {
RELEASE_LOCK(_lock);
RELEASE_LOCK(_api_lock);
inst_mgr->wait_request();
ACQUIRE_LOCK(_lock);
ACQUIRE_LOCK(_api_lock);
inst = inst_mgr->check_request();
}
RELEASE_LOCK(_lock);
RELEASE_LOCK(_api_lock);
return inst;
}
void
P3D_request_finish(P3D_request *request, bool handled) {
assert(P3DInstanceManager::get_global_ptr()->is_initialized());
ACQUIRE_LOCK(_lock);
ACQUIRE_LOCK(_api_lock);
if (request != (P3D_request *)NULL) {
((P3DInstance *)request->_instance)->finish_request(request, handled);
}
RELEASE_LOCK(_lock);
RELEASE_LOCK(_api_lock);
}
bool
@ -272,11 +273,11 @@ P3D_instance_feed_url_stream(P3D_instance *instance, int unique_id,
const void *this_data,
size_t this_data_size) {
assert(P3DInstanceManager::get_global_ptr()->is_initialized());
ACQUIRE_LOCK(_lock);
ACQUIRE_LOCK(_api_lock);
bool result = ((P3DInstance *)instance)->
feed_url_stream(unique_id, result_code, http_status_code,
total_expected_data,
(const unsigned char *)this_data, this_data_size);
RELEASE_LOCK(_lock);
RELEASE_LOCK(_api_lock);
return result;
}

View File

@ -79,6 +79,10 @@ PPInstance::
_p3d_inst = NULL;
}
if (_script_object != NULL) {
browser->releaseobject(_script_object);
}
// Free the tokens we allocated.
Tokens::iterator ti;
for (ti = _tokens.begin(); ti != _tokens.end(); ++ti) {
@ -386,6 +390,22 @@ handle_request(P3D_request *request) {
}
break;
case P3D_RT_notify:
{
logfile << "Got P3D_RT_notify: " << request->_request._notify._message
<< "\n" << flush;
if (_script_object != NULL &&
strcmp(request->_request._notify._message, "onpythonload") == 0) {
// Now that Python is running, initialize our script_object
// with the proper P3D object pointer.
P3D_object *obj = P3D_instance_get_script_object(_p3d_inst);
logfile << "late obj = " << obj << "\n" << flush;
_script_object->set_p3d_object(obj);
}
}
break;
default:
// Some request types are not handled.
logfile << "Unhandled request: " << request->_request_type << "\n";
@ -405,6 +425,7 @@ NPObject *PPInstance::
get_script_object() {
logfile << "get_script_object\n" << flush;
if (_script_object != NULL) {
logfile << "returning _script_object ref = " << _script_object->referenceCount << "\n";
return _script_object;
}
@ -416,6 +437,9 @@ get_script_object() {
}
_script_object = PPObject::make_new(this, obj);
logfile << "_script_object ref = " << _script_object->referenceCount << "\n";
browser->retainobject(_script_object);
logfile << "after retain, _script_object ref = " << _script_object->referenceCount << "\n";
logfile << "ppobj = " << _script_object << "\n" << flush;
return _script_object;
}
@ -601,14 +625,6 @@ create_instance() {
}
P3D_instance_start(_p3d_inst, _p3d_filename.c_str(), tokens, _tokens.size());
send_window();
if (_script_object != NULL) {
// Now that we have an instance, initialize our script_object
// with the proper P3D object pointer.
P3D_object *obj = P3D_instance_get_script_object(_p3d_inst);
logfile << "late obj = " << obj << "\n" << flush;
_script_object->set_p3d_object(obj);
}
}
}

View File

@ -87,7 +87,7 @@ window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
////////////////////////////////////////////////////////////////////
// Function: request_ready
// Description: This function is attached as an asyncronous callback
// Description: This function is attached as an asynchronous callback
// to each instance; it will be notified when the
// instance has a request ready. This function may be
// called in a sub-thread.
@ -99,14 +99,17 @@ request_ready(P3D_instance *instance) {
// << " thread = " << GetCurrentThreadId()
<< "\n" << flush;
#ifdef _WIN32
// Since we might be in a sub-thread at this point, use a Windows
// message to forward this event to the main thread.
#ifdef _WIN32
PostThreadMessage(main_thread_id, WM_USER, 0, 0);
#else
// TODO: send the message to the main thread properly.
handle_request_loop();
// On Mac, we ignore this asynchronous event, and rely on detecting
// it within HandleEvent(). TODO: enable a timer to ensure we get
// HandleEvent callbacks in a timely manner? Or maybe we should
// enable a one-shot timer in response to this asynchronous event?
#endif
}
@ -366,6 +369,10 @@ NPP_Print(NPP instance, NPPrint *platformPrint) {
int16
NPP_HandleEvent(NPP instance, void *event) {
// logfile << "HandleEvent\n";
// Here's a fine opportunity to check for new requests.
handle_request_loop();
return 0;
}

View File

@ -251,7 +251,8 @@ run(int argc, char *argv[]) {
}
// All instances have finished; we can exit.
cerr << "clean exit\n";
unload_plugin();
return 0;
}
@ -404,7 +405,8 @@ handle_request(P3D_request *request) {
cerr << "Got P3D_RT_notify: " << request->_request._notify._message
<< "\n";
// Ignore notifications.
/*
{
// Temporary.
P3D_object *obj = P3D_instance_get_script_object(request->_instance);
@ -428,6 +430,8 @@ handle_request(P3D_request *request) {
P3D_OBJECT_FINISH(obj);
}
}
*/
break;
default:

View File

@ -22,7 +22,7 @@ See pack3d.py for a script that generates these p3d files.
import sys
from direct.showbase import VFSImporter
from direct.showbase.DirectObject import DirectObject
from pandac.PandaModules import VirtualFileSystem, Filename, Multifile, loadPrcFileData, getModelPath, HTTPClient
from pandac.PandaModules import VirtualFileSystem, Filename, Multifile, loadPrcFileData, unloadPrcFile, getModelPath, HTTPClient
from direct.stdpy import file
from direct.task.TaskManagerGlobal import taskMgr
from direct.showbase import AppRunnerGlobal
@ -64,6 +64,9 @@ class AppRunner(DirectObject):
# This is the default requestFunc that is installed if we
# never call setRequestFunc().
def defaultRequestFunc(*args):
if args[1] == 'notify':
# Quietly ignore notifies.
return
print "Ignoring request: %s" % (args,)
self.requestFunc = defaultRequestFunc
@ -149,6 +152,10 @@ class AppRunner(DirectObject):
# for this instance.
self.instanceId = instanceId
# Now that we have an instanceId, we can response to queries
# and such.
self.sendRequest('notify', 'onpythonload')
tokenDict = dict(tokens)
fname = Filename.fromOsSpecific(p3dFilename)
if not p3dFilename:
@ -246,7 +253,7 @@ class AppRunner(DirectObject):
def windowEvent(self, win):
print "Got window event in runp3d"
self.sendRequest('notify', 'window_opened')
self.sendRequest('notify', 'onwindowopen')
def parseSysArgs(self):
""" Converts sys.argv into (p3dFilename, tokens). """