Ruby: scripts run in a separate thread

Originally committed to SVN as r921.
This commit is contained in:
pomyk 2007-02-02 19:40:50 +00:00
parent 6da6f1bc57
commit 2f52b36910
5 changed files with 102 additions and 43 deletions

View file

@ -366,9 +366,7 @@ namespace Automation4 {
if (script_finished) {
if (!debug_visible) {
if(IsModal())
EndModal(0);
else Show(false);
EndModal(0);
} else {
cancel_button->Enable(true);
cancel_button->SetLabel(_("Close"));
@ -452,10 +450,7 @@ namespace Automation4 {
cancelled = true;
cancel_button->Enable(false);
} else {
if(this->IsModal())
EndModal(0);
else
Show(false);
EndModal(0);
}
}

View file

@ -232,6 +232,7 @@ namespace Automation4 {
void OnIdle(wxIdleEvent &evt);
void OnConfigDialog(ShowConfigDialogEvent &evt);
void DoUpdateDisplay();
protected:
volatile bool cancelled;
@ -241,7 +242,6 @@ namespace Automation4 {
virtual ~ProgressSink();
public:
void DoUpdateDisplay();
void SetProgress(float _progress);
void SetTask(const wxString &_task);
void SetTitle(const wxString &_title);

View file

@ -56,7 +56,7 @@ namespace Automation4 {
RubyScript * RubyScript::inst = NULL; // current Ruby Script
RubyProgressSink* RubyProgressSink::inst = NULL;
VALUE RubyScript::RubyAegisub;
RubyAssFile *RubyAssFile::raf = NULL;
// RubyAssFile *RubyAssFile::raf = NULL;
// RubyScriptReader
RubyScriptReader::RubyScriptReader(const wxString &filename)
@ -137,7 +137,8 @@ namespace Automation4 {
rb_protect(rbLoadWrapper, rb_str_new2(t), &status);
if(status > 0) // something bad happened (probably parsing error)
{
//throw StringValueCStr(ruby_errinfo);
VALUE err = rb_errinfo();
throw StringValueCStr(err);
}
VALUE global_var = rb_gv_get("$script_name");
@ -281,11 +282,20 @@ namespace Automation4 {
return true;
try {
RubyProgressSink::inst = NULL;
RubyAssFile *subsobj = new RubyAssFile(subs, true, true);
VALUE sel = CreateIntegerArray(selected); // selected items
RubyObjects::Get()->Register(sel);
VALUE result = rbFunCall(rb_mKernel, rb_to_id(validation_fun), 3, subsobj->rbAssFile, sel, rb_int2inum(active));
RubyObjects::Get()->Unregister(sel);
VALUE *argv = ALLOCA_N(VALUE, 3);
argv[0] = subsobj->rbAssFile;
argv[1] = CreateIntegerArray(selected); // selected items;
argv[2] = INT2FIX(active);
RubyCallArguments arg(rb_mKernel, rb_to_id(validation_fun), 3, argv);
VALUE result;
RubyThreadedCall call(&arg, &result);
wxThread::ExitCode code = call.Wait();
if(code)
{
return false;
}
if(result != Qnil && result != Qfalse)
return true;
}catch (const char* e) {
@ -302,26 +312,67 @@ namespace Automation4 {
delete RubyProgressSink::inst;
RubyProgressSink::inst = new RubyProgressSink(progress_parent, false);
RubyProgressSink::inst->SetTitle(GetName());
RubyProgressSink::inst->Show(true);
// do call
RubyAssFile *subsobj = new RubyAssFile(subs, true, true);
VALUE sel = CreateIntegerArray(selected); // selected items
RubyObjects::Get()->Register(sel);
VALUE result = rbFunCall(rb_mKernel, rb_to_id(macro_fun), 3, subsobj->rbAssFile, sel, rb_int2inum(active));
RubyObjects::Get()->Unregister(sel);
if(result != Qnil && result != Qfalse)
VALUE *argv = ALLOCA_N(VALUE, 3);
argv[0] = subsobj->rbAssFile;
argv[1] = CreateIntegerArray(selected); // selected items;
argv[2] = INT2FIX(active);
RubyCallArguments arg(rb_mKernel, rb_to_id(macro_fun), 3, argv);
VALUE result;
RubyThreadedCall call(&arg, &result);
RubyProgressSink::inst->ShowModal();
wxThread::ExitCode code = call.Wait();
RubyProgressSink::inst = NULL;
if(code)
{
if(TYPE(result) == T_STRING)
throw StringValueCStr(result);
else throw "Unknown Error";
}
else if(result != Qnil && result != Qfalse)
{
subsobj->RubyUpdateAssFile(result);
}
delete subsobj;
} catch (const char* e) {
wxString *err = new wxString(e, wxConvUTF8);
wxMessageBox(*err, _T("Error running macro"),wxICON_ERROR | wxOK);
}
RubyProgressSink::inst->script_finished = true;
}
RubyThreadedCall::RubyThreadedCall(RubyCallArguments *a, VALUE *res)
: wxThread(wxTHREAD_JOINABLE)
,args(a), result(res)
{
int prio = Options.AsInt(_T("Automation Thread Priority"));
if (prio == 0) prio = 50; // normal
else if (prio == 1) prio = 30; // below normal
else if (prio == 2) prio = 10; // lowest
else prio = 50; // fallback normal
Create();
SetPriority(prio);
Run();
}
wxThread::ExitCode RubyThreadedCall::Entry()
{
int error = 0;
*result = rb_protect(rbCallWrapper, reinterpret_cast<VALUE>(args), &error);
if(RubyProgressSink::inst)
{
RubyProgressSink::inst->script_finished = true;
wxWakeUpIdle();
}
if(error) {
*result = rb_errinfo();
return (wxThread::ExitCode)1;
}
return (wxThread::ExitCode)0;
}
// RubyFeatureFilter
RubyFeatureFilter::RubyFeatureFilter(const wxString &_name, const wxString &_description,
int merit, VALUE _filter_fun, VALUE _dialog_fun)
@ -363,19 +414,32 @@ namespace Automation4 {
delete RubyProgressSink::inst;
RubyProgressSink::inst = new RubyProgressSink(export_dialog, false);
RubyProgressSink::inst->SetTitle(GetName());
RubyProgressSink::inst->Show(true);
RubyAssFile *subsobj = new RubyAssFile(subs, true/*modify*/, false/*undo*/);
VALUE result = rbFunCall(rb_mKernel, rb_to_id(filter_fun), 2, subsobj->rbAssFile, Qnil /* config */);
if(result != Qnil && result != Qfalse)
VALUE *argv = ALLOCA_N(VALUE, 2);
argv[0] = subsobj->rbAssFile;
argv[1] = Qnil; // config
RubyCallArguments arg(rb_mKernel, rb_to_id(filter_fun), 2, argv);
VALUE result;
RubyThreadedCall call(&arg, &result);
RubyProgressSink::inst->ShowModal();
wxThread::ExitCode code = call.Wait();
RubyProgressSink::inst = NULL;
if(code)
{
if(TYPE(result) == T_STRING)
throw StringValueCStr(result);
else throw "Unknown Error";
}
else if(result != Qnil && result != Qfalse)
{
subsobj->RubyUpdateAssFile(result);
}
delete subsobj;
} catch (const char* e) {
wxString *err = new wxString(e, wxConvUTF8);
wxMessageBox(*err, _T("Error running filter"),wxICON_ERROR | wxOK);
}
RubyProgressSink::inst->script_finished = true;
}
ScriptConfigDialog* RubyFeatureFilter::GenerateConfigDialog(wxWindow *parent)
@ -422,8 +486,6 @@ namespace Automation4 {
{
float _progr = rb_num2dbl(progress);
RubyProgressSink::inst->SetProgress(_progr);
RubyProgressSink::inst->DoUpdateDisplay();
wxSafeYield(RubyProgressSink::inst);
return Qtrue;
}
@ -431,7 +493,6 @@ namespace Automation4 {
{
wxString _t(StringValueCStr(task), wxConvUTF8);
RubyProgressSink::inst->SetTask(_t);
RubyProgressSink::inst->DoUpdateDisplay();
return Qtrue;
}
@ -439,8 +500,6 @@ namespace Automation4 {
{
wxString _t(StringValueCStr(title), wxConvUTF8);
RubyProgressSink::inst->SetTitle(_t);
//wxSafeYield(RubyProgressSink::inst);
RubyProgressSink::inst->DoUpdateDisplay();
return Qtrue;
}
@ -461,8 +520,6 @@ namespace Automation4 {
else args[1] = args[0];
wxString _m(StringValueCStr(args[1]), wxConvUTF8);
RubyProgressSink::inst->AddDebugOutput(_m);
RubyProgressSink::inst->DoUpdateDisplay();
wxSafeYield(RubyProgressSink::inst);
return Qtrue;
}
@ -564,7 +621,8 @@ namespace Automation4 {
VALUE result;
result = rb_protect(rbCallWrapper, reinterpret_cast<VALUE>(&arg), &error);
if(error) {
//throw StringValueCStr(ruby_errinfo);
VALUE err = rb_errinfo();
throw StringValueCStr(err);
}
return result;
}

View file

@ -76,12 +76,12 @@ namespace Automation4 {
static int RubyUnparseTagData();
static int RubySetUndoPoint();
~RubyAssFile();
public:
void RubyUpdateAssFile(VALUE subtitles);
static VALUE AssEntryToRuby(AssEntry *e); // makes a Ruby representation of AssEntry
static AssEntry *RubyToAssEntry(VALUE ass_entry); // creates an AssEntry object from a Ruby representation
RubyAssFile(AssFile *_ass, bool _can_modify, bool _can_set_undo);
~RubyAssFile();
static RubyAssFile *raf;
VALUE rbAssFile;
@ -245,6 +245,17 @@ namespace Automation4 {
RubyCallArguments(VALUE _recv, ID _id, int _n, VALUE *_argv);
};
// A single call to a Ruby function, run inside a separate thread.
// This object should be created on the stack in the function that does the call.
class RubyThreadedCall : public wxThread {
private:
RubyCallArguments *args;
VALUE *result;
public:
RubyThreadedCall(RubyCallArguments *args, VALUE *result);
virtual ExitCode Entry();
};
VALUE rbCallWrapper(VALUE arg);
VALUE rbExecWrapper(VALUE arg);
VALUE rbLoadWrapper(VALUE arg);

View file

@ -337,8 +337,8 @@ namespace Automation4 {
// If the first line is dialogue we leave header from the original (styles, info, etc)
void RubyAssFile::RubyUpdateAssFile(VALUE subtitles)
{
RubyObjects::Get()->Register(subtitles);
int size = rb_num2long(rb_funcall(subtitles, rb_to_id(rb_str_new2("size")), 0));
//RubyObjects::Get()->Register(subtitles);
int size = RARRAY(subtitles)->len;
if(size <= 0) return; // empty - leave the original
@ -378,7 +378,7 @@ namespace Automation4 {
rb_set_errinfo(Qnil);
}
}
RubyObjects::Get()->Unregister(subtitles);
//RubyObjects::Get()->Unregister(subtitles);
}
int RubyAssFile::RubyParseTagData()
@ -401,7 +401,6 @@ namespace Automation4 {
RubyAssFile::~RubyAssFile()
{
RubyObjects::Get()->Unregister(rbAssFile);
}
RubyAssFile::RubyAssFile(AssFile *_ass, bool _can_modify, bool _can_set_undo)
@ -409,12 +408,8 @@ namespace Automation4 {
, can_modify(_can_modify)
, can_set_undo(_can_set_undo)
{
if(RubyAssFile::raf)
delete RubyAssFile::raf; // delete previous if there is one
RubyAssFile::raf = this; // set pointer to this obj
rbAssFile = rb_ary_new2(ass->Line.size());
RubyObjects::Get()->Register(rbAssFile);
std::list<AssEntry*>::iterator entry;
int status;