Bug 1249739 - Improve performance in XHR in workers - part 3 - Implement XMLHttpRequestStringSnapshot, r=smaug

This commit is contained in:
Andrea Marchesini 2016-09-12 09:06:33 +02:00
Родитель def1f6e7db
Коммит 284e7077e7
6 изменённых файлов: 265 добавлений и 23 удалений

Просмотреть файл

@ -567,9 +567,7 @@ NS_IMETHODIMP
XMLHttpRequestMainThread::GetResponseText(nsAString& aResponseText)
{
ErrorResult rv;
nsString responseText;
GetResponseText(responseText, rv);
aResponseText = responseText;
GetResponseText(aResponseText, rv);
return rv.StealNSResult();
}
@ -577,7 +575,20 @@ void
XMLHttpRequestMainThread::GetResponseText(nsAString& aResponseText,
ErrorResult& aRv)
{
aResponseText.Truncate();
XMLHttpRequestStringSnapshot snapshot;
GetResponseText(snapshot, aRv);
if (aRv.Failed()) {
return;
}
snapshot.GetAsString(aResponseText);
}
void
XMLHttpRequestMainThread::GetResponseText(XMLHttpRequestStringSnapshot& aSnapshot,
ErrorResult& aRv)
{
aSnapshot.Reset();
if (mResponseType != XMLHttpRequestResponseType::_empty &&
mResponseType != XMLHttpRequestResponseType::Text &&
@ -588,7 +599,7 @@ XMLHttpRequestMainThread::GetResponseText(nsAString& aResponseText,
if (mResponseType == XMLHttpRequestResponseType::Moz_chunked_text &&
!mInLoadProgressEvent) {
aResponseText.SetIsVoid(true);
aSnapshot.SetVoid();
return;
}
@ -601,7 +612,7 @@ XMLHttpRequestMainThread::GetResponseText(nsAString& aResponseText,
// more.
if ((!mResponseXML && !mErrorParsingXML) ||
mResponseBodyDecodedPos == mResponseBody.Length()) {
mResponseText.GetAsString(aResponseText);
mResponseText.CreateSnapshot(aSnapshot);
return;
}
@ -623,7 +634,7 @@ XMLHttpRequestMainThread::GetResponseText(nsAString& aResponseText,
mResponseBodyDecodedPos = 0;
}
mResponseText.GetAsString(aResponseText);
mResponseText.CreateSnapshot(aSnapshot);
}
nsresult
@ -746,7 +757,7 @@ XMLHttpRequestMainThread::GetResponse(JSContext* aCx,
case XMLHttpRequestResponseType::Text:
case XMLHttpRequestResponseType::Moz_chunked_text:
{
nsString str;
nsAutoString str;
aRv = GetResponseText(str);
if (aRv.Failed()) {
return;

Просмотреть файл

@ -470,6 +470,10 @@ public:
virtual void
GetResponseText(nsAString& aResponseText, ErrorResult& aRv) override;
void
GetResponseText(XMLHttpRequestStringSnapshot& aSnapshot,
ErrorResult& aRv);
virtual nsIDocument*
GetResponseXML(ErrorResult& aRv) override;

Просмотреть файл

@ -5,6 +5,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "XMLHttpRequestString.h"
#include "mozilla/Mutex.h"
#include "nsISupportsImpl.h"
namespace mozilla {
@ -13,12 +14,26 @@ namespace dom {
class XMLHttpRequestStringBuffer final
{
friend class XMLHttpRequestStringWriterHelper;
friend class XMLHttpRequestStringSnapshotReaderHelper;
public:
NS_INLINE_DECL_REFCOUNTING(XMLHttpRequestStringBuffer)
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(XMLHttpRequestStringBuffer)
NS_DECL_OWNINGTHREAD
XMLHttpRequestStringBuffer()
: mMutex("XMLHttpRequestStringBuffer::mMutex")
{
}
uint32_t
Length()
{
MutexAutoLock lock(mMutex);
return mData.Length();
}
uint32_t
UnsafeLength() const
{
return mData.Length();
}
@ -26,12 +41,16 @@ public:
void
Append(const nsAString& aString)
{
NS_ASSERT_OWNINGTHREAD(XMLHttpRequestStringBuffer);
MutexAutoLock lock(mMutex);
mData.Append(aString);
}
void
GetAsString(nsAString& aString)
{
MutexAutoLock lock(mMutex);
aString = mData;
}
@ -41,15 +60,33 @@ public:
return mData.SizeOfExcludingThisIfUnshared(aMallocSizeOf);
}
void
GetAsString(nsAString& aString, uint32_t aLength)
{
MutexAutoLock lock(mMutex);
MOZ_ASSERT(aLength <= mData.Length());
aString.Assign(mData.BeginReading(), aLength);
}
void
CreateSnapshot(XMLHttpRequestStringSnapshot& aSnapshot)
{
MutexAutoLock lock(mMutex);
aSnapshot.Set(this, mData.Length());
}
private:
~XMLHttpRequestStringBuffer()
{}
nsString& Data()
nsString& UnsafeData()
{
return mData;
}
Mutex mMutex;
// The following member variable is protected by mutex.
nsString mData;
};
@ -101,30 +138,116 @@ XMLHttpRequestString::IsEmpty() const
return !mBuffer->Length();
}
void
XMLHttpRequestString::CreateSnapshot(XMLHttpRequestStringSnapshot& aSnapshot)
{
mBuffer->CreateSnapshot(aSnapshot);
}
// ---------------------------------------------------------------------------
// XMLHttpRequestStringSnapshot
XMLHttpRequestStringSnapshot::XMLHttpRequestStringSnapshot()
: mLength(0)
, mVoid(false)
{
}
XMLHttpRequestStringSnapshot::~XMLHttpRequestStringSnapshot()
{
}
XMLHttpRequestStringSnapshot&
XMLHttpRequestStringSnapshot::operator=(const XMLHttpRequestStringSnapshot& aOther)
{
mBuffer = aOther.mBuffer;
mLength = aOther.mLength;
mVoid = aOther.mVoid;
return *this;
}
void
XMLHttpRequestStringSnapshot::ResetInternal(bool aIsVoid)
{
mBuffer = nullptr;
mLength = 0;
mVoid = aIsVoid;
}
void
XMLHttpRequestStringSnapshot::Set(XMLHttpRequestStringBuffer* aBuffer,
uint32_t aLength)
{
MOZ_ASSERT(aBuffer);
MOZ_ASSERT(aLength <= aBuffer->UnsafeLength());
mBuffer = aBuffer;
mLength = aLength;
mVoid = false;
}
void
XMLHttpRequestStringSnapshot::GetAsString(nsAString& aString) const
{
if (mBuffer) {
MOZ_ASSERT(!mVoid);
mBuffer->GetAsString(aString, mLength);
return;
}
aString.Truncate();
if (mVoid) {
aString.SetIsVoid(true);
}
}
// ---------------------------------------------------------------------------
// XMLHttpRequestStringWriterHelper
XMLHttpRequestStringWriterHelper::XMLHttpRequestStringWriterHelper(XMLHttpRequestString& aString)
: mBuffer(aString.mBuffer)
, mLock(aString.mBuffer->mMutex)
{
}
bool
XMLHttpRequestStringWriterHelper::AddCapacity(int32_t aCapacity)
{
return mBuffer->Data().SetCapacity(mBuffer->Length() + aCapacity, fallible);
return mBuffer->UnsafeData().SetCapacity(mBuffer->UnsafeLength() + aCapacity, fallible);
}
char16_t*
XMLHttpRequestStringWriterHelper::EndOfExistingData()
{
return mBuffer->Data().BeginWriting() + mBuffer->Length();
return mBuffer->UnsafeData().BeginWriting() + mBuffer->UnsafeLength();
}
void
XMLHttpRequestStringWriterHelper::AddLength(int32_t aLength)
{
mBuffer->Data().SetLength(mBuffer->Length() + aLength);
mBuffer->UnsafeData().SetLength(mBuffer->UnsafeLength() + aLength);
}
// ---------------------------------------------------------------------------
// XMLHttpRequestStringReaderHelper
XMLHttpRequestStringSnapshotReaderHelper::XMLHttpRequestStringSnapshotReaderHelper(XMLHttpRequestStringSnapshot& aSnapshot)
: mBuffer(aSnapshot.mBuffer)
, mLock(aSnapshot.mBuffer->mMutex)
{
}
const char16_t*
XMLHttpRequestStringSnapshotReaderHelper::Buffer() const
{
return mBuffer->UnsafeData().BeginReading();
}
uint32_t
XMLHttpRequestStringSnapshotReaderHelper::Length() const
{
return mBuffer->UnsafeLength();
}
} // dom namespace

Просмотреть файл

@ -10,11 +10,20 @@
#include "nsString.h"
namespace mozilla {
class Mutex;
namespace dom {
class XMLHttpRequestStringBuffer;
class XMLHttpRequestStringSnapshot;
class XMLHttpRequestStringWriterHelper;
class XMLHttpRequestStringSnapshotReaderHelper;
// We want to avoid the dup of strings when XHR in workers has access to
// responseText for events dispatched during the loading state. For this reason
// we use this class, able to create snapshots of the current size of itself
// without making extra copies.
class XMLHttpRequestString final
{
friend class XMLHttpRequestStringWriterHelper;
@ -29,6 +38,9 @@ public:
void Append(const nsAString& aString);
// This method should be called only when the string is really needed because
// it can cause the duplication of the strings in case the loading of the XHR
// is not completed yet.
void GetAsString(nsAString& aString) const;
size_t SizeOfThis(MallocSizeOf aMallocSizeOf) const;
@ -37,11 +49,18 @@ public:
bool IsEmpty() const;
void CreateSnapshot(XMLHttpRequestStringSnapshot& aSnapshot);
private:
XMLHttpRequestString(const XMLHttpRequestString&) = delete;
XMLHttpRequestString& operator=(const XMLHttpRequestString&) = delete;
XMLHttpRequestString& operator=(const XMLHttpRequestString&&) = delete;
RefPtr<XMLHttpRequestStringBuffer> mBuffer;
};
class XMLHttpRequestStringWriterHelper final
// This class locks the buffer and allows the callee to write data into it.
class MOZ_STACK_CLASS XMLHttpRequestStringWriterHelper final
{
public:
explicit XMLHttpRequestStringWriterHelper(XMLHttpRequestString& aString);
@ -56,7 +75,83 @@ public:
AddLength(int32_t aLength);
private:
XMLHttpRequestStringWriterHelper(const XMLHttpRequestStringWriterHelper&) = delete;
XMLHttpRequestStringWriterHelper& operator=(const XMLHttpRequestStringWriterHelper&) = delete;
XMLHttpRequestStringWriterHelper& operator=(const XMLHttpRequestStringWriterHelper&&) = delete;
RefPtr<XMLHttpRequestStringBuffer> mBuffer;
MutexAutoLock mLock;
};
// This class is the internal XMLHttpRequestStringBuffer of the
// XMLHttpRequestString plus the current length. GetAsString will return the
// string with that particular length also if the XMLHttpRequestStringBuffer is
// grown in the meantime.
class XMLHttpRequestStringSnapshot final
{
friend class XMLHttpRequestStringBuffer;
friend class XMLHttpRequestStringSnapshotReaderHelper;
public:
XMLHttpRequestStringSnapshot();
~XMLHttpRequestStringSnapshot();
XMLHttpRequestStringSnapshot& operator=(const XMLHttpRequestStringSnapshot&);
void Reset()
{
ResetInternal(false /* isVoid */);
}
void SetVoid()
{
ResetInternal(true /* isVoid */);
}
bool IsVoid() const
{
return mVoid;
}
bool IsEmpty() const
{
return !mLength;
}
void GetAsString(nsAString& aString) const;
private:
XMLHttpRequestStringSnapshot(const XMLHttpRequestStringSnapshot&) = delete;
XMLHttpRequestStringSnapshot& operator=(const XMLHttpRequestStringSnapshot&&) = delete;
void Set(XMLHttpRequestStringBuffer* aBuffer, uint32_t aLength);
void ResetInternal(bool aIsVoid);
RefPtr<XMLHttpRequestStringBuffer> mBuffer;
uint32_t mLength;
bool mVoid;
};
// This class locks the buffer and allows the callee to read data from it.
class MOZ_STACK_CLASS XMLHttpRequestStringSnapshotReaderHelper final
{
public:
explicit XMLHttpRequestStringSnapshotReaderHelper(XMLHttpRequestStringSnapshot& aSnapshot);
const char16_t*
Buffer() const;
uint32_t
Length() const;
private:
XMLHttpRequestStringSnapshotReaderHelper(const XMLHttpRequestStringSnapshotReaderHelper&) = delete;
XMLHttpRequestStringSnapshotReaderHelper& operator=(const XMLHttpRequestStringSnapshotReaderHelper&) = delete;
XMLHttpRequestStringSnapshotReaderHelper& operator=(const XMLHttpRequestStringSnapshotReaderHelper&&) = delete;
RefPtr<XMLHttpRequestStringBuffer> mBuffer;
MutexAutoLock mLock;
};
} // dom namespace

Просмотреть файл

@ -476,7 +476,7 @@ class EventRunnable final : public MainThreadProxyRunnable
nsString mType;
nsString mResponseType;
JS::Heap<JS::Value> mResponse;
nsString mResponseText;
XMLHttpRequestStringSnapshot mResponseText;
nsString mResponseURL;
nsCString mStatusText;
uint64_t mLoaded;
@ -1129,7 +1129,10 @@ EventRunnable::PreDispatch(WorkerPrivate* /* unused */)
MOZ_ASSERT(false, "This should never fail!");
}
mResponseTextResult = xhr->GetResponseText(mResponseText);
ErrorResult rv;
xhr->GetResponseText(mResponseText, rv);
mResponseTextResult = rv.StealNSResult();
if (NS_SUCCEEDED(mResponseTextResult)) {
mResponseResult = mResponseTextResult;
if (mResponseText.IsVoid()) {
@ -1172,7 +1175,6 @@ EventRunnable::PreDispatch(WorkerPrivate* /* unused */)
}
if (doClone) {
ErrorResult rv;
Write(cx, response, transferable, rv);
if (NS_WARN_IF(rv.Failed())) {
NS_WARNING("Failed to clone response!");
@ -1186,7 +1188,6 @@ EventRunnable::PreDispatch(WorkerPrivate* /* unused */)
mStatusResult = xhr->GetStatus(&mStatus);
ErrorResult rv;
xhr->GetStatusText(mStatusText, rv);
MOZ_ASSERT(!rv.Failed());
@ -2391,10 +2392,11 @@ XMLHttpRequestWorker::GetResponse(JSContext* /* unused */,
mStateData.mResponse =
JS_GetEmptyStringValue(mWorkerPrivate->GetJSContext());
} else {
XMLHttpRequestStringSnapshotReaderHelper helper(mStateData.mResponseText);
JSString* str =
JS_NewUCStringCopyN(mWorkerPrivate->GetJSContext(),
mStateData.mResponseText.get(),
mStateData.mResponseText.Length());
helper.Buffer(), helper.Length());
if (!str) {
aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
@ -2414,7 +2416,13 @@ void
XMLHttpRequestWorker::GetResponseText(nsAString& aResponseText, ErrorResult& aRv)
{
aRv = mStateData.mResponseTextResult;
aResponseText = mStateData.mResponseText;
if (aRv.Failed()) {
return;
}
nsAutoString foo;
mStateData.mResponseText.GetAsString(foo);
aResponseText.Assign(foo.BeginReading(), foo.Length());
}
void

Просмотреть файл

@ -9,6 +9,7 @@
#include "WorkerHolder.h"
#include "XMLHttpRequest.h"
#include "XMLHttpRequestString.h"
#include "mozilla/dom/TypedArray.h"
namespace mozilla {
@ -27,7 +28,7 @@ class XMLHttpRequestWorker final : public XMLHttpRequest,
public:
struct StateData
{
nsString mResponseText;
XMLHttpRequestStringSnapshot mResponseText;
nsString mResponseURL;
uint32_t mStatus;
nsCString mStatusText;
@ -266,7 +267,7 @@ public:
void
NullResponseText()
{
mStateData.mResponseText.SetIsVoid(true);
mStateData.mResponseText.SetVoid();
mStateData.mResponse.setNull();
}