Bug 785909 - Don't init FileMediaResource::mSize on the main thread. r=roc
authorChris Pearce <cpearce@mozilla.com>
Wed, 29 Aug 2012 15:55:57 +1200
changeset 105770 e6517dae3b952faed11a8c5e4f7e9f23d98a0278
parent 105769 4a4696fcfd03c2a81c4f442fdab6a5ab9da37dc0
child 105771 747584155b62aaf0c2a9919104c730df29fe63d1
push id55
push usershu@rfrn.org
push dateThu, 30 Aug 2012 01:33:09 +0000
reviewersroc
bugs785909
milestone18.0a1
Bug 785909 - Don't init FileMediaResource::mSize on the main thread. r=roc
content/media/MediaResource.cpp
--- a/content/media/MediaResource.cpp
+++ b/content/media/MediaResource.cpp
@@ -913,17 +913,18 @@ ChannelMediaResource::PossiblyResume()
   }
 }
 
 class FileMediaResource : public MediaResource
 {
 public:
   FileMediaResource(nsMediaDecoder* aDecoder, nsIChannel* aChannel, nsIURI* aURI) :
     MediaResource(aDecoder, aChannel, aURI), mSize(-1),
-    mLock("FileMediaResource.mLock")
+    mLock("FileMediaResource.mLock"),
+    mSizeInitialized(false)
   {
   }
   ~FileMediaResource()
   {
   }
 
   // Main thread
   virtual nsresult Open(nsIStreamListener** aStreamListener);
@@ -950,17 +951,21 @@ public:
   virtual void    Pin() {}
   virtual void    Unpin() {}
   virtual double  GetDownloadRate(bool* aIsReliable)
   {
     // The data's all already here
     *aIsReliable = true;
     return 100*1024*1024; // arbitray, use 100MB/s
   }
-  virtual int64_t GetLength() { return mSize; }
+  virtual int64_t GetLength() {
+    MutexAutoLock lock(mLock);
+    EnsureLengthInitialized();
+    return mSize;
+  }
   virtual int64_t GetNextCachedData(int64_t aOffset)
   {
     return (aOffset < mSize) ? aOffset : -1;
   }
   virtual int64_t GetCachedDataEnd(int64_t aOffset) { return NS_MAX(aOffset, mSize); }
   virtual bool    IsDataCachedToEndOfResource(int64_t aOffset) { return true; }
   virtual bool    IsSuspendedByCache(MediaResource** aActiveResource)
   {
@@ -969,16 +974,19 @@ public:
     }
     return false;
   }
   virtual bool    IsSuspended() { return false; }
 
   nsresult GetCachedRanges(nsTArray<MediaByteRange>& aRanges);
 
 private:
+  // Ensures mSize is initialized, if it can be.
+  void EnsureLengthInitialized();
+
   // The file size, or -1 if not known. Immutable after Open().
   int64_t mSize;
 
   // This lock handles synchronisation between calls to Close() and
   // the Read, Seek, etc calls. Close must not be called while a
   // Read or Seek is in progress since it resets various internal
   // values to null.
   // This lock protects mSeekable and mInput.
@@ -986,16 +994,21 @@ private:
 
   // Seekable stream interface to file. This can be used from any
   // thread.
   nsCOMPtr<nsISeekableStream> mSeekable;
 
   // Input stream for the media data. This can be used from any
   // thread.
   nsCOMPtr<nsIInputStream>  mInput;
+
+  // Whether we've attempted to initialize mSize. Note that mSize can be -1
+  // when mSizeInitialized is true if we tried and failed to get the size
+  // of the file.
+  bool mSizeInitialized;
 };
 
 class LoadedEvent : public nsRunnable
 {
 public:
   LoadedEvent(nsMediaDecoder* aDecoder) :
     mDecoder(aDecoder)
   {
@@ -1010,18 +1023,37 @@ public:
     mDecoder->NotifyDownloadEnded(NS_OK);
     return NS_OK;
   }
 
 private:
   nsRefPtr<nsMediaDecoder> mDecoder;
 };
 
