@@ -208,12 +208,10 @@ namespace Js
208
208
}
209
209
}
210
210
211
- ArrayBufferDetachedStateBase* ArrayBuffer::DetachAndGetState ()
211
+ void ArrayBuffer::Detach ()
212
212
{
213
213
Assert (!this ->isDetached );
214
214
215
- AutoPtr<ArrayBufferDetachedStateBase> arrayBufferState (this ->CreateDetachedState (this ->buffer , this ->bufferLength ));
216
-
217
215
this ->buffer = nullptr ;
218
216
this ->bufferLength = 0 ;
219
217
this ->isDetached = true ;
@@ -235,7 +233,13 @@ namespace Js
235
233
this ->DetachBufferFromParent (item->Get ());
236
234
});
237
235
}
236
+ }
238
237
238
+ ArrayBufferDetachedStateBase* ArrayBuffer::DetachAndGetState ()
239
+ {
240
+ // Save the state before detaching
241
+ AutoPtr<ArrayBufferDetachedStateBase> arrayBufferState (this ->CreateDetachedState (this ->buffer , this ->bufferLength ));
242
+ Detach ();
239
243
return arrayBufferState.Detach ();
240
244
}
241
245
@@ -594,7 +598,7 @@ namespace Js
594
598
else if (length > 0 )
595
599
{
596
600
Recycler* recycler = GetType ()->GetLibrary ()->GetRecycler ();
597
- if (recycler->ReportExternalMemoryAllocation (length))
601
+ if (recycler->RequestExternalMemoryAllocation (length))
598
602
{
599
603
buffer = (BYTE*)allocator (length);
600
604
if (buffer == nullptr )
@@ -607,7 +611,7 @@ namespace Js
607
611
{
608
612
recycler->CollectNow <CollectOnTypedArrayAllocation>();
609
613
610
- if (recycler->ReportExternalMemoryAllocation (length))
614
+ if (recycler->RequestExternalMemoryAllocation (length))
611
615
{
612
616
buffer = (BYTE*)allocator (length);
613
617
if (buffer == nullptr )
@@ -909,10 +913,10 @@ namespace Js
909
913
// Expanding buffer
910
914
if (newBufferLength > this ->bufferLength )
911
915
{
912
- if (!recycler->ReportExternalMemoryAllocation (newBufferLength - this ->bufferLength ))
916
+ if (!recycler->RequestExternalMemoryAllocation (newBufferLength - this ->bufferLength ))
913
917
{
914
918
recycler->CollectNow <CollectOnTypedArrayAllocation>();
915
- if (!recycler->ReportExternalMemoryAllocation (newBufferLength - this ->bufferLength ))
919
+ if (!recycler->RequestExternalMemoryAllocation (newBufferLength - this ->bufferLength ))
916
920
{
917
921
reportFailureFn ();
918
922
}
@@ -1001,7 +1005,7 @@ namespace Js
1001
1005
WebAssemblyArrayBuffer* WebAssemblyArrayBuffer::Create (byte* buffer, uint32 length, DynamicType * type)
1002
1006
{
1003
1007
Recycler* recycler = type->GetScriptContext ()->GetRecycler ();
1004
- WebAssemblyArrayBuffer* result;
1008
+ WebAssemblyArrayBuffer* result = nullptr ;
1005
1009
if (buffer)
1006
1010
{
1007
1011
result = RecyclerNewFinalized (recycler, WebAssemblyArrayBuffer, buffer, length, type);
@@ -1018,9 +1022,10 @@ namespace Js
1018
1022
{
1019
1023
result = RecyclerNewFinalized (recycler, WebAssemblyArrayBuffer, length, type);
1020
1024
}
1025
+ // Only add external memory when we create a new internal buffer
1026
+ recycler->AddExternalMemoryUsage (length);
1021
1027
}
1022
1028
Assert (result);
1023
- recycler->AddExternalMemoryUsage (length);
1024
1029
return result;
1025
1030
}
1026
1031
@@ -1040,71 +1045,75 @@ namespace Js
1040
1045
Assert (UNREACHED);
1041
1046
JavascriptError::ThrowTypeError (GetScriptContext (), WASMERR_BufferGrowOnly);
1042
1047
}
1043
- uint32 growSize = newBufferLength - this ->bufferLength ;
1044
1048
1045
- bool failedReport = false ;
1046
- const auto reportFailedFn = [&failedReport] { failedReport = true ; };
1049
+ uint32 growSize = newBufferLength - this ->bufferLength ;
1050
+ const auto finalizeGrowMemory = [&](WebAssemblyArrayBuffer* newArrayBuffer)
1051
+ {
1052
+ AssertOrFailFast (newArrayBuffer && newArrayBuffer->GetByteLength () == newBufferLength);
1053
+ // Detach the buffer from this ArrayBuffer
1054
+ this ->Detach ();
1055
+ return newArrayBuffer;
1056
+ };
1057
+
1058
+ // We're not growing the buffer, just create a new WebAssemblyArrayBuffer and detach this
1059
+ if (growSize == 0 )
1060
+ {
1061
+ return finalizeGrowMemory (this ->GetLibrary ()->CreateWebAssemblyArrayBuffer (this ->buffer , this ->bufferLength ));
1062
+ }
1047
1063
1048
- WebAssemblyArrayBuffer* newArrayBuffer = nullptr ;
1049
1064
#if ENABLE_FAST_ARRAYBUFFER
1065
+ // 8Gb Array case
1050
1066
if (CONFIG_FLAG (WasmFastArray))
1051
1067
{
1052
1068
AssertOrFailFast (this ->buffer );
1053
- ReportDifferentialAllocation (newBufferLength, reportFailedFn);
1054
- if (failedReport)
1069
+ const auto virtualAllocFunc = [&]
1055
1070
{
1056
- return nullptr ;
1057
- }
1058
-
1059
- if (growSize > 0 )
1071
+ return !!VirtualAlloc (this ->buffer + this ->bufferLength , growSize, MEM_COMMIT, PAGE_READWRITE);
1072
+ };
1073
+ if (!this ->GetRecycler ()->DoExternalAllocation (growSize, virtualAllocFunc))
1060
1074
{
1061
- LPVOID newMem = VirtualAlloc (this ->buffer + this ->bufferLength , growSize, MEM_COMMIT, PAGE_READWRITE);
1062
- if (!newMem)
1063
- {
1064
- Recycler* recycler = this ->GetRecycler ();
1065
- recycler->ReportExternalMemoryFailure (newBufferLength);
1066
- return nullptr ;
1067
- }
1075
+ return nullptr ;
1068
1076
}
1069
- newArrayBuffer = GetLibrary ()->CreateWebAssemblyArrayBuffer (this ->buffer , newBufferLength);
1077
+ return finalizeGrowMemory ( this -> GetLibrary ()->CreateWebAssemblyArrayBuffer (this ->buffer , newBufferLength) );
1070
1078
}
1071
- else
1072
1079
#endif
1080
+
1081
+ // No previous buffer case
1073
1082
if (this ->GetByteLength () == 0 )
1074
1083
{
1075
- if (growSize > 0 )
1076
- {
1077
- newArrayBuffer = GetLibrary ()->CreateWebAssemblyArrayBuffer (newBufferLength);
1078
- }
1079
- else
1080
- {
1081
- newArrayBuffer = GetLibrary ()->CreateWebAssemblyArrayBuffer (this ->buffer , 0 );
1082
- }
1084
+ Assert (newBufferLength == growSize);
1085
+ // Creating a new buffer will do the external memory allocation report
1086
+ return finalizeGrowMemory (this ->GetLibrary ()->CreateWebAssemblyArrayBuffer (newBufferLength));
1083
1087
}
1084
- else
1088
+
1089
+ // Regular growing case
1085
1090
{
1086
- ReportDifferentialAllocation (newBufferLength, reportFailedFn);
1087
- if (failedReport)
1091
+ // Disable Interrupts while doing a ReAlloc to minimize chances to end up in a bad state
1092
+ AutoDisableInterrupt autoDisableInterrupt (this ->GetScriptContext ()->GetThreadContext (), false );
1093
+
1094
+ byte* newBuffer = nullptr ;
1095
+ const auto reallocFunc = [&]
1088
1096
{
1089
- return nullptr ;
1090
- }
1091
- byte* newBuffer = ReallocZero (this ->buffer , this ->bufferLength , newBufferLength);
1092
- if (!newBuffer)
1097
+ newBuffer = ReallocZero (this ->buffer , this ->bufferLength , newBufferLength);
1098
+ if (newBuffer != nullptr )
1099
+ {
1100
+ // Realloc freed this->buffer
1101
+ // if anything goes wrong before we detach, we can't recover the state and should failfast
1102
+ autoDisableInterrupt.RequireExplicitCompletion ();
1103
+ }
1104
+ return !!newBuffer;
1105
+ };
1106
+
1107
+ if (!this ->GetRecycler ()->DoExternalAllocation (growSize, reallocFunc))
1093
1108
{
1094
- this ->GetRecycler ()->ReportExternalMemoryFailure (newBufferLength - this ->bufferLength );
1095
1109
return nullptr ;
1096
1110
}
1097
- newArrayBuffer = GetLibrary ()->CreateWebAssemblyArrayBuffer (newBuffer, newBufferLength);
1098
- }
1099
1111
1100
- if (!newArrayBuffer || newArrayBuffer->GetByteLength () != newBufferLength)
1101
- {
1102
- return nullptr ;
1112
+ WebAssemblyArrayBuffer* newArrayBuffer = finalizeGrowMemory (this ->GetLibrary ()->CreateWebAssemblyArrayBuffer (newBuffer, newBufferLength));
1113
+ // We've successfully Detached this buffer and created a new WebAssemblyArrayBuffer
1114
+ autoDisableInterrupt.Completed ();
1115
+ return newArrayBuffer;
1103
1116
}
1104
-
1105
- AutoDiscardPTR<Js::ArrayBufferDetachedStateBase> state (DetachAndGetState ());
1106
- state->MarkAsClaimed ();
1107
- return newArrayBuffer;
1108
1117
}
1109
1118
1110
1119
ArrayBuffer * WebAssemblyArrayBuffer::TransferInternal (uint32 newBufferLength)
0 commit comments