Move DME loading to a background thread

This commit is contained in:
Tad Hardesty 2017-11-16 16:06:29 -08:00
parent 9d2b8bccbd
commit 9ee34bc010
3 changed files with 130 additions and 48 deletions

View file

@ -117,6 +117,7 @@ macro_rules! action {
#[allow(unused_mut)]
fn main() {
// This RefCell is only a minor layer of safety in a wildly unsafe setup
let mut state_cell = RefCell::new(State::new());
macro_rules! state {
() => {&mut *state_cell.borrow_mut()}
@ -359,60 +360,75 @@ impl State {
}
unsafe fn load_env(&mut self, path: PathBuf) {
let self_ptr: *mut Self = self;
let window_ptr = self.widgets.window;
println!("Environment: {}", path.display());
let mut preprocessor;
match dm::preprocessor::Preprocessor::new(path.clone()) {
Err(_) => return show_error(self.widgets.window(), &format!("Could not open for reading:\n{}", path.display())),
Ok(pp) => preprocessor = pp,
};
let path2 = path.clone();
qt::future::spawn(move || -> Option<ObjectTree> {
let mut preprocessor;
match dm::preprocessor::Preprocessor::new(path2.clone()) {
Err(_) => {
show_error(&mut *static_cast_mut(window_ptr), &format!("Could not open for reading:\n{}", path2.display()));
return None;
},
Ok(pp) => preprocessor = pp,
};
let objtree;
match dm::parser::parse(dm::indents::IndentProcessor::new(&mut preprocessor)) {
Err(e) => {
let mut message = format!("\
Could not parse the environment:\n\
{}\n\n\
This may be caused by incorrect or unusual code, but is typically a parser bug. \
Change the code to use a more common form, or report the parsing problem.\n\
", path.display());
let mut message_buf = Vec::new();
let _ = dm::pretty_print_error(&mut message_buf, &preprocessor, &e);
message.push_str(&String::from_utf8_lossy(&message_buf[..]));
return show_error(self.widgets.window(), &message);
},
Ok(t) => objtree = t,
}
// fill the object tree
{
let widget = self.widgets.tree();
widget.clear();
let root = objtree.root();
for &root_child in ["area", "turf", "obj", "mob"].iter() {
let ty = root.child(root_child, &objtree).expect("builtins missing");
let mut root_item = TreeWidgetItem::new(());
root_item.set_text(0, qstr!(&ty.name));
add_children(&mut root_item, ty, &objtree);
widget.add_top_level_item(qt_own!(root_item));
match dm::parser::parse(dm::indents::IndentProcessor::new(&mut preprocessor)) {
Err(e) => {
let mut message = format!("\
Could not parse the environment:\n\
{}\n\n\
This may be caused by incorrect or unusual code, but is typically a parser bug. \
Change the code to use a more common form, or report the parsing problem.\n\
", path2.display());
let mut message_buf = Vec::new();
let _ = dm::pretty_print_error(&mut message_buf, &preprocessor, &e);
message.push_str(&String::from_utf8_lossy(&message_buf[..]));
show_error(&mut *static_cast_mut(window_ptr), &message);
None
},
Ok(t) => Some(t)
}
}
self.config.make_recent(&path);
self.update_recent();
self.config.save();
}, move |objtree| {
let this = &mut *self_ptr;
let objtree = match objtree {
Some(t) => t,
None => return
};
self.env = Some(Environment {
root: path.parent().unwrap().to_owned(),
dme: path,
objtree: objtree,
// fill the object tree
{
let widget = this.widgets.tree();
widget.clear();
let root = objtree.root();
for &root_child in ["area", "turf", "obj", "mob"].iter() {
let ty = root.child(root_child, &objtree).expect("builtins missing");
let mut root_item = TreeWidgetItem::new(());
root_item.set_text(0, qstr!(&ty.name));
add_children(&mut root_item, ty, &objtree);
widget.add_top_level_item(qt_own!(root_item));
}
}
this.config.make_recent(&path);
this.update_recent();
this.config.save();
this.env = Some(Environment {
root: path.parent().unwrap().to_owned(),
dme: path,
objtree: objtree,
});
// un-disable the actions
let actions = this.widgets.menu_file().actions();
for i in 0..actions.count() {
(**actions.at(i)).set_disabled(false);
}
});
// un-disable the actions
let actions = self.widgets.menu_file().actions();
for i in 0..actions.count() {
(**actions.at(i)).set_disabled(false);
}
}
unsafe fn new_map(&mut self) {

65
src/qt-extras/future.rs Normal file
View file

@ -0,0 +1,65 @@
use std::sync::mpsc;
use libc::c_void;
// doesn't check for Send or Sync
pub unsafe fn spawn<F1, F2, R>(task: F1, callback: F2) where
F1: 'static + FnOnce() -> R,
F2: 'static + FnOnce(R),
{
let (tx, rx) = mpsc::channel();
let (task, task_data) = make_callback(move || {
let _ = tx.send(task());
});
let (cb, cb_data) = make_callback(move || {
if let Ok(data) = rx.try_recv() {
callback(data);
}
});
qt_spawn_future(task, task_data, cb, cb_data);
}
unsafe fn make_callback<F: FnOnce()>(f: F) -> (callback_fn, *mut c_void) {
unsafe extern "C" fn callback<F: FnOnce()>(ptr: *mut c_void) {
Box::from_raw(ptr as *mut F)();
}
(callback::<F>, Box::into_raw(Box::new(f)) as *mut c_void)
}
#[allow(non_camel_case_types)]
type callback_fn = unsafe extern "C" fn(*mut c_void);
extern {
fn qt_spawn_future(task: callback_fn, task_data: *mut c_void, cb: callback_fn, cb_data: *mut c_void);
}
cpp! {{
#include <QtCore/QFuture.h>
#include <QtCore/QFutureWatcher.h>
#include <QtConcurrent/QtConcurrent>
extern "C" typedef void (*callback_fn)(void*);
class RustSignaller: public QFutureWatcher<void> {
callback_fn callback;
void* data;
void handleFinished() {
callback(data);
delete this;
}
public:
RustSignaller(callback_fn callback, void* data)
: callback(callback)
, data(data)
{
QObject::connect(this, &QFutureWatcher::finished, this, &RustSignaller::handleFinished);
}
};
extern "C" void qt_spawn_future(callback_fn task, void* taskData, callback_fn cb, void* cbData) {
RustSignaller* signaller = new RustSignaller(cb, cbData);
QFuture<void> future = QtConcurrent::run([=]() {
task(taskData);
});
signaller->setFuture(future);
}
}}

View file

@ -6,6 +6,7 @@ pub extern crate cpp_utils;
#[macro_use] extern crate cpp;
pub mod glium_widget;
pub mod future;
pub use widgets::qt_core as core;
pub use widgets::qt_gui as gui;