1
0
mirror of https://github.com/tomahawk-player/tomahawk.git synced 2025-08-12 09:04:33 +02:00

Pimple ControlConnection

This commit is contained in:
Uwe L. Korn
2013-06-16 21:37:16 +02:00
parent d3ce480480
commit 992b6a1a76
3 changed files with 149 additions and 87 deletions

View File

@@ -18,7 +18,7 @@
* along with Tomahawk. If not, see <http://www.gnu.org/licenses/>. * along with Tomahawk. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include "ControlConnection.h" #include "ControlConnection_p.h"
#include "database/Database.h" #include "database/Database.h"
#include "database/DatabaseCommand_CollectionStats.h" #include "database/DatabaseCommand_CollectionStats.h"
@@ -39,10 +39,7 @@ using namespace Tomahawk;
ControlConnection::ControlConnection( Servent* parent ) ControlConnection::ControlConnection( Servent* parent )
: Connection( parent ) : Connection( parent )
, m_dbsyncconn( 0 ) , d_ptr( new ControlConnectionPrivate( this ) )
, m_registered( false )
, m_shutdownOnEmptyPeerInfos( true )
, m_pingtimer( 0 )
{ {
qDebug() << "CTOR controlconnection"; qDebug() << "CTOR controlconnection";
setId("ControlConnection()"); setId("ControlConnection()");
@@ -57,34 +54,39 @@ ControlConnection::ControlConnection( Servent* parent )
ControlConnection::~ControlConnection() ControlConnection::~ControlConnection()
{ {
QReadLocker locker( &m_sourceLock ); Q_D( ControlConnection );
QReadLocker locker( &d->sourceLock );
tDebug( LOGVERBOSE ) << Q_FUNC_INFO << id() << name(); tDebug( LOGVERBOSE ) << Q_FUNC_INFO << id() << name();
if ( !m_source.isNull() ) if ( !d->source.isNull() )
{ {
m_source->setOffline(); d->source->setOffline();
} }
delete m_pingtimer; delete d->pingtimer;
servent()->unregisterControlConnection( this ); servent()->unregisterControlConnection( this );
if ( m_dbsyncconn ) if ( d->dbsyncconn )
m_dbsyncconn->deleteLater(); d->dbsyncconn->deleteLater();
delete d_ptr;
} }
source_ptr source_ptr
ControlConnection::source() const ControlConnection::source() const
{ {
Q_D( const ControlConnection );
// We return a copy of the shared pointer, no need for a longer lock // We return a copy of the shared pointer, no need for a longer lock
QReadLocker locker( &m_sourceLock ); QReadLocker locker( &d->sourceLock );
return m_source; return d->source;
} }
void void
ControlConnection::unbindFromSource() ControlConnection::unbindFromSource()
{ {
QWriteLocker locker( &m_sourceLock ); Q_D( ControlConnection );
m_source.clear(); QWriteLocker locker( &d->sourceLock );
d->source.clear();
} }
@@ -101,10 +103,11 @@ ControlConnection::clone()
void void
ControlConnection::setup() ControlConnection::setup()
{ {
Q_D( ControlConnection );
qDebug() << Q_FUNC_INFO << id() << name(); qDebug() << Q_FUNC_INFO << id() << name();
QWriteLocker sourceLocker( &m_sourceLock ); QWriteLocker sourceLocker( &d->sourceLock );
if ( !m_source.isNull() ) if ( !d->source.isNull() )
{ {
qDebug() << "This source seems to be online already."; qDebug() << "This source seems to be online already.";
Q_ASSERT( false ); Q_ASSERT( false );
@@ -116,24 +119,24 @@ ControlConnection::setup()
tDebug() << "Detected name:" << name() << friendlyName; tDebug() << "Detected name:" << name() << friendlyName;
// setup source and remote collection for this peer // setup source and remote collection for this peer
m_source = SourceList::instance()->get( id(), friendlyName, true ); d->source = SourceList::instance()->get( id(), friendlyName, true );
QSharedPointer<QMutexLocker> locker = m_source->acquireLock(); QSharedPointer<QMutexLocker> locker = d->source->acquireLock();
if ( m_source->setControlConnection( this ) ) if ( d->source->setControlConnection( this ) )
{ {
// We are the new ControlConnection for this source // We are the new ControlConnection for this source
// delay setting up collection/etc until source is synced. // delay setting up collection/etc until source is synced.
// we need it DB synced so it has an ID + exists in DB. // we need it DB synced so it has an ID + exists in DB.
connect( m_source.data(), SIGNAL( syncedWithDatabase() ), connect( d->source.data(), SIGNAL( syncedWithDatabase() ),
SLOT( registerSource() ), Qt::QueuedConnection ); SLOT( registerSource() ), Qt::QueuedConnection );
m_source->setOnline(); d->source->setOnline();
m_pingtimer = new QTimer; d->pingtimer = new QTimer;
m_pingtimer->setInterval( 5000 ); d->pingtimer->setInterval( 5000 );
connect( m_pingtimer, SIGNAL( timeout() ), SLOT( onPingTimer() ) ); connect( d->pingtimer, SIGNAL( timeout() ), SLOT( onPingTimer() ) );
m_pingtimer->start(); d->pingtimer->start();
m_pingtimer_mark.start(); d->pingtimer_mark.start();
} }
else else
{ {
@@ -147,17 +150,18 @@ ControlConnection::setup()
void void
ControlConnection::registerSource() ControlConnection::registerSource()
{ {
QReadLocker sourceLocker( &m_sourceLock ); Q_D( ControlConnection );
QSharedPointer<QMutexLocker> locker = m_source->acquireLock(); QReadLocker sourceLocker( &d->sourceLock );
QSharedPointer<QMutexLocker> locker = d->source->acquireLock();
// Only continue if we are still the ControlConnection associated with this source. // Only continue if we are still the ControlConnection associated with this source.
if ( !m_source.isNull() && m_source->controlConnection() == this ) if ( !d->source.isNull() && d->source->controlConnection() == this )
{ {
qDebug() << Q_FUNC_INFO << m_source->id(); qDebug() << Q_FUNC_INFO << d->source->id();
Source* source = (Source*) sender(); Source* source = (Source*) sender();
Q_UNUSED( source ) Q_UNUSED( source )
Q_ASSERT( source == m_source.data() ); Q_ASSERT( source == d->source.data() );
m_registered = true; d->registered = true;
setupDbSyncConnection(); setupDbSyncConnection();
} }
} }
@@ -166,47 +170,48 @@ ControlConnection::registerSource()
void void
ControlConnection::setupDbSyncConnection( bool ondemand ) ControlConnection::setupDbSyncConnection( bool ondemand )
{ {
QReadLocker locker( &m_sourceLock ); Q_D( ControlConnection );
if ( m_source.isNull() ) QReadLocker locker( &d->sourceLock );
if ( d->source.isNull() )
{ {
// We were unbind from the Source, nothing to do here, just waiting to be deleted. // We were unbind from the Source, nothing to do here, just waiting to be deleted.
return; return;
} }
qDebug() << Q_FUNC_INFO << ondemand << m_source->id() << m_dbconnkey << m_dbsyncconn << m_registered; qDebug() << Q_FUNC_INFO << ondemand << d->source->id() << d->dbconnkey << d->dbsyncconn << d->registered;
if ( m_dbsyncconn || !m_registered ) if ( d->dbsyncconn || !d->registered )
return; return;
Q_ASSERT( m_source->id() > 0 ); Q_ASSERT( d->source->id() > 0 );
if ( !m_dbconnkey.isEmpty() ) if ( !d->dbconnkey.isEmpty() )
{ {
qDebug() << "Connecting to DBSync offer from peer..."; qDebug() << "Connecting to DBSync offer from peer...";
m_dbsyncconn = new DBSyncConnection( servent(), m_source ); d->dbsyncconn = new DBSyncConnection( servent(), d->source );
servent()->createParallelConnection( this, m_dbsyncconn, m_dbconnkey ); servent()->createParallelConnection( this, d->dbsyncconn, d->dbconnkey );
m_dbconnkey.clear(); d->dbconnkey.clear();
} }
else if ( !outbound() || ondemand ) // only one end makes the offer else if ( !outbound() || ondemand ) // only one end makes the offer
{ {
qDebug() << "Offering a DBSync key to peer..."; qDebug() << "Offering a DBSync key to peer...";
m_dbsyncconn = new DBSyncConnection( servent(), m_source ); d->dbsyncconn = new DBSyncConnection( servent(), d->source );
QString key = uuid(); QString key = uuid();
servent()->registerOffer( key, m_dbsyncconn ); servent()->registerOffer( key, d->dbsyncconn );
QVariantMap m; QVariantMap m;
m.insert( "method", "dbsync-offer" ); m.insert( "method", "dbsync-offer" );
m.insert( "key", key ); m.insert( "key", key );
sendMsg( m ); sendMsg( m );
} }
if ( m_dbsyncconn ) if ( d->dbsyncconn )
{ {
connect( m_dbsyncconn, SIGNAL( finished() ), connect( d->dbsyncconn, SIGNAL( finished() ),
m_dbsyncconn, SLOT( deleteLater() ) ); d->dbsyncconn, SLOT( deleteLater() ) );
connect( m_dbsyncconn, SIGNAL( destroyed( QObject* ) ), connect( d->dbsyncconn, SIGNAL( destroyed( QObject* ) ),
SLOT( dbSyncConnFinished( QObject* ) ), Qt::DirectConnection ); SLOT( dbSyncConnFinished( QObject* ) ), Qt::DirectConnection );
} }
} }
@@ -215,11 +220,12 @@ ControlConnection::setupDbSyncConnection( bool ondemand )
void void
ControlConnection::dbSyncConnFinished( QObject* c ) ControlConnection::dbSyncConnFinished( QObject* c )
{ {
Q_D( ControlConnection );
qDebug() << Q_FUNC_INFO << "DBSync connection closed (for now)"; qDebug() << Q_FUNC_INFO << "DBSync connection closed (for now)";
if ( (DBSyncConnection*)c == m_dbsyncconn ) if ( (DBSyncConnection*)c == d->dbsyncconn )
{ {
//qDebug() << "Setting m_dbsyncconn to NULL"; //qDebug() << "Setting m_dbsyncconn to NULL";
m_dbsyncconn = NULL; d->dbsyncconn = NULL;
} }
else else
qDebug() << "Old DbSyncConn destroyed?!"; qDebug() << "Old DbSyncConn destroyed?!";
@@ -229,23 +235,25 @@ ControlConnection::dbSyncConnFinished( QObject* c )
DBSyncConnection* DBSyncConnection*
ControlConnection::dbSyncConnection() ControlConnection::dbSyncConnection()
{ {
if ( !m_dbsyncconn ) Q_D( ControlConnection );
if ( !d->dbsyncconn )
{ {
setupDbSyncConnection( true ); setupDbSyncConnection( true );
// Q_ASSERT( m_dbsyncconn ); // Q_ASSERT( m_dbsyncconn );
} }
return m_dbsyncconn; return d->dbsyncconn;
} }
void void
ControlConnection::handleMsg( msg_ptr msg ) ControlConnection::handleMsg( msg_ptr msg )
{ {
Q_D( ControlConnection );
if ( msg->is( Msg::PING ) ) if ( msg->is( Msg::PING ) )
{ {
// qDebug() << "Received Connection PING, nice." << m_pingtimer_mark.elapsed(); // qDebug() << "Received Connection PING, nice." << m_pingtimer_mark.elapsed();
m_pingtimer_mark.restart(); d->pingtimer_mark.restart();
return; return;
} }
@@ -276,7 +284,7 @@ ControlConnection::handleMsg( msg_ptr msg )
} }
else if ( m.value( "method" ).toString() == "dbsync-offer" ) else if ( m.value( "method" ).toString() == "dbsync-offer" )
{ {
m_dbconnkey = m.value( "key" ).toString() ; d->dbconnkey = m.value( "key" ).toString() ;
setupDbSyncConnection(); setupDbSyncConnection();
} }
else if ( m.value( "method" ) == "protovercheckfail" ) else if ( m.value( "method" ) == "protovercheckfail" )
@@ -302,7 +310,8 @@ ControlConnection::authCheckTimeout()
if ( isReady() ) if ( isReady() )
return; return;
Servent::instance()->queueForAclResult( bareName(), m_peerInfos ); Q_D( ControlConnection );
Servent::instance()->queueForAclResult( bareName(), d->peerInfos );
tDebug( LOGVERBOSE ) << "Closing connection, not authed in time."; tDebug( LOGVERBOSE ) << "Closing connection, not authed in time.";
shutdown(); shutdown();
@@ -312,10 +321,11 @@ ControlConnection::authCheckTimeout()
void void
ControlConnection::onPingTimer() ControlConnection::onPingTimer()
{ {
if ( m_pingtimer_mark.elapsed() >= TCP_TIMEOUT * 1000 ) Q_D( ControlConnection );
if ( d->pingtimer_mark.elapsed() >= TCP_TIMEOUT * 1000 )
{ {
QReadLocker locker( &m_sourceLock ); QReadLocker locker( &d->sourceLock );
qDebug() << "Timeout reached! Shutting down connection to" << m_source->friendlyName(); qDebug() << "Timeout reached! Shutting down connection to" << d->source->friendlyName();
shutdown( true ); shutdown( true );
} }
@@ -326,23 +336,26 @@ ControlConnection::onPingTimer()
void void
ControlConnection::addPeerInfo( const peerinfo_ptr& peerInfo ) ControlConnection::addPeerInfo( const peerinfo_ptr& peerInfo )
{ {
Q_D( ControlConnection );
peerInfo->setControlConnection( this ); peerInfo->setControlConnection( this );
m_peerInfos.insert( peerInfo ); d->peerInfos.insert( peerInfo );
} }
void void
ControlConnection::removePeerInfo( const peerinfo_ptr& peerInfo ) ControlConnection::removePeerInfo( const peerinfo_ptr& peerInfo )
{ {
Q_D( ControlConnection );
peerInfoDebug( peerInfo ) << "Remove peer from control connection:" << name(); peerInfoDebug( peerInfo ) << "Remove peer from control connection:" << name();
Q_ASSERT( peerInfo->controlConnection() == this ); Q_ASSERT( peerInfo->controlConnection() == this );
// TODO: find out why this happens // TODO: find out why this happens
// Q_ASSERT( m_peerInfos.contains( peerInfo ) ); // Q_ASSERT( m_peerInfos.contains( peerInfo ) );
m_peerInfos.remove( peerInfo ); d->peerInfos.remove( peerInfo );
if ( m_peerInfos.isEmpty() && m_shutdownOnEmptyPeerInfos ) if ( d->peerInfos.isEmpty() && d->shutdownOnEmptyPeerInfos )
{ {
shutdown( true ); shutdown( true );
} }
@@ -351,8 +364,9 @@ ControlConnection::removePeerInfo( const peerinfo_ptr& peerInfo )
void void
ControlConnection::setShutdownOnEmptyPeerInfos( bool shutdownOnEmptyPeerInfos ) ControlConnection::setShutdownOnEmptyPeerInfos( bool shutdownOnEmptyPeerInfos )
{ {
m_shutdownOnEmptyPeerInfos = shutdownOnEmptyPeerInfos; Q_D( ControlConnection );
if ( m_peerInfos.isEmpty() && m_shutdownOnEmptyPeerInfos ) d->shutdownOnEmptyPeerInfos = shutdownOnEmptyPeerInfos;
if ( d->peerInfos.isEmpty() && d->shutdownOnEmptyPeerInfos )
{ {
shutdown( true ); shutdown( true );
} }
@@ -362,5 +376,6 @@ ControlConnection::setShutdownOnEmptyPeerInfos( bool shutdownOnEmptyPeerInfos )
const QSet< peerinfo_ptr > const QSet< peerinfo_ptr >
ControlConnection::peerInfos() const ControlConnection::peerInfos() const
{ {
return m_peerInfos; Q_D( const ControlConnection );
return d->peerInfos;
} }

View File

@@ -2,6 +2,7 @@
* *
* Copyright 2010-2011, Christian Muehlhaeuser <muesli@tomahawk-player.org> * Copyright 2010-2011, Christian Muehlhaeuser <muesli@tomahawk-player.org>
* Copyright 2010-2011, Jeff Mitchell <jeff@tomahawk-player.org> * Copyright 2010-2011, Jeff Mitchell <jeff@tomahawk-player.org>
* Copyright 2013, Uwe L. Korn <uwelk@xhochy.com>
* *
* Tomahawk is free software: you can redistribute it and/or modify * Tomahawk is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@@ -27,17 +28,13 @@
#ifndef CONTROLCONNECTION_H #ifndef CONTROLCONNECTION_H
#define CONTROLCONNECTION_H #define CONTROLCONNECTION_H
#include "Typedefs.h"
#include "Connection.h" #include "Connection.h"
#include "DllMacro.h" #include "DllMacro.h"
#include "Typedefs.h"
#include <QReadWriteLock> class ControlConnectionPrivate;
#include <QTime>
#include <QTimer>
class Servent;
class DBSyncConnection; class DBSyncConnection;
class Servent;
class DLLEXPORT ControlConnection : public Connection class DLLEXPORT ControlConnection : public Connection
{ {
@@ -75,23 +72,10 @@ private slots:
void onPingTimer(); void onPingTimer();
private: private:
Q_DECLARE_PRIVATE( ControlConnection )
ControlConnectionPrivate* d_ptr;
void setupDbSyncConnection( bool ondemand = false ); void setupDbSyncConnection( bool ondemand = false );
Tomahawk::source_ptr m_source;
/**
* Lock acces to the source member. A "write" access is only if we change the value of source, not if doing a non-const call.
*/
mutable QReadWriteLock m_sourceLock;
DBSyncConnection* m_dbsyncconn;
QString m_dbconnkey;
bool m_registered;
bool m_shutdownOnEmptyPeerInfos;
QTimer* m_pingtimer;
QTime m_pingtimer_mark;
QSet< Tomahawk::peerinfo_ptr > m_peerInfos;
}; };
#endif // CONTROLCONNECTION_H #endif // CONTROLCONNECTION_H

View File

@@ -0,0 +1,63 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright 2010-2011, Christian Muehlhaeuser <muesli@tomahawk-player.org>
* Copyright 2010-2011, Jeff Mitchell <jeff@tomahawk-player.org>
* Copyright 2013, Uwe L. Korn <uwelk@xhochy.com>
*
* Tomahawk is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Tomahawk is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Tomahawk. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef CONTROLCONNECTION_P_H
#define CONTROLCONNECTION_P_H
#include "ControlConnection.h"
#include <QReadWriteLock>
#include <QTime>
#include <QTimer>
class ControlConnectionPrivate
{
public:
ControlConnectionPrivate( ControlConnection* q )
: q_ptr ( q )
, dbsyncconn( 0 )
, registered( false )
, shutdownOnEmptyPeerInfos( true )
, pingtimer( 0 )
{
}
ControlConnection* q_ptr;
Q_DECLARE_PUBLIC ( ControlConnection )
private:
Tomahawk::source_ptr source;
/**
* Lock acces to the source member. A "write" access is only if we change the value of source, not if doing a non-const call.
*/
mutable QReadWriteLock sourceLock;
DBSyncConnection* dbsyncconn;
QString dbconnkey;
bool registered;
bool shutdownOnEmptyPeerInfos;
QTimer* pingtimer;
QTime pingtimer_mark;
QSet< Tomahawk::peerinfo_ptr > peerInfos;
};
#endif // CONTROLCONNECTION_P_H