Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Try to avoid crashes on long output into Python console in a short time #2253

Merged
merged 1 commit into from
Apr 6, 2015

Conversation

michalgregor
Copy link
Contributor

Fixing crashes on very long output being written to the console in a short time.

Fixes #2251

@ccordoba12 ccordoba12 added this to the v2.4 milestone Mar 17, 2015
@ccordoba12 ccordoba12 changed the title Fixing crashes on long output into console in a short time. spyder-ide#2... Fixing crashes on long output into Python console in a short time Mar 17, 2015
@ccordoba12
Copy link
Member

@michalgregor, thanks a lot for doing the PR. I'd like to say two things about it:

  1. Could you post an example of a simple program that causes the problem you're referring to?
  2. I wonder if that line has something to do with running PyQt/PySide applications in our consoles. We need to check that removing it doesn't cause other bugs.

@michalgregor
Copy link
Contributor Author

The first is easy to provide:

for i in range(100000):
    print('A rather large number of rather long lines. A rather large number of rather long lines. A rather large number of rather long lines. A rather large number of rather long lines. A rather large number of rather long lines. A rather large number of rather long lines. A rather large number of rather long lines. A rather large number of rather long lines.A rather large number of rather long lines.A rather large number of rather long lines. A rather large number of rather long lines.')

I wouldn't know about the second thing. However – for what it's worth – I have been running PyQt 4 applications with Spyder since the patch and haven't noticed any difference in behaviour.

@@ -291,7 +291,6 @@ def get_stderr(self):

def write_output(self):
self.shell.write(self.get_stdout(), flush=True)
QApplication.processEvents()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of erasing this line, could you comment it like this?

# Commenting the line below fixes Issue 2251
# QApplication.processEvents()

If other problems appear because of this change, it'd be easier to come back to this place later :-)

@Nodd
Copy link
Contributor

Nodd commented Mar 25, 2015

I confirm the crash using your example, but commenting the line doesn't fix the problem for me.

Fore reference, here is the console output:

Fatal Python error: Cannot recover from stack overflow.

Thread 0x00007f4e867fc700 (most recent call first):
  File "/home/joseph/prog/spyder.git/spyderlib/utils/bsdsocket.py", line 31 in temp_fail_retry
  File "/home/joseph/prog/spyder.git/spyderlib/utils/bsdsocket.py", line 74 in read_packet
  File "/home/joseph/prog/spyder.git/spyderlib/widgets/externalshell/introspection.py", line 162 in run

Thread 0x00007f4e86ffd700 (most recent call first):
  File "/usr/lib/python3.4/site-packages/IPython/kernel/channels.py", line 152 in run
  File "/usr/lib/python3.4/threading.py", line 920 in _bootstrap_inner
  File "/usr/lib/python3.4/threading.py", line 888 in _bootstrap

Thread 0x00007f4e877fe700 (most recent call first):
  File "/usr/lib/python3.4/site-packages/zmq/sugar/poll.py", line 101 in poll
  File "/usr/lib/python3.4/site-packages/zmq/eventloop/ioloop.py", line 122 in poll
  File "/usr/lib/python3.4/site-packages/tornado/ioloop.py", line 815 in start
  File "/usr/lib/python3.4/site-packages/zmq/eventloop/ioloop.py", line 151 in start
  File "/usr/lib/python3.4/site-packages/IPython/kernel/threaded.py", line 159 in run
  File "/usr/lib/python3.4/threading.py", line 920 in _bootstrap_inner
  File "/usr/lib/python3.4/threading.py", line 888 in _bootstrap

Thread 0x00007f4eb58a0700 (most recent call first):
  File "/home/joseph/prog/spyder.git/spyderlib/utils/bsdsocket.py", line 31 in temp_fail_retry
  File "/home/joseph/prog/spyder.git/spyderlib/utils/bsdsocket.py", line 74 in read_packet
  File "/home/joseph/prog/spyder.git/spyderlib/widgets/externalshell/introspection.py", line 162 in run

Thread 0x00007f4eb67fc700 (most recent call first):
  File "/usr/lib/python3.4/socket.py", line 187 in accept
  File "/home/joseph/prog/spyder.git/spyderlib/spyder.py", line 2627 in start_open_files_server
  File "/usr/lib/python3.4/threading.py", line 868 in run
  File "/usr/lib/python3.4/threading.py", line 920 in _bootstrap_inner
  File "/usr/lib/python3.4/threading.py", line 888 in _bootstrap

