octave-maintainers
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: interrupt handing and the GUI (bug #37672)


From: Daniel J Sebald
Subject: Re: interrupt handing and the GUI (bug #37672)
Date: Sun, 10 Nov 2013 12:25:25 -0600
User-agent: Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.2.24) Gecko/20111108 Fedora/3.1.16-1.fc14 Thunderbird/3.1.16

On 11/10/2013 11:07 AM, John W. Eaton wrote:
I have marked bug 37672 (https://savannah.gnu.org/bugs/?37672) as a
blocker for the release. I have some ideas about why this is not
working and what we can do to fix it.

In the CLI version of Octave, the terminal driver sends a SIGINT
signal to the Octave process when the user types Ctrl-C at the
command prompt. Then the SIGINT signal is caught and we end up in the
function user_abort.

In the GUI, typing Ctrl-C in the terminal window doesn't generate a
SIGINT signal directly as it does when Octave is running in a
terminal. Instead, it eventually calls the function
TerminalView::copyClipboard (in the Unix version of the libqterminal
code) or QWinTerminalImpl::copyClipboard (in the Windows version of
the libqterminal code). Then, if there is no selection, we call
::raise (SIGINT) to generate an interrupt signal for the process.
 From there, we end up in user_abort.

Ignoring things like "debug_on_interrupt", the function user_abort
may do one of two things to handle the signal.

The first is to set the global variable octave_interrupt_state and
return. Then Octave periodically checks this variable (typically
using the OCTAVE_QUIT macro) and if it is set, throws an exception and
returns to the command prompt.

This method of interrupt handling works in the CLI and GUI versions of
Octave. This is how interrupts inside loops like

while (true) disp ('foo-i-hithere'); end

are handled.

The second method of signal handling is to call the function
jump_to_enclosing_context, which calls longjmp to return to a "safe"
location at which point Octave throws an exception and returns to the
command prompt.

This type of interrupt handling is used when we need to be able to
interrupt calls to library functions that we don't have control over
so we can't insert calls to OCTAVE_QUIT to make them abort. This
method works for the CLI version of Octave but not with the GUI. The
difference is that in the CLI version, we only have one thread but in
the GUI, we have multiple threads and the exception is being thrown in
the thread where the GUI is running instead of the thread where the
Octave interpreter is running.

I was able to examine this behavior by using "info threads" in gdb
while doing something like

x = rand (3000);
inv (x);

in Octave and typing Ctrl-C in the command window when Octave is busy
working on the inverse.

My first thought was that we need to make sure that the SIGINT signal
and interrupt handler are executed inside the same thread where Octave
is running, so instead of simply calling ::raise inside the
copyClipboard function, I modified Octave to emit a Qt signal there
that would be handled by a Qt slot function running in the Octave
interpreter thread. Of course this doesn't work because in that
thread the Octave interpreter never returns to give control back to
the Qt thread manager.

Yes, this doesn't sound good. I sort of like the way it already exists, i.e., that the GUI interprets the SIGINT based upon what window is active, and if it is the terminal window that is active issue an interrupt back to the OS with the target being the Octave-core thread which the GUI knows because it is the one that launched the Octave-core thread and should have a record of process information.


Here is my proposed solution:

Instead of using setjmp/longjmp and relying on Unix signal handling to
interrupt the process, we execute these long-running external
functions in separate threads that we can cancel when an interrupt
arrives. Then in the main thread we just wait for the thread to
finish and periodically check to see whether the
octave_interrupt_state variable is set. If it is, then we cancel the
thread and throw an exception. That will all happen inside the thread
that is running the Octave interpreter, so we won't be trying to throw
an exception across threads.

I understand how to do this with pthreads. I'll either need help with
Windows threads or maybe we could use the pthreads-win32 library
(LGPL; available here: http://www.sourceware.org/pthreads-win32).

Another question is whether we should always use this approach or
continue to use the setjmp/longjmp method for the CLI version of
Octave.

Does anyone see a better way?

This seems vaguely familiar and I recall looking at the interrupt code at some point. This might have been around the time we cleaned up the header files for the SIGxx definitions and found duplicate symbols that we didn't know exactly how to handle. I haven't time to search through where or what I might have written, but I recall looking at the way interrupts were handled and thought that some change would be needed in the Octave core code for some reason or another. It was either that the core would have to not grab control of the SIGINT at initialization or that it would have to do so under the condition of running embedded inside some GUI. Again, it's all vague now.

Anyway, what you are proposing sounds better, i.e., no exception across threads--but what I just said might not be a proper characterization, what I mean is no function calls across threads as a result of a SIGINT. However, periodically checking some variable as regards to an interrupt signal doesn't feel elegant. I would think there might be a better way of bouncing signals about by setting up or redefining interrupt handlers differently.

One thing I wonder is whether the notion of an exception across threads is correct. Unlike threads sharing memory or function calls, signals sort of go back to the OS.

Long external functions inside separate threads I'm fine with (hey, could then maybe make use of multiple cores), but why if they are run in the main thread are they not interruptable by the current mechanism?

Dan


reply via email to

[Prev in Thread] Current Thread [Next in Thread]