From a033c4c5b8b1b2f4416199ff3ce7a17f1ed07bad Mon Sep 17 00:00:00 2001 From: Josh Aas Date: Mon, 19 Nov 2012 18:02:21 -0500 Subject: [PATCH] Back out fix for bug 405407 due to regressions. --- netwerk/cache/nsCacheEntryDescriptor.h | 1 - netwerk/cache/nsDiskCacheStreams.cpp | 473 ++++++++++++++----- netwerk/cache/nsDiskCacheStreams.h | 21 +- toolkit/components/telemetry/Histograms.json | 12 + 4 files changed, 379 insertions(+), 128 deletions(-) diff --git a/netwerk/cache/nsCacheEntryDescriptor.h b/netwerk/cache/nsCacheEntryDescriptor.h index 016215bac415..0f301566f1f7 100644 --- a/netwerk/cache/nsCacheEntryDescriptor.h +++ b/netwerk/cache/nsCacheEntryDescriptor.h @@ -175,7 +175,6 @@ class nsCacheEntryDescriptor : { nsCacheServiceAutoLock lock(LOCK_TELEM(NSOUTPUTSTREAMWRAPPER_CLOSE)); mDescriptor->mOutput = nullptr; - mOutput = nullptr; } NS_RELEASE(mDescriptor); } diff --git a/netwerk/cache/nsDiskCacheStreams.cpp b/netwerk/cache/nsDiskCacheStreams.cpp index 8016ff676ea7..7c16c0097f3c 100644 --- a/netwerk/cache/nsDiskCacheStreams.cpp +++ b/netwerk/cache/nsDiskCacheStreams.cpp @@ -15,16 +15,14 @@ #include "mozilla/Telemetry.h" #include "mozilla/TimeStamp.h" + + // Assumptions: // - cache descriptors live for life of streams // - streams will only be used by FileTransport, // they will not be directly accessible to clients // - overlapped I/O is NOT supported -// we pick 16k as the max buffer size because that is the threshold above which -// we are unable to store the data in the cache block files -// see nsDiskCacheMap.[cpp,h] -#define kMaxBufferSize (16 * 1024) /****************************************************************************** * nsDiskCacheInputStream @@ -180,53 +178,113 @@ nsDiskCacheInputStream::IsNonBlocking(bool * nonBlocking) return NS_OK; } + /****************************************************************************** - * nsDiskCacheStreamIO + * nsDiskCacheOutputStream *****************************************************************************/ -NS_IMPL_THREADSAFE_ISUPPORTS2(nsDiskCacheStreamIO, nsIOutputStream, nsIDiskCacheStreamInternal) +class nsDiskCacheOutputStream : public nsIOutputStream + , public nsIDiskCacheStreamInternal +{ +public: + nsDiskCacheOutputStream( nsDiskCacheStreamIO * parent); + virtual ~nsDiskCacheOutputStream(); -nsDiskCacheStreamIO::nsDiskCacheStreamIO(nsDiskCacheBinding * binding) - : mBinding(binding) - , mInStreamCount(0) - , mFD(nullptr) - , mStreamPos(0) - , mStreamEnd(0) - , mBufPos(0) - , mBufEnd(0) - , mBufSize(0) - , mBufDirty(false) - , mOutputStreamIsOpen(false) - , mBuffer(nullptr) + NS_DECL_ISUPPORTS + NS_DECL_NSIOUTPUTSTREAM + NS_DECL_NSIDISKCACHESTREAMINTERNAL + + void ReleaseStreamIO() { NS_IF_RELEASE(mStreamIO); } + +private: + nsDiskCacheStreamIO * mStreamIO; // backpointer to parent + bool mClosed; +}; + + +NS_IMPL_THREADSAFE_ISUPPORTS2(nsDiskCacheOutputStream, + nsIOutputStream, + nsIDiskCacheStreamInternal) + +nsDiskCacheOutputStream::nsDiskCacheOutputStream( nsDiskCacheStreamIO * parent) + : mStreamIO(parent) + , mClosed(false) { - mDevice = (nsDiskCacheDevice *)mBinding->mCacheEntry->CacheDevice(); + NS_ADDREF(mStreamIO); +} - // acquire "death grip" on cache service - nsCacheService *service = nsCacheService::GlobalInstance(); - NS_ADDREF(service); + +nsDiskCacheOutputStream::~nsDiskCacheOutputStream() +{ + Close(); + ReleaseStreamIO(); } -nsDiskCacheStreamIO::~nsDiskCacheStreamIO() +NS_IMETHODIMP +nsDiskCacheOutputStream::Close() { - if (mOutputStreamIsOpen) { - nsCacheService::AssertOwnsLock(); - CloseInternal(); + nsresult rv = NS_OK; + mozilla::TimeStamp start = mozilla::TimeStamp::Now(); + + if (!mClosed) { + mClosed = true; + // tell parent streamIO we are closing + rv = mStreamIO->CloseOutputStream(this); } - NS_ASSERTION(!mOutputStreamIsOpen, "output stream still open"); - NS_ASSERTION(mInStreamCount == 0, "input stream still open"); - NS_ASSERTION(!mFD, "file descriptor not closed"); + mozilla::Telemetry::ID id; + if (NS_IsMainThread()) + id = mozilla::Telemetry::NETWORK_DISK_CACHE_OUTPUT_STREAM_CLOSE_MAIN_THREAD; + else + id = mozilla::Telemetry::NETWORK_DISK_CACHE_OUTPUT_STREAM_CLOSE; - DeleteBuffer(); + mozilla::Telemetry::AccumulateTimeDelta(id, start); - // release "death grip" on cache service - nsCacheService *service = nsCacheService::GlobalInstance(); - NS_RELEASE(service); + return rv; } +NS_IMETHODIMP +nsDiskCacheOutputStream::CloseInternal() +{ + nsresult rv = NS_OK; + mozilla::TimeStamp start = mozilla::TimeStamp::Now(); + + if (!mClosed) { + mClosed = true; + // tell parent streamIO we are closing + rv = mStreamIO->CloseOutputStreamInternal(this); + } + + mozilla::Telemetry::ID id; + if (NS_IsMainThread()) + id = mozilla::Telemetry::NETWORK_DISK_CACHE_OUTPUT_STREAM_CLOSE_INTERNAL_MAIN_THREAD; + else + id = mozilla::Telemetry::NETWORK_DISK_CACHE_OUTPUT_STREAM_CLOSE_INTERNAL; + + mozilla::Telemetry::AccumulateTimeDelta(id, start); + + return rv; +} NS_IMETHODIMP -nsDiskCacheStreamIO::WriteFrom(nsIInputStream *inStream, uint32_t count, uint32_t *bytesWritten) +nsDiskCacheOutputStream::Flush() +{ + if (mClosed) return NS_BASE_STREAM_CLOSED; + // yeah, yeah, well get to it...eventually... + return NS_OK; +} + + +NS_IMETHODIMP +nsDiskCacheOutputStream::Write(const char *buf, uint32_t count, uint32_t *bytesWritten) +{ + if (mClosed) return NS_BASE_STREAM_CLOSED; + return mStreamIO->Write(buf, count, bytesWritten); +} + + +NS_IMETHODIMP +nsDiskCacheOutputStream::WriteFrom(nsIInputStream *inStream, uint32_t count, uint32_t *bytesWritten) { NS_NOTREACHED("WriteFrom"); return NS_ERROR_NOT_IMPLEMENTED; @@ -234,10 +292,10 @@ nsDiskCacheStreamIO::WriteFrom(nsIInputStream *inStream, uint32_t count, uint32_ NS_IMETHODIMP -nsDiskCacheStreamIO::WriteSegments(nsReadSegmentFun reader, - void * closure, - uint32_t count, - uint32_t * bytesWritten) +nsDiskCacheOutputStream::WriteSegments( nsReadSegmentFun reader, + void * closure, + uint32_t count, + uint32_t * bytesWritten) { NS_NOTREACHED("WriteSegments"); return NS_ERROR_NOT_IMPLEMENTED; @@ -245,18 +303,67 @@ nsDiskCacheStreamIO::WriteSegments(nsReadSegmentFun reader, NS_IMETHODIMP -nsDiskCacheStreamIO::IsNonBlocking(bool * nonBlocking) +nsDiskCacheOutputStream::IsNonBlocking(bool * nonBlocking) { - *nonBlocking = false; - return NS_OK; + *nonBlocking = false; + return NS_OK; } -NS_IMETHODIMP + +/****************************************************************************** + * nsDiskCacheStreamIO + *****************************************************************************/ +NS_IMPL_THREADSAFE_ISUPPORTS0(nsDiskCacheStreamIO) + +// we pick 16k as the max buffer size because that is the threshold above which +// we are unable to store the data in the cache block files +// see nsDiskCacheMap.[cpp,h] +#define kMaxBufferSize (16 * 1024) + +nsDiskCacheStreamIO::nsDiskCacheStreamIO(nsDiskCacheBinding * binding) + : mBinding(binding) + , mOutStream(nullptr) + , mInStreamCount(0) + , mFD(nullptr) + , mStreamPos(0) + , mStreamEnd(0) + , mBufPos(0) + , mBufEnd(0) + , mBufSize(0) + , mBufDirty(false) + , mBuffer(nullptr) +{ + mDevice = (nsDiskCacheDevice *)mBinding->mCacheEntry->CacheDevice(); + + // acquire "death grip" on cache service + nsCacheService *service = nsCacheService::GlobalInstance(); + NS_ADDREF(service); +} + + +nsDiskCacheStreamIO::~nsDiskCacheStreamIO() +{ + Close(); + + // release "death grip" on cache service + nsCacheService *service = nsCacheService::GlobalInstance(); + NS_RELEASE(service); +} + + +void nsDiskCacheStreamIO::Close() { - nsCacheServiceAutoLock lock(LOCK_TELEM(NSDISKCACHESTREAMIO_CLOSEOUTPUTSTREAM)); - return CloseInternal(); + // this should only be called from our destructor + // no one is interested in us anymore, so we don't need to grab any locks + + // assert streams closed + NS_ASSERTION(!mOutStream, "output stream still open"); + NS_ASSERTION(mInStreamCount == 0, "input stream still open"); + NS_ASSERTION(!mFD, "file descriptor not closed"); + + DeleteBuffer(); } @@ -271,8 +378,8 @@ nsDiskCacheStreamIO::GetInputStream(uint32_t offset, nsIInputStream ** inputStre if (!mBinding) return NS_ERROR_NOT_AVAILABLE; - if (mOutputStreamIsOpen) { - NS_WARNING("already have the output stream open"); + if (mOutStream) { + NS_WARNING("already have an output stream open"); return NS_ERROR_NOT_AVAILABLE; } @@ -316,10 +423,10 @@ nsDiskCacheStreamIO::GetOutputStream(uint32_t offset, nsIOutputStream ** outputS *outputStream = nullptr; if (!mBinding) return NS_ERROR_NOT_AVAILABLE; - - NS_ASSERTION(!mOutputStreamIsOpen, "already have the output stream open"); + + NS_ASSERTION(!mOutStream, "already have an output stream open"); NS_ASSERTION(mInStreamCount == 0, "we already have input streams open"); - if (mOutputStreamIsOpen || mInStreamCount) return NS_ERROR_NOT_AVAILABLE; + if (mOutStream || mInStreamCount) return NS_ERROR_NOT_AVAILABLE; // mBuffer lazily allocated, but might exist if a previous stream already // created one. @@ -327,97 +434,69 @@ nsDiskCacheStreamIO::GetOutputStream(uint32_t offset, nsIOutputStream ** outputS mStreamPos = 0; mStreamEnd = mBinding->mCacheEntry->DataSize(); - if (offset > mStreamEnd) { - NS_WARNING("seek offset out of range"); - return NS_ERROR_INVALID_ARG; - } - nsresult rv; - // Seek and truncate at the desired offset - if (mBinding->mRecord.DataLocationInitialized() && - (mBinding->mRecord.DataFile() == 0)) { - // File storage, seek in file - rv = OpenCacheFile(PR_WRONLY | PR_CREATE_FILE, &mFD); - NS_ENSURE_SUCCESS(rv, rv); - if (offset) { - int32_t newPos = PR_Seek(mFD, offset, PR_SEEK_SET); - if (newPos == -1) { - return NS_ErrorAccordingToNSPR(); - } - } - - // Truncate at start position (offset) - rv = nsDiskCache::Truncate(mFD, offset); - NS_ENSURE_SUCCESS(rv, rv); - - mStreamPos = mStreamEnd = offset; - UpdateFileSize(); - } else if (offset) { - // else, read and seek in mBuffer - rv = ReadCacheBlocks(); - NS_ENSURE_SUCCESS(rv, rv); - - // Start writing at the provided offset - mBufEnd = mBufPos = offset; - mStreamPos = mStreamEnd = offset; + if (offset) { + rv = Seek(PR_SEEK_SET, offset); + if (NS_FAILED(rv)) return rv; } + rv = SetEOF(); + if (NS_FAILED(rv)) return rv; - mOutputStreamIsOpen = true; - // return myself as the output stream - NS_ADDREF(*outputStream = this); + // create a new output stream + mOutStream = new nsDiskCacheOutputStream(this); + if (!mOutStream) return NS_ERROR_OUT_OF_MEMORY; + + NS_ADDREF(*outputStream = mOutStream); return NS_OK; } - nsresult nsDiskCacheStreamIO::ClearBinding() { nsresult rv = NS_OK; - if (mBinding && mOutputStreamIsOpen) + if (mBinding && mOutStream) rv = Flush(); mBinding = nullptr; return rv; } +nsresult +nsDiskCacheStreamIO::CloseOutputStream(nsDiskCacheOutputStream * outputStream) +{ + nsCacheServiceAutoLock lock(LOCK_TELEM(NSDISKCACHESTREAMIO_CLOSEOUTPUTSTREAM)); // grab service lock + return CloseOutputStreamInternal(outputStream); +} -NS_IMETHODIMP -nsDiskCacheStreamIO::CloseInternal() +nsresult +nsDiskCacheStreamIO::CloseOutputStreamInternal( + nsDiskCacheOutputStream * outputStream) { - mozilla::TimeStamp start = mozilla::TimeStamp::Now(); + nsresult rv; - if (mOutputStreamIsOpen) { - if (!mBinding) { // if we're severed, just clear member variables - NS_ASSERTION(!mBufDirty, "oops"); - } else { - nsresult rv = Flush(); - NS_ENSURE_SUCCESS(rv, rv); - } - mOutputStreamIsOpen = PR_FALSE; + if (outputStream != mOutStream) { + NS_WARNING("mismatched output streams"); + return NS_ERROR_UNEXPECTED; } - - // Make sure to always close the FileDescriptor - if (mFD) { - (void) PR_Close(mFD); - mFD = nullptr; + + // output stream is closing + if (!mBinding) { // if we're severed, just clear member variables + NS_ASSERTION(!mBufDirty, "oops"); + mOutStream = nullptr; + outputStream->ReleaseStreamIO(); + return NS_ERROR_NOT_AVAILABLE; } - mozilla::Telemetry::ID id; - if (NS_IsMainThread()) - id = mozilla::Telemetry::NETWORK_DISK_CACHE_OUTPUT_STREAM_CLOSE_INTERNAL_MAIN_THREAD; - else - id = mozilla::Telemetry::NETWORK_DISK_CACHE_OUTPUT_STREAM_CLOSE_INTERNAL; - - mozilla::Telemetry::AccumulateTimeDelta(id, start); + rv = Flush(); + if (NS_FAILED(rv)) + NS_WARNING("Flush() failed"); - return NS_OK; + mOutStream = nullptr; + return rv; } - -NS_IMETHODIMP +nsresult nsDiskCacheStreamIO::Flush() { - if (!mOutputStreamIsOpen) return NS_BASE_STREAM_CLOSED; - NS_ASSERTION(mBinding, "oops"); CACHE_LOG_DEBUG(("CACHE: Flush [%x doomed=%u]\n", @@ -514,15 +593,12 @@ nsDiskCacheStreamIO::Flush() // never have both output and input streams open // OnDataSizeChanged() will have already been called to update entry->DataSize() -NS_IMETHODIMP +nsresult nsDiskCacheStreamIO::Write( const char * buffer, uint32_t count, uint32_t * bytesWritten) { - if (!mOutputStreamIsOpen) { - return NS_BASE_STREAM_CLOSED; - } - + nsresult rv = NS_OK; nsCacheServiceAutoLock lock(LOCK_TELEM(NSDISKCACHESTREAMIO_WRITE)); // grab service lock if (!mBinding) return NS_ERROR_NOT_AVAILABLE; @@ -589,7 +665,7 @@ nsDiskCacheStreamIO::Write( const char * buffer, } } - return NS_OK; + return rv; } @@ -698,7 +774,7 @@ nsDiskCacheStreamIO::FlushBufferToFile() record->SetDataFileGeneration(mBinding->mGeneration); // allocate file - rv = OpenCacheFile(PR_WRONLY | PR_CREATE_FILE, &mFD); + rv = OpenCacheFile(PR_RDWR | PR_CREATE_FILE, &mFD); if (NS_FAILED(rv)) return rv; int64_t dataSize = mBinding->mCacheEntry->PredictedDataSize(); @@ -746,3 +822,158 @@ nsDiskCacheStreamIO::SizeOfIncludingThis(nsMallocSizeOfFun aMallocSizeOf) return usage; } + +// NOTE: called with service lock held +nsresult +nsDiskCacheStreamIO::Seek(int32_t whence, int32_t offset) +{ + int32_t newPos; + if (!mBinding) return NS_ERROR_NOT_AVAILABLE; + + if (uint32_t(offset) > mStreamEnd) return NS_ERROR_FAILURE; + + if (mBinding->mRecord.DataLocationInitialized()) { + if (mBinding->mRecord.DataFile() == 0) { + if (!mFD) { + // we need an mFD, we better open it now + nsresult rv = OpenCacheFile(PR_RDWR | PR_CREATE_FILE, &mFD); + if (NS_FAILED(rv)) return rv; + } + } + } + + if (mFD) { + // do we have data in the buffer that needs to be flushed? + if (mBufDirty) { + // XXX optimization: are we just moving within the current buffer? + nsresult rv = FlushBufferToFile(); + if (NS_FAILED(rv)) return rv; + } + + newPos = PR_Seek(mFD, offset, (PRSeekWhence)whence); + if (newPos == -1) + return NS_ErrorAccordingToNSPR(); + + mStreamPos = (uint32_t) newPos; + mBufPos = 0; + mBufEnd = 0; + return NS_OK; + } + + // else, seek in mBuffer + + switch(whence) { + case PR_SEEK_SET: + newPos = offset; + break; + + case PR_SEEK_CUR: // relative from current posistion + newPos = offset + (uint32_t)mStreamPos; + break; + + case PR_SEEK_END: // relative from end + newPos = offset + (uint32_t)mBufEnd; + break; + + default: + return NS_ERROR_INVALID_ARG; + } + + // read data into mBuffer if not read yet. + if (mStreamEnd && !mBufEnd) { + if (newPos > 0) { + nsresult rv = ReadCacheBlocks(); + if (NS_FAILED(rv)) return rv; + } + } + + // stream buffer sanity checks + NS_ASSERTION(mBufEnd <= kMaxBufferSize, "bad stream"); + NS_ASSERTION(mBufPos <= mBufEnd, "bad stream"); + NS_ASSERTION(mStreamPos == mBufPos, "bad stream"); + NS_ASSERTION(mStreamEnd == mBufEnd, "bad stream"); + + if ((newPos < 0) || (uint32_t(newPos) > mBufEnd)) { + NS_WARNING("seek offset out of range"); + return NS_ERROR_INVALID_ARG; + } + + mStreamPos = newPos; + mBufPos = newPos; + return NS_OK; +} + + +// called only from nsDiskCacheOutputStream::Tell +nsresult +nsDiskCacheStreamIO::Tell(uint32_t * result) +{ + NS_ENSURE_ARG_POINTER(result); + *result = mStreamPos; + return NS_OK; +} + + +// NOTE: called with service lock held +nsresult +nsDiskCacheStreamIO::SetEOF() +{ + nsresult rv; + bool needToCloseFD = false; + + NS_ASSERTION(mStreamPos <= mStreamEnd, "bad stream"); + if (!mBinding) return NS_ERROR_NOT_AVAILABLE; + + if (mBinding->mRecord.DataLocationInitialized()) { + if (mBinding->mRecord.DataFile() == 0) { + if (!mFD) { + // we need an mFD, we better open it now + rv = OpenCacheFile(PR_RDWR | PR_CREATE_FILE, &mFD); + if (NS_FAILED(rv)) return rv; + needToCloseFD = true; + } + } else { + // data in cache block files + if ((mStreamPos != 0) && (mStreamPos != mBufPos)) { + // only read data if there will be some left after truncation + rv = ReadCacheBlocks(); + if (NS_FAILED(rv)) return rv; + } + + // We need to make sure we reflect this change in Flush(). + // In particular, if mStreamPos is 0 and we never write to + // the buffer, we want the storage to be deleted. + mBufDirty = true; + } + } + + if (mFD) { + rv = nsDiskCache::Truncate(mFD, mStreamPos); +#ifdef DEBUG + uint32_t oldSizeK = (mStreamEnd + 0x03FF) >> 10; + NS_ASSERTION(mBinding->mRecord.DataFileSize() == oldSizeK, "bad disk cache entry size"); + } else { + // data stored in buffer. + NS_ASSERTION(mStreamEnd <= kMaxBufferSize, "buffer truncation inadequate"); + NS_ASSERTION(mBufPos == mStreamPos, "bad stream"); + NS_ASSERTION(mBuffer ? mBufEnd == mStreamEnd : true, "bad stream"); +#endif + } + + NS_ASSERTION(mStreamEnd == mBinding->mCacheEntry->DataSize(), "cache entry not updated"); + // we expect nsCacheEntryDescriptor::TransportWrapper::OpenOutputStream() + // to eventually update the cache entry + + mStreamEnd = mStreamPos; + mBufEnd = mBufPos; + + if (mFD) { + UpdateFileSize(); + if (needToCloseFD) { + (void) PR_Close(mFD); + mFD = nullptr; + } + } + + return NS_OK; +} diff --git a/netwerk/cache/nsDiskCacheStreams.h b/netwerk/cache/nsDiskCacheStreams.h index 995aa963dec9..821134f52566 100644 --- a/netwerk/cache/nsDiskCacheStreams.h +++ b/netwerk/cache/nsDiskCacheStreams.h @@ -14,25 +14,30 @@ #include "nsIInputStream.h" #include "nsIOutputStream.h" -#include "nsIDiskCacheStreamInternal.h" #include "pratom.h" class nsDiskCacheInputStream; +class nsDiskCacheOutputStream; class nsDiskCacheDevice; -class nsDiskCacheStreamIO : public nsIOutputStream, nsIDiskCacheStreamInternal { +class nsDiskCacheStreamIO : public nsISupports { public: nsDiskCacheStreamIO(nsDiskCacheBinding * binding); virtual ~nsDiskCacheStreamIO(); NS_DECL_ISUPPORTS - NS_DECL_NSIOUTPUTSTREAM - NS_DECL_NSIDISKCACHESTREAMINTERNAL nsresult GetInputStream(uint32_t offset, nsIInputStream ** inputStream); nsresult GetOutputStream(uint32_t offset, nsIOutputStream ** outputStream); + nsresult CloseOutputStream(nsDiskCacheOutputStream * outputStream); + nsresult CloseOutputStreamInternal(nsDiskCacheOutputStream * outputStream); + + nsresult Write( const char * buffer, + uint32_t count, + uint32_t * bytesWritten); + nsresult Seek(int32_t whence, int32_t offset); nsresult Tell(uint32_t * position); nsresult SetEOF(); @@ -52,14 +57,19 @@ class nsDiskCacheStreamIO : public nsIOutputStream, nsIDiskCacheStreamInternal { // and OS/2 requires that it not be private nsDiskCacheStreamIO() { NS_NOTREACHED("oops"); } private: + + + void Close(); nsresult OpenCacheFile(int flags, PRFileDesc ** fd); nsresult ReadCacheBlocks(); nsresult FlushBufferToFile(); void UpdateFileSize(); void DeleteBuffer(); + nsresult Flush(); nsDiskCacheBinding * mBinding; // not an owning reference nsDiskCacheDevice * mDevice; + nsDiskCacheOutputStream * mOutStream; // not an owning reference int32_t mInStreamCount; nsCOMPtr mLocalFile; PRFileDesc * mFD; @@ -69,8 +79,7 @@ class nsDiskCacheStreamIO : public nsIOutputStream, nsIDiskCacheStreamInternal { uint32_t mBufPos; // current mark in buffer uint32_t mBufEnd; // current end of data in buffer uint32_t mBufSize; // current end of buffer - bool mBufDirty; // Where there is unflushed data in the buffer - bool mOutputStreamIsOpen; // Whether the output stream is open (for writing...) + bool mBufDirty; char * mBuffer; }; diff --git a/toolkit/components/telemetry/Histograms.json b/toolkit/components/telemetry/Histograms.json index f80ee68bbca9..dd2c779d12e3 100644 --- a/toolkit/components/telemetry/Histograms.json +++ b/toolkit/components/telemetry/Histograms.json @@ -1635,6 +1635,18 @@ "n_buckets": 10, "description": "Total Time spent (ms) during disk cache revalidation" }, + "NETWORK_DISK_CACHE_OUTPUT_STREAM_CLOSE": { + "kind": "exponential", + "high": "10000", + "n_buckets": 10, + "description": "Time spent in nsDiskCacheOutputStream::Close() on non-main thread (ms)" + }, + "NETWORK_DISK_CACHE_OUTPUT_STREAM_CLOSE_MAIN_THREAD": { + "kind": "exponential", + "high": "10000", + "n_buckets": 10, + "description": "Time spent in nsDiskCacheOutputStream::Close() on the main thread (ms)" + }, "NETWORK_DISK_CACHE_OUTPUT_STREAM_CLOSE_INTERNAL": { "kind": "exponential", "high": "10000",