This is because for each source object which has signals connected to callables, a receiver object is generated (class PythonQtSignalReceiver) by PythonQt, and each time a callable is connected to a signal of this object, a virtual slot is generated on this receiver, which gets its own, consecutive slot ID. Slot IDs seem to be internally restricted to 16bit (effectively 15bit) by the connection record used by Qt. So after slot ID 32767 connects stop working.
It seems we need to reuse unused slot IDs after 32767. I would propose to start again with the initial start ID and increase it until a free ID is found.
There is a small risk that the reused slot IDs lead to the wrong callable being called in certain circumstances. For this to happen, the following things need to happen:
- A signal "A" of an object is connected to a callable: An receiver object is created as a child of the sender object.
- The QObject::destroyed signal is connected to a callable: This leads to the receiver being reparented, since otherwise this signal might not be received before the receiver is deleted (special life-time code is employed in this case).
- The sender object is moved to another thread. The sender and its receiver object now belong to different threads.
- The sender object emits signal "A". Because the connection type is Qt::AutoConnection, a QMetaCallEvent is put into the event queue of the receiver's thread.
- Signal "A" is disconnected from the callable.
- An undeterminate number of connects and disconnects to callables happen on the sender.
- A signal from the sender object is connected to a different callable, which coincidentally gets the same slot ID as the previous receiver slot (since it is free now)
- Events are processed on the receiver's thread: The QMetaCallEvent is passed to the receiver object, and the slot ID matches that of the new receiver slot: In this case the wrong callable is called.
An easy remedy to prevent this case would be to use Qt::DirectConnection instead of Qt::AutoConnection when connecting to callables. Callables would always be called on the thread that emits the signal, which probably is what users would expect anway. But we probably need to think somewhat more about thread-safety.
This is because for each source object which has signals connected to callables, a receiver object is generated (class PythonQtSignalReceiver) by PythonQt, and each time a callable is connected to a signal of this object, a virtual slot is generated on this receiver, which gets its own, consecutive slot ID. Slot IDs seem to be internally restricted to 16bit (effectively 15bit) by the connection record used by Qt. So after slot ID 32767 connects stop working.
It seems we need to reuse unused slot IDs after 32767. I would propose to start again with the initial start ID and increase it until a free ID is found.
There is a small risk that the reused slot IDs lead to the wrong callable being called in certain circumstances. For this to happen, the following things need to happen:
An easy remedy to prevent this case would be to use Qt::DirectConnection instead of Qt::AutoConnection when connecting to callables. Callables would always be called on the thread that emits the signal, which probably is what users would expect anway. But we probably need to think somewhat more about thread-safety.