Skip to content

Add validation to control objects#9

Closed
ywwg wants to merge 7 commits into
mixxxdj:masterfrom
ywwg:covalidation
Closed

Add validation to control objects#9
ywwg wants to merge 7 commits into
mixxxdj:masterfrom
ywwg:covalidation

Conversation

@ywwg
Copy link
Copy Markdown
Member

@ywwg ywwg commented Jun 8, 2013

No description provided.

Comment thread mixxx/src/control/control.cpp Outdated
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also not a giant fan of the one line if-statement/body ;).

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that's approved google style!

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1 for always use braces
if ( ... ) {
return;
}

Good ..

  • for recognising control flow.
  • clean diffs if we insert a statement before return later

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

set() should become bool set() to inform the caller if the set was successful

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

@daschuer
Copy link
Copy Markdown
Member

daschuer commented Jun 9, 2013

I wander how much flexibility of the control object we really need (vs. simplicity and performance).
This affects the behavior and the validator. I think in general if it is good to have them exactly there but with this NULL pointer approach we have to deal with updates for the objects when they are not fully setup.

I am not sure if this is possible, but I would really like to have an owner/user concept for the control concept.
The owner is responsible to setup the control object and once it is finished, it can be released for usage.

What is the difference between validator and behaviour? Is it possible to join them?

@rryan
Copy link
Copy Markdown
Member

rryan commented Jun 11, 2013

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.

@ywwg
Copy link
Copy Markdown
Member Author

ywwg commented Jun 11, 2013

Updated with some notes addressed. Still no enable/disable, but with better pointer ownership and cleaner implementation.

Comment thread mixxx/src/controlobject.h Outdated
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

@rryan
Copy link
Copy Markdown
Member

rryan commented Jun 11, 2013

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.

@ywwg
Copy link
Copy Markdown
Member Author

ywwg commented Jun 11, 2013

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?

Comment thread mixxx/src/control/controlvalidator.h Outdated
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that's weird because when I didn't have the constructor/destructor the compiler complained about possible undefined behavior without a virtual destructor

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yea you're right, it should have a virtual destructor. I've done that elsewhere in Mixxx and I should fix that too.

@daschuer
Copy link
Copy Markdown
Member

Hi Owen,

I had time to think a little bit about your validation concept ... and I think it will not work in all cases.
Sorry about this late concerns.

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

@rryan
Copy link
Copy Markdown
Member

rryan commented Jun 11, 2013

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.

@rryan
Copy link
Copy Markdown
Member

rryan commented Jun 11, 2013

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.

@daschuer
Copy link
Copy Markdown
Member

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"

@ywwg
Copy link
Copy Markdown
Member Author

ywwg commented Jun 11, 2013

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.

@daschuer
Copy link
Copy Markdown
Member

As I pointed out, there might be a problem with interrupting after changing m_Enabled.
If we offer a general solution for this, it opens the door for race conditions later on other controls as well.

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.
But I like a clean "play indicator" solution more.

@daschuer daschuer mentioned this pull request Aug 6, 2013
@daschuer
Copy link
Copy Markdown
Member

can we close this one?

@ywwg
Copy link
Copy Markdown
Member Author

ywwg commented Aug 21, 2013

oh yeah

@ywwg ywwg closed this Aug 21, 2013
@daschuer daschuer mentioned this pull request Dec 28, 2014
daschuer referenced this pull request in daschuer/mixxx Oct 19, 2015
There is no need for signal upstream shoutcast because user can't change options when shoutcast sending is on-air.
daschuer pushed a commit that referenced this pull request May 25, 2017
Some beat size box improvements
@daschuer daschuer mentioned this pull request Aug 13, 2017
19 tasks
@daschuer daschuer mentioned this pull request Jun 3, 2018
@daschuer daschuer mentioned this pull request Jun 11, 2018
11 tasks
Be-ing pushed a commit that referenced this pull request Dec 30, 2018
uklotzde pushed a commit that referenced this pull request Dec 17, 2020
ColumnCache: Verify consistency of all column maps
ywwg pushed a commit that referenced this pull request Mar 7, 2022
…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.
koushikcs562 pushed a commit to koushikcs562/mixxx that referenced this pull request Dec 23, 2025
Signed-off-by: Rupesh Chiluka <rchiluka@marvell.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants