File indexing completed on 2024-04-28 04:49:52
0001 /* 0002 SPDX-FileCopyrightText: 1998-2008 Sebastian Trueg <trueg@k3b.org> 0003 SPDX-License-Identifier: GPL-2.0-or-later 0004 */ 0005 0006 #include "k3bmediacache.h" 0007 #include "k3bmediacache_p.h" 0008 #include "k3bmedium.h" 0009 #include "k3bmedium_p.h" 0010 #include "k3bcddb.h" 0011 #include "k3bdevicemanager.h" 0012 #include "k3bdeviceglobals.h" 0013 #include "k3bcore.h" 0014 #include "k3b_i18n.h" 0015 0016 #include <QDebug> 0017 #include <QThread> 0018 #include <QMutex> 0019 #include <QEvent> 0020 #include <QRandomGenerator> 0021 0022 #include <KCDDB/Client> 0023 0024 0025 0026 K3b::MediaCache::DeviceEntry::DeviceEntry( K3b::MediaCache* c, K3b::Device::Device* dev ) 0027 : medium(dev), 0028 blockedId(0), 0029 cache(c) 0030 { 0031 thread = new K3b::MediaCache::PollThread( this ); 0032 connect( thread, SIGNAL(mediumChanged(K3b::Device::Device*)), 0033 c, SLOT(_k_mediumChanged(K3b::Device::Device*)), 0034 Qt::QueuedConnection ); 0035 connect( thread, SIGNAL(checkingMedium(K3b::Device::Device*,QString)), 0036 c, SIGNAL(checkingMedium(K3b::Device::Device*,QString)), 0037 Qt::QueuedConnection ); 0038 } 0039 0040 0041 K3b::MediaCache::DeviceEntry::~DeviceEntry() 0042 { 0043 delete thread; 0044 } 0045 0046 0047 void K3b::MediaCache::PollThread::run() 0048 { 0049 while( m_deviceEntry->blockedId == 0 ) { 0050 bool unitReady = m_deviceEntry->medium.device()->testUnitReady(); 0051 bool mediumCached = ( m_deviceEntry->medium.diskInfo().diskState() != K3b::Device::STATE_NO_MEDIA ); 0052 0053 // 0054 // we only get the other information in case the disk state changed or if we have 0055 // no info at all (FIXME: there are drives around that are not able to provide a proper 0056 // disk state) 0057 // 0058 if( m_deviceEntry->medium.diskInfo().diskState() == K3b::Device::STATE_UNKNOWN || 0059 unitReady != mediumCached ) { 0060 0061 if( m_deviceEntry->blockedId == 0 ) 0062 emit checkingMedium( m_deviceEntry->medium.device(), QString() ); 0063 0064 // 0065 // we block for writing before the update 0066 // This is important to make sure we do not overwrite a reset operation 0067 // 0068 m_deviceEntry->writeMutex.lock(); 0069 0070 // 0071 // The medium has changed. We need to update the information. 0072 // 0073 K3b::Medium m( m_deviceEntry->medium.device() ); 0074 m.update(); 0075 0076 // block the info since it is not valid anymore 0077 m_deviceEntry->readMutex.lock(); 0078 0079 m_deviceEntry->medium = m; 0080 0081 // the information is valid. let the info go. 0082 m_deviceEntry->readMutex.unlock(); 0083 m_deviceEntry->writeMutex.unlock(); 0084 0085 // 0086 // inform the media cache about the media change 0087 // 0088 if( m_deviceEntry->blockedId == 0 ) 0089 emit mediumChanged( m_deviceEntry->medium.device() ); 0090 } 0091 0092 if( m_deviceEntry->blockedId == 0 ) 0093 QThread::sleep( 2 ); 0094 } 0095 } 0096 0097 0098 0099 0100 0101 // //////////////////////////////////////////////////////////////////////////////// 0102 // MEDIA CACHE IMPL 0103 // //////////////////////////////////////////////////////////////////////////////// 0104 0105 0106 class K3b::MediaCache::Private 0107 { 0108 public: 0109 QMap<K3b::Device::Device*, DeviceEntry*> deviceMap; 0110 KCDDB::Client cddbClient; 0111 0112 K3b::MediaCache* q; 0113 0114 void _k_mediumChanged( K3b::Device::Device* ); 0115 void _k_cddbJobFinished( KJob* job ); 0116 }; 0117 0118 0119 // called from the device thread which updated the medium 0120 void K3b::MediaCache::Private::_k_mediumChanged( K3b::Device::Device* dev ) 0121 { 0122 if ( q->medium( dev ).content() & K3b::Medium::ContentAudio ) { 0123 K3b::CDDB::CDDBJob* job = K3b::CDDB::CDDBJob::queryCddb( q->medium( dev ) ); 0124 connect( job, SIGNAL(result(KJob*)), 0125 q, SLOT(_k_cddbJobFinished(KJob*)) ); 0126 emit q->checkingMedium( dev, i18n( "CDDB Lookup" ) ); 0127 } 0128 else { 0129 emit q->mediumChanged( dev ); 0130 } 0131 } 0132 0133 0134 // once the cddb job is finished the medium is really updated 0135 void K3b::MediaCache::Private::_k_cddbJobFinished( KJob* job ) 0136 { 0137 K3b::CDDB::CDDBJob* cddbJob = dynamic_cast<K3b::CDDB::CDDBJob*>( job ); 0138 K3b::Medium oldMedium = cddbJob->medium(); 0139 0140 // make sure the medium did not change during the job 0141 if ( oldMedium.sameMedium( q->medium( oldMedium.device() ) ) ) { 0142 if ( !job->error() ) { 0143 // update it 0144 deviceMap[oldMedium.device()]->medium.d->cddbInfo = cddbJob->cddbResult(); 0145 emit q->mediumCddbChanged( oldMedium.device() ); 0146 } 0147 0148 emit q->mediumChanged( oldMedium.device() ); 0149 } 0150 } 0151 0152 0153 0154 K3b::MediaCache::MediaCache( QObject* parent ) 0155 : QObject( parent ), 0156 d( new Private() ) 0157 { 0158 d->q = this; 0159 } 0160 0161 0162 K3b::MediaCache::~MediaCache() 0163 { 0164 clearDeviceList(); 0165 delete d; 0166 } 0167 0168 0169 int K3b::MediaCache::blockDevice( K3b::Device::Device* dev ) 0170 { 0171 qDebug() << dev->blockDeviceName(); 0172 DeviceEntry* e = findDeviceEntry( dev ); 0173 if( e ) { 0174 if( e->blockedId ) 0175 return -1; 0176 else { 0177 // block the information 0178 e->readMutex.lock(); 0179 0180 // create (hopefully) unique id 0181 e->blockedId = QRandomGenerator::global()->bounded(RAND_MAX); 0182 0183 // let the info go 0184 e->readMutex.unlock(); 0185 0186 // wait for the thread to stop 0187 e->thread->wait(); 0188 0189 return e->blockedId; 0190 } 0191 } 0192 else 0193 return -1; 0194 } 0195 0196 0197 bool K3b::MediaCache::unblockDevice( K3b::Device::Device* dev, int id ) 0198 { 0199 qDebug() << dev->blockDeviceName(); 0200 DeviceEntry* e = findDeviceEntry( dev ); 0201 if( e && e->blockedId && e->blockedId == id ) { 0202 e->blockedId = 0; 0203 0204 e->medium = K3b::Medium( dev ); 0205 0206 // restart the poll thread 0207 e->thread->start(); 0208 0209 return true; 0210 } 0211 else 0212 return false; 0213 } 0214 0215 0216 bool K3b::MediaCache::isBlocked( K3b::Device::Device* dev ) 0217 { 0218 if( DeviceEntry* e = findDeviceEntry( dev ) ) 0219 return ( e->blockedId != 0 ); 0220 else 0221 return false; 0222 } 0223 0224 0225 K3b::Medium K3b::MediaCache::medium( K3b::Device::Device* dev ) 0226 { 0227 if( DeviceEntry* e = findDeviceEntry( dev ) ) { 0228 e->readMutex.lock(); 0229 K3b::Medium m = e->medium; 0230 e->readMutex.unlock(); 0231 return m; 0232 } 0233 else 0234 return K3b::Medium(); 0235 } 0236 0237 0238 K3b::Device::DiskInfo K3b::MediaCache::diskInfo( K3b::Device::Device* dev ) 0239 { 0240 if( DeviceEntry* e = findDeviceEntry( dev ) ) { 0241 e->readMutex.lock(); 0242 K3b::Device::DiskInfo di = e->medium.diskInfo(); 0243 e->readMutex.unlock(); 0244 return di; 0245 } 0246 else 0247 return K3b::Device::DiskInfo(); 0248 } 0249 0250 0251 K3b::Device::Toc K3b::MediaCache::toc( K3b::Device::Device* dev ) 0252 { 0253 if( DeviceEntry* e = findDeviceEntry( dev ) ) { 0254 e->readMutex.lock(); 0255 K3b::Device::Toc toc = e->medium.toc(); 0256 e->readMutex.unlock(); 0257 return toc; 0258 } 0259 else 0260 return K3b::Device::Toc(); 0261 } 0262 0263 0264 K3b::Device::CdText K3b::MediaCache::cdText( K3b::Device::Device* dev ) 0265 { 0266 if( DeviceEntry* e = findDeviceEntry( dev ) ) { 0267 e->readMutex.lock(); 0268 K3b::Device::CdText cdt = e->medium.cdText(); 0269 e->readMutex.unlock(); 0270 return cdt; 0271 } 0272 else 0273 return K3b::Device::CdText(); 0274 } 0275 0276 0277 QList<int> K3b::MediaCache::writingSpeeds( K3b::Device::Device* dev ) 0278 { 0279 if( DeviceEntry* e = findDeviceEntry( dev ) ) { 0280 e->readMutex.lock(); 0281 QList<int> ws = e->medium.writingSpeeds(); 0282 e->readMutex.unlock(); 0283 return ws; 0284 } 0285 else 0286 return QList<int>(); 0287 } 0288 0289 0290 QString K3b::MediaCache::mediumString( K3b::Device::Device* device, bool useContent ) 0291 { 0292 if( DeviceEntry* e = findDeviceEntry( device ) ) { 0293 return e->medium.shortString( useContent ? Medium::WithContents : Medium::NoStringFlags ); 0294 } 0295 else 0296 return QString(); 0297 } 0298 0299 0300 void K3b::MediaCache::clearDeviceList() 0301 { 0302 qDebug(); 0303 0304 // make all the threads stop 0305 for( QMap<K3b::Device::Device*, DeviceEntry*>::iterator it = d->deviceMap.begin(); 0306 it != d->deviceMap.end(); ++it ) { 0307 it.value()->blockedId = 1; 0308 } 0309 0310 // and remove them 0311 for( QMap<K3b::Device::Device*, DeviceEntry*>::iterator it = d->deviceMap.begin(); 0312 it != d->deviceMap.end(); ++it ) { 0313 qDebug() << " waiting for info thread " << it.key()->blockDeviceName() << " to finish"; 0314 it.value()->thread->wait(); 0315 delete it.value(); 0316 } 0317 0318 d->deviceMap.clear(); 0319 } 0320 0321 0322 void K3b::MediaCache::buildDeviceList( K3b::Device::DeviceManager* dm ) 0323 { 0324 // remember blocked ids 0325 QMap<K3b::Device::Device*, int> blockedIds; 0326 for( QMap<K3b::Device::Device*, DeviceEntry*>::iterator it = d->deviceMap.begin(); 0327 it != d->deviceMap.end(); ++it ) 0328 blockedIds.insert( it.key(), it.value()->blockedId ); 0329 0330 clearDeviceList(); 0331 0332 QList<K3b::Device::Device *> items(dm->allDevices()); 0333 for( QList<K3b::Device::Device *>::const_iterator it = items.constBegin(); 0334 it != items.constEnd(); ++it ) { 0335 d->deviceMap.insert( *it, new DeviceEntry( this, *it ) ); 0336 QMap<K3b::Device::Device*, int>::const_iterator bi_it = blockedIds.constFind( *it ); 0337 if( bi_it != blockedIds.constEnd() ) 0338 d->deviceMap[*it]->blockedId = bi_it.value(); 0339 } 0340 0341 // start all the polling threads 0342 for( QMap<K3b::Device::Device*, DeviceEntry*>::iterator it = d->deviceMap.begin(); 0343 it != d->deviceMap.end(); ++it ) { 0344 if( !it.value()->blockedId ) 0345 it.value()->thread->start(); 0346 } 0347 } 0348 0349 0350 K3b::MediaCache::DeviceEntry* K3b::MediaCache::findDeviceEntry( K3b::Device::Device* dev ) 0351 { 0352 QMap<K3b::Device::Device*, DeviceEntry*>::iterator it = d->deviceMap.find( dev ); 0353 if( it != d->deviceMap.end() ) 0354 return it.value(); 0355 else 0356 return 0; 0357 } 0358 0359 0360 void K3b::MediaCache::lookupCddb( K3b::Device::Device* dev ) 0361 { 0362 K3b::Medium m = medium( dev ); 0363 if ( m.content() & K3b::Medium::ContentAudio ) { 0364 K3b::CDDB::CDDBJob* job = K3b::CDDB::CDDBJob::queryCddb( m ); 0365 connect( job, SIGNAL(result(KJob*)), 0366 this, SLOT(_k_cddbJobFinished(KJob*)) ); 0367 emit checkingMedium( dev, i18n( "CDDB Lookup" ) ); 0368 } 0369 } 0370 0371 0372 void K3b::MediaCache::resetDevice( K3b::Device::Device* dev ) 0373 { 0374 if( DeviceEntry* e = findDeviceEntry( dev ) ) { 0375 qDebug() << "Resetting medium in" << dev->blockDeviceName(); 0376 e->writeMutex.lock(); 0377 e->readMutex.lock(); 0378 e->medium.reset(); 0379 e->readMutex.unlock(); 0380 e->writeMutex.unlock(); 0381 // no need to emit mediumChanged here. The poll thread will act on it soon 0382 } 0383 } 0384 0385 #include "moc_k3bmediacache.cpp" 0386 #include "moc_k3bmediacache_p.cpp"