Thread 0x00007f4eb6ffd700 (most recent call first):
  File "/home/joseph/prog/spyder.git/spyderlib/utils/bsdsocket.py", line 31 in temp_fail_retry
  File "/home/joseph/prog/spyder.git/spyderlib/utils/bsdsocket.py", line 74 in read_packet
  File "/home/joseph/prog/spyder.git/spyderlib/widgets/externalshell/introspection.py", line 162 in run

Thread 0x00007f4eb7fff700 (most recent call first):
  File "/usr/lib/python3.4/socket.py", line 187 in accept
  File "/home/joseph/prog/spyder.git/spyderlib/widgets/externalshell/introspection.py", line 67 in run
  File "/usr/lib/python3.4/threading.py", line 920 in _bootstrap_inner
  File "/usr/lib/python3.4/threading.py", line 888 in _bootstrap

Thread 0x00007f4ebcc0a700 (most recent call first):
  File "/usr/lib/python3.4/socket.py", line 187 in accept
  File "/home/joseph/prog/spyder.git/spyderlib/widgets/externalshell/introspection.py", line 67 in run
  File "/usr/lib/python3.4/threading.py", line 920 in _bootstrap_inner
  File "/usr/lib/python3.4/threading.py", line 888 in _bootstrap

Current thread 0x00007f4f36ae4700 (most recent call first):
  File "/home/joseph/prog/spyder.git/spyderlib/widgets/sourcecode/base.py", line 38 in insert_text_to
  File "/home/joseph/prog/spyder.git/spyderlib/widgets/sourcecode/base.py", line 1262 in append_text_to_shell
  File "/home/joseph/prog/spyder.git/spyderlib/widgets/shell.py", line 612 in insert_text
  File "/home/joseph/prog/spyder.git/spyderlib/widgets/shell.py", line 597 in flush
  File "/home/joseph/prog/spyder.git/spyderlib/widgets/shell.py", line 586 in write
  File "/home/joseph/prog/spyder.git/spyderlib/widgets/externalshell/baseshell.py", line 293 in write_output
  File "/home/joseph/prog/spyder.git/spyderlib/widgets/shell.py", line 598 in flush
  File "/home/joseph/prog/spyder.git/spyderlib/widgets/shell.py", line 586 in write
  File "/home/joseph/prog/spyder.git/spyderlib/widgets/externalshell/baseshell.py", line 293 in write_output
  File "/home/joseph/prog/spyder.git/spyderlib/widgets/shell.py", line 598 in flush
  File "/home/joseph/prog/spyder.git/spyderlib/widgets/shell.py", line 586 in write
  File "/home/joseph/prog/spyder.git/spyderlib/widgets/externalshell/baseshell.py", line 293 in write_output
  File "/home/joseph/prog/spyder.git/spyderlib/widgets/shell.py", line 598 in flush
  File "/home/joseph/prog/spyder.git/spyderlib/widgets/shell.py", line 586 in write
  File "/home/joseph/prog/spyder.git/spyderlib/widgets/externalshell/baseshell.py", line 293 in write_output
  File "/home/joseph/prog/spyder.git/spyderlib/widgets/shell.py", line 598 in flush
  File "/home/joseph/prog/spyder.git/spyderlib/widgets/shell.py", line 586 in write
  File "/home/joseph/prog/spyder.git/spyderlib/widgets/externalshell/baseshell.py", line 293 in write_output
  File "/home/joseph/prog/spyder.git/spyderlib/widgets/shell.py", line 598 in flush
  File "/home/joseph/prog/spyder.git/spyderlib/widgets/shell.py", line 586 in write
  File "/home/joseph/prog/spyder.git/spyderlib/widgets/externalshell/baseshell.py", line 293 in write_output
  File "/home/joseph/prog/spyder.git/spyderlib/widgets/shell.py", line 598 in flush
  File "/home/joseph/prog/spyder.git/spyderlib/widgets/shell.py", line 586 in write
  File "/home/joseph/prog/spyder.git/spyderlib/widgets/externalshell/baseshell.py", line 293 in write_output
  File "/home/joseph/prog/spyder.git/spyderlib/widgets/shell.py", line 598 in flush
  File "/home/joseph/prog/spyder.git/spyderlib/widgets/shell.py", line 586 in write
  File "/home/joseph/prog/spyder.git/spyderlib/widgets/externalshell/baseshell.py", line 293 in write_output
  File "/home/joseph/prog/spyder.git/spyderlib/widgets/shell.py", line 598 in flush
  File "/home/joseph/prog/spyder.git/spyderlib/widgets/internalshell.py", line 313 in flush
  File "/home/joseph/prog/spyder.git/spyderlib/widgets/shell.py", line 588 in write
  File "/home/joseph/prog/spyder.git/spyderlib/widgets/internalshell.py", line 232 in stderr_avail
  File "/home/joseph/prog/spyder.git/spyderlib/widgets/internalshell.py", line 67 in write
  File "/home/joseph/prog/spyder.git/spyderlib/widgets/sourcecode/base.py", line 44 in insert_text_to
  File "/home/joseph/prog/spyder.git/spyderlib/widgets/sourcecode/base.py", line 1262 in append_text_to_shell
  File "/home/joseph/prog/spyder.git/spyderlib/widgets/shell.py", line 612 in insert_text
  File "/home/joseph/prog/spyder.git/spyderlib/widgets/shell.py", line 597 in flush
  File "/home/joseph/prog/spyder.git/spyderlib/widgets/shell.py", line 586 in write
  File "/home/joseph/prog/spyder.git/spyderlib/widgets/externalshell/baseshell.py", line 293 in write_output
  File "/home/joseph/prog/spyder.git/spyderlib/widgets/shell.py", line 598 in flush
  File "/home/joseph/prog/spyder.git/spyderlib/widgets/shell.py", line 586 in write
  File "/home/joseph/prog/spyder.git/spyderlib/widgets/externalshell/baseshell.py", line 293 in write_output
  File "/home/joseph/prog/spyder.git/spyderlib/widgets/shell.py", line 598 in flush
  File "/home/joseph/prog/spyder.git/spyderlib/widgets/shell.py", line 586 in write
  File "/home/joseph/prog/spyder.git/spyderlib/widgets/externalshell/baseshell.py", line 293 in write_output
  File "/home/joseph/prog/spyder.git/spyderlib/widgets/shell.py", line 598 in flush
  File "/home/joseph/prog/spyder.git/spyderlib/widgets/shell.py", line 586 in write
  File "/home/joseph/prog/spyder.git/spyderlib/widgets/externalshell/baseshell.py", line 293 in write_output
  File "/home/joseph/prog/spyder.git/spyderlib/widgets/shell.py", line 598 in flush
  File "/home/joseph/prog/spyder.git/spyderlib/widgets/shell.py", line 586 in write
  File "/home/joseph/prog/spyder.git/spyderlib/widgets/externalshell/baseshell.py", line 293 in write_output
  File "/home/joseph/prog/spyder.git/spyderlib/widgets/shell.py", line 598 in flush
  File "/home/joseph/prog/spyder.git/spyderlib/widgets/shell.py", line 586 in write
  File "/home/joseph/prog/spyder.git/spyderlib/widgets/externalshell/baseshell.py", line 293 in write_output
  File "/home/joseph/prog/spyder.git/spyderlib/widgets/shell.py", line 598 in flush
  File "/home/joseph/prog/spyder.git/spyderlib/widgets/shell.py", line 586 in write
  File "/home/joseph/prog/spyder.git/spyderlib/widgets/externalshell/baseshell.py", line 293 in write_output
  File "/home/joseph/prog/spyder.git/spyderlib/widgets/shell.py", line 598 in flush
  File "/home/joseph/prog/spyder.git/spyderlib/widgets/shell.py", line 586 in write
  File "/home/joseph/prog/spyder.git/spyderlib/widgets/externalshell/baseshell.py", line 293 in write_output
  File "/home/joseph/prog/spyder.git/spyderlib/widgets/shell.py", line 598 in flush
  File "/home/joseph/prog/spyder.git/spyderlib/widgets/shell.py", line 586 in write
  File "/home/joseph/prog/spyder.git/spyderlib/widgets/externalshell/baseshell.py", line 293 in write_output
  File "/home/joseph/prog/spyder.git/spyderlib/widgets/shell.py", line 598 in flush
  File "/home/joseph/prog/spyder.git/spyderlib/widgets/shell.py", line 586 in write
  File "/home/joseph/prog/spyder.git/spyderlib/widgets/externalshell/baseshell.py", line 293 in write_output
  File "/home/joseph/prog/spyder.git/spyderlib/widgets/shell.py", line 598 in flush
  File "/home/joseph/prog/spyder.git/spyderlib/widgets/shell.py", line 586 in write
  File "/home/joseph/prog/spyder.git/spyderlib/widgets/externalshell/baseshell.py", line 293 in write_output
  File "/home/joseph/prog/spyder.git/spyderlib/widgets/shell.py", line 598 in flush
  File "/home/joseph/prog/spyder.git/spyderlib/widgets/shell.py", line 586 in write
  File "/home/joseph/prog/spyder.git/spyderlib/widgets/externalshell/baseshell.py", line 293 in write_output
  File "/home/joseph/prog/spyder.git/spyderlib/widgets/shell.py", line 598 in flush
  File "/home/joseph/prog/spyder.git/spyderlib/widgets/shell.py", line 586 in write
  File "/home/joseph/prog/spyder.git/spyderlib/widgets/externalshell/baseshell.py", line 293 in write_output
  File "/home/joseph/prog/spyder.git/spyderlib/widgets/shell.py", line 598 in flush
  File "/home/joseph/prog/spyder.git/spyderlib/widgets/shell.py", line 586 in write
  File "/home/joseph/prog/spyder.git/spyderlib/widgets/externalshell/baseshell.py", line 293 in write_output
  File "/home/joseph/prog/spyder.git/spyderlib/widgets/shell.py", line 598 in flush
  File "/home/joseph/prog/spyder.git/spyderlib/widgets/shell.py", line 586 in write
  File "/home/joseph/prog/spyder.git/spyderlib/widgets/externalshell/baseshell.py", line 293 in write_output
  File "/home/joseph/prog/spyder.git/spyderlib/widgets/shell.py", line 598 in flush
  File "/home/joseph/prog/spyder.git/spyderlib/widgets/shell.py", line 586 in write
  File "/home/joseph/prog/spyder.git/spyderlib/widgets/externalshell/baseshell.py", line 293 in write_output
  File "/home/joseph/prog/spyder.git/spyderlib/widgets/shell.py", line 598 in flush
  File "/home/joseph/prog/spyder.git/spyderlib/widgets/shell.py", line 586 in write
  File "/home/joseph/prog/spyder.git/spyderlib/widgets/externalshell/baseshell.py", line 293 in write_output
  File "/home/joseph/prog/spyder.git/spyderlib/widgets/shell.py", line 598 in flush
  File "/home/joseph/prog/spyder.git/spyderlib/widgets/shell.py", line 586 in write
  File "/home/joseph/prog/spyder.git/spyderlib/widgets/externalshell/baseshell.py", line 293 in write_output
  File "/home/joseph/prog/spyder.git/spyderlib/widgets/shell.py", line 598 in flush
  File "/home/joseph/prog/spyder.git/spyderlib/widgets/shell.py", line 586 in write
  File "/home/joseph/prog/spyder.git/spyderlib/widgets/externalshell/baseshell.py", line 293 in write_output
  File "/home/joseph/prog/spyder.git/spyderlib/widgets/shell.py", line 598 in flush
  File "/home/joseph/prog/spyder.git/spyderlib/widgets/shell.py", line 586 in write
  File "/home/joseph/prog/spyder.git/spyderlib/widgets/externalshell/baseshell.py", line 293 in write_output
  File "/home/joseph/prog/spyder.git/spyderlib/widgets/shell.py", line 598 in flush
  File "/home/joseph/prog/spyder.git/spyderlib/widgets/shell.py", line 586 in write
  File "/home/joseph/prog/spyder.git/spyderlib/widgets/externalshell/baseshell.py", line 293 in write_output
  File "/home/joseph/prog/spyder.git/spyderlib/widgets/shell.py", line 598 in flush
  File "/home/joseph/prog/spyder.git/spyderlib/widgets/shell.py", line 586 in write
  ...
