Ticket #6330: locklesstfw-v5.3.diff

File locklesstfw-v5.3.diff, 22.4 KB (added by Matthias Dahl <devel@…>, 15 years ago)
  • mythtv/libs/libmythtv/libmythtv.pro

    diff -uNr a/mythtv/libs/libmythtv/libmythtv.pro b/mythtv/libs/libmythtv/libmythtv.pro
    a b  
    156156HEADERS += transporteditor.h        listingsources.h
    157157HEADERS += myth_imgconvert.h
    158158HEADERS += channelgroup.h           channelgroupsettings.h
     159HEADERS += ThreadedFileWriterLockless.h
    159160
    160161# Remove when everything is switched to MythUI
    161162HEADERS += proglist_qt.h
     
    181182SOURCES += transporteditor.cpp
    182183SOURCES += channelgroup.cpp         channelgroupsettings.cpp
    183184SOURCES += myth_imgconvert.cpp
     185SOURCES += ThreadedFileWriterLockless.cpp
    184186
    185187# Remove when everything is switched to MythUI
    186188SOURCES += proglist_qt.cpp
  • mythtv/libs/libmythtv/RingBuffer.cpp

    diff -uNr a/mythtv/libs/libmythtv/RingBuffer.cpp b/mythtv/libs/libmythtv/RingBuffer.cpp
    a b  
    2424#include "RingBuffer.h"
    2525#include "remotefile.h"
    2626#include "remoteencoder.h"
    27 #include "ThreadedFileWriter.h"
     27#include "ThreadedFileWriterLockless.h"
    2828#include "livetvchain.h"
    2929#include "DVDRingBuffer.h"
    3030#include "util.h"
     
    169169        }
    170170        else
    171171        {
    172             tfw = new ThreadedFileWriter(
     172            tfw = new ThreadedFileWriterLockless(
    173173                filename, O_WRONLY|O_TRUNC|O_CREAT|O_LARGEFILE, 0644);
    174174
    175175            if (!tfw->Open())
  • mythtv/libs/libmythtv/RingBuffer.h

    diff -uNr a/mythtv/libs/libmythtv/RingBuffer.h b/mythtv/libs/libmythtv/RingBuffer.h
    a b  
    1616
    1717class RemoteFile;
    1818class RemoteEncoder;
    19 class ThreadedFileWriter;
     19class ThreadedFileWriterLockless;
    2020class DVDRingBufferPriv;
    2121class LiveTVChain;
    2222
     
    120120    QString filename;
    121121    QString subtitlefilename;
    122122
    123     ThreadedFileWriter *tfw;
     123    ThreadedFileWriterLockless *tfw;
    124124    int fd2;
    125125
    126126    bool writemode;
  • mythtv/libs/libmythtv/ThreadedFileWriterLockless.cpp

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

    diff -uNr a/mythtv/libs/libmythtv/ThreadedFileWriterLockless.h b/mythtv/libs/libmythtv/ThreadedFileWriterLockless.h
    a b  
     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