Ticket #1835: 1835-v4.patch

File 1835-v4.patch, 10.0 KB (added by danielk, 13 years ago)

Reviewed, but untested version of patch.

  • programs/mythbackend/mainserver.cpp

     
    5858/** Number of threads in process request thread pool at startup. */
    5959#define PRT_STARTUP_THREAD_COUNT 5
    6060
     61QMutex MainServer::truncate_and_close_lock;
     62
    6163class ProcessRequestThread : public QThread
    6264{
    6365  public:
     
    13401342        tvchain->DeleteProgram(pginfo);
    13411343
    13421344    int err;
     1345    int fd = -1;
    13431346    QString filename = ds->filename;
    13441347    bool followLinks = gContext->GetNumSetting("DeletesFollowLinks", 0);
    13451348
    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())
    13481354    {
    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)
    13511361        {
    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);
    13551364        }
    13561365    }
    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    }
    13601370   
    13611371    sleep(2);
    13621372
     
    13761386        gContext->dispatch(me);
    13771387
    13781388        deletelock.unlock();
     1389        if (fd != -1)
     1390            close(fd);
    13791391        return;
    13801392    }
    13811393
     
    14781490    delete pginfo;
    14791491
    14801492    deletelock.unlock();
     1493
     1494    if (fd != -1)
     1495    {
     1496        m_expirer->TruncatePending();
     1497        TruncateAndClose(m_expirer, fd, ds->filename);
     1498        m_expirer->TruncateFinished();
     1499    }
    14811500}
    14821501
     1502/** \fn MainServer::OpenAndUnlink(const QString&)
     1503 *  \brief Opens a file, unlinks it and returns the file descriptor.
     1504 */
     1505int 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 */
     1534bool 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
    14831588void MainServer::HandleCheckRecordingActive(QStringList &slist,
    14841589                                            PlaybackSock *pbs)
    14851590{
  • programs/mythbackend/mainserver.h

     
    149149    void AddToChains(LiveTVChain *chain);
    150150    void DeleteChain(LiveTVChain *chain);
    151151
     152    static int  OpenAndUnlink(const QString &filename);
     153    static bool TruncateAndClose(const AutoExpire *expirer,
     154                                 int fd, const QString &filename);
     155
    152156    QPtrList<LiveTVChain> liveTVChains;
    153157    QMutex liveTVChainsLock;
    154158
     
    188192    QMutex deferredDeleteLock;
    189193    QTimer *deferredDeleteTimer;
    190194    QValueList<DeferredDeleteStruct> deferredDeleteList;
     195
     196    static QMutex truncate_and_close_lock;
    191197};
    192198
    193199#endif
  • programs/mythbackend/autoexpire.cpp

     
    5353 */
    5454AutoExpire::AutoExpire(bool runthread, bool master)
    5555    : 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)
    5859{
    5960    if (runthread)
    6061    {
     
    179180    instance_lock.lock();
    180181    desired_space      = expireMinKB;
    181182    desired_freq       = expireFreq;
     183    max_record_rate    = (totalKBperMin * 1024)/60;
    182184    record_file_prefix = recordFilePrefix;
    183185    instance_lock.unlock();
    184186    DBG_CALC_PARAM("CalcParams() -- end");
     
    227229 */
    228230void AutoExpire::RunExpirer(void)
    229231{
    230     QTime curTime;
    231232    QTime timer;
     233    QDateTime curTime;
     234    QDateTime next_expire = QDateTime::currentDateTime().addSecs(60);
    232235
    233236    // wait a little for main server to come up and things to settle down
    234237    sleep(20);
     
    243246
    244247        UpdateDontExpireSet();
    245248
    246         curTime = QTime::currentTime();
     249        curTime = QDateTime::currentDateTime();
    247250
    248251        // Expire Short LiveTV files for this backend every 2 minutes
    249         if ((curTime.minute() % 2) == 0)
     252        if ((curTime.time().minute() % 2) == 0)
    250253            ExpireLiveTV(emShortLiveTVPrograms);
    251254
    252255        // Expire normal recordings depending on frequency calculated
    253         if ((curTime.minute() % desired_freq) == 0)
     256        if (curTime >= next_expire)
    254257        {
     258            // Wait for all pending truncates to finish
     259            WaitForPendingTruncates();
     260
     261            next_expire =
     262                QDateTime::currentDateTime().addSecs(desired_freq * 60);
     263
    255264            ExpireLiveTV(emNormalLiveTVPrograms);
    256265
    257266            if (is_master_backend)
     
    281290    }
    282291}
    283292
     293void 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
     302void 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
     315void 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
    284325/** \fn AutoExpire::ExpireLiveTV(int type)
    285326 *  \brief This expires LiveTV programs.
    286327 */
  • programs/mythbackend/autoexpire.h

     
    99
    1010#include <qmap.h>
    1111#include <qmutex.h>
     12#include <qwaitcondition.h>
    1213#include <qobject.h>
    1314
    1415using namespace std;
     
    3536    void CalcParams(vector<EncoderLink*>);
    3637    void FillExpireList();
    3738    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
    3848    void GetAllExpiring(QStringList &strList);
    3949
    4050    static void Update(QMap<int, EncoderLink*>*, bool immediately);
     
    5262                            bool deleteAll = false);
    5363    void ClearExpireList(void);
    5464    void Sleep(int sleepTime);
     65    void WaitForPendingTruncates(void);
    5566
    5667    void UpdateDontExpireSet(void);
    5768    bool IsInDontExpireSet(QString chanid, QDateTime starttime);
     
    6576    QString       record_file_prefix;
    6677    size_t        desired_space;
    6778    uint          desired_freq;
     79    size_t        max_record_rate; // bytes/sec
    6880    bool          expire_thread_running;
    6981    bool          is_master_backend;
    7082
     83    // Pending truncates monitor
     84    mutable QMutex truncate_monitor_lock;
     85    QWaitCondition truncate_monitor_condition;
     86    int            truncates_pending;
     87
    7188    // update info
    7289    bool          update_pending;
    7390    pthread_t     update_thread;