LCOV - code coverage report
Current view: top level - dom/media - FileBlockCache.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 0 235 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 30 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       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/. */
       6             : 
       7             : #include "FileBlockCache.h"
       8             : #include "MediaCache.h"
       9             : #include "MediaPrefs.h"
      10             : #include "mozilla/SharedThreadPool.h"
      11             : #include "VideoUtils.h"
      12             : #include "prio.h"
      13             : #include <algorithm>
      14             : #include "nsAnonymousTemporaryFile.h"
      15             : #include "mozilla/dom/ContentChild.h"
      16             : #include "nsXULAppAPI.h"
      17             : 
      18             : namespace mozilla {
      19             : 
      20             : #undef LOG
      21             : LazyLogModule gFileBlockCacheLog("FileBlockCache");
      22             : #define LOG(x, ...) MOZ_LOG(gFileBlockCacheLog, LogLevel::Debug, \
      23             :   ("%p " x, this, ##__VA_ARGS__))
      24             : 
      25             : static void
      26           0 : CloseFD(PRFileDesc* aFD)
      27             : {
      28             :   PRStatus prrc;
      29           0 :   prrc = PR_Close(aFD);
      30           0 :   if (prrc != PR_SUCCESS) {
      31           0 :     NS_WARNING("PR_Close() failed.");
      32             :   }
      33           0 : }
      34             : 
      35             : void
      36           0 : FileBlockCache::SetCacheFile(PRFileDesc* aFD)
      37             : {
      38           0 :   LOG("SetFD(aFD=%p) mThread=%p", aFD, mThread.get());
      39             : 
      40           0 :   if (!aFD) {
      41             :     // Failed to get a temporary file. Shutdown.
      42           0 :     Close();
      43           0 :     return;
      44             :   }
      45             :   {
      46           0 :     MutexAutoLock lock(mFileMutex);
      47           0 :     mFD = aFD;
      48             :   }
      49             :   {
      50           0 :     MutexAutoLock lock(mDataMutex);
      51           0 :     if (mThread) {
      52             :       // Still open, complete the initialization.
      53           0 :       mInitialized = true;
      54           0 :       if (mIsWriteScheduled) {
      55             :         // A write was scheduled while waiting for FD. We need to run/dispatch a
      56             :         // task to service the request.
      57           0 :         nsCOMPtr<nsIRunnable> event = mozilla::NewRunnableMethod(
      58             :           "FileBlockCache::SetCacheFile -> PerformBlockIOs",
      59             :           this,
      60           0 :           &FileBlockCache::PerformBlockIOs);
      61           0 :         mThread->Dispatch(event.forget(), NS_DISPATCH_NORMAL);
      62             :       }
      63           0 :       return;
      64             :     }
      65             :   }
      66             :   // We've been closed while waiting for the file descriptor.
      67             :   // Close the file descriptor we've just received, if still there.
      68           0 :   MutexAutoLock lock(mFileMutex);
      69           0 :   if (mFD) {
      70           0 :     CloseFD(mFD);
      71           0 :     mFD = nullptr;
      72             :   }
      73             : }
      74             : 
      75             : nsresult
      76           0 : FileBlockCache::Init()
      77             : {
      78           0 :   MutexAutoLock mon(mDataMutex);
      79           0 :   if (mThread) {
      80           0 :     LOG("Init() again");
      81             :     // Just discard pending changes, assume MediaCache won't read from
      82             :     // blocks it hasn't written to.
      83           0 :     mChangeIndexList.clear();
      84           0 :     mBlockChanges.Clear();
      85           0 :     return NS_OK;
      86             :   }
      87             : 
      88           0 :   LOG("Init()");
      89             : 
      90           0 :   nsresult rv = NS_NewNamedThread("FileBlockCache",
      91           0 :                                   getter_AddRefs(mThread),
      92             :                                   nullptr,
      93           0 :                                   SharedThreadPool::kStackSize);
      94           0 :   if (NS_FAILED(rv)) {
      95           0 :     return rv;
      96             :   }
      97             : 
      98           0 :   if (XRE_IsParentProcess()) {
      99           0 :     RefPtr<FileBlockCache> self = this;
     100           0 :     rv = mThread->Dispatch(
     101           0 :       NS_NewRunnableFunction("FileBlockCache::Init",
     102           0 :                              [self] {
     103           0 :                                PRFileDesc* fd = nullptr;
     104           0 :                                nsresult rv = NS_OpenAnonymousTemporaryFile(&fd);
     105           0 :                                if (NS_SUCCEEDED(rv)) {
     106           0 :                                  self->SetCacheFile(fd);
     107             :                                } else {
     108           0 :                                  self->Close();
     109             :                                }
     110           0 :                              }),
     111           0 :       NS_DISPATCH_NORMAL);
     112             :   } else {
     113             :     // We must request a temporary file descriptor from the parent process.
     114           0 :     RefPtr<FileBlockCache> self = this;
     115           0 :     rv = dom::ContentChild::GetSingleton()->AsyncOpenAnonymousTemporaryFile(
     116           0 :       [self](PRFileDesc* aFD) { self->SetCacheFile(aFD); });
     117             :   }
     118             : 
     119           0 :   if (NS_FAILED(rv)) {
     120           0 :     Close();
     121             :   }
     122             : 
     123           0 :   return rv;
     124             : }
     125             : 
     126             : int32_t
     127           0 : FileBlockCache::GetMaxBlocks() const
     128             : {
     129             :   // We look up the cache size every time. This means dynamic changes
     130             :   // to the pref are applied.
     131             :   const uint32_t cacheSizeKb =
     132           0 :     std::min(MediaPrefs::MediaCacheSizeKb(), uint32_t(INT32_MAX) * 2);
     133             :   // Ensure we can divide BLOCK_SIZE by 1024.
     134             :   static_assert(MediaCacheStream::BLOCK_SIZE % 1024 == 0,
     135             :                 "BLOCK_SIZE should be a multiple of 1024");
     136             :   // Ensure BLOCK_SIZE/1024 is at least 2.
     137             :   static_assert(MediaCacheStream::BLOCK_SIZE / 1024 >= 2,
     138             :                 "BLOCK_SIZE / 1024 should be at least 2");
     139             :   // Ensure we can convert BLOCK_SIZE/1024 to a uint32_t without truncation.
     140             :   static_assert(MediaCacheStream::BLOCK_SIZE / 1024 <= int64_t(UINT32_MAX),
     141             :                 "BLOCK_SIZE / 1024 should be at most UINT32_MAX");
     142             :   // Since BLOCK_SIZE is a strict multiple of 1024,
     143             :   // cacheSizeKb * 1024 / BLOCK_SIZE == cacheSizeKb / (BLOCK_SIZE / 1024),
     144             :   // but the latter formula avoids a potential overflow from `* 1024`.
     145             :   // And because BLOCK_SIZE/1024 is at least 2, the maximum cache size
     146             :   // INT32_MAX*2 will give a maxBlocks that can fit in an int32_t.
     147             :   constexpr uint32_t blockSizeKb =
     148           0 :     uint32_t(MediaCacheStream::BLOCK_SIZE / 1024);
     149           0 :   const int32_t maxBlocks = int32_t(cacheSizeKb / blockSizeKb);
     150           0 :   return std::max(maxBlocks, int32_t(1));
     151             : }
     152             : 
     153           0 : FileBlockCache::FileBlockCache()
     154             :   : mFileMutex("MediaCache.Writer.IO.Mutex")
     155             :   , mFD(nullptr)
     156             :   , mFDCurrentPos(0)
     157             :   , mDataMutex("MediaCache.Writer.Data.Mutex")
     158             :   , mIsWriteScheduled(false)
     159           0 :   , mIsReading(false)
     160             : {
     161           0 : }
     162             : 
     163           0 : FileBlockCache::~FileBlockCache()
     164             : {
     165           0 :   Close();
     166           0 : }
     167             : 
     168             : void
     169           0 : FileBlockCache::Close()
     170             : {
     171           0 :   LOG("Close()");
     172             : 
     173           0 :   nsCOMPtr<nsIThread> thread;
     174             :   {
     175           0 :     MutexAutoLock mon(mDataMutex);
     176           0 :     if (!mThread) {
     177           0 :       return;
     178             :     }
     179           0 :     thread.swap(mThread);
     180             :   }
     181             : 
     182             :   PRFileDesc* fd;
     183             :   {
     184           0 :     MutexAutoLock lock(mFileMutex);
     185           0 :     fd = mFD;
     186           0 :     mFD = nullptr;
     187             :   }
     188             : 
     189             :   // Let the thread close the FD, and then trigger its own shutdown.
     190             :   // Note that mThread is now empty, so no other task will be posted there.
     191             :   // Also mThread and mFD are empty and therefore can be reused immediately.
     192           0 :   nsresult rv = thread->Dispatch(
     193           0 :     NS_NewRunnableFunction("FileBlockCache::Close",
     194           0 :                            [thread, fd] {
     195           0 :                              if (fd) {
     196           0 :                                CloseFD(fd);
     197             :                              }
     198             :                              // We must shut down the thread in another
     199             :                              // runnable. This is called
     200             :                              // while we're shutting down the media cache, and
     201             :                              // nsIThread::Shutdown()
     202             :                              // can cause events to run before it completes,
     203             :                              // which could end up
     204             :                              // opening more streams, while the media cache is
     205             :                              // shutting down and
     206             :                              // releasing memory etc!
     207             :                              nsCOMPtr<nsIRunnable> event =
     208           0 :                                new ShutdownThreadEvent(thread);
     209             :                              SystemGroup::Dispatch("ShutdownThreadEvent",
     210             :                                                    TaskCategory::Other,
     211           0 :                                                    event.forget());
     212           0 :                            }),
     213           0 :     NS_DISPATCH_NORMAL);
     214           0 :   NS_ENSURE_SUCCESS_VOID(rv);
     215             : }
     216             : 
     217             : template<typename Container, typename Value>
     218             : bool
     219           0 : ContainerContains(const Container& aContainer, const Value& value)
     220             : {
     221           0 :   return std::find(aContainer.begin(), aContainer.end(), value)
     222           0 :          != aContainer.end();
     223             : }
     224             : 
     225             : nsresult
     226           0 : FileBlockCache::WriteBlock(uint32_t aBlockIndex,
     227             :   Span<const uint8_t> aData1, Span<const uint8_t> aData2)
     228             : {
     229           0 :   MutexAutoLock mon(mDataMutex);
     230             : 
     231           0 :   if (!mThread) {
     232           0 :     return NS_ERROR_FAILURE;
     233             :   }
     234             : 
     235             :   // Check if we've already got a pending write scheduled for this block.
     236           0 :   mBlockChanges.EnsureLengthAtLeast(aBlockIndex + 1);
     237           0 :   bool blockAlreadyHadPendingChange = mBlockChanges[aBlockIndex] != nullptr;
     238           0 :   mBlockChanges[aBlockIndex] = new BlockChange(aData1, aData2);
     239             : 
     240           0 :   if (!blockAlreadyHadPendingChange || !ContainerContains(mChangeIndexList, aBlockIndex)) {
     241             :     // We either didn't already have a pending change for this block, or we
     242             :     // did but we didn't have an entry for it in mChangeIndexList (we're in the process
     243             :     // of writing it and have removed the block's index out of mChangeIndexList
     244             :     // in Run() but not finished writing the block to file yet). Add the blocks
     245             :     // index to the end of mChangeIndexList to ensure the block is written as
     246             :     // as soon as possible.
     247           0 :     mChangeIndexList.push_back(aBlockIndex);
     248             :   }
     249           0 :   NS_ASSERTION(ContainerContains(mChangeIndexList, aBlockIndex), "Must have entry for new block");
     250             : 
     251           0 :   EnsureWriteScheduled();
     252             : 
     253           0 :   return NS_OK;
     254             : }
     255             : 
     256           0 : void FileBlockCache::EnsureWriteScheduled()
     257             : {
     258           0 :   mDataMutex.AssertCurrentThreadOwns();
     259           0 :   MOZ_ASSERT(mThread);
     260             : 
     261           0 :   if (mIsWriteScheduled || mIsReading) {
     262           0 :     return;
     263             :   }
     264           0 :   mIsWriteScheduled = true;
     265           0 :   if (!mInitialized) {
     266             :     // We're still waiting on a file descriptor. When it arrives,
     267             :     // the write will be scheduled.
     268           0 :     return;
     269             :   }
     270           0 :   nsCOMPtr<nsIRunnable> event = mozilla::NewRunnableMethod(
     271             :     "FileBlockCache::EnsureWriteScheduled -> PerformBlockIOs",
     272             :     this,
     273           0 :     &FileBlockCache::PerformBlockIOs);
     274           0 :   mThread->Dispatch(event.forget(), NS_DISPATCH_NORMAL);
     275             : }
     276             : 
     277           0 : nsresult FileBlockCache::Seek(int64_t aOffset)
     278             : {
     279           0 :   mFileMutex.AssertCurrentThreadOwns();
     280             : 
     281           0 :   if (mFDCurrentPos != aOffset) {
     282           0 :     MOZ_ASSERT(mFD);
     283           0 :     int64_t result = PR_Seek64(mFD, aOffset, PR_SEEK_SET);
     284           0 :     if (result != aOffset) {
     285           0 :       NS_WARNING("Failed to seek media cache file");
     286           0 :       return NS_ERROR_FAILURE;
     287             :     }
     288           0 :     mFDCurrentPos = result;
     289             :   }
     290           0 :   return NS_OK;
     291             : }
     292             : 
     293           0 : nsresult FileBlockCache::ReadFromFile(int64_t aOffset,
     294             :                                       uint8_t* aDest,
     295             :                                       int32_t aBytesToRead,
     296             :                                       int32_t& aBytesRead)
     297             : {
     298           0 :   LOG("ReadFromFile(offset=%" PRIu64 ", len=%u)", aOffset, aBytesToRead);
     299           0 :   mFileMutex.AssertCurrentThreadOwns();
     300           0 :   MOZ_ASSERT(mFD);
     301             : 
     302           0 :   nsresult res = Seek(aOffset);
     303           0 :   if (NS_FAILED(res)) return res;
     304             : 
     305           0 :   aBytesRead = PR_Read(mFD, aDest, aBytesToRead);
     306           0 :   if (aBytesRead <= 0)
     307           0 :     return NS_ERROR_FAILURE;
     308           0 :   mFDCurrentPos += aBytesRead;
     309             : 
     310           0 :   return NS_OK;
     311             : }
     312             : 
     313           0 : nsresult FileBlockCache::WriteBlockToFile(int32_t aBlockIndex,
     314             :                                           const uint8_t* aBlockData)
     315             : {
     316           0 :   LOG("WriteBlockToFile(index=%u)", aBlockIndex);
     317             : 
     318           0 :   mFileMutex.AssertCurrentThreadOwns();
     319           0 :   MOZ_ASSERT(mFD);
     320             : 
     321           0 :   nsresult rv = Seek(BlockIndexToOffset(aBlockIndex));
     322           0 :   if (NS_FAILED(rv)) return rv;
     323             : 
     324           0 :   int32_t amount = PR_Write(mFD, aBlockData, BLOCK_SIZE);
     325           0 :   if (amount < BLOCK_SIZE) {
     326           0 :     NS_WARNING("Failed to write media cache block!");
     327           0 :     return NS_ERROR_FAILURE;
     328             :   }
     329           0 :   mFDCurrentPos += BLOCK_SIZE;
     330             : 
     331           0 :   return NS_OK;
     332             : }
     333             : 
     334           0 : nsresult FileBlockCache::MoveBlockInFile(int32_t aSourceBlockIndex,
     335             :                                          int32_t aDestBlockIndex)
     336             : {
     337           0 :   LOG("MoveBlockInFile(src=%u, dest=%u)", aSourceBlockIndex, aDestBlockIndex);
     338             : 
     339           0 :   mFileMutex.AssertCurrentThreadOwns();
     340             : 
     341             :   uint8_t buf[BLOCK_SIZE];
     342           0 :   int32_t bytesRead = 0;
     343           0 :   if (NS_FAILED(ReadFromFile(BlockIndexToOffset(aSourceBlockIndex),
     344             :                              buf,
     345             :                              BLOCK_SIZE,
     346             :                              bytesRead))) {
     347           0 :     return NS_ERROR_FAILURE;
     348             :   }
     349           0 :   return WriteBlockToFile(aDestBlockIndex, buf);
     350             : }
     351             : 
     352             : void
     353           0 : FileBlockCache::PerformBlockIOs()
     354             : {
     355           0 :   NS_ASSERTION(!NS_IsMainThread(), "Don't call on main thread");
     356           0 :   MutexAutoLock mon(mDataMutex);
     357           0 :   NS_ASSERTION(mIsWriteScheduled, "Should report write running or scheduled.");
     358             : 
     359           0 :   LOG("Run() mFD=%p mThread=%p", mFD, mThread.get());
     360             : 
     361           0 :   while (!mChangeIndexList.empty()) {
     362           0 :     if (!mThread) {
     363             :       // We've been closed, abort, discarding unwritten changes.
     364           0 :       mIsWriteScheduled = false;
     365           0 :       return;
     366             :     }
     367             : 
     368           0 :     if (mIsReading) {
     369             :       // We're trying to read; postpone all writes. (Reader will resume writes.)
     370           0 :       mIsWriteScheduled = false;
     371           0 :       return;
     372             :     }
     373             : 
     374             :     // Process each pending change. We pop the index out of the change
     375             :     // list, but leave the BlockChange in mBlockChanges until the change
     376             :     // is written to file. This is so that any read which happens while
     377             :     // we drop mDataMutex to write will refer to the data's source in
     378             :     // memory, rather than the not-yet up to date data written to file.
     379             :     // This also ensures we will insert a new index into mChangeIndexList
     380             :     // when this happens.
     381             : 
     382             :     // Hold a reference to the change, in case another change
     383             :     // overwrites the mBlockChanges entry for this block while we drop
     384             :     // mDataMutex to take mFileMutex.
     385           0 :     int32_t blockIndex = mChangeIndexList.front();
     386           0 :     RefPtr<BlockChange> change = mBlockChanges[blockIndex];
     387           0 :     MOZ_ASSERT(change,
     388             :                "Change index list should only contain entries for blocks "
     389             :                "with changes");
     390             :     {
     391           0 :       MutexAutoUnlock unlock(mDataMutex);
     392           0 :       MutexAutoLock lock(mFileMutex);
     393           0 :       if (!mFD) {
     394             :         // We may be here if mFD has been reset because we're closing, so we
     395             :         // don't care anymore about writes.
     396           0 :         return;
     397             :       }
     398           0 :       if (change->IsWrite()) {
     399           0 :         WriteBlockToFile(blockIndex, change->mData.get());
     400           0 :       } else if (change->IsMove()) {
     401           0 :         MoveBlockInFile(change->mSourceBlockIndex, blockIndex);
     402             :       }
     403             :     }
     404           0 :     mChangeIndexList.pop_front();
     405             :     // If a new change has not been made to the block while we dropped
     406             :     // mDataMutex, clear reference to the old change. Otherwise, the old
     407             :     // reference has been cleared already.
     408           0 :     if (mBlockChanges[blockIndex] == change) {
     409           0 :       mBlockChanges[blockIndex] = nullptr;
     410             :     }
     411             :   }
     412             : 
     413           0 :   mIsWriteScheduled = false;
     414             : }
     415             : 
     416           0 : nsresult FileBlockCache::Read(int64_t aOffset,
     417             :                               uint8_t* aData,
     418             :                               int32_t aLength,
     419             :                               int32_t* aBytes)
     420             : {
     421           0 :   MutexAutoLock mon(mDataMutex);
     422             : 
     423           0 :   if (!mThread || (aOffset / BLOCK_SIZE) > INT32_MAX) {
     424           0 :     return NS_ERROR_FAILURE;
     425             :   }
     426             : 
     427           0 :   mIsReading = true;
     428           0 :   auto exitRead = MakeScopeExit([&] {
     429           0 :     mIsReading = false;
     430           0 :     if (!mChangeIndexList.empty()) {
     431             :       // mReading has stopped or prevented pending writes, resume them.
     432           0 :       EnsureWriteScheduled();
     433             :     }
     434           0 :   });
     435             : 
     436           0 :   int32_t bytesToRead = aLength;
     437           0 :   int64_t offset = aOffset;
     438           0 :   uint8_t* dst = aData;
     439           0 :   while (bytesToRead > 0) {
     440           0 :     int32_t blockIndex = static_cast<int32_t>(offset / BLOCK_SIZE);
     441           0 :     int32_t start = offset % BLOCK_SIZE;
     442           0 :     int32_t amount = std::min(BLOCK_SIZE - start, bytesToRead);
     443             : 
     444             :     // If the block is not yet written to file, we can just read from
     445             :     // the memory buffer, otherwise we need to read from file.
     446           0 :     int32_t bytesRead = 0;
     447           0 :     RefPtr<BlockChange> change = mBlockChanges[blockIndex];
     448           0 :     if (change && change->IsWrite()) {
     449             :       // Block isn't yet written to file. Read from memory buffer.
     450           0 :       const uint8_t* blockData = change->mData.get();
     451           0 :       memcpy(dst, blockData + start, amount);
     452           0 :       bytesRead = amount;
     453             :     } else {
     454           0 :       if (change && change->IsMove()) {
     455             :         // The target block is the destination of a not-yet-completed move
     456             :         // action, so read from the move's source block from file. Note we
     457             :         // *don't* follow a chain of moves here, as a move's source index
     458             :         // is resolved when MoveBlock() is called, and the move's source's
     459             :         // block could be have itself been subject to a move (or write)
     460             :         // which happened *after* this move was recorded.
     461           0 :         blockIndex = mBlockChanges[blockIndex]->mSourceBlockIndex;
     462             :       }
     463             :       // Block has been written to file, either as the source block of a move,
     464             :       // or as a stable (all changes made) block. Read the data directly
     465             :       // from file.
     466             :       nsresult res;
     467             :       {
     468           0 :         MutexAutoUnlock unlock(mDataMutex);
     469           0 :         MutexAutoLock lock(mFileMutex);
     470           0 :         if (!mFD) {
     471             :           // Not initialized yet, or closed.
     472           0 :           return NS_ERROR_FAILURE;
     473             :         }
     474           0 :         res = ReadFromFile(BlockIndexToOffset(blockIndex) + start,
     475             :                            dst,
     476             :                            amount,
     477           0 :                            bytesRead);
     478             :       }
     479           0 :       NS_ENSURE_SUCCESS(res,res);
     480             :     }
     481           0 :     dst += bytesRead;
     482           0 :     offset += bytesRead;
     483           0 :     bytesToRead -= bytesRead;
     484             :   }
     485           0 :   *aBytes = aLength - bytesToRead;
     486           0 :   return NS_OK;
     487             : }
     488             : 
     489           0 : nsresult FileBlockCache::MoveBlock(int32_t aSourceBlockIndex, int32_t aDestBlockIndex)
     490             : {
     491           0 :   MutexAutoLock mon(mDataMutex);
     492             : 
     493           0 :   if (!mThread) {
     494           0 :     return NS_ERROR_FAILURE;
     495             :   }
     496             : 
     497           0 :   mBlockChanges.EnsureLengthAtLeast(std::max(aSourceBlockIndex, aDestBlockIndex) + 1);
     498             : 
     499             :   // The source block's contents may be the destination of another pending
     500             :   // move, which in turn can be the destination of another pending move,
     501             :   // etc. Resolve the final source block, so that if one of the blocks in
     502             :   // the chain of moves is overwritten, we don't lose the reference to the
     503             :   // contents of the destination block.
     504           0 :   int32_t sourceIndex = aSourceBlockIndex;
     505           0 :   BlockChange* sourceBlock = nullptr;
     506           0 :   while ((sourceBlock = mBlockChanges[sourceIndex]) &&
     507           0 :           sourceBlock->IsMove()) {
     508           0 :     sourceIndex = sourceBlock->mSourceBlockIndex;
     509             :   }
     510             : 
     511           0 :   if (mBlockChanges[aDestBlockIndex] == nullptr ||
     512           0 :       !ContainerContains(mChangeIndexList, aDestBlockIndex)) {
     513             :     // Only add another entry to the change index list if we don't already
     514             :     // have one for this block. We won't have an entry when either there's
     515             :     // no pending change for this block, or if there is a pending change for
     516             :     // this block and we're in the process of writing it (we've popped the
     517             :     // block's index out of mChangeIndexList in Run() but not finished writing
     518             :     // the block to file yet.
     519           0 :     mChangeIndexList.push_back(aDestBlockIndex);
     520             :   }
     521             : 
     522             :   // If the source block hasn't yet been written to file then the dest block
     523             :   // simply contains that same write. Resolve this as a write instead.
     524           0 :   if (sourceBlock && sourceBlock->IsWrite()) {
     525           0 :     mBlockChanges[aDestBlockIndex] = new BlockChange(sourceBlock->mData.get());
     526             :   } else {
     527           0 :     mBlockChanges[aDestBlockIndex] = new BlockChange(sourceIndex);
     528             :   }
     529             : 
     530           0 :   EnsureWriteScheduled();
     531             : 
     532           0 :   NS_ASSERTION(ContainerContains(mChangeIndexList, aDestBlockIndex),
     533             :     "Should have scheduled block for change");
     534             : 
     535           0 :   return NS_OK;
     536             : }
     537             : 
     538             : } // End namespace mozilla.
     539             : 
     540             : // avoid redefined macro in unified build
     541             : #undef LOG

Generated by: LCOV version 1.13