Ticket #1835: 1835-v4.patch
File 1835-v4.patch, 10.0 KB (added by , 18 years ago) |
---|
-
programs/mythbackend/mainserver.cpp
58 58 /** Number of threads in process request thread pool at startup. */ 59 59 #define PRT_STARTUP_THREAD_COUNT 5 60 60 61 QMutex MainServer::truncate_and_close_lock; 62 61 63 class ProcessRequestThread : public QThread 62 64 { 63 65 public: … … 1340 1342 tvchain->DeleteProgram(pginfo); 1341 1343 1342 1344 int err; 1345 int fd = -1; 1343 1346 QString filename = ds->filename; 1344 1347 bool followLinks = gContext->GetNumSetting("DeletesFollowLinks", 0); 1345 1348 1346 VERBOSE(VB_FILE, QString("About to unlink/delete file: %1").arg(filename)); 1347 if (followLinks) 1349 VERBOSE(VB_FILE, QString("About to unlink/delete file: '%1'") 1350 .arg(filename)); 1351 1352 QFileInfo finfo(filename); 1353 if (finfo.isSymLink()) 1348 1354 { 1349 QFileInfo finfo(filename); 1350 if (finfo.isSymLink() && (err = unlink(finfo.readLink().local8Bit()))) 1355 if (followLinks) 1356 fd = OpenAndUnlink(finfo.readLink()); 1357 1358 err = unlink(filename.local8Bit()); 1359 1360 if (err) 1351 1361 { 1352 VERBOSE(VB_IMPORTANT, QString("Error deleting '%1' @ '%2', %3") 1353 .arg(filename).arg(finfo.readLink().local8Bit()) 1354 .arg(strerror(errno))); 1362 VERBOSE(VB_IMPORTANT, 1363 QString("Error deleting '%1'").arg(filename) + ENO); 1355 1364 } 1356 1365 } 1357 if ((err = unlink(filename.local8Bit()))) 1358 VERBOSE(VB_IMPORTANT, QString("Error deleting '%1', %2") 1359 .arg(filename).arg(strerror(errno))); 1366 else 1367 { 1368 fd = OpenAndUnlink(filename); 1369 } 1360 1370 1361 1371 sleep(2); 1362 1372 … … 1376 1386 gContext->dispatch(me); 1377 1387 1378 1388 deletelock.unlock(); 1389 if (fd != -1) 1390 close(fd); 1379 1391 return; 1380 1392 } 1381 1393 … … 1478 1490 delete pginfo; 1479 1491 1480 1492 deletelock.unlock(); 1493 1494 if (fd != -1) 1495 { 1496 m_expirer->TruncatePending(); 1497 TruncateAndClose(m_expirer, fd, ds->filename); 1498 m_expirer->TruncateFinished(); 1499 } 1481 1500 } 1482 1501 1502 /** \fn MainServer::OpenAndUnlink(const QString&) 1503 * \brief Opens a file, unlinks it and returns the file descriptor. 1504 */ 1505 int MainServer::OpenAndUnlink(const QString &filename) 1506 { 1507 QString msg = QString("Error deleting '%1'").arg(filename); 1508 int fd = open(filename.local8Bit(), O_WRONLY); 1509 1510 if (fd == -1) 1511 { 1512 VERBOSE(VB_IMPORTANT, msg + ENO); 1513 return -1; 1514 } 1515 1516 if (unlink(filename.local8Bit())) 1517 { 1518 VERBOSE(VB_IMPORTANT, msg + ENO); 1519 close(fd); 1520 return -1; 1521 } 1522 1523 return fd; 1524 } 1525 1526 /** \fn MainServer::TruncateAndClose(const AutoExpire*,int,const QString&) 1527 * \brief Repeatedly truncate an open file in small increments. 1528 * 1529 * When the file is small enough this closes the file and returns. 1530 * 1531 * NOTE: This aquires a lock so that only one instance of TruncateAndClose() 1532 * is running at a time. 1533 */ 1534 bool MainServer::TruncateAndClose(const AutoExpire *expirer, 1535 int fd, const QString &filename) 1536 { 1537 QMutexLocker locker(&truncate_and_close_lock); 1538 1539 // Time between truncation steps in milliseconds 1540 const size_t sleep_time = 500; 1541 1542 // Compute the truncate increment such that we delete 1543 // 20% faster than the maximum recording rate. 1544 size_t increment; 1545 increment = (expirer->GetMinTruncateRate() * sleep_time + 999) / 1000; 1546 1547 VERBOSE(VB_FILE, 1548 QString("Truncating '%1' by %2 MB every %3 milliseconds") 1549 .arg(filename) 1550 .arg(increment / (1024.0 * 1024.0), 0, 'f', 2) 1551 .arg(sleep_time)); 1552 1553 const QString err_msg = QString("Error truncating '%1'").arg(filename); 1554 1555 size_t fsize = 0; 1556 while (true) 1557 { 1558 // Get the on disk file size and disk block size. 1559 struct stat buf; 1560 fstat(fd, &buf); 1561 fsize = buf.st_blksize * buf.st_blocks; 1562 1563 if (fsize <= increment) 1564 break; 1565 1566 // Round truncate increment up to a blocksize, w/min of 1 block. 1567 increment = ((increment / buf.st_blksize) + 1) * buf.st_blksize; 1568 1569 int err = ftruncate(fd, fsize - increment); 1570 if (err) 1571 { 1572 VERBOSE(VB_IMPORTANT, err_msg + ENO); 1573 return 0 == close(fd); 1574 } 1575 1576 usleep(sleep_time * 1000); 1577 } 1578 1579 bool ok = (0 == close(fd)); 1580 1581 usleep((sleep_time * fsize * 1000) / increment); 1582 1583 VERBOSE(VB_FILE, QString("Finished truncating '%1'").arg(filename)); 1584 1585 return ok; 1586 } 1587 1483 1588 void MainServer::HandleCheckRecordingActive(QStringList &slist, 1484 1589 PlaybackSock *pbs) 1485 1590 { -
programs/mythbackend/mainserver.h
149 149 void AddToChains(LiveTVChain *chain); 150 150 void DeleteChain(LiveTVChain *chain); 151 151 152 static int OpenAndUnlink(const QString &filename); 153 static bool TruncateAndClose(const AutoExpire *expirer, 154 int fd, const QString &filename); 155 152 156 QPtrList<LiveTVChain> liveTVChains; 153 157 QMutex liveTVChainsLock; 154 158 … … 188 192 QMutex deferredDeleteLock; 189 193 QTimer *deferredDeleteTimer; 190 194 QValueList<DeferredDeleteStruct> deferredDeleteList; 195 196 static QMutex truncate_and_close_lock; 191 197 }; 192 198 193 199 #endif -
programs/mythbackend/autoexpire.cpp
53 53 */ 54 54 AutoExpire::AutoExpire(bool runthread, bool master) 55 55 : record_file_prefix("/"), desired_space(3*1024*1024), 56 desired_freq(10), expire_thread_running(runthread), 57 is_master_backend(master), update_pending(false) 56 desired_freq(10), max_record_rate(5*1024*1024), 57 expire_thread_running(runthread), is_master_backend(master), 58 truncates_pending(0), update_pending(false) 58 59 { 59 60 if (runthread) 60 61 { … … 179 180 instance_lock.lock(); 180 181 desired_space = expireMinKB; 181 182 desired_freq = expireFreq; 183 max_record_rate = (totalKBperMin * 1024)/60; 182 184 record_file_prefix = recordFilePrefix; 183 185 instance_lock.unlock(); 184 186 DBG_CALC_PARAM("CalcParams() -- end"); … … 227 229 */ 228 230 void AutoExpire::RunExpirer(void) 229 231 { 230 QTime curTime;231 232 QTime timer; 233 QDateTime curTime; 234 QDateTime next_expire = QDateTime::currentDateTime().addSecs(60); 232 235 233 236 // wait a little for main server to come up and things to settle down 234 237 sleep(20); … … 243 246 244 247 UpdateDontExpireSet(); 245 248 246 curTime = Q Time::currentTime();249 curTime = QDateTime::currentDateTime(); 247 250 248 251 // Expire Short LiveTV files for this backend every 2 minutes 249 if ((curTime. minute() % 2) == 0)252 if ((curTime.time().minute() % 2) == 0) 250 253 ExpireLiveTV(emShortLiveTVPrograms); 251 254 252 255 // Expire normal recordings depending on frequency calculated 253 if ( (curTime.minute() % desired_freq) == 0)256 if (curTime >= next_expire) 254 257 { 258 // Wait for all pending truncates to finish 259 WaitForPendingTruncates(); 260 261 next_expire = 262 QDateTime::currentDateTime().addSecs(desired_freq * 60); 263 255 264 ExpireLiveTV(emNormalLiveTVPrograms); 256 265 257 266 if (is_master_backend) … … 281 290 } 282 291 } 283 292 293 void AutoExpire::TruncatePending(void) 294 { 295 QMutexLocker locker(&truncate_monitor_lock); 296 297 truncates_pending++; 298 299 VERBOSE(VB_FILE, LOC<<truncates_pending<<" file truncates are pending"); 300 } 301 302 void AutoExpire::TruncateFinished(void) 303 { 304 QMutexLocker locker(&truncate_monitor_lock); 305 306 truncates_pending--; 307 308 if (truncates_pending <= 0) 309 { 310 VERBOSE(VB_FILE, LOC + "All file truncates have finished"); 311 truncate_monitor_condition.wakeAll(); 312 } 313 } 314 315 void AutoExpire::WaitForPendingTruncates(void) 316 { 317 QMutexLocker locker(&truncate_monitor_lock); 318 while (truncates_pending > 0) 319 { 320 VERBOSE(VB_FILE, LOC + "Waiting for pending file truncates"); 321 truncate_monitor_condition.wait(&truncate_monitor_lock); 322 } 323 } 324 284 325 /** \fn AutoExpire::ExpireLiveTV(int type) 285 326 * \brief This expires LiveTV programs. 286 327 */ -
programs/mythbackend/autoexpire.h
9 9 10 10 #include <qmap.h> 11 11 #include <qmutex.h> 12 #include <qwaitcondition.h> 12 13 #include <qobject.h> 13 14 14 15 using namespace std; … … 35 36 void CalcParams(vector<EncoderLink*>); 36 37 void FillExpireList(); 37 38 void PrintExpireList(); 39 void TruncatePending(void); 40 void TruncateFinished(void); 41 42 size_t GetMaxRecordRate(void) const 43 { return max_record_rate; } 44 45 size_t GetMinTruncateRate(void) const 46 { return ((max_record_rate * 6) / 5) + 1; } 47 38 48 void GetAllExpiring(QStringList &strList); 39 49 40 50 static void Update(QMap<int, EncoderLink*>*, bool immediately); … … 52 62 bool deleteAll = false); 53 63 void ClearExpireList(void); 54 64 void Sleep(int sleepTime); 65 void WaitForPendingTruncates(void); 55 66 56 67 void UpdateDontExpireSet(void); 57 68 bool IsInDontExpireSet(QString chanid, QDateTime starttime); … … 65 76 QString record_file_prefix; 66 77 size_t desired_space; 67 78 uint desired_freq; 79 size_t max_record_rate; // bytes/sec 68 80 bool expire_thread_running; 69 81 bool is_master_backend; 70 82 83 // Pending truncates monitor 84 mutable QMutex truncate_monitor_lock; 85 QWaitCondition truncate_monitor_condition; 86 int truncates_pending; 87 71 88 // update info 72 89 bool update_pending; 73 90 pthread_t update_thread;