Bug 1591579 - XHR uses a ref-counted ArrayBufferBuilder, r=smaug

Differential Revision: https://phabricator.services.mozilla.com/D50919

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Andrea Marchesini 2019-10-31 10:26:54 +00:00
Родитель 1045168094
Коммит d7261d6930
2 изменённых файлов: 75 добавлений и 42 удалений

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

@ -229,6 +229,7 @@ XMLHttpRequestMainThread::XMLHttpRequestMainThread()
mFirstStartRequestSeen(false),
mInLoadProgressEvent(false),
mResultJSON(JS::UndefinedValue()),
mArrayBufferBuilder(new ArrayBufferBuilder()),
mResultArrayBuffer(nullptr),
mIsMappedArrayBuffer(false),
mXPCOMifier(nullptr),
@ -317,7 +318,7 @@ void XMLHttpRequestMainThread::ResetResponse() {
mResponseBlob = nullptr;
mBlobStorage = nullptr;
mResultArrayBuffer = nullptr;
mArrayBufferBuilder.reset();
mArrayBufferBuilder = new ArrayBufferBuilder();
mResultJSON.setUndefined();
mLoadTransferred = 0;
mResponseBodyDecodedPos = 0;
@ -351,7 +352,7 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(XMLHttpRequestMainThread,
XMLHttpRequestEventTarget)
tmp->mResultArrayBuffer = nullptr;
tmp->mArrayBufferBuilder.reset();
tmp->mArrayBufferBuilder = nullptr;
tmp->mResultJSON.setUndefined();
tmp->mResponseBlobImpl = nullptr;
@ -679,7 +680,7 @@ void XMLHttpRequestMainThread::GetResponse(
}
if (!mResultArrayBuffer) {
mResultArrayBuffer = mArrayBufferBuilder.getArrayBuffer(aCx);
mResultArrayBuffer = mArrayBufferBuilder->GetArrayBuffer(aCx);
if (!mResultArrayBuffer) {
aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
return;
@ -1512,11 +1513,11 @@ nsresult XMLHttpRequestMainThread::StreamReaderFunc(
!xmlHttpRequest->mIsMappedArrayBuffer) {
// get the initial capacity to something reasonable to avoid a bunch of
// reallocs right at the start
if (xmlHttpRequest->mArrayBufferBuilder.capacity() == 0)
xmlHttpRequest->mArrayBufferBuilder.setCapacity(
if (xmlHttpRequest->mArrayBufferBuilder->Capacity() == 0)
xmlHttpRequest->mArrayBufferBuilder->SetCapacity(
std::max(count, XML_HTTP_REQUEST_ARRAYBUFFER_MIN_SIZE));
if (NS_WARN_IF(!xmlHttpRequest->mArrayBufferBuilder.append(
if (NS_WARN_IF(!xmlHttpRequest->mArrayBufferBuilder->Append(
reinterpret_cast<const uint8_t*>(fromRawSegment), count,
XML_HTTP_REQUEST_ARRAYBUFFER_MAX_GROWTH))) {
return NS_ERROR_OUT_OF_MEMORY;
@ -1861,7 +1862,7 @@ XMLHttpRequestMainThread::OnStartRequest(nsIRequest* request) {
if (!jarFile) {
mIsMappedArrayBuffer = false;
} else {
rv = mArrayBufferBuilder.mapToFileInPackage(file, jarFile);
rv = mArrayBufferBuilder->MapToFileInPackage(file, jarFile);
// This can happen legitimately if there are compressed files
// in the jarFile. See bug #1357219. No need to warn on the error.
if (NS_FAILED(rv)) {
@ -1881,7 +1882,7 @@ XMLHttpRequestMainThread::OnStartRequest(nsIRequest* request) {
rv = channel->GetContentLength(&contentLength);
if (NS_SUCCEEDED(rv) && contentLength > 0 &&
contentLength < XML_HTTP_REQUEST_MAX_CONTENT_LENGTH_PREALLOCATE) {
mArrayBufferBuilder.setCapacity(static_cast<int32_t>(contentLength));
mArrayBufferBuilder->SetCapacity(static_cast<int32_t>(contentLength));
}
}
}
@ -2154,7 +2155,7 @@ XMLHttpRequestMainThread::OnStopRequest(nsIRequest* request, nsresult status) {
mResponseType == XMLHttpRequestResponseType::Arraybuffer) {
// set the capacity down to the actual length, to realloc back
// down to the actual size
if (!mArrayBufferBuilder.setCapacity(mArrayBufferBuilder.length())) {
if (!mArrayBufferBuilder->SetCapacity(mArrayBufferBuilder->Length())) {
// this should never happen!
status = NS_ERROR_UNEXPECTED;
}
@ -3651,11 +3652,13 @@ nsXMLHttpRequestXPCOMifier::GetInterface(const nsIID& aIID, void** aResult) {
}
ArrayBufferBuilder::ArrayBufferBuilder()
: mDataPtr(nullptr), mCapacity(0), mLength(0), mMapPtr(nullptr) {}
: mMutex("ArrayBufferBuilder"),
mDataPtr(nullptr),
mCapacity(0),
mLength(0),
mMapPtr(nullptr) {}
ArrayBufferBuilder::~ArrayBufferBuilder() { reset(); }
void ArrayBufferBuilder::reset() {
ArrayBufferBuilder::~ArrayBufferBuilder() {
if (mDataPtr) {
JS_free(nullptr, mDataPtr);
}
@ -3669,7 +3672,13 @@ void ArrayBufferBuilder::reset() {
mCapacity = mLength = 0;
}
bool ArrayBufferBuilder::setCapacity(uint32_t aNewCap) {
bool ArrayBufferBuilder::SetCapacity(uint32_t aNewCap) {
MutexAutoLock lock(mMutex);
return SetCapacityInternal(aNewCap, lock);
}
bool ArrayBufferBuilder::SetCapacityInternal(
uint32_t aNewCap, const MutexAutoLock& aProofOfLock) {
MOZ_ASSERT(!mMapPtr);
// To ensure that realloc won't free mDataPtr, use a size of 1
@ -3693,8 +3702,9 @@ bool ArrayBufferBuilder::setCapacity(uint32_t aNewCap) {
return true;
}
bool ArrayBufferBuilder::append(const uint8_t* aNewData, uint32_t aDataLen,
bool ArrayBufferBuilder::Append(const uint8_t* aNewData, uint32_t aDataLen,
uint32_t aMaxGrowth) {
MutexAutoLock lock(mMutex);
MOZ_ASSERT(!mMapPtr);
CheckedUint32 neededCapacity = mLength;
@ -3720,14 +3730,14 @@ bool ArrayBufferBuilder::append(const uint8_t* aNewData, uint32_t aDataLen,
newcap = neededCapacity;
}
if (!setCapacity(newcap.value())) {
if (!SetCapacityInternal(newcap.value(), lock)) {
return false;
}
}
// Assert that the region isn't overlapping so we can memcpy.
MOZ_ASSERT(
!areOverlappingRegions(aNewData, aDataLen, mDataPtr + mLength, aDataLen));
!AreOverlappingRegions(aNewData, aDataLen, mDataPtr + mLength, aDataLen));
memcpy(mDataPtr + mLength, aNewData, aDataLen);
mLength += aDataLen;
@ -3735,7 +3745,19 @@ bool ArrayBufferBuilder::append(const uint8_t* aNewData, uint32_t aDataLen,
return true;
}
JSObject* ArrayBufferBuilder::getArrayBuffer(JSContext* aCx) {
uint32_t ArrayBufferBuilder::Length() {
MutexAutoLock lock(mMutex);
return mLength;
}
uint32_t ArrayBufferBuilder::Capacity() {
MutexAutoLock lock(mMutex);
return mCapacity;
}
JSObject* ArrayBufferBuilder::GetArrayBuffer(JSContext* aCx) {
MutexAutoLock lock(mMutex);
if (mMapPtr) {
JSObject* obj = JS::NewMappedArrayBufferWithContents(aCx, mLength, mMapPtr);
if (!obj) {
@ -3751,7 +3773,7 @@ JSObject* ArrayBufferBuilder::getArrayBuffer(JSContext* aCx) {
// we need to check for mLength == 0, because nothing may have been
// added
if (mCapacity > mLength || mLength == 0) {
if (!setCapacity(mLength)) {
if (!SetCapacityInternal(mLength, lock)) {
return nullptr;
}
}
@ -3765,8 +3787,10 @@ JSObject* ArrayBufferBuilder::getArrayBuffer(JSContext* aCx) {
return obj;
}
nsresult ArrayBufferBuilder::mapToFileInPackage(const nsCString& aFile,
nsresult ArrayBufferBuilder::MapToFileInPackage(const nsCString& aFile,
nsIFile* aJarFile) {
MutexAutoLock lock(mMutex);
nsresult rv;
// Open Jar file to get related attributes of target file.
@ -3801,7 +3825,7 @@ nsresult ArrayBufferBuilder::mapToFileInPackage(const nsCString& aFile,
}
/* static */
bool ArrayBufferBuilder::areOverlappingRegions(const uint8_t* aStart1,
bool ArrayBufferBuilder::AreOverlappingRegions(const uint8_t* aStart1,
uint32_t aLength1,
const uint8_t* aStart2,
uint32_t aLength2) {

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

@ -73,24 +73,17 @@ struct OriginAttributesDictionary;
// before creating the ArrayBuffer itself. Will do doubling
// based reallocation, up to an optional maximum growth given.
//
// When all the data has been appended, call getArrayBuffer,
// When all the data has been appended, call GetArrayBuffer,
// passing in the JSContext* for which the ArrayBuffer object
// is to be created. This also implicitly resets the builder,
// or it can be reset explicitly at any point by calling reset().
// is to be created. This also implicitly resets the builder.
class ArrayBufferBuilder {
uint8_t* mDataPtr;
uint32_t mCapacity;
uint32_t mLength;
void* mMapPtr;
public:
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ArrayBufferBuilder);
ArrayBufferBuilder();
~ArrayBufferBuilder();
void reset();
// Will truncate if aNewCap is < length().
bool setCapacity(uint32_t aNewCap);
// Will truncate if aNewCap is < Length().
bool SetCapacity(uint32_t aNewCap);
// Append aDataLen bytes from data to the current buffer. If we
// need to grow the buffer, grow by doubling the size up to a
@ -99,13 +92,13 @@ class ArrayBufferBuilder {
//
// The data parameter must not overlap with anything beyond the
// builder's current valid contents [0..length)
bool append(const uint8_t* aNewData, uint32_t aDataLen,
bool Append(const uint8_t* aNewData, uint32_t aDataLen,
uint32_t aMaxGrowth = 0);
uint32_t length() { return mLength; }
uint32_t capacity() { return mCapacity; }
uint32_t Length();
uint32_t Capacity();
JSObject* getArrayBuffer(JSContext* aCx);
JSObject* GetArrayBuffer(JSContext* aCx);
// Memory mapping to starting position of file(aFile) in the zip
// package(aJarFile).
@ -113,11 +106,27 @@ class ArrayBufferBuilder {
// The file in the zip package has to be uncompressed and the starting
// position of the file must be aligned according to array buffer settings
// in JS engine.
nsresult mapToFileInPackage(const nsCString& aFile, nsIFile* aJarFile);
nsresult MapToFileInPackage(const nsCString& aFile, nsIFile* aJarFile);
protected:
static bool areOverlappingRegions(const uint8_t* aStart1, uint32_t aLength1,
private:
~ArrayBufferBuilder();
ArrayBufferBuilder(const ArrayBufferBuilder&) = delete;
ArrayBufferBuilder& operator=(const ArrayBufferBuilder&) = delete;
ArrayBufferBuilder& operator=(const ArrayBufferBuilder&&) = delete;
bool SetCapacityInternal(uint32_t aNewCap, const MutexAutoLock& aProofOfLock);
static bool AreOverlappingRegions(const uint8_t* aStart1, uint32_t aLength1,
const uint8_t* aStart2, uint32_t aLength2);
Mutex mMutex;
// All of these are protected by mMutex.
uint8_t* mDataPtr;
uint32_t mCapacity;
uint32_t mLength;
void* mMapPtr;
};
class nsXMLHttpRequestXPCOMifier;
@ -718,7 +727,7 @@ class XMLHttpRequestMainThread final : public XMLHttpRequest,
JS::Heap<JS::Value> mResultJSON;
ArrayBufferBuilder mArrayBufferBuilder;
RefPtr<ArrayBufferBuilder> mArrayBufferBuilder;
JS::Heap<JSObject*> mResultArrayBuffer;
bool mIsMappedArrayBuffer;