Results 1 to 18 of 18

Thread: GUI Freezes unless there's incoming data

Hybrid View

Previous Post Previous Post   Next Post Next Post
  1. #1
    Join Date
    Oct 2008
    Posts
    11
    Thanks
    4
    Qt products
    Qt4
    Platforms
    Unix/X11

    Unhappy GUI Freezes unless there's incoming data

    Hi all

    I'm programming a basic IRC client, and have an IRC api/library, the Qt Designer generated interface file, and a separate module in the middle to link the two into some kind of functionality.

    In the IRC library, I have a member function that checks a socket I've set up for any incoming data:

    Qt Code:
    1. def recvLoop(self):
    2. try:
    3. data = self.irc.recv(4096)
    4. if data.find('PING') != -1:
    5. self.irc.send('PONG ' + data.split() [1] + '\r\n')
    6. if len(data) > 0:
    7. incomingBuffer.put(data)
    8. #print data
    9. #time.sleep(1)
    10. except:
    11. print 'Error: Possible interupt.'
    To copy to clipboard, switch view to plain text mode 
    Then in the main module (mainwindow.py) that uses the IRC library, I have the following:

    Qt Code:
    1. @pyqtSignature("")
    2. def on_connectButton_clicked(self):
    3.  
    4. self.displayBrowser.append('Connecting...')
    5. client = crisp_irc.IRC()
    6. client.newConnection('irc.oftc.net', 6667, 'crispycream', 'crisp', 'crisp')
    7. client.join('#crasp')
    8. client.channelSend('#crasp', 'blah')
    9. client.set_recv_function(crisp_irc.my_receive)
    10.  
    11. while 1:
    12. client.recvLoop()
    13. QApplication.processEvents()
    14. if crisp_irc.incomingBuffer.qsize() > 0:
    15. print crisp_irc.incomingBuffer.qsize()
    16. item = crisp_irc.incomingBuffer.get()
    17. #print item
    18. self.displayBrowser.append(item)
    19. crisp_irc.incomingBuffer.task_done()
    20. QApplication.processEvents()
    21.  
    22. ...
    23.  
    24. if __name__ == "__main__":
    25. import sys
    26.  
    27. app = QApplication(sys.argv)
    28. form = CrispIRCMainWindow()
    29.  
    30. form.show()
    31. app.exec_()
    To copy to clipboard, switch view to plain text mode 
    So if there's data, the IRC lib puts it into a (global - for now) buffer, and the mainwindow.py reads from that buffer to see if its empty or not. If it isnt, it appends it to the main display window.

    The QApplication.processEvents() calls have solved half of my problem. The remaining problem is that the GUI freezes UNLESS the buffer is not empty, at which point it updates everything to how it should be, then goes straight back to being frozen/in a crash like state. The client does stay connected during this, and does receive messages etc, hence it updates when there's something to show.

    How do I get rid of this behaviour (I've tried sleeping for a second etc, no dice)? Is there a better way to pass data along to the GUI from the library I've made? Many many thanks in advance!!

    EDIT: Maybe there's a way to detect when there's data to be received, and only execute the function when there is something to be sent down the socket? Kind of data drive rather than a constant infinite loop that keep polling things, which is possibly the source of my problem?
    Last edited by crisp; 25th January 2009 at 22:10.

  2. #2
    Join Date
    Jan 2009
    Location
    Germany
    Posts
    387
    Thanks
    101
    Thanked 15 Times in 15 Posts
    Qt products
    Qt4 Qt5
    Platforms
    Unix/X11 Windows

    Default Re: GUI Freezes unless there's incoming data

    Your gui freezes because you are keeping the gui thread busy with an infinite loop in an event handler. It only works because you step the gui thread forward sometimes with a processEvents().

    A much nicer way would be to fork out a parallel process that handles the IRC communication and feeds the data into a buffer. The gui thread should only read from that buffer without performing any unnecessary calculations and show the data on the screen.

    Or at least make sure that you are using non blocking network communication, so that the processEvents() calls can be placed regulary, even if there is no data.

  3. The following user says thank you to Cruz for this useful post:

    crisp (26th January 2009)

  4. #3
    Join Date
    Oct 2008
    Posts
    11
    Thanks
    4
    Qt products
    Qt4
    Platforms
    Unix/X11

    Default Re: GUI Freezes unless there's incoming data

    Thanks for the reply. I did have a version of the library that hands made connections out to another separate class as a thread, using the Threading module. I disguarded this some time ago, do you recommend going back to this model? Could you give me a starting point in starting a new parallel process if this is not what you meant?

    Thanks once again, this is much appreciated!

  5. #4
    Join Date
    Jan 2006
    Location
    Warsaw, Poland
    Posts
    33,368
    Thanks
    3
    Thanked 5,018 Times in 4,794 Posts
    Qt products
    Qt3 Qt4 Qt5 Qt/Embedded
    Platforms
    Unix/X11 Windows Android Maemo/MeeGo
    Wiki edits
    10

    Default Re: GUI Freezes unless there's incoming data

    I would recommend against using threads. Instead I would opt for setting up a timer to fire periodically (like every 250ms) and in its slot or event handler check if there is anything to be read from the socket. If your library uses blocking calls, you might want to use QSocketNotifier instead of the timer. It will tell you when something arrives at the socket and then you can call the reading routine without blocking the event loop.

  6. The following user says thank you to wysota for this useful post:

    crisp (26th January 2009)

  7. #5
    Join Date
    Oct 2008
    Posts
    11
    Thanks
    4
    Qt products
    Qt4
    Platforms
    Unix/X11

    Unhappy Re: GUI Freezes unless there's incoming data

    Well thanks for the advice guys, I'm still really stuck though. I'm so tired and frustrated now that I think I'll leave it until tomorrow, fresh eyes and all that. I've tried implementing a timer (used QTimer) and it didn't seem to help the situation, so tomorrow I'll try QSocketNotifier. If any good samaritans feel like looking over my (sloppy) code, I've posted it here: http://driversgb.com/Qt/crispirc.zip - if I can get over this hurdle then the path is clear for me to bang out a load of code! Cheers guys.

  8. #6
    Join Date
    Jan 2006
    Location
    Warsaw, Poland
    Posts
    33,368
    Thanks
    3
    Thanked 5,018 Times in 4,794 Posts
    Qt products
    Qt3 Qt4 Qt5 Qt/Embedded
    Platforms
    Unix/X11 Windows Android Maemo/MeeGo
    Wiki edits
    10

    Default Re: GUI Freezes unless there's incoming data

    At worst get rid of the IRC library. IRC protocol is based on text, it's very easy to parse and handle it withough additional libraries.

  9. #7
    Join Date
    Jan 2009
    Location
    Germany
    Posts
    387
    Thanks
    101
    Thanked 15 Times in 15 Posts
    Qt products
    Qt4 Qt5
    Platforms
    Unix/X11 Windows

    Default Re: GUI Freezes unless there's incoming data

    @crisp
    Yes, I think using the threaded version of the IRC library is the easiest solution for you.

    @wysota
    This topic already came up once and we didn't quite finish it. I don't understand your aversion against threads. With any approach based on the processEvents() call you take away the control from the Qt framework and provide your own mechanism to process the gui events with your own pace. With a timer based approach you can at least return the event handler immidiately, but still you set your own pace to step a process forward. With a seperate thread however the optimized mechanisms of the operating system will take care of driving your process. In the case of crisp for example the seperate thread can wait in a blocking recvfrom() call and use no resources at all. New data will be handled exactly when they arrived and not too early or too late as it's usually the case with a timer based approach. Parallel processes also lead to a better utilization of multicore cpus, which are becoming the standard as of now.

  10. #8
    Join Date
    Jan 2006
    Location
    Warsaw, Poland
    Posts
    33,368
    Thanks
    3
    Thanked 5,018 Times in 4,794 Posts
    Qt products
    Qt3 Qt4 Qt5 Qt/Embedded
    Platforms
    Unix/X11 Windows Android Maemo/MeeGo
    Wiki edits
    10

    Default Re: GUI Freezes unless there's incoming data

    Quote Originally Posted by Cruz View Post
    I don't understand your aversion against threads.
    I'm not against using threads in general, I like threads. I'm against using threads where they provide more problems than they solve or when they are not needed at all. And in general I am against using processEvents(). See my article at Qt Quarterly for more info.

    With a seperate thread however the optimized mechanisms of the operating system will take care of driving your process.
    This is completely not true, it would be slower than using a single thread And even if it was, try gracefully stopping a thread that is sleeping on read() or write().

    In the case of crisp for example the seperate thread can wait in a blocking recvfrom() call and use no resources at all.
    This is exactly the same as when using other approaches. The event loops spins anyway, so there is nothing wrong in periodically calling non-blocking select() (which happens under the hood) to see if anything arrived at the socket.

    Threads use up resources by the way...

    \ New data will be handled exactly when they arrived and not too early or too late as it's usually the case with a timer based approach.
    Sure, sky will fall on your head if you see an IRC message 10ms later. What? you are using an LCD screen with refresh rate of 60Hz? Then you will have a bigger delay from your LCD that from using the timer...

    Parallel processes also lead to a better utilization of multicore cpus, which are becoming the standard as of now.
    This is meaningless in this particular situation as both threads couldn't run at once anyway because at some point you have to synchronize them after reading from the socket because you want to display something on the screen.

    People often use threads as an escape from having to write proper event driven code. This is fine as long as the use case is simple. But at some point in time your thread will become more and more complex and you will have more and more problems with synchronizing threads completelly discarding all advantages of using the thread in the first place.

    "GUI freezes unless there is incoming data" title of this thread perfectly illustrates one of synchronization issues that has a chance of arising.

    By the way, most low latency applications try to avoid using blocking calls in favour of non-blocking ones. In Qt we should always try to avoid making synchronous calls and using threads is not always a good option for that.

  11. #9
    Join Date
    Jan 2009
    Location
    Germany
    Posts
    387
    Thanks
    101
    Thanked 15 Times in 15 Posts
    Qt products
    Qt4 Qt5
    Platforms
    Unix/X11 Windows

    Default Re: GUI Freezes unless there's incoming data

    Quote Originally Posted by wysota View Post
    This is completely not true, it would be slower than using a single thread And even if it was, try gracefully stopping a thread that is sleeping on read() or write().
    I don't understand this point (why it's slower?). Can you explain it briefly or point me to a link? As for the gracefully stopping, if you need to do that you are most likely doing something wrong and probably used a thread where you shouldn't have. Same goes if you feel the need to synchronize. In my ten years of programing I have never encountered a single case, where a thread needed to be synchronized or influenced in any way.


    Quote Originally Posted by wysota View Post
    Sure, sky will fall on your head if you see an IRC message 10ms later. What? you are using an LCD screen with refresh rate of 60Hz? Then you will have a bigger delay from your LCD that from using the timer...
    Well no, nothing bad will happen. You can make this simple case work with any approach and there will be no noticable difference to the user. It just doesn't "feel" right, because events occur when the timer says so and not when they actually happen.


    Quote Originally Posted by wysota View Post
    This is meaningless in this particular situation as both threads couldn't run at once anyway because at some point you have to synchronize them after reading from the socket because you want to display something on the screen.
    I don't see why they can't. Or I don't understand what you mean by needing to synchronize. The gui thread reads data periodically from a buffer and displays it on the screen, whatever happens to be in the buffer at that time. And a worker thread runs in the background and refreshes the buffer whenever new data arrives from the socket. These are two perfectly parallel processes with no need for synchronization. With a large amount of data and the requirement for absolute failsafeness (which is not the case in this little IRC example) you might want to use a double buffering technique to thread safe your data buffer, but that doesn't imply a synchronization of the timings of the two threads in any way.

    I actually never looked this up (but stress tested it on a million examples): flipping a pointer is an atomic operation right?


    Quote Originally Posted by wysota View Post
    People often use threads as an escape from having to write proper event driven code.
    Amen to that. I think we both agree that people tend to abuse concepts, be it threads or not. So I think it all comes down to the synchronization detail.

  12. #10
    Join Date
    Jan 2006
    Location
    Warsaw, Poland
    Posts
    33,368
    Thanks
    3
    Thanked 5,018 Times in 4,794 Posts
    Qt products
    Qt3 Qt4 Qt5 Qt/Embedded
    Platforms
    Unix/X11 Windows Android Maemo/MeeGo
    Wiki edits
    10

    Default Re: GUI Freezes unless there's incoming data

    Quote Originally Posted by Cruz View Post
    I don't understand this point (why it's slower?). Can you explain it briefly or point me to a link?
    Because of resource allocation in the kernel and high possibility of preempting the process while sitting in read(), write() or similar system call which is followed by context switch that also takes time.

    As for the gracefully stopping, if you need to do that you are most likely doing something wrong and probably used a thread where you shouldn't have.
    This is exactly the case here - i.e. I'm using IRC (hence the reading thread is blocked on read()) and I want to close the application.

    Same goes if you feel the need to synchronize. In my ten years of programing I have never encountered a single case, where a thread needed to be synchronized or influenced in any way.
    Ok, then please write an application using Qt that reads text from a socket in a worker thread and displays in on a widget. Just to make your life easier and help you avoid solutions I will call "incorrect" - QCoreApplication::postEvent() and QCoreApplication::sendEvent() cause synchronization if ran across threads (sorry, no thread-safe atomic queues implemented yet) and signals and slots across threads are implemented by posting or sending an event.

    Well no, nothing bad will happen. You can make this simple case work with any approach and there will be no noticable difference to the user. It just doesn't "feel" right, because events occur when the timer says so and not when they actually happen.
    Events occur when some timer fires, sees that they occured and notifies you. So there is a timer involved either way and handling many "events" at once can often be much more optimal than handling one at a time (like when inserting data into Qt models). If you are running a semi-busy loop anyway, running a timer won't make things worse as the semi-busy loop will be awoken much more often than the timer fires and the timer can only fire when the loop is awake and running as timers are delivered using events.

    I don't see why they can't. Or I don't understand what you mean by needing to synchronize. The gui thread reads data periodically from a buffer and displays it on the screen, whatever happens to be in the buffer at that time. And a worker thread runs in the background and refreshes the buffer whenever new data arrives from the socket. These are two perfectly parallel processes with no need for synchronization.
    Sorry, you just failed your exam from the multithreading course - you need to synchronize the buffer or else you'll have a race condition.

    With a large amount of data and the requirement for absolute failsafeness (which is not the case in this little IRC example) you might want to use a double buffering technique to thread safe your data buffer, but that doesn't imply a synchronization of the timings of the two threads in any way.
    You can use as many buffers you want but at some point you will have to free a cell in the buffer and at the same time another thread might want to append data to the buffer which is a straight way to a segfault or a deadlock.

    I actually never looked this up (but stress tested it on a million examples): flipping a pointer is an atomic operation right?
    No, it is not bound to be although most architectures assure that, but this is irrelevant here - this is not enough to provide thread safety. Consider a situation where there is a linked list with one item in the list and at the same time one threads wants to append an item to the end of the list and the other wants to remove the item from the list - either you will lose both items (when removing thread is preemptied while doing the operation and before it goes back from sleep the other one has managed to complete it task) or you will get a crash (when the adding thread is interrupted and the removing one manages to delete the item in queue before the other thread is awaken).

  13. #11
    Join Date
    Jan 2009
    Location
    Germany
    Posts
    387
    Thanks
    101
    Thanked 15 Times in 15 Posts
    Qt products
    Qt4 Qt5
    Platforms
    Unix/X11 Windows

    Default Re: GUI Freezes unless there's incoming data

    This is exactly the case here - i.e. I'm using IRC (hence the reading thread is blocked on read()) and I want to close the application.
    What happens if you don't use a thread, not even a gui, you just write a plain old sequential command line application that prints IRC input to stdout. And then you close it with CTRL-C while it's hanging in a read(). Isn't that the same situation?

    Ok, then please write an application using Qt that reads text from a socket in a worker thread and displays in on a widget.
    Actually I did that with a similar technique like I described. A worker thread reads data from a serial connection and notifies an object using signals and slots that new data arrived. The data is passed as argument to the slot. The object builds a widget from the data and addWidget()s it to the gui. You wouldn't call it valid, becuase it uses signals and slots, but nevertheless it worked great so far. So what exactly is wrong with this approach?

    Besides that, in what I was suggesting as solution for the IRC example the gui thread is only _reading_ from the buffer. How can a segfault occur like that? The worst thing that can happen is that you read corrupt half old half new data. And against that you can do this:

    Qt Code:
    1. dataBuffer1[];
    2. dataBuffer2[];
    3. *readPointer = dataBuffer1;
    4. *writePointer = dataBuffer2;
    5. *tempPointer;
    6.  
    7. wroker_thread()
    8. {
    9. forever()
    10. {
    11. // retreive data from somewhere
    12. data = readDataFromSocket();
    13.  
    14. // write data into the current write buffer
    15. writeData(writePointer, data);
    16.  
    17. // flip pointers
    18. tempPointer = readPointer;
    19. readPointer = writePointer; // this needs to be atomic
    20. writePointer = tempPointer;
    21. }
    22.  
    23. main_thread()
    24. {
    25. *copyPointer;
    26.  
    27. forever()
    28. {
    29. // obtain a copy of the read pointer
    30. copyPointer = readPointer; // this needs to be atomic
    31.  
    32. // read the data
    33. data = readData(copyPointer);
    34.  
    35. /* Even if the worker thread flips the readPointer while we are reading here,
    36.   * it doesn't matter because we have a copy. */
    37. }
    38. }
    To copy to clipboard, switch view to plain text mode 

    No, it is not bound to be although most architectures assure that, but this is irrelevant here...
    I think it's very relevant. If flipping a pointer is atomic, then the code above is safe up to the point where the processes run in such a weird asynchronous way, that the worker thread is executed twice while the main thread is still reading, so that it starts to write into the buffer the main thread is reading from.

    All theory aside, I stress tested this approach on Linux and on Windows using C and Java, because I really wanted to know. I had a worker thread writing to the buffer at 100Hz and I spawned first one and then 100 reading threads reading the buffer over and over again with randomized sleeps and some of them at maximum capacity. I recorded how many times the writing of the worker thread and the reading of the main thread overlapped. It was 0. Without the double buffering it occured like 1 out of 1000 times, with it no accidents happened in 1 million read and write operations (over an hour of testing).

    If flipping a buffer is not atomic, a segfault can occur because you could get "half" a pointer.

    And for the case where two threads need to write to the same buffer, which I yet have to encounter, there are mutexes. What's wrong with them?

Similar Threads

  1. Best way to display lots of data fast
    By New2QT in forum Newbie
    Replies: 4
    Last Post: 16th October 2008, 22:46
  2. Replies: 7
    Last Post: 29th August 2008, 10:24
  3. Replies: 4
    Last Post: 19th October 2007, 19:47
  4. speed of setdata - lots of items in treeview
    By Big Duck in forum Qt Programming
    Replies: 4
    Last Post: 6th July 2006, 12:53
  5. Replies: 16
    Last Post: 7th March 2006, 15:57

Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  
Qt is a trademark of The Qt Company.