inbound scripting controls
This commit is contained in:
parent
cd80487c05
commit
ed037be160
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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). """
|
||||
|
|
|
|||
Loading…
Reference in New Issue