Why does PyQt crashes without information? (exit code 0xC0000409)

45,417

PyQt is thread-safe to the same extent that Qt is thread-safe. The Qt docs will tell you which parts of their API are guaranteed to be so, and under what circumstances.

Cross-thread signals are thread-safe, so calling the updatePB method in your example is okay. Your ConnDisconn method is not thread-safe, but that has got nothing to do with PyQt or Qt - it's just a consequence of how you wrote it. The serialEnabled attribute could be read/written by two threads simultaneously, so the behaviour is strictly undefined. A thread-safe way of writing this would be to use a mutex, like so:

class serialThreadC(QThread):
    updateOutBox = QtCore.pyqtSignal(str)
    updateStatus = QtCore.pyqtSignal(int)

    def __init__(self):
        super(serialThreadC, self).__init__()
        self.ser = False
        self.state = 0
        self._mutex = QMutex()
        self.serialEnabled = False   

    def ConnDisconn(self):
        self._mutex.lock()
        self.serialEnabled = not self.serialEnabled
        self._mutex.unlock()

    def run(self):
        while True:
            if self.state == -3 or self.state == -2:
                self._mutex.lock()
                if self.serialEnabled:
                    self.updatePB(20)
                self._mutex.unlock()
            elif self.state == 0:
                self._mutex.lock()
                if self.serialEnabled:
                    self.updatePB(20)
                self._mutex.unlock()

(NB: if you're using any kind of IDE or debugger, and you are getting unexpected errors or crashes, your first step in diagnosing the problem should always be to test the code in a standard console. Quite often, the IDE or debugger itself can be the cause of the problem, or may mask error messages comming either from Python or from underlying libraries, such as Qt).

Share:
45,417
brazoayeye
Author by

brazoayeye

Updated on December 23, 2021

Comments

  • brazoayeye
    brazoayeye over 2 years

    I'm trying to develop a software with PyQt, but I often get stuck on software crashes without debug information (only the exit code 0xC0000409). I'm using QThread, and I wrote a system like this:

    class serialThreadC(QThread):
        updateOutBox = QtCore.pyqtSignal(str)
        updateStatus = QtCore.pyqtSignal(int)
    
        def __init__(self):
            super(serialThreadC, self).__init__()
            self.ser = False
            self.state = 0
            self.serialEnabled = False
    
        def run(self):
            while True:
                if self.state == -3 or self.state == -2:
                    if self.SerialEnabled:
                        self.updatePB(20)
                elif self.state == 0:
                    if self.serialEnabled:
                        self.updatePB(20)
    
        def ConnDisconn(self):
            self.serialEnabled = not self.serialEnabled
    
        def updatePB(self, stat):
            self.state = stat
            self.updateStatus.emit(self.state)
    
    serialThread = serialThreadC()
    serialThread.start()
    
    ## sw is a QDialog already loaded
    serialThread.updateOutBox.connect(sw.updateOutBox)
    serialThread.updateStatus.connect(sw.updateStatus)
    
    sw.PB_ConnDisconn.clicked.connect(serialThread.ConnDisconn)
    

    I have crashes when I read/write serialEnabled in run() or in ConnDisconn(). I know that PyQt is not thread-safe and that a wrong handling of variables gives crashes of my type, but I can't understand what is wrong with my code. My idea (maybe wrong) is that all serialThread methods are executed on the same thread, also if they are connected to a gui (main thread). Is that wrong? In the same way, I emit events from serialThread and I connected them to the GUI, but that never gave me problems.

    Can you see the mistake I made? Is there a way to debug the code if there is a crash without other infos? (I use PyCharm 2017.1.3).

  • brazoayeye
    brazoayeye over 6 years
    I don't get the point. Since you told that the ConnDisconn(self) is a different thread than serialThread (that makes sense, ConnDisconn is connected to a GUI signal) the same should be for sw.updateStatus (that's not in the main thread). In the sw.updateStatus I MyButton.setText(), is this operation allowed? And more, is it also forbidden to read variables concurrently using different threads?
  • ekhumoro
    ekhumoro over 6 years
    @brazoayeye. I did not say that ConnDisconn is in a different thread (and it isn't, it lives in the main thread, and is called by the main thread). But I did say that updatePB is okay, because it emits a cross-thread signal, which is guaranteed (by Qt) to be thread-safe. So yes, as a direct consequence of that, it is safe to call setText(). This is all standard stuff in PyQt, and there are dozens of similar questions about it on SO. It is never forbidden to read/write anything by multiple threads - that is the whole problem! (And that is what the mutex is for).
  • ekhumoro
    ekhumoro over 6 years
    @brazoayeye. I should add, though, that for your specific example, the mutex is probably overkill, because (I assume) there is only ever one thread that modifies serialEnabled (i.e. the main thread). So your example code looks okay, even though it is not 100% strictly correct. As far as fixing your code is concerned, the most important part of my answer is probably the note at the bottom. The rest just tries to answer the more general questions you asked.
  • brazoayeye
    brazoayeye over 6 years
    if ConnDisconn lives in the main thread because it's called by the main thread with signal-slots, why doesn't sw.updateStatus lives in the serialThread since it's called by serialThread in a mostly identical way? Can you post most relevant questions-tutorial to deepen the topic?
  • ekhumoro
    ekhumoro over 6 years
    @brazoayeye. Because serialThread also lives in the main thread. The QThread class is not itself a thread - it is merely a class which manages an underlying thread provided by the OS. So your serialThread and its methods all live in the main thread. The only part of your example that is executed outside the main thread is the code in the run method. This calls updatePB, which emits a signal. Qt automatically detects when a signal is sent from one thread to another, and so does it in a thread-safe way (by posting an event to the event-loop of the receiving thread).
  • Sourabh Desai
    Sourabh Desai over 3 years
    I used anaconda terminal to run the code and voila, I spotted the bug! Thanks @ekhumoro