Results 1 to 6 of 6

Thread: Slot re-entrancy Windows7 using QT dlls

  1. #1
    Join Date
    Mar 2016
    Posts
    3
    Qt products
    Qt5
    Platforms
    MacOS X Windows

    Default Slot re-entrancy Windows7 using QT dlls

    We have a QSpinBox which is connected to a SLOTfor the valueChanged SIGNAL. When the user presses the up arrow, the valueChanged SLOT is called and processing begins. This is, of course, as desired.

    However, while in the SLOT valueChanged we do our processing. When the processing takes too long, we get another valueChanged (but not from the user, from, we believe, inside QT). This causes us to process again (and the spin box value to increment). I will stop and mention at this point that we will be moving to a more multi-threaded approach, but we wanted to get our prototype done before we delved into that. I see now reason why this should not work as expected. an XCode build works as desired.

    To further the issue, if QApplication::processEvents() is called after the long processing, then the valueChanged SLOT is called again; becoming re-entrant. I read an old posting: Preventing a Slot to be called again before it has finished running, which suggested that this was impossible whether asynchronous or synchronous, and/or was due to heap corruption. One poster pointed out some ideas, one with processEvents. With all that being said, I took to writing a simple application that exposed the issue I am having.

    I won't call it a QT bug, but it sure shows the slot being called multiple times for a single user click of the up arrow. It also shows a re-entrancy issue when processEvents() is called and the user has clicked the arrow several times. Attached is the sample code. Now I will walk thru the sample code and the output.

    My question is: Is this a bug or something we are not realizing about slots in a single threaded QT application. Code and output follows.

    Qt Code:
    1. #include "mainwindow.h"
    2.  
    3. #include <QDebug>
    4.  
    5. #include <thread>
    6. #include <chrono>
    7.  
    8. // set to connect/disconnect the slot
    9. #define CONNECT_DISCONNECT_FIX 0
    10. // set PROCESS_EVENTS to 0 to not issue QApplication::processEvents();
    11. #define PROCESS_EVENTS 0
    12. // set SLEEP_MILLISECONDS to how long to delay in slot (< 350 seems to work, 2000 is usually guaranteed
    13. #define SLEEP_MILLISECONDS 2000
    14.  
    15. // show some output
    16. #define DEBUG_OUTPUT(strDescription,bInSlot,nID) qDebug() << (strDescription) << "\n\tID: " << (nID) << "\n\tInSlot: "<< (bInSlot) << "\n\tThreadID: " << _threadid << "\n\tTimeStamp: " << std::chrono::high_resolution_clock::now().time_since_epoch().count() << "\n\n";
    17.  
    18. MainWindow::MainWindow(QWidget* parent) :
    19. QMainWindow(parent)
    20. {
    21. m_UI.setupUi(this);
    22.  
    23. bool bResult = connect(m_UI.spinBox,SIGNAL(valueChanged(int)),this,SLOT(OnSlotSpinBox(int)),Qt::QueuedConnection);
    24. }
    25.  
    26. MainWindow::~MainWindow()
    27. {
    28. }
    29.  
    30. void MainWindow::OnSlotSpinBox(int nValue)
    31. {
    32. #if CONNECT_DISCONNECT_FIX
    33. bool bResult = disconnect(m_UI.spinBox,SIGNAL(valueChanged(int)),this,SLOT(OnSlotSpinBox(int)));
    34. #endif
    35.  
    36. // unique ID
    37. static uint32_t snID = 0;
    38.  
    39. // transfer to local, so it is right when re-entrancy occurs
    40. uint32_t nID = ++snID;
    41.  
    42. static bool sbInSlot = false;
    43. DEBUG_OUTPUT("[enter] OnSlotSpinBox",sbInSlot,nID);
    44.  
    45. if (!sbInSlot) {
    46. sbInSlot = true;
    47.  
    48. DEBUG_OUTPUT("[sleep-start] OnSlotSpinBox",sbInSlot,nID);
    49. std::this_thread::sleep_for(std::chrono::milliseconds(SLEEP_MILLISECONDS));
    50.  
    51. #if PROCESS_EVENTS
    52. DEBUG_OUTPUT("[processEvents-start] OnSlotSpinBox",sbInSlot,nID);
    53. QApplication::processEvents();
    54. DEBUG_OUTPUT("[processEvents-end] OnSlotSpinBox",sbInSlot,nID);
    55. #endif
    56.  
    57. DEBUG_OUTPUT("[sleep-end] OnSlotSpinBox",sbInSlot,nID);
    58.  
    59. sbInSlot = false;
    60. }
    61. else {
    62. DEBUG_OUTPUT("[RE-ENTRANCY DETECTED] OnSlotSpinBox",sbInSlot,nID);
    63. }
    64.  
    65. DEBUG_OUTPUT("[exit] OnSlotSpinBox",sbInSlot,nID);
    66.  
    67. #if CONNECT_DISCONNECT_FIX
    68. bResult = connect(m_UI.spinBox,SIGNAL(valueChanged(int)),this,SLOT(OnSlotSpinBox(int)),Qt::QueuedConnection);
    69. #endif
    70. }
    To copy to clipboard, switch view to plain text mode 

    Scenario #1. Select up arrow once. (no processEvents() or connect/disconnect). The output clearly shows that the SLOT was called twice in a row.
    [enter] OnSlotSpinBox
    ID: 1
    InSlot: false
    ThreadID: 14080
    TimeStamp: 14581510183319621
    [sleep-start] OnSlotSpinBox
    ID: 1
    InSlot: true
    ThreadID: 14080
    TimeStamp: 14581510183319621
    [sleep-end] OnSlotSpinBox
    ID: 1
    InSlot: true
    ThreadID: 14080
    TimeStamp: 14581510203443750
    [exit] OnSlotSpinBox
    ID: 1
    InSlot: false
    ThreadID: 14080
    TimeStamp: 14581510203443750
    [enter] OnSlotSpinBox
    ID: 2
    InSlot: false
    ThreadID: 14080
    TimeStamp: 14581510203443750
    [sleep-start] OnSlotSpinBox
    ID: 2
    InSlot: true
    ThreadID: 14080
    TimeStamp: 14581510203443750
    [sleep-end] OnSlotSpinBox
    ID: 2
    InSlot: true
    ThreadID: 14080
    TimeStamp: 14581510223567879
    [exit] OnSlotSpinBox
    ID: 2
    InSlot: false
    ThreadID: 14080
    TimeStamp: 14581510223567879

    Scenario #2. Select up arrow five times as fast as possible. (processEvents() and no connect/disconnect). The output clearly shows that the SLOT was called while in the slot (re-entrancy).
    [enter] OnSlotSpinBox
    ID: 1
    InSlot: false
    ThreadID: 16500
    TimeStamp: 14581513537474084
    [sleep-start] OnSlotSpinBox
    ID: 1
    InSlot: true
    ThreadID: 16500
    TimeStamp: 14581513537474084
    [processEvents-start] OnSlotSpinBox
    ID: 1
    InSlot: true
    ThreadID: 16500
    TimeStamp: 14581513557598213
    [processEvents-end] OnSlotSpinBox
    ID: 1
    InSlot: true
    ThreadID: 16500
    TimeStamp: 14581513557598213
    [sleep-end] OnSlotSpinBox
    ID: 1
    InSlot: true
    ThreadID: 16500
    TimeStamp: 14581513557598213
    [exit] OnSlotSpinBox
    ID: 1
    InSlot: false
    ThreadID: 16500
    TimeStamp: 14581513557598213
    [enter] OnSlotSpinBox
    ID: 2
    InSlot: false
    ThreadID: 16500
    TimeStamp: 14581513557598213
    [sleep-start] OnSlotSpinBox
    ID: 2
    InSlot: true
    ThreadID: 16500
    TimeStamp: 14581513557598213
    [processEvents-start] OnSlotSpinBox
    ID: 2
    InSlot: true
    ThreadID: 16500
    TimeStamp: 14581513577722342
    [enter] OnSlotSpinBox
    ID: 3
    InSlot: true
    ThreadID: 16500
    TimeStamp: 14581513577722342
    [RE-ENTRANCY DETECTED] OnSlotSpinBox
    ID: 3
    InSlot: true
    ThreadID: 16500
    TimeStamp: 14581513577722342
    [exit] OnSlotSpinBox
    ID: 3
    InSlot: true
    ThreadID: 16500
    TimeStamp: 14581513577722342
    [enter] OnSlotSpinBox
    ID: 4
    InSlot: true
    ThreadID: 16500
    TimeStamp: 14581513577722342
    [RE-ENTRANCY DETECTED] OnSlotSpinBox
    ID: 4
    InSlot: true
    ThreadID: 16500
    TimeStamp: 14581513577722342
    [exit] OnSlotSpinBox
    ID: 4
    InSlot: true
    ThreadID: 16500
    TimeStamp: 14581513577722342
    [enter] OnSlotSpinBox
    ID: 5
    InSlot: true
    ThreadID: 16500
    TimeStamp: 14581513577722342
    [RE-ENTRANCY DETECTED] OnSlotSpinBox
    ID: 5
    InSlot: true
    ThreadID: 16500
    TimeStamp: 14581513577722342
    [exit] OnSlotSpinBox
    ID: 5
    InSlot: true
    ThreadID: 16500
    TimeStamp: 14581513577722342
    [processEvents-end] OnSlotSpinBox
    ID: 2
    InSlot: true
    ThreadID: 16500
    TimeStamp: 14581513577722342
    [sleep-end] OnSlotSpinBox
    ID: 2
    InSlot: true
    ThreadID: 16500
    TimeStamp: 14581513577722342
    [exit] OnSlotSpinBox
    ID: 2
    InSlot: false
    ThreadID: 16500
    TimeStamp: 14581513577722342
    Attached Files Attached Files

  2. #2
    Join Date
    Jan 2006
    Location
    Graz, Austria
    Posts
    8,416
    Thanks
    37
    Thanked 1,544 Times in 1,494 Posts
    Qt products
    Qt3 Qt4 Qt5
    Platforms
    Unix/X11 Windows

    Default Re: Slot re-entrancy Windows7 using QT dlls

    When you do processEvents() then Qt's event loop will process events, such as user interaction.
    If the user interaction results in a value change of the spinbox, it will invoke the slot again.
    So don't do that if you want to avoid artificial reentrancy.

    Also: why do you make a queued connection for within the same thread?
    If you leave it an auto connection, it will be a direct connection and the slot will be called immediately on the signal.
    Currently you can queue up several signal emits.

    Also: I don't see where in your first example you have reentrancy.
    There is
    enter -> sleep start -> sleep end -> exit for ID 1
    then again for ID 2

    So invocation for 1 has ended before 2 starts.

    Cheers,
    _

  3. #3
    Join Date
    Mar 2016
    Posts
    3
    Qt products
    Qt5
    Platforms
    MacOS X Windows

    Default Re: Slot re-entrancy Windows7 using QT dlls

    Thank you for your reply. The original questions still remains unanswered. Why is my slot called twice when do long processing? There is only 1 connect and the user has only clicked once.

    In response to your questions, here are my answers. I hope it helps.

    You wrote...
    When you do processEvents() then Qt's event loop will process events, such as user interaction.
    If the user interaction results in a value change of the spinbox, it will invoke the slot again.
    So don't do that if you want to avoid artificial reentrancy.
    I understand processEvents() processes events, however, this is event should not be "pending" as it is already being handled? Is that not correct? It would seem to me that if processEvents() recalled all events that were in process then there would even be a bigger problem. There was only 1 user interaction which caused the original slot call.

    You wrote...
    Also: why do you make a queued connection for within the same thread?
    If you leave it an auto connection, it will be a direct connection and the slot will be called immediately on the signal.
    Currently you can queue up several signal emits.
    None of the ConnectionType enum's change anything. That was put there as one of the attempts at fixing it.

    You wrote...
    Also: I don't see where in your first example you have reentrancy.
    There is
    enter -> sleep start -> sleep end -> exit for ID 1
    then again for ID 2
    Correct. Scenario #1 does not call processEvents(), therefore there is no re-entrancy. However, there are two calls to the slot, in response to 1 user click.

    Further clarification:

    1. Without processEvents(), the call happens twice when a long processing occurs. Yes, the calls are non-rentrant and can be considered queued. However, there is still only 1 user click; so I would not expect two slot calls.
    2. With processEvents(), the call happens twice and the second call happens while the slot is running (i.e. re-entrancy).

    note: the connect is only called once, so this idea of multiple connects (as stated in other threads) does not apply.

  4. #4
    Join Date
    Jan 2006
    Location
    Graz, Austria
    Posts
    8,416
    Thanks
    37
    Thanked 1,544 Times in 1,494 Posts
    Qt products
    Qt3 Qt4 Qt5
    Platforms
    Unix/X11 Windows

    Default Re: Slot re-entrancy Windows7 using QT dlls

    Is the value for both invocations the same?
    Is there more than one connection?

    Cheers,
    _

  5. #5
    Join Date
    Mar 2016
    Posts
    3
    Qt products
    Qt5
    Platforms
    MacOS X Windows

    Default Re: Slot re-entrancy Windows7 using QT dlls

    Thanks again for your reply.

    Please see the attached code. The outputs are from two scenarios/tests. The first one does NOT call processEvents(). The second one does.

    There is only one connection and it is called once. If you run the code in Windows 7 you will see the problem easily,

    This is not an issue of multiple calls to connect.

    As I debug further, I can extrapolate that that because the sleep takes too long, the signal is resent/requeued. If processEvents() (scenario #2) is called after the timeout, then the signal is processed inline. I would suggest that the QT signal processor is not removing the signal before it is processed. Therefore, in scenario #2 it is processed again (as it is still pending).

  6. #6
    Join Date
    Jan 2006
    Location
    Graz, Austria
    Posts
    8,416
    Thanks
    37
    Thanked 1,544 Times in 1,494 Posts
    Qt products
    Qt3 Qt4 Qt5
    Platforms
    Unix/X11 Windows

    Default Re: Slot re-entrancy Windows7 using QT dlls

    A normally connected signal is a direct method invocation in the single threaded case, so nothing is "sent".

    So assuming you've fixed your connection mess, it would have to be multiple signal emits.

    QSpinBox should only emit its valueChanged() signal when the value changed, hence my question if you are getting two different values.

    Cheers,
    _

Similar Threads

  1. Signal/Slot connection doesn't work across DLLs
    By Liachtei in forum Qt Programming
    Replies: 1
    Last Post: 26th March 2015, 16:08
  2. Qt dlls versus Qt creator dlls;
    By mut in forum Newbie
    Replies: 1
    Last Post: 27th June 2014, 21:04
  3. Build Qt 4.7.3 Using VS2010 Under Windows7..
    By jiapei100 in forum Installation and Deployment
    Replies: 1
    Last Post: 29th August 2011, 09:05
  4. Qt winXP binary on windows7?
    By Teuniz in forum Qt Programming
    Replies: 2
    Last Post: 1st December 2010, 19:35
  5. Qt 4.7.1 on Windows7 (64Bit)
    By caius9090 in forum Installation and Deployment
    Replies: 1
    Last Post: 18th November 2010, 22:59

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
  •  
Digia, Qt and their respective logos are trademarks of Digia Plc in Finland and/or other countries worldwide.