| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757 |
- //-------------------------------------------------------------------------------------------------------
- // Copyright (C) Microsoft. All rights reserved.
- // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
- //-------------------------------------------------------------------------------------------------------
- #include "RuntimeLibraryPch.h"
- namespace Js
- {
- #if DBG
- void SharedContents::AddAgent(DWORD_PTR agent)
- {
- AutoCriticalSection autoCS(&csAgent);
- if (allowedAgents == nullptr)
- {
- allowedAgents = HeapNew(SharableAgents, &HeapAllocator::Instance);
- }
- allowedAgents->Add(agent);
- }
- bool SharedContents::IsValidAgent(DWORD_PTR agent)
- {
- AutoCriticalSection autoCS(&csAgent);
- return allowedAgents != nullptr && allowedAgents->Contains(agent);
- }
- #endif
- long SharedContents::AddRef()
- {
- return InterlockedIncrement(&refCount);
- }
- long SharedContents::Release()
- {
- long ret = InterlockedDecrement(&refCount);
- AssertOrFailFastMsg(ret >= 0, "Buffer already freed");
- return ret;
- }
- void SharedContents::Cleanup()
- {
- Assert(refCount == 0);
- buffer = nullptr;
- bufferLength = 0;
- #if DBG
- {
- AutoCriticalSection autoCS(&csAgent);
- if (allowedAgents != nullptr)
- {
- HeapDelete(allowedAgents);
- allowedAgents = nullptr;
- }
- }
- #endif
- if (indexToWaiterList != nullptr)
- {
- // TODO: the map should be empty here?
- // or we need to wake all the waiters from current context?
- indexToWaiterList->Map([](uint index, WaiterList *waiters)
- {
- if (waiters != nullptr)
- {
- waiters->Cleanup();
- HeapDelete(waiters);
- waiters = nullptr;
- }
- });
- HeapDelete(indexToWaiterList);
- indexToWaiterList = nullptr;
- }
- }
- Var SharedArrayBuffer::NewInstance(RecyclableObject* function, CallInfo callInfo, ...)
- {
- PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
- ARGUMENTS(args, callInfo);
- ScriptContext* scriptContext = function->GetScriptContext();
- AssertMsg(args.Info.Count > 0, "Should always have implicit 'this'");
- Var newTarget = args.GetNewTarget();
- bool isCtorSuperCall = JavascriptOperators::GetAndAssertIsConstructorSuperCall(args);
- if (!(callInfo.Flags & CallFlags_New) || (newTarget && JavascriptOperators::IsUndefinedObject(newTarget)))
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_ClassConstructorCannotBeCalledWithoutNew, _u("SharedArrayBuffer"));
- }
- uint32 byteLength = 0;
- if (args.Info.Count > 1)
- {
- byteLength = ArrayBuffer::ToIndex(args[1], JSERR_ArrayLengthConstructIncorrect, scriptContext, MaxSharedArrayBufferLength);
- }
- RecyclableObject* newArr = scriptContext->GetLibrary()->CreateSharedArrayBuffer(byteLength);
- return isCtorSuperCall ?
- JavascriptOperators::OrdinaryCreateFromConstructor(VarTo<RecyclableObject>(newTarget), newArr, nullptr, scriptContext) :
- newArr;
- }
- // SharedArrayBuffer.prototype.byteLength
- Var SharedArrayBuffer::EntryGetterByteLength(RecyclableObject* function, CallInfo callInfo, ...)
- {
- PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
- ARGUMENTS(args, callInfo);
- ScriptContext* scriptContext = function->GetScriptContext();
- Assert(!(callInfo.Flags & CallFlags_New));
- if (args.Info.Count == 0 || !VarIs<SharedArrayBuffer>(args[0]))
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_NeedSharedArrayBufferObject);
- }
- SharedArrayBuffer* sharedArrayBuffer = VarTo<SharedArrayBuffer>(args[0]);
- return JavascriptNumber::ToVar(sharedArrayBuffer->GetByteLength(), scriptContext);
- }
- // SharedArrayBuffer.prototype.slice
- Var SharedArrayBuffer::EntrySlice(RecyclableObject* function, CallInfo callInfo, ...)
- {
- PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
- ARGUMENTS(args, callInfo);
- ScriptContext* scriptContext = function->GetScriptContext();
- AssertMsg(args.Info.Count > 0, "Should always have implicit 'this'");
- Assert(!(callInfo.Flags & CallFlags_New));
- if (!VarIs<SharedArrayBuffer>(args[0]))
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_NeedSharedArrayBufferObject);
- }
- JavascriptLibrary* library = scriptContext->GetLibrary();
- SharedArrayBuffer* currentBuffer = VarTo<SharedArrayBuffer>(args[0]);
- int64 currentLen = (int64)currentBuffer->GetByteLength();
- int64 start = 0, end = 0;
- int64 newLen = 0;
- // If no start or end arguments, use the entire length
- if (args.Info.Count < 2)
- {
- newLen = currentLen;
- }
- else
- {
- start = JavascriptArray::GetIndexFromVar(args[1], currentLen, scriptContext);
- // If no end argument, use length as the end
- if (args.Info.Count < 3 || args[2] == library->GetUndefined())
- {
- end = currentLen;
- }
- else
- {
- end = JavascriptArray::GetIndexFromVar(args[2], currentLen, scriptContext);
- }
- newLen = end > start ? end - start : 0;
- }
- // We can't have allocated an SharedArrayBuffer with byteLength > MaxArrayBufferLength.
- // start and end are clamped to valid indices, so the new length also cannot exceed MaxArrayBufferLength.
- // Therefore, should be safe to cast down newLen to uint32.
- Assert(newLen < MaxSharedArrayBufferLength);
- uint32 newbyteLength = static_cast<uint32>(newLen);
- SharedArrayBuffer* newBuffer = nullptr;
- JavascriptFunction* defaultConstructor = scriptContext->GetLibrary()->GetSharedArrayBufferConstructor();
- RecyclableObject* constructor = JavascriptOperators::SpeciesConstructor(currentBuffer, defaultConstructor, scriptContext);
- AssertOrFailFast(JavascriptOperators::IsConstructor(constructor));
- bool isDefaultConstructor = constructor == defaultConstructor;
- Js::Var newVar = JavascriptOperators::NewObjectCreationHelper_ReentrancySafe(constructor, isDefaultConstructor, scriptContext->GetThreadContext(), [=]()->Js::Var
- {
- Js::Var constructorArgs[] = { constructor, JavascriptNumber::ToVar(newbyteLength, scriptContext) };
- Js::CallInfo constructorCallInfo(Js::CallFlags_New, _countof(constructorArgs));
- return JavascriptOperators::NewScObject(constructor, Js::Arguments(constructorCallInfo, constructorArgs), scriptContext);
- });
- if (!VarIs<SharedArrayBuffer>(newVar))
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_NeedSharedArrayBufferObject);
- }
- newBuffer = VarTo<SharedArrayBuffer>(newVar);
- if (newBuffer == currentBuffer)
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_NeedSharedArrayBufferObject);
- }
- if (newBuffer->GetByteLength() < newbyteLength)
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_ArgumentOutOfRange, _u("SharedArrayBuffer.prototype.slice"));
- }
- Assert(newBuffer);
- Assert(newBuffer->GetByteLength() >= newbyteLength);
- // Don't bother doing memcpy if we aren't copying any elements
- if (newbyteLength > 0)
- {
- AssertMsg(currentBuffer->GetBuffer() != nullptr, "buffer must not be null when we copy from it");
- js_memcpy_s(newBuffer->GetBuffer(), newbyteLength, currentBuffer->GetBuffer() + start, newbyteLength);
- }
- return newBuffer;
- }
- Var SharedArrayBuffer::EntryGetterSymbolSpecies(RecyclableObject* function, CallInfo callInfo, ...)
- {
- ARGUMENTS(args, callInfo);
- Assert(args.Info.Count > 0);
- return args[0];
- }
- BYTE* SharedArrayBuffer::AllocBuffer(uint32 length, uint32 maxLength)
- {
- Unused(maxLength); // WebAssembly only
- #if ENABLE_FAST_ARRAYBUFFER
- if (this->IsValidVirtualBufferLength(length))
- {
- return (BYTE*)AsmJsVirtualAllocator(length);
- }
- else
- #endif
- {
- return HeapNewNoThrowArray(BYTE, length);
- }
- }
- void SharedArrayBuffer::FreeBuffer(BYTE* buffer, uint32 length, uint32 maxLength)
- {
- Unused(maxLength); // WebAssembly only
- #if ENABLE_FAST_ARRAYBUFFER
- //AsmJS Virtual Free
- if (this->IsValidVirtualBufferLength(length))
- {
- FreeMemAlloc(buffer);
- }
- else
- #endif
- {
- HeapDeleteArray(length, buffer);
- }
- }
- void SharedArrayBuffer::Init(uint32 length, uint32 maxLength)
- {
- AssertOrFailFast(!sharedContents && length <= maxLength);
- BYTE * buffer = nullptr;
- if (length > MaxSharedArrayBufferLength)
- {
- // http://tc39.github.io/ecmascript_sharedmem/shmem.html#DataTypesValues.SpecTypes.DataBlocks.CreateSharedByteDataBlock
- // Let db be a new Shared Data Block value consisting of size bytes.
- // If it is impossible to create such a Shared Data Block, throw a RangeError exception.
- JavascriptError::ThrowRangeError(GetScriptContext(), JSERR_FunctionArgument_Invalid);
- }
- SharedContents* localSharedContents = HeapNewNoThrow(SharedContents, nullptr, length, maxLength);
- if (localSharedContents == nullptr)
- {
- JavascriptError::ThrowOutOfMemoryError(GetScriptContext());
- }
- struct AutoCleanupSharedContents
- {
- SharedContents* sharedContents;
- bool allocationCompleted = false;
- AutoCleanupSharedContents(SharedContents* sharedContents) : sharedContents(sharedContents) {}
- ~AutoCleanupSharedContents()
- {
- if (!allocationCompleted)
- {
- HeapDelete(sharedContents);
- }
- }
- } autoCleanupSharedContents(localSharedContents);
- Recycler* recycler = GetType()->GetLibrary()->GetRecycler();
- if (maxLength != 0)
- {
- if (recycler->RequestExternalMemoryAllocation(length))
- {
- buffer = this->AllocBuffer(length, maxLength);
- if (buffer == nullptr)
- {
- recycler->CollectNow<CollectOnTypedArrayAllocation>();
- buffer = this->AllocBuffer(length, maxLength);
- if (buffer == nullptr)
- {
- recycler->ReportExternalMemoryFailure(length);
- JavascriptError::ThrowOutOfMemoryError(GetScriptContext());
- }
- }
- }
- else
- {
- JavascriptError::ThrowOutOfMemoryError(GetScriptContext());
- }
- Assert(buffer != nullptr);
- ZeroMemory(buffer, length);
- }
- localSharedContents->buffer = buffer;
- #if DBG
- localSharedContents->AddAgent((DWORD_PTR)GetScriptContext());
- #endif
- sharedContents = localSharedContents;
- autoCleanupSharedContents.allocationCompleted = true;
- }
- SharedArrayBuffer::SharedArrayBuffer(DynamicType * type) :
- ArrayBufferBase(type), sharedContents(nullptr)
- {
- }
- SharedArrayBuffer::SharedArrayBuffer(SharedContents * contents, DynamicType * type) :
- ArrayBufferBase(type), sharedContents(nullptr)
- {
- if (contents == nullptr || contents->bufferLength > MaxSharedArrayBufferLength)
- {
- JavascriptError::ThrowTypeError(GetScriptContext(), JSERR_FunctionArgument_Invalid);
- }
- if (contents->AddRef() > 1)
- {
- sharedContents = contents;
- }
- else
- {
- Js::Throw::FatalInternalError();
- }
- #if DBG
- sharedContents->AddAgent((DWORD_PTR)GetScriptContext());
- #endif
- }
- SharedArrayBuffer * SharedArrayBuffer::GetAsSharedArrayBuffer()
- {
- AssertOrFailFast(VarIsCorrectType(this));
- return this;
- }
- CriticalSection SharedArrayBuffer::csSharedArrayBuffer;
- WaiterList *SharedArrayBuffer::GetWaiterList(uint index)
- {
- if (sharedContents != nullptr)
- {
- // REVIEW: only lock creating the map and pass the lock to the map?
- // use one lock per instance?
- AutoCriticalSection autoCS(&csSharedArrayBuffer);
- if (sharedContents->indexToWaiterList == nullptr)
- {
- sharedContents->indexToWaiterList = HeapNew(IndexToWaitersMap, &HeapAllocator::Instance);
- }
- WaiterList * waiters = nullptr;
- if (!sharedContents->indexToWaiterList->TryGetValue(index, &waiters))
- {
- waiters = HeapNew(WaiterList);
- sharedContents->indexToWaiterList->Add(index, waiters);
- }
- return waiters;
- }
- Assert(false);
- return nullptr;
- }
- uint32 SharedArrayBuffer::GetByteLength() const
- {
- return sharedContents != nullptr ? sharedContents->bufferLength : 0;
- }
- BYTE* SharedArrayBuffer::GetBuffer() const
- {
- return sharedContents != nullptr ? sharedContents->buffer : nullptr;
- }
- BOOL SharedArrayBuffer::GetDiagTypeString(StringBuilder<ArenaAllocator>* stringBuilder, ScriptContext* requestContext)
- {
- stringBuilder->AppendCppLiteral(_u("Object, (SharedArrayBuffer)"));
- return TRUE;
- }
- BOOL SharedArrayBuffer::GetDiagValueString(StringBuilder<ArenaAllocator>* stringBuilder, ScriptContext* requestContext)
- {
- stringBuilder->AppendCppLiteral(_u("[object SharedArrayBuffer]"));
- return TRUE;
- }
- JavascriptSharedArrayBuffer::JavascriptSharedArrayBuffer(DynamicType * type) :
- SharedArrayBuffer(type)
- {
- }
- JavascriptSharedArrayBuffer::JavascriptSharedArrayBuffer(SharedContents *sharedContents, DynamicType * type) :
- SharedArrayBuffer(sharedContents, type)
- {
- }
- JavascriptSharedArrayBuffer* JavascriptSharedArrayBuffer::Create(uint32 length, DynamicType * type)
- {
- Recycler* recycler = type->GetScriptContext()->GetRecycler();
- JavascriptSharedArrayBuffer* result = RecyclerNewFinalized(recycler, JavascriptSharedArrayBuffer, type);
- result->Init(length, length);
- recycler->AddExternalMemoryUsage(length);
- return result;
- }
- JavascriptSharedArrayBuffer* JavascriptSharedArrayBuffer::Create(SharedContents *sharedContents, DynamicType * type)
- {
- AssertOrFailFast(!sharedContents || !sharedContents->IsWebAssembly());
- Recycler* recycler = type->GetScriptContext()->GetRecycler();
- JavascriptSharedArrayBuffer* result = RecyclerNewFinalized(recycler, JavascriptSharedArrayBuffer, sharedContents, type);
- return result;
- }
- bool SharedArrayBuffer::IsValidVirtualBufferLength(uint length) const
- {
- #if ENABLE_FAST_ARRAYBUFFER
- /*
- 1. length >= 2^16
- 2. length is power of 2 or (length > 2^24 and length is multiple of 2^24)
- 3. length is a multiple of 4K
- */
- return !PHASE_OFF1(Js::TypedArrayVirtualPhase) &&
- JavascriptArrayBuffer::IsValidAsmJsBufferLengthAlgo(length, true);
- #else
- return false;
- #endif
- }
- void JavascriptSharedArrayBuffer::Finalize(bool isShutdown)
- {
- if (sharedContents == nullptr)
- {
- return;
- }
- uint ref = sharedContents->Release();
- if (ref == 0)
- {
- this->FreeBuffer(sharedContents->buffer, sharedContents->bufferLength, sharedContents->maxBufferLength);
- Recycler* recycler = GetType()->GetLibrary()->GetRecycler();
- recycler->ReportExternalMemoryFree(sharedContents->bufferLength);
- sharedContents->Cleanup();
- HeapDelete(sharedContents);
- }
- sharedContents = nullptr;
- }
- void JavascriptSharedArrayBuffer::Dispose(bool isShutdown)
- {
- /* See JavascriptArrayBuffer::Finalize */
- }
- #ifdef ENABLE_WASM_THREADS
- WebAssemblySharedArrayBuffer::WebAssemblySharedArrayBuffer(DynamicType * type):
- JavascriptSharedArrayBuffer(type)
- {
- AssertOrFailFast(Wasm::Threads::IsEnabled());
- }
- WebAssemblySharedArrayBuffer::WebAssemblySharedArrayBuffer(SharedContents *sharedContents, DynamicType * type) :
- JavascriptSharedArrayBuffer(sharedContents, type)
- {
- AssertOrFailFast(Wasm::Threads::IsEnabled());
- ValidateBuffer();
- }
- void WebAssemblySharedArrayBuffer::ValidateBuffer()
- {
- #if DBG && _WIN32
- if (CONFIG_FLAG(WasmSharedArrayVirtualBuffer))
- {
- MEMORY_BASIC_INFORMATION info = { 0 };
- size_t size = 0;
- size_t allocationSize = 0;
- // Make sure the beggining of the buffer is committed memory to the expected size
- if (sharedContents->bufferLength > 0)
- {
- size = VirtualQuery((LPCVOID)sharedContents->buffer, &info, sizeof(info));
- Assert(size > 0);
- allocationSize = info.RegionSize + ((uintptr_t)info.BaseAddress - (uintptr_t)info.AllocationBase);
- Assert(allocationSize == sharedContents->bufferLength && info.State == MEM_COMMIT && info.Type == MEM_PRIVATE);
- }
- // Make sure the end of the buffer is reserved memory to the expected size
- size_t expectedAllocationSize = sharedContents->maxBufferLength;
- #if ENABLE_FAST_ARRAYBUFFER
- if (CONFIG_FLAG(WasmFastArray))
- {
- expectedAllocationSize = MAX_WASM__ARRAYBUFFER_LENGTH;
- }
- #endif
- // If the whole buffer has been committed, no need to verify this
- if (expectedAllocationSize > sharedContents->bufferLength)
- {
- size = VirtualQuery((LPCVOID)(sharedContents->buffer + sharedContents->bufferLength), &info, sizeof(info));
- Assert(size > 0);
- allocationSize = info.RegionSize + ((uintptr_t)info.BaseAddress - (uintptr_t)info.AllocationBase);
- Assert(allocationSize == expectedAllocationSize && info.State == MEM_RESERVE && info.Type == MEM_PRIVATE);
- }
- }
- #endif
- }
- WebAssemblySharedArrayBuffer* WebAssemblySharedArrayBuffer::Create(uint32 length, uint32 maxLength, DynamicType * type)
- {
- AssertOrFailFast(Wasm::Threads::IsEnabled());
- Recycler* recycler = type->GetScriptContext()->GetRecycler();
- WebAssemblySharedArrayBuffer* result = RecyclerNewFinalized(recycler, WebAssemblySharedArrayBuffer, type);
- result->Init(length, maxLength);
- result->sharedContents->SetIsWebAssembly();
- result->ValidateBuffer();
- recycler->AddExternalMemoryUsage(length);
- return result;
- }
- WebAssemblySharedArrayBuffer* WebAssemblySharedArrayBuffer::Create(SharedContents *sharedContents, DynamicType * type)
- {
- AssertOrFailFast(Wasm::Threads::IsEnabled());
- AssertOrFailFast(sharedContents && sharedContents->IsWebAssembly());
- Recycler* recycler = type->GetScriptContext()->GetRecycler();
- WebAssemblySharedArrayBuffer* result = RecyclerNewFinalized(recycler, WebAssemblySharedArrayBuffer, sharedContents, type);
- return result;
- }
- bool WebAssemblySharedArrayBuffer::IsValidVirtualBufferLength(uint length) const
- {
- #if ENABLE_FAST_ARRAYBUFFER
- if (CONFIG_FLAG(WasmFastArray))
- {
- return true;
- }
- #endif
- #ifdef _WIN32
- if (CONFIG_FLAG(WasmSharedArrayVirtualBuffer))
- {
- return true;
- }
- #endif
- return false;
- }
- _Must_inspect_result_ bool WebAssemblySharedArrayBuffer::GrowMemory(uint32 newBufferLength)
- {
- uint32 bufferLength = sharedContents->bufferLength;
- BYTE* buffer = sharedContents->buffer;
- if (newBufferLength < bufferLength || newBufferLength > sharedContents->maxBufferLength)
- {
- AssertMsg(newBufferLength <= sharedContents->maxBufferLength, "This shouldn't happen");
- Assert(UNREACHED);
- JavascriptError::ThrowTypeError(GetScriptContext(), WASMERR_BufferGrowOnly);
- }
- uint32 growSize = newBufferLength - bufferLength;
- // We're not growing the buffer, do nothing
- if (growSize == 0)
- {
- return true;
- }
- AssertOrFailFast(buffer);
- if (IsValidVirtualBufferLength(newBufferLength))
- {
- auto virtualAllocFunc = [=]
- {
- return !!VirtualAlloc(buffer + bufferLength, growSize, MEM_COMMIT, PAGE_READWRITE);
- };
- if (!this->GetRecycler()->DoExternalAllocation(growSize, virtualAllocFunc))
- {
- return false;
- }
- this->GetRecycler()->AddExternalMemoryUsage(growSize);
- }
- else
- {
- // We have already allocated maxLength in the heap if we're not using virtual alloc
- }
- ZeroMemory(buffer + bufferLength, growSize);
- sharedContents->bufferLength = newBufferLength;
- ValidateBuffer();
- return true;
- }
- BYTE* WebAssemblySharedArrayBuffer::AllocBuffer(uint32 length, uint32 maxLength)
- {
- #if ENABLE_FAST_ARRAYBUFFER
- if (CONFIG_FLAG(WasmFastArray))
- {
- return (BYTE*)WasmVirtualAllocator(length);
- }
- #endif
- #ifdef _WIN32
- if (CONFIG_FLAG(WasmSharedArrayVirtualBuffer))
- {
- return (BYTE*)AllocWrapper(length, maxLength);
- }
- #endif
- AssertOrFailFast(maxLength >= length);
- uint32 additionalSize = maxLength - length;
- if (additionalSize > 0)
- {
- // SharedArrayBuffer::Init already requested External Memory for `length`, we need to request the balance
- if (!this->GetRecycler()->RequestExternalMemoryAllocation(additionalSize))
- {
- // Failed to request for more memory
- return nullptr;
- }
- }
- // Allocate the full size of the buffer if we can't do VirtualAlloc
- return HeapNewNoThrowArray(BYTE, maxLength);
- }
- void WebAssemblySharedArrayBuffer::FreeBuffer(BYTE* buffer, uint32 length, uint32 maxLength)
- {
- if (IsValidVirtualBufferLength(length))
- {
- FreeMemAlloc(buffer);
- }
- else
- {
- HeapDeleteArray(maxLength, buffer);
- AssertOrFailFast(maxLength >= length);
- // JavascriptSharedArrayBuffer::Finalize will only report freeing `length`, we have to take care of the balance
- uint32 additionalSize = maxLength - length;
- if (additionalSize > 0)
- {
- Recycler* recycler = GetType()->GetLibrary()->GetRecycler();
- recycler->ReportExternalMemoryFree(additionalSize);
- }
- }
- }
- #endif
- WaiterList::WaiterList()
- : m_waiters(nullptr)
- {
- InitWaiterList();
- }
- void WaiterList::InitWaiterList()
- {
- Assert(m_waiters == nullptr);
- m_waiters = HeapNew(Waiters, &HeapAllocator::Instance);
- }
- void WaiterList::Cleanup()
- {
- if (m_waiters != nullptr)
- {
- HeapDelete(m_waiters);
- m_waiters = nullptr;
- }
- }
- bool WaiterList::Contains(DWORD_PTR agent)
- {
- Assert(m_waiters != nullptr);
- for (int i = 0; i < m_waiters->Count(); i++)
- {
- if (m_waiters->Item(i).identity == agent)
- {
- return true;
- }
- }
- return false;
- }
- bool _Requires_lock_held_(csForAccess.cs) WaiterList::AddAndSuspendWaiter(DWORD_PTR waiter, uint32 timeout)
- {
- #ifdef _WIN32
- Assert(m_waiters != nullptr);
- Assert(waiter != NULL);
- Assert(!Contains(waiter));
- AgentOfBuffer agent(waiter, CreateEvent(NULL, TRUE, FALSE, NULL));
- m_waiters->Add(agent);
- csForAccess.Leave();
- DWORD result = WaitForSingleObject(agent.event, timeout);
- csForAccess.Enter();
- return result == WAIT_OBJECT_0;
- #else
- // TODO for xplat
- return false;
- #endif
- }
- void WaiterList::RemoveWaiter(DWORD_PTR waiter)
- {
- #ifdef _WIN32
- Assert(m_waiters != nullptr);
- for (int i = m_waiters->Count() - 1; i >= 0; i--)
- {
- if (m_waiters->Item(i).identity == waiter)
- {
- CloseHandle(m_waiters->Item(i).event);
- m_waiters->RemoveAt(i);
- return;
- }
- }
- Assert(false);
- #endif
- // TODO for xplat
- }
- uint32 WaiterList::RemoveAndWakeWaiters(int32 count)
- {
- Assert(m_waiters != nullptr);
- Assert(count >= 0);
- uint32 removed = 0;
- #ifdef _WIN32
- while (count > 0 && m_waiters->Count() > 0)
- {
- AgentOfBuffer agent = m_waiters->Item(0);
- m_waiters->RemoveAt(0);
- count--; removed++;
- SetEvent(agent.event);
- // This agent will be closed when their respective call to wait has returned
- }
- #endif
- return removed;
- }
- bool AgentOfBuffer::AgentCanSuspend(ScriptContext *scriptContext)
- {
- // Currently we are allowing every agent (including main page) to suspend. If we want only web-worker to suspend un-comment below code.
- // return scriptContext != nullptr && scriptContext->webWorkerId != Constants::NonWebWorkerContextId;
- return true;
- }
- }
|