Bug 1415090. P4 - don't modify mResourceID off the main thread. draft
authorJW Wang <jwwang@mozilla.com>
Wed, 15 Nov 2017 16:45:57 +0800
changeset 699435 68ed9786c9e50e59c426691830f8a97b46bbb001
parent 699434 502f829114d1cece67896e6c6bcbb26884bceb54
child 699436 070dc6418d76c427a08761c6a1a8c304fe22c761
push id89568
push userjwwang@mozilla.com
push dateFri, 17 Nov 2017 06:22:23 +0000
bugs1415090
milestone59.0a1
Bug 1415090. P4 - don't modify mResourceID off the main thread. There are some works to do when we allow a stream whose download ends abnormally to continue sharing the resource: 1. Abort Read() when download error happens. We might still have a chance to get all the data successfully. However, it doesn't really matter since the stream data is incomplete and we will encounter decode errors sooner or later. 2. Update() needs to check mChannelEnded since an ended stream will not download data needed by other streams. MozReview-Commit-ID: LGCecQ5rpzq
dom/media/MediaCache.cpp
--- a/dom/media/MediaCache.cpp
+++ b/dom/media/MediaCache.cpp
@@ -1442,17 +1442,17 @@ MediaCache::Update()
         enableReading = predictedNewDataUse < latestNextUse;
       }
     }
 
     if (enableReading) {
       for (uint32_t j = 0; j < i; ++j) {
         MediaCacheStream* other = mStreams[j];
         if (other->mResourceID == stream->mResourceID && !other->mClosed &&
-            !other->mClientSuspended &&
+            !other->mClientSuspended && !other->mChannelEnded &&
             OffsetToBlockIndexUnchecked(other->mSeekTarget != -1
                                           ? other->mSeekTarget
                                           : other->mChannelOffset) ==
               OffsetToBlockIndexUnchecked(desiredOffset)) {
           // This block is already going to be read by the other stream.
           // So don't try to read it from this stream as well.
           enableReading = false;
           LOG("Stream %p waiting on same block (%" PRId32 ") from stream %p",
@@ -2155,35 +2155,39 @@ MediaCacheStream::NotifyDataEndedInterna
     mClient->CacheClientSeek(mChannelOffset, false);
     return;
     // Note CacheClientSeek() will call Seek() asynchronously which might fail
     // and close the stream. This is OK for it is not an error we can recover
     // from and we have a consistent behavior with that where CacheClientSeek()
     // is called from MediaCache::Update().
   }
 
-  if (NS_FAILED(aStatus)) {
-    // Disconnect from other streams sharing our resource, since they
-    // should continue trying to load. Our load might have been deliberately
-    // canceled and that shouldn't affect other streams.
-    mResourceID = mMediaCache->AllocateResourceID();
-  }
-
   // It is prudent to update channel/cache status before calling
   // CacheClientNotifyDataEnded() which will read |mChannelEnded|.
-  FlushPartialBlockInternal(true, mon);
   mChannelEnded = true;
   mMediaCache->QueueUpdate();
 
+  if (NS_FAILED(aStatus)) {
+    // Notify the client about this network error.
+    mDidNotifyDataEnded = true;
+    mNotifyDataEndedStatus = aStatus;
+    mClient->CacheClientNotifyDataEnded(aStatus);
+    // Wake up the readers so they can fail gracefully.
+    mon.NotifyAll();
+    return;
+  }
+
+  // Note we don't flush the partial block when download ends abnormally for
+  // the padding zeros will give wrong data to other streams.
+  FlushPartialBlockInternal(true, mon);
+
   MediaCache::ResourceStreamIterator iter(mMediaCache, mResourceID);
   while (MediaCacheStream* stream = iter.Next()) {
-    if (NS_SUCCEEDED(aStatus)) {
-      // We read the whole stream, so remember the true length
-      stream->mStreamLength = mChannelOffset;
-    }
+    // We read the whole stream, so remember the true length
+    stream->mStreamLength = mChannelOffset;
     if (!stream->mDidNotifyDataEnded) {
       stream->mDidNotifyDataEnded = true;
       stream->mNotifyDataEndedStatus = aStatus;
       stream->mClient->CacheClientNotifyDataEnded(aStatus);
     }
   }
 }
 
@@ -2577,16 +2581,21 @@ MediaCacheStream::Read(char* aBuffer, ui
   auto buffer = MakeSpan<char>(aBuffer, aCount);
 
   // Read one block (or part of a block) at a time
   while (!buffer.IsEmpty()) {
     if (mClosed) {
       return NS_ERROR_ABORT;
     }
 
+    if (mDidNotifyDataEnded && NS_FAILED(mNotifyDataEndedStatus)) {
+      // Abort reading since download ends abnormally.
+      return NS_ERROR_FAILURE;
+    }
+
     if (!IsOffsetAllowed(streamOffset)) {
       LOGE("Stream %p invalid offset=%" PRId64, this, streamOffset);
       return NS_ERROR_ILLEGAL_VALUE;
     }
 
     if (mStreamLength >= 0 && streamOffset >= mStreamLength) {
       // Don't try to read beyond the end of the stream
       break;