1
0
mirror of https://github.com/tomahawk-player/tomahawk.git synced 2025-08-13 17:43:59 +02:00

Queue up spotify db ops if playlist is busy, otherwise we hit a race condition

This commit is contained in:
Leo Franchi
2012-04-13 13:47:12 -04:00
parent 78a8c3cfc4
commit 7169c6d070
5 changed files with 77 additions and 5 deletions

View File

@@ -86,6 +86,7 @@ SpotifyPlaylistUpdater::init()
connect( playlist().data(), SIGNAL( tracksInserted( QList<Tomahawk::plentry_ptr>, int ) ), this, SLOT( tomahawkTracksInserted( QList<Tomahawk::plentry_ptr>, int ) ) ); connect( playlist().data(), SIGNAL( tracksInserted( QList<Tomahawk::plentry_ptr>, int ) ), this, SLOT( tomahawkTracksInserted( QList<Tomahawk::plentry_ptr>, int ) ) );
connect( playlist().data(), SIGNAL( tracksRemoved( QList<Tomahawk::query_ptr> ) ), this, SLOT( tomahawkTracksRemoved( QList<Tomahawk::query_ptr> ) ) ); connect( playlist().data(), SIGNAL( tracksRemoved( QList<Tomahawk::query_ptr> ) ), this, SLOT( tomahawkTracksRemoved( QList<Tomahawk::query_ptr> ) ) );
connect( playlist().data(), SIGNAL( renamed( QString, QString ) ), this, SLOT( tomahawkPlaylistRenamed( QString, QString ) ) ); connect( playlist().data(), SIGNAL( renamed( QString, QString ) ), this, SLOT( tomahawkPlaylistRenamed( QString, QString ) ) );
connect( playlist().data(), SIGNAL( revisionLoaded( Tomahawk::PlaylistRevision ) ), this, SLOT( playlistRevisionLoaded() ), Qt::QueuedConnection ); // Queued so that in playlist.cpp:443 we let the playlist clear its own queue first
// TODO reorders in a playlist // TODO reorders in a playlist
} }
@@ -147,6 +148,19 @@ SpotifyPlaylistUpdater::checkDeleteDialog() const
} }
void
SpotifyPlaylistUpdater::playlistRevisionLoaded()
{
if ( m_queuedOps.isEmpty() ) // nothing queued
return;
if ( playlist()->busy() ) // not ready yet, we'll get another revision loaded
return;
_detail::Closure* next = m_queuedOps.dequeue();
next->forceInvoke();
}
void void
SpotifyPlaylistUpdater::saveToSettings( const QString& group ) const SpotifyPlaylistUpdater::saveToSettings( const QString& group ) const
@@ -204,6 +218,14 @@ SpotifyPlaylistUpdater::sync() const
void void
SpotifyPlaylistUpdater::spotifyTracksAdded( const QVariantList& tracks, const QString& startPosId, const QString& newRev, const QString& oldRev ) SpotifyPlaylistUpdater::spotifyTracksAdded( const QVariantList& tracks, const QString& startPosId, const QString& newRev, const QString& oldRev )
{ {
if( playlist()->busy() )
{
// We might still be waiting for a add/remove tracks command to finish, so the entries we get here might be stale
// wait for any to be complete
m_queuedOps << NewClosure( 0, "", this, SLOT(spotifyTracksAdded(QVariantList, QString, QString, QString)), tracks, startPosId, newRev, oldRev );
return;
}
const QList< query_ptr > queries = variantToQueries( tracks ); const QList< query_ptr > queries = variantToQueries( tracks );
qDebug() << Q_FUNC_INFO << "inserting tracks in middle of tomahawk playlist, from spotify command!" << tracks << startPosId << newRev << oldRev; qDebug() << Q_FUNC_INFO << "inserting tracks in middle of tomahawk playlist, from spotify command!" << tracks << startPosId << newRev << oldRev;
@@ -227,7 +249,7 @@ SpotifyPlaylistUpdater::spotifyTracksAdded( const QVariantList& tracks, const QS
if ( pos == -1 || pos > entries.size() ) if ( pos == -1 || pos > entries.size() )
pos = entries.size(); pos = entries.size();
qDebug() << Q_FUNC_INFO << "inserting tracks at position:" << pos; qDebug() << Q_FUNC_INFO << "inserting tracks at position:" << pos << "(playlist has current size:" << entries << ")";
m_blockUpdatesForNextRevision = true; m_blockUpdatesForNextRevision = true;
playlist()->insertEntries( queries, pos, playlist()->currentrevision() ); playlist()->insertEntries( queries, pos, playlist()->currentrevision() );
@@ -237,6 +259,14 @@ SpotifyPlaylistUpdater::spotifyTracksAdded( const QVariantList& tracks, const QS
void void
SpotifyPlaylistUpdater::spotifyTracksRemoved( const QVariantList& trackIds, const QString& newRev, const QString& oldRev ) SpotifyPlaylistUpdater::spotifyTracksRemoved( const QVariantList& trackIds, const QString& newRev, const QString& oldRev )
{ {
if( playlist()->busy() )
{
// We might still be waiting for a add/remove tracks command to finish, so the entries we get here might be stale
// wait for any to be complete
m_queuedOps << NewClosure( 0, "", this, SLOT(spotifyTracksRemoved(QVariantList, QString, QString)), trackIds, newRev, oldRev );
return;
}
qDebug() << Q_FUNC_INFO << "remove tracks in middle of tomahawk playlist, from spotify command!" << trackIds << newRev << oldRev; qDebug() << Q_FUNC_INFO << "remove tracks in middle of tomahawk playlist, from spotify command!" << trackIds << newRev << oldRev;
// Uh oh, dont' want to get out of sync!! // Uh oh, dont' want to get out of sync!!
// Q_ASSERT( m_latestRev == oldRev ); // Q_ASSERT( m_latestRev == oldRev );
@@ -285,6 +315,14 @@ SpotifyPlaylistUpdater::spotifyTracksRemoved( const QVariantList& trackIds, cons
void void
SpotifyPlaylistUpdater::spotifyPlaylistRenamed( const QString& title, const QString& newRev, const QString& oldRev ) SpotifyPlaylistUpdater::spotifyPlaylistRenamed( const QString& title, const QString& newRev, const QString& oldRev )
{ {
if( playlist()->busy() )
{
// We might still be waiting for a add/remove tracks command to finish, so the entries we get here might be stale
// wait for any to be complete
m_queuedOps << NewClosure( 0, "", this, SLOT(spotifyPlaylistRenamed(QString, QString, QString)), title, newRev, oldRev );
return;
}
Q_UNUSED( newRev ); Q_UNUSED( newRev );
Q_UNUSED( oldRev ); Q_UNUSED( oldRev );
/// @note to self: should do some checking before trying to update /// @note to self: should do some checking before trying to update
@@ -309,6 +347,13 @@ void
SpotifyPlaylistUpdater::spotifyTracksMoved( const QVariantList& tracks, const QString& newRev, const QString& oldRev ) SpotifyPlaylistUpdater::spotifyTracksMoved( const QVariantList& tracks, const QString& newRev, const QString& oldRev )
{ {
// TODO // TODO
// if( playlist()->busy() )
// {
// // We might still be waiting for a add/remove tracks command to finish, so the entries we get here might be stale
// // wait for any to be complete
// m_queuedOps << NewClosure( 0, "", this, "spotifyPlaylistRenamed", title, newRev, oldRev );
// return;
// }
} }

View File

@@ -20,7 +20,9 @@
#define SPOTIFYPLAYLISTUPDATER_H #define SPOTIFYPLAYLISTUPDATER_H
#include "playlist/PlaylistUpdaterInterface.h" #include "playlist/PlaylistUpdaterInterface.h"
#include "utils/closure.h"
#include <QQueue>
#include <QVariant> #include <QVariant>
namespace Tomahawk { namespace Tomahawk {
@@ -53,6 +55,7 @@ public:
QString spotifyId() const { return m_spotifyId; } QString spotifyId() const { return m_spotifyId; }
public slots:
/// Spotify callbacks when we are directly instructed from the resolver /// Spotify callbacks when we are directly instructed from the resolver
void spotifyTracksAdded( const QVariantList& tracks, const QString& startPosId, const QString& newRev, const QString& oldRev ); void spotifyTracksAdded( const QVariantList& tracks, const QString& startPosId, const QString& newRev, const QString& oldRev );
void spotifyTracksRemoved( const QVariantList& tracks, const QString& newRev, const QString& oldRev ); void spotifyTracksRemoved( const QVariantList& tracks, const QString& newRev, const QString& oldRev );
@@ -72,6 +75,8 @@ private slots:
void onTracksRemovedReturn( const QString& msgType, const QVariantMap& msg ); void onTracksRemovedReturn( const QString& msgType, const QVariantMap& msg );
void checkDeleteDialog() const; void checkDeleteDialog() const;
void playlistRevisionLoaded();
private: private:
void init(); void init();
@@ -86,6 +91,7 @@ private:
bool m_blockUpdatesForNextRevision; bool m_blockUpdatesForNextRevision;
bool m_sync; bool m_sync;
QQueue<_detail::Closure*> m_queuedOps;
#ifndef ENABLE_HEADLESS #ifndef ENABLE_HEADLESS
static QPixmap* s_typePixmap; static QPixmap* s_typePixmap;
#endif #endif

View File

@@ -308,7 +308,7 @@ ScriptResolver::cmdExited( int code, QProcess::ExitStatus status )
return; return;
} }
if ( m_num_restarts < 10 ) if ( m_num_restarts < 0 )
{ {
m_num_restarts++; m_num_restarts++;
tLog() << "*** Restart num" << m_num_restarts; tLog() << "*** Restart num" << m_num_restarts;

View File

@@ -54,7 +54,17 @@ Closure::Closure(QObject* sender,
Closure::~Closure() { Closure::~Closure() {
} }
void Closure::forceInvoke()
{
Invoked();
}
void Closure::Connect(QObject* sender, const char* signal) { void Closure::Connect(QObject* sender, const char* signal) {
if (!sender)
return;
bool success = connect(sender, signal, SLOT(Invoked())); bool success = connect(sender, signal, SLOT(Invoked()));
Q_ASSERT(success); Q_ASSERT(success);
success = connect(sender, SIGNAL(destroyed()), SLOT(Cleanup())); success = connect(sender, SIGNAL(destroyed()), SLOT(Cleanup()));

View File

@@ -18,6 +18,8 @@
#ifndef CLOSURE_H #ifndef CLOSURE_H
#define CLOSURE_H #define CLOSURE_H
#include "dllmacro.h"
#include <tr1/functional> #include <tr1/functional>
#include <QMetaMethod> #include <QMetaMethod>
@@ -29,7 +31,7 @@
namespace _detail { namespace _detail {
class ClosureArgumentWrapper { class DLLEXPORT ClosureArgumentWrapper {
public: public:
virtual ~ClosureArgumentWrapper() {} virtual ~ClosureArgumentWrapper() {}
@@ -49,7 +51,7 @@ class ClosureArgument : public ClosureArgumentWrapper {
T data_; T data_;
}; };
class Closure : public QObject, boost::noncopyable { class DLLEXPORT Closure : public QObject, boost::noncopyable {
Q_OBJECT Q_OBJECT
public: public:
@@ -67,6 +69,15 @@ class Closure : public QObject, boost::noncopyable {
virtual ~Closure(); virtual ~Closure();
/**
* If you don't this Closure to act on a signal, but just act like
* a closure in that it saves some args and delivers them on demand later
*
* Only call this is you passed a null QObject* as a sender! Otherwise you
* might delete your object twice :)
*/
void forceInvoke();
private slots: private slots:
void Invoked(); void Invoked();
void Cleanup(); void Cleanup();
@@ -84,7 +95,7 @@ class Closure : public QObject, boost::noncopyable {
boost::scoped_ptr<const ClosureArgumentWrapper> val3_; boost::scoped_ptr<const ClosureArgumentWrapper> val3_;
}; };
class SharedPointerWrapper { class DLLEXPORT SharedPointerWrapper {
public: public:
virtual ~SharedPointerWrapper() {} virtual ~SharedPointerWrapper() {}
virtual QObject* data() const = 0; virtual QObject* data() const = 0;