fish: Job 1, “./bootstrap.py &” terminated by signal SIGABRT (Abort)

@michalgregor
Copy link
Contributor Author

Yes, you are right. I have applied the fix to the 10 PCs I use in my class and I can confirm that we still sometimes experience crashes – although not as consistently as we did with the line uncommented.

So, unfortunately, this does not fully fix the issue. The problem must be somewhere else.

@Nodd
Copy link
Contributor

Nodd commented Mar 26, 2015

I think that by removing the line spyder does less things between each printed line, so the overflow is less likely to occur.

As a temporary workaround, you can execute the script in an external terminal instead of the integrated one (there is an option in the script configuration, F6 by default). I don't know if this option is enabled in all platforms.

Are you sure you need to print that much lines in succession ? This bug apart, it looks like a bad idea, writing to a file may be more appropriate.

@ccordoba12
Copy link
Member

@michalgregor, why don't you use the IPython console instead? It has much better support for these kind of things

@michalgregor
Copy link
Contributor Author

I don't like it mainly because it keeps displaying images inline and in a non-interactive manner. I guess that can be configured somehow but since I see no significant downsides – apart from the bug – in using the standard console I don't see why we should switch.

Also – yes, we could execute the script in an external terminal, but then we lose most of the integration like the interactive mode and keeping track of variables. I don't see what use there would be in using Spyder at all in that case...

In any case – this is supposed to work. Let's focus on fixing it. I know how to work around the problem in the meantime so don't worry about that too much.

@ccordoba12
Copy link
Member

Yes, absolutely. This is a definitely a bug that we should fix, but in the meantime at least there is an alternative you can use.

In fact, I think this bug is related to issue #1728, and it's not easy to fix.

@ccordoba12
Copy link
Member

I'm going to merge this one because according to @michalgregor, it helps to improve things a little bit.

@ccordoba12 ccordoba12 changed the title Fixing crashes on long output into Python console in a short time Try to avoid crashes on long output into Python console in a short time Apr 6, 2015
ccordoba12 added a commit that referenced this pull request Apr 6, 2015
Try to avoid crashes on long output into Python console in a short time
@ccordoba12 ccordoba12 merged commit ea724b3 into spyder-ide:master Apr 6, 2015
ccordoba12 added a commit that referenced this pull request Apr 6, 2015
@michalgregor
Copy link
Contributor Author

I believe I can provide further input on this interesting issue. I have made some experiments with mutexes (QMutex from QtCore) trying to check for potential thread collisions that could cause the crashes. I am still using the long output example for testing.

When I protect ExternalShellBase's write_output method with a mutex, the entire spyder interface locks up before even the first line of the example is printed. If the mutex is made recursive, the interface keeps going and spyder crashes after a while – as is its usual habit on this example.

