Ticket #6330: locklesstfw-v5.1.diff

File locklesstfw-v5.1.diff, 22.2 KB (added by Matthias "mortalmatt" Dahl <devel@…>, 11 years ago)
  • mythtv/libs/libmythtv/ThreadedFileWriterLockless.h

     
     1#ifndef TFW_LOCKLESS_H_
     2#define TFW_LOCKLESS_H_
     3
     4#include <pthread.h>
     5
     6#include <QAtomicInt>
     7#include <QMutex>
     8#include <QWaitCondition>
     9#include <QString>
     10
     11class ThreadedFileWriterLockless
     12{
     13  public:
     14    ThreadedFileWriterLockless(const QString &fname, int flags, mode_t mode);
     15    ~ThreadedFileWriterLockless();
     16
     17    bool Open(void);
     18    long long Seek(long long pos, int whence);
     19    uint Write(const void *data, uint count);
     20
     21    void SetWriteBufferSize(uint newSize = DEFAULT_BUFFER_SIZE);
     22    void SetWriteBufferMinWriteSize(uint newMinSize = MIN_WRITE_SIZE);
     23
     24    uint BufUsed(void) const;
     25    uint BufFree(void) const;
     26
     27    void Sync(void);
     28    void Flush(void);
     29
     30    static uint safe_write(int fd, const void *data, uint count, bool &ok);
     31
     32  protected:
     33    static void *BootWriter(void *);
     34    static void *BootFileAdviser(void *);
     35    void DiskWriterLoop(void);
     36    void FileAdviserLoop(void);
     37
     38    uint WriteFwdFree(int readPos, int writePos) const;
     39    uint ReadFwdFree(int readPos, int writePos) const;
     40
     41    void SetupBuffer(uint reqBufferSize);
     42
     43  private:
     44    // file handling related
     45    QString    fileName;
     46    int        fileFlags;
     47    mode_t     fileMode;
     48    int        fileFD;
     49    QAtomicInt fileMinWriteSize;
     50    long long  fileBytesWritten;
     51
     52    // state flags
     53    QAtomicInt isFlushing;
     54    QAtomicInt isWriterRunning;
     55    QAtomicInt isFileAdviserRunning;
     56    QAtomicInt isInDestructor;
     57    QAtomicInt isIgnoringWrites;
     58
     59    // locks and wait conditions
     60    mutable QMutex bufferWriterLock;
     61    mutable QMutex diskWriterLock;
     62
     63    QWaitCondition bufferEmpty;
     64    QWaitCondition bufferHasData;
     65    QWaitCondition bufferWroteData;
     66
     67    // buffer and related
     68    char       *buffer;
     69    int         bufferSize;
     70    QAtomicInt  bufferReadPos;
     71    QAtomicInt  bufferWritePos;
     72
     73    // threads
     74    pthread_t writer;
     75    pthread_t adviser;
     76
     77  private:
     78    // constants
     79    /// default buffer size
     80    static const int DEFAULT_BUFFER_SIZE;
     81    /// maximum chunk size to write to disk in one go
     82    static const int MAX_WRITE_SIZE;
     83    /// minimum chunk size to write to disk in one go (except when flushing)
     84    static const int MIN_WRITE_SIZE;
     85    /// number of extra bytes to reserve before and after the actual buffer
     86    static const int BUFFER_CUSHION;
     87    /// maximum write errors for a single call to safe_write before giving up
     88    static const int MAX_WRITE_ERRORS;
     89};
     90#endif
  • mythtv/libs/libmythtv/libmythtv.pro

     
    164164HEADERS += transporteditor.h
    165165HEADERS += myth_imgconvert.h
    166166HEADERS += channelgroup.h           channelgroupsettings.h
     167HEADERS += ThreadedFileWriterLockless.h
    167168
    168169# Remove when everything is switched to MythUI
    169170HEADERS += proglist_qt.h
     
    188189SOURCES += channelsettings.cpp      previewgenerator.cpp
    189190SOURCES += transporteditor.cpp
    190191SOURCES += channelgroup.cpp         channelgroupsettings.cpp
     192SOURCES += ThreadedFileWriterLockless.cpp
    191193
    192194contains( CONFIG_SWSCALE, yes ) {
    193195    SOURCES += myth_imgconvert.cpp
  • mythtv/libs/libmythtv/ThreadedFileWriterLockless.cpp

     
     1// ANSI C headers
     2#include <cerrno>
     3#include <cstring>
     4
     5// Unix C headers
     6#include <sys/types.h>
     7#include <sys/stat.h>
     8#include <fcntl.h>
     9#include <unistd.h>
     10#include <signal.h>
     11
     12// MythTV headers
     13#include "ThreadedFileWriterLockless.h"
     14#include "mythcontext.h"
     15#include "compat.h"
     16#include "mythverbose.h"
     17
     18#define LOC QString("TFWL: ")
     19#define LOC_ERR QString("TFWL, Error: ")
     20
     21const int ThreadedFileWriterLockless::DEFAULT_BUFFER_SIZE = 2*1024*1024;
     22const int ThreadedFileWriterLockless::MAX_WRITE_SIZE      = DEFAULT_BUFFER_SIZE / 4;
     23const int ThreadedFileWriterLockless::MIN_WRITE_SIZE      = DEFAULT_BUFFER_SIZE / 32;
     24const int ThreadedFileWriterLockless::BUFFER_CUSHION      = 512;
     25const int ThreadedFileWriterLockless::MAX_WRITE_ERRORS    = 10;
     26
     27/** \class ThreadedFileWriterLockless
     28 *  \brief A (almost) lock-free threaded disk writer that buffers data written
     29 *         to it and writes it to the disk in a seperate thread.
     30 *
     31 *  This class implements a threaded ring buffer. All data written via ::Write()
     32 *  is inserted into the ring buffer while a seperate thread reads the data from
     33 *  the buffer and writes it to disk.
     34 *
     35 *  The implementation is intentionally not thread-safe but reentrant.
     36 */
     37
     38/** \fn ThreadedFileWriterLockless::safe_write(int fd, const void *data, uint count, bool &ok)
     39 *  \brief Writes all data to disk and retries in case of errors.
     40 *
     41 *  The standard POSIX write() can return early with no or just a portion of
     42 *  the desired data written to disk. This function tries to make sure that
     43 *  all data gets written to disk by...
     44 *
     45 *    1) endlessly retrying in case of EAGAIN or EINTR errors
     46 *    2) retrying up to MAX_WRITE_ERRORS times in case of other errors
     47 *    3) writing out the rest amount of data until no data is left
     48 *
     49 *  \param fd    File descriptor
     50 *  \param data  Pointer to data to write
     51 *  \param count Size of data to write in bytes
     52 *
     53 *  \return Number of written bytes
     54 */
     55uint ThreadedFileWriterLockless::safe_write(int fd, const void *data,
     56                                            uint count, bool &ok)
     57{
     58    uint bytesWritten = 0;
     59    int errors        = 0;
     60
     61    while (bytesWritten < count && errors < MAX_WRITE_ERRORS)
     62    {
     63        int ret = write(fd, (char*)data + bytesWritten, count - bytesWritten);
     64
     65        if (ret >= 0)
     66            bytesWritten += ret;
     67        else if (errno != EAGAIN || errno != EINTR)
     68            ++errors;
     69    }
     70
     71    ok = (errors < MAX_WRITE_ERRORS);
     72
     73    return bytesWritten;
     74}
     75
     76/** \fn ThreadedFileWriterLockless::BootWriter(void *tfw)
     77 *  \brief Helper function which simply runs the DiskWriterLoop().
     78 *
     79 *  \param tfw Pointer to ThreadedFileWriterLockless instance
     80 */
     81void *ThreadedFileWriterLockless::BootWriter(void *tfw)
     82{
     83#ifndef USING_MINGW
     84    signal(SIGXFSZ, SIG_IGN);
     85#endif
     86    ((ThreadedFileWriterLockless *)tfw)->DiskWriterLoop();
     87    return NULL;
     88}
     89
     90/** \fn ThreadedFileWriterLockless::BootFileAdviser(void *tfw)
     91 *  \brief Helper function which simply runs the FileAdviserLoop().
     92 *
     93 *  \param tfw Pointer to ThreadedFileWriterLockless instance
     94 */
     95void *ThreadedFileWriterLockless::BootFileAdviser(void *tfw)
     96{
     97    ((ThreadedFileWriterLockless *)tfw)->FileAdviserLoop();
     98    return NULL;
     99}
     100
     101/** \fn ThreadedFileWriterLockless::ThreadedFileWriterLockless(const QString &fname, int flags, mode_t mode)
     102 *  \brief Constructor
     103 *
     104 *  The constructor sets the filename, the access/creation/status flags and
     105 *  the file permissions. It does not create the file nor does it start the
     106 *  writer thread.
     107 *
     108 *  \param fname Filename
     109 *  \param flags Access/Creation/Status flags (see POSIX open())
     110 *  \param mode  File permissions (see POSIX open())
     111 */
     112ThreadedFileWriterLockless::ThreadedFileWriterLockless(const QString &fname,
     113                                                       int flags, mode_t mode) :
     114    // file handling related
     115    fileName(fname), fileFlags(flags), fileMode(mode), fileFD(-1),
     116    fileMinWriteSize(MIN_WRITE_SIZE), fileBytesWritten(0),
     117    // state flags
     118    isFlushing(0), isWriterRunning(0), isFileAdviserRunning(0), isInDestructor(0),
     119    isIgnoringWrites(0),
     120    // buffer and related
     121    buffer(NULL), bufferSize(0), bufferReadPos(0), bufferWritePos(0)
     122{
     123    fileName.detach();
     124}
     125
     126/** \fn ThreadedFileWriterLockless::Open(void)
     127 *  \brief Opens the file we will be writing to and starts the DiskWriter thread.
     128 *
     129 *  \return TRUE if file was opened successfully and DiskWriter thread started
     130 */
     131bool ThreadedFileWriterLockless::Open(void)
     132{
     133    if (isWriterRunning)
     134    {
     135        VERBOSE(VB_IMPORTANT, LOC_ERR +
     136                QString("Opening file '%1' failed. Writer already running")
     137                .arg(fileName));
     138
     139        return false;
     140    }
     141
     142    // those do _not_ need to be atomic since the writer is not running
     143    isFlushing       = 0;
     144    isInDestructor   = 0;
     145    isIgnoringWrites = 0;
     146
     147    bool result = false;
     148
     149    if (fileName == "-")
     150        fileFD = fileno(stdout);
     151    else
     152        fileFD = open(fileName.toAscii().constData(), fileFlags, fileMode);
     153
     154    if (fileFD < 0)
     155    {
     156        VERBOSE(VB_IMPORTANT, LOC_ERR +
     157                QString("Opening file '%1'.").arg(fileName) + ENO);
     158
     159        result = false;
     160    }
     161    else
     162    {
     163        SetupBuffer(DEFAULT_BUFFER_SIZE);
     164        fileMinWriteSize = MIN_WRITE_SIZE;
     165
     166        pthread_create(&writer, NULL, BootWriter, this);
     167        pthread_create(&adviser, NULL, BootFileAdviser, this);
     168/*
     169        if(gContext->GetNumSetting("RealtimePriority", 1))
     170        {
     171            // thread priority in respect to SCHED_RR: 1
     172            struct sched_param sp = {2};
     173
     174            if(!pthread_setschedparam(writer, SCHED_RR, &sp));
     175            {
     176                VERBOSE(VB_GENERAL, LOC +
     177                        "Using realtime priority.");
     178            }
     179            else
     180            {
     181                VERBOSE(VB_IMPORTANT, LOC_ERR +
     182                        "Realtime priority requires sufficient user priviledges.");
     183            }
     184        }
     185*/
     186        result = true;
     187    }
     188
     189    return result;
     190}
     191
     192/** \fn ThreadedFileWriterLockless::~ThreadedFileWriterLockless()
     193 *  \brief Destructor which commits all writes, ends the DiskWriter thread
     194 *         and closes the file.
     195 */
     196ThreadedFileWriterLockless::~ThreadedFileWriterLockless()
     197{
     198    isInDestructor.fetchAndStoreOrdered(1);
     199
     200    if (fileFD >= 0)
     201    {
     202        // wake everyone
     203        bufferHasData.wakeAll();
     204        bufferEmpty.wakeAll();
     205
     206        // if there is still data, flush it to disk
     207        Flush();
     208
     209        pthread_join(writer, NULL);
     210        pthread_join(adviser, NULL);
     211
     212        close(fileFD);
     213    }
     214
     215    if (buffer)
     216        delete [] (buffer - BUFFER_CUSHION);
     217}
     218
     219/** \fn ThreadedFileWriterLockless::Write(const void *data, uint count)
     220 *  \brief Writes data to the internal buffer.
     221 *
     222 *  This function writes data to the internal buffer.
     223 *
     224 *  \param data  Pointer to data
     225 *  \param count Size of data in bytes
     226 *
     227 *  \return Returns the number of bytes written
     228 */
     229uint ThreadedFileWriterLockless::Write(const void *data, uint count)
     230{
     231    if (count == 0 || isIgnoringWrites)
     232        return 0;
     233
     234    uint  totalWritten = 0;
     235    char* dataPtr      = (char*)data;
     236
     237    while (totalWritten < count)
     238    {
     239        // using local copies for efficiency reasons: a) doing arithmetic on
     240        // instances of QAtomicInt is expensive and b) the architecture MythTV
     241        // is currently running on could probably not have native/wait-free
     242        // support for the atomic operation fetchAndStore
     243        int localWritePos = bufferWritePos;
     244
     245        uint writeFwdFree = WriteFwdFree(bufferReadPos, localWritePos);
     246        while (writeFwdFree == 0)
     247        {
     248            bufferWriterLock.lock();
     249            bufferWroteData.wait(&bufferWriterLock, 100);
     250            bufferWriterLock.unlock();
     251
     252            writeFwdFree = WriteFwdFree(bufferReadPos, localWritePos);
     253        }
     254
     255        int chunkSize = (count-totalWritten < writeFwdFree) ? count-totalWritten
     256                                                            : writeFwdFree;
     257
     258        if (localWritePos + chunkSize > bufferSize)
     259        {
     260            uint partSize = bufferSize - localWritePos;
     261            memcpy(buffer + localWritePos, dataPtr, partSize);
     262
     263            totalWritten += partSize;
     264            chunkSize    -= partSize;
     265            dataPtr      += partSize;
     266            localWritePos = 0;
     267        }
     268        memcpy(buffer + localWritePos, dataPtr, chunkSize);
     269
     270        totalWritten  += chunkSize;
     271        localWritePos  = (localWritePos + chunkSize) % bufferSize;
     272        dataPtr       += chunkSize;
     273
     274        bufferWritePos.fetchAndStoreOrdered(localWritePos);
     275
     276        bufferHasData.wakeAll();
     277    }
     278
     279    return totalWritten;
     280}
     281
     282/** \fn ThreadedFileWriterLockless::Seek(long long pos, int whence)
     283 *  \brief Seek to a position within the disk file
     284 *
     285 *  \param pos    New file offset (with regards to whence)
     286 *  \param whence Specifies how to interpret offset (see lseek())
     287 *
     288 *  \return Resulting offset
     289 */
     290long long ThreadedFileWriterLockless::Seek(long long pos, int whence)
     291{
     292    Flush();
     293
     294    return lseek(fileFD, pos, whence);
     295}
     296
     297/** \fn ThreadedFileWriterLockless::Flush(void)
     298 *  \brief Instructs the DiskWriter thread to flush all remaining data in the
     299 *         ring buffer to the disk. If the DiskWriter thread is currently ignoring
     300 *         writes to the disk because of e.g. previous errors, the buffer just
     301 *         gets discarded.
     302 *
     303 *  \warning This call is expensive!
     304 */
     305void ThreadedFileWriterLockless::Flush(void)
     306{
     307    isFlushing.fetchAndStoreOrdered(1);
     308
     309    // wake up DiskWriter thread
     310    bufferHasData.wakeAll();
     311
     312    diskWriterLock.lock();
     313    // loop as long as there is still data in the buffer or an ongoing write()
     314    // is registered which could potentially fill up the buffer
     315    while (BufUsed() > 0)
     316    {
     317        if (!bufferEmpty.wait(&diskWriterLock, 2000))
     318           VERBOSE(VB_IMPORTANT, LOC + "Taking a long time to flush...");
     319    }
     320    diskWriterLock.unlock();
     321
     322    isFlushing.fetchAndStoreOrdered(0);
     323}
     324
     325/** \fn ThreadedFileWriterLockless::Sync(void)
     326 *  \brief Instructs the OS to sync all file data associated data to the disk.
     327 *
     328 *  \warning This is a very expensive call which blocks until the data is on
     329 *           the disk. Some filesystems (e.g. ext3) sync the entire filesystem
     330 *           data to disk, not only the file associated ones. This can cause
     331 *           high latency and delays if used extensively. Use with care.
     332 */
     333void ThreadedFileWriterLockless::Sync(void)
     334{
     335    if (fileFD >= 0)
     336    {
     337#if defined(_POSIX_SYNCHRONIZED_IO) && _POSIX_SYNCHRONIZED_IO > 0
     338        fdatasync(fileFD);
     339#else
     340        fsync(fileFD);
     341#endif
     342    }
     343}
     344
     345/** \fn ThreadedFileWriterLockless::SetWriteBufferSize(uint newSize)
     346 *  \brief Sets the total size of the internal ringbuffer.
     347 *
     348 *  \warning This is an expensive call because all data currently in the ring
     349 *           buffer is flushed to the disk.
     350 *
     351 *  \param newSize New ringbuffer size in bytes
     352 */
     353void ThreadedFileWriterLockless::SetWriteBufferSize(uint newSize)
     354{
     355    if (newSize <= 0)
     356        return;
     357
     358    Flush();
     359    SetupBuffer(newSize);
     360}
     361
     362/** \fn ThreadedFileWriterLockless::SetWriteBufferMinWriteSize(uint newMinSize)
     363 *  \brief Sets the minumum number of bytes to write to disk in a single write.
     364 *
     365 *  \param newMinSize New minimum size in bytes for a single write
     366 */
     367void ThreadedFileWriterLockless::SetWriteBufferMinWriteSize(uint newMinSize)
     368{
     369    fileMinWriteSize.fetchAndStoreOrdered(newMinSize);
     370}
     371
     372/** \fn ThreadedFileWriterLockless::DiskLoop(void)
     373 *  \brief DiskWriter thread which reads data from the ring buffer and writes it
     374 *         out to the disk.
     375 *
     376 *  The DiskWriterLoop() tries to free space as soon as possible but is limited
     377 *  by the minimum and maximum write size for a single write. Setting those
     378 *  higher can increase throughput but also latency.
     379 */
     380void ThreadedFileWriterLockless::DiskWriterLoop(void)
     381{
     382    isWriterRunning.fetchAndStoreOrdered(1);
     383
     384    int readFwdFree = 1;
     385    while (!isInDestructor || ReadFwdFree(bufferReadPos, bufferWritePos) > 0)
     386    {
     387        readFwdFree = ReadFwdFree(bufferReadPos, bufferWritePos);
     388
     389        if (!readFwdFree)
     390            bufferEmpty.wakeAll();
     391
     392        if (!readFwdFree || (!isInDestructor && !isFlushing &&
     393                             readFwdFree < fileMinWriteSize
     394                             && fileBytesWritten >= fileMinWriteSize))
     395        {
     396            diskWriterLock.lock();
     397            bufferHasData.wait(&diskWriterLock, 250);
     398            diskWriterLock.unlock();
     399            continue;
     400        }
     401
     402        int  localReadPos = bufferReadPos;
     403        bool wasWriteOk   = true;
     404        uint chunkSize    = 0;
     405
     406        while (wasWriteOk && (readFwdFree >= fileMinWriteSize
     407                              || (isFlushing && readFwdFree > 0)))
     408        {
     409            if(localReadPos + readFwdFree > bufferSize)
     410            {
     411                int remainder = bufferSize - localReadPos;
     412                chunkSize = (remainder <= MAX_WRITE_SIZE || isFlushing)
     413                             ? remainder : MAX_WRITE_SIZE;
     414            }
     415            else
     416            {
     417                chunkSize = (readFwdFree <= MAX_WRITE_SIZE || isFlushing)
     418                             ? readFwdFree : MAX_WRITE_SIZE;
     419            }
     420
     421            if (!isIgnoringWrites)
     422                chunkSize = safe_write(fileFD, buffer + localReadPos, chunkSize, wasWriteOk);
     423
     424            fileBytesWritten += chunkSize;
     425            localReadPos      = (localReadPos + chunkSize) % bufferSize;
     426
     427            bufferReadPos.fetchAndStoreOrdered(localReadPos);
     428            bufferWroteData.wakeAll();
     429
     430            readFwdFree = ReadFwdFree(localReadPos, bufferWritePos);
     431        }
     432
     433        if (!isIgnoringWrites && !wasWriteOk && (EFBIG == errno))
     434        {
     435            QString msg =
     436                "Maximum file size exceeded by '%1'"
     437                "\n\t\t\t"
     438                "You must either change the process ulimits, configure"
     439                "\n\t\t\t"
     440                "your operating system with \"Large File\" support, or use"
     441                "\n\t\t\t"
     442                "a filesystem which supports 64-bit or 128-bit files."
     443                "\n\t\t\t"
     444                "HINT: FAT32 is a 32-bit filesystem.";
     445
     446            VERBOSE(VB_IMPORTANT, msg.arg(fileName));
     447            isIgnoringWrites.fetchAndStoreOrdered(1);
     448        }
     449    }
     450
     451    isWriterRunning.fetchAndStoreOrdered(0);
     452}
     453
     454/** \fn ThreadedFileWriterLockless::FileAdviserLoop(void)
     455 *  \brief Periodically frees related system file cache.
     456 *
     457 *  This function periodically calls posix_fadvise() to free related system
     458 *  file cache for the active file descriptor. Since the system call can be
     459 *  expensive and cause delay spikes, it was put in a seperate thread.
     460 */
     461void ThreadedFileWriterLockless::FileAdviserLoop(void)
     462{
     463    isFileAdviserRunning.fetchAndStoreOrdered(1);
     464
     465    while (!isInDestructor)
     466    {
     467        if (fileFD >= 0)
     468            posix_fadvise(fileFD, 0, 0, POSIX_FADV_DONTNEED);
     469
     470        sleep(2);
     471    }
     472
     473    isFileAdviserRunning.fetchAndStoreOrdered(0);
     474}
     475
     476/** \fn ThreadedFileWriterLockless::BufUsed(void) const
     477 *  \brief Gets the buffer usage in bytes which are queued for disk write
     478 *
     479 *  \return Buffer usage in bytes
     480 */
     481uint ThreadedFileWriterLockless::BufUsed(void) const
     482{
     483    return (bufferSize-1) - BufFree();
     484}
     485
     486/** \fn ThreadedFileWriterLockless::BufFree(void) const
     487 *  \brief Gets the free buffer space in bytes
     488 *
     489 *  \return Free buffer space in bytes
     490 */
     491uint ThreadedFileWriterLockless::BufFree(void) const
     492{
     493    return WriteFwdFree(bufferReadPos, bufferWritePos);
     494}
     495
     496/** \fn ThreadedFileWriterLockless::WriteFwdFree(void) const
     497 *  \brief Gets the number of bytes available for write to the ring buffer
     498 *         from the write pointer with regards to the passed pointers.
     499 *
     500 *  \return Free buffer space in bytes for write.
     501 */
     502inline uint ThreadedFileWriterLockless::WriteFwdFree(int readPos, int writePos) const
     503{
     504    return (readPos + bufferSize - 1 - writePos) % bufferSize;
     505}
     506
     507/** \fn ThreadedFileWriterLockless::ReadFwdFree(void) const
     508 *  \brief Gets the number of bytes available for read from the ring buffer
     509 *         from the read pointer with regards to the passed pointers.
     510 *
     511 *  \return Bytes available for read from the buffer
     512 */
     513inline uint ThreadedFileWriterLockless::ReadFwdFree(int readPos, int writePos) const
     514{
     515    return (writePos + bufferSize - readPos) % bufferSize;
     516}
     517
     518/** \fn ThreadedFileWriterLockless::SetupBuffer(void)
     519 *  \brief Helper function which sets up the buffer and all necessary pointers.
     520 *
     521 *  \param reqBufferSize Requested buffer size
     522 */
     523void ThreadedFileWriterLockless::SetupBuffer(uint reqBufferSize)
     524{
     525    if (buffer)
     526        delete [] (buffer - BUFFER_CUSHION);
     527
     528    buffer = new char[1 + reqBufferSize + 2 * BUFFER_CUSHION];
     529    bzero(buffer, 1 + reqBufferSize + 2 * BUFFER_CUSHION);
     530
     531    buffer    += BUFFER_CUSHION;
     532    bufferSize = reqBufferSize;
     533
     534    bufferWritePos.fetchAndStoreOrdered(0);
     535    bufferReadPos.fetchAndStoreOrdered(0);
     536}
  • mythtv/libs/libmythtv/RingBuffer.cpp

     
    2323#include "RingBuffer.h"
    2424#include "remotefile.h"
    2525#include "remoteencoder.h"
     26
    2627#include "ThreadedFileWriter.h"
     28#include "ThreadedFileWriterLockless.h"
     29
    2730#include "livetvchain.h"
    2831#include "DVDRingBuffer.h"
    2932#include "util.h"
     
    132135
    133136    if (write)
    134137    {
     138#if QT_VERSION < 0x040400
    135139        tfw = new ThreadedFileWriter(
     140#else
     141        tfw = new ThreadedFileWriterLockless(
     142#endif
    136143            filename, O_WRONLY|O_TRUNC|O_CREAT|O_LARGEFILE, 0644);
    137144
    138145        if (!tfw->Open())
  • mythtv/libs/libmythtv/RingBuffer.h

     
    1616
    1717class RemoteFile;
    1818class RemoteEncoder;
     19
    1920class ThreadedFileWriter;
     21class ThreadedFileWriterLockless;
     22
    2023class DVDRingBufferPriv;
    2124class LiveTVChain;
    2225
     
    118121  private:
    119122    QString filename;
    120123
     124#if QT_VERSION < 0x040400
    121125    ThreadedFileWriter *tfw;
     126#else
     127    ThreadedFileWriterLockless *tfw;
     128#endif
    122129    int fd2;
    123130
    124131    bool writemode;