> I do not like the idea that ControlObject receives signals. For me it is not clear in which thread it lives. The only signal it gets is a DirectConnection so the thread shouldn't matter. > And I would like to allow two QObjects in the same thread communicate with signals. Could you explain more? I don't think I get it. > What about this: > * An Object has a COT member with set(double value); > * The COT calls set(value, this) of the CO > * The CO emits valueChanged(double value, QObject* sender) in any case > * COT is connected to this signal and remits valueChanged(double value) in case this != sender > This allows that all other QObjects will receive the valueChange signals regardless in which thread it lives. > The resend of COT is "cheap" a direct connection in any case. Yea, I also consider DirectConnection to be cheap performance-wise. Basically, what I've implemented is almost exactly what you're describing except it has some additional benefits. Think of it like this: * I have renamed ControlObject to ControlNumericPrivate. * I pulled out all the logic that was previously specialized by sub-classing ControlObject into ControlBehavior. * All of the previous ControlObject classes (CO, ControlPotmeter, ControlPushButton, etc.) have now been converted into ControlObjectThreads except they also create a CNP and set its behavior if it does not exist. > Code that is resend resistant, can work with CO without COT the wrapper. No code in Mixxx has been consciously written to be resend resistant except the slots that intentionally listen to both valueChanged and valueChangedFromEngine. If any other code is resend resistant then that is purely an accident :). Making CO emit valueChanged() for every set() is super dangerous. The benefits are: * Separating the behavior of a control from its type. In the future when we have multiple control types that you can lookup it will be handy to be able to represent the behavior separately from the plumbing of the values. It could also allow injection of logic into a control from outside. For example, if you wanted to make a custom control that only allowed set() under a certain circumstance, you could sub-class a behavior to describe the logic you desire and then set your custom behavior on your control. * No more master/slave relationship between CO and COT. The reason it was this way was because the CO system was designed to only be used in the engine. Today controls are defined and used everywhere in Mixxx and it's really more of a shared key-value store than it is a way to communicate with the engine. Plus, we have to deal with ugly things like NULL-ifying our pointer to the CO when it is deleted in the COT and every time you want to create a COT, you have to call ControlObject::getControl() first. The way I have it, there is still a master/slave but the master is not available to anyone for direct use. CNP is the master, both CO and COT are slaves. * This will benefit the engine-control-refactor branch. In that branch, CO is not used to communicate with the engine anymore. There is a new type, a CallbackControl which is connected to the CO system via a FIFO of control changes. The reason for this is that it solves our shared-state issues in the engine by making valueChanged() signals fire in the engine callback thread and not in whatever thread called CO::set(). This allows us to get rid of mutexes in the engine code. In a world where CNP exists, it's easier for CallbackControl to act as just another slave to CNP, just like CO and COT.