If I instead use tryLock and return from the function if the mutex is locked already the crashes are resolved (obviously at the cost of scrapping some of the output messages).

In any case it seems that the issue is indeed thread related and that write_output is being triggered while another one is still in process – this could easily cause a crash if the underlying text insertion operations in Qt's widgets are not thread safe.

Since there is no change in behaviour when a recursive mutex is used, it seems that the issue is caused by write_output being called recursively – I guess this could happen if anything is written into stdout while write_output is still in progress.

I hope this information helps in resolving the issue.

@michalgregor
Copy link
Contributor Author

Guys, I have implemented a simple workaround for this. It uses two mutexes – the first one is used to check whether a different call to write_output is being processed at the moment. If so, the message is stored in a buffer instead of being printed (the other mutex protects the buffer). The buffer is then itself printed before write_output exists. In code it looks something like this:

   if not self.write_lock.tryLock():
        self.buffer_lock.lock()
        self.buffer.append(self.get_stdout())
        self.buffer_lock.unlock()

        return

    self.shell.write(self.get_stdout(), flush=True)

    while True:
        self.buffer_lock.lock()
        messages = self.buffer
        self.buffer = []
        self.buffer_lock.unlock()

        if not len(messages): break
        else:
            for msg in messages:
                self.shell.write(msg, flush=True)                

    self.write_lock.unlock()

I admit it is not most elegant of solutions, but it does seem to work. Where should I commit and post this so that it can get tested by you and merged?

@goanpeca
Copy link
Member

@michalgregor awesome work :-), thanks!

Could you open a new PR in a separate branch?

@ccordoba12
Copy link
Member

Yes, please open a pull request against our master branch so we can test it and merge it :-)

michalgregor added a commit to michalgregor/spyder that referenced this pull request Mar 2, 2016
@michalgregor michalgregor deleted the patch-long-output branch March 2, 2016 16:58
michalgregor added a commit to michalgregor/spyder that referenced this pull request Mar 2, 2016
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Spyder crashing on very long output
4 participants