From ed037be1600220bd30d905d8bd9495d0a227facc Mon Sep 17 00:00:00 2001 From: David Rose Date: Sat, 4 Jul 2009 19:12:24 +0000 Subject: [PATCH] inbound scripting controls --- direct/src/plugin/load_plugin.cxx | 24 +++++- direct/src/plugin/p3dInstance.I | 13 +++ direct/src/plugin/p3dInstance.cxx | 16 ++-- direct/src/plugin/p3dInstance.h | 2 + direct/src/plugin/p3dInstanceManager.cxx | 100 +++++++++++++++++++++-- direct/src/plugin/p3dInstanceManager.h | 24 +++++- direct/src/plugin/p3dPythonRun.cxx | 50 +----------- direct/src/plugin/p3dPythonRun.h | 13 +-- direct/src/plugin/p3dSession.cxx | 57 ++----------- direct/src/plugin/p3dSession.h | 13 +-- direct/src/plugin/p3d_lock.h | 50 +++++++++++- direct/src/plugin/p3d_plugin.cxx | 83 +++++++++---------- direct/src/plugin_npapi/ppInstance.cxx | 32 ++++++-- direct/src/plugin_npapi/startup.cxx | 15 +++- direct/src/plugin_standalone/panda3d.cxx | 8 +- direct/src/showutil/runp3d.py | 11 ++- 16 files changed, 315 insertions(+), 196 deletions(-) diff --git a/direct/src/plugin/load_plugin.cxx b/direct/src/plugin/load_plugin.cxx index 9ef36bb23f..a5f4d394b2 100755 --- a/direct/src/plugin/load_plugin.cxx +++ b/direct/src/plugin/load_plugin.cxx @@ -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); diff --git a/direct/src/plugin/p3dInstance.I b/direct/src/plugin/p3dInstance.I index 18b151f438..5e57520da7 100644 --- a/direct/src/plugin/p3dInstance.I +++ b/direct/src/plugin/p3dInstance.I @@ -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 diff --git a/direct/src/plugin/p3dInstance.cxx b/direct/src/plugin/p3dInstance.cxx index 4bd11930ab..751a50f11c 100644 --- a/direct/src/plugin/p3dInstance.cxx +++ b/direct/src/plugin/p3dInstance.cxx @@ -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); } //////////////////////////////////////////////////////////////////// diff --git a/direct/src/plugin/p3dInstance.h b/direct/src/plugin/p3dInstance.h index aecd5068a4..81ecbf4977 100644 --- a/direct/src/plugin/p3dInstance.h +++ b/direct/src/plugin/p3dInstance.h @@ -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); diff --git a/direct/src/plugin/p3dInstanceManager.cxx b/direct/src/plugin/p3dInstanceManager.cxx index e47f49b1af..d3d43331bd 100644 --- a/direct/src/plugin/p3dInstanceManager.cxx +++ b/direct/src/plugin/p3dInstanceManager.cxx @@ -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; +} diff --git a/direct/src/plugin/p3dInstanceManager.h b/direct/src/plugin/p3dInstanceManager.h index 51da747a9d..01f1e6c7d0 100644 --- a/direct/src/plugin/p3dInstanceManager.h +++ b/direct/src/plugin/p3dInstanceManager.h @@ -20,6 +20,7 @@ #include #include +#include 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 NotifyInstances; + NotifyInstances _notify_instances; + P3DConditionVar _notify_ready; + static P3DInstanceManager *_global_ptr; }; diff --git a/direct/src/plugin/p3dPythonRun.cxx b/direct/src/plugin/p3dPythonRun.cxx index 6febd0e624..d8f8b506c4 100755 --- a/direct/src/plugin/p3dPythonRun.cxx +++ b/direct/src/plugin/p3dPythonRun.cxx @@ -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. diff --git a/direct/src/plugin/p3dPythonRun.h b/direct/src/plugin/p3dPythonRun.h index c02cd8ddf5..ff539231f1 100755 --- a/direct/src/plugin/p3dPythonRun.h +++ b/direct/src/plugin/p3dPythonRun.h @@ -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 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; diff --git a/direct/src/plugin/p3dSession.cxx b/direct/src/plugin/p3dSession.cxx index 0551c0dd00..544a0eb687 100644 --- a/direct/src/plugin/p3dSession.cxx +++ b/direct/src/plugin/p3dSession.cxx @@ -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 diff --git a/direct/src/plugin/p3dSession.h b/direct/src/plugin/p3dSession.h index a2e05f22c7..936c439c2d 100644 --- a/direct/src/plugin/p3dSession.h +++ b/direct/src/plugin/p3dSession.h @@ -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; }; diff --git a/direct/src/plugin/p3d_lock.h b/direct/src/plugin/p3d_lock.h index 3843cb1d14..73fa587aaf 100755 --- a/direct/src/plugin/p3d_lock.h +++ b/direct/src/plugin/p3d_lock.h @@ -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 diff --git a/direct/src/plugin/p3d_plugin.cxx b/direct/src/plugin/p3d_plugin.cxx index 6ecd6ee688..8a30707ad7 100644 --- a/direct/src/plugin/p3d_plugin.cxx +++ b/direct/src/plugin/p3d_plugin.cxx @@ -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; } diff --git a/direct/src/plugin_npapi/ppInstance.cxx b/direct/src/plugin_npapi/ppInstance.cxx index bf3f308798..503ce2fc93 100644 --- a/direct/src/plugin_npapi/ppInstance.cxx +++ b/direct/src/plugin_npapi/ppInstance.cxx @@ -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); - } } } diff --git a/direct/src/plugin_npapi/startup.cxx b/direct/src/plugin_npapi/startup.cxx index 37a150c0a0..87343ceb3d 100644 --- a/direct/src/plugin_npapi/startup.cxx +++ b/direct/src/plugin_npapi/startup.cxx @@ -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; } diff --git a/direct/src/plugin_standalone/panda3d.cxx b/direct/src/plugin_standalone/panda3d.cxx index 370a39b198..471969c0ee 100644 --- a/direct/src/plugin_standalone/panda3d.cxx +++ b/direct/src/plugin_standalone/panda3d.cxx @@ -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: diff --git a/direct/src/showutil/runp3d.py b/direct/src/showutil/runp3d.py index a64101b7c2..30e2601347 100755 --- a/direct/src/showutil/runp3d.py +++ b/direct/src/showutil/runp3d.py @@ -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). """