# HG changeset patch # User Daniel J Sebald # Date 1366261994 18000 # Node ID 58dd034ecb472051b9fbaef48e5f8e6e7526f8ef # Parent e192525236ad2d32978fe1ed9cf5b44a712ee682 New background command queue for GUI to send commands and receive data. * file-editor-tab.cc (file_editor_tab::file_editor_tab): Added file_editor pointer to constructor. Connect background command signal to file_editor equivalent signal. (file_editor_tab::run_file_callback, file_editor_tab::add_breakpoint_callback, file_editor_tab::remove_breakpoint_callback, file_editor_tab::remove_all_breakpoints_callback): Remove. (file_editor_tab::request_add_breakpoint): Remove post_event, queue 'dbstop ()' command. (file_editor_tab::request_remove_breakpoint): Remove post_event, queue 'dbclear ()' command. (file_editor_tab::remove_all_breakpoints): Remove post_event, queue command 'dbclear ()' command. (file_editor_tab::add_all_breakpoints): New. Gets octave_value for 'file' and 'line', converts 'line' to array for 'file' name match, and calls do_breakpoint_marker for each entry in line number array. (file_editor_tab::load_file): Issue 'dbstatus()' command to background queue. * file-editor-tab.h: Add and "oct-obj.h" headers. (file_editor_tab::run_file_callback, file_editor_tab::add_breakpoint_callback, file_editor_tab::remove_breakpoint_callback, file_editor_tab::remove_all_breakpoints_callback): Remove. (file_editor_tab::add_all_breakpoints): New. * file-editor.cc (file_editor::request_new_file, file_editor::request_open_file): Add 'this' to list of file_editor_tab arguments. (file_editor::add_file_editor_tab): Connect have_octave_command_result file_editor signal to add_all_breakpoints file_editor_tab slot. * file-editor.h (file_editor::queue_background_octave_command, file_editor::have_octave_command_result): New signals. * main-window.cc (main_window::handle_save_workspace_request): Remove post_event, queue 'save ()' command. (main_window::handle_load_workspace_request): Remove post_event, queue 'load ()' command. (main_window::handle_clear_workspace_request): Remove post_event, queue 'clear ()' command. (main_window::handle_rename_variable_request): Remove post_event, queue assignment (=) command followed by 'clear()' command. (main_window::handle_clear_command_window_request): Remove post_event, queue 'clc ()' command. (main_window::handle_clear_history_request): Remove post_event, queue 'history ('-c')' command. (main_window::browse_for_directory): Remove post_event, queue 'cd ()' command. (main_window::set_current_working_directory): Remove post_event, queue 'cd ()' command. (main_window::debug_continue, main_window::debug_step_into, main_window::debug_step_out): Remove post_event, queue 'dbcont ()' command. (main_window::debug_quit): Remove post_event, queue 'dbquit ()' command. (main_window::closeEvent): Remove post_event, queue 'exit ()' command. (main_window::save_workspace_callback, main_window::load_workspace_callback, main_window::clear_workspace_callback, main_window::rename_variable_callback, main_window::clear_command_window_callback, main_window::clear_history_callback, main_window::change_directory_callback, main_window::debug_continue_callback, main_window::debug_continue_callback, main_window::debug_step_into_callback, main_window::debug_step_over_callback, main_window::debug_step_out_callback, main_window::debug_quit_callback, main_window::exit_callback): Remove. (main_window::construct_octave_qt_link): Connect signal queue_background_octave_command to slot handle_queue_octave_command for main window and _octave_qt_link and file editor window and _octave_qt_link. Connect _octave_qt_link have_octave_command_result signal to file editor equivalent. * main_window.h (main_window : public QMainWindow): Remove all member functions listed above for main_window.cc. Add queue_background_octave_command member. * octave-qt-link.cc (octave_qt_link::octave_qt_link): Register meta type octave_value_list, connect transmit_octave_command_result to receive_octave_command_result, connect finished_with_command_result to handle_finished_with_command_result. (octave_qt_link::do_process_events): New. Virtual override. Lock mutex, get command, unlock mutex, evaluate command with eval_string (), lock mutex, signal across thread, wait, unlock mutex. (octave_qt_link::handle_queue_octave_command): New. Lock mutex, place command info in queue, unlock mutex. (octave_qt_link::receive_octave_command_result): New. Emit signal to GUI object connections, signal finished with data. (octave_qt_link::handle_finished_with_command_result): New. Try locking mutex. If fail after 5 seconds, indicate connection lost. If success, unlock mutex and wake Octave worker thread. * octave-qt-link.h: Add and "oct-obj.h" headers. (octave_qt_link : public QObject, public octave_link): Add command status enumeration. Add command_info structure. Add QList to hold command_info. New do_process_events () member. Add background_queue_mutex, events_process_mutex, and events_process_waitcondition Qt members. New signals and slots as described for octave-qt-link.cc above. * octave-link.h (octave_link): Change do_process_events to virtual. diff -r e192525236ad -r 58dd034ecb47 libgui/src/m-editor/file-editor-tab.cc --- a/libgui/src/m-editor/file-editor-tab.cc Tue Apr 16 21:34:39 2013 +0200 +++ b/libgui/src/m-editor/file-editor-tab.cc Thu Apr 18 00:13:14 2013 -0500 @@ -47,14 +47,20 @@ #include "file-editor-tab.h" #include "file-editor.h" +#if USE_CALLBACKS #include "octave-link.h" +#else +#include "octave-qt-link.h" +#include "ov-struct.h" +#endif #include "debug.h" #include "oct-env.h" // Make parent null for the file editor tab so that warning // WindowModal messages don't affect grandparents. -file_editor_tab::file_editor_tab (QString directory) +file_editor_tab::file_editor_tab (file_editor *p, QString directory) + : QWidget (p) { // Make sure there is a slash at the end of the directory name // for identification when saved later. @@ -121,6 +127,14 @@ QSettings *settings = resource_manager::get_settings (); if (settings) notice_settings (settings); + + // For issuing debugger-related commands to the Octave core. + connect (this, + SIGNAL (queue_background_octave_command (const QString&, + const int)), + p, + SIGNAL (queue_background_octave_command (const QString&, + const int))); } file_editor_tab::~file_editor_tab () @@ -350,12 +364,13 @@ delete printer; } - +#if USE_CALLBACKS void file_editor_tab::run_file_callback (void) { // Maybe someday we will do something here? } +#endif void file_editor_tab::run_file (const QWidget* ID) @@ -374,10 +389,12 @@ function_name.chop (file_info.suffix ().length () + 1); emit process_octave_code (QString ("cd \'%1\'\n%2\n") .arg(path).arg (function_name)); - + +#if USE_CALLBACKS // TODO: Sending a run event crashes for long scripts. Find out why. // octave_link::post_event // (this, &file_editor_tab::run_file_callback, _file_name.toStdString ())); +#endif } void @@ -431,6 +448,7 @@ _edit_area->markerDeleteAll (bookmark); } +#if USE_CALLBACKS void file_editor_tab::add_breakpoint_callback (const bp_info& info) { @@ -468,6 +486,7 @@ if (intmap.size() > 0) _edit_area->markerDeleteAll (breakpoint); } +#endif void file_editor_tab::request_add_breakpoint (int line) @@ -479,10 +498,19 @@ // We have to cut off the suffix, because octave appends it. function_name.chop (file_info.suffix ().length () + 1); +#if USE_CALLBACKS bp_info info (path, function_name, line); octave_link::post_event (this, &file_editor_tab::add_breakpoint_callback, info); +#else + QString command; + command = "dbstop (\"" + function_name + "\", "; + command += QString ().setNum (line) + ");"; + + // There is only one output argument for dbstop. + emit queue_background_octave_command (command, 1); +#endif } void @@ -495,10 +523,19 @@ // We have to cut off the suffix, because octave appends it. function_name.chop (file_info.suffix ().length () + 1); +#if USE_CALLBACKS bp_info info (path, function_name, line); octave_link::post_event (this, &file_editor_tab::remove_breakpoint_callback, info); +#else + QString command; + command = "dbclear (\"" + function_name + "\", "; + command += QString ().setNum (line + 1) + ");"; + + // There are no output arguments for dbclear. + emit queue_background_octave_command (command, 0); +#endif } void @@ -556,12 +593,56 @@ // We have to cut off the suffix, because octave appends it. function_name.chop (file_info.suffix ().length () + 1); +#if USE_CALLBACKS bp_info info (path, function_name, 0); octave_link::post_event (this, &file_editor_tab::remove_all_breakpoints_callback, info); +#else + QString command; + command = "dbclear (\"" + function_name + "\");"; + + // There are no output arguments for dbclear. + emit queue_background_octave_command (command, 0); +#endif } +#if !USE_CALLBACKS +void +file_editor_tab::add_all_breakpoints (const QObject *ID, const QString& command, + const int status, + const octave_value_list& output) +{ + + // FIXME: Would like to use ID to avoid running code if possible. + // However, because of the SIGNAL/SIGNAL connection the sender that + // is stored in the queue is not the sender of the original signal. + // Either signal directly to the octave_qt_link, or place the original + // sender as an argument variable to queue_background_octave_command. + // + // Really don't need the ID in this case because dbstatus contains + // file information. +// if (ID != this) +// return; + if (command.contains ("dbstatus") && status == octave_qt_link::CommandOK) + { + octave_value file = output (0).map_value ().contents ("file"); + octave_value line = output (0).map_value ().contents ("line"); + string_vector svfile = file.all_strings (); + for (octave_idx_type i = 0; i < svfile.length (); i++) + { + if (svfile[i] == _file_name.toStdString ()) + { + octave_value_list linelst = line.list_value (); + NDArray linenr = linelst (i).array_value (); + for (octave_idx_type j = 0; j < linenr.length (); j++) + do_breakpoint_marker (true, this, linenr (j) - 1); + } + } + } +} +#endif + void file_editor_tab::comment_selected_text (const QWidget* ID) { @@ -774,7 +855,7 @@ } QString -file_editor_tab::load_file(const QString& fileName) +file_editor_tab::load_file (const QString& fileName) { QFile file (fileName); if (!file.open (QFile::ReadOnly)) @@ -791,6 +872,12 @@ update_window_title (false); // window title (no modification) _edit_area->setModified (false); // loaded file is not modified yet +#if USE_CALLBACKS +#else + // Inquire from Octave core any breakpoints that are set. + emit queue_background_octave_command ("dbstatus ();", 1); +#endif + return QString (); } diff -r e192525236ad -r 58dd034ecb47 libgui/src/m-editor/file-editor-tab.h --- a/libgui/src/m-editor/file-editor-tab.h Tue Apr 16 21:34:39 2013 +0200 +++ b/libgui/src/m-editor/file-editor-tab.h Thu Apr 18 00:13:14 2013 -0500 @@ -30,13 +30,19 @@ #include #include "find-dialog.h" +//#include "octave-qt-link.h" +// FIXME -- we should not be including config.h in header files. +#ifdef HAVE_CONFIG_H +#include +#endif +#include "oct-obj.h" class file_editor; class file_editor_tab : public QWidget { Q_OBJECT public: - file_editor_tab (QString directory = ""); + file_editor_tab (file_editor *p, QString directory = ""); ~file_editor_tab (); public slots: @@ -73,6 +79,8 @@ void next_breakpoint (const QWidget* ID); void previous_breakpoint (const QWidget* ID); void remove_all_breakpoints (const QWidget* ID); + void add_all_breakpoints (const QObject *ID, const QString& command, + const int status, const octave_value_list& output); void comment_selected_text (const QWidget* ID); void uncomment_selected_text (const QWidget* ID); @@ -99,6 +107,9 @@ void mru_add_file (const QString& file_name); void editor_check_conflict_save (const QString& saveFileName, bool remove_on_success); void process_octave_code (const QString& command); +#if !USE_CALLBACKS + void queue_background_octave_command (const QString& command, const int nargout); +#endif protected: void closeEvent (QCloseEvent *event); @@ -142,10 +153,12 @@ int check_file_modified (); void do_comment_selected_text (bool comment); +#if USE_CALLBACKS void run_file_callback (void); void add_breakpoint_callback (const bp_info& info); void remove_breakpoint_callback (const bp_info& info); void remove_all_breakpoints_callback (const bp_info& info); +#endif void center_current_line (); QsciScintilla * _edit_area; diff -r e192525236ad -r 58dd034ecb47 libgui/src/m-editor/file-editor.cc --- a/libgui/src/m-editor/file-editor.cc Tue Apr 16 21:34:39 2013 +0200 +++ b/libgui/src/m-editor/file-editor.cc Thu Apr 18 00:13:14 2013 -0500 @@ -138,7 +138,7 @@ // editor tab has yet to be created and there is no object to // pass a signal to. Hence, functionality is here. - file_editor_tab *fileEditorTab = new file_editor_tab (ced); + file_editor_tab *fileEditorTab = new file_editor_tab (this, ced); if (fileEditorTab) { add_file_editor_tab (fileEditorTab, ""); // new tab with empty title @@ -217,7 +217,7 @@ } else { - file_editor_tab *fileEditorTab = new file_editor_tab (); + file_editor_tab *fileEditorTab = new file_editor_tab (this); if (fileEditorTab) { QString result = fileEditorTab->load_file(openFileName); @@ -989,6 +989,19 @@ connect (this, SIGNAL (fetab_do_breakpoint_marker (bool, const QWidget *, int)), f, SLOT (do_breakpoint_marker (bool, const QWidget *, int))); + // This can be done after constructing the file_editor_tab, which does a + // load file and queues a background dbstatus command. The following signal + // will not be emitted until the GUI thread gets around to handling the + // recieved command result, which happens after this function returns. The + // file management construction steps could be cleaned up a little. + connect (this, + SIGNAL (have_octave_command_result (const QObject *, const QString&, + const int, + const octave_value_list&)), + f, + SLOT (add_all_breakpoints (const QObject *, const QString&, + const int, const octave_value_list&))); + _tab_widget->setCurrentWidget (f); } diff -r e192525236ad -r 58dd034ecb47 libgui/src/m-editor/file-editor.h --- a/libgui/src/m-editor/file-editor.h Tue Apr 16 21:34:39 2013 +0200 +++ b/libgui/src/m-editor/file-editor.h Thu Apr 18 00:13:14 2013 -0500 @@ -98,6 +98,11 @@ void fetab_delete_debugger_pointer (const QWidget* ID, int line = -1); void fetab_do_breakpoint_marker (bool insert, const QWidget* ID, int line = -1); void fetab_set_focus (const QWidget* ID); +#if !USE_CALLBACKS + void queue_background_octave_command (const QString& command, const int nargout); + void have_octave_command_result (const QObject *, const QString&, + const int, const octave_value_list&); +#endif public slots: void focus (void); diff -r e192525236ad -r 58dd034ecb47 libgui/src/main-window.cc --- a/libgui/src/main-window.cc Tue Apr 16 21:34:39 2013 +0200 +++ b/libgui/src/main-window.cc Thu Apr 18 00:13:14 2013 -0500 @@ -131,12 +131,23 @@ void main_window::handle_save_workspace_request (void) { + // FIXME: Freezing the GUI waiting for user input should be avoided. QString selectedFile = QFileDialog::getSaveFileName (this, tr ("Save Workspace As"), resource_manager::get_home_path ()); + if (!selectedFile.isEmpty ()) +#if USE_CALLBACKS octave_link::post_event (this, &main_window::save_workspace_callback, selectedFile.toStdString ()); +#else + { + QString command = "save ('" + selectedFile + "');"; + + // There are no output arguments for save. + emit queue_background_octave_command (command, 0); + } +#endif } void @@ -144,18 +155,33 @@ { QString file = file_arg; + // FIXME: Freezing the GUI waiting for user input should be avoided. if (file.isEmpty ()) file = QFileDialog::getOpenFileName (this, tr ("Load Workspace"), resource_manager::get_home_path ()); if (! file.isEmpty ()) +#if USE_CALLBACKS octave_link::post_event (this, &main_window::load_workspace_callback, file.toStdString ()); +#else + { + QString command = "load ('" + file + "');"; + + // Loading whole workspace, no output arguments are needed. + emit queue_background_octave_command (command, 0); + } +#endif } void main_window::handle_clear_workspace_request (void) { +#if USE_CALLBACKS octave_link::post_event (this, &main_window::clear_workspace_callback); +#else + // There are no output arguments for clear. + emit queue_background_octave_command ("clear ();", 0); +#endif } void @@ -165,20 +191,43 @@ { name_pair names (old_name.toStdString (), new_name.toStdString ()); +#if USE_CALLBACKS octave_link::post_event (this, &main_window::rename_variable_callback, names); +#else + // If it is desired to check that the first command passed before + // issuing the second command, a slot will be needed that implements + // a simple state machine. + + QString command = new_name + " = " + old_name + ";"; + // There are no output arguments for clear. + emit queue_background_octave_command (command, 0); + // There are no output arguments for clear. + emit queue_background_octave_command ("clear ('" + old_name + "');", 0); +#endif } void main_window::handle_clear_command_window_request (void) { +#if USE_CALLBACKS octave_link::post_event (this, &main_window::clear_command_window_callback); +#else + // There are no output arguments for clc. + emit queue_background_octave_command ("clc ();", 0); + command_editor::interrupt (true); +#endif } void main_window::handle_clear_history_request (void) { +#if USE_CALLBACKS octave_link::post_event (this, &main_window::clear_history_callback); +#else + // Just clearing all history, so no output arguments are needed. + emit queue_background_octave_command ("history ('-c');", 0); +#endif } void @@ -326,13 +375,24 @@ void main_window::browse_for_directory (void) { + // FIXME: Freezing the GUI waiting for user input should be avoided. QString dir = QFileDialog::getExistingDirectory (this, tr ("Set working directory")); if (! dir.isEmpty ()) +#if USE_CALLBACKS octave_link::post_event (this, &main_window::change_directory_callback, dir.toStdString ()); +#else + { + QString command = "cd ('" + dir + "');"; + + // For now, no output arguments are needed and there is no need to + // check for success because the file search dialog is fairly robust. + emit queue_background_octave_command (command, 0); + } +#endif } void @@ -345,8 +405,18 @@ QFileInfo fileInfo (xdir); if (fileInfo.exists () && fileInfo.isDir ()) +#if USE_CALLBACKS octave_link::post_event (this, &main_window::change_directory_callback, xdir.toStdString ()); +#else + { + QString command = "cd ('" + xdir + "');"; + + // For now, no output arguments are needed and there is no need to + // check for success because the file search dialog is fairly robust. + emit queue_background_octave_command (command, 0); + } +#endif } void @@ -411,31 +481,61 @@ void main_window::debug_continue (void) { +#if USE_CALLBACKS octave_link::post_event (this, &main_window::debug_continue_callback); +#else + // There are no output arguments for dbcont. + emit queue_background_octave_command ("dbcont ();", 0); + command_editor::interrupt (true); +#endif } void main_window::debug_step_into (void) { +#if USE_CALLBACKS octave_link::post_event (this, &main_window::debug_step_into_callback); +#else + // There are no output arguments for dbstep. + emit queue_background_octave_command ("dbstep ('in');", 0); + command_editor::interrupt (true); +#endif } void main_window::debug_step_over (void) { +#if USE_CALLBACKS octave_link::post_event (this, &main_window::debug_step_over_callback); +#else + // There are no output arguments for dbstep. + emit queue_background_octave_command ("dbstep ();", 0); + command_editor::interrupt (true); +#endif } void main_window::debug_step_out (void) { +#if USE_CALLBACKS octave_link::post_event (this, &main_window::debug_step_out_callback); +#else + // There are no output arguments for dbstep. + emit queue_background_octave_command ("dbstep ('out');", 0); + command_editor::interrupt (true); +#endif } void main_window::debug_quit (void) { +#if USE_CALLBACKS octave_link::post_event (this, &main_window::debug_quit_callback); +#else + // There are no output arguments for dbquit. + emit queue_background_octave_command ("dbquit ();", 0); + command_editor::interrupt (true); +#endif } void @@ -450,7 +550,12 @@ main_window::closeEvent (QCloseEvent *e) { e->ignore (); +#if USE_CALLBACKS octave_link::post_event (this, &main_window::exit_callback); +#else + // There are no output arguments for exit. + emit queue_background_octave_command ("exit ();", 0); +#endif } void @@ -782,6 +887,27 @@ editor_window, SLOT (handle_delete_debugger_pointer_request (const QString&, int))); +#if !USE_CALLBACKS + connect (this, + SIGNAL (queue_background_octave_command (const QString&, const int)), + _octave_qt_link, + SLOT (handle_queue_octave_command (const QString&, const int))); + + connect (editor_window, + SIGNAL (queue_background_octave_command (const QString&, const int)), + _octave_qt_link, + SLOT (handle_queue_octave_command (const QString&, const int))); + + connect (_octave_qt_link, + SIGNAL (have_octave_command_result (const QObject *, const QString&, + const int, + const octave_value_list&)), + editor_window, + SIGNAL (have_octave_command_result (const QObject *, const QString&, + const int, + const octave_value_list&))); +#endif + connect (_workspace_model, SIGNAL (rename_variable (const QString&, const QString&)), this, @@ -1306,6 +1432,7 @@ this, SLOT (change_directory_up ())); } +#if USE_CALLBACKS void main_window::save_workspace_callback (const std::string& file) { @@ -1410,6 +1537,7 @@ { Fquit (); } +#endif void main_window::find_files(const QString &start_dir) diff -r e192525236ad -r 58dd034ecb47 libgui/src/main-window.h --- a/libgui/src/main-window.h Tue Apr 16 21:34:39 2013 +0200 +++ b/libgui/src/main-window.h Thu Apr 18 00:13:14 2013 -0500 @@ -79,6 +79,9 @@ void relay_command_signal (const QString&); void new_file_signal (const QString&); void open_file_signal (const QString&); +#if !USE_CALLBACKS + void queue_background_octave_command (const QString&, const int); +#endif public slots: void report_status_message (const QString& statusMessage); @@ -175,6 +178,7 @@ void establish_octave_link (void); +#if USE_CALLBACKS void save_workspace_callback (const std::string& file); void load_workspace_callback (const std::string& file); @@ -200,6 +204,7 @@ void debug_quit_callback (void); void exit_callback (void); +#endif // Data models. workspace_model *_workspace_model; diff -r e192525236ad -r 58dd034ecb47 libgui/src/octave-qt-link.cc --- a/libgui/src/octave-qt-link.cc Tue Apr 16 21:34:39 2013 +0200 +++ b/libgui/src/octave-qt-link.cc Thu Apr 18 00:13:14 2013 -0500 @@ -29,6 +29,7 @@ #include #include "str-vec.h" +#include "parse.h" #include "dialog.h" #include "workspace-element.h" @@ -37,7 +38,34 @@ octave_qt_link::octave_qt_link (octave_main_thread *mt) : octave_link (), main_thread (mt) -{ } +{ +#if !USE_CALLBACKS + // Register octave_value_list so that command results may be signaled + // to objects in the GUI thread. + qRegisterMetaType ("octave_value_list"); + + // This is a cross-thread connection when this object exists in the + // GUI thread and transmit_octave_command_result is emitted from the + // worker (Octave core) thread. + connect (this, + SIGNAL (transmit_octave_command_result (const QObject *, + const QString&, + const int, + const octave_value_list&)), + this, + SLOT (receive_octave_command_result (const QObject *, + const QString&, + const int, + const octave_value_list&))); + + // This is a cross-thread confirmation that the GUI is thread is finished + // with the data resulting from a background Octave command. + connect (this, + SIGNAL (finished_with_command_result (void)), + this, + SLOT (handle_finished_with_command_result (void))); +#endif +} octave_qt_link::~octave_qt_link (void) { } @@ -261,3 +289,113 @@ { emit delete_debugger_pointer_signal (QString::fromStdString (file), line); } + +void +octave_qt_link::do_process_events (void) +{ +#if USE_CALLBACKS + octave_link::do_process_events (); +#else + while (! background_octave_command_queue.isEmpty ()) + { + background_queue_mutex.lock (); + command_info ci = background_octave_command_queue.takeFirst (); + background_queue_mutex.unlock (); +fprintf(stderr,"Your wish is my command: %s\n",ci.command.toStdString().c_str()); + // Evaluate this string without producing any output or advancing the + // line number shown at the terminal command line prompt. + octave_value_list retval; + int parse_status; + retval = eval_string (ci.command.toStdString (), false, + parse_status, ci.nargout); + + int command_status; + if (parse_status) + command_status = ParseFail; + else if (error_state) + command_status = ProcessFail; + else + command_status = CommandOK; + + // This lock is to ensure that this Octave worker thread becomes + // unlocked by the QWaitCondition a few lines further down before + // the GUI thread attempts to wake the process. Otherwise, there + // is the risk of this thread being awaken before going to sleep. + events_process_mutex.lock (); + +fprintf (stderr, "Here's right before transmitting signal.\n"); + emit transmit_octave_command_result (ci.senderid, ci.command, + command_status, retval); +fprintf (stderr, "Here's right after transmitting signal.\n"); + + events_process_waitcondition.wait (&events_process_mutex); + events_process_mutex.unlock (); + } +#endif +} + +#if !USE_CALLBACKS +void +octave_qt_link::handle_queue_octave_command (const QString& command, + const int nargout) +{ + background_queue_mutex.lock (); + command_info cominf = {sender (), command, nargout}; + background_octave_command_queue.append (cominf); + background_queue_mutex.unlock (); +} + +void +octave_qt_link::receive_octave_command_result (const QObject *ID, + const QString& command, + const int status, + const octave_value_list& output) +{ + // Passing octave_value_list through a thread is thread safe. Passing an + // octave_value through a thread is not because octave_value uses a + // reference count which could be mangled in the queued signal/slot copy and + // delete mechanism. (When a signal is emitted from one thread to an object + // in another thread, queued signals result, not direct signals. That means + // a copy-construct is done in one thread, while the destruct is done in + // another thread.) No type of operation on the octave_value of the output + // list should be done in this routine. However, because the worker thread + // is asleep, operations on octave_value types and its member derivatives + // is fine in DIRECT connections to the following signal because then + // the reference count increment and decrement are in the same thread and + // happen before the worker thread is awaken. (Direct connections are those + // for which the signal and slot object are in the same thread.) + +fprintf(stderr,"GUI? Here's the result for: %s\n",command.toStdString().c_str()); + emit have_octave_command_result (ID, command, status, output); + +fprintf(stderr,"OK, All done.\n"); + emit finished_with_command_result (); +} + +void +octave_qt_link::handle_finished_with_command_result (void) +{ + // Wake up Octave worker process so that it continues but make sure the + // process is asleep first just in case there is some rare condition in + // which the GUI thread reaches this point before the Octave worker thread + // goes into to a sleep state. + if (! events_process_mutex.tryLock (5000)) + { +fprintf(stderr, "Failed Unlock.\n"); + // Create a NonModal message about error. + QMessageBox* msgBox = new QMessageBox ( + QMessageBox::Critical, tr ("Octave Qt Link"), + "Connection Timeout: Lost link to worker process.", + QMessageBox::Ok, 0); + msgBox->setWindowModality (Qt::NonModal); + msgBox->setAttribute (Qt::WA_DeleteOnClose); + msgBox->show (); + } + else + { +fprintf(stderr, "Now waking worker thread.\n"); + events_process_mutex.unlock (); + events_process_waitcondition.wakeAll (); + } +} +#endif diff -r e192525236ad -r 58dd034ecb47 libgui/src/octave-qt-link.h --- a/libgui/src/octave-qt-link.h Tue Apr 16 21:34:39 2013 +0200 +++ b/libgui/src/octave-qt-link.h Thu Apr 18 00:13:14 2013 -0500 @@ -31,9 +31,16 @@ #include #include #include +#include +#include #include "octave-link.h" #include "octave-main-thread.h" +// FIXME -- we should not be including config.h in header files. +#ifdef HAVE_CONFIG_H +#include +#endif +#include "oct-obj.h" // \class OctaveLink // \brief Provides threadsafe access to octave. @@ -49,6 +56,16 @@ public: +#define USE_CALLBACKS 0 +#if !USE_CALLBACKS + enum + { + CommandOK = 0, + ParseFail, + ProcessFail + }; +#endif + octave_qt_link (octave_main_thread *mt); ~octave_qt_link (void); @@ -113,6 +130,23 @@ // Thread running octave_main. octave_main_thread *main_thread; +#if !USE_CALLBACKS + // GUI events issue background Octave commands which are stored in a queue. + struct command_info + { + const QObject *senderid; + const QString command; + const int nargout; + }; + QList background_octave_command_queue; + + // Process GUI events in the background Octave command queue. + void do_process_events (void); + QMutex background_queue_mutex; + QMutex events_process_mutex; + QWaitCondition events_process_waitcondition; +#endif + signals: void exit_signal (int status); @@ -142,6 +176,28 @@ void insert_debugger_pointer_signal (const QString&, int); void delete_debugger_pointer_signal (const QString&, int); + +#if !USE_CALLBACKS + // Emit this signal from within the Octave worker thread. There should + // be only one slot connected to this signal, receive_octave_command_result. + void transmit_octave_command_result (const QObject *, const QString&, + const int, const octave_value_list&); + // Emit this signal from within the GUI thread. Many QObject slots may + // connect to this signal, but the programming rule is that none of them + // should modify any contents of the output octave_value. ONLY retrieve + // results. + void have_octave_command_result (const QObject *, const QString&, + const int, const octave_value_list&); + void finished_with_command_result (void); + +public slots: + void handle_queue_octave_command (const QString& command, const int nargout); + void receive_octave_command_result (const QObject *ID, + const QString& command, + const int status, + const octave_value_list& output); + void handle_finished_with_command_result (void); +#endif }; #endif diff -r e192525236ad -r 58dd034ecb47 libinterp/interpfcn/octave-link.h --- a/libinterp/interpfcn/octave-link.h Tue Apr 16 21:34:39 2013 +0200 +++ b/libinterp/interpfcn/octave-link.h Thu Apr 18 00:13:14 2013 -0500 @@ -285,7 +285,7 @@ bool link_enabled; void do_generate_events (void); - void do_process_events (void); + virtual void do_process_events (void); void do_discard_events (void); template