Browse Source

fix up X11 threading issues

David Rose 16 years ago
parent
commit
a86a95b4ec

+ 2 - 1
direct/src/plugin/p3dConditionVar.cxx

@@ -140,7 +140,8 @@ wait(double timeout) {
   ts.tv_nsec += (int)((timeout - seconds) * 1000000.0);
   ts.tv_nsec += (int)((timeout - seconds) * 1000000.0);
 
 
   int result = pthread_cond_timedwait(&_cvar, &_lock, &ts);
   int result = pthread_cond_timedwait(&_cvar, &_lock, &ts);
-  if (result != 0 && result != ETIMEDOUT && errno != ETIMEDOUT) {
+  if (result != 0 && result != ETIMEDOUT) {
+    errno = result;
     perror("pthread_cond_timedwait");
     perror("pthread_cond_timedwait");
     assert(false);
     assert(false);
   }
   }

+ 1 - 0
direct/src/plugin/p3dSession.cxx

@@ -157,6 +157,7 @@ shutdown() {
       result = waitpid(_p3dpython_pid, &status, WNOHANG);
       result = waitpid(_p3dpython_pid, &status, WNOHANG);
     }
     }
 
 
+    nout << "Python process has successfully stopped.\n";
     if (WIFEXITED(status)) {
     if (WIFEXITED(status)) {
       nout << "  exited normally, status = "
       nout << "  exited normally, status = "
            << WEXITSTATUS(status) << "\n";
            << WEXITSTATUS(status) << "\n";

+ 1 - 0
direct/src/plugin/p3dWinSplashWindow.cxx

@@ -60,6 +60,7 @@ P3DWinSplashWindow::
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: P3DWinSplashWindow::set_image_filename
 //     Function: P3DWinSplashWindow::set_image_filename
 //       Access: Public, Virtual
 //       Access: Public, Virtual
+//  Description: Specifies the name of a JPEG image file that is
 //               displayed in the center of the splash window.  If
 //               displayed in the center of the splash window.  If
 //               image_filename_temp is true, the file is immediately
 //               image_filename_temp is true, the file is immediately
 //               deleted after it has been read.
 //               deleted after it has been read.

+ 277 - 70
direct/src/plugin/p3dX11SplashWindow.cxx

@@ -16,7 +16,12 @@
 
 
 #ifdef HAVE_X11
 #ifdef HAVE_X11
 
 
+#include "get_tinyxml.h"
 #include <time.h>
 #include <time.h>
+#include <fcntl.h>
+#include <sys/wait.h>
+#include <sys/select.h>
+#include <signal.h>
 
 
 // Sleeps for a short time.
 // Sleeps for a short time.
 #define MILLISLEEP() \
 #define MILLISLEEP() \
@@ -36,7 +41,10 @@ P3DX11SplashWindow::
 P3DX11SplashWindow(P3DInstance *inst) : 
 P3DX11SplashWindow(P3DInstance *inst) : 
   P3DSplashWindow(inst)
   P3DSplashWindow(inst)
 {
 {
-  INIT_THREAD(_thread);
+  // Init for parent process
+  _subprocess_pid = -1;
+
+  // Init for subprocess
   _display = None;
   _display = None;
   _window = None;
   _window = None;
   _image = NULL;
   _image = NULL;
@@ -49,16 +57,13 @@ P3DX11SplashWindow(P3DInstance *inst) :
   _resized_width = 0;
   _resized_width = 0;
   _resized_height = 0;
   _resized_height = 0;
   _graphics_context = None;
   _graphics_context = None;
-  _thread_running = false;
   _got_install = false;
   _got_install = false;
   _image_filename_changed = false;
   _image_filename_changed = false;
   _image_filename_temp = false;
   _image_filename_temp = false;
   _install_label_changed = false;
   _install_label_changed = false;
   _install_progress = 0.0;
   _install_progress = 0.0;
 
 
-  INIT_LOCK(_install_lock);
-
-  start_thread();
+  start_subprocess();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -68,14 +73,13 @@ P3DX11SplashWindow(P3DInstance *inst) :
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 P3DX11SplashWindow::
 P3DX11SplashWindow::
 ~P3DX11SplashWindow() {
 ~P3DX11SplashWindow() {
-  stop_thread();
-
-  DESTROY_LOCK(_install_lock);
+  stop_subprocess();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: P3DX11SplashWindow::set_image_filename
 //     Function: P3DX11SplashWindow::set_image_filename
 //       Access: Public, Virtual
 //       Access: Public, Virtual
+//  Description: Specifies the name of a JPEG image file that is
 //               displayed in the center of the splash window.  If
 //               displayed in the center of the splash window.  If
 //               image_filename_temp is true, the file is immediately
 //               image_filename_temp is true, the file is immediately
 //               deleted after it has been read.
 //               deleted after it has been read.
@@ -83,21 +87,21 @@ P3DX11SplashWindow::
 void P3DX11SplashWindow::
 void P3DX11SplashWindow::
 set_image_filename(const string &image_filename,
 set_image_filename(const string &image_filename,
                    bool image_filename_temp) {
                    bool image_filename_temp) {
-  ACQUIRE_LOCK(_install_lock);
-  if (_image_filename != image_filename) {
-    _image_filename = image_filename;
-    _image_filename_temp = image_filename_temp;
-    _image_filename_changed = true;
+  if (_subprocess_pid == -1) {
+    return;
   }
   }
-  RELEASE_LOCK(_install_lock);
-
-  MILLISLEEP();
 
 
-  if (!_thread_running && _thread_continue) {
-    // The user must have closed the window.  Let's shut down the
-    // instance, too.
-    _inst->request_stop();
-  }
+  TiXmlDocument doc;
+  TiXmlDeclaration *decl = new TiXmlDeclaration("1.0", "utf-8", "");
+  TiXmlElement *xcommand = new TiXmlElement("command");
+  xcommand->SetAttribute("cmd", "set_image_filename");
+  xcommand->SetAttribute("image_filename", image_filename);
+  xcommand->SetAttribute("image_filename_temp", (int)image_filename_temp);
+  doc.LinkEndChild(decl);
+  doc.LinkEndChild(xcommand);
+  write_xml(_pipe_write, &doc, nout);
+
+  check_stopped();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -108,20 +112,20 @@ set_image_filename(const string &image_filename,
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void P3DX11SplashWindow::
 void P3DX11SplashWindow::
 set_install_label(const string &install_label) {
 set_install_label(const string &install_label) {
-  ACQUIRE_LOCK(_install_lock);
-  if (_install_label != install_label) {
-    _install_label = install_label;
-    _install_label_changed = true;
+  if (_subprocess_pid == -1) {
+    return;
   }
   }
-  RELEASE_LOCK(_install_lock);
 
 
-  MILLISLEEP();
+  TiXmlDocument doc;
+  TiXmlDeclaration *decl = new TiXmlDeclaration("1.0", "utf-8", "");
+  TiXmlElement *xcommand = new TiXmlElement("command");
+  xcommand->SetAttribute("cmd", "set_install_label");
+  xcommand->SetAttribute("install_label", install_label);
+  doc.LinkEndChild(decl);
+  doc.LinkEndChild(xcommand);
+  write_xml(_pipe_write, &doc, nout);
 
 
-  if (!_thread_running && _thread_continue) {
-    // The user must have closed the window.  Let's shut down the
-    // instance, too.
-    _inst->request_stop();
-  }
+  check_stopped();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -131,56 +135,194 @@ set_install_label(const string &install_label) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void P3DX11SplashWindow::
 void P3DX11SplashWindow::
 set_install_progress(double install_progress) {
 set_install_progress(double install_progress) {
-  _got_install = true;
+  if (_subprocess_pid == -1) {
+    return;
+  }
+
+  TiXmlDocument doc;
+  TiXmlDeclaration *decl = new TiXmlDeclaration("1.0", "utf-8", "");
+  TiXmlElement *xcommand = new TiXmlElement("command");
+  xcommand->SetAttribute("cmd", "set_install_progress");
+  xcommand->SetDoubleAttribute("install_progress", install_progress);
+  doc.LinkEndChild(decl);
+  doc.LinkEndChild(xcommand);
+  write_xml(_pipe_write, &doc, nout);
+
+  check_stopped();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: P3DX11SplashWindow::start_subprocess
+//       Access: Private
+//  Description: Spawns the subprocess that runs the window.  We have
+//               to use a subprocess instead of just a sub-thread, to
+//               protect X11 against mutual access.
+////////////////////////////////////////////////////////////////////
+void P3DX11SplashWindow::
+start_subprocess() {
+  assert(_subprocess_pid == -1);
+
+  // Create a directional pipe to send messages to the sub-process.
+  // (We don't need a receive pipe.)
+  int to_fd[2];
+  if (pipe(to_fd) < 0) {
+    perror("failed to create pipe");
+  }
 
 
-  ACQUIRE_LOCK(_install_lock);
-  _install_progress = install_progress;
-  RELEASE_LOCK(_install_lock);
+  // Fork and exec.
+  pid_t child = fork();
+  if (child < 0) {
+    close(to_fd[0]);
+    close(to_fd[1]);
+    perror("fork");
+    return;
+  }
 
 
-  MILLISLEEP();
+  if (child == 0) {
+    // Here we are in the child process.
 
 
-  if (!_thread_running && _thread_continue) {
-    // The user must have closed the window.  Let's shut down the
-    // instance, too.
-    _inst->request_stop();
+    // Open the read end of the pipe, and close the write end.
+    _pipe_read.open_read(to_fd[0]);
+    close(to_fd[1]);
+
+    subprocess_run();
+    _exit(0);
   }
   }
+
+  // In the parent process.
+  _subprocess_pid = child;
+  _pipe_write.open_write(to_fd[1]);
+  close(to_fd[0]);
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: P3DX11SplashWindow::start_thread
+//     Function: P3DX11SplashWindow::stop_subprocess
 //       Access: Private
 //       Access: Private
-//  Description: Spawns the sub-thread.
+//  Description: Terminates the subprocess.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void P3DX11SplashWindow::
 void P3DX11SplashWindow::
-start_thread() {
-  _thread_continue = true;
-  INIT_THREAD(_thread);
-  SPAWN_THREAD(_thread, thread_run, this);
-  if (_thread != 0) {
-    _thread_running = true;
+stop_subprocess() {
+  if (_subprocess_pid == -1) {
+    // Already stopped.
+    return;
+  }
+
+  // Ask the subprocess to stop.
+  TiXmlDocument doc;
+  TiXmlDeclaration *decl = new TiXmlDeclaration("1.0", "utf-8", "");
+  TiXmlElement *xcommand = new TiXmlElement("command");
+  xcommand->SetAttribute("cmd", "exit");
+  doc.LinkEndChild(decl);
+  doc.LinkEndChild(xcommand);
+  write_xml(_pipe_write, &doc, nout);
+
+  // Also close the pipe, to help underscore the point.
+  _pipe_write.close();
+
+  static const int max_wait_ms = 2000;
+  
+  // Wait for a certain amount of time for the process to stop by
+  // itself.
+  struct timeval start;
+  gettimeofday(&start, NULL);
+  int start_ms = start.tv_sec * 1000 + start.tv_usec / 1000;
+  
+  int status;
+  pid_t result = waitpid(_subprocess_pid, &status, WNOHANG);
+  while (result != _subprocess_pid) {
+    if (result == -1) {
+      perror("waitpid");
+      break;
+    }
+    
+    struct timeval now;
+    gettimeofday(&now, NULL);
+    int now_ms = now.tv_sec * 1000 + now.tv_usec / 1000;
+    int elapsed = now_ms - start_ms;
+    
+    if (elapsed > max_wait_ms) {
+      // Tired of waiting.  Kill the process.
+      nout << "Force-killing splash window process, pid " << _subprocess_pid 
+           << "\n" << flush;
+      kill(_subprocess_pid, SIGKILL);
+      start_ms = now_ms;
+    }
+    
+    // Yield the timeslice and wait some more.
+    struct timeval tv;
+    tv.tv_sec = 0;
+    tv.tv_usec = 1;
+    select(0, NULL, NULL, NULL, &tv);
+    result = waitpid(_subprocess_pid, &status, WNOHANG);
+  }
+
+  nout << "Splash window process has successfully stopped.\n";
+  if (WIFEXITED(status)) {
+    nout << "  exited normally, status = "
+         << WEXITSTATUS(status) << "\n";
+  } else if (WIFSIGNALED(status)) {
+    nout << "  signalled by " << WTERMSIG(status) << ", core = " 
+         << WCOREDUMP(status) << "\n";
+  } else if (WIFSTOPPED(status)) {
+    nout << "  stopped by " << WSTOPSIG(status) << "\n";
   }
   }
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: P3DX11SplashWindow::stop_thread
+//     Function: P3DX11SplashWindow::check_stopped
 //       Access: Private
 //       Access: Private
-//  Description: Terminates and joins the sub-thread.
+//  Description: Shuts down the instance if the window is closed
+//               prematurely (for instance, due to user action).
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void P3DX11SplashWindow::
 void P3DX11SplashWindow::
-stop_thread() {
-  _thread_continue = false;
-  MILLISLEEP();
+check_stopped() {
+  if (_subprocess_pid == -1) {
+    // Already stopped.
+    return;
+  }
+
+  int status;
+  int result = waitpid(_subprocess_pid, &status, WNOHANG);
+  if (result == 0) {
+    // Process is still running.
+    return;
+  }
+
+  if (result == -1) {
+    // Error in waitpid.
+    perror("waitpid");
+    return;
+  }
 
 
-  JOIN_THREAD(_thread);
+  // Process has stopped.
+  assert(result == _subprocess_pid);
+
+  nout << "Splash window process has stopped unexpectedly.\n";
+  if (WIFEXITED(status)) {
+    nout << "  exited normally, status = "
+         << WEXITSTATUS(status) << "\n";
+  } else if (WIFSIGNALED(status)) {
+    nout << "  signalled by " << WTERMSIG(status) << ", core = " 
+         << WCOREDUMP(status) << "\n";
+  } else if (WIFSTOPPED(status)) {
+    nout << "  stopped by " << WSTOPSIG(status) << "\n";
+  }
+
+  _subprocess_pid = -1;
+  _inst->request_stop();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: P3DX11SplashWindow::thread_run
+//     Function: P3DX11SplashWindow::subprocess_run
 //       Access: Private
 //       Access: Private
-//  Description: The sub-thread's main run method.
+//  Description: The subprocess's main run method.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void P3DX11SplashWindow::
 void P3DX11SplashWindow::
-thread_run() {
+subprocess_run() {
+  // Since we're now isolated in a subprocess, we can safely make all
+  // the X calls we like, and run independently of the browser
+  // process.
+
   make_window();
   make_window();
   setup_gc();
   setup_gc();
 
 
@@ -190,8 +332,10 @@ thread_run() {
   bool override = true, have_event = false;
   bool override = true, have_event = false;
   string prev_label;
   string prev_label;
   double prev_progress;
   double prev_progress;
-  
-  while (_thread_continue) {
+
+  _subprocess_continue = true;
+  while (_subprocess_continue) {
+    // First, scan for X events.
     have_event = XCheckTypedWindowEvent(_display, _window, Expose, &event)
     have_event = XCheckTypedWindowEvent(_display, _window, Expose, &event)
               || XCheckTypedWindowEvent(_display, _window, GraphicsExpose, &event);
               || XCheckTypedWindowEvent(_display, _window, GraphicsExpose, &event);
     
     
@@ -205,7 +349,6 @@ thread_run() {
       _height = event.xconfigure.height;
       _height = event.xconfigure.height;
     }
     }
     
     
-    ACQUIRE_LOCK(_install_lock);
     double install_progress = _install_progress;
     double install_progress = _install_progress;
     string install_label = _install_label;
     string install_label = _install_label;
     
     
@@ -214,8 +357,6 @@ thread_run() {
     }
     }
     _image_filename_changed = false;
     _image_filename_changed = false;
     
     
-    RELEASE_LOCK(_install_lock);
-    
     if (override || have_event || install_label != prev_label) {
     if (override || have_event || install_label != prev_label) {
       redraw(install_label);
       redraw(install_label);
       override = false;
       override = false;
@@ -228,11 +369,82 @@ thread_run() {
     }
     }
     prev_label = install_label;
     prev_label = install_label;
     prev_progress = install_progress;
     prev_progress = install_progress;
-    MILLISLEEP();
+
+    // Now check for input from the parent.
+    int read_fd = _pipe_read.get_handle();
+    fd_set fds;
+    FD_ZERO(&fds);
+    FD_SET(read_fd, &fds);
+
+    struct timeval tv;
+    tv.tv_sec = 0;
+    tv.tv_usec = 1;  // Sleep a bit to yield the timeslice if there's
+                     // nothing new.
+
+    int result = select(read_fd + 1, &fds, NULL, NULL, &tv);
+    if (result == 1) {
+      // There is some noise on the pipe, so read it.
+      receive_command();
+    } else if (result == -1) {
+      // Error in select.
+      perror("select");
+    }
   }
   }
 
 
   close_window();
   close_window();
-  _thread_running = false;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: P3DX11SplashWindow::receive_command
+//       Access: Private
+//  Description: Receives a command from the parent.
+////////////////////////////////////////////////////////////////////
+void P3DX11SplashWindow::
+receive_command() {
+  TiXmlDocument *doc = read_xml(_pipe_read, cerr);
+  if (doc == NULL) {
+    // Pipe closed or something.
+    _subprocess_continue = false;
+    return;
+  }
+
+  TiXmlElement *xcommand = doc->FirstChildElement("command");
+  if (xcommand != NULL) {
+    const char *cmd = xcommand->Attribute("cmd");
+    if (cmd != NULL) {
+      if (strcmp(cmd, "exit") == 0) {
+        _subprocess_continue = false;
+
+      } else if (strcmp(cmd, "set_image_filename") == 0) {
+        const char *str = xcommand->Attribute("image_filename");
+        int image_filename_temp = 0;
+        xcommand->Attribute("image_filename_temp", &image_filename_temp);
+        if (str != NULL) {
+          if (_image_filename != string(str)) {
+            _image_filename = str;
+            _image_filename_temp = image_filename_temp;
+            _image_filename_changed = true;
+          }
+        }
+
+      } else if (strcmp(cmd, "set_install_label") == 0) {
+        const char *str = xcommand->Attribute("install_label");
+        if (str != NULL) {
+          if (_install_label != string(str)) {
+            _install_label = str;
+            _install_label_changed = true;
+          }
+        }
+
+      } else if (strcmp(cmd, "set_install_progress") == 0) {
+        double install_progress = 0.0;
+        xcommand->Attribute("install_progress", &install_progress);
+
+        _got_install = true;
+        _install_progress = install_progress;
+      }
+    }
+  }
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -348,11 +560,9 @@ setup_gc() {
     return;
     return;
   }
   }
 
 
-  ACQUIRE_LOCK(_install_lock);
   string install_label = _install_label;
   string install_label = _install_label;
   double install_progress = _install_progress;
   double install_progress = _install_progress;
   _install_label_changed = false;
   _install_label_changed = false;
-  RELEASE_LOCK(_install_lock);
   
   
   XFontStruct* fs = XLoadQueryFont(_display, "6x13");
   XFontStruct* fs = XLoadQueryFont(_display, "6x13");
   XGCValues gcval;
   XGCValues gcval;
@@ -455,9 +665,6 @@ update_image_filename(const string &image_filename, bool image_filename_temp) {
   // Now load the image.
   // Now load the image.
   _image = XCreateImage(_display, CopyFromParent, DefaultDepth(_display, _screen), 
   _image = XCreateImage(_display, CopyFromParent, DefaultDepth(_display, _screen), 
                 ZPixmap, 0, (char *) new_data, _image_width, _image_height, 32, 0);
                 ZPixmap, 0, (char *) new_data, _image_width, _image_height, 32, 0);
-
-  nout << "Loaded splash file image: " << image_filename << "\n"
-       << flush;
 }
 }
 
 
 #endif  // HAVE_X11
 #endif  // HAVE_X11

+ 15 - 10
direct/src/plugin/p3dX11SplashWindow.h

@@ -20,7 +20,7 @@
 #ifdef HAVE_X11
 #ifdef HAVE_X11
 
 
 #include "p3dSplashWindow.h"
 #include "p3dSplashWindow.h"
-#include "p3d_lock.h"
+#include "handleStream.h"
 
 
 #include <X11/Xlib.h>
 #include <X11/Xlib.h>
 #include <X11/Xutil.h>
 #include <X11/Xutil.h>
@@ -41,13 +41,19 @@ public:
   virtual void set_install_progress(double install_progress);
   virtual void set_install_progress(double install_progress);
 
 
 private:
 private:
-  void start_thread();
-  void stop_thread();
+  void start_subprocess();
+  void stop_subprocess();
+  void check_stopped();
 
 
 private:
 private:
-  // These methods run only within the window thread.
-  void thread_run();
-  THREAD_CALLBACK_DECLARATION(P3DX11SplashWindow, thread_run);
+  // Data members that are stored in the parent process.
+  pid_t _subprocess_pid;
+  HandleStream _pipe_write;
+
+private:
+  // These methods run only within the subprocess.
+  void subprocess_run();
+  void receive_command();
 
 
   void redraw(string label);
   void redraw(string label);
   void make_window();
   void make_window();
@@ -57,6 +63,9 @@ private:
   void close_window();
   void close_window();
 
 
 private:
 private:
+  // Data members that are stored in the subprocess.
+  bool _subprocess_continue;
+  HandleStream _pipe_read;
   int _width, _height;
   int _width, _height;
   
   
   bool _own_display;
   bool _own_display;
@@ -67,12 +76,9 @@ private:
   bool _install_label_changed;
   bool _install_label_changed;
   string _install_label;
   string _install_label;
   double _install_progress;
   double _install_progress;
-  LOCK _install_lock;
   
   
   string _label_text;
   string _label_text;
 
 
-  bool _thread_continue;
-  bool _thread_running;
   Display *_display;
   Display *_display;
   int _screen;
   int _screen;
   GC _graphics_context;
   GC _graphics_context;
@@ -81,7 +87,6 @@ private:
   int _image_width, _image_height;
   int _image_width, _image_height;
   int _resized_width, _resized_height;
   int _resized_width, _resized_height;
   
   
-  THREAD _thread;
   Window _window;
   Window _window;
 };
 };
 
 

+ 1 - 1
direct/src/plugin/p3d_lock.h

@@ -59,7 +59,7 @@ public:
 // SPAWN_THREAD call.  The wrapper will in turn call the method
 // SPAWN_THREAD call.  The wrapper will in turn call the method
 // function you provide.
 // function you provide.
 #define THREAD_CALLBACK_DECLARATION(class, callback_function) \
 #define THREAD_CALLBACK_DECLARATION(class, callback_function) \
-  static DWORD WINAPI class::                                        \
+  static DWORD WINAPI                           \
   win_ ## callback_function(LPVOID data) {        \
   win_ ## callback_function(LPVOID data) {        \
     ((class *)data)->callback_function();       \
     ((class *)data)->callback_function();       \
     return 0;                                   \
     return 0;                                   \

+ 48 - 18
direct/src/plugin_npapi/ppInstance.cxx

@@ -469,12 +469,12 @@ handle_request(P3D_request *request) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void PPInstance::
 void PPInstance::
 generic_browser_call() {
 generic_browser_call() {
-#ifndef HAS_PLUGIN_THREAD_ASYNC_CALL
+  //#ifndef HAS_PLUGIN_THREAD_ASYNC_CALL
   // If we can't ask Mozilla to call us back using
   // If we can't ask Mozilla to call us back using
   // NPN_PluginThreadAsyncCall(), then we'll do it explicitly now,
   // NPN_PluginThreadAsyncCall(), then we'll do it explicitly now,
   // since we know we're in the main thread here.
   // since we know we're in the main thread here.
   handle_request_loop();
   handle_request_loop();
-#endif
+  //#endif
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -1099,13 +1099,12 @@ handle_request_loop() {
     p3d_inst = P3D_check_request(false);
     p3d_inst = P3D_check_request(false);
   }
   }
 
 
-  // Also check to see if we have any file datas that need streaming.
-  // Only stream up to the front four in the queue, so we don't
-  // overwhelm the browser all at once.
-  size_t num_file_datas = min(_file_datas.size(), (size_t)4);
+  // Also check to see if we have any file_data objects that have
+  // finished and may be deleted.
+  size_t num_file_datas = _file_datas.size();
   size_t i = 0;
   size_t i = 0;
   while (i < num_file_datas) {
   while (i < num_file_datas) {
-    if (_file_datas[i]->feed_data()) {
+    if (!_file_datas[i]->is_done()) {
       // This one keeps going.
       // This one keeps going.
       ++i;
       ++i;
     } else {
     } else {
@@ -1184,6 +1183,13 @@ StreamingFileData(PPDownloadRequest *req, const string &filename,
 
 
   // Then return to the beginning to read the data.
   // Then return to the beginning to read the data.
   _file.seekg(0, ios::beg);
   _file.seekg(0, ios::beg);
+
+  // Now start up the thread.
+  _thread_done = false;
+  _thread_continue = true;
+  INIT_THREAD(_thread);
+
+  SPAWN_THREAD(_thread, thread_run, this);
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -1193,23 +1199,38 @@ StreamingFileData(PPDownloadRequest *req, const string &filename,
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 PPInstance::StreamingFileData::
 PPInstance::StreamingFileData::
 ~StreamingFileData() {
 ~StreamingFileData() {
+  // Time to stop.
+  _thread_continue = false;
+
+  JOIN_THREAD(_thread);
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: PPInstance::StreamingFileData::feed_data
+//     Function: PPInstance::StreamingFileData::is_done
 //       Access: Public
 //       Access: Public
-//  Description: Feeds the next batch of the file to the instance.
-//               Returns true if there is more to come and this method
-//               should be called again, false if we're done.
+//  Description: Returns true if the file has been fully read and this
+//               object is ready to be deleted, or false if there is
+//               more work to do.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 bool PPInstance::StreamingFileData::
 bool PPInstance::StreamingFileData::
-feed_data() {
+is_done() const {
+  return _thread_done;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PPInstance::StreamingFileData::thread_run
+//       Access: Private
+//  Description: The main function of the file thread.  This reads the
+//               file contents and feeds it to the core API.
+////////////////////////////////////////////////////////////////////
+void PPInstance::StreamingFileData::
+thread_run() {
   static const size_t buffer_size = 81920;
   static const size_t buffer_size = 81920;
   char buffer[buffer_size];
   char buffer[buffer_size];
 
 
   _file.read(buffer, buffer_size);
   _file.read(buffer, buffer_size);
   size_t count = _file.gcount();
   size_t count = _file.gcount();
-  if (count != 0) {
+  while (count != 0) {
     _total_count += count;
     _total_count += count;
     bool download_ok = P3D_instance_feed_url_stream
     bool download_ok = P3D_instance_feed_url_stream
       (_p3d_inst, _user_id, P3D_RC_in_progress,
       (_p3d_inst, _user_id, P3D_RC_in_progress,
@@ -1217,11 +1238,20 @@ feed_data() {
 
 
     if (!download_ok) {
     if (!download_ok) {
       // Never mind.
       // Never mind.
-      return false;
+      _thread_done = true;
+      return;
     }
     }
 
 
-    // So far, so good.
-    return true;
+    if (!_thread_continue) {
+      // Interrupted by the main thread.  Presumably we're being shut
+      // down.
+      _thread_done = true;
+      return;
+    }
+
+    // So far, so good.  Read some more.
+    _file.read(buffer, buffer_size);
+    count = _file.gcount();
   }
   }
 
 
   // End of file.
   // End of file.
@@ -1234,6 +1264,6 @@ feed_data() {
   P3D_instance_feed_url_stream
   P3D_instance_feed_url_stream
     (_p3d_inst, _user_id, result, 0, _total_count, NULL, 0);
     (_p3d_inst, _user_id, result, 0, _total_count, NULL, 0);
 
 
-  // We're done.
-  return false;
+  // All done.
+  _thread_done = true;
 }
 }

+ 11 - 1
direct/src/plugin_npapi/ppInstance.h

@@ -18,6 +18,7 @@
 #include "nppanda3d_common.h"
 #include "nppanda3d_common.h"
 #include "fileSpec.h"
 #include "fileSpec.h"
 #include "get_tinyxml.h"
 #include "get_tinyxml.h"
+#include "p3d_lock.h"
 
 
 #include <vector>
 #include <vector>
 
 
@@ -107,15 +108,24 @@ private:
                       P3D_instance *p3d_inst);
                       P3D_instance *p3d_inst);
     ~StreamingFileData();
     ~StreamingFileData();
 
 
-    bool feed_data();
+    bool is_done() const;
 
 
   private:
   private:
+    void thread_run();
+    THREAD_CALLBACK_DECLARATION(PPInstance::StreamingFileData, thread_run);
+
+  private:
+    bool _thread_done;
+    bool _thread_continue;
+
     P3D_instance *_p3d_inst;
     P3D_instance *_p3d_inst;
     int _user_id;
     int _user_id;
     string _filename;
     string _filename;
     ifstream _file;
     ifstream _file;
     size_t _file_size;
     size_t _file_size;
     size_t _total_count;
     size_t _total_count;
+
+    THREAD _thread;
   };
   };
 
 
   typedef vector<StreamingFileData *> FileDatas;
   typedef vector<StreamingFileData *> FileDatas;

+ 1 - 0
direct/src/plugin_npapi/startup.cxx

@@ -286,6 +286,7 @@ NPP_WriteReady(NPP instance, NPStream *stream) {
 int32
 int32
 NPP_Write(NPP instance, NPStream *stream, int32 offset, 
 NPP_Write(NPP instance, NPStream *stream, int32 offset, 
           int32 len, void *buffer) {
           int32 len, void *buffer) {
+  //  logfile << "Write " << stream->url << ", " << len << "\n" << flush;
   PPInstance::generic_browser_call();
   PPInstance::generic_browser_call();
   PPInstance *inst = (PPInstance *)(instance->pdata);
   PPInstance *inst = (PPInstance *)(instance->pdata);
   assert(inst != NULL);
   assert(inst != NULL);