content/media/FileBlockCache.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     2 /* vim:set ts=2 sw=2 sts=2 et cindent: */
     3 /* This Source Code Form is subject to the terms of the Mozilla Public
     4  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
     5  * You can obtain one at http://mozilla.org/MPL/2.0/. */
     7 #include "FileBlockCache.h"
     8 #include "VideoUtils.h"
     9 #include "prio.h"
    10 #include <algorithm>
    12 namespace mozilla {
    14 nsresult FileBlockCache::Open(PRFileDesc* aFD)
    15 {
    16   NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
    17   NS_ENSURE_TRUE(aFD != nullptr, NS_ERROR_FAILURE);
    18   {
    19     MonitorAutoLock mon(mFileMonitor);
    20     mFD = aFD;
    21   }
    22   {
    23     MonitorAutoLock mon(mDataMonitor);
    24     nsresult res = NS_NewThread(getter_AddRefs(mThread),
    25                                 nullptr,
    26                                 MEDIA_THREAD_STACK_SIZE);
    27     mIsOpen = NS_SUCCEEDED(res);
    28     return res;
    29   }
    30 }
    32 FileBlockCache::FileBlockCache()
    33   : mFileMonitor("MediaCache.Writer.IO.Monitor"),
    34     mFD(nullptr),
    35     mFDCurrentPos(0),
    36     mDataMonitor("MediaCache.Writer.Data.Monitor"),
    37     mIsWriteScheduled(false),
    38     mIsOpen(false)
    39 {
    40   MOZ_COUNT_CTOR(FileBlockCache);
    41 }
    43 FileBlockCache::~FileBlockCache()
    44 {
    45   NS_ASSERTION(!mIsOpen, "Should Close() FileBlockCache before destroying");
    46   {
    47     // Note, mThread will be shutdown by the time this runs, so we won't
    48     // block while taking mFileMonitor.
    49     MonitorAutoLock mon(mFileMonitor);
    50     if (mFD) {
    51       PRStatus prrc;
    52       prrc = PR_Close(mFD);
    53       if (prrc != PR_SUCCESS) {
    54         NS_WARNING("PR_Close() failed.");
    55       }
    56       mFD = nullptr;
    57     }
    58   }
    59   MOZ_COUNT_DTOR(FileBlockCache);
    60 }
    63 void FileBlockCache::Close()
    64 {
    65   NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
    66   MonitorAutoLock mon(mDataMonitor);
    68   mIsOpen = false;
    70   if (mThread) {
    71     // We must shut down the thread in another runnable. This is called
    72     // while we're shutting down the media cache, and nsIThread::Shutdown()
    73     // can cause events to run before it completes, which could end up
    74     // opening more streams, while the media cache is shutting down and
    75     // releasing memory etc! Also note we close mFD in the destructor so
    76     // as to not disturb any IO that's currently running.
    77     nsCOMPtr<nsIRunnable> event = new ShutdownThreadEvent(mThread);
    78     mThread = nullptr;
    79     NS_DispatchToMainThread(event);
    80   }
    81 }
    83 nsresult FileBlockCache::WriteBlock(uint32_t aBlockIndex, const uint8_t* aData)
    84 {
    85   MonitorAutoLock mon(mDataMonitor);
    87   if (!mIsOpen)
    88     return NS_ERROR_FAILURE;
    90   // Check if we've already got a pending write scheduled for this block.
    91   mBlockChanges.EnsureLengthAtLeast(aBlockIndex + 1);
    92   bool blockAlreadyHadPendingChange = mBlockChanges[aBlockIndex] != nullptr;
    93   mBlockChanges[aBlockIndex] = new BlockChange(aData);
    95   if (!blockAlreadyHadPendingChange || !mChangeIndexList.Contains(aBlockIndex)) {
    96     // We either didn't already have a pending change for this block, or we
    97     // did but we didn't have an entry for it in mChangeIndexList (we're in the process
    98     // of writing it and have removed the block's index out of mChangeIndexList
    99     // in Run() but not finished writing the block to file yet). Add the blocks
   100     // index to the end of mChangeIndexList to ensure the block is written as
   101     // as soon as possible.
   102     mChangeIndexList.PushBack(aBlockIndex);
   103   }
   104   NS_ASSERTION(mChangeIndexList.Contains(aBlockIndex), "Must have entry for new block");
   106   EnsureWriteScheduled();
   108   return NS_OK;
   109 }
   111 void FileBlockCache::EnsureWriteScheduled()
   112 {
   113   mDataMonitor.AssertCurrentThreadOwns();
   115   if (!mIsWriteScheduled) {
   116     mThread->Dispatch(this, NS_DISPATCH_NORMAL);
   117     mIsWriteScheduled = true;
   118   }
   119 }
   121 nsresult FileBlockCache::Seek(int64_t aOffset)
   122 {
   123   mFileMonitor.AssertCurrentThreadOwns();
   125   if (mFDCurrentPos != aOffset) {
   126     int64_t result = PR_Seek64(mFD, aOffset, PR_SEEK_SET);
   127     if (result != aOffset) {
   128       NS_WARNING("Failed to seek media cache file");
   129       return NS_ERROR_FAILURE;
   130     }
   131     mFDCurrentPos = result;
   132   }
   133   return NS_OK;
   134 }
   136 nsresult FileBlockCache::ReadFromFile(int64_t aOffset,
   137                                       uint8_t* aDest,
   138                                       int32_t aBytesToRead,
   139                                       int32_t& aBytesRead)
   140 {
   141   mFileMonitor.AssertCurrentThreadOwns();
   143   nsresult res = Seek(aOffset);
   144   if (NS_FAILED(res)) return res;
   146   aBytesRead = PR_Read(mFD, aDest, aBytesToRead);
   147   if (aBytesRead <= 0)
   148     return NS_ERROR_FAILURE;
   149   mFDCurrentPos += aBytesRead;
   151   return NS_OK;
   152 }
   154 nsresult FileBlockCache::WriteBlockToFile(int32_t aBlockIndex,
   155                                           const uint8_t* aBlockData)
   156 {
   157   mFileMonitor.AssertCurrentThreadOwns();
   159   nsresult rv = Seek(BlockIndexToOffset(aBlockIndex));
   160   if (NS_FAILED(rv)) return rv;
   162   int32_t amount = PR_Write(mFD, aBlockData, BLOCK_SIZE);
   163   if (amount < BLOCK_SIZE) {
   164     NS_WARNING("Failed to write media cache block!");
   165     return NS_ERROR_FAILURE;
   166   }
   167   mFDCurrentPos += BLOCK_SIZE;
   169   return NS_OK;
   170 }
   172 nsresult FileBlockCache::MoveBlockInFile(int32_t aSourceBlockIndex,
   173                                          int32_t aDestBlockIndex)
   174 {
   175   mFileMonitor.AssertCurrentThreadOwns();
   177   uint8_t buf[BLOCK_SIZE];
   178   int32_t bytesRead = 0;
   179   if (NS_FAILED(ReadFromFile(BlockIndexToOffset(aSourceBlockIndex),
   180                              buf,
   181                              BLOCK_SIZE,
   182                              bytesRead))) {
   183     return NS_ERROR_FAILURE;
   184   }
   185   return WriteBlockToFile(aDestBlockIndex, buf);
   186 }
   188 nsresult FileBlockCache::Run()
   189 {
   190   MonitorAutoLock mon(mDataMonitor);
   191   NS_ASSERTION(!NS_IsMainThread(), "Don't call on main thread");
   192   NS_ASSERTION(!mChangeIndexList.IsEmpty(), "Only dispatch when there's work to do");
   193   NS_ASSERTION(mIsWriteScheduled, "Should report write running or scheduled.");
   195   while (!mChangeIndexList.IsEmpty()) {
   196     if (!mIsOpen) {
   197       // We've been closed, abort, discarding unwritten changes.
   198       mIsWriteScheduled = false;
   199       return NS_ERROR_FAILURE;
   200     }
   202     // Process each pending change. We pop the index out of the change
   203     // list, but leave the BlockChange in mBlockChanges until the change
   204     // is written to file. This is so that any read which happens while
   205     // we drop mDataMonitor to write will refer to the data's source in
   206     // memory, rather than the not-yet up to date data written to file.
   207     // This also ensures we will insert a new index into mChangeIndexList
   208     // when this happens.
   210     // Hold a reference to the change, in case another change
   211     // overwrites the mBlockChanges entry for this block while we drop
   212     // mDataMonitor to take mFileMonitor.
   213     int32_t blockIndex = mChangeIndexList.PopFront();
   214     nsRefPtr<BlockChange> change = mBlockChanges[blockIndex];
   215     NS_ABORT_IF_FALSE(change,
   216       "Change index list should only contain entries for blocks with changes");
   217     {
   218       MonitorAutoUnlock unlock(mDataMonitor);
   219       MonitorAutoLock lock(mFileMonitor);
   220       if (change->IsWrite()) {
   221         WriteBlockToFile(blockIndex, change->mData.get());
   222       } else if (change->IsMove()) {
   223         MoveBlockInFile(change->mSourceBlockIndex, blockIndex);
   224       }
   225     }
   226     // If a new change has not been made to the block while we dropped
   227     // mDataMonitor, clear reference to the old change. Otherwise, the old
   228     // reference has been cleared already.
   229     if (mBlockChanges[blockIndex] == change) {
   230       mBlockChanges[blockIndex] = nullptr;
   231     }
   232   }
   234   mIsWriteScheduled = false;
   236   return NS_OK;
   237 }
   239 nsresult FileBlockCache::Read(int64_t aOffset,
   240                               uint8_t* aData,
   241                               int32_t aLength,
   242                               int32_t* aBytes)
   243 {
   244   MonitorAutoLock mon(mDataMonitor);
   246   if (!mFD || (aOffset / BLOCK_SIZE) > INT32_MAX)
   247     return NS_ERROR_FAILURE;
   249   int32_t bytesToRead = aLength;
   250   int64_t offset = aOffset;
   251   uint8_t* dst = aData;
   252   while (bytesToRead > 0) {
   253     int32_t blockIndex = static_cast<int32_t>(offset / BLOCK_SIZE);
   254     int32_t start = offset % BLOCK_SIZE;
   255     int32_t amount = std::min(BLOCK_SIZE - start, bytesToRead);
   257     // If the block is not yet written to file, we can just read from
   258     // the memory buffer, otherwise we need to read from file.
   259     int32_t bytesRead = 0;
   260     nsRefPtr<BlockChange> change = mBlockChanges[blockIndex];
   261     if (change && change->IsWrite()) {
   262       // Block isn't yet written to file. Read from memory buffer.
   263       const uint8_t* blockData = change->mData.get();
   264       memcpy(dst, blockData + start, amount);
   265       bytesRead = amount;
   266     } else {
   267       if (change && change->IsMove()) {
   268         // The target block is the destination of a not-yet-completed move
   269         // action, so read from the move's source block from file. Note we
   270         // *don't* follow a chain of moves here, as a move's source index
   271         // is resolved when MoveBlock() is called, and the move's source's
   272         // block could be have itself been subject to a move (or write)
   273         // which happened *after* this move was recorded.
   274         blockIndex = mBlockChanges[blockIndex]->mSourceBlockIndex;
   275       }
   276       // Block has been written to file, either as the source block of a move,
   277       // or as a stable (all changes made) block. Read the data directly
   278       // from file.
   279       nsresult res;
   280       {
   281         MonitorAutoUnlock unlock(mDataMonitor);
   282         MonitorAutoLock lock(mFileMonitor);
   283         res = ReadFromFile(BlockIndexToOffset(blockIndex) + start,
   284                            dst,
   285                            amount,
   286                            bytesRead);
   287       }
   288       NS_ENSURE_SUCCESS(res,res);
   289     }
   290     dst += bytesRead;
   291     offset += bytesRead;
   292     bytesToRead -= bytesRead;
   293   }
   294   *aBytes = aLength - bytesToRead;
   295   return NS_OK;
   296 }
   298 nsresult FileBlockCache::MoveBlock(int32_t aSourceBlockIndex, int32_t aDestBlockIndex)
   299 {
   300   NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
   301   MonitorAutoLock mon(mDataMonitor);
   303   if (!mIsOpen)
   304     return NS_ERROR_FAILURE;
   306   mBlockChanges.EnsureLengthAtLeast(std::max(aSourceBlockIndex, aDestBlockIndex) + 1);
   308   // The source block's contents may be the destination of another pending
   309   // move, which in turn can be the destination of another pending move,
   310   // etc. Resolve the final source block, so that if one of the blocks in
   311   // the chain of moves is overwritten, we don't lose the reference to the
   312   // contents of the destination block.
   313   int32_t sourceIndex = aSourceBlockIndex;
   314   BlockChange* sourceBlock = nullptr;
   315   while ((sourceBlock = mBlockChanges[sourceIndex]) &&
   316           sourceBlock->IsMove()) {
   317     sourceIndex = sourceBlock->mSourceBlockIndex;
   318   }
   320   if (mBlockChanges[aDestBlockIndex] == nullptr ||
   321       !mChangeIndexList.Contains(aDestBlockIndex)) {
   322     // Only add another entry to the change index list if we don't already
   323     // have one for this block. We won't have an entry when either there's
   324     // no pending change for this block, or if there is a pending change for
   325     // this block and we're in the process of writing it (we've popped the
   326     // block's index out of mChangeIndexList in Run() but not finished writing
   327     // the block to file yet.
   328     mChangeIndexList.PushBack(aDestBlockIndex);
   329   }
   331   // If the source block hasn't yet been written to file then the dest block
   332   // simply contains that same write. Resolve this as a write instead.
   333   if (sourceBlock && sourceBlock->IsWrite()) {
   334     mBlockChanges[aDestBlockIndex] = new BlockChange(sourceBlock->mData.get());
   335   } else {
   336     mBlockChanges[aDestBlockIndex] = new BlockChange(sourceIndex);
   337   }
   339   EnsureWriteScheduled();
   341   NS_ASSERTION(mChangeIndexList.Contains(aDestBlockIndex),
   342     "Should have scheduled block for change");
   344   return NS_OK;
   345 }
   347 } // End namespace mozilla.

mercurial