Add validation to control objects#9
Conversation
There was a problem hiding this comment.
This has a race condition -- m_pValidator could become NULL between the m_pValidator check and m_pValidator->validateChange call. You should copy it to a local first.
There was a problem hiding this comment.
Also not a giant fan of the one line if-statement/body ;).
There was a problem hiding this comment.
that's approved google style!
There was a problem hiding this comment.
+1 for always use braces
if ( ... ) {
return;
}
Good ..
- for recognising control flow.
- clean diffs if we insert a statement before return later
There was a problem hiding this comment.
set() should become bool set() to inform the caller if the set was successful
There was a problem hiding this comment.
Yea :) it's a personal preference and not in the Google C++ style guide. My eyes can easily scan over a 1-liner with return dangling at the end while a block is harder to miss.
|
I wander how much flexibility of the control object we really need (vs. simplicity and performance). I am not sure if this is possible, but I would really like to have an owner/user concept for the control concept. What is the difference between validator and behaviour? Is it possible to join them? |
|
I think validator and behavior are useful to keep separate. I think of validators as a user-defined validation function while the behavior is more intrinsic to the type of the control. |
|
Updated with some notes addressed. Still no enable/disable, but with better pointer ownership and cleaner implementation. |
There was a problem hiding this comment.
Since you are providing an inline definition of a method that uses the class you can't forward declare it. The only reason this works is this file includes control.h which includes the controlvalidator.h.
|
Nice! But this still has the lifecycle and threading issues I pointed out in the last round of comments. The validator outlives the EngineBuffer so any sets to the control after the EngineBuffer is deleted will segfault. Also, no thread should touch EngineBuffer except the callback thread and letting the valdiator have a pointer to EngineBuffer means any thread that changes the play control can call code in EngineBuffer. I think the right solution here is either an enabled/disabled property for controls or an EnabledValidator / DisabledValidator that you set on the play control when the state changes within EngineBuffer. |
|
OK, I thought adding the check in the validator for if(m_pEngine ==NULL) was enough to solve the first problem. but you're right, a more generic enabled/disabled is needed. What about controls that require more validation than yes, no, however, like the seek validation? |
There was a problem hiding this comment.
that's weird because when I didn't have the constructor/destructor the compiler complained about possible undefined behavior without a virtual destructor
There was a problem hiding this comment.
Yea you're right, it should have a virtual destructor. I've done that elsewhere in Mixxx and I should fix that too.
|
Hi Owen, I had time to think a little bit about your validation concept ... and I think it will not work in all cases. We have always the tiny moment between validation and the actual set of the new value. The high priority audio thread can always interrupt the GUI thread in particular this moment. So all validation might be invalid after that. The problem in the current trunk solution is that parts of mixxx rely on the play control as indicator for play. e.g. The he feedback LED might be on without a playing track. One solution might be to handle controls like play as the user command. (You can't stop the user to press play) In addition we need a "playing" or "play_fb"control, which has always the value for the play button feedback LED. Since Mixxx can only react on play commands each time the audio callback is executed. It is fine to evaluate the "play" control there and set the feedback "playing" and the "play" control back to the real "Play value from the engine buffer. Kind regards, Daniel |
|
The fact that validation can be interrupted after it runs is fine I think. If the validator validates a value, it has validated that value regardless of whether the control has been updated in the meantime. Take for example playposition. The user wants to seek to 0 and the current value is 0.5. Let's say there's a validator that makes sure that you set it to a positive number (contrived I know since we support the lead-in) but it validates 0 then is interrupted. The engine runs and updates playposition to 0.6. The GUI thread resumes, and the set to 0 is completed. It is still valid to seek to 0 because that's what the user requested. |
|
Making a play indicator control would work but there is already tons of existing state (skins, controller scripts) using play as a toggle button and indicator combined. |
|
Hi RJ, in your case everything is fine, because we have several seek controls for the user command and the play position control for the "truth" feedback. If a user seeks a track to 0.5 which is currently unloaded nothing bad will happen. Owens Validator concept is fine if the validate rules are not changed during run time. Introducing a play indicator will not effect the current solutions in skins if they are not changed. The still have the "problem" that the playbutton is lit when no track is loaded for a short moment, but so what. All the critical things like AutoDJ and also new skins could work with the new "play indicator" |
|
OK I've added enabled/disabled. I'm worried that we might want to call set() and overwrite the value even if the control is disabled. See how I had to be careful where to set enabled/disabled on the play button. |
|
As I pointed out, there might be a problem with interrupting after changing m_Enabled. What are the controls which have a benefit of this? Where it is really needed? We may offer a thread save solution for this: We may put checking m_Enabled and writing in a critical section. |
|
can we close this one? |
|
oh yeah |
There is no need for signal upstream shoutcast because user can't change options when shoutcast sending is on-air.
Some beat size box improvements
ColumnCache: Verify consistency of all column maps
…h sync
When loading a track that is not yet present in the library (and thus
doesn't have any BPM because it hasn't been analyzed yet) while another
deck is playing and both decks have sync enabled, a debug assertion is
triggered:
DEBUG ASSERT: "isValid()" in function double mixxx::Bpm::value() const at src/track/bpm.h:53
Aborted (core dumped)
The backtrace looks as follows:
#0 0x00007f175c87234c in __pthread_kill_implementation () at /usr/lib/libc.so.6
#1 0x00007f175c8254b8 in raise () at /usr/lib/libc.so.6
#2 0x00007f175c80f534 in abort () at /usr/lib/libc.so.6
#3 0x00007f175cf05ee4 in qt_assert(char const*, char const*, int) () at /usr/lib/libQt5Core.so.5
#4 0x000055deb2e67e1c in mixxx::(anonymous namespace)::handleMessage(QtMsgType, QMessageLogContext const&, QString const&) (type=<optimized out>, context=<optimized out>, input=<optimized out>) at src/util/logging.cpp:355
#5 0x00007f175cf47128 in () at /usr/lib/libQt5Core.so.5
#6 0x00007f175cf3fd8a in () at /usr/lib/libQt5Core.so.5
#7 0x00007f175cf06526 in QMessageLogger::critical(char const*, ...) const () at /usr/lib/libQt5Core.so.5
#8 0x000055deb2e5c720 in mixxx_debug_assert(char const*, char const*, int, char const*) (assertion=assertion@entry=0x55deb39bd0db "isValid()", file=file@entry=0x55deb39bbf30 "src/track/bpm.h", line=line@entry=53, function=function@entry=0x55deb39bbf08 "double mixxx::Bpm::value() const") at gsrc/util/assert.h:9
#9 0x000055deb2ee7e7e in mixxx_debug_assert_return_true(char const*, char const*, int, char const*) (function=0x55deb39bbf08 "double mixxx::Bpm::value() const", line=53, file=0x55deb39bbf30 "src/track/bpm.h", assertion=0x55deb39bd0db "isValid()") at gsrc/util/assert.h:18
#10 mixxx::Bpm::value() const (this=<synthetic pointer>) at src/track/bpm.h:53
#11 mixxx::operator*(mixxx::Bpm, double) (multiple=1, bpm=...) at src/track/bpm.h:160
#12 SyncControl::setLocalBpm(mixxx::Bpm) (this=<optimized out>, localBpm=...) at src/engine/sync/synccontrol.cpp:567
#13 0x000055deb34c7ba3 in EngineBuffer::postProcess(int) (this=0x55deb56eb060, iBufferSize=2048) at src/engine/enginebuffer.cpp:1318
#14 0x000055deb3139023 in EngineMaster::processChannels(int) (this=0x55deb5449440, iBufferSize=<optimized out>) at src/engine/enginemaster.cpp:383
#15 0x000055deb31394f7 in EngineMaster::process(int) (this=0x55deb5449440, iBufferSize=iBufferSize@entry=2048) at src/engine/enginemaster.cpp:410
#16 0x000055deb2f91d0b in SoundManager::onDeviceOutputCallback(long) (this=<optimized out>, iFramesPerBuffer=iFramesPerBuffer@entry=1024) at src/soundio/soundmanager.cpp:596
#17 0x000055deb32dd794 in SoundDevicePortAudio::callbackProcessClkRef(long, float*, float const*, PaStreamCallbackTimeInfo const*, unsigned long) (this=0x55deb553e6b0, framesPerBuffer=1024, out=<optimized out>, in=<optimized out>, timeInfo=<optimized out>, statusFlags=<optimized out>) at src/soundio/sounddeviceportaudio.cpp:965
This happens because `newLocalBpm` is invalid when `localBpm` is
invalid. Trying to do sync decks while no tempo information is available
does not make sense, so we only synchronize decks if the local BPM is
available.
Signed-off-by: Rupesh Chiluka <rchiluka@marvell.com>
No description provided.