fix up X11 threading issues

This commit is contained in:
David Rose 2009-07-20 23:03:23 +00:00
parent 429d3306a1
commit a86a95b4ec
9 changed files with 362 additions and 106 deletions

View File

@ -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);
} }

View File

@ -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";

View File

@ -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.

View File

@ -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_subprocess();
start_thread();
} }
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
@ -68,14 +73,13 @@ P3DX11SplashWindow(P3DInstance *inst) :
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
P3DX11SplashWindow:: P3DX11SplashWindow::
~P3DX11SplashWindow() { ~P3DX11SplashWindow() {
stop_thread(); stop_subprocess();
DESTROY_LOCK(_install_lock);
} }
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
// 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 (_subprocess_pid == -1) {
if (_image_filename != image_filename) { return;
_image_filename = image_filename;
_image_filename_temp = image_filename_temp;
_image_filename_changed = true;
} }
RELEASE_LOCK(_install_lock);
MILLISLEEP(); 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);
if (!_thread_running && _thread_continue) { check_stopped();
// The user must have closed the window. Let's shut down the
// instance, too.
_inst->request_stop();
}
} }
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
@ -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 (_subprocess_pid == -1) {
if (_install_label != install_label) { return;
_install_label = install_label;
_install_label_changed = true;
} }
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) { check_stopped();
// The user must have closed the window. Let's shut down the
// instance, too.
_inst->request_stop();
}
} }
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
@ -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;
}
ACQUIRE_LOCK(_install_lock); TiXmlDocument doc;
_install_progress = install_progress; TiXmlDeclaration *decl = new TiXmlDeclaration("1.0", "utf-8", "");
RELEASE_LOCK(_install_lock); 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);
MILLISLEEP(); check_stopped();
}
if (!_thread_running && _thread_continue) { ////////////////////////////////////////////////////////////////////
// The user must have closed the window. Let's shut down the // Function: P3DX11SplashWindow::start_subprocess
// instance, too. // Access: Private
_inst->request_stop(); // 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");
}
// Fork and exec.
pid_t child = fork();
if (child < 0) {
close(to_fd[0]);
close(to_fd[1]);
perror("fork");
return;
}
if (child == 0) {
// Here we are in the child process.
// 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::stop_subprocess
// Access: Private
// Description: Terminates the subprocess.
////////////////////////////////////////////////////////////////////
void P3DX11SplashWindow::
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::start_thread // Function: P3DX11SplashWindow::check_stopped
// Access: Private // Access: Private
// Description: Spawns the sub-thread. // Description: Shuts down the instance if the window is closed
// prematurely (for instance, due to user action).
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
void P3DX11SplashWindow:: void P3DX11SplashWindow::
start_thread() { check_stopped() {
_thread_continue = true; if (_subprocess_pid == -1) {
INIT_THREAD(_thread); // Already stopped.
SPAWN_THREAD(_thread, thread_run, this); return;
if (_thread != 0) {
_thread_running = true;
} }
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;
}
// 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::stop_thread // Function: P3DX11SplashWindow::subprocess_run
// Access: Private // Access: Private
// Description: Terminates and joins the sub-thread. // Description: The subprocess's main run method.
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
void P3DX11SplashWindow:: void P3DX11SplashWindow::
stop_thread() { subprocess_run() {
_thread_continue = false; // Since we're now isolated in a subprocess, we can safely make all
MILLISLEEP(); // the X calls we like, and run independently of the browser
// process.
JOIN_THREAD(_thread);
}
////////////////////////////////////////////////////////////////////
// Function: P3DX11SplashWindow::thread_run
// Access: Private
// Description: The sub-thread's main run method.
////////////////////////////////////////////////////////////////////
void P3DX11SplashWindow::
thread_run() {
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

View File

@ -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 start_subprocess();
void stop_thread(); void stop_subprocess();
void check_stopped();
private: private:
// These methods run only within the window thread. // Data members that are stored in the parent process.
void thread_run(); pid_t _subprocess_pid;
THREAD_CALLBACK_DECLARATION(P3DX11SplashWindow, thread_run); 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;
}; };

View File

@ -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; \

View File

@ -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. // Also check to see if we have any file_data objects that have
// Only stream up to the front four in the queue, so we don't // finished and may be deleted.
// overwhelm the browser all at once. size_t num_file_datas = _file_datas.size();
size_t num_file_datas = min(_file_datas.size(), (size_t)4);
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. // Description: Returns true if the file has been fully read and this
// Returns true if there is more to come and this method // object is ready to be deleted, or false if there is
// should be called again, false if we're done. // 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. if (!_thread_continue) {
return true; // 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. // All done.
return false; _thread_done = true;
} }

View File

@ -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;

View File

@ -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);