+void FileMediaResource::EnsureLengthInitialized()
+{
+  mLock.AssertCurrentThreadOwns();
+  if (mSizeInitialized) {
+    return;
+  }
+  mSizeInitialized = true;
+  // Get the file size and inform the decoder.
+  uint64_t size;
+  nsresult res = mInput->Available(&size);
+  if (NS_SUCCEEDED(res) && size <= PR_INT64_MAX) {
+    mSize = (int64_t)size;
+    nsCOMPtr<nsIRunnable> event = new LoadedEvent(mDecoder);
+    NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
+  }
+}
+
 nsresult FileMediaResource::GetCachedRanges(nsTArray<MediaByteRange>& aRanges)
 {
+  MutexAutoLock lock(mLock);
+  EnsureLengthInitialized();
   if (mSize == -1) {
     return NS_ERROR_FAILURE;
   }
   aRanges.AppendElement(MediaByteRange(0, mSize));
   return NS_OK;
 }
 
 nsresult FileMediaResource::Open(nsIStreamListener** aStreamListener)
@@ -1066,26 +1098,16 @@ nsresult FileMediaResource::Open(nsIStre
   if (!mSeekable) {
     // XXX The file may just be a .url or similar
     // shortcut that points to a Web site. We need to fix this by
     // doing an async open and waiting until we locate the real resource,
     // then using that (if it's still a file!).
     return NS_ERROR_FAILURE;
   }
 
-  // Get the file size and inform the decoder.
-  uint64_t size;
-  rv = mInput->Available(&size);
-  NS_ENSURE_SUCCESS(rv, rv);
-  NS_ENSURE_TRUE(size <= PR_INT64_MAX, NS_ERROR_FILE_TOO_BIG);
- 
-  mSize = (int64_t)size;
-
-  nsCOMPtr<nsIRunnable> event = new LoadedEvent(mDecoder);
-  NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
   return NS_OK;
 }
 
 nsresult FileMediaResource::Close()
 {
   NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
 
   MutexAutoLock lock(mLock);
@@ -1135,16 +1157,17 @@ MediaResource* FileMediaResource::CloneD
     return nullptr;
 
   return new FileMediaResource(aDecoder, channel, mURI);
 }
 
 nsresult FileMediaResource::ReadFromCache(char* aBuffer, int64_t aOffset, uint32_t aCount)
 {
   MutexAutoLock lock(mLock);
+  EnsureLengthInitialized();
   if (!mInput || !mSeekable)
     return NS_ERROR_FAILURE;
   int64_t offset = 0;
   nsresult res = mSeekable->Tell(&offset);
   NS_ENSURE_SUCCESS(res,res);
   res = mSeekable->Seek(nsISeekableStream::NS_SEEK_SET, aOffset);
   NS_ENSURE_SUCCESS(res,res);
   uint32_t bytesRead = 0;
@@ -1164,38 +1187,41 @@ nsresult FileMediaResource::ReadFromCach
 
   // Else we succeed if the reset-seek succeeds.
   return seekres;
 }
 
 nsresult FileMediaResource::Read(char* aBuffer, uint32_t aCount, uint32_t* aBytes)
 {
   MutexAutoLock lock(mLock);
+  EnsureLengthInitialized();
   if (!mInput)
     return NS_ERROR_FAILURE;
   return mInput->Read(aBuffer, aCount, aBytes);
 }
 
 nsresult FileMediaResource::Seek(int32_t aWhence, int64_t aOffset)
 {
   NS_ASSERTION(!NS_IsMainThread(), "Don't call on main thread");
 
   MutexAutoLock lock(mLock);
   if (!mSeekable)
     return NS_ERROR_FAILURE;
+  EnsureLengthInitialized();
   return mSeekable->Seek(aWhence, aOffset);
 }
 
 int64_t FileMediaResource::Tell()
 {
   NS_ASSERTION(!NS_IsMainThread(), "Don't call on main thread");
 
   MutexAutoLock lock(mLock);
   if (!mSeekable)
     return 0;
+  EnsureLengthInitialized();
 
   int64_t offset = 0;
   mSeekable->Tell(&offset);
   return offset;
 }
 
 MediaResource*
 MediaResource::Create(nsMediaDecoder* aDecoder, nsIChannel* aChannel)